提问者:小点点

口水堆积正在更新一个无关紧要的事实


在Drools中使用累积时,会针对未更新的事实评估和触发规则。

规则是这样的:

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

这个想法很简单:计算每个UserWidget对象的数量。checkIsUser()是一个简单的相等检查,但表示不能使用索引的检查。

以下是Drools7.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

在这里,我们有5个插入内存的User事实,并且所有这些事实的起始Widget计数为10。fire All规则的第一次调用显示正确的打印,因为所有User事实都插入到内存中。接下来是更新,其中Widget从User3移动到User1。存在预期的输出,但是User5没有理由显示为正在更新。

要在视觉上突出问题:

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)

User5与User2或User4的唯一不同之处在于它是最后添加的用户事实,那么为什么它的行为不同呢?

这个额外的评估是预期的吗?它有什么目的?

请注意,问题不在于如何避免或绕过额外的触发器。


共1个答案

匿名用户

我自己在drools核心周围挖掘以获得一些答案。我发现了以下内容(在7.0.0和7.39.0版本中):

在评估累积节点PhreakAccumulateNode. doRightUpdate的更新的方法中,就在方法doRightUpdatesProcess儿童(用于左元组和右元组之间的匹配)之前,存在以下代码:

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

基于此,无论内存中的元组中是否存在匹配,如果存在作为累积一部分的事实的更新,则第一个左元组(在本例中为User5)将始终被视为匹配。

我查看了另一个beta节点JoinNode。该节点的代码类似于AccumulateNode,但是它缺少第一个左元组的额外更新,让我相信它是意外添加的,没有理由存在。

我这样说对吗?