Samrat
Samrat

Reputation: 1389

drools: rules executed more than one time

I am trying hands on at the Drools rule engine , I am quite a beginner.

I have the following rules in place in a single rule file:

rule "A stand alone rule" 
salience 2
no-loop
when
    $account : Account()
    Account($account.balance>100)
then
    System.out.println("balance>100");
    System.out.println($account.getBalance());
    System.out.println($account.getCustomer().getName());    
end

rule "A second Rule"
salience 1
no-loop
when
    $account : Account()
    Account($account.balance<100)
then
    System.out.println("balance<100");
    System.out.println($account.getBalance());
    System.out.println($account.getCustomer().getName());
end 

In the StatefulKnowledgeSession I am passing TWO accounts , one with balance 15000 another with balance 15 ,

Account account=new Account(7l,15000l);
        Account account1=new Account(5l,15l);

        Customer customer = new Customer("Samrat", 28, "Sector51", account);
        Customer customer1 = new Customer("Alexi", 28, "Sector50", account1);
        account.setCustomer(customer);
        account1.setCustomer(customer1);
        session.insert(account);
        session.insert(account1);

        session.fireAllRules();

According to me the expected result should be that each rule should be fired only once and the corresponding object should be printed.

But the result I am getting is :

balance>100
15000
Samrat
balance>100
15000
Samrat
balance<100
15
Alexi
balance<100
15
Alexi

I am not able to understand why each rule is running twice ????

Upvotes: 6

Views: 6997

Answers (2)

Shridhar
Shridhar

Reputation: 69

I was comparing two objects of same class and was wondering why the rules are getting fired multiple times. However after reading explanation from Esteban Aliverti I thought that my rule might also be creating Cartesian product.

So I replaced "and" from the rules to "," and it worked perfectly. However, I could not understand why "and" was creating Cartesian product.

Earlier my rule was --

rule "Rule 1"
    when
       $first : RuleC() and
       second : RuleC(this != $first) and
           RuleC($first.outcome < outcome) and 
           RuleC($first.target == target)
    then
        System.out.println("The rule has been fired ");
end

Later my rule became (And it is working absolutely fine) --

rule "Rule 1"
    when
       $first : RuleC() and
       second : RuleC(this != $first, $first.outcome < outcome, $first.target == target)
    then
        System.out.println("The rule has been fired ");
end

Upvotes: 2

Esteban Aliverti
Esteban Aliverti

Reputation: 6322

Using multiple patterns (and not specifying any relation between them) will create a full Cartesian product (just like a select on multiple tables without a join clause). So, the rule:

rule A
when
    Account()
    Account()
then
    ...
end

will be activated N^2 times for N objects of type Account. One solution could be to use the magic field 'this' to specify that the second account is the same as the first one:

rule A
when
    $a: Account()
    Account(this == $a)
then
    ...
end

But, going back to your example, I think you don't even need to use 2 different patterns. You could rewrite your rules as following:

rule "A stand alone rule" 
salience 2
no-loop
when
    $account: Account(balance>100)
then
    System.out.println("balance>100");
    System.out.println($account.getBalance());
    System.out.println($account.getCustomer().getName());    
end

rule "A second Rule"
salience 1
no-loop
when
    $account: Account(balance<100)
then
    System.out.println("balance<100");
    System.out.println($account.getBalance());
    System.out.println($account.getCustomer().getName());
end

Hope it helps,

Upvotes: 7

Related Questions