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
首先是几个技术细节:
- 不要限定具有 class 个名称的方法调用。
- 要引用字段,仅字段名称就足够了 - 无需调用 getter.
- 不需要在最外层使用
and
,这是隐含的。
- 您可以将变量绑定到字段并在单个构造中为该字段编写断言。
- 最好使用
modify
更新事实。
- 有运算符
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
不太安全。查看上述文档以了解其他选项。
我想触发所有规则并退出。在我的示例中,我只有 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
首先是几个技术细节:
- 不要限定具有 class 个名称的方法调用。
- 要引用字段,仅字段名称就足够了 - 无需调用 getter.
- 不需要在最外层使用
and
,这是隐含的。 - 您可以将变量绑定到字段并在单个构造中为该字段编写断言。
- 最好使用
modify
更新事实。 - 有运算符
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
不太安全。查看上述文档以了解其他选项。