英文:
Drools rule - null check and accumulate condition
问题
我目前正在对一个运行一组Drools
(呃!)规则的Java批处理进行一些修复。
我需要修复的规则如下:
rule "Insert a too old condition"
salience -1
when
$person : Person()
$tooOldInstant : DateTime() from now.minusDays(10)
DateTime( this < $tooOldInstant ) from accumulate (
LastData( $date : lastDate ) from $person.personLastDatas,
maxValue($date)
)
then
insert(new Condition("submitTooOldCondition"));
end
为了简化,假设Person
是一个带有personLastDatas
属性的简单Bean,而LastData
具有org.joda.time.DateTime
的lastDate
属性。
问题:如果$person.personLastDatas
为null,如何插入一个新条件使规则生效?
类似于:
rule "Insert a too old condition modified"
salience -1
when
$person : Person()
$tooOldInstant : DateTime() from now.minusDays(10)
$maxLastDate : DateTime() from accumulate (
LastData( $date : lastDate ) from $person.personLastDatas,
maxValue($date)
)
($maxLastDate == null || $maxLastDate < $tooOldInstant)
then
insert(new Condition("submitTooOldCondition"));
end
英文:
I'm currently doing some fix on a java batch which run a set of Drools
(yeuch!) rules.
The rule I have to fix is this:
rule "Insert a too old condition"
salience -1
when
$person : Person()
$tooOldInstant : DateTime() from now.minusDays(10)
DateTime( this < $tooOldInstant ) from accumulate (
LastData( $date : lastDate ) from $person.personLastDatas,
maxValue($date)
)
then
insert(new Condition("submitTooOldCondition"));
end
where for simplification Person
is a simple bean with a personLastDatas Set<LastData>
AND LastData
has a org.joda.time.DateTime
lastDate property.
Question: How do I insert a new condition where if $person.personLastDatas
is null the rule apply?
Something like:
rule "Insert a too old condition modified"
salience -1
when
$person : Person()
$tooOldInstant : DateTime() from now.minusDays(10)
$maxLastDate : DateTime() from accumulate (
LastData( $date : lastDate ) from $person.personLastDatas,
maxValue($date)
)
($maxLastDate == null || $maxLastDate < $tooOldInstant)
then
insert(new Condition("submitTooOldCondition"));
end
答案1
得分: 1
你应该有两个规则,一个用于处理空条件,另一个用于比较日期。
以下是空条件规则;它验证了Person存在,但没有personLastDatas
属性:
rule "Insert a too old condition modified - null case"
salience -1
when
$person: Person( personLastDatas == null )
then
insert(new Condition("submitTooOldCondition"));
end
你现有的规则已足够处理日期比较检查。
一般来说,如果你发现自己试图在规则的两边进行复杂的 if-else 逻辑,那是一个很好的指示,你应该有两个规则。因为这两个规则不能同时为真,你只会插入这种类型的一种条件。
话虽如此,如果你正在使用现代版本的 Drools,你可以使用条件和命名空间后果。文档详细介绍了这个功能(我链接的是 7.37.0.Final 版本;大多数近期的 7.30+ 版本都有这个功能)。以下是你的规则可能是什么样子的示例:
rule "Insert a too old condition"
salience -1
when
$person : Person( $lastDatas: personLastDatas )
if ( $lastDatas == null ) break[noData]
$tooOldInstant : DateTime() from now.minusDays(10)
DateTime( this < $tooOldInstant ) from accumulate (
LastData( $date : lastDate ) from $person.personLastDatas,
maxValue($date)
)
then
insert(new Condition("submitTooOldCondition"));
then[noData]
// any special logic for the null case goes here
insert(new Condition("submitTooOldCondition"));
end
(这是伪代码;我在这台电脑上没有 Drools 项目,但应该类似。)
基本上,尽管这种语法可能更难阅读,但它将允许你处理这些重复/部分共享的案例规则。通常建议在一个规则扩展另一个规则的情况下使用它们,这样共同条件的子集可以触发一个结果,而条件的完整集合可以触发另一个结果。虽然这与你在这里的情况不完全相同,但该功能可以为你的用例定制。
break
关键字告诉引擎一旦满足条件就停止评估左侧条件;还有一个 do
关键字允许继续评估。
英文:
You should have two rules, one for the null condition and one for the one that compares the dates.
Here is the null condition rule; it verifies that the Person exists but that it has no personLastDatas
property:
rule "Insert a too old condition modified - null case"
salience -1
when
$person: Person( personLastDatas == null )
then
insert(new Condition("submitTooOldCondition"));
end
Your existing rule is sufficient for the date comparison check.
In general, if you find yourself trying to do complex if-else logic on either side of the rule, it's a good indication that you should have two rules. Since these two rules cannot both be true, you'll only have one condition of this type inserted.
That being said, if you're using a modern version of drools, you can use conditional and namespaced consequences. The documentation covers this in detail (I've linked 7.37.0.Final; most of the recent 7.30+ versions have this functionality.) Below is an example of what your rule might look like:
rule "Insert a too old condition"
salience -1
when
$person : Person( $lastDatas: personLastDatas )
if ( $lastDatas == null ) break[noData]
$tooOldInstant : DateTime() from now.minusDays(10)
DateTime( this < $tooOldInstant ) from accumulate (
LastData( $date : lastDate ) from $person.personLastDatas,
maxValue($date)
)
then
insert(new Condition("submitTooOldCondition"));
then[noData]
// any special logic for the null case goes here
insert(new Condition("submitTooOldCondition"));
end
(This is pseudo-code; I don't have a drools project on this computer but it should be similar.)
Basically this syntax, though harder to read, will allow you to handle these sorts of repetitive/partial shared case rules. They're usually recommended for cases where you have two rules, where one extends the other, so a subset of the common conditions can trigger one consequence, while the full set of the conditions can trigger another consequence. That's not quite what you have here, but the functionality can be bastardized for your use case.
The break
keyword tells the engine to stop evaluating the left hand side once the condition is met; there's also a do
keyword which allows for continued evaluation.
答案2
得分: 1
Option 1
规则 "插入过时条件"
优先级 -1
当
Person(personLastDatas == null)
或
$person : Person()
并且 $tooOldInstant : DateTime() from now.minusDays(10)
并且 DateTime( this < $tooOldInstant ) from accumulate (
LastData( $date : lastDate ) from $person.personLastDatas,
maxValue($date)
)
则
System.out.println("提交过时条件");
end
Option 2
使您的聚合函数按您需要的方式工作。_根据规则中表达的业务,空的 DateTime 应该被视为最低可能值。_ 如果对于其他所有规则都是如此,您可以将此逻辑封装在您的 `maxValue` 函数中。
public Object getResult(HashSet<DateTime> context) throws Exception {
return context.isEmpty() ? new DateTime(0) /*null*/ : context.iterator().next();
}
使用上述逻辑,您的原始规则将按预期工作,无需任何修改。
---
测试
@DroolsSession(resources = "classpath:/test.drl")
public class PlaygroundTest {
@Rule
public DroolsAssert drools = new DroolsAssert();
@Test
@TestRules(expectedCount = { "2", "插入过时条件" })
public void testIt() {
drools.setGlobal("now", now());
drools.insertAndFire(new Person(newHashSet(new LastData(now().minusDays(100)), new LastData(now().minusDays(5)))));
drools.insertAndFire(new Person(newHashSet(new LastData(now().minusDays(100)), new LastData(now().minusDays(15)))));
drools.insertAndFire(new Person(null));
}
}
测试输出
00:00:00 --> inserted: Person[personLastDatas=[org.droolsassert.LastData@25243bc1, org.droolsassert.LastData@1e287667]]
00:00:00 --> 触发所有规则
00:00:00 --> inserted: Person[personLastDatas=[org.droolsassert.LastData@76f10035, org.droolsassert.LastData@5ab9b447]]
00:00:00 --> 触发所有规则
00:00:00 <-- '插入过时条件' 已被元组 [Person, DateTime, DateTime] 激活
提交过时条件
00:00:00 --> inserted: Person[personLastDatas=<null>]
00:00:00 --> 触发所有规则
00:00:00 <-- '插入过时条件' 已被元组 [Person] 激活
提交过时条件
函数源代码
public class MaxValueAccumalateFunction implements AccumulateFunction<HashSet<DateTime>> {
@Override
public void writeExternal(ObjectOutput out) throws IOException {
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
}
@Override
public HashSet<DateTime> createContext() {
return new HashSet<>();
}
@Override
public void init(HashSet<DateTime> context) throws Exception {
}
@Override
public void accumulate(HashSet<DateTime> context, Object value) {
if (context.isEmpty() || context.iterator().next().isBefore((DateTime) value)) {
context.clear();
context.add((DateTime) value);
}
}
@Override
public void reverse(HashSet<DateTime> context, Object value) throws Exception {
}
@Override
public Object getResult(HashSet<DateTime> context) throws Exception {
return context.isEmpty() ? new DateTime(0) /*null*/ : context.iterator().next();
}
@Override
public boolean supportsReverse() {
return false;
}
@Override
public Class<?> getResultType() {
return null;
}
}
规则源代码
import org.joda.time.DateTime;
import accumulate org.droolsassert.MaxValueAccumalateFunction maxValue;
global DateTime now;
规则 "插入过时条件"
优先级 -1
当
Person(personLastDatas == null)
或
$person : Person()
并且 $tooOldInstant : DateTime() from now.minusDays(10)
并且 DateTime( this < $tooOldInstant ) from accumulate (
LastData( $date : lastDate ) from $person.personLastDatas,
maxValue($date)
)
则
System.out.println("提交过时条件");
end
请注意,代码中的注释已经被移除。如果您需要保留注释,请在翻译后添加。
英文:
Option 1
rule "Insert a too old condition"
salience -1
when
Person(personLastDatas == null)
or
$person : Person()
and $tooOldInstant : DateTime() from now.minusDays(10)
and DateTime( this < $tooOldInstant ) from accumulate (
LastData( $date : lastDate ) from $person.personLastDatas,
maxValue($date)
)
then
System.out.println("submitTooOldCondition");
end
Option 2
Make your aggregate function work the way you need. From the business you expressed in the rule, null DateTime should be treated as a lowest possible value. If this is true for all other rules, you can encapsulate this logic in your maxValue
function.
public Object getResult(HashSet<DateTime> context) throws Exception {
return context.isEmpty() ? new DateTime(0) /*null*/ : context.iterator().next();
}
With the logic above your original rule will work as expected without any modification.
test
@DroolsSession(resources = "classpath:/test.drl")
public class PlaygroundTest {
@Rule
public DroolsAssert drools = new DroolsAssert();
@Test
@TestRules(expectedCount = { "2", "Insert a too old condition" })
public void testIt() {
drools.setGlobal("now", now());
drools.insertAndFire(new Person(newHashSet(new LastData(now().minusDays(100)), new LastData(now().minusDays(5)))));
drools.insertAndFire(new Person(newHashSet(new LastData(now().minusDays(100)), new LastData(now().minusDays(15)))));
drools.insertAndFire(new Person(null));
}
}
test output
00:00:00 --> inserted: Person[personLastDatas=[org.droolsassert.LastData@25243bc1, org.droolsassert.LastData@1e287667]]
00:00:00 --> fireAllRules
00:00:00 --> inserted: Person[personLastDatas=[org.droolsassert.LastData@76f10035, org.droolsassert.LastData@5ab9b447]]
00:00:00 --> fireAllRules
00:00:00 <-- 'Insert a too old condition' has been activated by the tuple [Person, DateTime, DateTime]
submitTooOldCondition
00:00:00 --> inserted: Person[personLastDatas=<null>]
00:00:00 --> fireAllRules
00:00:00 <-- 'Insert a too old condition' has been activated by the tuple [Person]
submitTooOldCondition
function source
public class MaxValueAccumalateFunction implements AccumulateFunction<HashSet<DateTime>> {
@Override
public void writeExternal(ObjectOutput out) throws IOException {
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
}
@Override
public HashSet<DateTime> createContext() {
return new HashSet<>();
}
@Override
public void init(HashSet<DateTime> context) throws Exception {
}
@Override
public void accumulate(HashSet<DateTime> context, Object value) {
if (context.isEmpty() || context.iterator().next().isBefore((DateTime) value)) {
context.clear();
context.add((DateTime) value);
}
}
@Override
public void reverse(HashSet<DateTime> context, Object value) throws Exception {
}
@Override
public Object getResult(HashSet<DateTime> context) throws Exception {
return context.isEmpty() ? new DateTime(0) /*null*/ : context.iterator().next();
}
@Override
public boolean supportsReverse() {
return false;
}
@Override
public Class<?> getResultType() {
return null;
}
}
rule source
import org.joda.time.DateTime;
import accumulate org.droolsassert.MaxValueAccumalateFunction maxValue;
global DateTime now;
rule "Insert a too old condition"
salience -1
when
Person(personLastDatas == null)
or
$person : Person()
and $tooOldInstant : DateTime() from now.minusDays(10)
and DateTime( this < $tooOldInstant ) from accumulate (
LastData( $date : lastDate ) from $person.personLastDatas,
maxValue($date)
)
then
System.out.println("submitTooOldCondition");
end
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论