I'm using the following structure in my DrL file built using RuleDescrBuilder APIs provided with the org.drools.compiler.lang.api.
I trigger an after match event to keep track of number of hits for each rule in the backend. However, when the input(Predicate) matches all the given conditions in Rule1, it's triggering multiple afterMatchFiredEvents for one input.
import com.objects.Predicate
global com.Util policyUtil
dialect "java"
rule "Rule1:RuleId"
salience 2147483647
when
predicate := Predicate( )
(
eval( policyUtil.evaluate(condition1) ) or
eval( policyUtil.evaluate(condition2) ) or
eval( policyUtil.evaluate(condition3) )
then
...
end
rule "defaultRule:defaultRule"
salience 0
when
predicate := Predicate( )
then
predicate.setValue1("default1");
predicate.setValue2("Default2");
drools.halt();
end
This is the after match event trigger:
public void afterMatchFired(AfterMatchFiredEvent event) {
logger.info("Matching rule Name:: " + event.getMatch().getRule().getName());
updateHitCountForRule(event.getMatch().getRule().getName());
}
This is how ruledescbuilder is used.
RuleDescrBuilder rdb = pdb.newRule();
CEDescrBuilder<?, ?> cedb = rdb.lhs();
cedb = cedb.and();
for(each condition in rule)
cedb.eval().constraint(constraint).end();
Q: Why is the afterMatchFired triggering multiple times for the same rule with OR conditions? I'm assuming that eval is not being used correctly. If eval is not used correct, what is the right way to build this kind of rule set using the above approach?
That is because of the way Drools deals with or
operator between patterns. What's happening behind the scenes is that Drools creates 3 different rules from your original rule:
rule "Rule1:RuleId 1"
salience 2147483647
when
predicate := Predicate( )
eval( policyUtil.evaluate(condition1) )
then
...
end
rule "Rule1:RuleId 2"
salience 2147483647
when
predicate := Predicate( )
eval( policyUtil.evaluate(condition2) )
then
...
end
rule "Rule1:RuleId 3"
salience 2147483647
when
predicate := Predicate( )
eval( policyUtil.evaluate(condition3) )
then
...
end
As you can see, there is no short-circuit or
operator between patterns in Drools. If all of your eval
match, you will not only receive the AfterMatchFiredEvent
3 times, the action
part of your rule will also be executed 3 times.
One possible way to avoid this (even if it is a little hacky) is to use a fact as a flag to avoid the multiple execution of the action
part:
rule "Rule1:RuleId"
salience 2147483647
when
not RuleExecuted()
predicate := Predicate( )
(
eval( policyUtil.evaluate(condition1) ) or
eval( policyUtil.evaluate(condition2) ) or
eval( policyUtil.evaluate(condition3) )
)
then
...
insert(new RuleExecuted());
end
In this scenario, you will still receive 3 BeforeMatchFiredEvent
events in your listener, but only 1 AfterMatchFiredEvent
. You will also receive 2 MatchCancelledEvent
events too.
Hope it helps,