转载请注明出处:http://blog.csdn.net/luonanqin
由于本篇篇幅较长,希望各位童鞋慢慢阅读,并仔细研究文档中或者我给出的例子。
1.Create Named Window
本篇的开头有说过named window是一种事件集合,它可以存储一种类型或多种类型的事件。如果我们不移除named window中的事件,那么事件应该存在生命周期,否则事件过多会有内存溢出的风险。所以我们在创建named window的时候会附带事件的过期策略,而各种过期策略就是用view来表达。named window的创建方式有三种:a.用已有的事件结构创建;b.自定义named window存放的事件格式;c.将已有的named window中包含的事件写入到新的named window中,即复制named window。
a.Creation by Modelling after an Existing Type
语法如下:
[context context_name] create window window_name.view_specifications [as] [select list_of_properties from] event
// FruitWindow保持最近10分钟的Apple事件 create window FruitWindow.win:time(10 min) as Apple // FruitWindow保持最近5分钟的Apple事件,但只包含size和price属性 create window FruitWindow.win:time(5 min) as select size, price from Apple
[context context_name] create window window_name.view_specifications [as] (column_name column_type[,column_name column_type [,...]])column_name表示属性名称,column_type表示属性类型。属性可以是一个,也可以是多个,多个用逗号分隔。示例如下:
// 创建包含普通属性的named window create window SecurityEvent.win:time(30 sec) (ipAddress string, userId String, numAttempts int, properties String[]) // 创建包含事件类型属性的named window create schema SecurityData (name String, roles String[]) create window SecurityEvent.win:time(30 sec) (ipAddress string, userId String, secData SecurityData, historySecData SecurityData[])
[context context_name] create window window_name.view_specifications as windowname insert [where filter_expression]windowname后面紧跟insert,表示将该window中的事件插入到新建的named window中。where filter_expression表示过滤插入的事件。实例如下:
create window ScratchBuyOrders.win:time(10) as OrdersNamedWindow insert where side = 'buy'
// 以数组形式反馈给UpdateListener @EventRepresentation(array=true)create window FooWindow.win:time(5 sec) as (string prop1) // 以Map形式反馈给UpdateListener @EventRepresentation(array=false)create window FooWindow.win:time(5 sec) as (string prop1) ... equals ... create window FooWindow.win:time(5 sec) as (string prop1)
2.Inserting Into Named Windows
创建好named window以后,我们就可以往里面插入事件了。插入的语法很简单,基本上和insert into语法一样。关于insert into,请参见《Esper学习之八:EPL语法(四)》。下面直接举几个例子:
1) // create named window with some properties of OrderEvent create window OrdersWindow.win:keepall() as select symbol, volume, price from OrderEvent // insert into events to named window insert into OrdersWindow(symbol, volume, price) select name, count, price from FXOrderEvent ... equals ... insert into OrdersWindow select name as symbol, vol as volume, price from FXOrderEvent 2) // create named window with POJO create window OrdersWindow.win:time(30) as com.mycompany.OrderEvent // insert into events to named window insert into OrdersWindow select * from com.mycompany.OrderEvent(symbol='IBM') 3) // create named window create window OrdersWindow.win:time(30) as select *, price as derivedPrice from OrderEvent // insert into events to named window with custom function insert into OrdersWindow select *, MyFunc.func(price, percent) as derivedPrice from OrderEvent 4) // create named window create window OrdersWindow.win:time(30) as select *, price as priceTotal from OrderEvent // insert into events to named window insert into OrdersWindow select *, price * unit as priceTotal from ServiceOrderEvent 5) // create a named window for the base class create window OrdersWindow.std:unique(name) as select * from ProductBaseEvent // The ServiceProductEvent class subclasses the ProductBaseEvent class insert into OrdersWindow select * from ServiceProductEvent 6) /** * interface InterfaceEvent { * public int getPrice(); * public String getName(); * } */ // create a named window for the inteface create window InterfaceWindow.win: time(2 sec) as select * from InterfaceEvent // The InterfaceEventImpl is the implements for InterfaceEvent insert into InterfaceWindow select * from InterfaceEventImpl从以上几个例子可以看得出,创建named window时用的事件类型只是用了该事件类型的属性定义,只要insert的事件列出了对应的属性名称就可以,当然属性的数据类型也得对应。如果属性名不对应,可以用as来重命名,就像例3和例4那样。第5个例子表明子类可以插入到父类定义的named window中。第六个例子表明实现类可以插入到用接口定义的named window中。
3.Selecting From Named Windows
这一节主要讲解如何查询named window中的事件。准确来说这并不是一种无条件的查询方式,监听器监听查询语句以后,必须发送对应的事件(创建window时指定的事件类型),然后先看是否满足named window的输出条件,再看是否满足查询语句的输出条件,所以并不是named window中的事件都会发给监听器。咱们先看一个例子:
// first, you should create the AllOrdersNamedWindow create named window AllOrdersNamedWindow.win:length_batch(3) as OrderEvent select * from AllOrdersNamedWindow这个select句子看似很简单,实际上并不是查询AllOrdersNamedWindow中的事件。实际过程是这样的:首先引擎检查AllOrdersNamedWindow中有多少事件,因为过期策略是length_batch(3),意思是当AllOrdersNamedWindow中包含3个事件,才会将这3个事件同时输出,然后再等待新的3个事件,然后再一起输出这3个事件。。。所以当输入某一个事件时,若不满足3个数量,则select语句不会返回任何内容。若满足3个数量,则监听select语句的监听器会得到AllOrdersNamedWindow中的最新的3各事件。
select rstream * from AllOrdersNamedWindow如果只想查询AllOrdernamedWindow里面的某几个属性,并且加上一些限制条件查看特定的事件,则查询语句改成下面的样子:
select symbol, avg(price) from AllOrdersNamedWindow(sector='energy') group by symbol因为上面所说的特点,所以查询语句可以在事件发送后再建立,也就是说我什么时候想查什么时候再建句子,named windows里有多少事件就如实返回多少。下面举个完整的例子总结一下:
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 3/19/14. */ class SelectEvent { private int price; private String name; public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "name="+name+", price="+price; } } class SelectNamedWindowListener implements UpdateListener{ public void update(EventBean[] newEvents, EventBean[] oldEvents) { if (newEvents != null) { System.out.println("There is "+newEvents.length+" events to be return!"); for (int i = 0; i < newEvents.length;i++) { System.out.println(newEvents[i].getUnderlying()); } } } } public class SelectNamedWindowTest{ public static void main(String[] args) { EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider(); EPAdministrator admin = epService.getEPAdministrator(); EPRuntime runtime = epService.getEPRuntime(); String selectEvent = SelectEvent.class.getName(); String epl1 = "create window SelectNamedWindow.win:length_batch(3) as " + selectEvent; admin.createEPL(epl1); System.out.println("Create named window: create window SelectNamedWindow.win:length_batch(3) as "+selectEvent); String epl2 = "insert into SelectNamedWindow select * from " + selectEvent; admin.createEPL(epl2); SelectEvent se1 = new SelectEvent(); se1.setName("se1"); se1.setPrice(1); System.out.println("Send SelecEvent1 " + se1); runtime.sendEvent(se1); SelectEvent se2 = new SelectEvent(); se2.setName("se2"); se2.setPrice(2); System.out.println("Send SelecEvent2 " + se2); runtime.sendEvent(se2); // Can't create "select * from SelectamedWindow.win:time(3 sec)" String epl3 = "select * from SelectNamedWindow(price>=2)"; EPStatement state3 = admin.createEPL(epl3); state3.addListener(new SelectNamedWindowListener()); System.out.println("Register select sentence: select * from SelectNamedWindow(price>=2)"); SelectEvent se3 = new SelectEvent(); se3.setName("se3"); se3.setPrice(3); System.out.println("Send SelecEvent3 " + se3 + "\n"); runtime.sendEvent(se3); } }
执行结果:
Create named window: create window SelectNamedWindow.win:length_batch(3) as example.SelectEvent Send SelecEvent1 name=se1, price=1 Send SelecEvent2 name=se2, price=2 Register select sentence: select * from SelectNamedWindow(price>=2) Send SelecEvent3 name=se3, price=3 There is 2 events to be return! name=se2, price=2 name=se3, price=3上面的例子有一段注释“select * from SelectamedWindow.win:time(3 sec)”,实际上是因为win:time(3 sec)这个东西,之前create named window的时候,已经使用了一个win的view,所以这里就不能再使用win的view了。这个与view相关,就一笔带过了。另外还有一点,在select句子中的filter如果使用了variable,当变量的值在句子创建后改变了,引擎不会读取新的值,这个需要额外注意。
4.Triggered Select on Named Windows:On Select
上一节说到的查询方式实际上并不是很好用,而这一节就提供了一个非常简单的查询办法。他是通过发送一个触发事件即可得到当前window里有些什么事件,你还可以设置这个触发事件满足什么要求才可触发,或者这个触发事件和window中的事件达到某种关联后输出符合这个关联的事件或事件的部分属性。先来看看语法:
on event_type[(filter_criteria)] [as stream_name] select select_list from window_name [as stream_name] [where criteria_expression] [group by grouping_expression_list] [having grouping_search_conditions] [order by order_by_expression_list]event_type表示用来触发的事件,可以是任何一种事件类型,也可以是pattern(用来表达较为复杂的触发条件)。后面用括号括起来的包含了触发事件的限制条件,必须满足这个里面约定的条件才可用来触发查询。as stream_name为可选参数,主要用于具体的查询语句或者与window做关联用。select语句就不说了,和普通的没太大区别。后面的where条件限制了查询结果,满足结果的才可返回给监听器。group by、having、order by和之前说的用法一样,这里不做说明了。我们先来看几个简单的例子:
// QueryEvent作为触发事件,查询OrderNamedWindow中的所有事件,并附带触发的事件作为结果返回(什么意思?) on QueryEvent select * from OrdersNamedWindow // QueryEvent作为触发事件,查询OrderNamedWindow中的所有事件作为结果返回(懂了吧) on QueryEvent select win.* from OrdersNamedWindow as win // volume大于0的QueryEvent作为触发事件,查询OrderNamedWindow中的事件,且事件的symbol要与QueryEvent的symbol一样,返回满足条件的window事件的symbol值,以及QueryEvent的symbol和volume值 on QueryEvent(volume>0) as query select query.symbol, query.volume, win.symbol from OrdersNamedWindow as win where win.symbol = query.symbol // group by, having, order by的用法与前面所讲无差别,都是针对查询出来的事件 on QueryEvent select symbol, sum(volume) from OrdersNamedWindow as win group by symbol having volume > 0 order by symbol // 每一次OrderNamedWindow有变化并满足限制条件即可触发监听器返回window中的事件。 on OrdersNamedWindow as trig select onw.symbol, sum(onw.volume) from OrdersNamedWindow as onw where onw.symbol = trig.symbol前两个例子我相信大家已经明白了。如果select子句里是*,则返回的不仅仅是window中的事件,还会返回触发查询的事件,并且返回的多行结果中每行都会包含这个触发事件。第三个例子可以看出as的用法。最后一个例子有些特别,触发的事件就是named window本身,目的就是为了named window变化了就能返回变化后的结果。但是这里的“变化”不是简单的事件有增加或者减少,而是指named window定义时指定的view达到触发条件有输出了,你才能真的看到查询结果。下面列一个完整的例子来概括一下上面说到的几点内容:
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 3/29/14. */ class OnSelectTrigger { private int trigger; public int getTrigger() { return trigger; } public void setTrigger(int trigger) { this.trigger = trigger; } public String toString() { return "trigger=" + trigger; } } class OnSelectEvent { private String name; private int size; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getSize() { return size; } public void setSize(int size) { this.size = size; } public String toString() { return "name=" + name + ", size=" + size; } } class OnSelectWindowListener implements UpdateListener { public void update(EventBean[] newEvents, EventBean[] oldEvents) { if (newEvents != null) { System.out.println("Trigger On Select:"); System.out.println("There is " + newEvents.length + " OnSelectEvent in OnSelectWindow!"); for (int i = 0; i < newEvents.length; i++) { System.out.println(newEvents[i].getUnderlying()); } } } } public class OnSelectWindowTest { public static void main(String[] args) { EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider(); EPAdministrator admin = epService.getEPAdministrator(); EPRuntime runtime = epService.getEPRuntime(); String triggerEvent = OnSelectTrigger.class.getName(); String selectEvent = OnSelectEvent.class.getName(); // win:keepall()使named window中的事件一直保留 // String epl1 = "create window OnSelectWindow.win:keepall() as select * from " + selectEvent; String epl1 = "create window OnSelectWindow.win:length(2) as select * from " + selectEvent; String epl2 = "insert into OnSelectWindow select * from " + selectEvent; String epl3 = "on " + triggerEvent + "(trigger>=2) select osw.* from OnSelectWindow as osw"; System.out.println("Create Window:" + epl1); System.out.println("Trigger sentence: " + epl3); System.out.println(); admin.createEPL(epl1); admin.createEPL(epl2); EPStatement state3 = admin.createEPL(epl3); state3.addListener(new OnSelectWindowListener()); OnSelectEvent ose1 = new OnSelectEvent(); ose1.setName("ose1"); ose1.setSize(1); runtime.sendEvent(ose1); System.out.println("Send OnSelectEvent 1: " + ose1); OnSelectEvent ose2 = new OnSelectEvent(); ose2.setName("ose2"); ose2.setSize(2); runtime.sendEvent(ose2); System.out.println("Send OnSelectEvent 2: " + ose2); OnSelectEvent ose3 = new OnSelectEvent(); ose3.setName("ose3"); ose3.setSize(3); runtime.sendEvent(ose3); System.out.println("Send OnSelectEvent 3: " + ose3); OnSelectTrigger ost1 = new OnSelectTrigger(); ost1.setTrigger(1); System.out.println("Send OnSelectTrigger " + ost1); runtime.sendEvent(ost1); OnSelectTrigger ost2 = new OnSelectTrigger(); ost2.setTrigger(2); System.out.println("Send OnSelectTrigger " + ost2 + "\n"); runtime.sendEvent(ost2); System.out.println(); String epl4 = "on OnSelectWindow select osw.* from OnSelectWindow as osw"; EPStatement state4 = admin.createEPL(epl4); state4.addListener(new OnSelectWindowListener()); System.out.println("Trigger sentence: " + epl4 + "\n"); OnSelectEvent ose4 = new OnSelectEvent(); ose4.setName("ose4"); ose4.setSize(4); System.out.println("Send OnSelectEvent 4(also a Trigger): " + ose4 + "\n"); runtime.sendEvent(ose4); System.out.println(); OnSelectEvent ose5 = new OnSelectEvent(); ose5.setName("ose5"); ose5.setSize(5); System.out.println("Send OnSelectEvent 5(also a Trigger): " + ose5 + "\n"); runtime.sendEvent(ose5); } }执行结果:
Create Window:create window OnSelectWindow.win:length(2) as select * from example.OnSelectEvent Trigger sentence: on example.OnSelectTrigger(trigger>=2) select osw.* from OnSelectWindow as osw Send OnSelectEvent 1: name=ose1, size=1 Send OnSelectEvent 2: name=ose2, size=2 Send OnSelectEvent 3: name=ose3, size=3 Send OnSelectTrigger trigger=1 Send OnSelectTrigger trigger=2 Trigger On Select: There is 2 OnSelectEvent in OnSelectWindow! name=ose2, size=2 name=ose3, size=3 Trigger sentence: on OnSelectWindow select osw.* from OnSelectWindow as osw Send OnSelectEvent 4(also a Trigger): name=ose4, size=4 Trigger On Select: There is 2 OnSelectEvent in OnSelectWindow! name=ose3, size=3 name=ose4, size=4 Send OnSelectEvent 5(also a Trigger): name=ose5, size=5 Trigger On Select: There is 2 OnSelectEvent in OnSelectWindow! name=ose4, size=4 name=ose5, size=5
5.Triggered Delete on Named Windows:On Delete
除了可以用on select语句查询named window,还可以用on delete语句删除named window中的事件。语法和on select基本一样,同样能够设置一定条件限制触发事件,以及删除符合特定条件的事件。语法如下:
on event_type[(filter_criteria)] [as stream_name] delete from window_name [as stream_name] [where criteria_expression]和on select不同的是,delete后面不跟属性什么的,因为删除的就是事件,不存在删除事件中的某些属性这种情况。filter_criteria用来限制触发事件,where criteria_expression用来限制要删除的事件。而且没有group by, having, Order by。举例如下:
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 3/30/14. */ class OnDeleteTrigger { private int trigger; public int getTrigger() { return trigger; } public void setTrigger(int trigger) { this.trigger = trigger; } public String toString() { return "trigger=" + trigger; } } class OnDeleteEvent { private String name; private int size; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getSize() { return size; } public void setSize(int size) { this.size = size; } public String toString() { return "name=" + name + ", size=" + size; } } class OnDeleteWindowListener implements UpdateListener { public void update(EventBean[] newEvents, EventBean[] oldEvents) { if (newEvents != null) { System.out.println(); System.out.println("Trigger On Delete:"); System.out.println("There is " + newEvents.length + " OnDeleteEvent to be deleted in OnDeleteWindow!"); for (int i = 0; i < newEvents.length; i++) { System.out.println(newEvents[i].getUnderlying()); } } } } class OnSelectListener implements UpdateListener{ public void update(EventBean[] newEvents, EventBean[] oldEvents) { if (newEvents != null) { System.out.println(); System.out.println("Trigger On Select:"); System.out.println("There is " + newEvents.length + " OnDeleteEvent in OnDeleteWindow!"); for (int i = 0; i < newEvents.length; i++) { System.out.println(newEvents[i].getUnderlying()); } } } } public class OnDeleteWindowTest { public static void main(String[] args) { EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider(); EPAdministrator admin = epService.getEPAdministrator(); EPRuntime runtime = epService.getEPRuntime(); String triggerEvent = OnDeleteTrigger.class.getName(); String deleteEvent = OnDeleteEvent.class.getName(); String epl1 = "create window OnDeleteWindow.win:keepall() as select * from " + deleteEvent; String epl2 = "insert into OnDeleteWindow select * from " + deleteEvent; String epl3 = "on " + triggerEvent + "(trigger>0) as odt delete from OnDeleteWindow as odw where odt.trigger=odw.size"; String epl4 = "on " + triggerEvent + "(trigger=0) select odw.* from OnDeleteWindow as odw"; System.out.println("Create Window: " + epl1); System.out.println("Delete Trigger: " + epl3); System.out.println("Select Trigger: " + epl4); System.out.println(); admin.createEPL(epl1); admin.createEPL(epl2); EPStatement state3 = admin.createEPL(epl3); state3.addListener(new OnDeleteWindowListener()); EPStatement state4 = admin.createEPL(epl4); state4.addListener(new OnSelectListener()); OnDeleteEvent ose1 = new OnDeleteEvent(); ose1.setName("ose1"); ose1.setSize(1); runtime.sendEvent(ose1); System.out.println("Send OnDeleteEvent 1: " + ose1); OnDeleteEvent ose2 = new OnDeleteEvent(); ose2.setName("ose2"); ose2.setSize(2); runtime.sendEvent(ose2); System.out.println("Send OnDeleteEvent 2: " + ose2); OnDeleteEvent ose3 = new OnDeleteEvent(); ose3.setName("ose3"); ose3.setSize(3); runtime.sendEvent(ose3); System.out.println("Send OnDeleteEvent 3: " + ose3); OnDeleteTrigger ost1 = new OnDeleteTrigger(); ost1.setTrigger(0); System.out.println("\nSend OnSelectTrigger " + ost1); runtime.sendEvent(ost1); OnDeleteTrigger ost2 = new OnDeleteTrigger(); ost2.setTrigger(2); System.out.println("\nSend OnDeleteTrigger " + ost2); runtime.sendEvent(ost2); OnDeleteTrigger ost3 = new OnDeleteTrigger(); ost3.setTrigger(0); System.out.println("\nSend OnSelectTrigger " + ost3); runtime.sendEvent(ost3); } }执行结果:
Create Window: create window OnDeleteWindow.win:keepall() as select * from example.OnDeleteEvent Delete Trigger: on example.OnDeleteTrigger(trigger>0) as odt delete from OnDeleteWindow as odw where odt.trigger=odw.size Select Trigger: on example.OnDeleteTrigger(trigger=0) select odw.* from OnDeleteWindow as odw Send OnDeleteEvent 1: name=ose1, size=1 Send OnDeleteEvent 2: name=ose2, size=2 Send OnDeleteEvent 3: name=ose3, size=3 Send OnSelectTrigger trigger=0 Trigger On Select: There is 3 OnDeleteEvent in OnDeleteWindow! name=ose1, size=1 name=ose2, size=2 name=ose3, size=3 Send OnDeleteTrigger trigger=2 Trigger On Delete: There is 1 OnDeleteEvent to be deleted in OnDeleteWindow! name=ose2, size=2 Send OnSelectTrigger trigger=0 Trigger On Select: There is 2 OnDeleteEvent in OnDeleteWindow! name=ose1, size=1 name=ose3, size=3
6.Triggered Select+Delete on Named Windows: the On Select Delete
如果我们想在从named window查询出结果的同时删掉查询结果,那么可以使用on select and delete语句。语法和on select语句几乎是一样的,只是多了个delete。语法如下:
on trigger select [and] delete select_list... ... (please see on-select for from, group by, having, order by)...直接上一个完整例子:
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 3/29/14. */ class OnSelectDeleteTrigger { private int trigger; public int getTrigger() { return trigger; } public void setTrigger(int trigger) { this.trigger = trigger; } public String toString() { return "trigger=" + trigger; } } class OnSelectDeleteEvent { private String name; private int size; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getSize() { return size; } public void setSize(int size) { this.size = size; } public String toString() { return "name=" + name + ", size=" + size; } } class OnSelectDeleteWindowListener implements UpdateListener { public void update(EventBean[] newEvents, EventBean[] oldEvents) { if (newEvents != null) { System.out.println(); System.out.println("Trigger On Select and Delete:"); System.out.println("There is " + newEvents.length + " OnSelectDeleteEvent in OnSelectDeleteWindow!"); for (int i = 0; i < newEvents.length; i++) { System.out.println(newEvents[i].getUnderlying()); } } } } public class OnSelectDeleteWindowTest { public static void main(String[] args) { EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider(); EPAdministrator admin = epService.getEPAdministrator(); EPRuntime runtime = epService.getEPRuntime(); String triggerEvent = OnSelectDeleteTrigger.class.getName(); String selectDeleteEvent = OnSelectDeleteEvent.class.getName(); String epl1 = "create window OnSelectDeleteWindow.win:keepall() as select * from " + selectDeleteEvent; String epl2 = "insert into OnSelectDeleteWindow select * from " + selectDeleteEvent; String epl3 = "on " + triggerEvent + " select and delete osw.* from OnSelectDeleteWindow as osw"; System.out.println("Create Window: " + epl1); System.out.println("Select and Delete Trigger: " + epl3); System.out.println(); admin.createEPL(epl1); admin.createEPL(epl2); EPStatement state3 = admin.createEPL(epl3); state3.addListener(new OnSelectDeleteWindowListener()); OnSelectDeleteEvent osde1 = new OnSelectDeleteEvent(); osde1.setName("osde1"); osde1.setSize(1); runtime.sendEvent(osde1); System.out.println("Send OnSelectDeleteEvent 1: " + osde1); OnSelectDeleteEvent osde2 = new OnSelectDeleteEvent(); osde2.setName("osde2"); osde2.setSize(2); runtime.sendEvent(osde2); System.out.println("Send OnSelectDeleteEvent 2: " + osde2); OnSelectDeleteEvent osde3 = new OnSelectDeleteEvent(); osde3.setName("osde3"); osde3.setSize(3); runtime.sendEvent(osde3); System.out.println("Send OnSelectDeleteEvent 3: " + osde3); OnSelectDeleteTrigger osdt1 = new OnSelectDeleteTrigger(); osdt1.setTrigger(1); System.out.println("Send OnSelectDeleteTrigger " + osdt1); runtime.sendEvent(osdt1); System.out.println(); OnSelectDeleteEvent osde4 = new OnSelectDeleteEvent(); osde4.setName("osde4"); osde4.setSize(4); System.out.println("Send OnSelectDeleteEvent 4: " + osde4); runtime.sendEvent(osde4); OnSelectDeleteTrigger osdt2 = new OnSelectDeleteTrigger(); osdt2.setTrigger(1); System.out.println("Send OnSelectDeleteTrigger " + osdt2); runtime.sendEvent(osdt2); } }执行结果:
Create Window: create window OnSelectDeleteWindow.win:keepall() as select * from example.OnSelectDeleteEvent Select and Delete Trigger: on example.OnSelectDeleteTrigger select and delete osw.* from OnSelectDeleteWindow as osw Send OnSelectDeleteEvent 1: name=osde1, size=1 Send OnSelectDeleteEvent 2: name=osde2, size=2 Send OnSelectDeleteEvent 3: name=osde3, size=3 Send OnSelectDeleteTrigger trigger=1 Trigger On Select and Delete: There is 3 OnSelectDeleteEvent in OnSelectDeleteWindow! name=osde1, size=1 name=osde2, size=2 name=osde3, size=3 Send OnSelectDeleteEvent 4: name=osde4, size=4 Send OnSelectDeleteTrigger trigger=1 Trigger On Select and Delete: There is 1 OnSelectDeleteEvent in OnSelectDeleteWindow! name=osde4, size=4
7.Updating Named Windows: the On Update clause
除了查询和删除window里的事件,我们还可以更新事件,而且同样也是通过发送一个特定事件来出发更新操作。语法如下:
on event_type[(filter_criteria)] [as stream_name] update window_name [as stream_name] set property_name = expression [, property_name = expression [,...]] [where criteria_expression]filter_criteria用来过滤触发事件,criteria_expression用来过滤window中的事件不予更改。和sql类似,set后面跟着要修改的属性及要赋的值,多个属性名值对用逗号分隔。先看一个简单的例子:
on NewOrderEvent(volume>0) as myNewOrders update AllOrdersNamedWindow as myNamedWindow set price = myNewOrders.price where myNamedWindow.symbol = myNewOrders.symbol我想不用做太多解释大家就能很容易看懂。不过下面这个例子呢:
on UpdateEvent as upd update MyWindow as win set field_a = 1, field_b = win.field_a, // assigns the value 1 field_c = initial.field_a // assigns the field_a original value before update
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; import java.io.Serializable; /** * Created by Luonanqin on 4/5/14. */ class OnUpdateTrigger { private int trigger; public int getTrigger() { return trigger; } public void setTrigger(int trigger) { this.trigger = trigger; } public String toString() { return "trigger=" + trigger; } } class OnUpdateEvent implements Serializable{ private String name; private int size; private int price; public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getSize() { return size; } public void setSize(int size) { this.size = size; } public String toString() { return "name=" + name + ", size=" + size + ", price=" + price; } } class OnUpdateWindowListener implements UpdateListener { public void update(EventBean[] newEvents, EventBean[] oldEvents) { if (newEvents != null) { System.out.println(); System.out.println("Trigger On Update:"); System.out.println("There is " + newEvents.length + " to be updated in OnUpdateWindow!"); for (int i = 0; i < newEvents.length; i++) { System.out.println(newEvents[i].getUnderlying()); } } } } class OnUpdateSelectWindowListener implements UpdateListener { public void update(EventBean[] newEvents, EventBean[] oldEvents) { if (newEvents != null) { System.out.println(); System.out.println("Trigger On Select:"); System.out.println("There is " + newEvents.length + " OnUpdateEvent in OnUpdateWindow!"); for (int i = 0; i < newEvents.length; i++) { System.out.println(newEvents[i].getUnderlying()); } } } } public class OnUpdateWindowTest { public static void main(String[] args) { EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider(); EPAdministrator admin = epService.getEPAdministrator(); EPRuntime runtime = epService.getEPRuntime(); String triggerEvent = OnUpdateTrigger.class.getName(); String updateEvent = OnUpdateEvent.class.getName(); String epl1 = "create window OnUpdateWindow.win:keepall() as select * from " + updateEvent; String epl2 = "insert into OnUpdateWindow select * from " + updateEvent; String epl3 = "on " + triggerEvent + "(trigger>0) as out update OnUpdateWindow as ouw set size=out.trigger, price=initial.size where out.trigger<ouw.price"; String epl4 = "on " + triggerEvent + "(trigger=0) select ouw.* from OnUpdateWindow as ouw"; System.out.println("Create Window: " + epl1); System.out.println("Update Trigger sentence: " + epl3); System.out.println(); admin.createEPL(epl1); admin.createEPL(epl2); EPStatement state3 = admin.createEPL(epl3); state3.addListener(new OnUpdateWindowListener()); EPStatement state4 = admin.createEPL(epl4); state4.addListener(new OnUpdateSelectWindowListener()); OnUpdateEvent oue1 = new OnUpdateEvent(); oue1.setName("oue1"); oue1.setSize(1); oue1.setPrice(2); runtime.sendEvent(oue1); System.out.println("Send OnUpdateEvent 1: " + oue1); OnUpdateEvent oue2 = new OnUpdateEvent(); oue2.setName("oue2"); oue2.setSize(2); oue2.setPrice(3); runtime.sendEvent(oue2); System.out.println("Send OnUpdateEvent 2: " + oue2); OnUpdateEvent oue3 = new OnUpdateEvent(); oue3.setName("oue3"); oue3.setSize(3); oue3.setPrice(4); runtime.sendEvent(oue3); System.out.println("Send OnUpdateEvent 3: " + oue3); OnUpdateTrigger ost1 = new OnUpdateTrigger(); ost1.setTrigger(0); System.out.println("\nSend OnUpdateTrigger " + ost1); runtime.sendEvent(ost1); OnUpdateTrigger ost2 = new OnUpdateTrigger(); ost2.setTrigger(2); System.out.println("\nSend OnUpdateTrigger " + ost2); runtime.sendEvent(ost2); OnUpdateTrigger ost3 = new OnUpdateTrigger(); ost3.setTrigger(0); System.out.println("\nSend OnUpdateTrigger " + ost3); runtime.sendEvent(ost3); } }执行结果:
Create Window: create window OnUpdateWindow.win:keepall() as select * from example.OnUpdateEvent Update Trigger sentence: on example.OnUpdateTrigger(trigger>0) as out update OnUpdateWindow as ouw set size=out.trigger, price=initial.size where out.trigger<ouw.price Send OnUpdateEvent 1: name=oue1, size=1, price=2 Send OnUpdateEvent 2: name=oue2, size=2, price=3 Send OnUpdateEvent 3: name=oue3, size=3, price=4 Send OnUpdateTrigger trigger=0 Trigger On Select: There is 3 OnUpdateEvent in OnUpdateWindow! name=oue1, size=1, price=2 name=oue2, size=2, price=3 name=oue3, size=3, price=4 Send OnUpdateTrigger trigger=2 Trigger On Update: There is 2 to be updated in OnUpdateWindow! name=oue2, size=2, price=2 name=oue3, size=2, price=3 Send OnUpdateTrigger trigger=0 Trigger On Select: There is 3 OnUpdateEvent in OnUpdateWindow! name=oue1, size=1, price=2 name=oue2, size=2, price=2 name=oue3, size=2, price=3OnUpdateEvent和平时我们定义的事件不太一样,它实现了Serializable接口。这是因为update更新属性前会复制一份同样的事件暂存,比如initial这种操作就需要更新前的值,所以就需要我们实现序列化接口。如果不想通过代码完成这个序列化要求,也可以通过配置完成,这个就不在这里说了。另外还有以下几点需要注意:
8.Triggered Upsert using the On-Merge Clause
除了上面的on select,on update,on delete操作,esper还支持on merge操作。他可以在满足不同条件的情况下完成对应的window中的事件的insert,update和delete操作,所以语法相对前面几种就复杂很多。语法如下:
on event_type[(filter_criteria)] [as stream_name] merge [into] window_name [as stream_name] [where criteria_expression] when [not] matched [and search_condition] then [ insert [into streamname] [ (property_name [, property_name] [,...]) ] select select_expression [, select_expression[,...]] [where filter_expression] | update set property_name = expression [, property_name = expression [,...]] [where filter_expression] | delete [where filter_expression] ] [then [insert|update|delete]] [,then ...] [when ... then ... [...]]a.第一行就不多说了,和前面的用法都一样。
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; import java.io.Serializable; import java.util.HashMap; import java.util.Map; /** * Created by Luonanqin on 4/8/14. */ class MergeEvent implements Serializable { private int mergeId; private String mergeStr; private int mergeSize; private boolean deleteFlag; public int getMergeId() { return mergeId; } public void setMergeId(int mergeId) { this.mergeId = mergeId; } public String getMergeStr() { return mergeStr; } public void setMergeStr(String mergeStr) { this.mergeStr = mergeStr; } public int getMergeSize() { return mergeSize; } public void setMergeSize(int mergeSize) { this.mergeSize = mergeSize; } public boolean isDeleteFlag() { return deleteFlag; } public void setDeleteFlag(boolean deleteFlag) { this.deleteFlag = deleteFlag; } public String toString() { return "mergeId=" + mergeId + ", mergeStr=" + mergeStr + ", mergeSize=" + mergeSize + ", deleteFlag=" + deleteFlag; } } class OnMergeWindowlistener implements UpdateListener { public void update(EventBean[] newEvents, EventBean[] oldEvents) { if (newEvents != null) { System.out.println("Trigger MergeWindow:"); for (int i = 0; i < newEvents.length; i++) { System.out.println(newEvents[i].getUnderlying()); } } } } class SelectLogWindowlistener implements UpdateListener { public void update(EventBean[] newEvents, EventBean[] oldEvents) { if (newEvents != null) { for (int i = 0; i < newEvents.length; i++) { System.out.println(newEvents[i].getUnderlying()); } } } } class SelectMergeWindowlistener implements UpdateListener { public void update(EventBean[] newEvents, EventBean[] oldEvents) { if (newEvents != null) { for (int i = 0; i < newEvents.length; i++) { System.out.println(newEvents[i].getUnderlying()); } } } } public class OnMergeWindowTest { public static void main(String[] args) { EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider(); EPAdministrator admin = epService.getEPAdministrator(); EPRuntime runtime = epService.getEPRuntime(); String mergeEvent = MergeEvent.class.getName(); String epl1 = "create window MergeWindow.win:keepall() select * from " + mergeEvent; String epl2 = "create schema LogEvent as (id int, name string)"; String epl3 = "create window LogWindow.win:keepall() as LogEvent"; String epl4 = "on " + mergeEvent + "(mergeSize > 0) me merge MergeWindow mw where me.mergeId = mw.mergeId " + "when matched and me.deleteFlag = true then delete " + "when matched then update set mergeSize = mergeSize + me.mergeSize where mw.mergeSize > 3 " // MergeWindow中不存在的事件都会在触发merge时插入到window中,同时将部分属性插入到LogWindow中用于记录 + "when not matched then insert select * then insert into LogWindow(id, name) select me.mergeId, me.mergeStr"; String epl5 = "on LogEvent(id=0) select lw.* from LogWindow as lw"; String epl6 = "on " + mergeEvent + "(mergeSize = 0) select mw.* from MergeWindow as mw"; System.out.println("Create Window: " + epl1); System.out.println("Merge Trigger: " + epl4); System.out.println(); admin.createEPL(epl1); admin.createEPL(epl2); admin.createEPL(epl3); EPStatement state1 = admin.createEPL(epl4); state1.addListener(new OnMergeWindowlistener()); EPStatement state2 = admin.createEPL(epl5); state2.addListener(new SelectLogWindowlistener()); EPStatement state3 = admin.createEPL(epl6); state3.addListener(new SelectMergeWindowlistener()); Map<String, Object> selectLog = new HashMap<String, Object>(); selectLog.put("id", 0); MergeEvent selectMerge = new MergeEvent(); selectMerge.setMergeSize(0); MergeEvent me1 = new MergeEvent(); me1.setDeleteFlag(false); me1.setMergeId(1); me1.setMergeSize(2); me1.setMergeStr("me1"); System.out.println("Send MergeEvent 1: " + me1); runtime.sendEvent(me1); MergeEvent me2 = new MergeEvent(); me2.setDeleteFlag(false); me2.setMergeId(2); me2.setMergeSize(3); me2.setMergeStr("me2"); System.out.println("Send MergeEvent 2: " + me2); runtime.sendEvent(me2); MergeEvent me3 = new MergeEvent(); me3.setDeleteFlag(false); me3.setMergeId(3); me3.setMergeSize(4); me3.setMergeStr("me3"); System.out.println("Send MergeEvent 3: " + me3); runtime.sendEvent(me3); /** * 查询之前插入的三个事件 */ System.out.println("\nSend MergeEvent to Select MergeWindow!"); runtime.sendEvent(selectMerge); /** * 查询LogWindow中记录的MergeEvent部分属性 */ System.out.println("\nSend LogEvent to Select LogWindow!"); runtime.sendEvent(selectLog, "LogEvent"); /** * 因为mergeId是3,所以MergeWindow中只有mergeId=3的事件有资格被更新。 * 并且mergeSize>3,所以可以执行更新操作。 */ MergeEvent me4 = new MergeEvent(); me4.setDeleteFlag(false); me4.setMergeId(3); me4.setMergeSize(5); me4.setMergeStr("me4"); System.out.println("\nSend MergeEvent 4: " + me4); runtime.sendEvent(me4); System.out.println("\nSend MergeEvent to Select MergeWindow!"); runtime.sendEvent(selectMerge); /** * 因为mergeId是1,所以MergeWindow中只有mergeId=1的事件有资格被更新。 * 并且deleteFlag=true,所以mergeId=1的事件将从MergeWindow中移除 */ MergeEvent me5 = new MergeEvent(); me5.setDeleteFlag(true); me5.setMergeId(1); me5.setMergeSize(6); me5.setMergeStr("me5"); System.out.println("\nSend MergeEvent 5: " + me5); runtime.sendEvent(me5); System.out.println("\nSend MergeEvent to Select MergeWindow!"); runtime.sendEvent(selectMerge); } }执行结果:
Create Window: create window MergeWindow.win:keepall() select * from example.MergeEvent Merge Trigger: on example.MergeEvent(mergeSize > 0) me merge MergeWindow mw where me.mergeId = mw.mergeId when matched and me.deleteFlag = true then delete when matched then update set mergeSize = mergeSize + me.mergeSize where mw.mergeSize > 3 when not matched then insert select * then insert into LogWindow(id, name) select me.mergeId, me.mergeStr Send MergeEvent 1: mergeId=1, mergeStr=me1, mergeSize=2, deleteFlag=false Trigger MergeWindow: mergeId=1, mergeStr=me1, mergeSize=2, deleteFlag=false Send MergeEvent 2: mergeId=2, mergeStr=me2, mergeSize=3, deleteFlag=false Trigger MergeWindow: mergeId=2, mergeStr=me2, mergeSize=3, deleteFlag=false Send MergeEvent 3: mergeId=3, mergeStr=me3, mergeSize=4, deleteFlag=false Trigger MergeWindow: mergeId=3, mergeStr=me3, mergeSize=4, deleteFlag=false Send MergeEvent to Select MergeWindow! mergeId=1, mergeStr=me1, mergeSize=2, deleteFlag=false mergeId=2, mergeStr=me2, mergeSize=3, deleteFlag=false mergeId=3, mergeStr=me3, mergeSize=4, deleteFlag=false Send LogEvent to Select LogWindow! {id=1, name=me1} {id=2, name=me2} {id=3, name=me3} Send MergeEvent 4: mergeId=3, mergeStr=me4, mergeSize=5, deleteFlag=false Trigger MergeWindow: mergeId=3, mergeStr=me3, mergeSize=9, deleteFlag=false Send MergeEvent to Select MergeWindow! mergeId=1, mergeStr=me1, mergeSize=2, deleteFlag=false mergeId=2, mergeStr=me2, mergeSize=3, deleteFlag=false mergeId=3, mergeStr=me3, mergeSize=9, deleteFlag=false Send MergeEvent 5: mergeId=1, mergeStr=me5, mergeSize=6, deleteFlag=true Send MergeEvent to Select MergeWindow! mergeId=2, mergeStr=me2, mergeSize=3, deleteFlag=false mergeId=3, mergeStr=me3, mergeSize=9, deleteFlag=false
9.Selecting/Updating/Deleting From Named Windows Using Fire-And-Forget Queries
上面对于named window的所有操作,都是要发送一个事件到引擎才可达到目的。那是否可以不发送事件直接执行select/update/delete语句达到目的呢?Esper确实为大家提供了一种简单的方式来操作named window。和之前说的有很大的不同,首先不需要发送任何事件到引擎即可select/update/delete,其次不需要监听器就可立即获得操作结果。4.9版本只支持这三种操作,不过现在的4.11已经增加了很多别的操作,详细内容请各位自己去看官方文档吧。由于本节内容较为简单,所以我直接给大家展示语法及一个完整的例子。
// select select *[, property_name[,...]] from window_name [where criteria_expression] [oder by] [having] [subquery] // update 子查询,聚合函数等不能用于expression update window_name [as stream_name] set property_name = expression [, property_name = expression [,...]] [where criteria_expression] // delete delete from window_name [as stream_name] [where criteria_expression]
package example; import com.espertech.esper.client.EPAdministrator; import com.espertech.esper.client.EPOnDemandQueryResult; import com.espertech.esper.client.EPRuntime; import com.espertech.esper.client.EPServiceProvider; import com.espertech.esper.client.EPServiceProviderManager; import com.espertech.esper.client.EventBean; import java.io.Serializable; /** * Created by Luonanqin on 4/9/14. */ class SelectEvent implements Serializable { private String name; private int size; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getSize() { return size; } public void setSize(int size) { this.size = size; } public String toString() { return "name=" + name + ", size=" + size; } } public class SelectWindowTest { public static void main(String[] args) { EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider(); EPAdministrator admin = epService.getEPAdministrator(); EPRuntime runtime = epService.getEPRuntime(); String selectEvent = SelectEvent.class.getName(); String epl1 = "create window SelectWindow.win:keepall() as select * from " + selectEvent; String epl2 = "insert into SelectWindow select * from " + selectEvent; admin.createEPL(epl1); admin.createEPL(epl2); SelectEvent se1 = new SelectEvent(); se1.setName("se1"); se1.setSize(1); runtime.sendEvent(se1); System.out.println("Send SelectEvent 1: " + se1); SelectEvent se2 = new SelectEvent(); se2.setName("se2"); se2.setSize(2); runtime.sendEvent(se2); System.out.println("Send SelectEvent 2: " + se2); String select = "select * from SelectWindow"; String update = "update SelectWindow set name='update1' where size = 2"; String delete = "delete from SelectWindow where size < 2"; System.out.println("\nSelect SelectWindow!"); EPOnDemandQueryResult selectResult = epService.getEPRuntime().executeQuery(select); EventBean[] events = selectResult.getArray(); for (int i = 0; i < events.length; i++) { System.out.println(events[i].getUnderlying()); } // 更新size=2的事件,将name改为'update1' System.out.println("\nUpdate SelectEvent(size = 2) in SelectWindow!"); EPOnDemandQueryResult updateResult = epService.getEPRuntime().executeQuery(update); events = updateResult.getArray(); for (int i = 0; i < events.length; i++) { System.out.println(events[i].getUnderlying()); } System.out.println("\nSelect SelectWindow!"); selectResult = epService.getEPRuntime().executeQuery(select); events = selectResult.getArray(); for (int i = 0; i < events.length; i++) { System.out.println(events[i].getUnderlying()); } // 删除size<2的事件 System.out.println("\nDelete SelectEvent(size < 2) in SelectWindow!"); EPOnDemandQueryResult deleteResult = epService.getEPRuntime().executeQuery(delete); events = deleteResult.getArray(); for (int i = 0; i < events.length; i++) { System.out.println(events[i].getUnderlying()); } System.out.println("\nSelect SelectWindow!"); selectResult = epService.getEPRuntime().executeQuery(select); events = selectResult.getArray(); for (int i = 0; i < events.length; i++) { System.out.println(events[i].getUnderlying()); } } }执行结果:
Send SelectEvent 1: name=se1, size=1 Send SelectEvent 2: name=se2, size=2 Select SelectWindow! name=se1, size=1 name=se2, size=2 Update SelectEvent(size = 2) in SelectWindow! name=update1, size=2 Select SelectWindow! name=se1, size=1 name=update1, size=2 Delete SelectEvent(size < 2) in SelectWindow! name=se1, size=1 Select SelectWindow! name=update1, size=2
10.Explicitly Indexing Named Windows
因为named window中可以存放事件,所以对存有大量的事件的window进行操作时,效率肯定是一个很大的问题。因此Esper支持对named window建立索引,具体来说是对named window中存放的事件的属性建立索引。我没有实际测试过加过索引之后操作时间有没有缩短,所以如果有人测试过并的确有好效果,希望能在本篇评论里向我反馈下,谢谢。
语法如下:
create [unique] index index_name on named_window_name (property [hash| btree] [, property] [hash|btree] [,...] )unique代表建立唯一索引,如果插入了重复的行,则会抛出异常并阻止重复行插入。如果不使用此关键字,则表示可以插入重复行。index_name为索引的名称,named_window_name是要建立索引的named window。
// create a unique index on user id(hash) and profile id(hash) create unique index UserProfileIndex on UserProfileWindow(userId, profileId) // create a non-unique index on symbol(hash) and buyPrice(btree) create index idx1 on TickEventWindow(symbol hash, buyPrice btree)
11.Dropping or Removing Named Windows
注销named window的方式是直接调用EPStatement对象的destroy方法。虽然注销,但是named window的名字仍然被占用着,所以你只能重新建立和之前的named window一样结构的window,否则会抛出异常。例如:
// Create DropWindow create window DropWindow.win:keepall() as select * from DropEvent // Destroy DropWindow EPStatement state = admin.createEPL("create window DropWindow.win:keepall() as select * from DropEvent"); state.destroy(); // Create DropEvent again(different with prior epl) create window DropWindow.win:keepall() as select name from DropEvent // throw Exception
总共11节,总算是把named window都内容都讲完了。为了完成这篇,我花了很多时间,尽量不遗漏重点,又还要保证代码测试通过,着实费了一番功夫。实际上看过官方文档的同学应该有发现我有一小块内容没写,因为短时间内我没能十分理解,所以为了能尽快为各位提供pattern的知识,所以暂时先记录下来,之后再补上。我争取下一篇能把第五章完结,之后各位非常期待的pattern就可以上场了,希望大家能再等等。