Drools:在forall中使用子查询

时间:2016-11-30 22:58:53

标签: drools

我正在尝试使用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中表达这一点?

2 个答案:

答案 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实现它。