Drools 规则 - 空检查和累积条件
Drools rule - null check and accumulate condition
我目前正在对 java 批次进行一些修复,其中 运行 一组 Drools
(耶!)规则。
我必须修正的规则是:
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 Set<LastData>
且 LastData
具有 org.joda.time.DateTime
[= 的简单 bean 28=]lastDate 属性.
问题:如何在 $person.personLastDatas
为空时应用规则的情况下插入新条件?
类似于:
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
您应该有两个规则,一个用于空条件,一个用于比较日期。
这里是空条件规则;它验证 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 逻辑,这很好地表明您应该有两个规则。由于这两条规则不能同时为真,您只能插入一个此类条件。
也就是说,如果您使用的是现代版本的流口水,则可以使用条件和命名空间结果。 documentation 对此进行了详细介绍(我已链接 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项目,但应该是类似的。)
基本上,这种语法虽然难以阅读,但可以让您处理这些类型的 repetitive/partial 共享大小写规则。通常建议在您有两个规则的情况下使用它们,其中一个规则扩展另一个规则,因此常见条件的子集可以触发一个结果,而完整的条件集可以触发另一个结果。这与您在这里所拥有的不完全相同,但可以针对您的用例对功能进行混杂处理。
break
关键字告诉引擎一旦满足条件就停止评估左侧;还有一个 do
关键字,允许继续评估。
选项 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
选项 2
使聚合函数按您需要的方式工作。 根据您在规则中表达的业务,null 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", "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));
}
}
测试输出
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
函数来源
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;
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
我目前正在对 java 批次进行一些修复,其中 运行 一组 Drools
(耶!)规则。
我必须修正的规则是:
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 Set<LastData>
且 LastData
具有 org.joda.time.DateTime
[= 的简单 bean 28=]lastDate 属性.
问题:如何在 $person.personLastDatas
为空时应用规则的情况下插入新条件?
类似于:
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
您应该有两个规则,一个用于空条件,一个用于比较日期。
这里是空条件规则;它验证 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 逻辑,这很好地表明您应该有两个规则。由于这两条规则不能同时为真,您只能插入一个此类条件。
也就是说,如果您使用的是现代版本的流口水,则可以使用条件和命名空间结果。 documentation 对此进行了详细介绍(我已链接 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项目,但应该是类似的。)
基本上,这种语法虽然难以阅读,但可以让您处理这些类型的 repetitive/partial 共享大小写规则。通常建议在您有两个规则的情况下使用它们,其中一个规则扩展另一个规则,因此常见条件的子集可以触发一个结果,而完整的条件集可以触发另一个结果。这与您在这里所拥有的不完全相同,但可以针对您的用例对功能进行混杂处理。
break
关键字告诉引擎一旦满足条件就停止评估左侧;还有一个 do
关键字,允许继续评估。
选项 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
选项 2
使聚合函数按您需要的方式工作。 根据您在规则中表达的业务,null 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", "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));
}
}
测试输出
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
函数来源
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;
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