0

Let's say I have red and green baskets holding boxes of strawberries and grapes:

public class Basket {
    public enum Color { RED, GREEN };

    private Color color;
    private List<FruitBox> fruitBoxes;

    //Assume appropriate constructors, getters exist
}

public class FruitBox {
    public enum Fruit { STRAWBERRY, GRAPE };

    private Fruit fruit;

    //Assume appropriate constructors, getters exist
}

And I need to use Drools to print the following calculations:

  1. The number of GRAPE or STRAWBERRY FruitBoxes in a Basket if it exceeds two
  2. The total number of FruitBoxes, grouped on FruitBox fruit and Basket Color

How might I code (1.) and (2.) as simple and efficient Drools rules? After reading the documentation, I have come up with the following solutions:

rule "Two or more of same fruit in any Basket"
    when
        //Bind the contents of each basket
        $Basket : Basket($FruitBoxes : fruitBoxes)

        //Compute A list of the unique fruits in current basket contents
        $fruits : Object() from accumulate( FruitBox($f : fruit) from $FruitBoxes, collectSet($f) )

        //Match and bind each unique fruit one by one
        $fruit : FruitBox.Fruit() from $fruits

        //Find the number of times the unique fruit occurs in this Basket
        $count : Number(intValue >= 2) from accumulate(
            $f : FruitBox(fruit == $fruit) from $FruitBoxes,
            count($f) )
    then
        System.out.println($Basket + " has " + $count + " of the fruit " + $fruit);
end

Which successfully prints, for one test dataset, output like:

com.sample.DroolsTest$Basket@10cd6753 has 4 of the fruit STRAWBERRY
com.sample.DroolsTest$Basket@10cd6753 has 3 of the fruit GRAPE
com.sample.DroolsTest$Basket@2e060819 has 2 of the fruit GRAPE

My concern is that my rule is long and complicated, and appears to iterate each Basket several times (once to find the unique fruits within, and then once more to tally each fruit). I suspect that this is a product of my lack of Drools knowledge and that a better solution exists. In plain Java, I would use a HashMap to store each fruit and its count in one pass, which would be simple and fast. Is there any way to rewrite my Drools code to improve performance and readability? The same question applies to my second rule:

rule "Totals by fruit and by basket color"
    when
        //Calculate the cross product of all fruits and colors
        $fruit : FruitBox.Fruit()
        $color : Basket.Color()

        //Count the number of FruitBoxes of the chosen fruit in Baskets of the chosen color
        $count : Number() from accumulate(
            $fb : FruitBox(fruit == $fruit) 
            and exists Basket(color == $color, fruitBoxes contains $fb),
            count($fb) )
    then
        System.out.println($count + " " + $fruit + " in " + $color + " baskets");
end

Which produces output like:

5 STRAWBERRY in GREEN baskets
6 STRAWBERRY in RED baskets
6 GRAPE in GREEN baskets
4 GRAPE in RED baskets

but, unfortunately, looks like it searches through all the Baskets for every FruitBox, when it would make far more sense to tally up one (or all) kind(s) of FruitBoxes while iterating over the Baskets once. Is there a better syntax for this rule as far as performance is concerned?

Thanks for the help!

Anthony
  • 341
  • 2
  • 11

1 Answers1

0

Hi for the first question I would use two separate rules:

rule "strawberriesInBasket"
dialect "mvel"
when
    $basket : Basket($fruitBoxes : fruitBoxes)
    Number($count : intValue() > 2) from accumulate ($fruitBox : FruitBox(fruit.name == "strawberry") from $fruitBoxes, count($fruitBox)) 
then
    System.out.println("Count of strawberry boxes in basket is: " + $count )
end

and

rule "grapesInBasket"
dialect "mvel"
when
    $basket : Basket($fruitBoxes : fruitBoxes)
    Number($count : intValue() > 2) from accumulate ($fruitBox : FruitBox(fruit.name == "grape") from $fruitBoxes, count($fruitBox)) 
then
    System.out.println("Count of grape boxes in basket is: " + $count )
end

Please notice I changed the enum Fruit to data object with field name.

jomarko
  • 127
  • 3
  • Thanks, Jomarko! The reason that I accumulated the fruits is that the code above is a trivial example of a much larger problem, where the values of 'fruit.name' are dynamically determined (there may be several hundred values). If only two values for 'fruit.name' were possible, then I agree that this would be a better solution. – Anthony Jul 23 '18 at 22:17
  • Ok, I understand now better. Several hundred values is really a lot of. However if the domain of values is finite, I would recommend you to have a look on xls decision tables or guided decision tables in kie workbench. In both cases you write the rule just once (template of the rule) and then just fill the values that differs in particular rules to simple table. – jomarko Jul 24 '18 at 05:43