Wrong answer when checking array of objects where the condition built from a group with "or" between objects, checked with other group (with "and")

28 views Asked by At

I'm trying to run a condition on an array of elements, where each element contains multiple params. I want to evaluate a dynamic condition (as in input from the user) where I want to check if:

(($person1: PersonClass(getName() == "tom") from $persons) || ($person2: PersonClass(getAddress() == "x") from $persons) && ($person3: PersonClass(getCarType() == "aa" from $persons)) && PersonClass($person1 == $person2, $person2 = $person3) from $persons)

I got the error:

unable to resolve method using strict-mode: PersonClass.$person1()]
[Near : {... $person1 == $person2 ....}]

The problem is that Drools evaluates the first group (the || of name equals tom and address equals x, and if true, it continues to the next section. while if the first group is true, this chosen person doesn't need to have a car of type "aa" (it can be any other person from the $persons list, even if its name is not Tom or its address is not "x")

How do I enforce Drools to check the attribute of the entire condition on the same person object?

I'm using latest version of Drools (9.44.0.Final)

2

There are 2 answers

1
Roddy of the Frozen Peas On

How do I enforce Drools to check the attribute of the entire condition on the same person object?

You run all your checks at the same time on the same object.

Your example is a little complicated, so I'm going to use a simpler one. Let's say we have an array of Fruit objects which each have some properties -- name, color, price.

// Fruits in working memory:
{ name: "strawberry", color: "red", price: 0.75 },
{ name: "tomato", color: "red", price: 2.50 },
{ name: "apple", color: "green", price: 1.00 },
{ name: "blueberry", color: "blue", price: 0.50 }

Consider this when clause:

when
  Fruit( color == "red", price < 1.00 )

This condition will match a fruit that is both red and costs less than $1.00. Considering the objects in working memory listed above, it will only match the strawberry (first item). It will not match the tomato, because the price condition isn't met; and it will not match the blueberry because the price is right but the color is no good.

Compare that to:

when
  Fruit( color == "blue" )
  Fruit( price > 2.00 )

This rule will match the first check (color == blue) against blueberry. Then it will match the second check against the only fruit that costs more than $2.00 -- tomato. Tomatoes are not blue, but the two conditions are not being applied to the same fruit because they're in disjoint checks.


Circling back to your rule, a good rule of thumb is that if you find yourself writing "or" conditions, it's very likely that you should instead have two rules.

First, the Drools error is not about the || at all. It's about the fact that syntactically, this condition makes no sense at all:

PersonClass($person1 == $person2, $person2 = $person3) from $persons

I can't even begin to try to figure out what you're trying to do here unfortunately.

As a completely wild guess, if you're looking to see if there is a person named Tom with address X OR a person with address X with address AA, then you should write two rules like this:

rule "Tom at address x"
when
  $person: PersonClass(name == "tom", address == "x") from $persons
then
  // ...
end

rule "address X with car type AA"
when
  $person: PersonClass(address == "x", carType == "aa") from $persons
then
  // ...
end
0
sami610 On

This problem occurs when the condition is of type (OR) AND (OR) where I want to check attributes for the object on both sides. So Drools has something called short-circuiting which in OR it passes checking the second part of the first part is true. So I get a compilation error as the $person2 variable is not bounded.

The solution for me was to configure these variables before the exists check, so for each part of the condition, I already define the variable it represents, and then I do an equal comparison between all the variables of the same type:

$person1: PersonClass() from $persons
$person2: PersonClass() from $persons
$person3: PersonClass() from $persons
((PersonClass(getName() == "tom") from $person1) || (PersonClass(getAddress() == "x") from $person2) && (PersonClass(getCarType() == "aa" from $person3)) && PersonClass($person1 == $person2, $person2 = $person3) from $persons)