转载请注明出处:http://blog.csdn.net/luonanqin

上篇说到了Esper的Context,要是不了解的同学请参看《 Esper学习之四:Context 》,看过的同学如果还是不理解的话可以给我评论,我将会尽可能的解答。之前有些同学问我Context和Group by有什么区别,其实如果只是很简单的用Context,那么确实没太大区别,无非是在Context下select可以不包含group by修饰的属性。但是Group by明显没有Context强大,很多复杂的分组Group by是没法做到的。不过在能达到同样效果的情况下,我还是建议使用Group by,毕竟Context的名字是不能重复的,而且在高并发的情况下Context会短时间锁住。至于原因,这已经是Esper的高级篇了,这里暂且不说。

今天开始讲解Esper的重中之重——EPL。EPL可以说是Esper的核心,要是不会将简单的业务需求转化为EPL,更别说复杂的EPL了,最后就等着被客户骂吧。接下来的很多篇都会围绕EPL讲解,大概会有6篇吧,毕竟英文文档都有140页了,草草两篇根本就说不完。废话不多说,先简单介绍下什么是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类似,但是更加灵活。。。还有很多东西,我再列举估计大家都要晕了,就先说说语法吧。

1.EPL Syntax

[plain] view plaincopy
  1. [annotations]
  2. [expression_declarations]
  3. [context context_name]
  4. [insert into insert_into_def]
  5. select select_list
  6. from stream_def [as name] [, stream_def [as name]] [,...]
  7. [where search_conditions]
  8. [group by grouping_expression_list]
  9. [having grouping_search_conditions]
  10. [output output_specification]
  11. [order by order_by_expression_list]
  12. [limit num_rows]

基本上大部分的EPL都是按照这个格式来定义。看过之前几篇的同学应该对里面的某些内容熟悉,比如context context_name,select select_list from stream_def等等。其他的可以先不关心,后面会有详解。比如wher,group by都会有专门的篇幅进行描述。

2.Time Periods

这部分的内容说的是Esper中时间的表达形式。语法如下:

[plain] view plaincopy
  1. time-period : [year-part] [month-part] [week-part] [day-part] [hour-part]
  2. [minute-part] [seconds-part] [milliseconds-part]
  3. year-part : (number|variable_name) ("years" | "year")
  4. month-part : (number|variable_name) ("months" | "month")
  5. week-part : (number|variable_name) ("weeks" | "week")
  6. day-part : (number|variable_name) ("days" | "day")
  7. hour-part : (number|variable_name) ("hours" | "hour")
  8. minute-part : (number|variable_name) ("minutes" | "minute" | "min")
  9. seconds-part : (number|variable_name) ("seconds" | "second" | "sec")
  10. milliseconds-part : (number|variable_name) ("milliseconds" | "millisecond" |
  11. "msec")

与时间相关的EPL基本都会用到上面列出的东西,举几个例子说明下:

[plain] view plaincopy
  1. // 计算过去的5分3秒中进入改语句的Fruit事件的平均price
  2. select avg(price) from Fruit.win:time(5 minute 3 sec)
  3. // 每一天输出一次用户的账户总额
  4. select sum(account) from User output every 1 day

用法比较简单,大家可以多试试。要注意的是,Esper规定每月的天数都是30天,所以对准确性要求高的业务,以月为单位进行计算会出现误差的。

3.Comments

注释基本上和Java差不多,只不过他没有/** */和/* */之分,只有/* */和//,毕竟不需要生成文档,所以就没那个必要了。//只能单行注释,而/* */可以多行注释。示例如下:

[plain] view plaincopy
  1. a.单行注释
  2. // This comment extends to the end of the line.
  3. // Two forward slashes with no whitespace between them begin such comments.
  4. select * from MyEvent
  5. b.多行注释
  6. select * from OtherEvent
  7. /* this is a very
  8. *important Event */
  9. c.混合注释
  10. select field1 // first comment
  11. /* second comment */ field2 from MyEvent

4.Reserved Keywords

EPL里如果某个事件属性,或者事件流的名称和EPL的关键字一样,那么必须要以`括起来才可用,`在键盘上esc的下面,1的左边,叫什么忘记了,估计说出来也有很多人不知道。比如:

[plain] view plaincopy
  1. // insert和Order是关键字,这个EPL无效
  2. select insert from Order
  3. // `insert`和`Order`是属性名称和事件流名称,这个EPL有效
  4. select `insert` from `Order`

5.Escaping Strings

在EPL中,字符串使用单引号或者双引号括起来的,那如果字符串里包含有单引号或者双引号怎么办呢。请看例子:

[plain] view plaincopy
  1. select * from OrderEvent(name='John')
  2. // 等同于
  3. select * from OrderEvent(name="John")

如果name=John's,则需要反斜杠进行转义。

[plain] view plaincopy
  1. select * from OrderEvent(name="John\"s")
  2. // 或者
  3. select * from OrderEvent(name='john\'s')

除了使用反斜杠,还可以使用unicode来表示单引号和双引号。

[plain] view plaincopy
  1. select * from OrderEvent(name="John\u0022s")
  2. // 或者
  3. select * from OrderEvent(name='john\u0027s')

注意在Java编写EPL的时候,反斜杠和无含义的双引号还得转义,不然会和String的双引号冲突。比如

[plain] view plaincopy
  1. epService.getEPAdministrator().createEPL("select * from OrderEvent(name='John\\'s')");
  2. // ... and for double quotes...
  3. epService.getEPAdministrator().createEPL("select * from OrderEvent(name=\"Quote \\\"Hello\\\"\")");

6.Data Types

EPL支持Java所有的数值数据类型,包括基本类型及其包装类,同时还支持java.math.BigInteger和java.math.BigDecimal,并且能自动转换数据类型不丢失精度(比如short转int,int转short则不行)。如果想在EPL内进行数据转换,可以使用cast函数。完整例子如下:

[java] view plaincopy
  1. import com.espertech.esper.client.EPAdministrator;
  2. import com.espertech.esper.client.EPRuntime;
  3. import com.espertech.esper.client.EPServiceProvider;
  4. import com.espertech.esper.client.EPServiceProviderManager;
  5. import com.espertech.esper.client.EPStatement;
  6. import com.espertech.esper.client.EventBean;
  7. import com.espertech.esper.client.UpdateListener;
  8. /**
  9. * 可用cast函数将其他的数值数据类型转为BigDecimal。
  10. *
  11. * @author luonanqin
  12. *
  13. */
  14. class Banana
  15. {
  16. private int price;
  17. public int getPrice()
  18. {
  19. return price;
  20. }
  21. public void setPrice(int price)
  22. {
  23. this.price = price;
  24. }
  25. }
  26. class CastDataTypeListener1 implements UpdateListener
  27. {
  28. public void update(EventBean[] newEvents, EventBean[] oldEvents)
  29. {
  30. if (newEvents != null)
  31. {
  32. EventBean event = newEvents[0];
  33. // cast(avg(price), int)中间的空格在EPL中可以不写,但是event.get的时候必须加上,建议用as一个别名来代表转换后的值
  34. System.out.println("Average Price: " + event.get("cast(avg(price), int)") + ", DataType is "
  35. + event.get("cast(avg(price), int)").getClass().getName());
  36. }
  37. }
  38. }
  39. class CastDataTypeListener2 implements UpdateListener
  40. {
  41. public void update(EventBean[] newEvents, EventBean[] oldEvents)
  42. {
  43. if (newEvents != null)
  44. {
  45. EventBean event = newEvents[0];
  46. System.out.println("Average Price: " + event.get("avg(price)") + ", DataType is " + event.get("avg(price)").getClass().getName());
  47. }
  48. }
  49. }
  50. public class CastDataTypeTest
  51. {
  52. public static void main(String[] args) throws InterruptedException
  53. {
  54. EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider();
  55. EPAdministrator admin = epService.getEPAdministrator();
  56. String banana = Banana.class.getName();
  57. String epl1 = "select cast(avg(price),int) from " + banana + ".win:length_batch(2)";
  58. String epl2 = "select avg(price) from " + banana + ".win:length_batch(2)";
  59. EPStatement state1 = admin.createEPL(epl1);
  60. state1.addListener(new CastDataTypeListener1());
  61. EPStatement state2 = admin.createEPL(epl2);
  62. state2.addListener(new CastDataTypeListener2());
  63. EPRuntime runtime = epService.getEPRuntime();
  64. Banana b1 = new Banana();
  65. b1.setPrice(1);
  66. runtime.sendEvent(b1);
  67. Banana b2 = new Banana();
  68. b2.setPrice(2);
  69. runtime.sendEvent(b2);
  70. }
  71. }

执行结果:

[plain] view plaincopy
  1. Average Price: 1, DataType is java.lang.Integer
  2. Average Price: 1.5, DataType is java.lang.Double

要提醒的是,如果某个数除以0,那么默认会返回正无穷大或者负无穷大,不过可以配置这个结果,比如用null来代替。

7.Annotation

EPL也可以写注解,种类不多,大部分简单而有效。不过有些注解内容较多,在这里只是简单介绍,以后会在具体的使用场景进行详细讲解。首先来了解下注解的语法。

[plain] view plaincopy
  1. // 不包含参数或者单个参数的注解
  2. @annotation_name [(annotation_parameters)]
  3. // 包含多个属性名-值对的注解
  4. @annotation_name (attribute_name = attribute_value, [name=value, ...])
  5. // 多个注解联合使用
  6. @annotation_name [(annotation_parameters)] [@annotation_name [(annotation_parameters)]] [...]

下面讲解具体注解时,会结合语法进行说明。

a) @Name 指定EPL的名称,参数只有一个。例如:@Name("MyEPL")

b) @Description 对EPL进行描述,参数只有一个。例如:@Description("This is MyEPL")

c) @Tag 对EPL进行额外的说明,参数有两个,分别为Tag的名称和Tag的值,用逗号分隔。例如:@Tag(name="author",value="luonanqin")

d) @Priority 指定EPL的优先级,参数只有一个,并且整数(可负可正)。例如:@Priority(10)

e) @Drop 指定事件经过此EPL后不再参与其他的EPL计算,该注解无参数。

f) @Hint 为EPL加上某些标记,让引擎对此EPL产生其他的操作,会改变EPL实例的内存占用,但通常不会改变输出。其参数固定,由Esper提供,之后的篇幅会穿插讲解这个注解的使用场景。

g) @Audit EPL添加此注解后,可以额外输出EPL运行情况,有点类似日志的感觉(当然没有日志的功能全啦),具体使用场景在此先不提。

h) @Hook 与SQL相关,这里暂且不说

i) @EventRepresentation 这是用来指定EPL产生的计算结果事件包含的数据形式。参数只有一个,即array=true或array=false。false为默认值,代表数据形式为Map,若为true,则数据形式为数组。

针对以上几个简单的注解,例子如下:

[java] view plaincopy
  1. import com.espertech.esper.client.EPAdministrator;
  2. import com.espertech.esper.client.EPRuntime;
  3. import com.espertech.esper.client.EPServiceProvider;
  4. import com.espertech.esper.client.EPServiceProviderManager;
  5. import com.espertech.esper.client.EPStatement;
  6. import com.espertech.esper.client.EventBean;
  7. import com.espertech.esper.client.UpdateListener;
  8. class Apple
  9. {
  10. private int price;
  11. public int getPrice()
  12. {
  13. return price;
  14. }
  15. public void setPrice(int price)
  16. {
  17. this.price = price;
  18. }
  19. }
  20. class SomeAnnotationListener implements UpdateListener
  21. {
  22. public void update(EventBean[] newEvents, EventBean[] oldEvents)
  23. {
  24. if (newEvents != null)
  25. {
  26. EventBean event = newEvents[0];
  27. // 当加上注解@EventRepresentation(array=true)时,结果事件类型为数组而不是Map。
  28. // array=false时,也就是默认情况,结果事件类型为数组是Map。
  29. System.out.println("Sum Price: " + event.get("sum(price)") + ", Event Type is " + event.getEventType().getUnderlyingType());
  30. }
  31. }
  32. }
  33. public class SomeAnnotationTest
  34. {
  35. public static void main(String[] args) throws InterruptedException
  36. {
  37. EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider();
  38. EPAdministrator admin = epService.getEPAdministrator();
  39. String apple = Apple.class.getName();
  40. String epl1 = "@Priority(10)@EventRepresentation(array=true) select sum(price) from " + apple + ".win:length_batch(2)";
  41. String epl2 = "@Name(\"EPL2\")select sum(price) from " + apple + ".win:length_batch(2)";
  42. String epl3 = "@Drop select sum(price) from " + apple + ".win:length_batch(2)";
  43. UpdateListener listenenr = new SomeAnnotationListener();
  44. EPStatement state1 = admin.createEPL(epl1);
  45. state1.addListener(listenenr);
  46. EPStatement state2 = admin.createEPL(epl2);
  47. state2.addListener(listenenr);
  48. System.out.println("epl2's name is " + state2.getName());
  49. EPStatement state3 = admin.createEPL(epl3);
  50. state3.addListener(listenenr);
  51. EPRuntime runtime = epService.getEPRuntime();
  52. Apple a1 = new Apple();
  53. a1.setPrice(1);
  54. runtime.sendEvent(a1);
  55. Apple a2 = new Apple();
  56. a2.setPrice(2);
  57. runtime.sendEvent(a2);
  58. }
  59. }

执行结果:

[plain] view plaincopy
  1. epl2's name is EPL2
  2. Sum Price: 3, Event Type is class [Ljava.lang.Object;
  3. Sum Price: 3, Event Type is interface java.util.Map
  4. Sum Price: 3, Event Type is interface java.util.Map

可以发现,@Name和@EventRepresentation都起效果了,但是@Priority和@Drop没用,那是因为这两个是要配置才能生效的。以后讲Esper的Configuration的时候会说到。

8.Expression

Expression类似自定义函数,通常用Lambda表达式来建立的(也有别的方法建立),而Lambda表达式就一个“ => ”符号,表示“gose to”。符号的左边表示输入参数,符号右边表示计算过程,计算结果就是这个表达式的返回值,即Expression的返回值。语法如下:

[plain] view plaincopy
  1. expression expression_name { expression_body }

expression是关键字,expression_name为expression的名称(唯一),expression_body是expression的具体内容。

[plain] view plaincopy
  1. expression_body: (input_param [,input_param [,...]]) => expression

input_param必须为事件流的别名,注意不是事件流名称。参数名写什么都可以(当然最好不用关键字),多个参数用逗号分隔,并用圆括号括起来。同时针对lamdba,举个例子一起说明下:

[plain] view plaincopy
  1. expression middle { x => (x.max+x.min)/2 } select middle(apple) from Apple as apple

1. x表示输入参数,而x.max和x.min都是x代表的事件流的属性,如果事件流没这个属性,expression的定义就是错误的。

2. express的定义必须在使用它的句子之前完成。使用时直接写expression的名字和用圆括号包含要计算的参数即可。再次提醒,expression的参数只能是事件流别名,即apple,别名的定义就如上面那样,事件流之后跟着as,然后再跟别名。

上面这个句子的执行结果,就是middle的计算结果,各位自己试试吧。

对于多个参数的expression定义,例子如下:

[plain] view plaincopy
  1. expression sumage { (x,y) => x.age+y.age } select sumage(me,you) from Me as me, You as you

要是两个age的数据类型不一样是什么结果呢?还是请各位自己试试。

expression_body除了可以用lambda表达式之外,还可以用聚合函数,变量,常量,子查询语句,甚至另一个表达式。子查询语句在没有in或者exist关键字的情况下,需要圆括号括起来。示例如下:

[plain] view plaincopy
  1. expression newsSubq(md) {
  2. (select sentiment from NewsEvent.std:unique(symbol) where symbol = md.symbol)
  3. }
  4. select newsSubq(mdstream) from MarketDataEvent mdstream

针对变量和常量的示例如下:

[plain] view plaincopy
  1. expression twoPI { Math.PI * 2} select twoPI() from SampleEvent

对于expression里用另一个expression,EPL不允许在一个句子里建立两个expression,所以就出现了Global-Expression。普通的expression只作用于定义它的epl,如上面所有的包含select子句的epl就是如此。Global-Expression的语法如下:

[plain] view plaincopy
  1. create expression expression_name { expression_body }

和普通的expression相比,就是多了个create,不过他不能和别的子句放在一起,即他是单独执行的。例如:

[java] view plaincopy
  1. epService.getEPAdministrator().createEPL("create expression avgPrice { x => (x.fist+x.last)/2 }");

最后再举个例子说一下某个expression里用另一个expression。

[java] view plaincopy
  1. // 先定义全局的avgPrice
  2. create expression avgPrice { x => (x.fist+x.last)/2 }
  3. // bananaPrice Banana事件中包含了first和last属性,否则将报错
  4. expression bananaPrice{ x => avgPrice(x) } select bananaPrice(b) from Banana as b

expression_body里的空格只是为了看着清晰,有没有空格不会有影响。

对于EPL的语法,今天的内容先说到这,各位消化起来应该还是很快的,如果有不明白的可以找我或者看看文档。下篇将讲解select子句和from子句,敬请期待。

Esper学习之五:EPL语法(一)相关推荐

  1. Esper事件处理引擎 EPL语法-模式匹配

    1.案例 假设有一次ping攻击 可以看到,正常的情况下,是每秒10个ping,在第25秒后突然逐渐增加,如果增加的很剧烈,配置后台资源,我们就可以判断这是一次ping攻击,可以做相关措施.下面模仿一 ...

  2. Esper学习之九:EPL语法(五)

    转载请注明出处:http://blog.csdn.net/luonanqin 暂停更新三个多月,转眼间就2014年了.年底相信都是大家最忙碌的时候,我也不例外,以至于真的是没腾出手来继续更新,好在年初 ...

  3. Esper学习之十二:EPL语法(八)

    今天的内容十分重要,在Esper的应用中是十分常用的功能之一.它是一种事件集合,我们可以对这个集合进行增删查改,所以在复杂的业务场景中我们肯定不会缺少它.它就是Named Window. 由于本篇篇幅 ...

  4. Esper学习笔记三:EPL语法(1)

    2019独角兽企业重金招聘Python工程师标准>>> 1.EPL语法简介 EPL全称Event Processing Language,是一种类似SQL的语言,包含了SELECT, ...

  5. Esper学习之三:进程模型 .

    之前对Esper所能处理的事件结构进行了概述,并结合了例子进行讲解,不清楚的同学请看Esper学习之二:事件类型.今天主要为大家解释一下Esper是怎么处理事件的,即Esper的进程模型. 1.Upd ...

  6. day 83 Vue学习之五DIY脚手架、webpack使用、vue-cli的使用、element-ui

    Vue学习之五DIY脚手架.webpack使用.vue-cli的使用.element-ui 本节目录 一 vue获取原生DOM的方式 二 DIY脚手架 三 vue-cli脚手架的使用 四 webpac ...

  7. java上下文控制,Esper事件处理引擎_8_EPL 语法_2_Context 上下文_2_条件控制

    EPL 语法代码见-GitHub代码地址:点击打开链接 涉及 Javabean,Listener,main 以 GitHub 包下为最新更新 EPL_2_Context_2 package com.f ...

  8. Esper学习笔记(未完成)

    1.什么是CEP CEP[是一种标准] Complex Event Process 复杂事件处理 基于事件流进行数据处理 数据抽象成事件 -> 事件传递到CEP引擎 -> 引擎根据输入的事 ...

  9. Esper学习之二:事件类型 .

    上周我们介绍了Esper,它是一个适合实时分析数据的内存计算引擎.若有不了解的同学可以看一下 Esper学习之一:Esper介绍.如果各位自己运行过之前的程序,应该对Esper的处理机制和EPL比较感 ...

最新文章

  1. requests.exceptions.ConnectionError: ('Connection aborted.', BadStatusLine('',))
  2. android开发常用的设计模式,android开发设计模式之——单例模式详解
  3. 如何定义和搭建可靠人工智能系统的规则?
  4. 神策用户画像 2 大功能:千人千面,一人千面,一次给你
  5. CMD-NET命令详解、NET命令大全(转)
  6. argv python 提示输入_Python解释器
  7. pearson, kendall 和spearman三种相关分析方法的区别
  8. Netsparker扫描IIS网站——IIS PUT 任意文件写入
  9. 算法设计与分析第二版第一章笔记
  10. 单片机高效c语言编程,飞思卡尔单片机高效C语言编程(中文).pdf
  11. 56个民族及民族代码的sql语句
  12. SpringBoot MySQL #1 报错 Error executing DDL ...
  13. Android 讯飞语音合成、语音播报(详细步骤+源码)
  14. mysql 五舍六入_sql的四舍六入五成双的函数 (1)
  15. 【ReID】ABD-Net: Attentive but Diverse Person Re-Identification
  16. Tomcat 乱码问题解决方法
  17. python列表元组字典集合实验心得_python学习小总结(列表、元组、字典、集合、字符串)...
  18. java计算机毕业设计病人跟踪治疗信息管理系统演示2021源码+数据库+系统+lw文档+部署
  19. 数据结构01秦九算法
  20. 威尔逊置信区间 php,Evvail | 威尔逊置信区间(Wilson confidence intervals )计算 | Omics - Hunter...

热门文章

  1. android canvas广告牌,亚马逊广告后台创建CPC站内广告教程
  2. 第十九节 串口通讯与终端设备
  3. (从完全入门开始)思科模拟器创建校园网,第一次写博客
  4. linux安装搜狗中文,Ubuntu 17.04 安装搜狗中文输入法
  5. 抱朴守拙是最稳健的进取之道
  6. 网络舆情分析系统的研究与设计
  7. 自己动手、丰衣足食!箭头 → ← → ← ---2
  8. php gb28181,EasyGBS国标流媒体服务器GB28181国标方案安装使用文档
  9. 交换机的转发原理 |VLAN详解·图解 |VLAN间通信 |华为,思科配置
  10. 宿舍管理系统,宿舍管理、学生管理、楼宇管理、宿管管理分别有哪些内容