Chapter 5. Event Pattern Reference

5.1. Event Pattern Overview

Event patterns match when an event or multiple events occur that match the pattern's definition. Patterns can also be time-based.

Pattern expressions can consist of filter expressions combined with pattern operators. Expressions can contain further nested pattern expressions by including the nested expression(s) in () round brackets.

There are 5 types of operators:

  1. Operators that control pattern finder creation and termination: every

  2. Logical operators: and, or, not

  3. Temporal operators that operate on event order: -> (followed-by)

  4. Guards are where-conditions that filter out events and cause termination of the pattern finder. Examples are timer:within.

  5. Observers observe time events as well as other events. Examples are timer:interval and timer:at.

5.2. How to use Patterns

5.2.1. Pattern Syntax

The createPattern method on the EPAdministrator administrative interface creates pattern statements for the given pattern expression string.

This is an example pattern expression that matches on every ServiceMeasurement events in which the value of the latency event property is over 20 seconds, and on everyServiceMeasurement events in which the success property is false. Either one or the other condition must be true for this pattern to matches.

every ( spike=ServiceMeasurement(latency>20000) or error=ServiceMeasurement(success=false) )

The Java code to create this trigger is below.

EPAdministrator admin = EPServiceProviderManager.getDefaultProvider().getEPAdministrator();

String eventName = ServiceMeasurement.class.getName();

EPStatement myTrigger = admin.createPattern(
    "every ( spike=" + eventName + "(latency>20000) or error=" + eventName + "(success=false) )");

The pattern expression starts with an every operator to indicate that the pattern should fire for every matching events and not just the first matching event. Within the every operator in round brackets is a nested pattern expression using the or operator. The left hand of the or operator is a filter expression that filters for events with a high latency value. The right hand of the operator contains a filter expression that filters for events with error status. Filter expressions are explained in Section 5.3, “Filter Expressions”.

5.2.2. Subscribing to Pattern Events

When a pattern fires it publishes one or more events to any listeners to the pattern statement. The listener interface is the net.esper.client.UpdateListener interface.

The example below shows an anonymous implementation of the net.esper.client.UpdateListener interface. We add the anonymous listener implementation to the myPattern statement created earlier. The listener code simply extracts the underlying event class.

myPattern.addListener(new UpdateListener()
{
	public void update(EventBean[] newEvents, EventBean[] oldEvents)
	{
		ServiceMeasurement spike = (ServiceMeasurement) newEvents[0].get("spike");
		ServiceMeasurement error = (ServiceMeasurement) newEvents[0].get("error");
		... // either spike or error can be null, depending on which occured
		... // add more logic here
	}
});

Listeners receive an array of EventBean instances in the newEvents parameter. There is one EventBean instance passed to the listener for each combination of events that matches the pattern expression. At least one EventBean instance is always passed to the listener.

The properties of each EventBean instance contain the underlying events that caused the pattern to fire, if events have been named in the filter expression via the name=eventType syntax. The property name is thus the name supplied in the pattern expression, while the property type is the type of the underlying class, in this example ServiceMeasurement.

5.2.3. Pulling Data from Patterns

Data can also be pulled from pattern statements via the iterator() method. If the pattern had fired at least once, then the iterator returns the last event for which it fired. The hasNext() method can be used to determine if the pattern had fired.

if (myPattern.iterator().hasNext())
{
	ServiceMeasurement event = (ServiceMeasurement) view.iterator().next().get("alert");
    ... // some more code here to process the event
}
else
{
    ... // no matching events at this time
}

5.3. Filter Expressions

This chapter outines how to filter events based on their properties.

The simplest form of filter is a filter for events of a given type. This filter matches any event of that type regardless of the event's properties. This example assumes that the name RfidEvent has been mapped via Configuration to a Java fully qualified class name representing the event.

RfidEvent

The fully-qualified class name can be used as well.

com.mypackage.myevents.RfidEvent

The filtering criteria to be applied to events of that type are placed within paranthesis. If there are no filter criteria the paranthesis can be left off.

mypackage.RfidEvent(category="Perishable")

The supported filter operators are

  • equals =

  • comparison operators < , > , >=, <=

  • ranges use the keyword in and round (...) or square brackets []

Ranges come in the following 4 varieties. The use of round () or square [] bracket dictates whether an endpoint is included or excluded.

  • Open ranges that contain neither endpoint (low, high)

  • Closed ranges that contain both endpoints [low, high]

  • Half-open ranges that contain the low endpoint but not the high endpoint [low, high)

  • Half-closed ranges that contain the high endpoint but not the low endpoint (low, high]

Filter criteria are listed in a comma-separated format. In the example below we look for RfidEvent events with a grade property between 1 and 2 (endpoints included), a price less then 1, and a category of "Perishable".

mypackage.RfidEvent(category="Perishable", price<1.00, grade in [1, 2])

Filter criteria can also refer to events maching prior named events in the same expression. Below pattern is an example in which the pattern matches once for every RfidEvent that is preceded by an RfidEvent with the same item id.

every A=mypackage.RfidEvent -> B=mypackage.RfidEvent(itemId=A.itemId)

The syntax shown above allows filter criteria to reference prior results by specifying the event name and event property. This syntax can be used with all filter operators.

Some limitations of filters are:

  • Range and comparison operators require the event property to be of a numeric type.

  • Null values in filter criteria are currently not allowed.

  • Filter criteria can list the same event property only once.

  • Events that have null values for event properties listed in the filter criteria do not match the criteria.

5.4. Pattern Operators

5.4.1. Every

The every operator indicates that the pattern expression should restart when the pattern matches. Without the every operator the pattern expressions matcher stops when the pattern matches once.

Thus the every operator works like a factory for the pattern expression contained within. When the pattern expression within it fires and thus quits checking for events, the every causes the start of a new pattern matcher listening for more occurances of the same event or set of events.

Every time a pattern expression within an every operator turns true a new active pattern matcher is started looking for more event(s) or timing conditions that match the pattern expression. If the every operator is not specified for an expression, the expression stops after the first match was found.

This pattern fires when encountering event A and then stops looking.

A

This pattern keeps firing when encountering event A, and doesn't stop looking.

every A

Lets consider an example event sequence as follows.

A1 B1 C1 B2 A2 D1 A3 B3 E1 A4 F1 B4

Table 5.1. 'Every' operator examples

ExampleDescription
every ( A -> B )

Detect event A followed by event B. At the time when B occurs the pattern matches, then the pattern matcher restarts and looks for event A again.

  1. Matches on B1 for combination {A1, B1}

  2. Matches on B3 for combination {A2, B3}

  3. Matches on B4 for combination {A4, B4}

every A -> B

The pattern fires for every event A followed by an event B.

  1. Matches on B1 for combination {A1, B1}

  2. Matches on B3 for combination {A2, B3} and {A3, B3}

  3. Matches on B4 for combination {A4, B4}

A -> every B

The pattern fires for an event A followed by every event B.

  1. Matches on B1 for combination {A1, B1}.

  2. Matches on B2 for combination {A1, B2}.

  3. Matches on B3 for combination {A1, B3}

  4. Matches on B4 for combination {A1, B4}

every A -> every B

The pattern fires for every event A followed by every event B.

  1. Matches on B1 for combination {A1, B1}.

  2. Matches on B2 for combination {A1, B2}.

  3. Matches on B3 for combination {A1, B3} and {A2, B3} and {A3, B3}

  4. Matches on B4 for combination {A1, B4} and {A2, B4} and {A3, B4} and {A4, B4}

The examples show that it is possible that a pattern fires for multiple combinations of events that match a pattern expression. Each combination is posted as an EventBean instance to the update method in the UpdateListener implementation.

5.4.2. And

Similar to the Java && operator the and operator requires both nested pattern expressions to turn true before the whole expression turns true (a join pattern).

Pattern matches when both event A and event B are found.

A and B

Pattern matches on any sequence A followed by B and C followed by D, or C followed by D and A followed by B

(A -> B) and (C -> D)

5.4.3. Or

Similar to the Java “||” operator the or operator requires either one of the expressions to turn true before the whole expression turns true.

Look for either event A or event B. As always, A and B can itself be nested expressions as well.

A or B

Detect all stock ticks that are either above or below a threshold.

every (StockTick(symbol='IBM', price < 100) or StockTick(symbol='IBM', price > 105)

5.4.4. Not

The not operator negates the truth value of an expression. Pattern expressions prefixed with not are automatically defaulted to true.

This pattern matches only when an event A is encountered followed by event B but only if no event C was encountered before event B.

( A -> B ) and not C

5.4.5. Followed-by

The followed by -> operator specifies that first the left hand expression must turn true and only then is the right hand expression evaluated for matching events.

Look for event A and if encountered, look for event B. As always, A and B can itself be nested event pattern expressions.

A -> B

Pattern to match when 2 status events indicating an error occur.

StatusEvent(status='ERROR') -> StatusEvent(status='ERROR')

5.5. Guards

5.5.1. timer:within

The timer:within guard acts like a stopwatch. If the associated pattern expression does not turn true within the specified time period it is stopped and permanently false.

Pattern for all A events that arrive within 5 seconds.

every A where timer:within (5000)

Pattern for any A or B events in the next 5 seconds.

( A or B ) where timer:within (5000)

Pattern if for any 2 errors that happen 10 seconds within each other.

every (StatusEvent(status='ERROR') -> StatusEvent(status='ERROR') where timer:within (10000))

5.6. Pattern Observers

5.6.1. timer:interval

The timer:interval observer takes a wait time in milliseconds and waits for the defined time before the truth value of the observer turns true.

After event A arrived wait 10 seconds then indicate that the pattern matches.

A -> timer:interval(10000) 

An expression that matches every 20 seconds.

every timer:interval(20000)

5.6.2. timer:at

The timer:at observer is similar in function to the Unix “crontab” command. At a specified time the expression turns true. The at operator can also be made to pattern match at regular intervals by using an every operator in front of the timer:at operator.

The syntax is: timer:at (minutes, hours, days of month, months, days of week [, seconds]).

The value for seconds is optional. Each element allows wildcard * values. Ranges can be specified by means of lower bounds then a colon ‘:’ then the upper bound. The division operator */x can be used to specify that every xth value is valid. Combinations of these operators can be used by placing these into square brackets([]).

This expression pattern matches every 5 minutes past the hour.

every timer:at(5, *, *, *, *)

The below at operator pattern matches every 15 minutes from 8am to 5pm on even numbered days of the month as well as on the first day of the month.

timer:at (*/15, 8:17, [*/2, 1], *, *)