Search code examples
javahibernatedrools

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


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

Solution

  • 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.