[英]Drools rule gets executed more times than expected
I'm new to Drools and am following along the book: Mastering JBoss Drools 6我是 Drools 的新手,正在阅读这本书: Mastering JBoss Drools 6
Chapter 2 shows an example where we create a Customer
and Order
, classify customers based on their order size, give discounts and generate Coupon
s.第 2 章展示了一个示例,我们创建了一个Customer
和Order
,根据订单大小对客户进行分类,提供折扣并生成Coupon
。
The example mentions:该示例提到:
SILVER
customer客户有一个包含 5 件商品的订单,因此被归类为SILVER
客户HIGH_RANGE
item每件商品的价格都超过 500,因此被归类为HIGH_RANGE
商品SILVER
customer therefore a Coupon
is created客户是SILVER
客户,因此创建了Coupon
SILVER
customer with orders with more than 2 items为每位SILVER
级客户提供超过 2 件订单的 10% 折扣Here are the Drools rules:以下是 Drools 规则:
add-discount.drl添加折扣.drl
rule "High Range order - 10% discount"
when
$o: Order($lines: orderLines, orderLines.size >= 2, $customer: customer, discount == null)
$c: Customer(category == Customer.Category.SILVER, this == $customer)
forall(
OrderLine(this memberOf $lines, $item: item)
Item(this == $item, category == Item.Category.HIGH_RANGE)
)
then
modify($o){
setDiscount(new Discount(10.0))
}
end
classify-customer-rules.drl分类客户规则.drl
rule "Classify Customer by order size"
when
$o: Order( orderLines.size >= 5, $customer: customer )
$c: Customer(this == $customer, category == Customer.Category.NA)
then
modify($c){
setCategory(Customer.Category.SILVER)
}
end
classify-items-rules.drl分类-items-rules.drl
rule "Classify Item - Low Range"
when
$i: Item(cost < 200, category == Category.NA)
then
$i.setCategory(Category.LOW_RANGE);
update($i);
end
rule "Classify Item - Mid Range"
when
$i: Item(cost > 200 && cost < 500, category == Category.NA)
then
$i.setCategory(Category.MID_RANGE);
update($i);
end
rule "Classify Item - High Range"
when
$i: Item(cost >= 500, category == Category.NA)
then
$i.setCategory(Category.HIGH_RANGE);
update($i);
end
coupons-creation.drl优惠券-creation.drl
rule "Create Coupons for silver customer"
when
$o: Order($customer: customer)
$c: Customer(this == $customer, category == Customer.Category.SILVER)
then
Coupon x = new Coupon($c, $o, Coupon.CouponType.POINTS);
System.out.println(x);
insert(x);
end
The number of rules executed should be 8. 5 items to classify (5 rules) + 1 customer to classify (1 rule) + 1 discount to add (1 rule) + 1 coupon to create (1 rule)执行的规则数应为 8。5 个要分类的项目(5 个规则)+ 1 个要分类的客户(1 个规则)+ 1 个要添加的折扣(1 个规则)+ 1 个要创建的优惠券(1 个规则)
But the actual number of rules fired is 9. Here is the test I used to check.但实际触发的规则数是 9。 这是我用来检查的测试。 The coupon creation rule is fired twice.优惠券创建规则被触发两次。 I do not understand why.我不明白为什么。 I checked the number of coupons created after firing the rule like so:我检查了触发规则后创建的优惠券数量,如下所示:
Collection<Coupon> coupons = Util.getFactsFromSession(kieSession, Coupon.class);
coupons.forEach(x -> System.out.println(x));
and I also have a print statement in the coupon creation rule.我在优惠券创建规则中也有打印声明。
This is what I get:这就是我得到的:
12:54:47.607 [main] DEBUG org.drools.core.impl.KnowledgeBaseImpl - Starting Engine in PHREAK mode
12:54:47.730 [main] DEBUG org.drools.core.common.DefaultAgenda - State was INACTIVE is nw FIRING_ALL_RULES
12:54:47.732 [main] DEBUG org.drools.core.common.DefaultAgenda - Fire Loop
12:54:47.795 [main] DEBUG org.drools.core.common.DefaultAgenda - Fire Loop
Coupon created com.example.droolstut.model.Coupon@b14cb989
12:54:47.797 [main] DEBUG org.drools.core.common.DefaultAgenda - Fire Loop
12:54:47.797 [main] DEBUG org.drools.core.common.DefaultAgenda - Fire Loop
12:54:47.799 [main] DEBUG org.drools.core.common.DefaultAgenda - Fire Loop
12:54:47.806 [main] DEBUG org.drools.core.common.DefaultAgenda - Fire Loop
Coupon created com.example.droolstut.model.Coupon@424f842a
12:54:47.806 [main] DEBUG org.drools.core.common.DefaultAgenda - Fire Loop
12:54:47.806 [main] DEBUG org.drools.core.common.DefaultAgenda - Fire Loop
12:54:47.806 [main] DEBUG org.drools.core.common.DefaultAgenda - State was FIRING_ALL_RULES is nw HALTING
12:54:47.806 [main] DEBUG org.drools.core.common.DefaultAgenda - State was HALTING is nw INACTIVE
Coupon found: com.example.droolstut.model.Coupon@424f842a
Coupon found: com.example.droolstut.model.Coupon@424f842a
So, it looks like the rule is executed twice, but only the second coupon is found post execution.因此,看起来规则执行了两次,但执行后只找到了第二张优惠券。
But, when I run the test in debug mode, the coupon rule is executed only once, and the total number of rules fired is 8 too.但是,当我在调试模式下运行测试时,优惠券规则只执行一次,触发的规则总数也是 8。
Did I miss something?我错过了什么? I'd appreciate any help.我将不胜感激任何帮助。
By the time a book on Drools gets published, it's out of date.当一本关于 Drools 的书出版时,它已经过时了。 Drools has fantastic documentation that you should be using as your primary source, not a very old book on a very old version of Drools. Drools 有很棒的文档,您应该将其用作您的主要来源,而不是一本关于非常旧版本的 Drools 的非常古老的书。 (Current version of Drools is Drools 8. Your book is for Drools 6.) (Drools 的当前版本是 Drools 8。您的书适用于 Drools 6。)
The other reason I recommend people not use books is because they tend to be awful.我建议人们不要使用书籍的另一个原因是它们往往很糟糕。 This one is no exception这个也不例外
The reason you're firing more rules than you think is because both "High Range order - 10% discount" forces a reevaluation of working memory, and "Create Coupons for silver customer" doesn't have anything to stop it from triggering multiple times.你触发比你想象的更多规则的原因是因为“高范围订单 - 10% 折扣”强制重新评估工作 memory,而“为白银客户创建优惠券”没有任何东西可以阻止它多次触发.
"High Range..." calls 'modify' which re-fires all rules with the modified item. “High Range...”调用“修改”,它会使用修改后的项目重新触发所有规则。 Since "Create Coupon..." remains valid to fire, it will trigger twice.由于“Create Coupon...”仍然有效,因此它会触发两次。 (It'll fire once, and then once "High Range..." forces the reevaluation of working memory, it'll fire a second time.) You'll also see that there's twice as many coupons in working memory as you'd expect -- there will be 2 coupons instead of one because you fired the coupon rule twice. (它会触发一次,然后一旦“High Range...”强制重新评估 working memory,它就会触发第二次。)你还会看到 working memory 的优惠券数量是你的两倍。 d expect -- 将有 2 张优惠券而不是一张,因为您触发了优惠券规则两次。
To keep this from happening, you need to make "Create Coupon..." not valid to fire twice.为了防止这种情况发生,您需要使“创建优惠券...”无效以触发两次。 (That is, once it's fired, it can't be triggered again.) You can do this, for example, by not allowing two of the same coupon. (也就是说,一旦触发,就不能再次触发。)例如,您可以通过不允许使用两张相同的优惠券来做到这一点。 Or you could do this by only allowing one coupon.或者您可以只允许一张优惠券来做到这一点。 Depends on your use case.取决于您的用例。
One way might be:一种方法可能是:
rule "Create Coupons for silver customer"
when
$o: Order($customer: customer)
$c: Customer(category == Customer.Category.SILVER)
// don't fire if this coupon already exists; I guessed on the variable names
not( Coupon( customer == $c, order == $o, type == Coupon.CouponType.POINTS) )
then
Coupon x = new Coupon($c, $o, Coupon.CouponType.POINTS);
insert(x);
end
In the Real World, you want to keep your rules simple.在现实世界中,您希望规则简单。 Don't stick the entire kitchen sink into a single KieBase.不要将整个厨房水槽粘在一个 KieBase 中。 The single responsibility principle is key here.单一职责原则是这里的关键。 Your "Item classification" rules should be fired separately from your customer classification rules should be fired separately from your discount rules.您的“项目分类”规则应该与您的客户分类规则分开触发,应该与您的折扣规则分开触发。
Also calling update
is a great way to get yourself into infinite loops and greatly increase your memory/cpu overhead.调用update
也是让自己陷入无限循环并大大增加内存/CPU 开销的好方法。 At my last company it was considered not only a code smell and bad practice, but actually outright banned (we checked for it in code reviews.)在我上一家公司,它不仅被认为是一种代码味道和不良做法,而且实际上被彻底禁止(我们在代码审查中检查过它。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.