Search code examples
droolsdrools-fusion

Drools: Firing a rule if a set of events has n distinct values for a property


Suppose we have an Event type that has 3 properties: constraint, common, and distinct. The goal is to write a rule in Drools that fires when a subset of Events exists that meets the following criteria:

  • Events happened in the last t seconds; and
  • Events Have a previously known value for constraint property; and
  • Share a previously unknown value for common property; and
  • There are at least n different values for distinct property

If the rule fires, we need the set of participating events for further processing.

How do you advise we approach this problem?

Note 1: This question is somewhat similar to link, and Steve's answer there seems promising but is incomplete.

Note 2: Performance is of the essence. We have successfully developed rules that do this task, but they reduce the performance of the whole rule-base dramatically, and thus are unacceptable.

Edit 1: The current (poorly performing) solution looks like this:

rule ""
when
    $event : Event(constraint == CONSTANT_VALUE)
    $events : ArrayList() from collect(
            Event(constraint == CONSTANT_VALUE, 
            common == $event.common)
            over window:time( t ))
    $distinctVals : Set( size >= n ) from accumulate(Event($d : distinct) from $events, collectSet($d))
then
    // process $events
end

Solution

  • Drools isn't meant to be used for computations that require repeated evaluations of a potentially big set of facts. In such a situation you'll have to offload the details to some Java code.

    class Collector {
        String constraint;
        String common;
        List<Event> events = ...; // or Queue
        Map<String,List<Event>> dist2events = ...;
        int diversity;
    
        public void addEvent( Event e ){
            // 1. remove Event sets X from events older than t
            // 2. remove all members of X from dist2events,
            //    delete map elements where the value list is empty
            events.add( e );
            List<Event> ofDistinct = dist2events.get( e.getDistinct() );
            if( ofDistinct == null ){
                ofDistinct = new ArrayList<Event>(); 
                dist2events.put( ofDistinct );
            }
            ofDistinct.add( e );
            diversity = dist2events.keySet().size();
        }
    }
    
    
    rule createCollector
    when
        Event( $constraint: constraint, $common: common )
        not Collector( constraint == constraint, common == $common )
    then
        insert( new Collector( $constraint, $common ) );
    end
    
    rule addEvent
    when
        $event: Event( $constraint: constraint, $common: common )
        $collector: Collector( constraint == constraint,
                               common == $common,
                               events not contains $event )
    then
        modify( $collector ){ addEvent( $event ) }
    end
    
    rule MoreThan
    when
        Collector( $constraint: constraint, $common: common,
                   $events: events,
                   diversity >= n )  // maybe only when n-1 to n?
    then
        ... process $events
    end
    

    You'll have to decide whether you want this rule to fire only when the threshold n is exceeded or whenever the set changes while diversity is >= n.

    You might want to add a rule to remove empty Collectors.