如何在 Drools 文件中传递多个对象并提取所需的对象

How to pass multiple objects in a Drools file and extract the desired object

我是 Drools 的新手。对于一个项目,我试图将多个对象传递到 drl 文件中,但我不知道如何在同一规则中处理多个对象!

这是我流口水的逻辑:

rule "SNAP when Employed"
    
    when
        citizenDataObject: CitizenData(planName=="SNAP" && employed==true)
    then
        PlanData planDataObject= new PlanData();
        planDataObject.setPlanStatus("DN");
        planDataObject.setDenialReason("Salaried Employee");
    end

当我执行此规则时,我无法检索 planDataObject。

在 java 端,我正在传递 CitizenData 对象和 PlanData 对象:

WorkingMemory workingMemory = ruleBase.newStatefulSession();
workingMemory.insert(citizenData);
workingMemory.insert(planData);
workingMemory.fireAllRules();

有多种方法可以从您的规则中获取数据。他们是:

  1. 使用 side-effects 调用操作。
  2. Set/modify 全局变量。
  3. 使用事实句柄从工作内存中提取数据。

第一个是最简单的,我建议这样做。第二个是我们在“过去”(大约 10 年前,使用 Drools 5)时的做法。最后一个在技术上是可行的,但非常复杂,我从未做过。

我已经在 this other question 的答案中涵盖了所有这些,我认为这是重复的,但不能投票给它,因为它没有被赞成的答案。


side-effects

的操作

这是执行此操作的推荐方法。具有副作用的动作是您所说的可以在规则之外看到的东西。一个极端的例子可能是保存到数据库,但一个更简单的例子可能是将 object 添加到列表中。

例如,考虑这种玩具情况。如果学生无故缺勤超过 3 次,则会生成一张便条,并随他们一起寄回家 parents。以下是我们可以对这种情况进行建模的两种方式。一条规则将注释添加到学生 object 本身的变量中。另一个规则调用实用程序函数将注释发送到 parents。第三个将规则添加到笔记集合中。

rule "Unexcused absences: notify by adding note to the student record"
when
  $student: Student( absences >= 3, $name: name )
then
  $student.addNote( "Student '" + $name + "' has had too many unexcused absences");
end

rule "Unexcused absences: notify by email"
when
  $student: Student( absences >= 3, $name: name )
  $emailSystem: MailSystem() // Some utility class which can send emails
then
  $emailSystem.sendNote(
    $student,
    "Student '" + $name + "' has had too many unexcused absences"
  );
end

rule "Unexcused absences: append note to a list"
when
  Student( absences >= 3, $name: name )
  $notes: List()
then
  $notes.add("Student '" + $name + "' has had too many unexcused absences.");
end

所有这些规则的工作方式是它们更改外部应用程序(邮件系统)或输入 objects(学生或笔记列表)中的数据。规则触发后,这些副作用将对外界可见。


全局变量

这是老办法,全局变量。它们 通常 与 Java 中的静态变量相同,但您不能依赖它们来跟踪规则执行之间的数据。 (就像静态变量和线程一样,规则可见的数据有点复杂。)

但如果我们只想跟踪规则输出,这是一个可行的选择。

首先,您在调用规则之前针对会话设置一个全局变量:

KieSession session = ruleBase.newStatefulSession();
session.insert(...); // insert data
session.setGlobal( "myGlobalFoo", value ); // sets the global; note the name must match the rule file!
session.fireAllRules();

然后在您的规则文件中,您将使用 global 关键字在文件顶部、导入下方声明全局。您的全局名称必须与您传递给 setGlobal 方法的名称完全匹配。

使用与之前相同的示例(缺勤 3 次以上的学生收到寄回家的便条),我们可以按如下方式使用全局。在第一个示例中,我们可以使用 List<string> notes 寄回家的便条,并将其设置为全局:

global List notes;

rule "Student absences: add note to global List"
when
  Student( absences >= 3, $name: name )
then
  notes.add("Student '" + $name + "' has been absent too many times!");
end

触发所有规则后,您传递到全局的 object 将添加值。请注意,保留对 object 的引用很重要——不要调用 session.setGlobal("notes", new ArrayList<>()) 之类的东西!这样你就不会引用那个笔记列表了。

KieSession javadoc 讨论了一些全局变量以及一些注意事项。


第三种方法是使用事实句柄提取数据。这没有很好的记录,我也没有亲自做过,但它应该是可能的。通常它用于对传递到工作内存的数据保留一个“句柄”,以便您可以将其取出进行单元测试或诸如此类的东西。我认为可以定位并提取您新实例化的数据。

就我个人而言,我不会走这条路,因为它不是标准的工作流程,而且与 Drools 库的其他部分相比,围绕这些 API 的文档很差(因为这主要用于内部和测试。)