Drools accumulate is updating an irrelevant fact

自作多情 提交于 2020-07-07 07:09:19

问题


When using an accumulate in Drools, a rule is evaluated and triggered for a fact that wasn’t updated.

Here is the rule:

rule "WidgetsPerUser"
    when
        $user : User()
        accumulate(
            Widget ( checkIsUser($user) ); 
            $widgetCount : sum(1)
        )
    then
        System.out.println($user + " has " +  $widgetCount + " widgets");
end

The idea is simple: count the number of Widget objects per User. checkIsUser() is a simple equality check, but represents a check that cannot use indexing.

Here is sample output from Drools 7.0.0.Final:

-- Calling fire all rules --
User5 has 10 widgets
User4 has 10 widgets
User3 has 10 widgets
User2 has 10 widgets
User1 has 10 widgets

Widget moved from User3 to User1

-- Calling fire all rules --
User5 has 10 widgets
User3 has 9 widgets
User1 has 11 widgets

Here we have 5 User facts inserted into memory, and all have a starting Widget count of 10. The first call for fireAllRules displays the correct prints, as all User facts are inserted into memory. Next is an update, where a Widget is moved from User3 to User1. The expected output is present, however User5 has no reason to be displayed as being updated.

To visually highlight the issue:

User5 has 10 widgets    (User5 should NOT be triggered!)

                        (User4 is correctly ignored)

User3 has 9 widgets     (User3 is correctly triggered)

                        (User2 is correctly ignored)

User1 has 11 widgets    (User1 is correctly triggered)

The only way User5 differs from User2 or User4 is that it was the last user fact added, so why does it behave differently?

Is this extra evaluation expected? What purpose does it serve?

Note that the question is not about how to avoid or work around the extra trigger.


回答1:


I dug around the drools core myself to get some answers. I found the following (in both versions 7.0.0 and 7.39.0):

In the method that evaluates the updates for the accumulate node PhreakAccumulateNode.doRightUpdates, just before the method doRightUpdatesProcessChildren (which is used for matching between left and right tuples), the following code is present:

// if LeftTupleMemory is empty, there are no matches to modify
if ( leftTuple != null ) {
   if ( leftTuple.getStagedType() == LeftTuple.NONE ) {
         trgLeftTuples.addUpdate( leftTuple ); //<----
   }
   doRightUpdatesProcessChildren( ARGS );
}

Based on this, regardless of there being a match within the tuples in the memory, if there is an update of a fact that is part of an accumulate, the first left tuple (in this case User5) will always be treated as being matched.

I took a look at another beta node JoinNode. The code for that node is similar to the AccumulateNode, however it is missing this additional update of the first left tuple, leading me to believe that it is accidentally added, and has no reason to be present.

Am I correct with this?



来源:https://stackoverflow.com/questions/62705527/drools-accumulate-is-updating-an-irrelevant-fact

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!