esper.codehaus.org and espertech.comDocumentation
EPL provides enumeration methods that work with lambda expressions to perform common tasks on subquery results, named windows, tables, event properties or inputs that are or can be projected to a collection of events, scalar values or objects.
Enumeration methods are stateless and the use of enumeration methods alone does not cause the engine to retain any events or other state (with the possible exception of short-lived caching of evaluation results).
A lambda expression is an anonymous expression. Lambda expressions are useful for encapsulating user-defined expressions that are applied to each element in a collection. This section discusses built-in enumeration methods and their lambda expression parameters.
Lambda expressions use the lambda operator =>
, which is read as "goes to" (->
may be used and is equivalent). The left side of the lambda operator specifies the lambda expression input parameter(s) (if any) and the right side holds the expression. The lambda expression x => x * x is read "x goes to x times x.".
Lambda expressions are also used for expression declaration as discussed in Section 5.2.9, “Expression Declaration”.
When writing lambdas, you do not have to specify a type for the input parameter(s) or output result(s) because the engine can infer all types based on the input and the expression body. So if you are querying an RFIDEvent, for example, then the input variable is inferred to be an RFIDEvent event, which means you have access to its properties and methods.
The term element in respect to enumeration methods means a single event, scalar value or object in a collection that is the input to an enumeraton method. The term collection means a sequence or group of elements.
The below table summarizes the built-in enumeration methods available:
The Item
event is defined as follows (access methods not shown for brevity):
public class Item { String assetId; // passenger or luggage asset id Location location; // (x,y) location boolean luggage; // true if this item is a luggage piece String assetIdPassenger; // if the item is luggage, contains passenger associated ...
The LocationReport
event contains a list of Item
items for which it reports events.
The LocationReport
event is defined as follows:
public class LocationReport { List<Item> items; ...
The Zone
event contains a zone name
and (x1, y1, x2, y2) rectangle
.
The Zone
event is defined as follows:
public class Zone { String name; Rectangle rectangle; ...
The Location
object is a nested object to Item
and provides the current (x,y) location:
public class Location { int x; int y; ...
The Rectangle
object is a nested object to Zone
and provides a zone rectangle(x1,y1,x2,y2):
public class Rectangle { int x1; int y1; int x2; int y2; ...
The syntax for enumeration methods is the same syntax as for any chained invocation:
input_coll.enum_method_name( [method_parameter [, method_parameter [,...]]]) .[ [enum_method_name(...) [...]] | property_name]
method_parameter: lamda_param => lamda_expression
method_parameter: (lamda_param [,lamda_param [,...]]) => lamda_expression
select items.where(i => i.location.x = 0 and i.location.y = 0) as zeroloc from LocationReport
As enumeration methods can be chained, this selection is equivalent:
select items.where(i => i.location.x = 0).where(i => i.location.y = 0) as zeroloc from LocationReport
select items.where( (item, indexElement) => indexElement < 3) as firstThreeItems from LocationReport
A property of an event that is itself a collection of events or classes, i.e. an indexed property.
The special prevwindow
, prev
and prevtail
single-row functions.
A plug-in single-row function, a user-defined function or an enum type.
A collection or array returned by a method call on an event or a method call on a variable.
A substitution parameter value provided by a prepared statement.
In an EPL pattern, events collected in a repeat ([...]
) and a repeat-until (... until ...
).
Subqueries can return the rows of another stream's data window or rows from a named window or table. By providing a where-clause the rows returned by a subquery can be correlated to data provided by stream(s) in the from-clause. See Section 5.11, “Subqueries”.
A subquery that selects (*)
wildcard provides a collection of events as input. A subquery that selects a single value expression provides a collection of scalar values as input. Subqueries that selects multiple value expressions are not allowed as input to enumeration methods.
The following example uses a subquery to retrieve all zones for each location report item where the location falls within the rectangle of the zone. Please see a description of example events and functions above.
select assetId, (select * from Zone.std:unique(name)).where(z => inrect(z.rectangle, location)) as zones from Item
You may place the subquery in an expression declaration to reuse the subquery in multiple places of the same EPL statement.
This sample EPL declares the same query as above in an expression declaration:
expression myquery {itm => (select * from Zone.std:unique(name)).where(z => inrect(z.rectangle, itm.location)) } select assetId, myquery(item) as subq, myquery(item).where(z => z.zone = 'Z01') as assetItem from Item as item
The above query also demonstrates how an enumeration method, in the example the where
-method, can be run across the results returned by a subquery in an expression declaration.
Place a single column in the subquery select-clause to provide a collection of scalar values as input.
The next example selects all names of zones and orders the names returning an order collection of string names every 30 seconds:
select (select name from Zone.std:unique(name)).orderBy() as orderedZones from pattern[every timer:interval(30)]
The next example utilizes a subquery that counts zone events per name and finds those that have a count greater then 1:
select (select name, count(*) as cnt from Zone.win:keepall() group by name) .where(v => cnt > 1) from LocationReport]
When the subquery selects a single column that is itself an event, the result of the subquery is a collection of events of that type and can provide input to enumeration methods.
For example:
create schema SettlementEvent (symbol string);
create schema PriceEvent (symbol string, price double);
create schema OrderEvent (orderId string, pricedata PriceEvent);
select (select pricedata from OrderEvent.std:unique(orderId)) .anyOf(v => v.symbol = 'GE') as has_ge from SettlementEvent(symbol = 'GE')
Note that the engine can cache intermediate results thereby is not forced to re-evaluate the subquery for each occurrence in the select
-clause.
Named windows are globally-visible data windows. See Section 6.2, “Named Window Usage”.
You may specify the named window name as input for an enumeration method and can optionally provide a correlation where-clause. The syntax is equivalent to a sub-query against a named window but much shorter.
Synopsis:
named-window-name[(correlation-expression)].enum-method-name(...)
When selecting all events in a named window you do not need the correlation-expression. To select a subset of data in the named window, specify a correlation-expression. From the perspective of best runtime performance, a correlation expression is preferred to reduce the number of rows returned.
The following example first declares a named window to hold the last zone event per zone name:
create window ZoneWindow.std:unique(name) as Zone
Then we create a statement to insert zone events that arrive to the named window:
insert into ZoneWindow select * from Zone
Finally this statement queries the named window to retrieve all zones for each location report item where the location falls within the rectangle of the zone:
select ZoneWindow.where(z => inrect(z.rectangle, location)) as zones from Item
If you have a filter or correlation expression, append the expression to the named window name and place in parenthesis.
This slightly modified query is the example above except that it adds a filter expression such that only zones with name Z1, Z2 or Z3 are considered:
select ZoneWindow(name in ('Z1', 'Z2', 'Z3')).where(z => inrect(z.rectangle, location)) as zones from Item
You may prefix property names provided by the named window with the name to disambiguate property names.
This sample query prefixed the name
property and returns the count of matching zones:
select ZoneWindow(ZoneWindow.name in ('Z1', 'Z2', 'Z3')).countof()) as zoneCount from Item
The engine internally interprets the shortcut syntax and creates a subquery from it. Thus all indexing and query planning for subqueries against named windows apply here as well.
Tables are globally-visible data structures. See Section 6.3, “Table Usage”.
Tables can hold aggregation state such as the window
and sorted
aggregation state.
The example EPL below declares a table to hold StockTick events in a column named theTicks
:
create table MyTable(theTicks window(*) @type(StockTick))
The table column can be input to an enumeration method, for example:
select MyTable.theTicks.anyOf(v=> price > 100) from MyEvent
select items.where(p => distance(0, 0, p.location.x, p.location.y) < 20) as centeritems from LocationReport
create schema MyEvent (seqone String[], seqtwo String[])
select seqone.sequenceEqual(seqtwo) from MyEvent
Event aggregation functions return an event or multiple events. They are aggregation functions and as such sensitive to the presence of group by
. See Section 10.2.2, “Event Aggregation Functions”.
You can use window
, first
or last
event aggregation functions as input to an enumeration method. Specify the *
wildcard as the parameter to the event aggregation function to provide a collection of events as input. Or specify a property name as the parameter to event aggregation function to provide a collection of scalar values as input.
You can use the sorted
, maxby
, minby
, maxbyever
or minbyever
event aggregation functions as input to an enumeration method. Specify one or more criteria expressions that provide the sort order as parameters to the event aggregation function.
In this example query the window(*)
aggregation function returns the last 10 seconds of item location reports for the same asset id as the incoming event. Among that last 10 seconds of events for the same asset id, the
enumeration method returns those item location reports where the distance to center is less then 20, for each arriving Item event.
Sample query:
select window(*).where(p => distance(0, 0, p.location.x, p.location.y) < 20) as centeritems from Item(type='P').win:time(10) group by assetId
The next sample query instead selects the asset id property of all events and returns an ordered collection:
select window(assetId).orderBy() as orderedAssetIds from Item.win:time(10) group by assetId
The following example outputs the 5 highest prices per symbol among the last 10 seconds of stock ticks:
select sorted(price desc).take(5) as highest5PricesPerSymbol from StockTick.win:time(10) group by symbol
The prev
, prevwindow
and prevtail
single-row functions allow access into a stream's data window however are not aggregation functions and and as such not sensitive to the presence of group by
. See Section 10.1.14, “The Previous-Window Function”.
When using any of the prev
single-row functions as input to a built-in enumeration method you can specify the stream name as a parameter to the function or an event property. The input to the enumeration method is a collection of events if you specify the stream name, or a collection of scalar value if you specify an event property.
In this example query the prevwindow(stream)
single-row function returns the last 10 seconds of item location reports, among which the
enumeration method filters those item location reports where the distance to center is less then 20, for each Item event that arrived in the last 10 seconds considering passenger-type Item events only (see filter type = 'P').
Sample query:
select prevwindow(items) .where(p => distance(0, 0, p.location.x, p.location.y) < 20) as centeritems from Item(type='P').win:time(10) as items
This sample query demonstrates the use of the prevwindow
function to return a collection of scalar values (collection of asset id) as input to orderby
:
select prevwindow(assetId).orderBy() as orderedAssetIds from Item.win:time(10) as items
public class ZoneFactory { public static Iterable<Zone> getZones() { List<Zone> zones = new ArrayList<Zone>(); zones.add(new Zone("Z1", new Rectangle(0, 0, 20, 20))); return zones; } }
The following query returns for each Item event all zones that the item belongs to:
select ZoneFactory.getZones().where(z => inrect(z.rectangle, item.location)) as zones from Item as item
select getZones().where(z => inrect(z.rectangle, item.location)) as zones from Item as item
For example, the static method getZoneNames()
returns a list of zone names:
public static String[] getZoneNames() { return new String[] { "Z1", "Z2"}; }
The following query returns zone names every 30 seconds and excludes zone Z1:
select getZoneNames().where(z => z != "Z1") from pattern[every timer:interval(30)]
An enum type can also be a useful source for enumerable values.
The following sample Java declares an enum type EnumOfZones
:
public enum EnumOfZones { ZONES_OUTSIDE(new String[] {"z1", "z2"}), ZONES_INSIDE(new String[] {"z3", "z4"}) private final String[] zones; private EnumOfZones(String[] zones) { this.zones = zones; } public String[] getZones() { return zones; } }
A sample statement that utilizes the enum type is shown next:
select EnumOfZones.ZONES_OUTSIDE.getZones().anyOf(v => v = zone) from Item
A declared expression may return input data for an enumeration method.
expression passengers { lr => lr.items.where(l => l.type='P') } select passengers(lr) as passengerCollection, passengers(lr).where(x => assetId = 'P01') as passengerP01 from LocationReport lr
The engine applies caching techniques to avoid re-evaluating the declared expression multiple times.
A variable may provide input data for an enumeration method.
This constant of array type carries a list of invalid zones:
create constant variable string[] invalid_zones = { 'Z1', 'Z2' };
select invalid_zones.anyOf(v => v = name) as flagged from Zone
select * from Order match_recognize ( measures A as a_array, B as b pattern (A* B) define B as A.anyOf(v=> v.itemId = B.itemId) )
Both the define
and the measures
clause can contain expressions utilizing enumeration methods.
Below is the complete EPL statement (one statement not multiple):
// expression to return a collection of lost luggage expression lostLuggage { lr => lr.items.where(l => l.type='L' and lr.items.some(p => p.type='P' and p.assetId=l.assetIdPassenger and LRUtil.distance(l.location.x, l.location.y, p.location.x, p.location.y) > 20)) } // expression to return all passengers expression passengers { lr => lr.items.where(l => l.type='P') } // expression to find the nearest owner expression nearestOwner { lr => lostLuggage(lr).toMap(key => key.assetId, value => passengers(lr).minBy( p => LRUtil.distance(value.location.x, value.location.y, p.location.x, p.location.y)) ) } select lostLuggage(lr) as val1, nearestOwner(lr) as val2 from LocationReport lr
An expression example with scalar values:
{1, 2, 3}.aggregate(0, (result, value) => result + value) // Returns 6
// Initialization value is zero. // Aggregate by adding up the price. select window(*).aggregate(0, (result, order) => result + order.price) as totalPrice from OrderEvent.win:time(10)
This example aggregation builds a comma-separated list of all asset ids of all items:
select items.aggregate('', (result, item) => result || (case when result='' then '' else ',' end) || item.assetId) as assets from LocationReport
The allof
enumeration method determines whether all elements satisfy the predicate condition.
An expression example with scalar values:
{1, 2, 3}.allOf(v => v > 0) // Returns true as all values are > 0 {1, 2, 3}.allOf(v => v > 1) // Returns false
The EPL statement below returns true when all items are within 1000 unit distance of center:
select items.allof(i => distance(i.location.x, i.location.y, 0, 0) < 1000) as centered from LocationReport
If the input is null the method returns null. If the input is empty the method returns true.
The anyof
enumeration method determines whether any element satisfies the predicate condition.
An expression example with scalar values:
{1, 2, 3}.anyOf(v => v > 0) // Returns true {1, 2, 3}.anyOf(v => v > 1) // Returns true {1, 2, 3}.anyOf(v => v > 3) // Returns false
The EPL statement below return true when any of the items are within 10 unit distance of center:
select items.anyof(i => distance(i.location.x, i.location.y, 0, 0) < 10) as centered from LocationReport
If the input is null the method returns null. If the input is empty the method returns false.
An expression example with scalar values:
{1, 2, 3}.average() // Returns 2
select items.average(i => distance(i.location.x, i.location.y, 0, 0)) as avgdistance from LocationReport
If the input is null the method returns null. If the input is empty the method returns double zero or BigDecimal zero. For BigDecimal precision and rounding, please see Section 16.4.22.6, “Math Context”.
An expression example with scalar values:
{1, 2, 3}.countOf() // Returns 3 {1, 2, 3}.countOf(v => v < 2) // Returns 1
The next sample statement counts the number of items:
select items.countOf() as cnt from LocationReport
select items.countOf(i => distance(i.location.x, i.location.y, 0, 0) < 20) as cntcenter from LocationReport
If the input is null the method returns null. If the input is empty the method returns integer zero.
The distinctOf
enumeration method returns distinct elements.
An expression example with scalar values:
{2, 3, 2, 1}.distinctOf() // Returns {2, 3, 1}
This example returns items distinct by item id returning the first item for each distinct item id:
select items.distinctOf(i => itemId) as itemsNearFirst from LocationReport
An expression example with scalar values:
{1, 2, 3}.except({1}) // Returns {2, 3}
select za.items.except(zb.items) as itemsCompared from LocationReport as za unidirectional, LocationReport.win:length(10) as zb
If the input is null the method returns null. For scalar values and objects equals-semantics apply.
An expression example with scalar values:
{1, 2, 3}.firstOf() // Returns 1 {1, 2, 3}.firstOf(v => v / 2 > 1) // Returns 3
select items.firstof(i => distance(i.location.x, i.location.y, 0, 0) < 20) as firstcenter from LocationReport
The next sample EPL returns the first item's asset id:
select items.firstof().assetId as firstAssetId from LocationReport
If the input is null, empty or if none of the elements match the condition the method returns null.
select items.where(type='L').groupby(i => assetIdPassenger) as luggagePerPerson from LocationReport
The query shown below generates a map of item asset id and distance to center:
select items.groupby( k => assetId, v => distance(v.location.x, v.location.y, 0, 0)) as distancePerItem from LocationReport
If the input is null the method returns null. Null values as key and value are allowed.
An expression example with scalar values:
{1, 2, 3}.intersect({2, 3}) // Returns {2, 3}
select za.items.intersect(zb.items) as itemsCompared from LocationReport as za unidirectional, LocationReport.win:length(10) as zb
If the input is null the method returns null. For scalar values and objects equals-semantics apply.
An expression example with scalar values:
{1, 2, 3}.lastOf() // Returns 3 {1, 2, 3}.lastOf(v => v < 3) // Returns 2
select items.lastof(i => distance(i.location.x, i.location.y, 0, 0) < 20) as lastcenter from LocationReport
The next sample EPL returns the last item's asset id:
select items.lastof().assetId as lastAssetId from LocationReport
If the input is null, empty or if none of the elements match the condition the method returns null.
An expression example with scalar values:
{1, 2, 3, 2, 1}.leastFrequent() // Returns 3
select items.leastFrequent(i => type) as leastFreqType from LocationReport
The max
enumeration method returns the maximum value among a collection of values.
If no value-selector lambda expression is provided, the method finds the maximum.
An expression example with scalar values:
{1, 2, 3, 2, 1}.max() // Returns 3
The next query returns the maximum distance of any item from center:
select items.max(i => distance(i.location.x, i.location.y, 0, 0)) as maxcenter from LocationReport
The next query returns the first item with the maximum distance to center:
select items.maxBy(i => distance(i.location.x, i.location.y, 0, 0)) as maxItemCenter from LocationReport
select items.maxBy(i => assetId).type as minAssetId from LocationReport
The min
enumeration method returns the minimum value among a collection of values.
If no value-selector lambda expression is provided, the method finds the minimum.
An expression example with scalar values:
{1, 2, 3, 2, 1}.min() // Returns 1
The next query returns the minimum distance of any item to center:
select items.min(i => distance(i.location.x, i.location.y, 0, 0)) as mincenter from LocationReport
The next query returns the first item with the minimum distance to center:
select items.minBy(i => distance(i.location.x, i.location.y, 0, 0)) as minItemCenter from LocationReport
select items.minBy(i => assetId).type as minAssetId from LocationReport
An expression example with scalar values:
{1, 2, 3, 2, 1, 2}.mostFrequent() // Returns 2
select items.leastFrequent(i => type) as leastFreqType from LocationReport
An expression example with scalar values:
{2, 3, 2, 1}.orderBy() // Returns {1, 2, 2, 3}
This example orders all items from a location report according to their distance from center:
select items.orderBy(i => distance(i.location.x, i.location.y, 0, 0)) as itemsNearFirst, items.orderByDesc(i => distance(i.location.x, i.location.y, 0, 0)) as itemsFarFirst from LocationReport
The reverse
enumeration method simply reverses the order of elements returning a collection.
An expression example with scalar values:
{2, 3, 2, 1}.reverse() // Returns {1, 2, 3, 2}
The following EPL reverses the items:
select items.reverse() as reversedItems from LocationReport
The enumeration method applies a transformation lambda expression to each element and returns the result of each transformation as a collection. Use the new
operator to yield multiple values for each element, see Section 9.13, “The 'new' Keyword”.
The next EPL query returns a collection of asset ids:
select items.selectFrom(i => assetId) as itemAssetIds from LocationReport
This sample EPL query evaluates each item and returns the asset id as well as the distance from center for each item:
select items.selectFrom(i => new { assetId, distanceCenter = distance(i.location.x, i.location.y, 0, 0) } ) as itemInfo from LocationReport
If the input is null the method returns null. If the input is empty the method returns an empty collection.
An expression example with scalar values:
{1, 2, 3}.sequenceEqual({1}) // Returns false {1, 2, 3}.sequenceEqual({1, 2, 3}) // Returns true
select items.selectFrom(i => assetId).sequenceEquals(ItemUtil.redListed()) from LocationReport
An expression example with scalar values:
{1, 2, 3}.sumOf() // Returns 6
The following example computes the sum of the distance of each item to center:
select items.sum(i => distance(i.location.x, i.location.y, 0, 0) as totalAllDistances from LocationReport
The take
enumeration method returns a specified number of contiguous elements from the start.
The enumeration method takes a single size (non-lambda) expression that returns an Integer value.
An expression example with scalar values:
{1, 2, 3}.take(2) // Returns {1, 2}
The following example returns the first 5 items:
select items.take(5) as first5Items from LocationReport
The takeLast
enumeration method returns a specified number of contiguous elements from the end.
The enumeration method takes a single size (non-lambda) expression that returns an Integer value.
An expression example with scalar values:
{1, 2, 3}.takeLast(2) // Returns {2, 3}
The following example returns the last 5 items:
select items.takeLast(5) as last5Items from LocationReport
An expression example with scalar values:
{1, 2, 3}.takeWhile(v => v < 3) // Returns {1, 2} {1, 2, 3}.takeWhile((v,ind) => ind > 2) // Returns {1, 2} {1, 2, -1, 4, 5, 6}.takeWhile((v,ind) => ind < 5 and v > 0) // Returns {1, 2} (Take while index<5 amd value>0)
select items.takeWhile(i => distance(i.location.x, i.location.y, 0, 0) < 20) from LocationReport
The next example is similar to the query above but also limits the result to the first 10 items:
select items.takeWhile((i, ind) => distance(i.location.x, i.location.y, 0, 0) < 20) and ind < 10) from LocationReport
An expression example with scalar values:
{1, 2, 3}.takeWhileLast(v => v < 3) // Returns {} (empty collection) {1, 2, 3}.takeWhileLast(v => v > 1) // Returns {2, 3} {1, 2, 3}.takeWhileLast((v,ind) => ind > 2) // Returns {2, 3} {1, 2, -1, 4, 5, 6}.takeWhileLast((v,ind) => ind < 5 and v > 0) // Returns {4, 5, 6} (Take while index<5 amd value>0)
select items.takeWhile(i => distance(i.location.x, i.location.y, 0, 0) < 20) from LocationReport
The next example is similar to the query above but also limits the result to the last 10 items:
select items.takeWhile((i, ind) => distance(i.location.x, i.location.y, 0, 0) < 20) and ind < 10) from LocationReport
The next example EPL outputs a map of item asset id and distance to center for each item:
select items.toMap(k => k.assetId, v => distance(v.location.x, v.location.y, 0, 0)) as assetDistance from LocationReport
If the input is null the method returns null. If the input is empty the method returns an empty map.
An expression example with scalar values:
{1, 2, 3}.union({4, 5}) // Returns {1, 2, 3, 4, 5}
select items.where(i => i.assetId = 'L001') .union(items.where(i => i.type = 'P')) as itemsUnion from LocationReport
The where
enumeration method filters elements based on a predicate.
An expression example with scalar values:
{1, 2, 3}.where(v => v != 2) // Returns {1, 3}
This example selects all items from a location report that are passenger-type:
select items.where(p => p.type = 'P') from LocationReport
select items.where((p, ind) => p.type = 'P' and ind > 2) from LocationReport