【问题标题】:Drools: get the 3 most recent EventsDrools:获取最近的 3 个事件
【发布时间】:2012-08-18 21:21:52
【问题描述】:

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

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

我在我的知识库中插入了数百个 Event 实例,现在我想要获取标记为 "OK" 的 3 个最新事件。我想出了以下代码,它有效:

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 &gt; 10 获取五个最近的事件怎么办?我最终会复制粘贴很多代码,我不想这样做:)。 此外,代码对我来说看起来不是很“漂亮”。我真的不喜欢重复的not Event... 约束,我也不喜欢一遍又一遍地重复相同的标签条件。 (这个例子是我真实应用程序的一个高度简化的版本,其中的条件实际上要复杂得多。)

如何改进此代码?

【问题讨论】:

    标签: java drools


    【解决方案1】:

    假设您使用的是 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
    

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

    【讨论】:

    • 谢谢!这看起来很有希望。但是,我无法让它工作;它根本什么都不做,并且 drl 文件中的其他规则也停止触发。我已经设置了EventProcessingOption.STREAM 并在drl 文件中用@role( event ) 声明了我的Event 类。我可能还缺少其他东西;你有什么想法吗?
    • 假设您使用的是 Drools 5.4,您可能还需要使用 @expires( x ) 为您的事件定义一个过期时间,其中 x 是您希望将事件保存在内存中的时间。
    • 我发现出了什么问题:从最近的 3 个事件中,它需要带有标签“OK”的那些,但我需要的是带有标签“OK”的 3 个最近的事件,不管带有可能与它们交错的其他标签的项目。所以我没有收到任何事件,因为在最后 3 个中没有任何“OK”事件。所以,不幸的是,这仍然不能解决我的问题......但它很接近 :)。
    • 没有注意到您编辑了答案;对不起!我什至开始赏金……越来越近了,但仍然不是我想要的:我对“最后 3 个事件中的 OK 事件”不感兴趣,但对“所有事件中的最后 3 个 OK 事件”不感兴趣事件”......不过,我会给你赏金,因为没有其他答案,而且你已经为此付出了很多努力:)。
    【解决方案2】:

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

    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 事件触发。如果会话不包含任何事件,它也会在开始时以空列表触发一次。根据documentation,滑动窗口立即开始匹配,定义滑动窗口并不意味着规则必须等待滑动窗口“满”才能匹配。例如,计算 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-06
      • 2016-12-28
      • 1970-01-01
      • 2015-07-27
      • 2014-03-21
      • 1970-01-01
      相关资源
      最近更新 更多