1、EPL Syntax
1 | - [annotations] |
2、Time Periods
1 | - time-period : [year-part] [month-part] [week-part] [day-part] [hour-part] |
示例:1
2
3
4
5// 计算过去的5分3秒中进入该语句的Fruit事件的平均price
select avg(price) from Fruit.win:time(5 minute 3 sec)
// 每一天输出一次用户的账户总额
select sum(account) from User output every 1 day
注:Esper规定每月天数30天
3、Comments
示例:1
2
3
4
5
6
7
8
9
10
11
12
13a.单行注释
// This comment extends to the end of the line.
// Two forward slashes with no whitespace between them begin such comments.
select * from MyEvent
b.多行注释
select * from OtherEvent
/* this is a very
*important Event*/
c.混合注释
select field1 // first comment
/* second comment */ field from MyEvent
4、Reserved Keywords
如果epl中的某个字段属于关键字 用括起来
1
2
3
4
5// insert和order是关键字,EPL无效
select insert from Order
// `insert`和`Order`是属性名和事件流名称,EPL有效
select `insert` from `Order`
5、Escaping Strings
在EPL中,字符串用单引号或双引号括起来,但若字符串包含单、双引号
示例:1
2
3select * from OrderEvent(name=`John`)
// 等同于
select * from OrderEvent(name="John")
如果name=John’s,则需要反斜杠转义1
2
3select * from OrderEvent(name="John\"s")
// 或者
select * from OrderEvent(name='john\'s')
除了反斜杠,还可以用unicode来表示单、双引号1
2
3select * from OrderEvent(name="John\u0022s")
// 或者
select * from OrderEvent(name='john\u0027s')
注:Java编写EPL的时候,反斜杠和无含义的双引号还得转义,不然会和String的双引号冲突1
2
3epService.getEPAdministrator().createEPL("select * from OrderEvent(name='John\\'s')");
// ... and for double quotes...
epService.getEPAdministrator().createEPL("select * from OrderEvent(name=\"Quote \\\"Hello\\\"\")");
6、Data Type
EPL支持Java所有数据类型,包括基本类型及其包装类,同时还支持java.math.BigInteger和java.math.BigDecimal,并能自动转换数据类型不丢失精度(比如short转int,int转short则不行),要在EPL内数据转换,可以用cast函数
示例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83import com.espertech.esper.client.EPAdministrator;
import com.espertech.esper.client.EPRuntime;
import com.espertech.esper.client.EPServiceProvider;
import com.espertech.esper.client.EPServiceProviderManager;
import com.espertech.esper.client.EPStatement;
import com.espertech.esper.client.EventBean;
import com.espertech.esper.client.UpdateListener;
/**
* 可用cast函数将其他的数值数据类型转为BigDecimal。
*
* @author luonanqin
*
*/
class Banana
{
private int price;
public int getPrice()
{
return price;
}
public void setPrice(int price)
{
this.price = price;
}
}
class CastDataTypeListener1 implements UpdateListener
{
public void update(EventBean[] newEvents, EventBean[] oldEvents)
{
if (newEvents != null)
{
EventBean event = newEvents[0];
// cast(avg(price), int)中间的空格在EPL中可以不写,但是event.get的时候必须加上,建议用as一个别名来代表转换后的值
System.out.println("Average Price: " + event.get("cast(avg(price), int)") + ", DataType is "
+ event.get("cast(avg(price), int)").getClass().getName());
}
}
}
class CastDataTypeListener2 implements UpdateListener
{
public void update(EventBean[] newEvents, EventBean[] oldEvents)
{
if (newEvents != null)
{
EventBean event = newEvents[0];
System.out.println("Average Price: " + event.get("avg(price)") + ", DataType is " + event.get("avg(price)").getClass().getName());
}
}
}
public class CastDataTypeTest
{
public static void main(String[] args) throws InterruptedException
{
EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider();
EPAdministrator admin = epService.getEPAdministrator();
String banana = Banana.class.getName();
String epl1 = "select cast(avg(price),int) from " + banana + ".win:length_batch(2)";
String epl2 = "select avg(price) from " + banana + ".win:length_batch(2)";
EPStatement state1 = admin.createEPL(epl1);
state1.addListener(new CastDataTypeListener1());
EPStatement state2 = admin.createEPL(epl2);
state2.addListener(new CastDataTypeListener2());
EPRuntime runtime = epService.getEPRuntime();
Banana b1 = new Banana();
b1.setPrice(1);
runtime.sendEvent(b1);
Banana b2 = new Banana();
b2.setPrice(2);
runtime.sendEvent(b2);
}
}
结果:1
2Average Price: 1, DataType is java.lang.Integer
Average Price: 1.5, DataType is java.lang.Double
注:如果某个数除以0,默认会返回正无穷大或者负无穷大,可以配置用null来代替
7、Annotation
EPL可以写注释1
2
3
4
5
6
7
8// 不包含参数或者单个参数的注解
_name [(annotation_parameters)]
// 包含多个属性名-值对的注解
_name (attribute_name = attribute_value, [name=value, ...])
// 多个注解联合使用
_name [(annotation_parameters)] [ _name [(annotation_parameters)]] [...]
- @Name 指定EPL的名称,参数只有一个。例如:@Name(“MyEPL”)
- @Description 对EPL进行描述,参数只有一个。例如:@Description(“This is MyEPL”)
- @Tag 对EPL进行额外的说明,参数有两个,分别为Tag的名称和Tag的值,用逗号分隔。例如:@Tag(name=”author”,value=”luonanqin”)
- @Priority 指定EPL的优先级,参数只有一个,并且整数(可负可正)。例如:@Priority(10)
- @Drop 指定事件经过此EPL后不再参与其他的EPL计算,该注解无参数。
- @Hint 为EPL加上某些标记,让引擎对此EPL产生其他的操作,会改变EPL实例的内存占用,但通常不会改变输出。其参数固定,由Esper提供,之后的篇幅会穿插讲解这个注解的使用场景。
- @Audit EPL添加此注解后,可以额外输出EPL运行情况,有点类似日志的感觉(当然没有日志的功能全啦),具体使用场景在此先不提。
- @Hook 与SQL相关,这里暂且不说
- @EventRepresentation 这是用来指定EPL产生的计算结果事件包含的数据形式。参数只有一个,即array=true或array=false。false为默认值,代表数据形式为Map,若为true,则数据形式为数组。
示例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70import com.espertech.esper.client.EPAdministrator;
import com.espertech.esper.client.EPRuntime;
import com.espertech.esper.client.EPServiceProvider;
import com.espertech.esper.client.EPServiceProviderManager;
import com.espertech.esper.client.EPStatement;
import com.espertech.esper.client.EventBean;
import com.espertech.esper.client.UpdateListener;
class Apple
{
private int price;
public int getPrice()
{
return price;
}
public void setPrice(int price)
{
this.price = price;
}
}
class SomeAnnotationListener implements UpdateListener
{
public void update(EventBean[] newEvents, EventBean[] oldEvents)
{
if (newEvents != null)
{
EventBean event = newEvents[0];
// 当加上注解@EventRepresentation(array=true)时,结果事件类型为数组而不是Map。
// array=false时,也就是默认情况,结果事件类型为数组是Map。
System.out.println("Sum Price: " + event.get("sum(price)") + ", Event Type is " + event.getEventType().getUnderlyingType());
}
}
}
public class SomeAnnotationTest
{
public static void main(String[] args) throws InterruptedException
{
EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider();
EPAdministrator admin = epService.getEPAdministrator();
String apple = Apple.class.getName();
String epl1 = "@Priority(10)@EventRepresentation(array=true) select sum(price) from " + apple + ".win:length_batch(2)";
String epl2 = "@Name(\"EPL2\")select sum(price) from " + apple + ".win:length_batch(2)";
String epl3 = "@Drop select sum(price) from " + apple + ".win:length_batch(2)";
UpdateListener listenenr = new SomeAnnotationListener();
EPStatement state1 = admin.createEPL(epl1);
state1.addListener(listenenr);
EPStatement state2 = admin.createEPL(epl2);
state2.addListener(listenenr);
System.out.println("epl2's name is " + state2.getName());
EPStatement state3 = admin.createEPL(epl3);
state3.addListener(listenenr);
EPRuntime runtime = epService.getEPRuntime();
Apple a1 = new Apple();
a1.setPrice(1);
runtime.sendEvent(a1);
Apple a2 = new Apple();
a2.setPrice(2);
runtime.sendEvent(a2);
}
}
结果:1
2
3
4epl2's name is EPL2
Sum Price: 3, Event Type is class [Ljava.lang.Object;
Sum Price: 3, Event Type is interface java.util.Map
Sum Price: 3, Event Type is interface java.util.Map
@Name和@EventRepresentation起作用了,但@Priority和@Drop没用,这是需要配置才能生效
8、Expression
Expression类似自定义函数,通常用Lambda表达式来建立,而Lambda表达式就一个”=>”符号,表示”gose to”,符号左边表示输入参数,右边表示计算过程,计算结果就是这个表达式的返回值
语法:1
expression expression_name{ expression_body }
expression是关键字
expression_name为expression的名称(唯一)
expression_body是expression的具体内容1
expression_body: (input_param [,input_param[,...]]) => expression
input_param必须为事件流的别名,注意不是事件流名。参数名写什么都可以,多个参数逗号分隔,并用圆括号括起来
示例:1
experssion middle {x => (x.max + x.min)/2 } select middle(apple) from Apple as apple
x:表示输入参数
x.max x.min:x代表的事件流属性,若事件流没有这个属性,则expression定义错误
express的定义必须放在句子斥千万次
对于多个参数的expression定义1
expressin sumage { (x,y) => x.age+y.age } select sumage(me,you) from Me as me, You as you
Select Clause
1、查询事件流的所有属性以及特定属性
1 | // EPL:查询完整的User对象 |
2、表达式
除了查询完整对象和特定属性,EPL还支持属性值的计算,以计算后的值作为结果返回,也能设置别名
示例:1
2// 求三长方形的面积
select length * width as area from Rectangle
除了加减乘除,还可以用事件流对象的某个方法
示例:1
2
3
4// 计算长方形面积
select r.getArea(r.length,r.width) as area from Rectangle as r
select r.getArea() as area from Rectangle as r
1 | // Rectangle类 |
如上,一个需要传参,一个不需要,并且要注意事件流需要设置别名才能使用方法,如r.getArea()
如果Rectangle没有计算面积方法,但是有一个专门计算面积的静态方法,表达式也可以调用,不过要事先加载这个包含方法的类
示例:1
2
3
4
5
6
7
8
9
10// 该类用于计算面积
public class ComputeArea{
public static int getArea(int length, int width){
return length*width;
}
}
// 加载
epService.getEPAdministrator().getConfiguration().addImport(ComputeArea.class);
1 | // 调用ComputeArea的getArea方法计算面积 |
注:一定要是静态方法,不然没有实例化是没法引用
4、insert和remove事件流
Esper对于事件流分输入和移出两种,分别对应监听器的两个参数newEvents和oldEvents,new通常对应事件的计算结果,old可以理解为上一次计算结果。默认情况下,只有new有值,old为null,若需要查看,则要使用一个参数
select rstream * from User
若使用了该参数,则会将上一次计算结果放入newEvents内,而不是oldEvents(没写错,是newEvents),且无法获得当前计算结果
1 | select irstream * from User |
1 | select istream * from User |
如果使用了该参数,会将当前结果放入new内,并无法获得上次的计算结果,同时该参数也是默认参数,可不写
5、Distinct
distinct的用法和SQL一样1
select distinct * from User.win:time(3 sec)
6、查询指定引擎的处理结果
select还可以针对某个引擎进行查询,因为引擎都有自己的URI,可以在select句子中增加URI标识来指定查询拿一个引擎的事件处理情况1
2// 引擎URI为Processor
select Processor.MyEvent.myProperty from Processor.MyEvent
From Clause
1、语法介绍
from主要内容是针对事件流的处理,包括事件流过滤,事件流的维持等等1
2
3
4from stream_def [as name] [unidirectional] [retain-union | retain-intersection] [, stream_def [as stream_name]] [, ...]
// 事件流
event_stream_name [(filter_criteria)] [contained_selection] [.view_spec] [.view_spec] [...]
2、事件流过滤
事件流过滤通常情况都是对其中某个或多个属性加以限制来达到过滤的目的,过滤表达式是紧跟在事件流名称之后而不是别名之后1
2
3
4
5
6
7// 只有age大于10的User对象才可查询到name值
select name from User(age>10) as user
// 当name=“luonanqin”时,可获得其age值
select age from User(name="luonanqin")
// 错误写法
select name from User as user(age>10)
过滤表达式写法多种多样,可以用符号,又或者使用and、or、between等逻辑语言1
2
3
4
5
6// 查询年龄大于15小于18的学生的姓名
select name from Student(age between 15 and 18)
// 等同于
select name from Student(age >= 15 and age <= 18)
// 等同于
select name from Student(age >= 15, age <= 18)
()
表示开区间,(low:high),如(10:15),表示10到15之间,并且不包含10和15
[]
表示一个闭区间,[low:high],如[10:15],表示10到15之间,并且包含10和15
()和[]可以混合使用,例如[10:15)或者(10:15]
in
配合()[]进行使用,表示在某个范围内1
select name from User(age in [10:15])
相应的,not in 表示不在此范围内
in和not in可以作用于字符串1
select age from User(name in ('张三','李四'))
静态方法过滤
类似于select字句中使用静态方法,过滤表达式中也可以使用,但返回值必须为布尔值,不然会报错1
2
3
4
5
6
7
8
9
10
11
12
13
14// 判断总数是否等于0
public class IsZero
{
public static boolean isZero(int sum)
{
return sum==0;
}
}
// 加载
epService.getEPAdministrator().getConfiguration().addImport(IsZero.class);
// 查询没有钱的用户的name值(User包含name和money属性)
select name from User(IsZero.isZero(money))