Rule

Figure 4.10. rule

rule

A rule specifies that when a particular set of conditions occur, specified in the Left Hand Side (LHS), then do what is specified as a list of actions in the Right Hand Side (RHS). A common question from users is "Why use when instead of if?" "When" was chosen over "if" because "if" is normally part of a procedural execution flow, where, at a specific point in time, a condition is to be checked. In contrast, "when" indicates that the condition evaluation is not tied to a specific evaluation sequence or point in time, but that it happens continually, at any time during the life time of the engine; whenever the condition is met, the actions are executed.

A rule must have a name, unique within its rule package. If you define a rule twice in the same DRL it produces an error while loading. If you add a DRL that includes a rule name already in the package, it replaces the previous rule. If a rule name is to have spaces, then it will need to be enclosd in double quotes (it is best to always use double quotes).

Attributes - described below - are optional. They are best written one per line.

The LHS of the rule follows the <kw>when</kw> keyword (ideally on a new line), similarly the RHS follows the <kw>then</kw> keyword (again, ideally on a newline). The rule is terminated by the keyword <kw>end</kw>. Rules cannot be nested.

Example 4.21. Rule Syntax Overview

rule "<name>"
    <attribute>*
when
    <conditional element>*
then
    <action>*
end

Example 4.22. A simple rule

rule "Approve if not rejected"
  salience -100 
  agenda-group "approval"
    when
        not Rejection() 
        p : Policy(approved == false, policyState:status)
        exists Driver(age > 25)
        Process(status == policyState)
    then
        log("APPROVED: due to no objections."); 
        p.setApproved(true);
end

Rule Attributes

Rule attributes provide a declarative way to influence the behavior of the rule. Some are quite simple, while others are part of complex subsystems such as ruleflow. To get the most from Drools you should make sure you have a proper understanding of each attribute.

Figure 4.11. rule attributes

rule attributes

<kw>no-loop</kw>

default value: false

type: Boolean

When the rule's consequence modifies a fact it may cause the Rule to activate again, causing recursion. Setting no-loop to true means the attempt to create the Activation for the current set of data will be ignored.

<kw>ruleflow-group</kw>

default value: N/A

type: String

Ruleflow is a Drools feature that lets you exercise control over the firing of rules. Rules that are assembled by the same ruleflow-group identifier fire only when their group is active.

<kw>lock-on-active</kw>

default value: false

type: Boolean

Whenever a ruleflow-group becomes active or an agenda-group receives the focus, any rule within that group that has lock-on-active set to true will not be activated any more; irrespective of the origin of the update, the activation of a matching rule is discarded. This is a stronger version of no-loop, because the change could now be caused not only by the rule itself. It's ideal for calculation rules where you have a number of rules that modify a fact and you don't want any rule re-matching and firing again. Only when the ruleflow-group is no longer active or the agenda-group loses the focus those rules with lock-on-active set to true become eligible again for their activations to be placed onto the agenda.

<kw>salience</kw>

default value : 0

type : integer

Each rule has a salience attribute that can be assigned an integer number, which defaults to zero and can be negative or positive. Salience is a form of priority where rules with higher salience values are given higher priority when ordered in the Activation queue.

<kw>agenda-group</kw>

default value: MAIN

type: String

Agenda groups allow the user to partition the Agenda providing more execution control. Only rules in the agenda group that has acquired the focus are allowed to fire.

<kw>auto-focus</kw>

default value: false

type: Boolean

When a rule is activated where the auto-focus value is true and the rule's agenda group does not have focus yet, then it is given focus, allowing the rule to potentially fire.

<kw>activation-group</kw>

default value: N/A

type: String

Rules that belong to the same activation-group, identified by this attribute's string value, will only fire exclusively. In other words, the first rule in an activation-group to fire will cancel the other rules' activations, i.e., stop them from firing.

Note: This used to be called Xor group, but technically it's not quite an Xor. You may still hear people mention Xor group; just swap that term in your mind with activation-group.

<kw>dialect</kw>

default value: as specified by the package

type: String

possible values: "java" or "mvel"

The dialect species the language to be used for any code expressions in the LHS or the RHS code block. Currently two dialects are available, Java and MVEL. While the dialect can be specified at the package level, this attribute allows the package definition to be overridden for a rule.

<kw>date-effective</kw>

default value: N/A

type: String, containing a date and time definition

A rule can only activate if the current date and time is after date-effective attribute.

<kw>date-expires</kw>

default value: N/A

type: String, containing a date and time definition

A rule cannot activate if the current date and time is after the date-expires attribute.

<kw>duration</kw>

default value: no default value

type: long

The duration dictates that the rule will fire after a specified duration, if it is still true.

Example 4.23. Some attribute examples

rule "my rule"
  salience 42
  agenda-group "number 1"
    when ...

Left Hand Side (when) Conditional Elements

The Left Hand Side (LHS) is a common name for the conditional part of the rule. It consists of zero or more Conditional Elements. If the LHS is left empty, it is re-written as eval(true), which means that the rule's condition is always true. It will be activated, once, when a new Working Memory session is created.

Figure 4.12. Left Hand Side

Left Hand Side

Example 4.24. Rule without a Conditional Element

rule "no CEs"
when
then
    <action>*
end

# The above rule is internally rewritten as:

rule "eval(true)"
when
    eval( true )
then
    <action>*
end

Conditional elements work on one or more patterns (which are described below). The most common one is <kw>and</kw>, which is implicit when you have multiple patterns in the LHS of a rule that are not connected in any way. Note that an <kw>and</kw> cannot have a leading declaration binding like <kw>or</kw>. This is obvious, since a declaration can only reference a single fact, and when the <kw>and</kw> is satisfied it matches more than one fact - so which fact would the declaration bind to?

Pattern

The pattern element is the most important Conditional Element. The entity relationship diagram below provides an overview of the various parts that make up the pattern's constraints and how they work together; each is then covered in more detail with railroad diagrams and examples.

Figure 4.13. Pattern Entity Relationship Diagram

Pattern Entity Relationship Diagram

At the top of the ER diagram you can see that the pattern consists of zero or more constraints and has an optional pattern binding. The railroad diagram below shows the syntax for this.

Figure 4.14. Pattern

Pattern

In its simplest form, with no constraints, a pattern matches against a fact of the given type. In the following case the type is Cheese, which means that the pattern will match against all Cheese objects in the Working Memory.

Notice that the type need not be the actual class of some fact object. Patterns may refer to superclasses or even interfaces, thereby potentially matching facts from many different classes.

Example 4.25. Simple Pattern

Cheese()

For referring to the matched object, use a pattern binding variable such as $c. The prefixed dollar symbol ('$') is optional; it can be useful in complex rules where it helps to more easily differentiate between variables and fields.

Example 4.26. Pattern with a binding variable

$c : Cheese()

Inside of the pattern parenthesis is where all the action happens. A constraint can be either a Field Constraint, Inline Eval, or a Constraint Group. Constraints can be separated by the following symbols: ',', '&&' or '||'.

Figure 4.15. Constraints

Constraints

Figure 4.16. Constraint

Constraint

Figure 4.17. constraintGroup

constraintGroup

The comma character (',') is used to separate constraint groups. It has implicit and connective semantics.

Example 4.27. Constraint Group connective ','

# Cheese type is stilton and price < 10 and age is mature.
Cheese( type == "stilton", price < 10, age == "mature" )


The above example has three constraint groups, each with a single constraint:

  • Group 1 - type == "stilton" requires that the type is stilton.

  • Group 2 - price < 10 demands a price less than 10.

  • Group 3 - age == "mature" accepts only mature cheese.

The '&&' (and) and '||' (or) constraint connectives allow constraint groups to have multiple constraints. Example:

Example 4.28. && and || Constraint Connectives

// Cheese type is "stilton" and price < 10, and age is mature
Cheese( type == "stilton" && price < 10, age == "mature" )
// Cheese type is "stilton" or price < 10, and age is mature
Cheese( type == "stilton" || price < 10, age == "mature" )

The above example has two constraint groups. The first has two constraints and the second has one constraint.

The connectives are evaluated in the following order, from first to last:

  1. &&

  2. ||

  3. ,

It is possible to change the evaluation priority by using parentheses, as in any logic or mathematical expression. Example:

Example 4.29. Using parentheses to change evaluation priority

# Cheese type is stilton and ( price is less than 20 or age is mature ).
Cheese( type == "stilton" && ( price < 20 || age == "mature" ) ) 


In the above example, the use of parentheses evaluates the connective '||' before the connective '&&'.

Also, it is important to note that besides having the same semantics, the connectives '&&' and ',' are resolved with different priorities, and ',' cannot be embedded in a composite constraint expression.

Example 4.30. Not Equivalent connectives

// invalid as ',' cannot be embedded in an expression:
Cheese( ( type == "stilton", price < 10 ) || age == "mature" )
// valid as '&&' can be embedded in an expression:
Cheese( ( type == "stilton" && price < 10 ) || age == "mature")

Field Constraints

A Field constraint specifies a restriction to be used on a named field; the field name can have an optional variable binding.

Figure 4.18. fieldConstraint

fieldConstraint

There are three types of restrictions: Single Value Restriction, Compound Value Restriction, and Multi Restriction.

Figure 4.19. restriction

restriction

JavaBeans as facts

A field is derived from an accessible method of the object. If your model objects follow the Java Bean pattern, then fields are exposed using "getXXX" or "isXXX" methods, where these methods take no arguments, and return something. Within patterns, fields can be accessed using the bean naming convention, so that "getType" would be accessed as "type". Drools uses the standard JDK Introspector class to do this mapping.

For example, referring to our Cheese class, the pattern Cheese(type == "brie") applies the getType() method to a Cheese instance. If a field name cannot be found, the compiler will resort to using the name as a method without arguments. Thus, the method toString() is called due to a constraint Cheese(toString == "cheddar"). In this case, you use the full name of the method with correct capitalization, but still without parentheses. Do please make sure that you are accessing methods that take no parameters, and that are in fact accessors which don't change the state of the object in a way that may effect the rules. Remember that the rule engine effectively caches the results of its matching in between invocations to make it faster.

Values

The field constraints can take a number of values; including literal, qualifiedIdentifier (enum), variable and returnValue.

Figure 4.20. literal

literal

Figure 4.21. qualifiedIdentifier

qualifiedIdentifier

Figure 4.22. variable

variable

Figure 4.23. returnValue

returnValue

You can do checks against fields that are or may be null, using '==' and '!=' as you would expect, and the literal <kw>null</kw> keyword, as in Cheese(type != null), where the evaluator will not throw an exception and return true if the value is null. Type coercion is always attempted if the field and the value are of different types; exceptions will be thrown if a bad coercion is attempted. For instance, if "ten" is provided as a string in a numeric evaluator, an exception is thrown, whereas "10" would coerce to a numeric 10. Coercion is always in favor of the field type and not the value type.

Single Value Restriction

Figure 4.24. singleValueRestriction

singleValueRestriction

A Single Value Restriction is a binary relation, applying a binary operator to the field value and another value, which may be a literal, a variable, a parenthesized expression ("return value"), or a qualified identifier, i.e., an enum constant.

Operators

Figure 4.25. Operators

Operators

The operators '==' and '!=' are valid for all types. Other relational operatory may be used whenever the type values are ordered; for date fields, '<' means "before". The pair <kw>matches</kw> and <kw>not matches</kw> is only applicable to string fields, <kw>contains</kw> and <kw>not contains</kw> require the field to be of some Collection type. Coercion to the correct value for the evaluator and the field will be attempted, as mentioned in the "Values" section.

The Operator <kw>matches</kw>

Matches a field against any valid Java Regular Expression. Typically that regexp is a string literal, but variables that resolve to a valid regexp are also allowed. It is important to note that, different from Java, within regular expressions written as string literals you don't need to escape '\'. Example:

Example 4.31. Regular Expression Constraint

Cheese( type matches "(Buffalo)?\S*Mozarella" )

The Operator <kw>not matches</kw>

The operator returns true if the string does not match the regular expression. The same rules apply as for the <kw>matches</kw> operator. Example:

Example 4.32. Regular Expression Constraint

Cheese( type not matches "(Buffulo)?\S*Mozarella" )

The Operator <kw>contains</kw>

The operator <kw>contains</kw> is used to check whether a field that is a Collection or array contains the specified value.

Example 4.33. Contains with Collections

CheeseCounter( cheeses contains "stilton" ) // contains with a String literal
CheeseCounter( cheeses contains $var ) // contains with a variable

The Operator <kw>not contains</kw>

The operator <kw>not contains</kw> is used to check whether a field that is a Collection or array does not contain the specified value.

Example 4.34. Literal Constraint with Collections

CheeseCounter( cheeses not contains "cheddar" ) // not contains with a String literal
CheeseCounter( cheeses not contains $var ) // not contains with a variable

Note

For backward compatibility, the <kw>excludes</kw> operator is supported as a synonym for <kw>not contains</kw>.

The Operator <kw>memberOf</kw>

The operator <kw>memberOf</kw> is used to check whether a field is a member of a collection or array; that collection must be a variable.

Example 4.35. Literal Constraint with Collections

CheeseCounter( cheese memberOf $matureCheeses )

The Operator <kw>not memberOf</kw>

The operator <kw>not memberOf</kw> is used to check whether a field is not a member of a collection or array; that collection must be a variable.

Example 4.36. Literal Constraint with Collections

CheeseCounter( cheese not memberOf $matureCheeses )

The Operator <kw>soundslike</kw>

This operator is similar to <kw>matches</kw>, but it checks whether a word has almost the same sound (using English pronounciation) as the given value. This is based on the Soundex algorithm (see http://en.wikipedia.org/wiki/Soundex).

Example 4.37. Test with soundslike

// match cheese "fubar" or "foobar"
Cheese( name soundslike 'foobar' )

Literal Restrictions

Literal restrictions are the simplest form of restrictions and evaluate a field against a specified literal, which may be numeric or a date, a string or a boolean.

Figure 4.26. literalRestriction

literalRestriction

Literal Restrictions using the operator '==' provide for faster execution as we can index using hashing to improve performance.

Numeric

All standard Java numeric primitives are supported.

Example 4.38. Numeric Literal Restriction

Cheese( quantity == 5 )

Date

The date format "dd-mmm-yyyy" is supported by default. You can customize this by providing an alternative date format mask as the System property named drools.dateformat. If more control is required, use the inline-eval constraint.

Example 4.39. Date Literal Restriction

Cheese( bestBefore < "27-Oct-2009" )

String

Any valid Java String is allowed.

Example 4.40. String Literal Restriction

Cheese( type == "stilton" )

Boolean

Only true or false can be used; 0 and 1 are not acceptable. A boolean field alone (as in Cheese( smelly ) is not permitted; you must compare this to a boolean literal.

Example 4.41. Boolean Literal Restriction

Cheese( smelly == true )

Qualified Identifier

Enums can be used as well, both JDK 1.4 and 5 style enums are supported. For the latter you must be executing on a JDK 5 environment.

Example 4.42. Boolean Literal Restriction

Cheese( smelly == SomeClass.TRUE )

Bound Variable Restriction

Figure 4.27. variableRestriction

variableRestriction

Variables can be bound to facts and their fields and then used in subsequent Field Constraints. A bound variable is called a Declaration. Valid operators are determined by the type of the field being constrained; coercion will be attempted where possible. Bound Variable Restrictions using the operator '==' provide for very fast execution as we can use hashing to improve performance.

Example 4.43. Bound Field using the operator '=='

Person( likes : favouriteCheese )
Cheese( type == likes )

Here, likes is the variable that is bound in its declaration to the field favouriteCheese of any matching Person instance. It is then used to constrain the type of Cheese in the following pattern. Any valid Java variable name can be used, and it may be prefixed with a '$', which you will often see used to help differentiate declarations from fields. The example below shows a declaration for $stilton, bound to the object matching the first pattern and used with a contains operator. - Note the optional use of '$'.

Example 4.44. Bound Fact using 'contains' operator

$stilton : Cheese( type == "stilton" )
Cheesery( cheeses contains $stilton )

Return Value Restriction

Figure 4.28. returnValueRestriction

returnValueRestriction

A Return Value restriction is a parenthesized expression composed from literals, any valid Java primitive or object, previously bound variables, function calls, and operators. Functions used in a Return Value must return results that do not depend on time.

Example 4.45. Return Value Restriction

Person( girlAge : age, sex == "F" )
Person( age == ( girlAge + 2) ), sex == 'M' )

Compound Value Restriction

The compound value restriction is used where there is more than one possible value to match. Currently only the <kw>in</kw> and <kw>not in</kw> evaluators support this. The second operand of this operator must be a comma-separated list of values, enclosed in parentheses. Values may be given as variables, literals, return values or qualified identifiers. Both evaluators are actually "syntactic sugar", internally rewritten as a list of multiple restrictions using the operators '!=' and '=='.

Figure 4.29. compoundValueRestriction

compoundValueRestriction

Example 4.46. Compound Restriction using "in"

Person( $cheese : favouriteCheese )
Cheese( type in ( "stilton", "cheddar", $cheese )

Multi Restriction

Multi restriction allows you to place more than one restriction on a field using the restriction connectives '&&' or '||'. Grouping via parentheses is permitted, resulting in a recursive syntax pattern.

Figure 4.30. multiRestriction

multiRestriction

Figure 4.31. restrictionGroup

restrictionGroup

Example 4.47. Multi Restriction

// Simple multi restriction using a single &&
Person( age > 30 && < 40 )
// Complex multi restriction using groupings of multi restrictions
Person( age ( (> 30 && < 40) ||
              (> 20 && < 25) ) )
// Mixing muti restrictions with constraint connectives
Person( age > 30 && < 40 || location == "london" )

Inline Eval Constraints

Figure 4.32. Inline Eval Expression

Inline Eval Expression

An inline eval constraint can use any valid dialect expression as long as it results to a primitive boolean. The expression must be constant over time. Any previously bound variable, from the current or previous pattern, can be used; autovivification is also used to auto-create field binding variables. When an identifier is found that is not a current variable, the builder looks to see if the identifier is a field on the current object type, if it is, the field binding is auto-created as a variable of the same name. This is called autovivification of field variables inside of inline evals.

This example will find all male-female pairs where the male is 2 years older than the female; the variable age is auto-created in the second pattern by the autovivification process.

Example 4.48. Return Value operator

Person( girlAge : age, sex = "F" )
Person( eval( age == girlAge + 2 ), sex = 'M' )

Nested Accessors

Drools permits nested accessors in in the field constraints using the MVEL accessor graph notation. Field constraints involving nested accessors are actually re-written as an MVEL dialect inline-eval. Care should be taken when using nested accessors as the Working Memory is not aware of any of the nested values, and does not know when they change; they should be considered immutable while any of their parent references are inserted into the Working Memory. If you wish to modify a nested value you should remove the parent objects first and re-assert afterwards. If you only have a single parent at the root of the graph, when in the MVEL dialect, you can use the <kw>modify</kw> construct and its block setters to write the nested accessor assignments while retracting and inserting the the root parent object as required. Nested accessors can be used on either side of the operator symbol.

Example 4.49. Nested Accessors

// Find a pet older than its owners first-born child
$p : Person( ) 
Pet( owner == $p, age > $p.children[0].age )

This is internally rewriten as an MVEL inline eval:

// Find a pet older than its owners first-born child
$p : Person( ) 
Pet( owner == $p, eval( age > $p.children[0].age ) ) 

Note: Nested accessors have a much greater performance cost than direct field accesses, so use them carefully.

Conditional Element <kw>and</kw>

The Conditional Element <kw>and</kw> is used to group other Conditional Elements into a logical conjunction. The root element of the LHS is an implicit prefix <kw>and</kw> and doesn't need to be specified. Drools supports both prefix <kw>and</kw> and infix <kw>and</kw>, but prefix is the preferred option as its implicit grouping avoids confusion.

Figure 4.33. prefixAnd

prefixAnd

Example 4.50. prefixAnd

(and Cheese( cheeseType : type )
     Person( favouriteCheese == cheeseType ) )

Example 4.51. implicit root prefixAnd

when
     Cheese( cheeseType : type )
     Person( favouriteCheese == cheeseType )

Infix <kw>and</kw> is supported along with explicit grouping with parentheses, should it be needed. The symbol '&&', as an alternative to <kw>and</kw>, is deprecated although it is still supported in the syntax for legacy support reasons.

Figure 4.34. infixAnd

infixAnd

Example 4.52. infixAnd

//infixAnd
Cheese( cheeseType : type ) and Person( favouriteCheese == cheeseType )
//infixAnd with grouping
( Cheese( cheeseType : type ) and
  ( Person( favouriteCheese == cheeseType ) or 
    Person( favouriteCheese == cheeseType ) )

Conditional Element <kw>or</kw>

The Conditional Element <kw>or</kw> is used to group other Conditional Elements into a logical disjunction. Drools supports both prefix <kw>or</kw> and infix <kw>or</kw>, but prefix is the preferred option as its implicit grouping avoids confusion. The behavior of the Conditional Element <kw>or</kw> is different from the connective '||' for constraints and restrictions in field constraints. The engine actually has no understanding of the Conditional Element <kw>or</kw>; instead, via a number of different logic transformations, a rule with <kw>or</kw> is rewritten as a number of subrules. This process ultimately results in a rule that has a single <kw>or</kw> as the root node and one subrule for each of its CEs. Each subrule can activate and fire like any normal rule; there is no special behavior or interaction between these subrules. - This can be most confusing to new rule authors.

Figure 4.35. prefixOr

prefixOr

Example 4.53. prefixOr

(or Person( sex == "f", age > 60 )
    Person( sex == "m", age > 65 )

Infix <kw>or</kw> is supported along with explicit grouping with parentheses, should it be needed. The symbol '||', as an alternative to <kw>or</kw>, is deprecated although it is still supported in the syntax for legacy support reasons.

Figure 4.36. infixOr

infixOr

Example 4.54. infixOr

//infixOr
Cheese( cheeseType : type ) or Person( favouriteCheese == cheeseType )
//infixOr with grouping
( Cheese( cheeseType : type ) or
  ( Person( favouriteCheese == cheeseType ) and
    Person( favouriteCheese == cheeseType ) )

The Conditional Element <kw>or</kw> also allows for optional pattern binding. This means that each resulting subrule will bind its pattern to the pattern binding. Each pattern must be bound separately, using eponymous variables:

Example 4.55. or with binding

(or pensioner : Person( sex == "f", age > 60 ) 
    pensioner : Person( sex == "m", age > 65 ) )

Since the conditional element <kw>or</kw> results in multiple subrule generation, one for each possible logically outcome, the example above would result in the internal generation of two rules. These two rules work independently within the Working Memory, which means both can match, activate and fire - there is no shortcutting.

The best way to think of the conditional element <kw>or</kw> is as a shortcut for generating two or more similar rules. When you think of it that way, it's clear that for a single rule there could be multiple activations if two or more terms of the disjunction are true.

Conditional Element <kw>eval</kw>

Figure 4.37. eval

eval

The CE <kw>eval</kw> is essentially a catch-all which allows any semantic code (that returns a primitive boolean) to be executed. This code can refer to variables that were bound in the LHS of the rule, and functions in the rule package. Overuse of eval reduces the declarativeness of your rules and can result in a poorly performing engine. While <kw>eval</kw> can be used anywhere in the patterns, the best practice is to add it as the last conditional element in the LHS of a rule.

Evals cannot be indexed and thus are not as efficient as Field Constraints. However this makes them ideal for being used when functions return values that change over time, which is not allowed within Field Constraints.

For folks who are familiar with Drools 2.x lineage, the old Drools parameter and condition tags are equivalent to binding a variable to an appropriate type, and then using it in an eval node.

Example 4.56. eval

p1 : Parameter() 
p2 : Parameter()
eval( p1.getList().containsKey(p2.getItem()) )
// call function isValid in the LHS
eval( isValid(p1, p2) )

Conditional Element <kw>not</kw>

Figure 4.38. not

not

The CE <kw>not</kw> is first order logic's non-existential quantifier and checks for the non-existence of something in the Working Memory. Think of "not" as meaning "there must be none of...".

The keyword <kw>not</kw> be followed by parentheses around the CEs that it applies to. In the simplest case of a single pattern (like below) you may optionally omit the parentheses.

Example 4.57. No Busses

not Bus()

Example 4.58. No red Busses

// Brackets are optional:
not Bus(color == "red")
// Brackets are optional:
not ( Bus(color == "red", number == 42) )
// "not" with nested infix <kw>and</kw> - two patterns,
// brackets are requires:
not ( Bus(color == "red") and
      Bus(color == "blue") )

Conditional Element <kw>exists</kw>

Figure 4.39. exists

exists

The CE <kw>exists</kw> is first order logic's existential quantifier and checks for the existence of something in the Working Memory. Think of "exists" as meaning "there is at least one..". It is different from just having the pattern on its own, which is more like saying "for each one of...". If you use <kw>exists</kw> with a pattern, the rule will only activate at most once, regardless of how much data there is in working memory that matches the condition inside of the <kw>exists</kw> pattern. Since only the existence matters, no bindings will be established.

The keyword <kw>exists</kw> must be followed by parentheses around the CEs that it applies to. In the simplest case of a single pattern (like below) you may optionally omit the parentheses.

Example 4.59. At least one Bus

exists Bus()

Example 4.60. At least one red Bus

exists Bus(color == "red")
// brackets are optional:
exists ( Bus(color == "red", number == 42) )
// "exists" with nested infix <kw>and</kw>,
// brackets are required:
exists ( Bus(color == "red") and
         Bus(color == "blue") )

Conditional Element <kw>forall</kw>

Figure 4.40. forall

forall

The Conditional Element <kw>forall</kw> completes the First Order Logic support in Drools. The Conditional Element <kw>forall</kw> evaluates to true when all facts that match the first pattern match all the remaining patterns. Example:

rule "All English buses are red"
when
    forall( $bus : Bus( type == 'english') 
                   Bus( this == $bus, color = 'red' ) )
then
    # all english buses are red
end

In the above rule, we "select" all Bus objects whose type is "english". Then, for each fact that matches this pattern we evaluate the following patterns and if they match, the forall CE will evaluate to true.

To state that all facts of a given type in the working memory must match a set of constraints, <kw>forall</kw> can be written with a single pattern for simplicity. Example:

Example 4.61. Single Pattern Forall

rule "All Buses are Red"
when
    forall( Bus( color == 'red' ) )
then
    # all asserted Bus facts are red
end


Another example shows multiple patterns inside the <kw>forall</kw>:

Example 4.62. Multi-Pattern Forall

rule "all employees have health and dental care programs"
when
    forall( $emp : Employee()
            HealthCare( employee == $emp )
            DentalCare( employee == $emp )
          )
then
    # all employees have health and dental care
end


Forall can be nested inside other CEs for complete expressiveness. For instance, <kw>forall</kw> can be used inside a <kw>not</kw> CE. Note that only single patterns have optional parentheses, so that with a nested forall parentheses must be used :

Example 4.63. Combining Forall with Not CE

rule "not all employees have health and dental care"
when 
    not ( forall( $emp : Employee()
                  HealthCare( employee == $emp )
                  DentalCare( employee == $emp ) ) 
        )
then
    # not all employees have health and dental care
end


As a side note, not( forall( p1 p2 p3...)) is equivalent to writing:

not(p1 and not(and p2 p3...))

Also, it is important to note that <kw>forall</kw> is a scope delimiter. Therefore, it can use any previously bound variable, but no variable bound inside it will be available for use outside of it.

Conditional Element <kw>from</kw>

Figure 4.41. from

from

The Conditional Element <kw>from</kw> enables users to specify an arbitrary source for data to be matched by LHS patterns. This allows the engine to reason over data not in the Working Memory. The data source could be a sub-field on a bound variable or the results of a method call. It is a powerful construction that allows out of the box integration with other application components and frameworks. One common example is the integration with data retrieved on-demand from databases using hibernate named queries.

The expression used to define the object source is any expression that follows regular MVEL syntax. Therefore, it allows you to easily use object property navigation, execute method calls and access maps and collections elements.

Here is a simple example of reasoning and binding on another pattern sub-field:

rule "validate zipcode"
when
    Person( $personAddress : address ) 
    Address( zipcode == "23920W") from $personAddress 
then
    # zip code is ok
end

With all the flexibility from the new expressiveness in the Drools engine you can slice and dice this problem many ways. This is the same but shows how you can use a graph notation with the 'from':

rule "validate zipcode"
when
    $p : Person( ) 
    $a : Address( zipcode == "23920W") from $p.address 
then
    # zip code is ok
end

Previous examples were evaluations using a single pattern. The CE <kw>from</kw> also support object sources that return a collection of objects. In that case, <kw>from</kw> will iterate over all objects in the collection and try to match each of them individually. For instance, if we want a rule that applies 10% discount to each item in an order, we could do:

rule "apply 10% discount to all items over US$ 100,00 in an order"
when
    $order : Order()
    $item  : OrderItem( value > 100 ) from $order.items
then
    # apply discount to $item
end

The above example will cause the rule to fire once for each item whose value is greater than 100 for each given order.

You must take caution, however, when using <kw>from</kw>, especially in conjunction with the <kw>lock-on-active</kw> rule attribute as it may produce unexpected results. Consider the example provided earlier, but now slightly modified as follows:

rule "Assign people in North Carolina (NC) to sales region 1"
ruleflow-group "test"
lock-on-active true
when
    $p : Person( ) 
    $a : Address( state == "NC") from $p.address 
then
    modify ($p) {} #Assign person to sales region 1 in a modify block
end

rule "Apply a discount to people in the city of Raleigh"
ruleflow-group "test"
lock-on-active true
when
    $p : Person( ) 
    $a : Address( city == "Raleigh") from $p.address 
then
    modify ($p) {} #Apply discount to person in a modify block
end

In the above example, persons in Raleigh, NC should be assigned to sales region 1 and receive a discount; i.e., you would expect both rules to activate and fire. Instead you will find that only the second rule fires.

If you were to turn on the audit log, you would also see that when the second rule fires, it deactivates the first rule. Since the rule attribute <kw>lock-on-active</kw> prevents a rule from creating new activations when a set of facts change, the first rule fails to reactivate. Though the set of facts have not changed, the use of <kw>from</kw> returns a new fact for all intents and purposes each time it is evaluated.

First, it's important to review why you would use the above pattern. You may have many rules across different rule-flow groups. When rules modify working memory and other rules downstream of your RuleFlow (in different rule-flow groups) need to be reevaluated, the use of <kw>modify</kw> is critical. You don't, however, want other rules in the same rule-flow group to place activations on one another recursively. In this case, the <kw>no-loop</kw> attribute is ineffective, as it would only prevent a rule from activating itself recursively. Hence, you resort to <kw>lock-on-active</kw>.

There are several ways to address this issue:

  • Avoid the use of <kw>from</kw> when you can assert all facts into working memory or use nested object references in your constraint expressions (shown below).

  • Place the variable assigned used in the modify block as the last sentence in your condition (LHS).

  • Avoid the use of <kw>lock-on-active</kw> when you can explicitly manage how rules within the same rule-flow group place activations on one another (explained below).

The preferred solution is to minimize use of <kw>from</kw> when you can assert all your facts into working memory directly. In the example above, both the Person and Address instance can be asserted into working memory. In this case, because the graph is fairly simple, an even easier solution is to modify your rules as follows:

rule "Assign people in North Carolina (NC) to sales region 1"
ruleflow-group "test"
lock-on-active true
when
    $p : Person(address.state == "NC" )  
then
    modify ($p) {} #Assign person to sales region 1 in a modify block
end

rule "Apply a discount to people in the city of Raleigh"
ruleflow-group "test"
lock-on-active true
when
    $p : Person(address.city == "Raleigh" )  
then
    modify ($p) {} #Apply discount to person in a modify block
end

Now, you will find that both rules fire as expected. However, it is not always possible to access nested facts as above. Consider an example where a Person holds one or more Addresses and you wish to use an existential quantifier to match people with at least one address that meets certain conditions. In this case, you would have to resort to the use of <kw>from</kw> to reason over the collection.

There are several ways to use <kw>from</kw> to achieve this and not all of them exhibit an issue with the use of <kw>lock-on-active</kw>. For example, the following use of <kw>from</kw> causes both rules to fire as expected:

rule "Assign people in North Carolina (NC) to sales region 1"
ruleflow-group "test"
lock-on-active true
when
    $p : Person($addresses : addresses)
    exists (Address(state == "NC") from $addresses)  
then
    modify ($p) {} #Assign person to sales region 1 in a modify block
end

rule "Apply a discount to people in the city of Raleigh"
ruleflow-group "test"
lock-on-active true
when
    $p : Person($addresses : addresses)
    exists (Address(city == "Raleigh") from $addresses)  
then
    modify ($p) {} #Apply discount to person in a modify block
end

However, the following slightly different approach does exhibit the problem:

rule "Assign people in North Carolina (NC) to sales region 1"
ruleflow-group "test"
lock-on-active true
when
    $assessment : Assessment()
    $p : Person()
    $addresses : List() from $p.addresses
    exists (Address( state == "NC") from $addresses) 
then
    modify ($assessment) {} #Modify assessment in a modify block
end

rule "Apply a discount to people in the city of Raleigh"
ruleflow-group "test"
lock-on-active true
when
    $assessment : Assessment()
    $p : Person()
    $addresses : List() from $p.addresses 
    exists (Address( city == "Raleigh") from $addresses)
then
    modify ($assessment) {} #Modify assessment in a modify block
end

In the above example, the $addresses variable is returned from the use of <kw>from</kw>. The example also introduces a new object, assessment, to highlight one possible solution in this case. If the $assessment variable assigned in the condition (LHS) is moved to the last condition in each rule, both rules fire as expected.

Though the above examples demonstrate how to combine the use of <kw>from</kw> with <kw>lock-on-active</kw> where no loss of rule activations occurs, they carry the drawback of placing a dependency on the order of conditions on the LHS. In addition, the solutions present greater complexity for the rule author in terms of keeping track of which conditions may create issues.

A better alternative is to assert more facts into working memory. In this case, a person's addresses may be asserted into working memory and the use of <kw>from</kw> would not be necessary.

There are cases, however, where asserting all data into working memory is not practical and we need to find other solutions. Another option is to reevaluate the need for <kw>lock-on-active</kw>. An alternative to <kw>lock-on-active</kw> is to directly manage how rules within the same rule-flow group activate one another by including conditions in each rule that prevent rules from activating each other recursively when working memory is modified. For example, in the case above where a discount is applied to citizens of Raleigh, a condition may be added to the rule that checks whether the discount has already been applied. If so, the rule does not activate.

Conditional Element <kw>collect</kw>

Figure 4.42. collect

collect

The Conditional Element <kw>collect</kw> allows rules to reason over a collection of objects obtained from the given source or from the working memory. In First Oder Logic terms this is the cardinality quantifier. A simple example:

import java.util.ArrayList

rule "Raise priority if system has more than 3 pending alarms"
when
    $system : System()
    $alarms : ArrayList( size >= 3 )
              from collect( Alarm( system == $system, status == 'pending' ) )
then
    # Raise priority, because system $system has
    # 3 or more alarms pending. The pending alarms
    # are $alarms.
end

In the above example, the rule will look for all pending alarms in the working memory for each given system and group them in ArrayLists. If 3 or more alarms are found for a given system, the rule will fire.

The result pattern of <kw>collect</kw> can be any concrete class that implements the java.util.Collection interface and provides a default no-arg public constructor. This means that you can use Java collections like ArrayList, LinkedList, HashSet, etc., or your own class, as long as it implements the java.util.Collection interface and provide a default no-arg public constructor.

Both source and result patterns can be constrained as any other pattern.

Variables bound before the <kw>collect</kw> CE are in the scope of both source and result patterns and therefore you can use them to constrain both your source and result patterns. But note that <kw>collect</kw> is a scope delimiter for bindings, so that any binding made inside of it is not available for use outside of it.

Collect accepts nested <kw>from</kw> CEs. The following example is a valid use of "collect":

import java.util.LinkedList;

rule "Send a message to all mothers"
when
    $town : Town( name == 'Paris' )
    $mothers : LinkedList() 
               from collect( Person( gender == 'F', children > 0 ) 
                             from $town.getPeople() 
                           )
then
    # send a message to all mothers
end

Conditional Element <kw>accumulate</kw>

Figure 4.43. accumulate

accumulate

The Conditional Element <kw>accumulate</kw> is a more flexible and powerful form of <kw>collect</kw>, the sense that it can be used to do what <kw>collect</kw> does and also achieve things that the CE <kw>collect</kw> is not capable of doing. Basically, what it does is that it allows a rule to iterate over a collection of objects, executing custom actions for each of the elements, and at the end it returns a result object.

The general syntax of the <kw>accumulate</kw> CE is:

<result pattern> <kw>from accumulate(</kw> <source pattern><kw>,</kw>
                                  <kw>init(</kw> <init code> <kw>),</kw>
                                  <kw>action(</kw> <action code> <kw>),</kw>
                                  <kw>reverse(</kw> <reverse code> <kw>),</kw>
                                  <kw>result(</kw> <result expression> <kw>) )</kw>

The meaning of each of the elements is the following:

  • <source pattern>: the source pattern is a regular pattern that the engine will try to match against each of the source objects.

  • <init code>: this is a semantic block of code in the selected dialect that will be executed once for each tuple, before iterating over the source objects.

  • <action code>: this is a semantic block of code in the selected dialect that will be executed for each of the source objects.

  • <reverse code>: this is an optional semantic block of code in the selected dialect that if present will be executed for each source object that no longer matches the source pattern. The objective of this code block is to undo any calculation done in the <action code> block, so that the engine can do decremental calculation when a source object is modified or retracted, hugely improving performance of these operations.

  • <result expression>: this is a semantic expression in the selected dialect that is executed after all source objects are iterated.

  • <result pattern>: this is a regular pattern that the engine tries to match against the object returned from the <result expression>. If it matches, the <kw>accumulate</kw> conditional element evaluates to true and the engine proceeds with the evaluation of the next CE in the rule. If it does not matches, the <kw>accumulate</kw> CE evaluates to false and the engine stops evaluating CEs for that rule.

It is easier to understand if we look at an example:

rule "Apply 10% discount to orders over US$ 100,00"
when
    $order : Order()
    $total : Number( doubleValue > 100 ) 
             from accumulate( OrderItem( order == $order, $value : value ),
                              init( double total = 0; ),
                              action( total += $value; ),
                              reverse( total -= $value; ),
                              result( total ) )
then
    # apply discount to $order
end

In the above example, for each Order in the Working Memory, the engine will execute the init code initializing the total variable to zero. Then it will iterate over all OrderItem objects for that order, executing the action for each one (in the example, it will sum the value of all items into the total variable). After iterating over all OrderItem objects, it will return the value corresponding to the result expression (in the above example, the value of variable total). Finally, the engine will try to match the result with the Number pattern, and if the double value is greater than 100, the rule will fire.

The example used Java as the semantic dialect, and as such, note that the usage of the semicolon as statement delimiter is mandatory in the init, action and reverse code blocks. The result is an expression and, as such, it does not admit ';'. If the user uses any other dialect, he must comply to that dialect's specific syntax.

As mentioned before, the reverse code is optional, but it is strongly recommended that the user writes it in order to benefit from the improved performance on update and retract.

The <kw>accumulate</kw> CE can be used to execute any action on source objects. The following example instantiates and populates a custom object:

rule "Accumulate using custom objects"
when
    $person   : Person( $likes : likes )
    $cheesery : Cheesery( totalAmount > 100 )
                from accumulate( $cheese : Cheese( type == $likes ),
                                 init( Cheesery cheesery = new Cheesery(); ),
                                 action( cheesery.addCheese( $cheese ); ),
                                 reverse( cheesery.removeCheese( $cheese ); ),
                                 result( cheesery ) );
then
    // do something
end
Accumulate Functions

The accumulate CE is a very powerful CE, but it gets real declarative and easy to use when using predefined functions that are known as Accumulate Functions. They work exactly like accumulate, but instead of explicitly writing custom code in every accumulate CE, the user can use predefined code for common operations.

For instance, the rule to apply discount on orders written in the previous section, could be written in the following way, using Accumulate Functions:

rule "Apply 10% discount to orders over US$ 100,00"
when
    $order : Order()
    $total : Number( doubleValue > 100 ) 
             from accumulate( OrderItem( order == $order, $value : value ),
                              sum( $value ) )
then
    # apply discount to $order
end

In the above example, sum is an Accumulate Function and will sum the $value of all OrderItems and return the result.

Drools ships with the following built-in accumulate functions:

  • average

  • min

  • max

  • count

  • sum

These common functions accept any expression as input. For instance, if someone wants to calculate the average profit on all items of an order, a rule could be written using the average function:

rule "Average profit"
when
    $order : Order()
    $profit : Number() 
              from accumulate( OrderItem( order == $order, $cost : cost, $price : price )
                               average( 1 - $cost / $price ) )
then
    # average profit for $order is $profit
end

Accumulate Functions are all pluggable. That means that if needed, custom, domain specific functions can easily be added to the engine and rules can start to use them without any restrictions. To implement a new Accumulate Functions all one needs to do is to create a Java class that implements the org.drools.base.acumulators.AccumulateFunction interface and add a line to the configuration file or set a system property to let the engine know about the new function. As an example of an Accumulate Function implementation, the following is the implementation of the <kw>average</kw> function:

/*
 * Copyright 2007 JBoss Inc
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Created on Jun 21, 2007
 */
package org.drools.base.accumulators;


/**
 * An implementation of an accumulator capable of calculating average values
 * 
 * @author etirelli
 *
 */
public class AverageAccumulateFunction implements AccumulateFunction {

    protected static class AverageData {
        public int    count = 0;
        public double total = 0;
    }

    /* (non-Javadoc)
     * @see org.drools.base.accumulators.AccumulateFunction#createContext()
     */
    public Object createContext() {
        return new AverageData();
    }

    /* (non-Javadoc)
     * @see org.drools.base.accumulators.AccumulateFunction#init(java.lang.Object)
     */
    public void init(Object context) throws Exception {
        AverageData data = (AverageData) context;
        data.count = 0;
        data.total = 0;
    }

    /* (non-Javadoc)
     * @see org.drools.base.accumulators.AccumulateFunction#accumulate(java.lang.Object,
     * java.lang.Object)
     */
    public void accumulate(Object context,
                           Object value) {
        AverageData data = (AverageData) context;
        data.count++;
        data.total += ((Number) value).doubleValue();
    }

    /* (non-Javadoc)
     * @see org.drools.base.accumulators.AccumulateFunction#reverse(java.lang.Object,
     * java.lang.Object)
     */
    public void reverse(Object context,
                        Object value) throws Exception {
        AverageData data = (AverageData) context;
        data.count--;
        data.total -= ((Number) value).doubleValue();
    }

    /* (non-Javadoc)
     * @see org.drools.base.accumulators.AccumulateFunction#getResult(java.lang.Object)
     */
    public Object getResult(Object context) throws Exception {
        AverageData data = (AverageData) context;
        return new Double( data.count == 0 ? 0 : data.total / data.count );
    }

    /* (non-Javadoc)
     * @see org.drools.base.accumulators.AccumulateFunction#supportsReverse()
     */
    public boolean supportsReverse() {
        return true;
    }

}

The code for the function is very simple, as we could expect, as all the "dirty" integration work is done by the engine. Finally, to plug the function into the engine, we added it to the configuration file:

drools.accumulate.function.average =
   org.drools.base.accumulators.AverageAccumulateFunction

Here, "drools.accumulate.function." is a prefix that must always be used, "average" is how the function will be used in the rule file, and "org.drools.base.accumulators.AverageAccumulateFunction" is the fully qualified name of the class that implements the function behavior.

The Right Hand Side (then)

Usage

The Right Hand Side (RHS) is a common name for the consequence or action part of the rule; this part should contain a list of actions to be executed. It is bad practice to use imperative or conditional code in the RHS of a rule; as a rule should be atomic in nature - "when this, then do this", not "when this, maybe do this". The RHS part of a rule should also be kept small, thus keeping it declarative and readable. If you find you need imperative and/or conditional code in the RHS, then maybe you should be breaking that rule down into multiple rules. The main purpose of the RHS is to insert, retractor modify working memory data. To assist with that there are a few convenience methods you can use to modify working memory; without having to first reference a working memory instance.

<kw>update(</kw>object, handle<kw>);</kw> will tell the engine that an object has changed (one that has been bound to something on the LHS) and rules may need to be reconsidered.

<kw>update(</kw>object<kw>);</kw> can also be used; here the Knowledge Helper will look up the facthandle for you, via an identity check, for the passed object. (Note that if you provide Property Change Listeners to your Java beans that you are inserting into the engine, you can avoid the need to call update() when the object changes.)

<kw>insert(new</kw> Something<kw>());</kw> will place a new object of your creation into the Working Memory.

<kw>insertLogical(new</kw> Something<kw>());</kw> is similar to insert, but the object will be automatically retracted when there are no more facts to support the truth of the currently firing rule.

<kw>retract(</kw>handle<kw>);</kw> removes an object from Working Memory.

These convenience methods are basically macros that provide short cuts to the KnowledgeHelper instance that lets you access your Working Memory from rules files. The predefined variable drools of type KnowledgeHelper lets you call several other useful methods. (Refer to the KnowledgeHelper interface documentation for more advanced operations).

  • The call drools.halt() terminates rule execution immediately. This is required for returning control to the point whence the current session was put to work with fireUntilHalt().

  • Methods insert(Object o), update(Object o) and retract(Object o) can be called on drools as well, but due to their frequent use they can be called without the object reference.

  • drools.getWorkingMemory() returns the WorkingMemory object.

  • drools.setFocus( String s) sets the focus to the specified agenda group.

  • drools.getRule().getName(), called from a rule's RHS, returns the name of the rule.

  • drools.getTuple() returns the Tuple that matches the currently executing rule, and drools.getActivation() delivers the corresponding Activation. (These calls are useful for logging and debugging purposes.)

The full Knowlege Runtime API is exposed through another predefined variable, kcontext, of type KnowledgeContext. Its method getKnowledgeRuntime() delivers an object of type KnowledgeRuntime, which, in turn, provides access to a wealth of methods, many of which are quite useful for coding RHS logic.

  • The call kcontext.getKnowledgeRuntime().halt() terminates rule execution immediately.

  • The accessor getAgenda() returns a reference to this session's Agenda, which in turn provides access to the various rule groups: activation groups, agenda groups, and rule flow groups. A fairly common paradigm is the activation of some agenda group, which could be done with the lengthy call:

    // give focus to the agenda group CleanUp
    kcontext.getKnowledgeRuntime().getAgenda().getAgendaGroup( "CleanUp" ).setFocus();

    (You can achieve the same using drools.setFocus( "CleanUp" ).)

  • To run a query, you call getQueryResults(String query), whereupon you may process the results, as explained in section “Query”.

  • A set of methods dealing with event management lets you, among other things, add and remove event listeners for the Working Memory and the Agenda.

  • MethodgetKnowledgeBase() returns the KnowledgeBase object, the backbone of all the Knowledge in your system, and the originator of the current session.

  • You can manage globals with setGlobal(...), getGlobal(...) and getGlobals().

  • Method getEnvironment() returns the runtime's Environment which works much like what you know as your operating system's environment.

The <kw>modify</kw> Statement

This language extension provides a structured approach to fact updates. It combines the update operation with a number of setter calls to change the object's fields. This is the syntax schema for the <kw>modify</kw> statement:

<kw>modify ( </kw><fact-expression><kw> ) {</kw>
    <expression> [ <kw>,</kw> <expression> ]*
<kw>}</kw>

The parenthesized <fact-expression> must yield a fact object reference. The expression list in the block should consist of setter calls for the given object, to be written without the usual object reference, which is automatically prepended by the compiler.

The example illustrates a simple fact modification.

Example 4.64. A modify statement

rule "modify stilton"
when
    $stilton : Cheese(type == "stilton")
then
    modify( $stilton ){
        setPrice( 20 ),
        setAge( "overripe" )
    }
end

A Note on Auto-boxing and Primitive Types

Drools attempts to preserve numbers in their primitive or object wrapper form, so a variable bound to an int primitive when used in a code block or expression will no longer need manual unboxing; unlike Drools 3.0 where all primitives were autoboxed, requiring manual unboxing. A variable bound to an object wrapper will remain as an object; the existing JDK 1.5 and JDK 5 rules to handle auto-boxing and unboxing apply in this case. When evaluating field constraints, the system attempts to coerce one of the values into a comparable format; so a primitive is comparable to an object wrapper.