上一篇开始了新一轮语法——Pattern的讲解,一开始为大家普及了几个基础知识,其中有说到操作符。当时只是把它们都列举出来了,所以今天这篇就是专门详解这些操作符的,但是由于篇幅限制,本篇先会讲几个,剩余的后面几篇会逐个讲解。
1. Followed-by
如果各位有看过官方文档,应该会发现Followed-by的讲解是在比较靠后的位置,而放在第一的是Every关键字。我把它提前主要是因为其他的关键字结合Followed-by能更好的说明那个关键字的特点。如果不习惯我这样的顺序硬要跟着文档学习的朋友,可以跳过这一节先看后面的内容。
Followed-by,顾名思义就是“紧跟,跟随”的意思,通常用于事件之间的关联。举个现实生活中的例子:比如下班了,我用钥匙把家门打开,这是一个事件,紧接着我打开了浴室的灯,这也是一个事件,由于我之前在中央控制系统里设定了一个规则:打开家门后如果开了浴室的灯,热水就会放好,我一会儿就能洗澡了。所以我之前的一系列操作就触发了放热水这个动作。可能这个例子不是比较实际,但是应该能很清楚的说明这个关键字的含义吧。
Followed-by的操作符用减号和大于号组成,即->,和C语言里的指针一模一样。操作符两边是事件名称,表示右边的事件跟随左边的事件发生之后发生,即进入引擎。如果只是简单的事件follow,在操作符的两边写上事先定义的事件名或者类全名。例如:
AppleEvent -> BananaEvent // or com.xxx.Benz -> com.yyy.Audi但是如果前后事件之间有关联,那事件名或者类全名就需要设置一个别名。例如:
a=AppleEvent -> b=BananaEvent(b.price = a.price) // equals: a=AppleEvent -> b=BananaEvent(price = a.price)设置别名的原因很简单,就是为了方便描述事件之间的关联信息。但是有意思的是,对于前两个简单follow不设置别名esper不会报语法错误,但是实际运行时你无法通过api获取满足条件的事件,而带上别名的事件是可以正常获取的。
package example; import 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; /** * Created by Luonanqin on 9/5/14. */ class FollowedEvent { private int size; public int getSize() { return size; } public void setSize(int size) { this.size = size; } public String toString() { return "FollowedEvent{" + "size=" + size + '}'; } } class PatternFollowedListener implements UpdateListener { public void update(EventBean[] newEvents, EventBean[] oldEvents) { if (newEvents != null) { for (int i = 0; i < newEvents.length; i++) { System.out.println(); EventBean event = newEvents[i]; System.out.println("Result:"); System.out.println(event.get("a") + " " + event.get("b")); } } } } public class PatternFollowedTest { public static void main(String[] args) { EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider(); EPAdministrator admin = epService.getEPAdministrator(); EPRuntime runtime = epService.getEPRuntime(); String followed = FollowedEvent.class.getName(); String epl = "select * from pattern[every a=" + followed + " -> b=" + followed + "(size < a.size)]"; System.out.println("EPL: " + epl+"\n"); EPStatement stat = admin.createEPL(epl); stat.addListener(new PatternFollowedListener()); FollowedEvent f1 = new FollowedEvent(); f1.setSize(1); System.out.println("Send Event1: " + f1); runtime.sendEvent(f1); System.out.println(); FollowedEvent f2 = new FollowedEvent(); f2.setSize(3); System.out.println("Send Event2: " + f2); runtime.sendEvent(f2); System.out.println(); FollowedEvent f3 = new FollowedEvent(); f3.setSize(2); System.out.println("Send Event3: " + f3); runtime.sendEvent(f3); } }执行结果:
EPL: select * from pattern[every a=example.FollowedEvent -> b=example.FollowedEvent(size < a.size)] Send Event1: FollowedEvent{size=1} Send Event2: FollowedEvent{size=3} Send Event3: FollowedEvent{size=2} Result: FollowedEvent{size=3} FollowedEvent{size=2}例子中的pattern是由every和followed-by两个结构组合而成,所实现的效果是针对每一个事件,都监听其follow后同类型的事件的size值小于follow前的事件。只要满足pattern定义,通过get对应的别名就可以获得触发时的具体事件。 并且满足触发条件的事件不会再次被监听。大家重点关注followed-by,every之后会有详细说明。
lhs_expression -[limit_expression]> rhs_expressionlhs_expression和rhs_expression就是之前所说的发生顺序有关联的两个事件,而中间的“->”被一个限制表达式分开了,这里的限制表达式需要返回一个int数值,也可以直接写一个数字。整体的含义是:当左边事件等待满足条件的右边事件时,最多只保留n个左边事件在引擎内等待触发,其余事件不留在引擎内。而这个n就是限制表达式的返回值。无论左边事件数量是否达到n,只要满足条件的右边事件到达并触发后,引擎便重新等待新的左边事件并重新计数,直到超过n。。。这个过程会不断循环。完整示例如下:
package example; import 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; /** * Created by Luonanqin on 9/10/14. */ class LimitEvent { private int age; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String toString() { return "LimitEvent{" + "age=" + age + '}'; } } class LimitFollowedListener implements UpdateListener { public void update(EventBean[] newEvents, EventBean[] oldEvents) { if (newEvents != null) { System.out.println("\nResult: "); for (int i = 0; i < newEvents.length; i++) { EventBean event = newEvents[i]; System.out.println("a=" + event.get("a") + " b=" + event.get("b")); } System.out.println(); } } } public class LimitFollowedTest { public static void main(String[] args) { EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider(); EPAdministrator admin = epService.getEPAdministrator(); EPRuntime runtime = epService.getEPRuntime(); String limit = LimitEvent.class.getName(); String follow = FollowedEvent.class.getName(); /* 在每次触发完成前最多只保留2个a事件,触发条件为b的size值大于a的age */ String epl = "every a=" + limit + " -[2]> b=" + follow + "(size > a.age)"; System.out.println("EPL: " + epl + "\n"); EPStatement stat = admin.createPattern(epl); stat.addListener(new LimitFollowedListener()); System.out.println("First Send!\n"); LimitEvent l1 = new LimitEvent(); l1.setAge(1); System.out.println("Send Event: " + l1); runtime.sendEvent(l1); LimitEvent l2 = new LimitEvent(); l2.setAge(2); System.out.println("Send Event: " + l2); runtime.sendEvent(l2); LimitEvent l3 = new LimitEvent(); l3.setAge(0); System.out.println("Send Event: " + l3); runtime.sendEvent(l3); FollowedEvent f1 = new FollowedEvent(); f1.setSize(3); System.out.println("Send Event: " + f1); runtime.sendEvent(f1); FollowedEvent f2 = new FollowedEvent(); f2.setSize(4); System.out.println("Send Event: " + f2); runtime.sendEvent(f2); System.out.println(); System.out.println("Second Send!\n"); System.out.println("Send Event: "+l1); runtime.sendEvent(l1); System.out.println("Send Event: " + l2); runtime.sendEvent(l2); System.out.println("Send Event: " + l3); runtime.sendEvent(l3); System.out.println("Send Event: " + f1); runtime.sendEvent(f1); } }执行结果:
EPL: every a=example.LimitEvent -[2]> b=example.FollowedEvent(size > a.age) First Send! Send Event: LimitEvent{age=1} Send Event: LimitEvent{age=2} Send Event: LimitEvent{age=0} Send Event: FollowedEvent{size=3} Result: a=LimitEvent{age=1} b=FollowedEvent{size=3} a=LimitEvent{age=2} b=FollowedEvent{size=3} Send Event: FollowedEvent{size=4} Second Send! Send Event: LimitEvent{age=1} Send Event: FollowedEvent{size=3} Result: a=LimitEvent{age=1} b=FollowedEvent{size=3}例子中的epl已经给了注释说明含义,从运行结果中也可以看出,第一次发送时,FollowedEvent只触发了前两个age为1和2的LimitEvent,说明-[2]>起到了限制等待的LimitEvent事件数量为2的效果。第二次发送时,可以看到第一次触发之后-[2]>重新开始计数,FollowedEvent到达后就直接触发了。
1). select * from pattern[every LimitEvent] // equals to select * from LimitEvent 2). every FollowedEvent(size > 2) 3). every a=LimitEvent -> b=FollowedEvent(size > a.age)第一个例子是针对每一个LimitEvent事件都监听,所以等同于另一个非pattern写法。第二个例子监听每一个FollowedEvent,且事件的size要大于2,也就是一个filter。第三个例子我在之前有讲过,->的左右组合在一起可以算是一个子表达式(即followed-by),但是every真的是针对这个子表达式么?其实不然,every的优先级要大于->,所以every为每一个LimitEvent建立监听实例,并根据一定条件等待FollowedEvent。
假设事件传入引擎的顺序是这样的: A1 B1 C1 B2 A2 D1 A3 B3 E1 A4 F1 B4 注意:every优先级高于->,但是圆括号优先级高于所有操作符 Pattern 1: every ( A -> B ) 匹配结果: {A1, B1} {A2, B3} {A4, B4} 说明:因为有括号,所以every针对的是每一个A->B。A2后面的B3到达前,出现了A3,但是B3到达后并未匹配A3,说明every只有在一个完整的匹配发生后再对A进行新的监听,因此A3不会被监听。比如说:A1 A2 A3 A4 B1这样的发生顺序只会导致A1->B1 Pattern 2: every A -> B 匹配结果: {A1, B1} {A2, B3} {A3, B3} {A4, B4} 说明:由于没有括号,所以every的优先级大于->,所以every针对的是A,而不是A->B。也就是说,引擎每进入一个A,every都为其新建一个pattern实例等待B事件的发生。所以可以从结果中可以看出,B3进入引擎后同时触发了A2和A3 Pattern 3: A -> every B 匹配结果: {A1, B1} {A1, B2} {A1, B3} {A1, B4} 说明:every的优先级大于->,且every只作用于B,所以->只会针对第一个A事件起作用,并且每一个B都可以匹配这个A。 Pattern 4: every A -> every B 匹配结果: {A1, B1} {A1, B2} {A1, B3} {A2, B3} {A3, B3} {A1, B4} {A2, B4} {A3, B4} {A4, B4} 说明:A和B都用every修饰,every的优先级大于->,所以针对每一个A和B都可以匹配->。再说得通俗一点,只要A在B前进入引擎,那么A后面的B都可以和这个A匹配成功。上面的四个例子可以很清楚的表达出every的意思,更为复杂的例子就是将A和B替换成别的子pattern表达式,各位可以试着自己写写看一下效果。比如:every A->B->C
every-distinct(distinct_value_expr [, distinct_value_expr[...] [, expiry_time_period])distinct_value_expr表示参与过滤的事件属性,比如:
every-distinct(a.num) a=A并且可以多个属性联合在一起,就像联合主键那样。比如:
every-distinct(a.num, b.age) a=A -> b=B用于过滤的事件属性值在量很大的情况下会占用很多内存,所以我们需要给它设定一个过期值,也就是语法中的expiry_time_period。这个关键字表示的时间到达时,pattern重新匹配新的事件,而不受之前事件的用于过滤的属性值的影响。比如说:
EPL: every-distinct(a.num, 3 sec) a=A Send: A1: {num=1} Send: A2: {num=1} After 3 seconds Send: A3: {num=1}第一次发送num为1的A1事件会触发监听器,紧接着发送num为1的A2事件,不会触发,因为num=1已经出现过了,所以A2被过滤。3秒过后,pattern被重置,这时发送A3,监听器收到该事件。这个过程一直持续,即每3秒重置一次pattern,直到EPL实例被销毁。
总结上面的所有内容,我写了个完整的例子供大家参考。
package example; import 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; /** * Created by Luonanqin on 9/15/14. */ class EveryDistinctEvent { private int num; public int getNum() { return num; } public void setNum(int num) { this.num = num; } public String toString() { return "EveryDistinctEvent{" + "num=" + num + '}'; } } class EveryDistinctListener implements UpdateListener { public void update(EventBean[] newEvents, EventBean[] oldEvents) { if (newEvents != null) { System.out.println("\nResult: "); for (int i = 0; i < newEvents.length; i++) { EventBean event = newEvents[i]; System.out.println(event.get("a")); } } } } public class EveryDistinctTest { public static void main(String[] args) throws InterruptedException { EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider(); EPAdministrator admin = epService.getEPAdministrator(); EPRuntime runtime = epService.getEPRuntime(); String everyDistinct = EveryDistinctEvent.class.getName(); String limit = LimitEvent.class.getName(); String epl1 = "every-distinct(a.num) a=" + everyDistinct; System.out.println("EPL1: " + epl1); EPStatement stat1 = admin.createPattern(epl1); stat1.addListener(new EveryDistinctListener()); EveryDistinctEvent ed1 = new EveryDistinctEvent(); ed1.setNum(1); EveryDistinctEvent ed2 = new EveryDistinctEvent(); ed2.setNum(2); EveryDistinctEvent ed3 = new EveryDistinctEvent(); ed3.setNum(1); System.out.println("\nSend Event: " + ed1); runtime.sendEvent(ed1); System.out.println("\nSend Event: " + ed2); runtime.sendEvent(ed2); System.out.println("\nSend Event: " + ed3); runtime.sendEvent(ed3); stat1.destroy(); String epl2 = "every-distinct(a.num) (a=" + everyDistinct + " and not " + limit + ")"; System.out.println("\nEPL2: " + epl2); EPStatement stat2 = admin.createPattern(epl2); stat2.addListener(new EveryDistinctListener()); LimitEvent l1 = new LimitEvent(); System.out.println("\nSend Event: " + ed1); runtime.sendEvent(ed1); System.out.println("\nSend Event: " + ed2); runtime.sendEvent(ed2); System.out.println("\nSend Event: " + l1); runtime.sendEvent(l1); System.out.println("\nSend Event: " + ed3); runtime.sendEvent(ed3); stat2.destroy(); String epl3 = "every-distinct(a.num, 3 sec) a=" + everyDistinct; System.out.println("\nEPL3: " + epl3); EPStatement stat3 = admin.createPattern(epl3); stat3.addListener(new EveryDistinctListener()); System.out.println("\nSend Event: " + ed1); runtime.sendEvent(ed1); System.out.println("\nSend Event: " + ed2); runtime.sendEvent(ed2); System.out.println("\nSleep 3 seconds!"); Thread.sleep(3000); System.out.println("\nSend Event: " + ed3); runtime.sendEvent(ed3); } }执行结果:
EPL1: every-distinct(a.num) a=example.EveryDistinctEvent Send Event: EveryDistinctEvent{num=1} Result: EveryDistinctEvent{num=1} Send Event: EveryDistinctEvent{num=2} Result: EveryDistinctEvent{num=2} Send Event: EveryDistinctEvent{num=1} EPL2: every-distinct(a.num) (a=example.EveryDistinctEvent and not example.LimitEvent) Send Event: EveryDistinctEvent{num=1} Result: EveryDistinctEvent{num=1} Send Event: EveryDistinctEvent{num=2} Result: EveryDistinctEvent{num=2} Send Event: LimitEvent{age=0} Send Event: EveryDistinctEvent{num=1} Result: EveryDistinctEvent{num=1} EPL3: every-distinct(a.num, 3 sec) a=example.EveryDistinctEvent Send Event: EveryDistinctEvent{num=1} Result: EveryDistinctEvent{num=1} Send Event: EveryDistinctEvent{num=2} Result: EveryDistinctEvent{num=2} Sleep 3 seconds! Send Event: EveryDistinctEvent{num=1} Result: EveryDistinctEvent{num=1}在EPL2中,every-distinct后面的子表达式是EveryDistinctEvent and not LimitEvent,所以在发送EveryDistinctEvent之后发送LimitEvent,就导致子表达式false,所以在此发送num=1的EveryDistinctEvent时监听器被触发。