5.5.2. Left Hand Side (when) Conditional Elements

5.5.2. 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 the rule is always true, and will be activated with a new Working Memory session is created.

Left Hand Side
Figure 5.10. Left Hand Side

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

Is internally re-written as:

rule "no CEs"
when
    eval( true )
then
    <action>*
end
Example 5.5. Rule Syntax Overview Example

Conditional elements work on one or more Patterns (which are described bellow). The most common one is "and" which is implicit when you have multiple Patterns in the LHS of a rule that are not connected in anyway. Note that an 'and' cannot have a leading declaration binding like 'or' - this is obvious when you think about it. A declaration can only reference a single Fact, when the 'and' is satisfied it matches more than one fact - which fact would the declaration bind to?

5.5.2.1. 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 rail road diagrams and examples.

Pattern Entity Relationship Diagram
Figure 5.11. 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 rail road diagram below shows the syntax for this.

Pattern
Figure 5.12. Pattern

At the simplest, with no constraints, it simply matches against a type, in the following case the type is "Cheese". This means the pattern will match against all Cheese objects in the Working Memory.

Cheese( )
Example 5.6. Pattern

To be able to refer to the matched object use a pattern binding variable such as '$c'. While this example variable is prefixed with a $ symbol, it is optional, but can be useful in complex rules as it helps to more easily differentiation between variables and fields.

$c : Cheese( )
Example 5.7. Pattern

Inside of the Pattern parenthesis is where all the action happens. A constraint can be either a Field Constraint, Inline Eval (called a predicate in 3.0) or a Constraint Group. Constraints can be separated by the following symbols ',', '&&' or '||'.

Constraints
Figure 5.13. Constraints

Constraint
Figure 5.14. Constraint

Group Constraint
Figure 5.15. Group Constraint

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

# Cheese type is stilton and price < 10 and age is mature.
Cheese( type == "stilton", price < 10, age == "mature" )
Example 5.8. Constraint Group connective ','

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

  • group 1: type is stilton -> type == "stilton"

  • group 2: price is less than 10 -> price < 10

  • group 3: age is mature -> age == "mature"

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

Cheese( type == "stilton" && price < 10, age == "mature" ) // 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
Example 5.9. && and || Constraint Connectives

The above example has two constraint groups. The first has 2 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 parenthesis, as in any logic or mathematical expression. Example:

# Cheese type is stilton and ( price is less than 20 or age is mature ).
Cheese( type == "stilton" && ( price < 20 || age == "mature" ) )
Example 5.10. Using parenthesis to change evaluation priority

In the above example, the use of parenthesis makes the || connective be evaluated 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.

Cheese( ( type == "stilton", price < 10 ) || age == "mature" ) // invalid as ',' cannot be embedded in an expression
Cheese( ( type == "stilton" && price < 10 ) || age == "mature") // valid as '&&' can be embedded in an expression
Example 5.11. Not Equivalent connectives

5.5.2.1.1. Field Constraints

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

fieldConstraint
Figure 5.16. fieldConstraint

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

restriction
Figure 5.17. restriction

5.5.2.1.1.1. JavaBeans as facts

A field is an accessible method on the object. If your model objects follow the java bean pattern, then fields are exposed using "getXXX" or "isXXX" methods (these are methods that take no arguments, and return something). You can access fields either by using the bean-name convention (so "getType" can be accessed as "type") - we use the standard jdk Introspector class to do this mapping.

For example, referring to our Cheese class, the following : Cheese(type == ...) uses the getType() method on the a cheese instance. If a field name cannot be found it will resort to calling the name as a no argument method; "toString()" on the Object for instance can be used with Cheese(toString == ..) - you use the full name of the method with correct capitalization, but not brackets. Do please make sure that you are accessing methods that take no parameters, and are in-fact "accessors" (as in, they 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).

5.5.2.1.1.2. Values

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

literal
Figure 5.18. literal

qualifiedIdentifier
Figure 5.19. qualifiedIdentifier

variable
Figure 5.20. variable

returnValue
Figure 5.21. returnValue

You can do checks against fields that are or maybe null, using == and != as you would expect, and the literal "null" keyword, like: Cheese(type != null). If a field is null the evaluator will not throw an exception and will only return true if the value is a null check. Coercion is always attempted if the field and the value are of different types; exceptions will be thrown if bad coercions are attempted. i.e. if "ten" is provided as a string in a number evaluator, where as "10" would coerce to a numeric 10. Coercion is always in favor of the field type and not the value type.

5.5.2.1.1.3. Single Value Restriction
singleValueRestriction
Figure 5.22. singleValueRestriction

5.5.2.1.1.3.1. Operators
Operators
Figure 5.23. Operators

Valid operators are dependent on the field type. Generally they are self explanatory based on the type of data: for instance, for date fields, "<" means "before" and so on. "Matches" is only applicable to string fields, "contains" and "not contains" is only applicable to Collection type fields. These operators can be used with any value and coercion to the correct value for the evaluator and filed will be attempted, as mention in the "Values" section.