[id='rule-unit-dsl_{context}']

= Rule Unit DSL

In addition to the standard Rule Unit API, {PRODUCT} 8 offers an alternative way of writing rules in combination with Rule Unit. You can now define the rules for Rule Units using a dedicated set of Java APIs. Let's learn with comments on the example.

[source,java]
----
public class HelloWorldUnit implements RuleUnitDefinition {

    private final DataStore<String> strings; // DataStore where you add String fact
    private final DataStore<Integer> ints; // DataStore where you add Integer fact
    private final List<String> results = new ArrayList<>(); // Store results. In traditional DRL, it is called `global`

    // omitting constructors and getters
    // ...

    @Override
    public void defineRules(RulesFactory rulesFactory) {
        // /strings[ this == "Hello World" ]
        rulesFactory.rule()
                    .on(strings)
                    .filter(EQUAL, "Hello World") // when no extractor is provided "this" is implicit
                    .execute(results, r -> r.add("it worked!")); // the consequence can ignore the matched facts

        // /strings[ length > 5 ]
        rulesFactory.rule()
                    .on(strings) // since the datasource has been already initialized its class can be inferred without the need of explicitly passing it
                    .filter(s -> s.length(), GREATER_THAN, 5) // when no property name is provided it's impossible to generate indexes and property reactivity
                    .execute(results, (r, s) -> r.add("it also worked with " + s.toUpperCase())); // this consequence also uses the matched fact

        // /strings[ length < 5 ]
        rulesFactory.rule("MyRule") // it is possible to optionally set a name for the rule
                    .on(strings)
                    .filter("length", s -> s.length(), LESS_THAN, 5) // providing the name of the property used in the constraint allows index and property reactivity generation
                    .execute(results, r -> r.add("this shouldn't fire"));

        // $s: /strings[ length > 5 ]
        // /ints[ this > 5, this == $s.length ]
        rulesFactory.rule()
                    .on(strings)
                    .filter("length", s -> s.length() > 5) // it is also possible to use a plain lambda predicate, but in this case no index can be generated
                    .join(
                          rule -> rule.on(ints) // creates a new pattern ...
                                      .filter(GREATER_THAN, 5) // ... add an alpha filter to it
                    ) // ... and join it with the former one
                    .filter(EQUAL, String::length) // this filter is applied to the result of the join, so it is a beta constraint
                    .execute(results, (r, s, i) -> r.add("String '" + s + "' is " + i + " characters long")); // the consequence captures all the joined variables positionally
    }
}
----

As you define rules using `defineRules` method, you can execute the rules, of course, without having to define them using DRL.

[source,java]
----
    public void helloWorld() {
        HelloWorldUnit unit = new HelloWorldUnit();
        unit.getStrings().add("Hello World");

        RuleUnitInstance<HelloWorldUnit> unitInstance = RuleUnitProvider.get().createRuleUnitInstance(unit);

        assertThat(unitInstance.fire()).isEqualTo(2);
        assertThat(unit.getResults()).containsExactlyInAnyOrder("it worked!", "it also worked with HELLO WORLD");

        unit.getResults().clear();
        unit.getInts().add(11);
        assertThat(unitInstance.fire()).isEqualTo(1);
        assertThat(unit.getResults()).containsExactly("String 'Hello World' is 11 characters long");

        unitInstance.close();
    }
----

You can find various test cases under https://github.com/kiegroup/drools/tree/main/drools-ruleunits/drools-ruleunits-dsl