Esper - Event Stream and Complex Event Processing for Java

Reference Documentation

1.12.0


Table of Contents

Preface
1. Technology Overview
1.1. Introduction to CEP and event stream analysis
1.2. CEP and relational databases
1.3. The Esper engine for CEP
1.4. Required 3rd Party Libraries
2. Event Representations
2.1. Event Underlying Java Objects
2.2. Event Properties
2.3. Dynamic Event Properties
2.4. Plain-Old Java Object Events
2.4.1. Java Object Event Properties
2.5. java.util.Map Events
2.6. org.w3c.dom.Node XML Events
3. Processing Model
3.1. Introduction
3.2. Insert Stream
3.3. Insert and Remove Stream
3.4. Filters and Where-clauses
3.5. Time Windows
3.5.1. Time Window
3.5.2. Time Batch
3.6. Batch Windows
3.7. Aggregation and Grouping
3.7.1. Insert and Remove Stream
3.7.2. Output for Aggregations
3.7.2.1. Un-aggregated and Un-grouped
3.7.2.2. Fully Aggregated and Un-grouped
3.7.2.3. Aggregated and Un-Grouped
3.7.2.4. Fully Aggregated and Grouped
3.7.2.5. Aggregated and Grouped
3.8. EventBean Query Results
4. EQL Reference: Clauses
4.1. EQL Introduction
4.2. EQL Syntax
4.2.1. Specifying Time Periods
4.2.2. Using Comments
4.3. Choosing Event Properties And Events: the Select Clause
4.3.1. Choosing all event properties: select *
4.3.2. Choosing specific event properties
4.3.3. Expressions
4.3.4. Renaming event properties
4.3.5. Choosing event properties and events in a join
4.3.6. Choosing event properties and events from a pattern
4.3.7. Selecting istream and rstream events
4.4. Specifying Event Streams: the From Clause
4.4.1. Filter-based Event Streams
4.4.1.1. Specifying an event type
4.4.1.2. Specifying filter criteria
4.4.1.3. Filtering Ranges
4.4.1.4. Filtering Sets of Values
4.4.1.5. Filter Limitations
4.4.2. Pattern-based Event Streams
4.4.3. Specifying Views
4.4.4. Using the Stream Name
4.5. Specifying Search Conditions: the Where Clause
4.6. Aggregates and grouping: the Group-by Clause and the Having Clause
4.6.1. Using aggregate functions
4.6.2. Organizing statement results into groups: the Group-by clause
4.6.3. Selecting groups of events: the Having clause
4.6.4. How the stream filter, Where, Group By and Having clauses interact
4.6.5. Comparing the Group By clause and the std:groupby view
4.7. Stabilizing and Limiting Output: the Output Clause
4.7.1. Output Clause Options
4.7.2. Group By, Having and Output clause interaction
4.8. Sorting Output: the Order By Clause
4.9. Merging Streams and Continuous Insertion: the Insert Into Clause
4.10. Joining Event Streams
4.11. Outer Joins
4.12. Subqueries
4.12.1. The 'exists' keyword
4.12.2. The 'in' keyword
4.13. Joining Relational Data via SQL
4.13.1. Joining SQL Query Results
4.13.2. SQL Query and the EQL Where Clause
4.13.3. Outer Joins With SQL Queries
4.13.4. Using Patterns to Request (Poll) Data
4.13.5. JDBC Implementation Overview
4.13.6. Oracle Drivers and No-Metadata Workaround
4.14. Joining Non-Relational Data via Method Invocation
4.14.1. Joining Method Invocation Results
4.14.2. Providing the Method
4.15. Creating and Using Named Windows
4.15.1. Creating Named Windows: the Create Window clause
4.15.2. Deleting From Named Windows: the On Delete clause
4.15.2.1. Using Patterns in the On Delete Clause
4.15.3. Inserting Into Named Windows
4.15.4. Selecting From Named Windows
4.15.5. Triggered Select on Named Windows: the On Select clause
4.15.6. Triggered Playback from Named Windows: the On Insert clause
4.16. Variables
4.16.1. Creating Variables: the Create Variable clause
4.16.2. Setting Variable Values: the On Set clause
4.16.3. Using Variables
5. EQL Reference: Patterns
5.1. Event Pattern Overview
5.2. How to use Patterns
5.2.1. Pattern Syntax
5.2.2. Subscribing to Pattern Events
5.2.3. Pulling Data from Patterns
5.3. Operator Precedence
5.4. Filter Expressions In Patterns
5.5. Pattern Operators
5.5.1. Every
5.5.1.1. Every Operator Example
5.5.1.2. Sensor Example
5.5.2. And
5.5.3. Or
5.5.4. Not
5.5.5. Followed-by
5.6. Pattern Guards
5.6.1. timer:within
5.7. Pattern Observers
5.7.1. timer:interval
5.7.2. timer:at
6. EQL Reference: Operators
6.1. Arithmetic Operators
6.2. Logical And Comparsion Operators
6.3. Concatenation Operators
6.4. Binary Operators
6.5. Array Definition Operator
6.6. The 'in' Keyword
6.7. The 'between' Keyword
6.8. The 'like' Keyword
6.9. The 'regexp' Keyword
7. EQL Reference: Functions
7.1. Single-row Function Reference
7.1.1. The Case Control Flow Function
7.1.2. The Cast Function
7.1.3. The Coalesce Function
7.1.4. The Current_Timestamp Function
7.1.5. The Exists Function
7.1.6. The Instance-Of Function
7.1.7. The Min and Max Functions
7.1.8. The Previous Function
7.1.8.1. Previous Event per Group
7.1.8.2. Restrictions
7.1.8.3. Comparison to the prior Function
7.1.9. The Prior Function
7.2. Aggregate Functions
7.3. User-Defined Functions
8. EQL Reference: Views
8.1. Window views
8.1.1. Length window (win:length)
8.1.2. Length batch window (win:length_batch)
8.1.3. Time window (win:time)
8.1.4. Externally-timed window (win:ext_timed)
8.1.5. Time batch window (win:time_batch)
8.1.6. Time-Length combination batch window (win:time_length_batch)
8.1.7. Time-Accumulating window (win:time_accum)
8.1.8. Keep-All window (win:keepall)
8.2. Standard view set
8.2.1. Unique (std:unique)
8.2.2. Group-By (std:groupby)
8.2.3. Size (std:size)
8.2.4. Last Event (std:lastevent)
8.3. Statistics views
8.3.1. Univariate statistics (stat:uni)
8.3.2. Regression (stat:linest)
8.3.3. Correlation (stat:correl)
8.3.4. Weighted average (stat:weighted_avg)
8.3.5. Multi-dimensional statistics (stat:cube)
8.4. Extension View Set
8.4.1. Sorted Window View (ext:sort)
8.4.2. Time-Order View (ext:time_order)
9. API Reference
9.1. API Overview
9.2. Engine Instances
9.3. The Administrative Interface
9.3.1. Creating Statements
9.3.2. Adding Listeners
9.3.3. Using Iterators
9.3.4. Managing Statements
9.3.5. Runtime Engine Configuration
9.4. The Runtime Interface
9.4.1. Receiving Unmatched Events
9.4.2. Emit Facility for Publish-Subscribe
9.5. Time-Keeping Events
9.6. Events Received from the Engine
9.7. Engine Threading and Concurrency
9.8. Statement Object Model
9.8.1. Building an Object Model
9.8.2. Building Complex Expressions
9.8.3. Building Patterns
9.8.4. Building Complete Statements
9.9. Prepared Statement and Substitution Parameters
10. Configuration
10.1. Programmatic Configuration
10.2. Configuration via XML File
10.3. XML Configuration File
10.4. Configuration Items
10.4.1. Events represented by Java Classes
10.4.1.1. Package of Java Event Classes
10.4.1.2. Event type alias to Java class mapping
10.4.1.3. Non-JavaBean and Legacy Java Event Classes
10.4.1.4. Specifying Event Properties for Java Classes
10.4.1.5. Turning off Code Generation
10.4.1.6. Case Sensitivity and Property Names
10.4.2. Events represented by java.util.Map
10.4.3. Events represented by org.w3c.dom.Node
10.4.3.1. Schema Resource
10.4.3.2. XPath Property
10.4.3.3. Absolute or Deep Property Resolution
10.4.4. Class and package imports
10.4.5. Cache Settings for Method Invocations
10.4.6. Variables
10.4.7. Relational Database Access
10.4.7.1. Connections obtained via DataSource
10.4.7.2. Connections obtained via DriverManager
10.4.7.3. Connections-level settings
10.4.7.4. Connections lifecycle settings
10.4.7.5. Cache settings
10.4.7.6. Column Change Case
10.4.7.7. SQL Types Mapping
10.4.7.8. Metadata Origin
10.4.8. Engine Settings related to Concurrency and Threading
10.4.8.1. Preserving the order of events delivered to listeners
10.4.8.2. Preserving the order of events for insert-into streams
10.4.8.3. Internal Timer Settings
10.4.9. Engine Settings related to Event Metadata
10.4.9.1. Java Class Property Names and Case Sensitivity
10.4.10. Engine Settings related to View Resources
10.4.10.1. Sharing View Resources between Statements
10.4.11. Engine Settings related to Logging
10.4.11.1. Execution Path Debug Logging
10.4.12. Engine Settings related to Variables
10.4.12.1. Variable Version Release Interval
11. Extension and Plug-in
11.1. Overview
11.2. Custom View Implementation
11.2.1. Implementing a View Factory
11.2.2. Implementing a View
11.2.3. Configuring View Namespace and Name
11.3. Custom Aggregation Functions
11.3.1. Implementing an Aggregation Function
11.3.2. Configuring Aggregation Function Name
11.4. Custom Pattern Guard
11.4.1. Implementing a Guard Factory
11.4.2. Implementing a Guard Class
11.4.3. Configuring Guard Namespace and Name
11.5. Custom Pattern Observer
11.5.1. Implementing an Observer Factory
11.5.2. Implementing an Observer Class
11.5.3. Configuring Observer Namespace and Name
12. Examples, Tutorials, Case Studies
12.1. Examples Overview
12.2. Market Data Feed Monitor
12.2.1. Input Events
12.2.2. Computing Rates Per Feed
12.2.3. Detecting a Fall-off
12.2.4. Event generator
12.3. JMS Server Shell and Client
12.3.1. Overview
12.3.2. JMS Messages as Events
12.3.3. JMX for Remote Dynamic Statement Management
12.4. Transaction 3-Event Challenge
12.4.1. The Events
12.4.2. Combined event
12.4.3. Real time summary data
12.4.4. Find problems
12.4.5. Event generator
12.5. J2EE Self-Service Terminal Management
12.5.1. Events
12.5.2. Detecting Customer Check-in Issues
12.5.3. Absence of Status Events
12.5.4. Activity Summary Data
12.5.5. Sample Application for J2EE Application Server
12.5.5.1. Running the Example
12.5.5.2. Building the Example
12.5.5.3. Running the Event Simulator and Receiver
12.6. Assets Moving Across Zones - An RFID Example
12.7. AutoID RFID Reader generating XML documents
12.8. StockTicker
12.9. MatchMaker
12.10. QualityOfService
12.11. LinearRoad
12.12. StockTick RSI
13. Performance
13.1. Performance Results
13.2. Performance Tips
13.2.1. Understand how to tune your Java virtual machine
13.2.2. Compare Esper to other solutions
13.2.3. Select the underlying event rather than individual fields
13.2.4. Prefer stream-level filtering over post-data-window filtering
13.2.5. Reduce the use of arithmetic in expressions
13.2.6. Consider using EventPropertyGetter for fast access to event properties
13.2.7. Consider casting the underlying event
13.2.8. Turn off logging
13.2.9. Disable view sharing
13.2.10. Disable delivery order guarantees
13.3. Using the performance kit
13.3.1. How to use the performance kit
13.3.2. How we use the performance kit
14. References
14.1. Reference List
Index

Preface

Analyzing and reacting to information in real-time oftentimes requires the development of custom applications. Typically these applications must obtain the data to analyze, filter data, derive information and then indicate this information through some form of presentation or communication. Data may arrive with high frequency requiring high throughput processing. And applications may need to be flexible and react to changes in requirements while the data is processed. Esper is an event stream processor that aims to enable a short development cycle from inception to production for these types of applications.

This document is a resource for software developers who develop event driven applications. It also contains information that is useful for business analysts and system architects who are evaluating Esper.

It is assumed that the reader is familiar with the Java programming language.

This document is relevant in all phases of your software development project: from design to deployment and support.

If you are new to Esper, please follow these steps:

  1. Read the tutorials, case studies and solution patterns available on the Esper public web site at http://esper.codehaus.org

  2. Read Section 1.1, “Introduction to CEP and event stream analysis” if you are new to CEP and ESP (complex event processing, event stream processing)

  3. Read Chapter 2, Event Representations that explains the different ways of representing events to Esper

  4. Read Chapter 3, Processing Model to gain insight into EQL continuous query results

  5. Read Section 4.1, “EQL Introduction” for an introduction to event stream processing via EQL

  6. Read Section 5.1, “Event Pattern Overview” for an overview over event patterns

  7. Then glance over the examples Section 12.1, “Examples Overview”

  8. Finally to test drive Esper performance, read Chapter 13, Performance

Chapter 1. Technology Overview

1.1. Introduction to CEP and event stream analysis

The Esper engine has been developed to address the requirements of applications that analyze and react to events. Some typical examples of applications are:

  • Business process management and automation (process monitoring, BAM, reporting exceptions)

  • Finance (algorithmic trading, fraud detection, risk management)

  • Network and application monitoring (intrusion detection, SLA monitoring)

  • Sensor network applications (RFID reading, scheduling and control of fabrication lines, air traffic)

What these applications have in common is the requirement to process events (or messages) in real-time or near real-time. This is sometimes referred to as complex event processing (CEP) and event stream analysis. Key considerations for these types of applications are throughput, latency and the complexity of the logic required.

  • High throughput - applications that process large volumes of messages (between 1,000 to 100k messages per second)

  • Low latency - applications that react in real-time to conditions that occur (from a few milliseconds to a few seconds)

  • Complex computations - applications that detect patterns among events (event correlation), filter events, aggregate time or length windows of events, join event streams, trigger based on absence of events etc.

The Esper engine was designed to make it easier to build and extend CEP applications.

1.2. CEP and relational databases

Relational databases and the standard query language (SQL) are designed for applications in which most data is fairly static and complex queries are less frequent. Also, most databases store all data on disks (except for in-memory databases) and are therefore optimized for disk access.

To retrieve data from a database an application must issue a query. If an application need the data 10 times per second it must fire the query 10 times per second. This does not scale well to hundreds or thousands of queries per second.

Database triggers can be used to fire in response to database update events. However database triggers tend to be slow and often cannot easily perform complex condition checking and implement logic to react.

In-memory databases may be better suited to CEP applications then traditional relational database as they generally have good query performance. Yet they are not optimized to provide immediate, real-time query results required for CEP and event stream analysis.

1.3. The Esper engine for CEP

The Esper engine works a bit like a database turned upside-down. Instead of storing the data and running queries against stored data, the Esper engine allows applications to store queries and run the data through. Response from the Esper engine is real-time when conditions occur that match queries. The execution model is thus continuous rather then only when a query is submitted.

Esper provides two principal methods or mechanisms to process events: event patterns and event stream queries.

Esper offers an event pattern language to specify expression-based event pattern matching. Underlying the pattern matching engine is a state machine implementation. This method of event processing matches expected sequences of presence or absence of events or combinations of events. It includes time-based correlation of events.

Esper also offers event stream queries that address the event stream analysis requirements of CEP applications. Event stream queries provide the windows, aggregation, joining and analysis functions for use with streams of events. These queries are following the EQL syntax. EQL has been designed for similarity with the SQL query language but differs from SQL in its use of views rather then tables. Views represent the different operations needed to structure data in an event stream and to derive data from an event stream.

Esper provides these two methods as alternatives through the same API.

1.4. Required 3rd Party Libraries

Esper requires the following 3rd-party libraries at runtime:

  • ANTLR is the parser generator used for parsing and parse tree walking of the pattern and EQL syntax. Credit goes to Terence Parr at http://www.antlr.org. The ANTLR license is in the lib directory. The library is required for compile-time only.

  • CGLIB is the code generation library for fast method calls. This open source software is under the Apache license. The Apache 2.0 license is in the lib directory.

  • LOG4J and Apache commons logging are logging components. This open source software is under the Apache license. The Apache 2.0 license is in the lib directory.

Esper requires the following 3rd-party libraries at compile-time and for running the test suite:

  • JUnit is a great unit testing framework. Its license has also been placed in the lib directory. The library is required for build-time only.

  • MySQL connector library is used for testing SQL integration and is required for running the automated test suite.

Chapter 2. Event Representations

2.1. Event Underlying Java Objects

An event is an immutable record of a past occurrence of an action or state change. Event properties capture the state information for an event. An event is represented by either a POJO (plain-old Java object), a java.util.Map or a XML document via org.w3c.dom.Node.

In Esper, an event can be represented by any of the following underlying Java objects:

Table 2.1. Event Underlying Java Objects

Java ClassDescription
java.lang.ObjectAny Java POJO (plain-old java object) with getter methods following JavaBean conventions; Legacy Java classes not following JavaBean conventions can also serve as events .
java.util.MapMap events are key-values pairs
org.w3c.dom.NodeXML document object model (DOM)

2.2. Event Properties

Event properties capture the state information for an event. Event properties be simple as well as indexed, mapped and nested event properties. The table below outlines the different types of properties and their syntax in an event expression. This syntax allows statements to query deep JavaBean objects graphs, XML structures and Map events.

Table 2.2. Types of Event Properties

TypeDescriptionSyntaxExample
SimpleA property that has a single value that may be retrieved.
name
sensorId
IndexedAn indexed property stores an ordered collection of objects (all of the same type) that can be individually accessed by an integer-valued, non-negative index (or subscript).
name[index]
sensor[0]
MappedA mapped property stores a keyed collection of objects (all of the same type).
name('key')
sensor('light')
NestedA nested property is a property that lives within another property of an event.
name.nestedname
sensor.value

Combinations are also possible. For example, a valid combination could be person.address('home').street[0].

2.3. Dynamic Event Properties

Dynamic (unchecked) properties are event properties that need not be known at statement compilation time. Such properties are resolved during runtime.

The idea behind dynamic properties is that for a given underlying event representation we don't always know all properties in advance. An underlying event may have additional properties that are not known at statement compilation time, that we want to query on. The concept is especially useful for events that represent rich, object-oriented domain models.

The syntax of dynamic properties consists of the property name and a question mark. Indexed, mapped and nested properties can also be dynamic properties:

Table 2.3. Types of Event Properties

TypeSyntax
Dynamic Simple
name?
Dynamic Indexed
name[index]?
Dynamic Mapped
name('key')?
Dynamic Nested
name?.nestedPropertyName

Dynamic properties always return the java.lang.Object type. Also, dynamic properties return a null value if the dynamic property does not exist on events processed at runtime.

As an example, consider an OrderEvent event that provides an "item" property. The "item" property is of type Object and holds a reference to an instance of either a Service or Product.

Assume that both Service and Product classes provide a property named "price". Via a dynamic property we can specify a query that obtains the price property from either object (Service or Product):

select item.price? from OrderEvent

As a second example, assume that the Service class contains a "serviceName" property that the Product class does not possess. The following query returns the value of the "serviceName" property for Service objects. It returns a null-value for Product objects that do not have the "serviceName" property:

select item.serviceName? from OrderEvent

Consider the case where OrderEvent has multiple implementation classes, some of which have a "timestamp" property. The next query returns the timestamp property of those implementations of the OrderEvent interface that feature the property:

select timestamp? from OrderEvent

The query as above returns a single column named "timestamp?" of type Object.

When dynamic properties are nested, then all properties under the dynamic property are also considered dynamic properties. In the below example the query asks for the "direction" property of the object returned by the "detail" dynamic property:

select detail?.direction from OrderEvent
// equivalent to 
select detail?.direction? from OrderEvent

The functions that are often useful in conjunction with dynamic properties are:

  • The cast function casts the value of a dynamic property (or the value of an expression) to a given type.

  • The exists function checks whether a dynamic property exists. It returns true if the event has a property of that name, or false if the property does not exist on that event.

  • The instanceof function checks whether the value of a dynamic property (or the value of an expression) is of any of the given types.

Dynamic event properties work with all event representations outlined next: Java objects, Map-based and XML DOM-based events.

2.4. Plain-Old Java Object Events

Plain-old Java object events are object instances that expose event properties through JavaBeans-style getter methods. Events classes or interfaces do not have to be fully compliant to the JavaBean specification; however for the Esper engine to obtain event properties, the required JavaBean getter methods must be present.

Esper supports JavaBeans-style event classes that extend a superclass or implement one or more interfaces. Also, Esper event pattern and EQL statements can refer to Java interface classes and abstract classes.

Classes that represent events should be made immutable. As events are recordings of a state change or action that occurred in the past, the relevant event properties should not be changeable. However this is not a hard requirement and the Esper engine accepts events that are mutable as well.

The hashCode and equals methods do not need to be implemented. The implementation of these methods by a Java event class does not affect the behavior of the engine in any way.

Please see Chapter 10, Configuration on options for naming event types represented by Java object event classes.

2.4.1. Java Object Event Properties

As outlined earlier, the different property types are supported by the standard JavaBeans specification, and some of which are uniquely supported by Esper:

  • Simple properties have a single value that may be retrieved. The underlying property type might be a Java language primitive (such as int, a simple object (such as a java.lang.String), or a more complex object whose class is defined either by the Java language, by the application, or by a class library included with the application.

  • Indexed - An indexed property stores an ordered collection of objects (all of the same type) that can be individually accessed by an integer-valued, non-negative index (or subscript). Alternatively, the entire set of values may be retrieved using an array.

  • Mapped - As an extension to standard JavaBeans APIs, Esper considers any property that accepts a String-valued key a mapped property.

  • Nested - A nested property is a property that lives within another Java object which itself is a property of an event.

Assume there is an EmployeeEvent event class as shown below. The mapped and indexed properties in this example return Java objects but could also return Java language primitive types (such as int or String). The Address object and Employee objects can themselves have properties that are nested within them, such as a streetName in the Address object or a name of the employee in the Employee object.

public class EmployeeEvent {
	public String getFirstName();
	public Address getAddress(String type);
	public Employee getSubordinate(int index);
	public Employee[] getAllSubordinates();
}

Simple event properties require a getter-method that returns the property value. In this example, the getFirstName getter method returns the firstName event property of type String.

Indexed event properties require either one of the following getter-methods. A method that takes an integer-type key value and returns the property value, such as the getSubordinate method. Or a method that returns an array-type such as the getSubordinates getter method, which returns an array of Employee. In an EQL or event pattern statement, indexed properties are accessed via the property[index] syntax.

Mapped event properties require a getter-method that takes a String-typed key value and returns the property value, such as the getAddress method. In an EQL or event pattern statement, mapped properties are accessed via the property('key') syntax.

Nested event properties require a getter-method that returns the nesting object. The getAddress and getSubordinate methods are mapped and indexed properties that return a nesting object. In an EQL or event pattern statement, nested properties are accessed via the property.nestedProperty syntax.

All event pattern and EQL statements allow the use of indexed, mapped and nested properties (or a combination of these) anywhere where one or more event property names are expected. The below example shows different combinations of indexed, mapped and nested properties in filters of event pattern expressions:

every EmployeeEvent(firstName='myName')
every EmployeeEvent(address('home').streetName='Park Avenue')
every EmployeeEvent(subordinate[0].name='anotherName')
every EmployeeEvent(allSubordinates[1].name='thatName')
every EmployeeEvent(subordinate[0].address('home').streetName='Water Street')

Similarly, the syntax can be used in EQL statements in all places where an event property name is expected, such as in select lists, where-clauses or join criteria.

select firstName, address('work'), subordinate[0].name, subordinate[1].name
from EmployeeEvent
where address('work').streetName = 'Park Ave'

Property names follows Java standards: the class java.beans.Introspector and method getBeanInfo returns the property names as derived from the name of getter methods. In addition, Esper configuration provides a flag to turn off case-sensitive property names. A sample list of getter methods and property names is:

Table 2.4. JavaBeans-style Getter Methods and Property Names

MethodProperty NameExample
getPrice()price
select price from MyEvent
getNAME()NAME
select NAME from MyEvent
getItemDesc()itemDesc
select itemDesc from MyEvent
getQ()q
select q from MyEvent
getQN()QN
select QN from MyEvent
getqn()qn
select qn from MyEvent
gets()s
select s from MyEvent

Constants are public static final fields in Java that may also participate in expressions of all kinds, as this example shows:

select * from MyEvent where property=MyConstantClass.FIELD_VALUE

Event properties that are enumeration values can be compared by their enumeration value:

select * from MyEvent where enumProp=EnumClass.ENUM_VALUE_1

Alternatively, a static method may be employed on a class, such as the enumeration class 'EnumClass' as below:

select * from MyEvent where enumProp=EnumClass.valueOf('ENUM_VALUE_1')

Instance methods may also be invoked on event instances by specifying a stream name, as shown below:

select myevent.computeSomething() as result from MyEvent as myevent

Java classes that do not follow JavaBean conventions, such as legacy Java classes that expose public fields, or methods not following naming conventions, require additional configuration. Via configuration it is also possible to control case sensitivity in property name resolution. The relevant section in the chapter on configuration is Section 10.4.1.3, “Non-JavaBean and Legacy Java Event Classes”.

2.5. java.util.Map Events

Events can also be represented by objects that implement the java.util.Map interface. Event properties of Map events are the values in the map accessible through the get method exposed by the java.util.Map interface.

The engine can process java.util.Map events via the sendEvent(Map map, String eventTypeAlias) method on the EPRuntime interface. Entries in the Map represent event properties. Keys must be of type java.util.String for the engine to be able to look up event property names specified by pattern or EQL statements.

Map event properties can be of any type. Map event properties that are Java application objects or that are of type java.util.Map offer additional power:

  • Properties that are Java application objects can be queried via the nested, indexed, mapped and dynamic property syntax as outlined earlier.

  • Properties that are of type Map allow Maps to be nested arbitrarily deep and thus can be used to represent complex domain information. The nested, indexed, mapped and dynamic property syntax can be used to query Maps within Maps..

In order to use Map events, the event type name and property names and types must be made known to the engine via Configuration. Please see the examples in Section 10.4.2, “Events represented by java.util.Map”.

The code snippet below creates and processes a Map event. The example assumes the CarLocationUpdateEvent event type alias has been configured.

Map event = new HashMap();
event.put("carId", carId);
event.put("direction", direction);
epRuntime.sendEvent(event, "CarLocUpdateEvent");

The CarLocUpdateEvent can now be used in a statement:

select carId from CarLocUpdateEvent.win:time(1 min) where direction = 1

The engine can also query Java objects as values in a Map event via the nested property syntax. Thus Map events can be used to aggregate multiple data structures into a single event and query the composite information in a convenient way. The example below demonstrates a Map event with a transaction and an account object.

Map event = new HashMap();
event.put("txn", txn);
event.put("account", account);
epRuntime.sendEvent(event, "TxnEvent");

An example statement could look as follows.

select account.id, account.rate * txn.amount from TxnEvent.win:time(60 sec) group by account.id

2.6. org.w3c.dom.Node XML Events

Events can also be represented as org.w3c.dom.Node instances and send into the engine via the sendEvent method on EPRuntime. Please note that configuration is required for allowing the engine to map the event type alias to Node element names. See Chapter 10, Configuration.

Esper allows configuring XPath expressions as event properties. You can specify arbitrary XPath functions or expressions and provide a property name by which their result values will be available for use in expressions. For XML documents that follow an XML schema, Esper can load and interrogate your schema and validate event property names and types against the schema information.

Nested, mapped and indexed event properties are also supported in expressions against org.w3c.dom.Node events. Thus XML trees can conveniently be interrogated using the existing event property syntax for querying JavaBean objects, JavaBean object graphs or java.util.Map events.

Let's look at how a sample XML document could be queried, given the sample XML below.

<?xml version="1.0" encoding="UTF-8"?>
<Sensor>
	<ID>urn:epc:1:4.16.36<ID>
	<Observation Command="READ_PALLET_TAGS_ONLY">
		<ID>00000001<ID>
		<Tag>
			<ID>urn:epc:1:2.24.400<ID>
		</Tag>
		<Tag>
			<ID>urn:epc:1:2.24.401<ID>
		</Tag>
	</Observation>
</Sensor>

To configure the engine for processing Sensor documents, simply configure a SensorEvent event type alias for the Sensor element name via Configuration. Now the document can be queried as below.

select ID, Observation.ID, Observation.Command, Observation.Tag[0], countTags
from SensorEvent.win:time(30 sec)

The equivalent XPath expressions to each of the properties are listed below.

  • The equivalent XPath expression to Observeration.ID is /Sensor/Observation/ID

  • The equivalent XPath expression to Observeration.Command is /Sensor/Observation/@Command

  • The equivalent XPath expression to Observeration.Tag[0] is /Sensor/Observation/Tag[position() = 1]

  • The equivalent XPath expression to countTags is count(/Sensor/Observation/Tag) for returning a count of tag elements. This assumes the countTags property has been configured as an XPath property.

By specifying an event property such below:

nestedElement.mappedElement('key').indexedElement[1]

The equivalent XPath expression is as follows:

/simpleEvent/nestedElement/mappedElement[@id='key']/indexedElement[position() = 2]

Chapter 3. Processing Model

3.1. Introduction

The Esper processing model is continuous: Update listeners to statements receive updated data as soon as the engine processes events for that statement, according to the statement's choice of event streams, views, filters and output rates.

As outlined in Chapter 9, API Reference the interface for listeners is net.esper.client.UpdateListener. Implementations must provide a single update method that the engine invokes when results become available:

The engine provides statement results to update listeners by placing results in net.esper.event.EventBean instances. A typical listener implementation queries the EventBean instances via getter methods to obtain the statement-generated results.

The get method on the EventBean interface can be used to retrieve result columns by name. The property name supplied to the get method can also be used to query nested, indexed or array properties of object graphs as discussed in more detail in Chapter 2, Event Representations.

The getUnderlying method on the EventBean interface allows update listeners to obtain the underlying event object. For wildcard selects, the underlying event is the event object that was sent into the engine via the sendEvent method. For joins and select clauses with expressions, the underlying object implements java.util.Map.

3.2. Insert Stream

In this section we look at the output of a very simple EQL statement. The statement selects an event stream without using a data window and without applying any filtering, as follows:

select * from Withdrawal

This statement selects all Withdrawal events. Every time the engine processes an event of type Withdrawal or any sub-type of Withdrawal, it invokes all update listeners, handing the new event to each of the statement's listeners.

The term insert stream denotes the new events arriving, and entering a data window or aggregation. The insert stream in this example is the stream of arriving Withdrawal events, and is posted to listeners as new events.

The diagram below shows a series of Withdrawal events 1 to 6 arriving over time. The number in parenthesis is the withdrawal amount, an event property that is used in the examples that discuss filtering.

Output example for a simple statement

Figure 3.1. Output example for a simple statement

The example statement above results in only new events and no old events posted by the engine to the statement's listeners.

3.3. Insert and Remove Stream

A length window instructs the engine to only keep the last N events for a stream. The next statement applies a length window onto the Withdrawal event stream. The statement serves to illustrate the concept of data window and events entering and leaving a data window:

select * from Withdrawal.win:length(5)

The size of this statement's length window is five events. The engine enters all arriving Withdrawal events into the length window. When the length window is full, the oldest Withdrawal event is pushed out the window. The engine indicates to listeners all events entering the window as new events, and all events leaving the window as old events.

While the term insert stream denotes new events arriving, the term remove stream denotes events leaving a data window, or changing aggregation values. In this example, the remove stream is the stream of Withdrawal events that leave the length window, and such events are posted to listeners as old events.

The next diagram illustrates how the length window contents change as events arrive and shows the events posted to an update listener.

Output example for a length window

Figure 3.2. Output example for a length window

As before, all arriving events are posted as new events to listeners. In addition, when event W1 leaves the length window on arrival of event W6, it is posted as an old event to listeners.

Similar to a length window, a time window also keeps the most recent events up to a given time period. A time window of 5 seconds, for example, keeps the last 5 seconds of events. As seconds pass, the time window actively pushes the oldest events out of the window resulting in one or more old events posted to update listeners.

Note EQL supports optional istream and rstream keywords on select-clauses and on insert-into clauses. These instruct the engine to only forward events that enter or leave data windows, or select only current or prior aggregation values, i.e. the insert stream or the remove stream.

3.4. Filters and Where-clauses

Filters to event streams allow filtering events out of a given stream before events enter a data window. The statement below shows a filter that selects Withdrawal events with an amount value of 200 or more.

select * from Withdrawal(amount>=200).win:length(5)

With the filter, any Withdrawal events that have an amount of less then 200 do not enter the length window and are therefore not passed to update listeners. Filters are discussed in more detail in Section 4.4.1, “Filter-based Event Streams” and Section 5.4, “Filter Expressions In Patterns”.

Output example for a statement with an event stream filter

Figure 3.3. Output example for a statement with an event stream filter

The where-clause and having-clause in statements eliminate potential result rows at a later stage in processing, after events have been processed into a statement's data window or other views.

The next statement applies a where-clause to Withdrawal events. Where-clauses are discussed in more detail in Section 4.5, “Specifying Search Conditions: the Where Clause”.

select * from Withdrawal.win:length(5) where amount >= 200

The where-clause applies to both new events and old events. As the diagram below shows, arriving events enter the window however only events that pass the where-clause are handed to update listeners. Also, as events leave the data window, only those events that pass the conditions in the where-clause are posted to listeners as old events.

Output example for a statement with where-clause

Figure 3.4. Output example for a statement with where-clause

The where-clause can contain complex conditions while event stream filters are more restrictive in the type of filters that can be specified. The next statement's where-clause applies the ceil function of the java.lang.Math Java library class in the where clause. The insert-into clause makes the results of the first statement available to the second statement:

insert into WithdrawalFiltered select * from Withdrawal where Math.ceil(amount) >= 200

select * from WithdrawalFiltered

3.5. Time Windows

In this section we explain the output model of statements employing a time window view and a time batch view.

3.5.1. Time Window

A time window is a moving window extending to the specified time interval into the past based on the system time. Time windows enable us to limit the number of events considered by a query, as do length windows.

As a practical example, consider the need to determine all accounts where the average withdrawal amount per account for the last 4 seconds of withdrawals is greater then 1000. The statement to solve this problem is shown below.

select account, avg(amount) 
from Withdrawal.win:time(4 sec) 
group by account
having amount > 1000

The next diagram serves to illustrate the functioning of a time window. For the diagram, we assume a query that simply selects the event itself and does not group or filter events.

select * from Withdrawal.win:time(4 sec)

The diagram starts at a given time t and displays the contents of the time window at t + 4 and t + 5 seconds and so on.

Output example for a statement with a time window

Figure 3.5. Output example for a statement with a time window

The activity as illustrated by the diagram:

  1. At time t + 4 seconds an event W1 arrives and enters the time window. The engine reports the new event to update listeners.

  2. At time t + 5 seconds an event W2 arrives and enters the time window. The engine reports the new event to update listeners.

  3. At time t + 6.5 seconds an event W3 arrives and enters the time window. The engine reports the new event to update listeners.

  4. At time t + 8 seconds event W1 leaves the time window. The engine reports the event as an old event to update listeners.

3.5.2. Time Batch

The time batch view buffers events and releases them every specified time interval in one update. Time windows control the evaluation of events, as does the length batch window.

The next diagram serves to illustrate the functioning of a time batch view. For the diagram, we assume a simple query as below:

select * from Withdrawal.win:time_batch(4 sec)

The diagram starts at a given time t and displays the contents of the time window at t + 4 and t + 5 seconds and so on.

Output example for a statement with a time batch view

Figure 3.6. Output example for a statement with a time batch view

The activity as illustrated by the diagram:

  1. At time t + 1 seconds an event W1 arrives and enters the batch. No call to inform update listeners occurs.

  2. At time t + 3 seconds an event W2 arrives and enters the batch. No call to inform update listeners occurs.

  3. At time t + 4 seconds the engine processes the batched events and a starts a new batch. The engine reports events W1 and W2 to update listeners.

  4. At time t + 6.5 seconds an event W3 arrives and enters the batch. No call to inform update listeners occurs.

  5. At time t + 8 seconds the engine processes the batched events and a starts a new batch. The engine reports the event W3 as new data to update listeners. The engine reports the events W1 and W2 as old data (prior batch) to update listeners.

3.6. Batch Windows

The built-in data windows that act on batches of events are the win:time_batch and the win:length_batch views. The win:time_batch data window collects events arriving during a given time interval and posts collected events as a batch to listeners at the end of the time interval. The win:length_batch data window collects a given number of events and posts collected events as a batch to listeners when the given number of events has collected.

Let's look at how a time batch window may be used:

select account, amount from Withdrawal.win:time_batch(1 sec)

The above statement collects events arriving during a one-second interval, at the end of which the engine posts the collected events as new events (insert stream) to each listener. The engine posts the events collected during the prior batch as old events (remove stream). The engine starts posting events to listeners one second after it receives the first event and thereon.

For statements containing aggregation functions and/or a group by clause, the engine posts consolidated aggregation results for an event batch. For example, consider the following statement:

select sum(amount) as mysum from Withdrawal.win:time_batch(1 sec)

Note that output rate limiting also generates batches of events following the output model as discussed here.

3.7. Aggregation and Grouping

3.7.1. Insert and Remove Stream

Statements that aggregate events via aggregation functions also post remove stream events as aggregated values change.

Consider the following statement that alerts when 2 Withdrawal events have been received:

select count(*) as mycount from Withdrawal having count(*) = 2

When the engine encounters the second withdrawal event, the engine posts a new event to update listeners. The value of the "mycount" property on that new event is 2. Additionally, when the engine encounters the third Withdrawal event, it posts an old event to update listeners containing the prior value of the count. The value of the "mycount" property on that old event is also 2.

The istream or rstream keyword can be used to eliminate either new events or old events posted to listeners. The next statement uses the istream keyword causing the engine to call the listener only once when the second Withdrawal event is received:

select istream count(*) as mycount from Withdrawal having count(*) = 2

3.7.2. Output for Aggregations

Following SQL (Standard Query Language) standards for queries against relational databases, the presence or absence of aggregation functions and the presence or absence of the group by clause dictates the number of rows posted by the engine to listeners. The next sections outline the output model for batched events under aggregation and grouping. The examples also apply to data windows that don't batch events and post results continously as events arrive or leave data windows. The examples also apply to patterns providing events when a complete pattern matches.

In summary, as in SQL, if your query only selects aggregation values, the engine provides one row of aggregated values. It provides that row every time the aggregation is updated (insert stream), which is when events arrive or a batch of events gets processed, and when the events leave a data window or a new batch of events arrives. The remove stream then consists of prior aggregation values.

Also as in SQL, if your query selects non-aggregated values along with aggregation values in the select clause, the engine provides a row per event. The insert stream then consists of the aggregation values at the time the event arrives, while the remove stream is the aggregation value at the time the event leaves a data window, if any is defined in your query.

The next sections provide examples for each case.

3.7.2.1. Un-aggregated and Un-grouped

An example statement for the un-aggregated and un-grouped case is as follows:

select * from Withdrawal.win:time_batch(1 sec)

At the end of a time interval, the engine posts to listeners one row for each event arriving during the time interval.

3.7.2.2. Fully Aggregated and Un-grouped

If your statement only selects aggregation values and does not group, your statement may look as the example below:

select sum(amount) 
from Withdrawal.win:time_batch(1 sec)

At the end of a time interval, the engine posts to listeners a single row indicating the aggregation result. The aggregation result aggregates all events collected during the time interval.

3.7.2.3. Aggregated and Un-Grouped

If your statement selects non-aggregated properties and aggregation values, and does not group, your statement may be similar to this statement:

select account, sum(amount) 
from Withdrawal.win:time_batch(1 sec)

At the end of a time interval, the engine posts to listeners one row per event. The aggregation result aggregates all events collected during the time interval.

3.7.2.4. Fully Aggregated and Grouped

If your statement selects aggregation values and all non-aggregated properties in the select clause are listed in the group by clause, then your statement may look similar to this example:

select account, sum(amount) 
from Withdrawal.win:time_batch(1 sec) 
group by account

At the end of a time interval, the engine posts to listeners one row per unique account number. The aggregation result aggregates per unique account.

3.7.2.5. Aggregated and Grouped

If your statement selects non-aggregated properties and aggregation values, and groups only some properties using the group by clause, your statement may look as below:

select account, accountName, sum(amount) 
from Withdrawal.win:time_batch(1 sec) 
group by account

At the end of a time interval, the engine posts to listeners one row per event. The aggregation result aggregates per unique account.

3.8. EventBean Query Results

The engine posts events to UpdateListener implementations as net.esper.event.EventBean instances. The EventBean represents a row (event) in your continuous query's result set.

Use the iterator method on EPStatement statements to poll or read data out of statements, if you require read-based access to statement result sets. Statement iterators also return EventBean instances.

The EventBean interface offers property type metadata via the getEventType method returning an EventType. The EventType provides property name, property type and underlying type information. This information can be useful to dynamically interrogate query results. The underlying event that an EventBean represents can be obtained via the getUnderlying method. Please see Chapter 2, Event Representations for more information on different event underlying objects.

Consider a statement that returns the symbol, count of events per symbol and average price per symbol for tick events. Our sample statement may declare a fully-qualified Java class name as the event type: org.sample.StockTickEvent. Assume that this class exists and exposes a symbol property of type String, and a price property of type (Java primitive) double.

select symbol, avg(price) as avgprice, count(*) as mycount 
from org.sample.StockTickEvent 
group by symbol

The next table summarizes the property names and types as posted by the statement above:

Table 3.1. Properties offered by sample statement aggregating price

NameTypeDescriptionJava code snippet
symboljava.lang.StringValue of symbol event property
eventBean.get("symbol")
avgpricejava.lang.DoubleAverage price per symbol
eventBean.get("avgprice")
mycountjava.lang.LongNumber of events per symbol
eventBean.get("mycount")

A code snippet out of a possible UpdateListener implementation to this statement may look as below:

String symbol = (String) newEvents[0].get("symbol");
Double price= (Double) newEvents[0].get("avgprice");
Long count= (Long) newEvents[0].get("mycount");

The engine supplies the boxed java.lang.Double and java.lang.Long types as property values rather then primitive Java types. This is because aggregated values can return a null value to indicate that no data is available for aggregation. Also, in a select statement that computes expressions, the underlying event objects to EventBean instances are of type java.util.Map.

Consider the next statement that specifies a wildcard selecting the same type of event:

select * from org.sample.StockTickEvent where price > 100

The property names and types provided by an EventBean query result row, as posted by the statement above are as follows:

Table 3.2. Properties offered by sample wildcard-select statement

NameTypeDescriptionJava code snippet
symboljava.lang.StringValue of symbol event property
eventBean.get("symbol")
pricedoubleValue of price event property
eventBean.get("price")

As an alternative to querying individual event properties via the get methods, the getUnderlying method on EventBean returns the underlying object representing the query result. In the sample statement that features a wildcard-select, the underlying event object is of type org.sample.StockTickEvent:

StockTickEvent tick = (StockTickEvent) newEvents[0].getUnderlying();

Chapter 4. EQL Reference: Clauses

4.1. EQL Introduction

The Event Query Language (EQL) is a SQL-like language with SELECT, FROM, WHERE, GROUP BY, HAVING and ORDER BY clauses. Streams replace tables as the source of data with events replacing rows as the basic unit of data. Since events are composed of data, the SQL concepts of correlation through joins, filtering and aggregation through grouping can be effectively leveraged. The INSERT INTO clause is recast as a means of forwarding events to other streams for further downstream processing. External data accessible through JDBC may be queried and joined with the stream data. Additional clauses such as the PATTERN and OUTPUT clauses are also available to provide the missing SQL language constructs specific to event processing.

EQL statements are used to derive and aggregate information from one or more streams of events, and to join or merge event streams. This section outlines EQL syntax. It also outlines the built-in views, which are the building blocks for deriving and aggregating information from event streams.

EQL statements contain definitions of one or more views. Similar to tables in a SQL statement, views define the data available for querying and filtering. Some views represent windows over a stream of events. Other views derive statistics from event properties, group events or handle unique event property values. Views can be staggered onto each other to build a chain of views. The Esper engine makes sure that views are reused among EQL statements for efficiency.

The built-in set of views is:

  1. Data window views: win:length, win:length_batch, win:time, win:time_batch, win:time_length_batch, win:time_accum, win:ext_timed, ext:sort_window, ext:time_order, std:unique, std:groupby, std:lastevent

  2. Views that derive statistics: std:size, stat:uni, stat:linest, stat:correl, stat:weighted_avg, stat:cube

EQL provides the concept of named window. Named windows are data windows that can be inserted-into and deleted-from by one or more statements, and that can queried by one or more statements. Named windows have a global character, being visible and shared across an engine instance beyond a single statement. Use the CREATE WINDOW clause to create named windows. Use the INSERT INTO clause to insert data into a named window, the ON DELETE clause to remove events from a named window, and the ON SELECT clause to perform a non-continuous fire-once query on a named window. Finally, the name of the named window can occur in a statement's FROM clause to query a named window or include the named window in a join or subquery.

Variables can come in handy to parameterize statements and change parameters on-the-fly and in response to events. Variables can be used in an expression anywhere in a statement as well as in the output clause for dynamic control of output rates.

Esper can be extended by plugging-in custom developed views and aggregation functions.

4.2. EQL Syntax

EQL queries are created and stored in the engine, and publish results to listeners as events are received by the engine or timer events occur that match the criteria specified in the query. Events can also be obtained from running EQL queries via the safeIterator and iterator methods that provide a pull-data API.

The select clause in an EQL query specifies the event properties or events to retrieve. The from clause in an EQL query specifies the event stream definitions and stream names to use. The where clause in an EQL query specifies search conditions that specify which event or event combination to search for. For example, the following statement returns the average price for IBM stock ticks in the last 30 seconds.

select avg(price) from StockTick.win:time(30 sec) where symbol='IBM'

EQL queries follow the below syntax. EQL queries can be simple queries or more complex queries. A simple select contains only a select clause and a single stream definition. Complex EQL queries can be build that feature a more elaborate select list utilizing expressions, may join multiple streams, may contain a where clause with search conditions and so on.

[insert into insert_into_def]
select select_list
from stream_def [as name] [, stream_def [as name]] [,...]
[where search_conditions]
[group by grouping_expression_list]
[having grouping_search_conditions]
[output output_specification]
[order by order_by_expression_list]

4.2.1. Specifying Time Periods

Time-based windows as well as pattern observers and guards take a time period as a parameter. Time periods follow the syntax below.

time-period : [day-part] [hour-part] [minute-part] [seconds-part] [milliseconds-part]

day-part : number ("days" | "day")
hour-part : number ("hours" | "hour")
minute-part : number ("minutes" | "minute" | "min")
seconds-part : number ("seconds" | "second" | "sec")
milliseconds-part : number ("milliseconds" | "millisecond" | "msec")

Some examples of time periods are:

10 seconds
10 minutes 30 seconds
20 sec 100 msec
1 day 2 hours 20 minutes 15 seconds 110 milliseconds
0.5 minutes

4.2.2. Using Comments

Comments can appear anywhere in the EQL or pattern statement text where whitespace is allowed. Comments can be written in two ways: slash-slash (// ...) comments and slash-star (/* ... */) comments.

Slash-slash comments extend to the end of the line:

// This comment extends to the end of the line.
// Two forward slashes with no whitespace between them begin such comments.

select * from MyEvent  // this is a slash-slash comment

// All of this text together is a valid statement.

Slash-star comments can span multiple lines:

/* This comment is a "slash-star" comment that spans multiple lines.
 * It begins with the slash-star sequence with no space between the '/' and '*' characters.
 * By convention, subsequent lines can begin with a star and are aligned, but this is 
 * not required.
 */		
select * from MyEvent  /* this also works */

Comments styles can also be mixed:

select field1, // first comment
  /* second comment*/  field2
  from MyEvent

4.3. Choosing Event Properties And Events: the Select Clause

The select clause is required in all EQL statements. The select clause can be used to select all properties via the wildcard *, or to specify a list of event properties and expressions. The select clause defines the event type (event property names and types) of the resulting events published by the statement, or pulled from the statement via the iterator methods.

The select clause also offers optional istream and rstream keywords to control how events are posted to UpdateListener instances listening to the statement.

The syntax for the select clause is summarized below.

select [rstream | istream] * | expression_list ... 

4.3.1. Choosing all event properties: select *

The syntax for selecting all event properties in a stream is:

select * from stream_def

The following statement selects StockTick events for the last 30 seconds of IBM stock ticks.

select * from StockTick(symbol='IBM').win:time(30 sec)

The * wildcard and expressions can also be combined in a select clause. The combination selects all event properties and in addition the computed values as specified by any additional expressions that are part of the select clause. Here is an example that selects all properties of stock tick events plus a computed product of price and volume that the statement names 'pricevolume':

select *, price * volume as pricevolume from StockTick(symbol='IBM')

When using wildcard (*), Esper does not actually copy your event properties out of your event or events. It simply wraps your native type in an EventBean interface. Your application has access to the underlying event object through the getUnderlying method and has access to the property values through the get method.

In a join statement, using the select * syntax selects one event property per stream to hold the event for that stream. The property name is the stream alias name in the from clause.

4.3.2. Choosing specific event properties

To choose the particular event properties to return:

select event_property [, event_property] [, ...] from stream_def

The following statement selects the count and standard deviation properties for the last 100 events of IBM stock ticks for volume.

select count, stdev from StockTick(symbol='IBM').win:length(100).stat:uni('volume')

4.3.3. Expressions

The select clause can contain one or more expressions.

select expression [, expression] [, ...] from stream_def

The following statement selects the volume multiplied by price for a time batch of the last 30 seconds of stock tick events.

select volume * price from StockTick.win:time_batch(30 sec)

4.3.4. Renaming event properties

Event properties and expressions can be renamed using below syntax.

select [event property | expression] as identifier [, ...]

The following statement selects volume multiplied by price and specifies the name volPrice for the event property.

select volume * price as volPrice from StockTick.win:length(100)

4.3.5. Choosing event properties and events in a join

If your statement is joining multiple streams, your may specify property names that are unique among the joined streams, or use wildcard (*) as explained earlier.

In case the property name in your select or other clauses is not unique considering all joined streams, you will need to use the alias name of the stream as a prefix to the property.

This example is a join between the two streams StockTick and News, respectively named as 'tick' and 'news'. The example selects from the StockTick event the symbol value using the 'tick' stream alias as a prefix:

select tick.symbol from StockTick.win:time(10) as tick, News.win:time(10) as news

Use the wildcard (*) selector in a join to generate a property for each stream, with the property value being the event itself. The output events of the statement below have two properties: the 'tick' property holds the StockTick event and the 'news' property holds the News event:

select * from StockTick.win:time(10) as tick, News.win:time(10) as news

The following syntax can also be used to specify what stream's properties to select:

select stream_name.* [as alias] from ...

The selection of tick.* selects the StockTick stream events only:

select tick.* from StockTick.win:time(10) as tick, News.win:time(10) as news
where tick.symbol = news.symbol

The next example uses the as keyword to name each stream's joined events. This instructs the engine to create a property for each named event:

select tick.* as stocktick, news.* as news 
from StockTick.win:time(10) as tick, News.win:time(10) as news
where stock.symbol = news.symbol

The output events of the above example have two properties 'stocktick' and 'news' that are the StockTick and News events.

4.3.6. Choosing event properties and events from a pattern

If your statement employs pattern expressions, then your pattern expression tags events with a tag name. Each tag name becomes available for use as a property in the select clause and all other clauses.

For example, here is a very simple pattern that matches on every StockTick event received within 30 seconds after start of the statement. The sample selects the symbol and price properties of the matching events:

select tick.symbol as symbol, tick.price as price
from pattern[every tick=StockTick where timer:within(10 sec)]

The use of the wildcard selector, as shown in the next statement, creates a property for each tagged event in the output. The next statement outputs events that hold a single 'tick' property whose value is the event itself:

select * from pattern[every tick=StockTick where timer:within(10 sec)]

You may also select the matching event itself using the tick.* syntax. The engine outputs the StockTick event itself to listeners:

select tick.* from pattern[every tick=StockTick where timer:within(10 sec)]

4.3.7. Selecting istream and rstream events

The optional istream and rstream keywords in the select clause define the event stream posted to listeners to the statement.

If neither keyword is specified, the engine posts insert stream events via the newEvents parameter to the update method of UpdateListener instances listening to the statement. The engine posts remove stream events to the oldEvents parameter of the update method. The insert stream consists of the events entering the respective window(s) or stream(s) or aggregations, while the remove stream consists of the events leaving the respective window(s) or the changed aggregation result. See Chapter 3, Processing Model for more information on insert and remove streams.

By specifying the istream keyword you can instruct the engine to only post insert stream events via the newEvents parameter to the update method on listeners. The engine will then not post any remove stream events, and the oldEvents parameter is always a null value.

By specifying the rstream keyword you can instruct the engine to only post remove stream events via the newEvents parameter to the update method on listeners. The engine will then not post any insert stream events, and the oldEvents parameter is also always a null value.

The following statement selects only the events that are leaving the 30 second time window.

select rstream * from StockTick.win:time(30 sec)

The istream and rstream keywords in the select clause are matched by same-name keywords available in the insert into clause. While the keywords in the select clause control the event stream posted to listeners to the statement, the same keywords in the insert into clause specify the event stream that the engine makes available to other statements.

4.4. Specifying Event Streams: the From Clause

The from clause is required in all EQL statements. It specifies one or more event streams or named windows. Each event stream or named window can optionally be given a name by means of the as syntax.

from stream_def [as name] [, stream_def [as stream_name]] [, ...]

The event stream definition stream_def as shown in the syntax above can consists of either a filter-based event stream definition or a pattern-based event stream definition.

For joins and outer joins, specify two or more event streams. Joins between pattern-based and filter-based event streams are also supported.

Esper supports joins against relational databases for access to historical or reference data as explained in Section 4.13, “Joining Relational Data via SQL”. Esper can also join results returned by an arbitrary method invocation, as discussed in Section 4.14, “Joining Non-Relational Data via Method Invocation”.

The stream_name is an optional identifier assigned to the stream. The stream name can itself occur in any expression and provides access to the event itself from the named stream. Also, a stream name may be combined with a method name to invoke instance methods on events of that stream.

4.4.1. Filter-based Event Streams

For filter-based event streams, the event stream definition stream_def as shown in the from clause syntax consists of an event type, optional filter expressions and an optional list of views that derive data from a stream. The syntax for a filter-based event stream is as below:

event_type ( [filter_criteria] ) [.view_spec] [.view_spec] [...]

The following EQL statement shows event type, filter criteria and views combined in one statement. It selects all event properties for the last 100 events of IBM stock ticks for volume. In the example, the event type is the fully qualified Java class name org.esper.example.StockTick. The expression filters for events where the property symbol has a value of "IBM". The optional view specifications for deriving data from the StockTick events are a length window and a view for computing statistics on volume. The name for the event stream is "volumeStats".

select * from 
  org.esper.example.StockTick(symbol='IBM').win:length(100).stat:uni('volume') as volumeStats

Esper filters out events in an event stream as defined by filter criteria before it sends events to subsequent views. Thus, compared to search conditions in a where clause, filter criteria remove unneeded events early. In the above example, events with a symbol other then IBM do not enter the time window.

4.4.1.1. Specifying an event type

The simplest form of filter is a filter for events of a given type without any conditions on the event property values. This filter matches any event of that type regardless of the event's properties. The example below is such a filter.

select * from com.mypackage.myevents.RfidEvent

Instead of the fully-qualified Java class name any other event name can be mapped via Configuration to a Java class, making the resulting statement more readable:

select * from RfidEvent

Interfaces and superclasses are also supported as event types. In the below example IRfidReadable is an interface class.

select * from org.myorg.rfid.IRfidReadable

4.4.1.2. Specifying filter criteria

The filtering criteria to filter for events with certain event property values are placed within parenthesis after the event type name:

select * from RfidEvent(category="Perishable")

All expressions can be used in filters, including static methods that return a boolean value:

select * from com.mycompany.RfidEvent(MyRFIDLib.isInRange(x, y) or (x < 0 and y < 0))

Filter expressions can be separated via a single comma ','. The comma represents a logical AND between filter expressions:

select * from RfidEvent(zone=1, category=10)
...is equivalent to...
select * from RfidEvent(zone=1 and category=10)

The following operators are highly optimized through indexing and are the preferred means of filtering in high-volume event streams:

  • equals =

  • not equals !=

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

  • ranges

    • use the between keyword for a closed range where both endpoints are included

    • use the in keyword and round () or square brackets [] to control how endpoints are included

    • for inverted ranges use the not keyword and the between or in keywords

  • list-of-values checks using the in keyword or the not in keywords followed by a comma-separated list of values

At compile time as well as at run time, the engine scans new filter expressions for sub-expressions that can be indexed. Indexing filter values to match event properties of incoming events enables the engine to match incoming events faster. The above list of operators represents the set of operators that the engine can best convert into indexes. The use of comma or logical and in filter expressions does not impact optimizations by the engine.

4.4.1.3. Filtering Ranges

Ranges come in the following 4 varieties. The use of round () or square [] bracket dictates whether an endpoint is included or excluded. The low point and the high-point of the range are separated by the colon : character.

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

  • Closed ranges that contain both endpoints [low:high]. The equivalent 'between' keyword also defines a closed range.

  • 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]

The next statement shows a filter specifying a range for x and y values of RFID events. The range includes both endpoints therefore uses [] hard brackets.

mypackage.RfidEvent(x in [100:200], y in [0:100])

The between keyword is equivalent for closed ranges. The same filter using the between keyword is:

mypackage.RfidEvent(x between 100 and 200, y between 0 and 50)

The not keyword can be used to determine if a value falls outside a given range:

mypackage.RfidEvent(x not in [0:100])

The equivalent statement using the between keyword is:

mypackage.RfidEvent(x not between 0 and 100)

4.4.1.4. Filtering Sets of Values

The in keyword for filter criteria determines if a given value matches any value in a list of values.

In this example we are interested in RFID events where the category matches any of the given values:

mypackage.RfidEvent(category in ('Perishable', 'Container'))

By using the not in keywords we can filter events with a property value that does not match any of the values in a list of values:

mypackage.RfidEvent(category not in ('Household', 'Electrical'))

4.4.1.5. Filter Limitations

The following restrictions apply to filter criteria:

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

  • Aggregation functions are not allowed within filter expressions.

  • The prev</