Drools + Hibernate: Need to simulate Left Outer Join with OR condition in rule

Brian English

Using the rule below, I am attempting to match an Account using a rule with an OR logical condition. In this situation, I have a table of Accounts and a table of Insurance records for those accounts. Not all accounts have insurance records. The Hibernate DAO objects exist and there is an association from Account to Insurance. I am not observing expected behavior from this rule. In this situation, I would expect accounts 1, 2, 3, and 4 to match this rule, as the rule should match any Account with status "Inactive" or any account with an Insurance CURRENT_IND value of 'N'. However, only accounts 2 and 4 match the rule. An account will match the rule only if it has an Insurance record. I want all Account records with status = 'Inactive', regardless of their Insurance record's existence.

I am currently testing this with Drools 5.6.10.FINAL and Hibernate 3.6.0.

Is it possible to create such a rule using Hibernate with Drools? What is causing the rule to filter on Insurance records' existence?

package com.app.Testing
import com.app.abilities.RuleBuilder.EvalObject;
import com.app.dao.Insurance;
import com.app.dao.Account;

rule "Null Test"
when
 $V1 : Account( )
 $V2 : Insurance( ) from $V1.getInsurance()
 $V3 : EvalObject(
    $V2.getCurrentInd == "N" || 
    $V1.getStatus == "Inactive"
 )
then
  reply.getReplyList().add("Null Test");
end

Example data:

Account:
ACCT_ID    STATUS
1          Inactive
2          Inactive
3          Inactive
4          Active
5          Active


Insurance:
ID    CURRENT_IND
2     N
4     N
5     Y
laune

Accessing a readily available field of some fact using "from" is completeley unnecessary - you can access via a bound variable or a getter. This is what causes the problem: a null value can't be "frommed" into a pattern and so any Account with insurance == null will block further evaluation.

Moreover, I find the use of a completely fact (EvalObject) for writing a boolean rather baroque. A simple eval should do nicely, even if you have to use the oh-so-terrible Java syntax.

Altogether, the following rule fires on all four of your Accounts:

rule "Better Test"
when
    $V1: Account( $sta: status, $ins: insurance )
    eval( $sta.equals( "Inactive" ) ||
          $ins != null && $ins.getCurrentInd().equals( "N" ) )
then
    System.out.println("Better Test " + $V1.getId() );
end

Logic is, of course, a many splendoured thing and it will follow the tug of the leash if applied nicely. Thus, you could also use a couple of rules:

rule "Third Test A"
when
    $V1: Account( $sta: status == "Inactive" )
then
    System.out.println("Third Test A " + $V1.getId() );
end
rule "Third Test B"
when
    $V1: Account( $sta: status == "Active" )
    Insurance( currentInd == "N" ) from $V1.getInsurance()
then
    System.out.println("Third Test B " + $V1.getId() );
end

"Ahh", I hear you say, "he's making me use two rules for what can be done with one? Duplicate the RHS code? Phooey!"

No, not me: this was just the bridge to the final. How about this:

rule "Fourth Test"
when
    $V1: Account( $sta: status == "Inactive" )
    or
    ($V1: Account( $sta: status == "Active" )
    and
    Insurance( currentInd == "N" ) from $V1.getInsurance()
    )
then
    System.out.println("Fourth Test " + $V1.getId() );
end

Edit If an account may be associated with any number of Insurance facts (i.e., getInsurance returns a Collection<Insurance>) the last three rules work just as fine. However, they will fire for each associated Insurance where currentInd == "N". To reduce this to a single activation, prefix the Insurance pattern with exists (or not for a somewhat different effect).

It is always worthwhile to consider entering dependant objects (Insurance) as facts as well. Also, a link from the child to the parent (Account) may improve matters.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related