在属性和包含对象上执行 Drools 规则的顺序

Ordering of executing Drools rules on attributes and containing object

我对 Drools 中的一个概念有点困惑,谁能澄清我是否以错误的方式看待这个问题?该解决方案有效,但似乎是错误的方法,我将在最后解释。

我有一个 Country 模型对象,包含 id、continent、area;其中 area 是经济区,例如欧盟或美国; 我有 CountryID、EconomicAreaId、ContinentID 的枚举; 我有一个包含 origin:Country、destination:Country、srucharge:Int 的请求对象; 我有两个 Drools 规则(实际上用 xls 表示,但我会在这里列出 drl):

<package and imports are correct>
rule "UK"
    when
        $c:Country($c.getId() in (CountryID.UNITED_KINGDOM))
    then
        $c.setArea(EconomicAreaID.EU);
        $c.setContinent(ContinentID.EUROPE);
        /* update($c); // I tried this too :) //
end

rule "US"
    when
        $c:Country($c.getId() in (CountryID.UNITED_STATES))
    then
        $c.setArea(EconomicAreaID.US);
        $c.setContinent(ContinentID.N_AMERICA);
end

rule "EU to EU"
    when
        $rr:ChargeRequest($rr.getOrigin.getArea() == EconomicAreaID.EU, $rr.getDestination.getArea() == EconomicAreaID.EU)
    then
        $rr.setCharge(1);
end

rule "US to US"
    when
        $rr:ChargeRequest($rr.getOrigin.getArea() == EconomicAreaID.US, $rr.getDestination.getArea() == EconomicAreaID.US)
    then
        $rr.setCharge(2);
end

rule "EU to US"
    when
        $rr:ChargeRequest($rr.getOrigin.getArea() == EconomicAreaID.EU, $rr.getDestination.getArea() == EconomicAreaID.US)
    then
        $rr.setCharge(3);
end

rule "US to EU"
    when
        $rr:ChargeRequest($rr.getOrigin.getArea() == EconomicAreaID.US, $rr.getDestination.getArea() == EconomicAreaID.EU)
    then
        $rr.setCharge(4);
end

首先我想插入两个国家对象,起点和终点,让 Drools 自动为它们的属性分配大陆和经济区;

然后我想让Drools计算附加费,这里简化为一个int。

为此,我必须添加两个国家/地区对象并触发所有规则以填充国家/地区对象,然后添加请求对象并再次触发所有规则以获取附加费。

KieSession kSession = kieContainer.newKieSession();
ChargeRequest rr = new ChargeRequest(new Country("UNITED_STATES"), new Country("UNITED_KINGDOM"));

kSession.insert(rr.getOrigin()); // add both country objects
kSession.insert(rr.getDestination());
kSession.fireAllRules(); // LINE 6 : fire rules to calculate the economic areas and continents of the country objects

kSession.insert(rr); // add the ChargeRequest object
kSession.fireAllRules(); // fire the rules to calculate the charge

return rr.toString(); // ChargeRequest(origin=Country(id=UNITED_STATES, continent=N_AMERICA, area=US), destination=Country(id=UNITED_KINGDOM, continent=EUROPE, area=EU), charge=4)

如果我省略第 6 行,则费用不会计算为 4,因为 Drools 对人口稀少的国家/地区对象执行费用规则。我认为 Drools 具有以正确顺序评估所有规则的概念,因此它将 "know" 将规则应用于国家/地区,然后再将它们用作收费规则的输入。

这只是我正在玩的一个简化示例,所以它似乎不应该这么复杂,所以我的概念方法有误吗?

哦,Drools 知道如何以及何时评估您的规则,当然它知道......唯一的事情是当模型中发生某些变化时,您必须让它知道。 您在规则右侧调用的设置器永远不会通知 Drools,因此它不知道您所做的更改。

Drools 中有一个名为 modify 的特殊关键字,可让引擎知道模型中的某些内容何时发生更改。

设置区域和大陆的规则应该是这样的(取决于你正在做的 Drools 版本,当你使用 modify 时你可能会开始遇到无限循环。我重写了左边 -遵守规则以避免它们):

rule "UK"
when
    $c:Country(
      area == null,
      continent == null,
      id in (CountryID.UNITED_KINGDOM)
    )
then
    modify($c){
      setArea(EconomicAreaID.EU),
      setContinent(ContinentID.EUROPE)
    };
end

现在,第二个问题是,在设置费用的规则中,Country 嵌套在您的 ChargeRequest 对象中。 Drools 不会自动理解当您修改 Country 时它必须重新评估与 ChargeRequests 相关的规则。您必须明确说明:

rule "EU to EU"
when
    $rr:ChargeRequest(
      $origin: origin, 
      $destination: destination
    )
    Country(
      this == $origin,
      area == EconomicAreaID.EU
    )
    Country(
      this == $destination,
      area == EconomicAreaID.EU
    )
then
    $rr.setCharge(1);
end

如你所见,Drools 很聪明,但你必须帮他一点忙 ;)


根据一组固定条件处理此类参数化值时,更好的方法是将条件本身建模为事实:

1.- 创建一个 class 来模拟约束和结果:

public class RequestCharge{
  private EconomicAreaID origin;
  private EconomicAreaID destination;
  private double charge;

  //getters and setters
}

2.- 将所有约束插入您的会话中:

kSession.insert(new RequestCharge(EconomicAreaID.EU, EconomicAreaID.EU, 1.0);
kSession.insert(new RequestCharge(EconomicAreaID.US, EconomicAreaID.US, 2.0);
kSession.insert(new RequestCharge(EconomicAreaID.EU, EconomicAreaID.US, 3.0);
kSession.insert(new RequestCharge(EconomicAreaID.US, EconomicAreaID.EU, 4.0);

3.- 有一个规则来处理所有请求(您可能想添加一个额外的规则来处理与任何已配置的 RequestCharges 不匹配的请求):

rule "Apply Charge"
when
    $rr:ChargeRequest(
      $origin: origin, 
      $destination: destination
    )
    $o: Country(  //You still need these Country patterns to react to the changes in the first rule
      this == $origin
    )
    $d: Country(
      this == $destination
    )
    RequestCharge(
      origin == $o,
      destination == $d,
      $charge: charge
    )
then
    $rr.setCharge($charge);
end

希望对您有所帮助,