Drools accumulate 正在更新一个不相关的事实
Drools accumulate is updating an irrelevant fact
在 Drools 中使用累积时,会针对未更新的事实评估并触发规则。
规则如下:
rule "WidgetsPerUser"
when
$user : User()
accumulate(
Widget ( checkIsUser($user) );
$widgetCount : sum(1)
)
then
System.out.println($user + " has " + $widgetCount + " widgets");
end
想法很简单:计算每个 User
的 Widget
个对象的数量。 checkIsUser()
是一个简单的相等性检查,但表示不能使用索引的检查。
这是 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
这里我们有 5 个 User
事实插入到内存中,并且所有的起始 Widget
计数都是 10。第一次调用 fireAllRules
显示正确的打印,因为所有 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 的唯一区别在于它是最后添加的用户事实,那么为什么它的行为不同?
这个额外的评估是预期的吗?它有什么作用?
请注意,问题不是关于如何避免或解决额外触发器的问题。
我自己挖掘了 drools core 以获得一些答案。我发现了以下内容(在 7.0.0 和 7.39.0 两个版本中):
在计算累积节点PhreakAccumulateNode.doRightUpdates
更新的方法中,就在方法doRightUpdatesProcessChildren
(用于左右元组之间的匹配)之前,存在以下代码:
// 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
,但是它缺少第一个左元组的额外更新,这让我相信它是意外添加的,没有理由存在。
我说得对吗?
在 Drools 中使用累积时,会针对未更新的事实评估并触发规则。
规则如下:
rule "WidgetsPerUser"
when
$user : User()
accumulate(
Widget ( checkIsUser($user) );
$widgetCount : sum(1)
)
then
System.out.println($user + " has " + $widgetCount + " widgets");
end
想法很简单:计算每个 User
的 Widget
个对象的数量。 checkIsUser()
是一个简单的相等性检查,但表示不能使用索引的检查。
这是 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
这里我们有 5 个 User
事实插入到内存中,并且所有的起始 Widget
计数都是 10。第一次调用 fireAllRules
显示正确的打印,因为所有 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 的唯一区别在于它是最后添加的用户事实,那么为什么它的行为不同?
这个额外的评估是预期的吗?它有什么作用?
请注意,问题不是关于如何避免或解决额外触发器的问题。
我自己挖掘了 drools core 以获得一些答案。我发现了以下内容(在 7.0.0 和 7.39.0 两个版本中):
在计算累积节点PhreakAccumulateNode.doRightUpdates
更新的方法中,就在方法doRightUpdatesProcessChildren
(用于左右元组之间的匹配)之前,存在以下代码:
// 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
,但是它缺少第一个左元组的额外更新,这让我相信它是意外添加的,没有理由存在。
我说得对吗?