drools rules that requires multiple other rules

3.2k views Asked by At

I'm new to drools and I've written some rules and they work as expected. However, I can't help but think there is a more concise way of writing these rules.

My situation is that many of the rules have a few similar basic requirements that I'm repeating in each rule. So, for instance, suppose I have the following rules:

rule "Main Valid Message"
  when Account (open && valid)
  then myResults.add(new Message("Your account is valid"))
end

rule "The user owns something Message"
  when Account (ownership.size() >= 1)
  then myResults.add(new Message("You own something"))
end

rule "Eligibility Message"
  when
    Account (open && valid)
    Account (ownership.size() >= 1)
  then myResults.add(new Message("You are eligible"))
end

Is there a way to rewrite the eligibility rule to take advantage of the first two rules instead of duplicating their content?

ANSWER: Using a combination of J Andy's and laune's replies below, I wrote it as follows:

declare IsValid
   account : Account
end

declare OwnsSomething
   account : Account 
end

rule "Main Valid Message"
   when $account : Account (open && valid)
   then 
      myResults.add(new Message("Your account is valid"))
      insertLogical(new IsValid($account));
end

rule "The user owns something Message"
   when $account : Account (ownership.sizeOwnsSomething() >= 1)
   then 
      myResults.add(new Message("You own something"))
      insertLogical(new OwnsSomething($account));
end

rule "Eligibility Message"
   when
      IsValid()
      OwnsSomething()
   then myResults.add(new Message("You are eligible"))
end
2

There are 2 answers

1
kaskelotti On BEST ANSWER

One option would be to use (internal) facts to handle state. Something like this

declare AuthenticatedUser
end

rule "Authenticate user"
  when
    Account( open && valid )
  then
    insert( new AuthenticatedUser() );
end

rule "Eligibility Message"
  when
    AuthenticatedUser()
  then
    myResults.add( new Message( "You are eligible" ) );
end

I'm not sure what's your use case for Account ownership, but I think you'll get the point from the above code sample.

The AuthenticatedUser fact is now declared inside the DRL file. It could just as well be normal Java class. Also, in the example it has no properties at all, but as mentioned earlier, it could even hold some state as well which you modify in the rules.

1
laune On

Then there is the possibility of extending a rule, which lets you inherit the conditions of one rule to any number of other rules:

rule "multiple account owners"
when
   $acc: Account( owners.size() > 1 )
then end

rule "multiple and open and valid"
extends "multiple account owners"
when
    Account( this == $acc, open && valid )
then

If you follow Andy's idea, consider using insertLogical and a reference to the Account in question (unless you're investigating one Account fact at a time):

rule "multiple account owners"
when
    $acc: Account( owners.size() > 1 )
then
    insertLogical( new Multiple( $acc ) );
end

Be careful with this approach so as not to have rules fire prematurely with the negated (!) form of the property represented by the auxiliary fact, e.g.,

rule "funny account"
when
    $acc : Account( balance > 10000000 ) # can have more than one owner
    not Multiple( account == $acc )
then ... end

This can fire before "multiple account owners" asserts Multiple, unless that rule is given a higher salience. Also, be aware of the effect of evaluating all of these properties by inserting auxiliary facts: this overhead is created anyway, even if a certain Account will not need them all in order to be processed.

Finally, there's the idea of an Adapter (or wrapper) for Account providing any number of fancy getters for Account:

rule "wrap account"
when $acc: Account() not Wrapper( account == $acc )
then insert( new Wrapper( $acc ) end

rule "multiple valid-open"
when Wrapper( multiple && validOpen )
then ... end

And you may, of course, resort to DRL functions:

function boolean multiOwners( Account account ){
    return account.getOwners().size() > 1;
}

rule "multi valid open"
when $acc: Account( eval( multiOwners($acc) && openValied($acc) ) )
then ... end

(I'm not sure whether you need the eval in the latest Drools version(s).)