我正在尝试使用Drools定义一个总是最终(类似于CTL)的查询操作。树由用路径id注释的节点(称为Artifact
s)组成。树中的每个拆分(具有多个子项的父项)通过生成新路径ID并将事实SplitFrom(child, parent)
插入知识库来表示。
基本上,我们希望从树的所有路径中查看某个起始路径id是否存在给定的Artifact
对象。我对此的尝试如下所示:
query alwaysFinally( String $type, String $productName, long $parentPathId )
Artifact( type == $type, productName == $productName, pathId == $parentPathId )
or
forall( SplitFrom( parent == $parentPathId, $childPathId := child )
and
?alwaysFinally( $type, $productName, $childPathId; ) )
end
不幸的是,这会导致以下错误:
java.lang.RuntimeException: Build Errors:
Error Messages:
Message [id=1, level=ERROR, path=edu/umn/crisys/sim/agent/cognition/futureworld/rules/process/antecedent/commonAntecedents.drl, line=47, column=0
text=[ERR 102] Line 47:6 mismatched input '?' in query]
Message [id=2, level=ERROR, path=edu/umn/crisys/sim/agent/cognition/futureworld/rules/process/antecedent/commonAntecedents.drl, line=0, column=0
text=Parser returned a null Package]
...
我玩过多种不同方式插入括号,但我认为这不是真正的问题。如果我删除and
并用逗号或换行符替换它,我会收到以下错误:
java.lang.ClassCastException: org.drools.core.rule.QueryElement cannot be cast to org.drools.core.rule.Pattern
at org.drools.compiler.rule.builder.ForallBuilder.build(ForallBuilder.java:57)
at org.drools.compiler.rule.builder.ForallBuilder.build(ForallBuilder.java:32)
at org.drools.compiler.rule.builder.GroupElementBuilder.build(GroupElementBuilder.java:66)
at org.drools.compiler.rule.builder.GroupElementBuilder.build(GroupElementBuilder.java:36)
at org.drools.compiler.rule.builder.GroupElementBuilder.build(GroupElementBuilder.java:66)
at org.drools.compiler.rule.builder.RuleBuilder.build(RuleBuilder.java:97)
at org.drools.compiler.builder.impl.KnowledgeBuilderImpl.addRule(KnowledgeBuilderImpl.java:1820)
at org.drools.compiler.builder.impl.KnowledgeBuilderImpl.compileRules(KnowledgeBuilderImpl.java:1111)
at org.drools.compiler.builder.impl.KnowledgeBuilderImpl.compileAllRules(KnowledgeBuilderImpl.java:989)
at org.drools.compiler.builder.impl.CompositeKnowledgeBuilderImpl.buildRules(CompositeKnowledgeBuilderImpl.java:260)
at org.drools.compiler.builder.impl.CompositeKnowledgeBuilderImpl.buildPackages(CompositeKnowledgeBuilderImpl.java:121)
at org.drools.compiler.builder.impl.CompositeKnowledgeBuilderImpl.build(CompositeKnowledgeBuilderImpl.java:105)
at org.drools.compiler.kie.builder.impl.AbstractKieModule.buildKnowledgePackages(AbstractKieModule.java:243)
at org.drools.compiler.kie.builder.impl.AbstractKieProject.verify(AbstractKieProject.java:64)
at org.drools.compiler.kie.builder.impl.KieBuilderImpl.buildKieProject(KieBuilderImpl.java:230)
at org.drools.compiler.kie.builder.impl.KieBuilderImpl.buildAll(KieBuilderImpl.java:198)
我不认为累加器会在这里工作,因为查询不是Pattern对象。
有没有人知道如何在Drools中表达这一点?
答案 0 :(得分:0)
你不需要这个咒语。这是Drools为所有匹配自动执行的操作,以继续匹配过程。 forall是你在这里不需要的通用量词,因为条件不一定适用于所有的事实。
query alwaysFinally( String $type, String $productName, long $parentPathId )
Artifact( type == $type, productName == $productName, pathId == $parentPathId )
or
( SplitFrom( parent == $parentPathId, $childPathId := child )
and
?alwaysFinally( $type, $productName, $childPathId; )
end
如果Artifact多次出现,可能会导致多次匹配。
答案 1 :(得分:0)
逻辑规则胜出。 “或”语句的后半部分可以表示为(使用集合表示法)
let childPathIds: { SplitFrom(parent == $parentPathId, child := childPathId }
forall x in childPathIds, P(x)
现在,我们可以加倍否定和保持身份。这让我们:
let childPathIds: { SplitFrom(parent == $parentPathId, child := childPathId }
exists x in childPathIds, not P(x)
因此,可以使用以下
在dools中表示accumulate( SplitFrom( parent == $parentPathId, $childPathId := child );
$childPaths: collectSet( $childPathId ) )
and
// Set non-empty
exists( $childPathId: Long() from $childPaths )
and
( not( exists( $childPathId: Long( ) from $childPaths
and
not (?alwaysFinally( $type, $productName, $childPathId; ) ) ) )
)
注意额外的非空检查?我们需要这个,因为如果$childPaths
为空,第3个条件将返回true。
所以,故事的道德:是的,我们可以用子查询来捕捉forall;但是我们必须使用更加笨重的语法来完成它。至少我们不必去Java实现它。