流口水规则-空检查并累积条件

时间:2020-05-19 12:16:37

标签: java drools

我目前正在对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>的简单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

2 个答案:

答案 0 :(得分: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逻辑,则很好地表明您应该有两个规则。由于这两个规则不能同时成立,因此只能插入一个这种类型的条件。

话虽如此,如果您使用的是现代版本的流口水,则可以使用有条件的命名空间后果。 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项目,但应该类似。)

虽然很难理解,但基本上该语法将使您能够处理这种重复/部分共享的大小写规则。通常建议在以下情况下使用它们:两条规则,一条规则扩展另一条规则,因此,一部分通用条件可以触发一个结果,而整套条件可以触发另一个结果。这不是您所拥有的,但是可以针对您的用例对功能进行混用。

关键字break告诉引擎,一旦满足条件,则停止评估左侧;还有一个do关键字,可以继续进行评估。

答案 1 :(得分:1)

选项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视为可能的最小值。如果所有其他规则都为true,则可以将此逻辑封装在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