8.12. Elements and Variables
8.12.1. Property Access on Java Beans (POJOs)
getMyProperty() (or isMyProperty() for a primitive boolean) which takes no arguments and return something.
Introspector class to do this mapping, so it follows the standard Java bean specification.
Warning
8.12.2. POJO Example
Person( age == 50 ) // this is the same as: Person( getAge() == 50 )
- The age property
- The age property is written as
agein DRL instead of the gettergetAge() - Property accessors
- You can use property access (
age) instead of getters explicitly (getAge()) because of performance enhancements through field indexing.
8.12.3. Working with POJOs
Procedure 8.9. Task
- Observe the example below:
public int getAge() { Date now = DateUtil.now(); // Do NOT do this return DateUtil.differenceInYears(now, birthday); } - To solve this, insert a fact that wraps the current date into working memory and update that fact between
fireAllRulesas needed.
8.12.4. POJO Fallbacks
8.12.5. Fallback Example
Person( age == 50 ) // If Person.getAge() does not exists, this falls back to: Person( age() == 50 )
Person( address.houseNumber == 50 ) // this is the same as: Person( getAddress().getHouseNumber() == 50 )
Warning
houseNumber changes, any Person with that Address must be marked as updated.
8.12.6. Java Expressions
Table 8.6. Java Expressions
| Capability | Example |
|---|---|
|
You can use any Java expression that returns a
boolean as a constraint inside the parentheses of a pattern. Java expressions can be mixed with other expression enhancements, such as property access.
|
Person( age == 50 ) |
|
You can change the evaluation priority by using parentheses, as in any logic or mathematical expression.
|
Person( age > 100 && ( age % 10 == 0 ) ) |
|
You can reuse Java methods.
|
Person( Math.round( weight / ( height * height ) ) < 25.0 ) |
|
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.
|
Person( age == "10" ) // "10" is coerced to 10 |
Warning
Warning
Person( System.currentTimeMillis() % 1000 == 0 ) // Do NOT do this
Important
== and !=.
== operator has null-safe equals() semantics:
// Similar to: java.util.Objects.equals(person.getFirstName(), "John") // so (because "John" is not null) similar to: // "John".equals(person.getFirstName()) Person( firstName == "John" )
!= operator has null-safe !equals() semantics:
// Similar to: !java.util.Objects.equals(person.getFirstName(), "John") Person( firstName != "John" )
8.12.7. Comma-Separated Operators
,') is used to separate constraint groups. It has implicit and connective semantics.
8.12.8. Comma-Separated Operator Example
// Person is at least 50 and weighs at least 80 kg Person( age > 50, weight > 80 )
// Person is at least 50, weighs at least 80 kg and is taller than 2 meter. Person( age > 50, weight > 80, height > 2 )
Note
,) operator cannot be embedded in a composite constraint expression, such as parentheses.
8.12.9. Binding Variables
8.12.10. Binding Variable Examples
// 2 persons of the same age Person( $firstAge : age ) // binding Person( age == $firstAge ) // constraint expression
Note
// Not recommended Person( $age : age * 2 < 100 )
// Recommended (separates bindings and constraint expressions) Person( age * 2 < 100, $age : age )
8.12.11. Unification
8.12.12. Unification Example
Person( $age := age ) Person( $age := age)
8.12.13. Options and Operators in Red Hat JBoss BRMS
Table 8.7. Options and Operators in Red Hat JBoss BRMS
| Option | Description | Example |
|---|---|---|
|
Date literal
|
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 a restriction.
|
Cheese( bestBefore < "27-Oct-2009" ) |
| List and Map access |
You can directly access a
List value by index.
|
// Same as childList(0).getAge() == 18 Person( childList[0].age == 18 ) |
| Value key |
You can directly access a
Map value by key.
|
// Same as credentialMap.get("jsmith").isValid()
Person( credentialMap["jsmith"].valid )
|
|
Abbreviated combined relation condition
|
This 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.
|
// Simple abbreviated combined relation condition using a single && Person( age > 30 && < 40 ) // Complex abbreviated combined relation using groupings
Person( age ( (> 30 && < 40) ||
(> 20 && < 25) ) )
// Mixing abbreviated combined relation with constraint connectives Person( age > 30 && < 40 || location == "london" ) |
| Operators |
Operators can be used on properties with natural ordering. For example, for Date fields,
< means before, for String fields, it means alphabetically lower.
|
Person( firstName < $otherFirstName ) Person( birthDate < $otherBirthDate ) |
|
Operator matches
|
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 only applies on String properties. Using matches against a null value always evaluates to false.
|
Cheese( type matches "(Buffalo)?\\S*Mozarella" ) |
|
Operator not matches
|
The operator returns true if the String does not match the regular expression. The same rules apply as for the
matches operator. It only applies on String properties.
|
Cheese( type not matches "(Buffulo)?\\S*Mozarella" ) |
|
The operator contains
|
CheeseCounter( cheeses contains "stilton" ) // contains with a String literal CheeseCounter( cheeses contains $var ) // contains with a variable | |
|
The operator not contains
|
The operator
not contains is used to check whether a field that is a Collection or array does not contain the specified value. It only applies on Collection properties.
|
CheeseCounter( cheeses not contains "cheddar" ) // not contains with a String literal CheeseCounter( cheeses not contains $var ) // not contains with a variable |
|
The operator memberOf
|
The operator
memberOf is used to check whether a field is a member of a collection or array; that collection must be a variable.
|
CheeseCounter( cheese memberOf $matureCheeses ) |
|
The operator not memberOf
|
The operator
not memberOf is used to check whether a field is not a member of a collection or array. That collection must be a variable.
|
CheeseCounter( cheese not memberOf $matureCheeses ) |
|
The operator soundslike
|
This operator is similar to
matches, but it checks whether a word has almost the same sound (using English pronunciation) as the given value.
|
// match cheese "fubar" or "foobar" Cheese( name soundslike 'foobar' ) |
|
The operator str
|
The operator
str is used to check whether a field that is a String starts with or ends with a certain value. It can also be used to check the length of the String.
|
Message( routingValue str[startsWith] "R1" ) Message( routingValue str[endsWith] "R2" ) Message( routingValue str[length] 17 ) |
|
Compound Value Restriction
|
Compound value restriction is used where there is more than one possible value to match. Currently only the
in and not in 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 ==.
|
Person( $cheese : favouriteCheese ) Cheese( type in ( "stilton", "cheddar", $cheese ) ) |
|
Inline Eval Operator (deprecated)
|
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 eval's.
|
Person( girlAge : age, sex = "F" ) Person( eval( age == girlAge + 2 ), sex = 'M' ) // eval() is actually obsolete in this example |
8.12.14. Operator Precedence
Table 8.8. Operator precedence
| Operator type | Operators | Notes |
|---|---|---|
| (nested) property access | . | Not normal Java semantics |
| List/Map access | [ ] | Not normal Java semantics |
| constraint binding | : | Not normal Java semantics |
| multiplicative | * /% | |
| additive | + - | |
| shift | << >>>>> | |
| relational | < ><= >=instanceof | |
| equality | == != | Does not use normal Java (not) same semantics: uses (not) equals semantics instead. |
| non-short circuiting AND | & | |
| non-short circuiting exclusive OR | ^ | |
| non-short circuiting inclusive OR | | | |
| logical AND | && | |
| logical OR | || | |
| ternary | ? : | |
| Comma separated AND | , | Not normal Java semantics |
8.12.15. Fine Grained Property Change Listeners
Note
8.12.16. Fine Grained Property Change Listener Example
- DRL example
declare Person @propertyReactive firstName : String lastName : String end- Java class example
@PropertyReactive public static class Person { private String firstName; private String lastName; }
8.12.17. Working with Fine Grained Property Change Listeners
8.12.18. Using Patterns with @watch
! and to make the pattern to listen for all or none of the properties of the type used in the pattern respectively with the wildcards * and !*.
8.12.19. @watch Example
// listens for changes on both firstName (inferred) and lastName
Person( firstName == $expectedFirstName ) @watch( lastName )
// listens for all the properties of the Person bean
Person( firstName == $expectedFirstName ) @watch( * )
// listens for changes on lastName and explicitly exclude firstName
Person( firstName == $expectedFirstName ) @watch( lastName, !firstName )
// listens for changes on all the properties except the age one
Person( firstName == $expectedFirstName ) @watch( *, !age )Note
8.12.20. Using @PropertySpecificOption
on option of the KnowledgeBuilderConfiguration. This new PropertySpecificOption can have one of the following 3 values:
- DISABLED => the feature is turned off and all the other related annotations are just ignored
- ALLOWED => this is the default behavior: types are not property reactive unless they are not annotated with @PropertySpecific
- ALWAYS => all types are property reactive by default8.12.21. Basic Conditional Elements
Table 8.9. Basic Conditional Elements
| Name | Description | Example | Additional options |
|---|---|---|---|
|
and
|
The Conditional Element
and is used to group other Conditional Elements into a logical conjunction. JBoss BRMS supports both prefix and and infix and. It supports explicit grouping with parentheses. You can also use traditional infix and prefix and.
|
//infixAnd Cheese( cheeseType : type ) and Person( favouriteCheese == cheeseType ) //infixAnd with grouping
( Cheese( cheeseType : type ) and
( Person( favouriteCheese == cheeseType ) or
Person( favouriteCheese == cheeseType ) )
|
Prefix
and is also supported:
(and Cheese( cheeseType : type )
Person( favouriteCheese == cheeseType ) )
The root element of the LHS is an implicit prefix
and and doesn't need to be specified:
when
Cheese( cheeseType : type )
Person( favouriteCheese == cheeseType )
then
...
|
|
or
|
This is a shortcut for generating two or more similar rules. JBoss BRMS supports both prefix
or and infix or. You can use traditional infix, prefix and explicit grouping parentheses.
|
//infixOr Cheese( cheeseType : type ) or Person( favouriteCheese == cheeseType ) //infixOr with grouping
( Cheese( cheeseType : type ) or
( Person( favouriteCheese == cheeseType ) and
Person( favouriteCheese == cheeseType ) )
(or Person( sex == "f", age > 60 )
Person( sex == "m", age > 65 )
|
Allows for optional pattern binding. Each pattern must be bound separately, using eponymous variables:
pensioner : ( Person( sex == "f", age > 60 ) or Person( sex == "m", age > 65 ) ) (or pensioner : Person( sex == "f", age > 60 )
pensioner : Person( sex == "m", age > 65 ) )
|
|
not
|
This checks to ensure an object specified as absent is not included in the Working Memory. It may be followed by parentheses around the condition elements it applies to. (In a single pattern you can omit the parentheses.)
|
// Brackets are optional:
not Bus(color == "red")
// Brackets are optional:
not ( Bus(color == "red", number == 42) )
// "not" with nested infix
| |
| exists |
This checks the working memory to see if a specified item exists. The keyword
exists must be followed by parentheses around the CEs that it applies to. (In a single pattern you can omit the parentheses.)
|
exists Bus(color == "red")
// brackets are optional:
exists ( Bus(color == "red", number == 42) )
// "exists" with nested infix
| |
Note
or is different from the connective || for constraints and restrictions in field constraints. The engine cannot interpret the Conditional Element or. Instead, a rule with or is rewritten as a number of subrules. This process ultimately results in a rule that has a single or 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.
8.12.22. The Conditional Element Forall
Forall can be nested inside other CEs. For instance, forall can be used inside a not CE. Only single patterns have optional parentheses, so with a nested forall parentheses must be used.
8.12.23. Forall Examples
- Evaluating to true
rule "All English buses are red" when forall( $bus : Bus( type == 'english') Bus( this == $bus, color = 'red' ) ) then // all English buses are red end- Single pattern forall
rule "All Buses are Red" when forall( Bus( color == 'red' ) ) then // all Bus facts are red end- 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- Nested forall
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
8.12.24. The Conditional Element From
from 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.
Important
from with lock-on-active rule attribute can result in rules not being fired.
- Avoid the use of
fromwhen 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
lock-on-activewhen you can explicitly manage how rules within the same rule-flow group place activations on one another.
8.12.25. From Examples
- Reasoning and binding on patterns
rule "validate zipcode" when Person( $personAddress : address ) Address( zipcode == "23920W") from $personAddress then // zip code is ok end- Using a graph notation
rule "validate zipcode" when $p : Person( ) $a : Address( zipcode == "23920W") from $p.address then // zip code is ok end- Iterating over all objects
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- Use with lock-on-active
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
8.12.26. The Conditional Element Collect
collect 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.
collect can be any concrete class that implements the java.util.Collection interface and provides a default no-arg public constructor. You can use Java collections like ArrayList, LinkedList and HashSet or your own class, as long as it implements the java.util.Collection interface and provide a default no-arg public constructor.
collect 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. Any binding made inside collect is not available for use outside of it.
8.12.27. The Conditional Element Accumulate
accumulate is a more flexible and powerful form of collect, in the sense that it can be used to do what collect does and also achieve results that the CE collect is not capable of doing. It allows a rule to iterate over a collection of objects, executing custom actions for each of the elements. At the end it returns a result object.
8.12.28. Syntax for the Conditional Element Accumulate
- Top level accumulate syntax
accumulate(<source pattern>;<functions>[;<constraints>] )- Syntax example
rule "Raise alarm" when $s : Sensor() accumulate( Reading( sensor == $s, $temp : temperature ); $min : min( $temp ), $max : max( $temp ), $avg : average( $temp ); $min < 20, $avg > 70 ) then // raise the alarm endIn the above example, min, max and average are Accumulate Functions and will calculate the minimum, maximum and average temperature values over all the readings for each sensor.
8.12.29. Functions of the Conditional Element Accumulate
- average
- min
- max
- count
- sum
- collectList
- collectSet
rule "Average profit"
when
$order : Order()
accumulate( OrderItem( order == $order, $cost : cost, $price : price );
$avgProfit : average( 1 - $cost / $price ) )
then
// average profit for $order is $avgProfit
end8.12.30. The Conditional Element accumulate and Pluggability
org.drools.runtime.rule.TypedAccumulateFunction interface and add a line to the configuration file or set a system property to let the engine know about the new function.
8.12.31. The Conditional Element accumulate and Pluggability Example
average function:
/**
* An implementation of an accumulator capable of calculating average values
*/
public class AverageAccumulateFunction implements org.drools.runtime.rule.TypedAccumulateFunction {
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
}
public void writeExternal(ObjectOutput out) throws IOException {
}
public static class AverageData implements Externalizable {
public int count = 0;
public double total = 0;
public AverageData() {}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
count = in.readInt();
total = in.readDouble();
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(count);
out.writeDouble(total);
}
}
/* (non-Javadoc)
* @see org.drools.base.accumulators.AccumulateFunction#createContext()
*/
public Serializable createContext() {
return new AverageData();
}
/* (non-Javadoc)
* @see org.drools.base.accumulators.AccumulateFunction#init(java.lang.Object)
*/
public void init(Serializable 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(Serializable 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(Serializable 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(Serializable 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;
}
/**
* {@inheritDoc}
*/
public Class< ? > getResultType() {
return Number.class;
}
}
8.12.32. Code for the Conditional Element Accumulate's Functions
- Code for plugging in functions (to be entered into the config file)
jbossrules.accumulate.function.average = org.jbossrules.base.accumulators.AverageAccumulateFunction
- Alternate Syntax: single function with return type
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
8.12.33. Accumulate with Inline Custom Code
Warning
accumulate CE with inline custom code is:
<result pattern>from accumulate(<source pattern>,init(<init code>),action(<action code>),reverse(<reverse code>),result(<result expression>) )
- <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
accumulateconditional element evaluates to true and the engine proceeds with the evaluation of the next CE in the rule. If it does not matches, theaccumulateCE evaluates to false and the engine stops evaluating CEs for that rule.
8.12.34. Accumulate with Inline Custom Code Examples
- Inline custom code
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 endIn the above example, for eachOrderin the Working Memory, the engine will execute the init code initializing the total variable to zero. Then it will iterate over allOrderItemobjects 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 allOrderItemobjects, it will return the value corresponding to the result expression (in the above example, the value of variabletotal). Finally, the engine will try to match the result with theNumberpattern, and if the double value is greater than 100, the rule will fire.- Instantiating and populating 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
8.12.35. Conditional Element Eval
eval 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 eval 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.
8.12.36. Conditional Element Eval Examples
eval looks like in use:
p1 : Parameter() p2 : Parameter() eval( p1.getList().containsKey( p2.getItem() ) )
p1 : Parameter() p2 : Parameter() // call function isValid in the LHS eval( isValid( p1, p2 ) )
8.12.37. The Right Hand Side
Note
8.12.38. RHS Convenience Methods
Table 8.10. RHS Convenience Methods
| Name | Description |
|---|---|
update(object, handle);
|
Tells the engine that an object has changed (one that has been bound to something on the LHS) and rules that need to be reconsidered.
|
update(object);
|
Using
update(), the Knowledge Helper will look up the facthandle via an identity check for the passed object. (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.). After a fact's field values have changed you must call update before changing another fact, or you will cause problems with the indexing within the rule engine. The modify keyword avoids this problem.
|
insert(newobject());
|
Places a new object of your creation into the Working Memory.
|
insertLogical(newobject());
|
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.
|
retract(handle);
|
Removes an object from Working Memory.
|
8.12.39. Convenience Methods using the Drools Variable
- 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 withfireUntilHalt(). - Methods
insert(Object o),update(Object o)andretract(Object o)can be called ondroolsas well, but due to their frequent use they can be called without the object reference. drools.getWorkingMemory()returns theWorkingMemoryobject.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, anddrools.getActivation()delivers the corresponding Activation. (These calls are useful for logging and debugging purposes.)
8.12.40. Convenience Methods using the Kcontext Variable
- The call
kcontext.getKieRuntime().halt()terminates rule execution immediately. - The accessor
getAgenda()returns a reference to the session'sAgenda, 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.getKieRuntime().getAgenda().getAgendaGroup( "CleanUp" ).setFocus();
(You can achieve the same usingdrools.setFocus( "CleanUp" ).) - To run a query, you call
getQueryResults(String query), whereupon you may process the results. - A set of methods dealing with event management lets you add and remove event listeners for the Working Memory and the Agenda.
- Method
getKieBase()returns theKieBaseobject, the backbone of all the Knowledge in your system, and the originator of the current session. - You can manage globals with
setGlobal(...),getGlobal(...)andgetGlobals(). - Method
getEnvironment()returns the runtime'sEnvironment.
8.12.41. The Modify Statement
Table 8.11. The Modify Statement
| Name | Description | Syntax | Example |
|---|---|---|---|
| modify |
This provides a structured approach to fact updates. It combines the update operation with a number of setter calls to change the object's fields.
|
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.
| rule "modify stilton"
when
$stilton : Cheese(type == "stilton")
then
modify( $stilton ){
setPrice( 20 ),
setAge( "overripe" )
}
end
|
8.12.42. Query Examples
Note
ksession.getQueryResults("name"), where "name" is the query's name. This returns a list of query results, which allow you to retrieve the objects that matched the query.
- Query for people over the age of 30
query "people over the age of 30" person : Person( age > 30 ) end- Query for people over the age of X, and who live in Y
query "people over the age of x" (int x, String y) person : Person( age > x, location == y ) end
8.12.43. QueryResults Example
QueryResults results = ksession.getQueryResults( "people over the age of 30" );
System.out.println( "we have " + results.size() + " people over the age of 30" );
System.out.println( "These people are are over 30:" );
for ( QueryResultsRow row : results ) {
Person person = ( Person ) row.get( "person" );
System.out.println( person.getName() + "\n" );
}8.12.44. Queries Calling Other Queries
Note
8.12.45. Queries Calling Other Queries Example
- Query calling another query
declare Location thing : String location : String end query isContainedIn( String x, String y ) Location(x, y;) or ( Location(z, y;) and ?isContainedIn(x, z;) ) end- Using live queries to reactively receive changes over time from query results
query isContainedIn( String x, String y ) Location(x, y;) or ( Location(z, y;) and isContainedIn(x, z;) ) end rule look when Person( $l : likes ) isContainedIn( $l, 'office'; ) then insertLogical( $l 'is in the office' ); end
8.12.46. Unification for Derivation Queries
org.drools.runtime.rule.Variable.v. (You must use v and not an alternative instance of Variable.) These are referred to as out arguments.
Note

Where did the comment section go?
Red Hat's documentation publication system recently went through an upgrade to enable speedier, more mobile-friendly content. We decided to re-evaluate our commenting platform to ensure that it meets your expectations and serves as an optimal feedback mechanism. During this redesign, we invite your input on providing feedback on Red Hat documentation via the discussion platform.