Esper学习之六:EPL语法(二)
从上一篇开始说EPL的语法,主要是关于注解的。今天来说说比较常用的语法,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.表达式
除了查询完整对象和特定属性,EPL还支持属性值的计算,以计算后的值作为结果返回,并且也能设置别名。这个计算的式子就是表达式。例如:
- // 计算长方形的面积(长乘以宽)
- select length * width as area from Rectangle
除了简单的加减乘除,还可以利用事件流对象的某个方法。例如:
- // 计算长方形的面积(长乘以宽)
- select r.getArea(r.length,r.width) as area from Rectangle as r
- select r.getArea() as area from Rectangle as r
- // Rectangle类
- public class Rectangle
- {
- private int length;
- private int width;
- /** 省略getter/setter方法 **/
- // 外部传入参数计算面积
- public int getArea(int l, int w){
- return l*w;
- }
- // 计算该对象的面积
- public int getArea(){
- return length * width;
- }
- }
如上所示,一个方法需要传参,另一个方法不需要,但是他会利用当前事件的length和width来计算面积。而且要注意的是事件流需要设置别名才能使用其方法,如:r.getArea()
如果Rectangle类里没有计算面积的方法,但是提供了一个专门计算面积的静态方法,表达式也可以直接引用。不过要事先加载这个包含方法的类。例如:
- // 该类用于计算面积
- public class ComputeArea{
- public static int getArea(int length, int width){
- return length*width;
- }
- }
- // 加载
- epService.getEPAdministrator().getConfiguration().addImport(ComputeArea.class);
- // 调用ComputeArea的getArea方法计算面积
- select ComputeArea.getArea(length,width) from Rectangle
注意一定要是静态方法,不然没有实例化是没法引用的。
3.多事件流的查询
和SQL类似,EPL也可以同时对多个事件流进行查询,即join,但是必须对每个事件流设置别名。例如:
- // 当老师的id和学生的id相同时,查询学生的姓名和老师的姓名
- select s.name, t.name from Student.win:time(10) as s, Teacher.win:time(10) as t where s.id=t.id
如果想查询Student或者Teacher,则EPL改写如下:
- select s.* as st, t.* as tr from Student.win:time(10) as s, Teacher.win:time(10) as t where s.id=t.id
如果想要查询的属性只有存在于一个事件,那么可以不用"别名.属性名",但是最好还是带上别名,万一哪天另一个事件流多了一个一样的属性,那时候不需要修改EPL也可以使用。关于join,可看看这篇文章《Esper学习之九:EPL语法(五)》的join部分
4.insert和remove事件流
Esper对于事件流分输入和移出两种,分别对应监听器的两个参数newEvents和oldEvents,关于监听器的内容可参看《Esper学习之三:进程模型》。newEvents通常对应事件的计算结果,oldEvents可以理解过上一次计算结果。默认情况下,只有newEvents有值,oldEvents为null。如果需要查看oldEvents,则需要使用一个参数。例如:
- select rstream * from User
如果使用了该参数,则会将上一次计算结果放入newEvents内,而不是oldEvents(以前我还以为这是一个bug,后面发现手册上官方明确就是newEvents,汗!)。并且无法获得当前的计算结果
- select irstream * from User
如果使用了该参数,则会将当前的计算结果放入newEvents内,上一次的计算结果放入oldEvents内。
- select istream * from User
- // 等同于
- select * from User
如果使用了该参数,则会将当前的计算结果放入newEvents内,并且无法获得上一次的计算结果。同时该参数也是默认参数,可不写。
如果想修改默认参数,需要调用配置接口修改配置。
5.Distinct
distinct的用法和SQL一样,放在需要修饰的属性或者*前即可。例如:
- select distinct * from User.win:time(3 sec)
6.查询指定引擎的处理结果
除了上述所说的一些特点外,select还可以针对某个引擎进行查询。因为引擎都有自己的URI,所以可以在select句子中增加URI标识来指定查询哪一个引擎的事件处理情况。例如:
- // 引擎URI为Processor
- select Processor.MyEvent.myProperty from Processor.MyEvent
From Clause
1.语法介绍
From的语法不难,主要内容是针对事件流的处理。包括事件流过滤,事件流的维持等等。语法如下:
- from 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] [...]
unidirectional,retain-union,retain-intersection,contained_selection,view_spec这几个关键字因为涉及到view的知识,所以这里没法讲解。待学完view之后再来回顾这几个参数会很容易理解的。下面讲讲怎么过滤事件流
2.事件流过滤
2.1.事件属性过滤
事件流过滤通常情况都是对其中某个或多个属性加以限制来达到过滤的目的。注意,过滤表达式是紧跟在事件流名称之后而不是别名之后。例如:
- // 只有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等逻辑语言。例如:
- // 查询年龄大于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)
看以看到,过滤表达式写法很多,并且多个表达式同时作用于一个事件流,用逗号连接即可。如果说满足其中一个条件即可,则需要用or连接。
2.2.过滤范围
刚才说到过滤表达式使用的符号很多,总结下来基本上有<, >, <=, >=, =, !=, between, in, not in, and, or, [ ], ( )。这里主要说下between,in,( ),[ ]
between……and……
和SQL的between……and……意思一样,是一个闭区间。比如说between 10 and 15,中文语义为10到15之间并包含10和15.
( )
表示一个开区间,语法为(low:high)。如(10:15),表示10到15之间,并且不包含10和15
[ ]
表示一个闭区间,语法为[low:high]。如[10:15],表示10到15之间,并且包含10和15
( )和[ ]可以混合用。比如[10:15)或者(10:15]
in
配合( )和[ ]进行使用,表示值在某个范围内。比如:
- select name from User(age in [10:15))
相应的,not in表示不在此范围内。
以上都是针对数字的例子,in和not in同样可以作用于字符串。比如:
- select age from User(name in ('张三', '李四'))
2.3 静态方法过滤
除了上面说的这些符号以外,类似于select子句中使用的静态方法,过滤表达式中也可以使用,但是返回值必须为布尔值,不然会报错。例如:
- // 判断总数是否等于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))
事件流的过滤并不能弄得很复杂,他有一下几个限制:
1. 要过滤的属性只能是数字和字符串。
2. 过滤表达式中不能使用聚合函数。
3. “prev”和“prior”函数不能用于过滤表达式(暂且不考虑这是什么)
Select和From的基础内容基本上就是上面所说的。当学过后面的章节之后,select和from可以写得很复杂,才能支持更为复杂的业务需求。特别是学过view和一些event function之后,变化就更加多样了。下一篇将讲解别的Clause,敬请期待。