即时在 drools 文件中创建新地图 <String, String>

Create new Map<String, String> in drools file on the fly

场景:
根据一些规则奖励学生。在这里,每个学生都有资格获得多项奖励,例如分数为 80 的学生有资格获得 award_65 和 award_75。因此,在评估结果后,我想要一份学生有资格获得的奖项列表。
这个用例有点不切实际,但我试图对我的问题进行类比(不能post,因为它包含敏感信息)。
流口水的输入将通过以下方法返回:这些返回的对象列表将是流口水的事实。

List<Object> getDroolsFacts(Student student, List<StudentAward> studentAwards) {
..........
}

示例规则如下:

//Assuming all the imports are perfectly done
import java.util.*;

rule "award_65"
when
  $Student: Student(status == "active" && marks > 65);
  $studentAwards: List();    // DOUBT - I
  Map<String, String> metadata = new HashMap<>();   // DOUBT - II
  metadata.put("status", "active");
  metadata.put("marks", "65");
then
  final StudentAward studentAward = StudentAward.from("Award65" , metadata);
  $studentAwards.add(studentAward);
end

rule "award_75"
when
  $Student: Student(status == "active" && marks > 75);
  $studentAwards: List();    // DOUBT - I
  Map<String, String> metadata = new HashMap<>();   // DOUBT - II
  metadata.put("status", "active");
  metadata.put("marks", "75");
then
  final StudentAward studentAward = StudentAward.from("Award75" , metadata);
  $studentAwards.add(studentAward);
end


根据当前情况,产品中存在 DOUBT-I 行,并且我添加了与元数据相关的行。

如果学生有 80 分,那么根据当前流口水(即没有元数据相关行),我能够在结果列表 studentAwards 中获得两个对象。

DOUBT - I:(参考被称为 DOUBT-I 的代码片段)

DOUBT - II:(参考 DOUBT-II 中提到的代码片段)

你用一种有趣的方式来解决这个问题。您的规则不起作用的部分原因是您试图将结果放入条件部分。

我们可以将您的问题分解为以下内容。

给定:

  • 一个学生,有状态和分数。

规则 1:

  • 当 status = active 且分数 > 65 时,学生有资格获得 Award65。

规则 2:

  • 当 status = active 且分数 > 75 时,学生有资格获得 Award75。

这很简单。

到目前为止,我们的规则如下所示:

rule "Student is eligible for Award65"
when
  Student( status == "active", marks > 65 )
then 
  // TODO
end

rule "Student is eligible for Awards75"
when
  Student( status == "active", marks > 75)
then
  // TODO
end

到目前为止,还不错。

现在,我们如何实际举办“有资格获得 awardXX”派对?查看您的示例,您的规则输入包括 List<StudentAward>;当学生符合条件时,我们将 StudentAward 的新实例添加到该列表中。 StudentAward 构造函数采用奖项名称和一些数据的映射。

我们现在可以更新上述规则中的 // TODO 评论来做到这一点:

rule "Student is eligible for Award65"
when
  $awards: List()
  Student( status == "active", marks > 65 )
then 
  Map<String, String> metadata = new HashMap<>();
  metadata.put("status", "active");
  metadata.put("marks", "65");
  StudentAward studentAward = StudentAward.from("Award65" , metadata);
  $awards.add(studentAward);
end

rule "Student is eligible for Awards75"
when
  $awards: List()
  Student( status == "active", marks > 75)
then
  Map<String, String> metadata = new HashMap<>();
  metadata.put("status", "active");
  metadata.put("marks", "75");
  StudentAward studentAward = StudentAward.from("Award75" , metadata);
  $awards.add(studentAward);
end

就是这样。剩下的就是实际调用规则了。

public List<StudentAward> getAwardsForStudent(Student student) {
    KieServices kieServices = KieServices.Factory.get();
    KieContainer kContainer = kieServices.getKieClasspathContainer();
    KieBase kBase = kContainer.getKieBase(kbaseName);
    
    List<StudentAward> awards = new ArrayList<>();

    KieSession session = kBase.newKieSession();
    session.insert(student); // insert the Student 
    session.insert(awards); // insert the list of awards
    session.fireAllRules();

    return awards; // at this point, awards contains all of the values from the rules
}

就是这样。给定一个学生,我们执行规则并获得奖励列表。

你的规则唯一真正的问题是你在规则的错误一侧实例化元数据映射(这是规则结果的一部分,而不是条件),以及你如何获得奖励列表.

此答案中的规则有很多重复代码,但由于我们正在处理一个“玩具”示例,我不想浪费太多精力重构为共享函数。如果你确实使用了类似的东西,你会想看看你是否可以使用共享函数甚至继承来简化你的规则。


您问了两个具体问题。我唯一没有明确回答的是这个:

Does $studentAwards: List() correspond to the list provided to drools or is it the new list getting created?

“when”子句中没有创建任何内容。那些是条件。这行是说“在工作内存中获取 List 并将其分配给变量 $studentAwards。”

如果工作内存中有多个列表,就会出现问题,这就是为什么我通常建议不要将这样的集合放入内存中。我通常的建议是创建某种“结果”对象,其中包含您希望规则输出的计算数据。


请注意,在某些版本的 Drools 中,我在尝试使用参数化类型时遇到了错误——如果您遇到这种情况,请尝试使用 Map foo = new HashMap() 而不是 Map<String, String>。我一直没弄清楚是什么原因导致了这个问题。