www.espertech.comDocumentation
EPL 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 runtime.
The syntax and examples outlined below discuss how to declare a script that is visible to the same statement that listed the script.
For declaring scripts that are visible across multiple 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 the runtime, 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 execution, your application may use any script execution that implements the API. Current JVM versions ship with a JavaScript script execution. Other script executors 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 22.2, “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 compiler 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 compiler 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 a 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 a 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.common.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 compiler 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 runtime 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.common.client.hook.expr.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 statement compilation, the compiler resolves script parameter types and performs script compilation. At runtime the runtime evaluates the script in its compiled form.
As the compiler cannot inspect scripts if is not possible for the compiler 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 compiler 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 the compiler 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 compiler does not check return value type.