5

我正在做一个小型 Drools 项目,因为我想了解更多关于使用规则引擎的信息。我有一个名为的类Event,它具有以下字段:

  • String tag;可以是任何字符串的标签。
  • long millis;时间戳。(实际上,这是从同样位于的 JodaTimeLocalDate字段转换而来的Event。)
  • int value;我想要推理的价值。

我将数百个Event实例插入到我的知识库中,现在我想获取 3 个最近的事件,这些事件都带有"OK". 我想出了以下代码,该代码有效:

rule "Three most recent events tagged with 'OK'"
when
    $e1 : Event( tag == "OK",
                 $millis1 : millis )
    $e2 : Event( tag == "OK",
                 millis < $millis1, $millis2 : millis )
    $e3 : Event( tag == "OK",
                 millis < $millis2, $millis3 : millis )

    not Event( tag == "OK",
               millis > $millis1 )
    not Event( tag == "OK",
               millis > $millis2 && millis < $millis1 )
    not Event( tag == "OK",
               millis > $millis3 && millis < $millis2 )
then
  # Do something with $e1.value, $e2.value and $e3.value
end

但我觉得应该有更好的方法来做到这一点。这非常冗长并且不容易重复使用:例如,如果我想用 获取五个最近的事件value > 10怎么办?我最终会复制粘贴很多代码,我不想这样做:)。此外,代码对我来说看起来不是很“漂亮”。我真的不喜欢重复的not Event...约束,我也不喜欢一遍又一遍地重复相同的标签条件。(这个例子是我真实应用程序的一个高度简化的版本,其中的条件实际上要复杂得多。)

如何改进此代码?

4

2 回答 2

4

假设您正在使用 STREAM 事件处理模式并且您的事件在流中排序:

rule "3 most recent events"
when
    accumulate( $e : Event( tag == "OK" ) over window:length(3),
                $events : collectList( $e ) )
then
    // $events is a list that contains your 3 most recent 
    // events by insertion order
end

===== 编辑 ====

根据您在下面的评论,这里是如何在 Drools 5.4+ 中实现您想要的:

declare window LastEvents
    Event() over window:length(3)
end

rule "OK events among the last 3 events"
when
    accumulate( $e : Event( tag == "OK" ) from window LastEvents,
                $events : collectList( $e ) )
then
    // $events is a list that contains the OK events among the last 3 
    // events by insertion order
end

仔细检查语法,因为我正在这样做,但它应该接近这个。

于 2012-08-24T15:06:33.390 回答
0

我能够像这样简化“非逻辑”

rule "Three most recent events tagged with 'OK'"
when
    $e1 : Event( tag == "OK")
    $e2 : Event( tag == "OK", millis < $e1.millis )
    $e3 : Event( tag == "OK", millis < $e2.millis )
    not Event( this != $e2, tag == "OK", $e3.millis < millis, millis < $e1.millis )
then
    System.out.printf("%s - %s - %s%n", $e1, $e2, $e3);
end

没有什么关于清洁活动的说法。通常这是可取的,因此您可以通过删除最后一个事件来实现相同的逻辑:

rule "Three most recent events tagged with 'OK'"
when
    $e1 : Event( tag == "OK")
    $e2 : Event( tag == "OK", millis < $e1.millis )
    $e3 : Event( tag == "OK", millis < $e2.millis )
then
    System.out.printf("%s - %s - %s%n", $e1, $e2, $e3);
    retract ($e3)
end

假设每秒你将插入一个事件一个“OK”另一个空“”,这是测试:

@DroolsSession("classpath:/test3.drl")
public class PlaygroundTest {
    
    @Rule
    public DroolsAssert drools = new DroolsAssert();
    
    @Test
    public void testIt() {
        for (int i = 0; i < 10; i++) {
            drools.advanceTime(1, SECONDS);
            drools.insertAndFire(new Event(i % 2 == 0 ? "OK" : "", i));
        }
    }
}

所有三个变体都将产生相同的触发逻辑:

00:00:01 --> inserted: Event[tag=OK,millis=0]
00:00:01 --> fireAllRules
00:00:02 --> inserted: Event[tag=,millis=1]
00:00:02 --> fireAllRules
00:00:03 --> inserted: Event[tag=OK,millis=2]
00:00:03 --> fireAllRules
00:00:04 --> inserted: Event[tag=,millis=3]
00:00:04 --> fireAllRules
00:00:05 --> inserted: Event[tag=OK,millis=4]
00:00:05 --> fireAllRules
00:00:05 <-- 'Three most recent events tagged with 'OK'' has been activated by the tuple [Event, Event, Event]
OK4 - OK2 - OK0
00:00:06 --> inserted: Event[tag=,millis=5]
00:00:06 --> fireAllRules
00:00:07 --> inserted: Event[tag=OK,millis=6]
00:00:07 --> fireAllRules
00:00:07 <-- 'Three most recent events tagged with 'OK'' has been activated by the tuple [Event, Event, Event]
OK6 - OK4 - OK2
00:00:08 --> inserted: Event[tag=,millis=7]
00:00:08 --> fireAllRules
00:00:09 --> inserted: Event[tag=OK,millis=8]
00:00:09 --> fireAllRules
00:00:09 <-- 'Three most recent events tagged with 'OK'' has been activated by the tuple [Event, Event, Event]
OK8 - OK6 - OK4
00:00:10 --> inserted: Event[tag=,millis=9]
00:00:10 --> fireAllRules

变体window:length(3)也将处理最后 3 个 OK 事件。但一开始就不同:它也会为 1 和 2 的第一个 OK 事件触发。如果会话不包含任何事件,它也会在开始时以空列表触发一次。根据文档,滑动窗口立即开始匹配,定义滑动窗口并不意味着规则必须等待滑动窗口“满”才能匹配。例如,计算 window:length(10) 上事件属性平均值的规则将立即开始计算平均值,对于无事件它将从 0(零)开始,并在事件到达时更新平均值逐个。

00:00:01 <-- 'Three most recent events tagged with 'OK'' has been activated by the tuple [InitialFactImpl, UnmodifiableRandomAccessList]
[]
00:00:01 --> inserted: Event[tag=OK,millis=0]
00:00:01 --> fireAllRules
00:00:01 <-- 'Three most recent events tagged with 'OK'' has been activated by the tuple [InitialFactImpl, UnmodifiableRandomAccessList]
[OK0]
00:00:02 --> inserted: Event[tag=,millis=1]
00:00:02 --> fireAllRules
00:00:03 --> inserted: Event[tag=OK,millis=2]
00:00:03 --> fireAllRules
00:00:03 <-- 'Three most recent events tagged with 'OK'' has been activated by the tuple [InitialFactImpl, UnmodifiableRandomAccessList]
[OK0, OK2]
00:00:04 --> inserted: Event[tag=,millis=3]
00:00:04 --> fireAllRules
00:00:05 --> inserted: Event[tag=OK,millis=4]
00:00:05 --> fireAllRules
00:00:05 <-- 'Three most recent events tagged with 'OK'' has been activated by the tuple [InitialFactImpl, UnmodifiableRandomAccessList]
[OK0, OK2, OK4]
00:00:06 --> inserted: Event[tag=,millis=5]
00:00:06 --> fireAllRules
00:00:07 --> inserted: Event[tag=OK,millis=6]
00:00:07 --> fireAllRules
00:00:07 <-- 'Three most recent events tagged with 'OK'' has been activated by the tuple [InitialFactImpl, UnmodifiableRandomAccessList]
[OK2, OK4, OK6]
00:00:08 --> inserted: Event[tag=,millis=7]
00:00:08 --> fireAllRules
00:00:09 --> inserted: Event[tag=OK,millis=8]
00:00:09 --> fireAllRules
00:00:09 <-- 'Three most recent events tagged with 'OK'' has been activated by the tuple [InitialFactImpl, UnmodifiableRandomAccessList]
[OK4, OK6, OK8]
00:00:10 --> inserted: Event[tag=,millis=9]
00:00:10 --> fireAllRules
于 2020-03-11T20:14:30.053 回答