user3509528
user3509528

Reputation: 107

Working with lists in Drools Rules Engine

I'm trying to work with lists in drools. I'm passing in a request which has a purchase list as part of it. I want to do several rules including checking if the size is correct, then if all elements are the same, if all purchases are authorized, ... I have the following code but I'm running into problems working with the list. Is this the right approach? Especially when checking for the size?

import com.rules.Purchase
import com.rules.PurchaseRequest

dialect  "mvel"

global Boolean eligibleForRefund

rule "Check for list not equal to two elements" salience 10
    when
        PurchaseRequest(getPurchases != null, getPurchases.size() != 2)

    then
        drools.getKieRuntime().setGlobal("eligibleForRefund", false);
end

rule "Check for two purchases" salience 9
    when:
        $purchaseRequest: PurchaseRequest()
        Number(intValue != 2) from accumulate(Purchase(getStatus() == "Approved") from $purchaseRequest.getPurchases(), count(1))

    then
        drools.getKieRuntime().setGlobal("eligibleForRefund", false);
end

rule "Check for the same purchases" salience 8
    when:
        $purchaseRequest: PurchaseRequest()

    then
        firstPurchase = $purchaseRequest.getPurchases().get(0).getCost();
        hasAllElements = true;

        for (Purchase purchase : $purchaseRequest.getPurchases()) {
            if (purchase.getCost() != firstPurchase) {
                hasAllElements = false;
            }
        }

        drools.getKieRuntime().setGlobal("eligibleForRefund", hasAllElements);
end

Upvotes: 1

Views: 2640

Answers (1)

Roddy of the Frozen Peas
Roddy of the Frozen Peas

Reputation: 15219

Assuming that your class definition looks like this:

class PurchaseRequest {
  private List<Purchase> purchases;
  
  public List<Purchase> getPurchases() { return this.purchases; }
}

You should be pulling references out of the holder instead of constantly interacting with things via the getters. In other projects this helps with keep data consistent especially with shared resources. Recall that if you have a getter whose name matches the format getXyz, you can refer to it simply as xyz and drools will automagically map it to the getter function. This allows us to get the purchases via PurchaseRequest( $purchases: purchases ) since purchases will be mapped to getPurchases(). (Note that if purchases happened to be a public variable, it would have mapped to that first; but since it's private it falls back on the public getter that follows bean naming conventions.)

Second you use an accumulate in a very simple scenario where a collect would probably be more appropriate. Generally you'd use accumulate for more complicated "get things that look like this" sort of situations; but for simple matching, a collect works just as well.

The third rule needs the most work. You do not want to do this kind of business logic on the right hand side of your rule. There's a whole lot of ways you could go about checking that all the elements are the same -- if you've implemented equals/hashCode you could just shove everything into a set and confirm that the length of the set is still the length of the list; you could invert the rule to instead check for at least one item that's different; you could use accumulate or collect; ...

Finally --

  • Avoid saliences. They're bad design. Your rules should stand alone. You only need saliences here because your third rule sets both true and false. If instead you defaulted to true and then used the rules to override it to false, you could get away with having absolutely no saliences at all.
  • It's very unusual to use primitives for a global variable. I'm frankly not convinced that this will even work with a primitive. Globals work because the object is passed in by reference, and updated in the rules, and therefore the caller which retains the reference to the object will get the updated value. That doesn't work with primitives.
rule "Check for list not equal to two elements" 
salience 1
when
  PurchaseRequest($purchases: purchases != null)
  List(size != 2) from $purchases
then
  drools.getKieRuntime().setGlobal("eligibleForRefund", false);
end

rule "Check for two purchases"
salience 1
when:
  PurchaseRequest( $purchases: purchases != null)
  List( size != 2 ) from collect( Purchase(status == "Approved") from $purchases)
then
  drools.getKieRuntime().setGlobal("eligibleForRefund", false);
end

// I've no idea what data type `getCost()` returns; I'm assuming "String"
rule "Check for the same purchases"
when:
  PurchaseRequest($purchases: purchases != null)

  // accumulate all of the costs into a set. if all costs are the same, set size = 1
  $costs: Set() from accumulate( Purchase( $cost: cost ) from $purchases;
                                 collectSet($cost))
then
  drools.getKieRuntime().setGlobal("eligibleForRefund", $costs.size() == 1);
end

Upvotes: 2

Related Questions