www.espertech.comDocumentation

Chapter 18. Script Support

18.1. Overview
18.2. Syntax
18.3. Examples
18.4. Built-In EPL Script Attributes
18.5. Performance Notes
18.6. Additional Notes

Esper allows the use scripting languages within EPL. You may use scripts for imperative programming to execute certain code as part of EPL processing by the engine.

The syntax and examples outlined below discuss how to declare a script that is visible to the same EPL statement that listed the script.

For declaring scripts that are visible across multiple EPL statements i.e. globally visible scripts please consult Section 5.18.3, “Global Scripts” that explains the create expression clause.

Any scripting language that supports JSR 223 and also the MVEL scripting language can be specified in EPL. This section provides MVEL and JavaScript examples.

For more information on the MVEL scripting language and its syntax, please refer to the MVEL documentation. MVEL is an expression language that has a natural syntax for Java-based applications and compiles to provide fast execution times. To use MVEL with Esper, please make sure to add the MVEL jar file to the application classpath.

For more information on JSR 223 scripting languages, please refer to external resources. As JSR 223 defines a standard API for script engines, your application may use any script engine that implements the API. Current JVM versions ship with a JavaScript script engine. Other script engines such as Groovy, Ruby and Python scripts can be used as implementations of JSR 223.

As an alternative to a script consider providing a custom single row function as described in Section 17.3, “Single-Row Function”

The syntax for scripts is:

expression [return_type] [@type(eventtype_name)] [dialect_identifier:] script_name [ (parameters) ] [ script_body ]

Use the expression keyword to declare a script.

The return_type is optional. If the script declaration provides a return type the engine can perform strong type checking: Any expressions that invoke the script and use the return value are aware of the return type. If no return type is provided the engine assumes the script returns java.lang.Object.

If the return type of the script is EventBean[] you must provide the @type(name) annotation after the return type to name the event type of events returned by the script. The @type is allowed only when the return type is EventBean instances.

The dialect_identifier is optional and identifies the scripting language. Use mvel for MVEL , js for JavaScript and python for Python and similar for other JSR 223 scripting languages. If no dialect identifier is specified, the default dialect that is configured applies, which is js unless your application changes the default configuration.

It follows the script name. You may use the same script name multiple times and thus overload providing multiple signatures under the same script name. The combination of script name and number of parameters must be unique however.

If you have script parameters, specify the parameter names for the script as a comma-separated list of identifiers in parenthesis. It is not necessary to list parameter types.

The script body is the actual MVEL or JavaScript or other scripting language script and is placed in square brackets: [ ... script body ...].

The next example shows an EPL statement that calls a JavaScript script which computes the Fibonacci total for a given number:

expression double js:fib(num) [
fib(num);
function fib(n) {
  if(n <= 1)
    return n;
  return fib(n-1) + fib(n-2);
}
]
select fib(intPrimitive) from SupportBean;

The expression keyword is followed by the return type (double), the dialect (js) and the script name (fib) that declares a single parameter (num). The JavaScript code that computes the Fibonacci total is between square brackets [].

The following example shows an EPL statement that calls a MVEL script which outputs all the different colors that are listed in the colors property of each ColorEvent:

expression mvel:printColors(colors) [
String c = null;
for (c : colors) {
   System.out.println(c);
}
]
select printColors(colors) from ColorEvent;

This example instead uses JavaScript to print colors and passes the event itself as a script parameter (this example is for Java 8 and Nashorn):

expression js:printColors(colorEvent) [
  print(java.util.Arrays.toString(colorEvent.getColors()));
]
select printColors(colorEvent) from ColorEvent as colorEvent

The next example creates a globally-visible script that returns ItemEvent events, assuming that the ItemEvent event type is an event type defined by create schema ItemEvent(id string):

create expression EventBean[] @type(ItemEvent) js:myScriptReturnsEvents() [
myScriptReturnsEvents();
function myScriptReturnsEvents() {
  var EventBeanArray = Java.type(\"com.espertech.esper.client.EventBean[]\");
  var events = new EventBeanArray(1);
  events[0] = epl.getEventBeanService().adapterForMap(java.util.Collections.singletonMap(\"id\", \"id1\"), \"ItemEvent\");
  return events;
}

// sample EPL:
// select myScriptReturnsEvents().where(v => v.id in ('id1', 'id3')) from MyEvent]

The engine provides a built-in script object under the variable name epl to all scripts. Your scripts may use this script object to share and retain state by setting and reading script attributes. The engine maintains a separate script object per context partition or per statement if not declaring a context. Therefore script attributes are not shared between statements, however multiple scripts executed by the same context partition receive the same script object.

The epl script object implements the interface com.espertech.esper.client.hook.EPLScriptContext. Please see the JavaDoc for services provided by EPLScriptContext.

For script state management, the EPLScriptContext interface has two methods: The void setScriptAttribute(String attribute, Object value) method to set an attribute value and the Object getScriptAttribute(String attribute) method to read an attribute value.

The next example demonstrates the use of the epl script object. It outputs a flag value true when an RFID event matched because the location is A, and outputs a flag value false when an RFID event matched because the location is B. The example works the same for either MVEL or JavaScript dialects: You may simple replace the js dialect with mvel.

expression boolean js:setFlag(name, value, returnValue) [
  if (returnValue) epl.setScriptAttribute(name, value);
  returnValue;
]
expression js:getFlag(name) [
  epl.getScriptAttribute(name);
]
select getFlag('locA') as flag from RFIDEvent(zone = 'Z1' and
  (setFlag('locA', true, location = 'A') or setFlag('locA', false, location = 'B')) )

The example above utilizes two scripts: The setFlag script receives an attribute name, attribute value and a return value. The script sets the script attribute only when the return value is true. The getFlag script simply returns the script attribute value.

Upon EPL statement compilation, the engine resolves script parameter types and performs script compilation. At runtime the engine evaluates the script in its compiled form.

As the engine cannot inspect scripts if is not possible for the engine to perform query planning or many optimizations based on the information in scripts. It is thus recommended to structure EPL such that basic filter and join expressions are EPL expressions and not script expressions.

Your EPL may declare a return type for the script. If no return type is declared and when using the MVEL dialect, the engine will infer the return type from the MVEL expression analysis result. If the return type is not provided and cannot be inferred or the dialect is not MVEL, the return type is Object.

If the EPL declares a numeric return type then engine performs coercion of the numeric result to the return type that is specified.

In the case that the EPL declares a return type that does not match the type of the actual script return value, the engine does not check return value type.