Drools:规则 2 未触发,而规则 1 在内部触发(进入循环)

Drools: rule 2 is not fired, while rule 1 is fired internally (enters in the loop)

我想触发所有规则并退出。在我的示例中,我只有 2 条规则,但它们是相互关联的,即规则 2 应在规则 1 之后触发。 问题是它只打印出规则 1 的输出。而且它看起来像是进入循环并在内部打印出相同的消息 (Client is interested in skiing)。

package com.sample;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.kie.api.KieServices;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;

/**
 * This is a sample class to launch a rule.
 */
public class TestSimpleRules {

    public static final void main(String[] args) {
        try {
            // load up the knowledge base
            KieServices ks = KieServices.Factory.get();
            KieContainer kContainer = ks.getKieClasspathContainer();
            KieSession kSession = kContainer.newKieSession("ksession-rules");

            // go !
            Client client = new Client();
            Season season = new Season();
            client.addProduct("snowboard");
            client.addProduct("ski poles");
            season.setSeason("winter");
            kSession.insert(client);
            kSession.insert(season);
            kSession.fireAllRules();
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }

    public static class Client {

        private Set<String> buyingHistory;
        private String interestedIn;

        public Client()
        {
            buyingHistory = new HashSet<String>();
        }

        public Set<String> getBuyingHistory() {
            return this.buyingHistory;
        }

        public void addProduct(String product) {
            this.buyingHistory.add(product);
        }

        public String getInterestedIn() {
            return this.interestedIn;
        }

        public void setInterestedIn(String interestedI) {
            this.interestedIn = interestedIn;
        }

    }

    public static class Season {

        private String currentSeason;

        public String getSeason() {
            return this.currentSeason;
        }

        public void setSeason(String season) {
            this.currentSeason = season;
        }

    }

}

规则:

package com.javacodegeeks.drools;

import com.sample.TestSimpleRules.Client;
import com.sample.TestSimpleRules.Season;

rule "Rule #1"
    when
        c : Client( Client.getBuyingHistory() contains "snowboard", thisBuyingHistory : buyingHistory) and
        s: Season( Season.getSeason() == "winter" )
    then
        System.out.println( thisBuyingHistory );
        c.setInterestedIn("skiing");
        System.out.println("Client is interested in skiing");
        update( c );
end

rule "Rule #2"
    when
       c: Client( Client.getInterestedIn() == "skiing" && !(Client.getBuyingHistory() contains "ski jacket"), thisBuyingHistory : buyingHistory) 
    then
       System.out.println("Ski jacket is recommended");
end

首先是几个技术细节:

  1. 不要限定具有 class 个名称的方法调用。
  2. 要引用字段,仅字段名称就足够了 - 无需调用 getter.
  3. 不需要在最外层使用and,这是隐含的。
  4. 您可以将变量绑定到字段并在单个构造中为该字段编写断言。
  5. 最好使用 modify 更新事实。
  6. 有运算符not contains,所以不需要写取反约束

(以上都可以从Drools手册中了解到。)

rule "Rule #1"
when
    c: Client( buyHist: buyingHistory contains "snowboard" ) 
    s: Season( season == "winter" )
then
    System.out.println( buyHist );
    modify( c ){ setInterestedIn("skiing") }
    System.out.println("Client is interested in skiing");
end

rule "Rule #2"
when
    c: Client( interestedIn == "skiing",
               buyingHistory not contains "ski jacket" ) 
then
    System.out.println("Ski jacket is recommended");
end

触发第一条规则的循环是由于字段 interestedIn 的修改导致重新评估所有引用某些 Client 事实的规则。由于 "Rule #1" 中没有取消其触发资格的约束,因此它再次触发。一个安全的补救措施是限制规则仅在 interestedIn 不等于 "skiing":

时触发
rule "Rule #1"
when
    c: Client( buyHist: buyingHistory contains "snowboard"
               interestedIn != "skiing" ) 
    s: Season( season == "winter" )
then ... end

还有其他方法可以处理这种情况。规则属性 no-loop 不太安全。查看上述文档以了解其他选项。