this.price = price;
}
public String getItemName() {
return itemName;
}
public double getPrice() {
return price;
}
}
2.EPL:EPL是ESPER的核心,它类似于SQL,但是和SQL的执行方式不同。SQL是数据在那里,你每次执行SQL就会触发一次查询;而EPL是查询在这里,数据输入达到一定条件即可触发查询。
这个条件可以有多种:
a).每个event对象来就触发一次查询,并只处理当前对象
select * from OrderEvent
这个EPL语句会在每个OrderEvent对象到达后,并将该event交给后续的Listener进行处理。但是这种用法不多见,意义不大。
b).窗口处理模式:
EPL最大的特色就是这个窗口处理模式,有两种窗口,时间窗口和长度窗口。
时间窗口:大家想一下,如果有一个场景,要获取最近3秒内OrderEvent的price的平均值,那该怎么做呢?一般的做法需要做个后台线程来做3秒的时间统计,时间到了再做后续处理,虽然不复杂,但是也挺繁琐的。
来看看EPL是怎么做的:
select avg(price) from test.OrderEvent.win:time(3 sec)
win:time(3sec)就是定义了3秒的时间窗口,avg(price)就是统计了3秒内的OrderEvent对象的price的平均值。
长度窗口:长度窗口和时间窗口比较类似
select avg(price) from test.OrderEvent.win:length(100)
win:length(10)就是定义了10个Event的,avg(price)就是统计了最近10个的OrderEvent对象的price的平均值。
EPL语法
EPL,全称Event Processing Language,是一种类似SQL的语言,包含了SELECT, FROM, WHERE, GROUP BY, HAVING 和 ORDER BY子句,同时用事件流代替了table作为数据源,并且能像SQL那样join,filtering和aggregation。所以如果各位有SQL基础的话,简单的EPL很容易掌握。除了select,EPL也有insert into,update,delete,不过含义和SQL并不是很接近。另外还有pattern和output子句,这两个是SQL所没有的。EPL还定义了一个叫view的东西,类似SQL的table,来决定哪些数据是可用的,Esper提供了十多个view,并且保证这些view可以被重复使用。而且用户还可以扩展view成为自定义view来满足需求。在view的基础上,EPL还提供了named window的定义,作用和view类似,但是更加灵活。。。
Select Clause和From Clause。这个两个可以说是写EPL必备,要想得到事件流的处理结果,基本上就靠他们俩了(Pattern除外)。
Select Clause
1.查询事件流的所有属性及特定属性
EPL的select和SQL的select很相近,SQL用*表示查询表的所有字段,而EPL用*表示查询事件流的所有属性值。SQL查询某个字段名,直接在select后跟字段名就ok,EPL也是将要查询的属性名放在select之后。若查多个属性值,则用逗号分割。和SQL一样,EPL查询属性也可以设置别名。示例如下:
// EPL:查询完整的User对象
select * from User
// 获取User对象
User u = newEvent.getUnderlying();
// EPL:查询User的name和id,id别名为i
select name, id as i from User
// 获取name和id
String name = (String)newEvent.get(“name”);
int id = (Integer)newEvent.get(“i”);
这里要注意,如果查询的是一个完整对象,需要调用getUnderlying()方法,而get方法是针对确定的属性名或者别名。另外*是不能设置别名的。
2.insert和remove事件流
Esper对于事件流分输入和移出两种,分别对应监听器的两个参数newEvents和oldEvents,关于监听器的内容可参看《Esper学习之三:进程模型》。newEvents通常对应事件的计算结果,oldEvents可以理解过上一次计算结果。默认情况下,只有newEvents有值,oldEvents为null。如果需要查看oldEvents,则需要使用一个参数。例如:
select rstream * from User
如果使用了该参数,则会将上一次计算结果放入newEvents内,而不是oldEvents。并且无法获得当前的计算结果。
select irstream * from User
如果使用了该参数,则会将当前的计算结果放入newEvents内,上一次的计算结果放入oldEvents内。
3.Aggregation
和SQL一样,EPL也有Aggregation,即聚合函数。语法如下:
aggregate_function([all|distinct] expression)
aggregate_function就是聚合函数的名字,比如avg,sum等。expression通常是事件流的某个属性,也可以是不同事件流的多个属性,或者是属性和常量、函数之间的运算。举例如下。
// 查询最新5秒的Apple的平均价格
select avg(price) as aPrice from Apple.win:time(5 sec)
// 查询最新10个Apple的价格总和的两倍
select sum(price*2) as sPrice from Apple.win:length(10)
// 查询最新10个Apple的价格,并用函数计算后再算平均值
select avg(Compute.getResult(price)) from Apple.win:length(10)
以上就是聚合函数的使用方法,除此之外需要注意以下几点:
1.聚合函数能用于Select和Having,但是不能用于Where
2.sum,avg,media,stddev,avedev只能计算数值,至于media,stddev和avedev代表什么意思,请自行百度。
3.Esper会忽略expression为null不让参与聚合运算,但是count函数除外,即使是null也认为是一个事件。如果事件流集合中没有包含任何事件,或者包含的事件中用于聚合计算的expression都是null(比如收集5秒内进入的事件即为一个事件流集合),则所有聚合函数都返回null。
4.Insert into
4.1 简单用法
EPL的Insert into和SQL的有比较大的区别。SQL是往一张表里插入数据,而EPL是把一个事件流的计算结果放入另一个事件流,然后可以对这个事件流进行别的计算。所以Insert into的一个好处就是可以将是事件流的计算结果不断级联,对于那种需要将上一个业务的结果数据放到下一个业务处理的场景再适合不过了。除此之外,Insert into还有合并多个计算结果的作用。到这里相信大家已经对他越来越好奇了,不急,咱们先来看看语法:
insert [istream | irstream | rstream] into event_stream_name [ (property_name [, property_name] ) ]
event_stream_name定义了事件流的名称,在执行完insert的定义之后,我们可以使用select对这个事件流进行别的计算。
istream | irstream | rstream表示该事件流允许另一个事件的输入/输入数据和输出/输出数据能够进入(解释好像很绕。。一会儿看例子就能明白了)
property_name表示该事件流里包含的属性名称,多个属性名之间用逗号分割,并且用小括号括起来。
上面的说明可能不是很好理解,咱们先看个例子:
//将新进入的Asus事件传递到Computer,且Asus的id,size和Computer的cid,csize对应
insert into Computer(cid,csize) select id,size from Asus
// 第二种写法
insert into Computer select id as cid, size as csize Asus
从例子中可以看到,insert into需要配合select进行使用,以表明前一个事件流有哪些计算结果将进入insert into定义的事件流。并且在select中的字段要和insert里的事件流的属性要对应(这里指的对应是数据类型对应,而且属性数量也必须一样)。如果说insert定义的事件流名称在之前已经定义过(insert into中定义的除外),重名是不允许的。
我个人推荐第二种写法,通过as设置的别名即为insert定义的事件流的属性,这样可以避免属性的个数不一致的错误。
参考博文: