Conceptual meaning of 'not' keyword; evaluating between objects - drools

I am trying to find a BucketTotal object which has the smallest total in a Drools Planner project. I adapted this from example code.
rule "insertMinimumBucketTotal"
when
$b : BucketTotal($total : total)
not BucketTotal(total > $total) // CONFUSED HERE
then
insertLogical(new MinimumBucketTotal($total));
end
As far as my reasoning went, it meant "find BucketTotal object $b, such that there doesnt exist another BucketTotal object whose total is greater than total of $b".
Turns out, it meant the opposite (and I corrected it).
Please explain how Drools reasons that statement to find $b.

Indeed your are confusing things. "not" means "not exists". So if you want to find the minimum total you can do:
rule "minimum"
when
BucketTotal( $min : total )
not BucketTotal( total < $min )
then
// do something with $min
end
The above is usually the more performant way of doing it, but you can also use accumulate if you prefer:
rule "minimum"
when
accumulate( BucketTotal( $total : total ),
$min : min( $total ) )
then
// do something with $min
end

Related

Drools rule using accumulate

Hello I it is my first time involved in drools project. I have created some simple rules that work fine, however I have trouble with more complex rules that use the accumulate function. Below I have this rule.
rule "1"
no-loop
when
$msg : Declaration(header.totalGrossMassMeasure != null,
header.totalGrossMassMeasure.compareTo(BigDecimal.ZERO) > 0 )
result : ValidationResult()
$netValue : Number() from accumulate (
GoodsItemsType($net : netNetWeightMeasure),
sum($net.doubleValue())
)
eval($netValue.doubleValue() > ($msg.getHeader().getTotalGrossMassMeasure().doubleValue() + (0.45 * $msg.getGoodsItems().size())))
then
RulesValidationError error = new RulesValidationError();
error.setErrorType(ErrorType.INCORECT_VALUE);
result.getErrorsList().add(error);
end
the concept is to sum the net value from a list of goodsItemType object and compare the sum to the total gross mass measure multiplied by one buffer number. The problem is I have been trying last couple of days not being able to fire the rule with anything. Could someone please help me?

Drools - Sliding window not working as expected

I'm new to drools. I've defined the following rule to add the last two numbers in the stream together. I then send in a stream of 100 events, with the values set from 1 to 100. So I would expect the output to be 0, 1, 3, 5, 7, 9, 11, 13 etc.
declare TestEvent
#role( event )
value : int
end
rule "Simple Rule"
when
$sum : Integer() from accumulate ( TestEvent( $value : value ) over window:length( 2 ); sum( $value) )
then
System.out.println("Value: " + $sum );
end
The session is started using "fireUntilHalt" in a separate thread. If I put a delay of 10 milliseconds between every event inserted it works as expected. However when I don't have the delay, the results aren't as expected. Usually it just prints 0 and 197. Sometimes I might get a number or two in-between as well.
Why does this happen and what I should do to fix?
Ok, I finally understand it.
Having fireUntilHalt running in a separate thread means that the rules are only evaluated every now and then (not sure what that time period is). My assumption was that they would be evaluated on every insert. So on every insert the accumulator values are updated, but the rules evaluated aren't evaluated. So because my rules are inserted quickly (in about one second), the first and last insert is all that seems to be evaluated.
To get this to work so every insert is evaluated, I removed the fireUntilHalt, and did a separate fireAllRules after each insert.
Sliding windows are a crutch. Replace it by simple logic.
class Pred {
Integer pred;
}
rule procInt1
when
$p: Pred( pred == null )
$i: Integer()
then
modify( $p ){ setPred( $i ); }
end
rule procInt
when
$p: Pred( $i1: pred != null )
$i2: Integer()
then
System.out.println( $i1 + $i2 );
modify( $p ){ setPred( $i2 ); }
end
The fact that you have two threads doesn't mean that you can't have race conditions. The thread inserting events isn't suspended and so everything is possible.
Time related rules rely on 'time'. Smallest time tick for drools seems to be 1ms. Rules will be evaluated on each insertion (and respective consequences will be added to the agenda) but agenda will be executed on next millisecond tick (or when you explicitly fire all rules).

Latest n events, matching a certain pattern

Is there a built-in feature in Drools, selecting the latest n events, matching a certain pattern? I've read about sliding length windows in the documentation and the stock tick example seemed to be exactly what I wanted:
"For instance, if the user wants to consider only the last 10 RHT Stock Ticks, independent of how old they are, the pattern would look like this:"
StockTick( company == "RHT" ) over window:length( 10 )
When testing the example, it seems to me that it is evaluted more like a
StockTick( company == "RHT" ) from StockTick() over window:length( 10 )
selecting the latest 10 StockTick events and afterwards filtering them by company == "RTH", resulting in 0 to 10 RHT-Ticks, event though the stream contains more then 10 RTH-events.
A workaround is something like:
$tick : StockTick( company == "RHT" )
accumulate(
$other : StockTick(this after $tick, company == "RHT" );
$cnt : count(other);
$cnt < 10)
which has bad performance and readability.
Most likely you are seeing an initial phase where the count of events in the window and according to the constraints hasn't reached the length specified in window:length yet. For instance,
rule "Your First Rule"
when
accumulate( $st : Applicant($age: age > 5) over window:length(10)
from entry-point X,
$avg: average ( $age ), $cnt: count( $st ))
then
System.out.println("~~~~~avg~~~~~");
System.out.println($avg + ", count=" + $cnt);
System.out.println("~~~~~avg~~~~~");
end
displays an output even before there are 10 matching Applicants but later on, $cnt never falls below 10, even though $age ranges from 0 to 9, periodically.
If you do think you have found an example supporting your claim, please provide full code for reproduction and make sure to indicate the Drools version.
Your workaround is very bad indeed, as it accumulates for each StockTick. But a window:length(n) can be very efficiently implemented by using an auxiliary fact maintaining a list of n events. This may even be more advantageous than window:length.

Create Accumulate function on Drools Decision table

I am trying to create accumulate function condition on Decision table. Please help me that How to create on Decision table.
My accumulate rule function is
when
$i : Double(doubleValue > 1000 ) from accumulate( Product($productQty:quantity),sum($productQty))
then
System.out.println( "The quantity is exceeded more than 1000 and the total value is " + $i );
You can create a column
rows
n condition
n+1 $i : Double() from accumulate( Product($productQty:quantity),sum($productQty))
n+2 doubleValue > $param
n+3 add quantitities and check
n+4 1000
Two comments.
This is not well-suited for decision tables unless you plan to check for different ranges of the accumulated value.
Why do you use double for counting what is, most likely, integral quantities? I'd be surprise if the accumulated stock exceeds Integer.MAX_VALUE. In short: use Number in the pattern and intValue in the constraint.

How to obtain the minimum of an updated value in Drools?

I want to use Drools to manage some questions in a form. Depending of the answer of the question, the question score be updated with a value. Then I want to obtain the minimum value of all questions answered in a category.
One example:
Category "Replicant or not?":
You’re in a desert walking along in the sand when all of the sudden you look down, and you see a tortoise, crawling toward you. You reach down, you flip the tortoise over on its back. Why is that?
1. it was an error. (5 points).
2. It is the funniest think to do at a dessert. (3 points).
3. You want to kill the tortoise in the most painful way (1 point).
//More other questions.
At the end of the test, the minimum value for each category will be the used one.
Then I have defined some drools rules to define the score for each one (in a spreadsheet, by I put the rule translations):
rule "Question_1"
when
$qs: Questions(checked == false);
$q: Question(category == 'cat1', question == "q1", answer == "a") from $qs.getQuestions();
then
$q.setValue(5);
$qs.setChecked(true);
update($qs);
end
checked value is used to avoid to reuse the rule when updating. category, question, answer are used for classifying the question.
And then, my rule for calculate the minimum is:
rule "Min value"
when
$qs: Questions(checked == true);
not q:Question($v: value; value < $v) from $qs.getQuestions();
then
$qs.setMinValue($v);
System.out.println("The smallest value is: "+ $v);
end
The error obtained is:
$v cannot be resolved to a variable
Then, the question is: How can I obtain the minimum value of a value setted by a previous rule?
The duplication of "from " can be avoided, using the accumulate CE which is about 25% faster.
rule "getmin"
when
Questions( $qs: questions )
accumulate( Question( $v: value ) from $qs; $min: min( $v ) )
then
System.out.println( "minimum is " + $min );
end
You are trying to get $v from a Fact that doesn't exist: not q:Question($v: value; value < $v) from $qs.getQuestions();
What is the expected value of $v is there is no Question that matches that pattern!?
What the seccond pattern is basically saying is: "there is no Question from Questions.getQuestions() that has a value ($v) and that that value is less than the same value"
What you have to do is to bind $v to the positive pattern. Something like this:
rule "Min value"
when
$qs: Questions(checked == true);
Question($v: value) from $qs.getQuestions()
not Question(value < $v) from $qs.getQuestions();
then
$qs.setMinValue($v);
System.out.println("The smallest value is: "+ $v);
end
Hope it helps,
---EDITED: added missing Question positive pattern