Variables can either be bound to properties of an object, or the entire object itself:

rule "example rule"
when
   Purchase ( $t : total )
  $p :  Purchase ( total > $t )
then
  System.out.println("amount " + $p.getTotal() + " > " + $t);
end

As seen here, variables can be accessed in both, a condition and consequence (then part) of a rule. If used within a condition, the variable must have been declared and bound previously, and must be used on the right-hand side of the evaluator (that is you’ll always use the order property, operator, then variable), otherwise our rule would expect that Purchase has a property $t, which would cause an exception to be thrown.

In order to introduce a few additional things you’ll want to be familiar with, let’s consider this example rule file:

package org.tacoshop

import org.tacoshop.model.Purchase

// hey here's a rule!
rule "example rule"
when
  $p : Purchase ( tacoCount >= 10, ( tacoCount % 2 == 0 ) )
then
  /* this consequence is pretty simple,
    but let's give it a comment anyways */
  $p.setDiscount(addDiscount($p, 0.15));
end

function double addDiscount(Purchase p, double discount) {
  return (p.getDiscount + discount);
}

We’re familiar with the package declaration and import syntax, nothing new there. However, we’ve not seen a global variable declaration yet. Drools files allow single-line comments using either the # or // prefix, as well as multi-line comments using the /* comment */ construct.

If you take a closer look at the rule condition, you’ll notice that we’ve formed an evaluative statement checking that the number of tacos in the purchase is evenly divisible by two. We’re able to use any Java expression within a pattern as part of the condition, so long as the return is a Boolean value contributing to the truth of that condition.

Lastly, notice the function declared in this file. Functions within rule files carry the same functionality and syntax you’d expect from any Java class method, and we’ve referenced our function from within the rule consequence. If you find yourself repeating a lot of code, then a function can help to eliminate redundancy and simplify refactoring.

Operators for constraints

Drools provides us with several operators for use within our rule constraints. Some of the most commonly used operators include the following:

  • &&: Both Booleans must be true. Previously we’ve used && implicitly by including multiple property evaluations in a comma-separated series and by including multiple conditions:

    Purchase ( tacoCount >= 2, discount < 0.15 )
    Purchase ( total > 15 && tacoCount >= 2 )
    
  • ||: One of the two Booleans must be true. Note that when using && or || on a single property, the syntax can be shortened as follows:

    Purchase ( total < 15 || total > 25 )
    Purchase ( total < 15 || > 25 )
    
  • ( ) : We can form more complex patterns using parentheses:

    Purchase ( ( total < 15 || > 25 ) && drinkIncluded )
    
  • this: This operator allows us to reference a fact within the condition of the pattern, which, among other uses, will disallow the same fact from satisfying multiple conditions:

    $p : Purchase ( total > 15 )
    Purchase ( this != $p, tacoCount > 2 )
    
  • in: This checks if a value exists within a comma-separated list of values for potentially matching (can be paired with not):

    Person ( name in ("Steve", "James", "Everett") )
    Person ( name not in ("Jane", "Becca", "Mary") )
    
  • matches: This matches against a regular expression (can be paired with not):

    Person ( name matches "^[abc]" )
    Person ( name not matches "^[xyz]" )
    
  • memberOf: This checks if a field exists within a collection (can be paired with not):

    Child ( name memberOf $niceList )
    Child ( name not memberOf $naughtyList )
    
  • contains: This is much like memberOf, but allows us to check the inverse—that a collection field contains a value (can be paired with not):

    NiceList ( names contains $childName )
    NaughtyList ( names not contains $childName )
    

Common operators: ==, !=, >, >=, <, <=, and so on

Conditional elements for patterns

There are also a handful of conditional elements that you should be aware of which allow for a more complex usage of patterns:

  • exists: At least one matching fact exists:

    exists Purchase ( total > 15 )
    
  • not: no matching facts exist:

    not Purchase ( total > 15 )
    
  • from: Allows access to nested collections:

    $school : School ( )
    Student ( name == "Joe" ) from $school.students
    
  • collect: Allows evaluation of multiple facts within the system as a group:

    $maleStudents : ArrayList ( )
    from collect ( Student ( gender == "male" ) )
    
  • accumulate: Functions similarly to collect in that we’re grouping facts, but also allows for manipulation on the collection prior to returning the result:

    $savings : Number ( )
        from accumulate ( PiggyBank ( $total : total ),
                             sum ( $total ) )
    
  • eval: Allows for execution of any block that returns a Boolean value. Please note that eval elements are not kind on engine performance and thus should only be used when absolutely necessary.

    eval( isTheSame( $a, $b ) )
    
  • forall (single): All facts of a given type match all the patterns supplied:

    forall ( Student ( age >= 7 ) )
    
  • forall (multi): All facts matching the first pattern must also match remaining patterns:

    forall ( $dog : Dog ( breed == "dachshund" )
      Dog ( this == $dog, color == "red" ) )
    
  • and: Both patterns must be true. Previously we’ve used and implicitly by including multiple conditions, but these can also be grouped with parentheses:

    ( Purchase ( tacoCount >= 2, discount < 0.15 )
      and Purchase ( total > 15 && tacoCount >= 2 ) )
    
  • or: One of the two patterns must be true:

    ( Purchase ( total < 15 || total > 25 )
      or Purchase ( total < 15 || > 25 ) )
    

Manipulating facts in rules

Inserting a fact from within a consequence looks like this:

then
  insertLogical ( new Person() );
end

or

then
  insert ( new Person() );
end

insertLogical binds insertion with the condition, so when rule’s when clause is not true, the fact is automatically removed. Thus in most practical cases insertLogical is the preferred way to insert objects.

In order to alter a fact from within a rule consequence, you’ll find that there are two options available to you, modify and update. Though similar in concept, there’s one very important difference between the two. When you use modify, the fact is altered and the engine is notified at the same time, preventing a gap between the two steps that could potentially lead to indexing issues with the engine. Update, on the other hand, does not provide this same functionality, so it is recommended that from within a rule consequence, you always use modify. The syntax is as follows:

then
  modify( $person ) { setMood( "happy" ) };
end

And lastly, we can also remove a fact from working memory from within a rule consequence.

then
  delete ( $person );
end

Note, you need delete only facts that were inserted using insert keyword. In all other cases the engine will do it automatically.