Слюни: получите 3 последних события

Я работаю над небольшим проектом Drools, потому что хочу узнать больше об использовании механизмов правил. У меня есть класс Event со следующими полями:

  • String tag; Тег, который может быть любой строкой.
  • long millis; Отметка времени. (На самом деле это преобразовано из поля JodaTime LocalDate, которое также находится в 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..., и мне также не нравится повторять одно и то же условие тега снова и снова. (Этот пример представляет собой сильно упрощенную версию моего реального приложения, в котором условия на самом деле намного сложнее.)

Как я могу улучшить этот код?


person jqno    schedule 23.08.2012    source источник


Ответы (2)


Предполагая, что вы используете режим обработки событий 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

Просто дважды проверьте синтаксис, поскольку я делаю это наизусть, но он должен быть близок к этому.

person Edson Tirelli    schedule 24.08.2012
comment
Спасибо! Это выглядит многообещающе. Однако я не могу заставить его работать; он просто ничего не делает, и другие правила в файле drl тоже перестали срабатывать. Я установил EventProcessingOption.STREAM и объявил свой класс Event с @role( event ) в файле drl. Я, вероятно, упускаю что-то еще; у тебя есть идеи? - person jqno; 26.08.2012
comment
Предполагая, что вы используете Drools 5.4, вам, вероятно, также потребуется определить срок действия для ваших событий с помощью @expires( x ), где x — это время, в течение которого вы хотите хранить события в памяти. - person Edson Tirelli; 27.08.2012
comment
Я обнаружил, что пошло не так: из 3 самых последних событий он берет те, что с тегом OK, но мне нужны 3 самых последних события с тегом OK, независимо от элементов с другими тегами, которые могут чередоваться с ними. . Итак, я не получал никаких событий, потому что среди последних 3 просто не было ни одного нормального события. Так что, к сожалению, это все еще не решает мою проблему... но это близко :). - person jqno; 27.08.2012
comment
Не заметил, как вы отредактировали свой ответ; Прости! И я даже запустил баунти на этот... Приближаюсь, но все равно не совсем то, что ищу: меня интересуют не ОК события среди последних 3 событий, а 3 последних ОК события среди всех событий.. , Однако я дам вам награду, поскольку других ответов нет, а вы уже приложили к этому столько усилий :). - person jqno; 16.09.2012

Я смог упростить «не логику» вот так

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

Скажем, каждую секунду вы будете вставлять событие одно «ОК», другое «пусто», вот тест:

@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. Он также будет запущен один раз с пустым списком в начале, если сеанс не содержит никаких событий. Согласно документации скользящие окна начинают сопоставляться немедленно, и определение скользящего окна не означает, что правило должно ждать, пока скользящее окно заполнится, чтобы выполнить сопоставление. Например, правило, которое вычисляет среднее значение свойства события в окне: длина (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
person Mike    schedule 11.03.2020