Esper学习之十三:EPL语法(九),esper学习epl语法


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



       距离上一篇博客已经有很多个月的时间了,由于工作的原因,没怎么腾出手来写博客。再加上自己已计划算法学习为第一要务,更新博客的事情暂时就放缓了脚步。所以各位童鞋大可不必等我的博客,先把文档看起来,有什么不懂的先记下来,可以私信问我也可以等文章出来后再理解一遍,这样就不耽误大家的学习了。

       上一篇我们说到了named window,这在Esper中是一个核心内容,基本上真实的业务场景下都会用到这东西,所以篇幅也较长,希望大家能消化好。今天这篇主要讨论的内容不复杂,都是几个小知识点,各位在之后的应用中也会用得到。


1. Splitting and Duplicating Streams

       在之前我们学过的内容中有一个insert into的语法,作用是将某个epl生成的事件流输入到另外的事件中,以方便再次对数据进行处理。当然我也可以将事件流不进行处理就输出到下一个事件流中,使同一个事件流能够交给不同的epl进行处理。但是你会发现这样写起来比较麻烦,每一个insert into是一个句子,引擎要分别对他们进行注册,这样管理起来并不太容易。这一节的内容讲的就是一种简便的方法来达到将同一个事件流分发到不同的事件流中,且他是以一个句子就可以完成的。语法如下:

[context context_name]
on event_type[(filter_criteria)] [as stream_name]
insert into insert_into_def select select_list [where condition]
[insert into insert_into_def select select_list [where condition]]
[insert into...]
[output first | all]

        第一行就不多说了,第二行为触发下面一系列操作的基础事件,也是基础事件流。event_type之后可以跟着过滤条件以及对事件流增加别名。第三行到第五行都是具体的insert句子,多个句子不需要用符号隔开,空格分离就好了。这里面的insert句子和之前学习的没区别,主要就是包含输入到什么事件流要写清楚,以及select的具体内容,where条件为可选。最后一行也是可选内容,output first表示从第一个insert句子开始到最后一个,基础事件依次匹配其where条件,只要满足了某一个,那么插入到对应的事件流以后,这个基础事件就不再有机会和之后的where进行匹配,即不会再进入之后的事件流中。而output all则表示基础事件和所有的insert句子的where进行匹配,能满足的就进入对应的事件流,即不会像first那样限制基础事件只能被输入到某一个流中。注意:select子句和where子句不能用聚合函数(这一点我觉得有点不爽,本来这个语法就是方便做事件分流的,不能用聚合函数的话就显得高不成低不就了)。下面我给大家一个完整的例子进行说明:

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/26/14.
 */
class SplitEvent implements Serializable {

	private int size;
	private int price;

	public SplitEvent(int price, int size) {
		this.price = price;
		this.size = size;
	}

	public int getPrice() {
		return price;
	}

	public void setPrice(int price) {
		this.price = price;
	}

	public int getSize() {
		return size;
	}

	public void setSize(int size) {
		this.size = size;
	}

	public String toString() {
		return "SplitEvent{" + "size=" + size + ", price=" + price + '}';
	}
}

public class SplitDuplicateTest {

	public static void main(String[] args) {
		EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider();
		EPAdministrator admin = epService.getEPAdministrator();
		EPRuntime runtime = epService.getEPRuntime();

		String splitEvent = SplitEvent.class.getName();

		String epl1 = "create window LargeOrder.win:keepall() (price int) ";
		String epl2 = "create window MediumOrder.win:keepall() (price int)";
		String epl3 = "create window SmallOrder.win:keepall() (price int)";
		String epl4 = "create window LargeSize.win:keepall() (size int)";
		String epl5 = "create window MediumSize.win:keepall() (size int)";
		String epl6 = "create window SmallSize.win:keepall() (size int)";

		// output first
		String insert1 = "insert into LargeOrder select price where price > 8";
		String insert2 = "insert into MediumOrder select price where price between 3 and 10";
		String insert3 = "insert into SmallOrder select price where price <= 3";
		String epl7 = "on " + splitEvent + " " + insert1 + " " + insert2 + " " + insert3;

		// output all
		String insert4 = "insert into LargeSize select size where size > 5";
		String insert5 = "insert into MediumSize select size where size between 2 and 8";
		String insert6 = "insert into SmallSize select size where size <= 2";
		String epl8 = "on " + splitEvent + " " + insert4 + " " + insert5 + " " + insert6 + " output all";

		System.out.println("Output first(default): ");
		System.out.println(insert1);
		System.out.println(insert2);
		System.out.println(insert3);

		System.out.println();

		System.out.println("Output all: ");
		System.out.println(insert4);
		System.out.println(insert5);
		System.out.println(insert6);

		String selectLargeOrder = "select * from LargeOrder";
		String selectMediumOrder = "select * from MediumOrder";
		String selectSmallOrder = "select * from SmallOrder";

		String selectLargeSize = "select * from LargeSize";
		String selectMediumSize = "select * from MediumSize";
		String selectSmallSize = "select * from SmallSize";

		System.out.println();

		admin.createEPL(epl1);
		admin.createEPL(epl2);
		admin.createEPL(epl3);
		admin.createEPL(epl4);
		admin.createEPL(epl5);
		admin.createEPL(epl6);

		admin.createEPL(epl7);
		admin.createEPL(epl8);

		SplitEvent se1 = new SplitEvent(1, 2);
		SplitEvent se2 = new SplitEvent(9, 4);
		SplitEvent se3 = new SplitEvent(3, 1);
		SplitEvent se4 = new SplitEvent(5, 6);
		SplitEvent se5 = new SplitEvent(7, 9);

		System.out.println(se1);
		runtime.sendEvent(se1);
		System.out.println(se2);
		runtime.sendEvent(se2);
		System.out.println(se3);
		runtime.sendEvent(se3);
		System.out.println(se4);
		runtime.sendEvent(se4);
		System.out.println(se5);
		runtime.sendEvent(se5);

		EPOnDemandQueryResult selectLOrder = runtime.executeQuery(selectLargeOrder);
		EPOnDemandQueryResult selectMOrder = runtime.executeQuery(selectMediumOrder);
		EPOnDemandQueryResult selectSOrder = runtime.executeQuery(selectSmallOrder);
		EPOnDemandQueryResult selectLSize = runtime.executeQuery(selectLargeSize);
		EPOnDemandQueryResult selectMSize = runtime.executeQuery(selectMediumSize);
		EPOnDemandQueryResult selectSSize = runtime.executeQuery(selectSmallSize);

		outputResult(selectLargeOrder, selectLOrder);
		outputResult(selectMediumOrder, selectMOrder);
		outputResult(selectSmallOrder, selectSOrder);
		outputResult(selectLargeSize, selectLSize);
		outputResult(selectMediumSize, selectMSize);
		outputResult(selectSmallSize, selectSSize);
	}

	private static void outputResult(String selectSentence, EPOnDemandQueryResult result) {
		System.out.println("\n" + selectSentence);
		EventBean[] events = result.getArray();
		for (int i = 0; i < events.length; i++) {
			EventBean event = events[i];
			System.out.println(event.getUnderlying());
		}
	}
}
执行结果:

Output first(default): 
insert into LargeOrder select price where price > 8
insert into MediumOrder select price where price between 3 and 10
insert into SmallOrder select price where price <= 3

Output all: 
insert into LargeSize select size where size > 5
insert into MediumSize select size where size between 2 and 8
insert into SmallSize select size where size <= 2

SplitEvent{size=6, price=5}
SplitEvent{size=2, price=1}
SplitEvent{size=4, price=9}
SplitEvent{size=1, price=3}
SplitEvent{size=9, price=7}

select * from LargeOrder
{price=9}

select * from MediumOrder
{price=3}
{price=5}
{price=7}

select * from SmallOrder
{price=1}

select * from LargeSize
{size=6}
{size=9}

select * from MediumSize
{size=2}
{size=4}
{size=6}

select * from SmallSize
{size=2}
{size=1}

从上面的例子中可以看出,在默认情况下,也就是output first的时候,price=9进入LargeOrder后就不再进入MediumOrder,即使9是大于3小于10。


2. Variables and Constants

       变量和常量在各类计算机语言中我相信大家都不陌生,在Esper同样有这个定义,并且用途广泛。比如说:时间、过滤条件等等,可以用于各种计算表达式。变量和常量都可以通过三种方式建立:API、EPL、配置文件。各有各的优点,这一节只讲前两种创建方式,配置文件后面会有专门的章节进行讲解。


调用API创建变量和常量:

EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider();
EPAdministrator admin = epService.getEPAdministrator();
ConfigurationOperations conf = admin.getConfiguration();
conf.addVariable("abc", String.class, "initVariable");
conf.addVariable("constabc", int.class.getName(), 123, true);

... equals ...

Configuration conf = new Configuration();
conf.addVariable("abc", String.class, "initVariable");
conf.addVariable("constabc", int.class.getName(), 123, true);
EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider(conf);
可以看到,调用API也有两种方式,一种是建立了EPAdministrator之后再设置变量及常量,一种是先配置好变量及常量,再生成epSerivce对象。两种各有好处,大家自己斟酌。


用EPL创建变量和常量:

create [constant] variable variable_type [[]]variable_name [ = assignment_expression ]
       constant为可选关键字,显式声明则表示声明的是常量,否则声明的是变量。variable_type和variable_name分别表示变量的数据类型和变量名,变量名必须唯一。variable_type之后的[]表示这是一个数组类型的变量。之后的assignment_expression为该变量的初始值,如果不声明则表示没有初始值。下面的列表是可声明的变量类型:

string\char\character\bool\boolean\byte\short\int\integer\long\double\float\object\enum_class\class_name\event_type_name


下面举几个例子来看看:

// 创建一个long类型的变量,没有初始值
create variable long var_threshold

// 创建一个integer类型的变量,初始值为10
create variable integer var_output_rate = 10

// 创建一个object类型的变量,没有初始值(使用时需要转换成对应的对象类型)
create variable object varobj

// 创建一个PageHitEvent事件类型的变量,没有初始值(在此之前需要导入这个事件类型到引擎)
create variable PageHitEvent varPageHitZero

// 创建一个OrderEvent事件类型的变量,没有初始值(可以直接使用类全名作为事件类型)
create variable com.abc.OrderEvent orderEvent

// 创建一个string类型的常量,初始值为'GE'
create constant variable string const_filter_symbol = 'GE'

// 创建一个string数组类型的常量,初始值为{'GE', 'MSFT'}
create constant variable string[] const_filters = {'GE', 'MSFT'}

// 创建一个enumeration数组类型的常量,初始值为{Color.RED, Color.BLUE}
create constant variable Color[] const_colors = {Color.RED, Color.BLUE}

常量创建后自然是不能修改的,但是变量肯定是可以改变的。语法如下:

on event_type[(filter_criteria)] [as stream_name]
  set variable_name = expression [, variable_name = expression [,...]]

简单来说就是通过接收某类事件并加上一定的过滤条件,就能够将变量重新赋值,并且可以同时为多个变量赋值。举例如下:

on NewOutputRateEvent set var_output_rate = rate

on ThresholdUpdateEvent as t set var_threshold_lower = t.lower, var_threshold_higher = t.higher

3. Contained-Event Selection

       这一节讲解的查询语法比较特殊,该语法主要是针对某个事件的属性仍是事件的一种查询方式。很多人都有把它翻译成中文的习惯,比如:容器事件查询,又或者查询容器事件。啥叫容器事件?反正我是翻译不出来,而且我也建议的大家不要翻译,以免在学习的过程中对不上,只要理解这个词的概念就行了。下面我们先用最简单的select-from子句进行讲解。语法如下:

[select select_expressions from]
	contained_expression [@type(eventtype_name)] 
[as alias_name] [where filter_expression]
        方括号里的东西是可选的,并且大部分大家应该都很熟悉,除了那个@type,这个我们之后再讲。该语法的重点在于contained_expression,这个表达式需要返回事件的属性,该属性可以是另一个事件也可以是普通数据类型,也可以是一组查询结果,比如一个数组,一个集合等等。官方文档的5.20节有一个xml的例子,下面的示例我将以此作为基础。并且由于在之前我没有讲解xml作为事件是如何处理的,所以特地将xml转换成了对应的POJO进行测试,方便各位理解。


基础事件定义:

public class Item {
	private int itemId;
	private int productId;
	private int amount;
	private double price;

	// getter and setter method...

	public String toString() {
		return "Item{" + "itemId=" + itemId + ", productId=" + productId + ", amount=" + amount + ", price=" + price + '}';
	}
}

public class Items {
	private List<Item> item;
	// getter and setter method...

}

public class Review {
	private int reviewId;
	private String comment;

	// getter and setter method...

	public String toString() {
		return "Review{" + "reviewId=" + reviewId + ", comment='" + comment + '\'' + '}';
	}
}

public class Book {
	private int bookId;
	private String author;
	private Review review;

	// getter and setter method...

	public String toString() {
		return "Book{" + "bookId=" + bookId + ", author='" + author + '\'' + ", review=" + review + '}';
	}
}

public class Books {
	private List<Book> book;

	// getter and setter method...
}

public class MediaOrder {
	private int orderId;
	private Items items;
	private Books books;

	// getter and setter method...

	public String toString() {
		return "MediaOrder{" + "orderId=" + orderId + ", items=" + items + ", books=" + books + '}';
	}
}

主类:

class SelectContainedListener implements UpdateListener {

	public void update(EventBean[] newEvents, EventBean[] oldEvents) {
		if (newEvents != null) {
			for (int i = 0; i < newEvents.length; i++) {
				if (newEvents[i] == null) {
					continue;
				}
				System.out.println(newEvents[i].getUnderlying());
			}
		}
	}
}

public class SelectContainedEventTest {

	public static void main(String[] args) {
		EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider();
		EPAdministrator admin = epService.getEPAdministrator();
		EPRuntime runtime = epService.getEPRuntime();

		Review r1 = new Review();
		r1.setReviewId(1);
		r1.setComment("r1");

		Book b1 = new Book();
		b1.setAuthor("b1");
		b1.setBookId(1);
		b1.setReview(r1);

		Book b2 = new Book();
		b2.setAuthor("b2");
		b2.setBookId(2);

		Item i1 = new Item();
		i1.setItemId(1);
		i1.setProductId(1);
		i1.setPrice(1.11);
		i1.setAmount(2);

		MediaOrder mo1 = new MediaOrder();
		Books bs = new Books();
		Items is = new Items();
		List<Item> items = new ArrayList<Item>();
		List<Book> books = new ArrayList<Book>();
		items.add(i1);
		books.add(b1);
		books.add(b2);
		mo1.setOrderId(1);
		bs.setBook(books);
		is.setItem(items);
		mo1.setItems(is);
		mo1.setBooks(bs);

		String mediaOrder = MediaOrder.class.getName();
		String epl = "select * from " + mediaOrder + "[books.book]";
		EPStatement stat1 = admin.createEPL(epl);
		stat1.addListener(new SelectContainedListener());

		runtime.sendEvent(mo1);
	}
}
输出结果:

Book{bookId=1, author='b1', review=Review{reviewId=1, comment='r1'}}
Book{bookId=2, author='b2', review=null}

例子很简单,就是将Books中的Book筛选出来。再进一步,如果只想把Review筛选出来,句子应该这么写:
select * from MediaOrder[books.book][review]

// not valid
select * from MediaOrder[books.book.review]
第二种写法错误是因为books.book返回的是一个Book对象的数组,数组自然是不会包含Review属性的。所以改成下面这样就可以了:

select * from MediaOrder[books.book[1].review] // 取Book数组中下标为1的对象的review属性


3.1 Select Clause in a Contained-Event Selection

        上面的几个例子都是查出某个对象的所有属性,比如Book和Review。如果要查部分属性,则需要select子句的帮助,也就是之前说的可选部分[select select_expressions from]

还是上面的数据,我们写几个句子看看:

// 查询bookId,orderId
1)select * from MediaOrder[select bookId, orderId from books.book][review]

... equals ...

2)select * from MediaOrder[books.book][select bookId, orderId from review]

// not valid
3)select * from MediaOrder[select bookId, orderId, reviewId from books.book][review]

// valid 查询bookId,orderId,reviewId
4)select * from MediaOrder[books.book][select bookId, orderId, reviewId from review]
        大家可能会有疑问,为什么1)中orderId是来自books.book?又为什么3)的这种写法不对?既然orderId可以,reviewId不可以?文档中对这点没有任何的说明,相当于是一种固定语法了。但是细想一下,orderId和book都是MediaOrder的属性并且是同级的。所以我个人认为(纯属猜想),从语法分析的角度考虑,同一级别的属性,不会只能解析出book而不会找不到orderId。因此3)中,由于reviewId是book的review属性的属性,所以只能来自review,没法跳过review,直接从book中获得。

         1)和2)中的select子句只有bookId和orderId,所以1)最后的[review]实际上没有什么用,除非你写成这样:select * from MediaOrder[select bookId, orderId from books.book][select * from review]。得出的就是bookId,orderId以及Review对象的所有属性。

在使用上面的语法时,要注意子查询,聚合函数等不能用在contained-event的select子句中。

3.2 Where Clause in a Contained-Event Selection

除了可以在Contained-Event上用select子句,同样也可以用where子句,比如:

// 查询author是luonanqin的book
select * from MediaOrder[books.book where author = 'luonanqin']

// 查询comment中包含good字符串的review
select * from MediaOrder[books.book][review where comment like 'good']


3.3 Contained-Event Selection and Joins

       Contained-Event除了一些普通的用法之外,还可以使用在join查询中。包括内联、左联、右联以及全外连接。由于用法简单,所以我直接给了一个完整的例子。POJO类仍然利用上面的定义不变。

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 example.model.contained.Book;
import example.model.contained.Books;
import example.model.contained.Item;
import example.model.contained.Items;
import example.model.contained.MediaOrder;
import example.model.contained.Review;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by Luonanqin on 7/30/14.
 */
class JoinContainedListener implements UpdateListener {

	public void update(EventBean[] newEvents, EventBean[] oldEvents) {
		if (newEvents != null) {
			for (int i = 0; i < newEvents.length; i++) {
				if (newEvents[i] == null) {
					continue;
				}
				System.out.println(newEvents[i].getUnderlying());
			}
		}
	}
}

public class JoinContainedEventTest {

	public static void main(String[] args) {
		EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider();
		EPAdministrator admin = epService.getEPAdministrator();
		EPRuntime runtime = epService.getEPRuntime();

		Review r1 = new Review();
		r1.setReviewId(1);
		r1.setComment("r1");

		Book b1 = new Book();
		b1.setAuthor("b1");
		b1.setBookId(1);
		b1.setReview(r1);

		Book b2 = new Book();
		b2.setAuthor("b2");
		b2.setBookId(2);
		b2.setReview(new Review());

		Item i1 = new Item();
		i1.setItemId(1);
		i1.setProductId(1);
		i1.setPrice(1.11);
		i1.setAmount(2);

		Item i2 = new Item();
		i2.setItemId(3);
		i2.setProductId(3);
		i2.setPrice(3.11);
		i2.setAmount(5);

		MediaOrder mo1 = new MediaOrder();
		Books bs = new Books();
		Items is = new Items();
		List<Item> items = new ArrayList<Item>();
		List<Book> books = new ArrayList<Book>();
		items.add(i1);
		items.add(i2);
		books.add(b1);
		books.add(b2);
		mo1.setOrderId(1);
		bs.setBook(books);
		is.setItem(items);
		mo1.setItems(is);
		mo1.setBooks(bs);

		String mediaOrder = MediaOrder.class.getName();
		String join1 = "select book.bookId, item.itemId from " + mediaOrder + "[books.book] as book, " + mediaOrder
				+ "[items.item] as item where productId = bookId";

		EPStatement stat1 = admin.createEPL(join1);
		stat1.addListener(new JoinContainedListener());

		System.out.println("EPL1: " + join1);
		runtime.sendEvent(mo1);
		stat1.destroy();
		System.out.println();

		String join2 = "select book.bookId, item.itemId from " + mediaOrder + "[books.book] as book left outer join " + mediaOrder
		  + "[items.item] as item on productId = bookId";

		EPStatement stat2 = admin.createEPL(join2);
		stat2.addListener(new JoinContainedListener());

		System.out.println("EPL2: " + join2);
		runtime.sendEvent(mo1);
		stat2.destroy();
		System.out.println();

		String join3 = "select book.bookId, item.itemId from " + mediaOrder + "[books.book] as book full outer join " + mediaOrder
		  + "[items.item] as item on productId = bookId";

		EPStatement stat3 = admin.createEPL(join3);
		stat3.addListener(new JoinContainedListener());

		System.out.println("EPL3: " + join3);
		runtime.sendEvent(mo1);
		stat3.destroy();
		System.out.println();

		String join4 = "select count(*) from " + mediaOrder + "[books.book] as book, " + mediaOrder
		  + "[items.item] as item where productId = bookId";

		EPStatement stat4 = admin.createEPL(join4);
		stat4.addListener(new JoinContainedListener());

		System.out.println("EPL4: " + join4);
		runtime.sendEvent(mo1);
		runtime.sendEvent(mo1);
		stat4.destroy();
		System.out.println();

		String join5 = "select count(*) from " + mediaOrder + "[books.book] as book unidirectional, " + mediaOrder
		  + "[items.item] as item where productId = bookId";

		EPStatement stat5 = admin.createEPL(join5);
		stat5.addListener(new JoinContainedListener());

		System.out.println("EPL5: " + join5);
		runtime.sendEvent(mo1);
		runtime.sendEvent(mo1);
		stat5.destroy();
		System.out.println();
	}
}
执行结果:

EPL1: select book.bookId, item.itemId from example.model.contained.MediaOrder[books.book] as book, example.model.contained.MediaOrder[items.item] as item where productId = bookId
{item.itemId=1, book.bookId=1}

EPL2: select book.bookId, item.itemId from example.model.contained.MediaOrder[books.book] as book left outer join example.model.contained.MediaOrder[items.item] as item on productId = bookId
{item.itemId=1, book.bookId=1}
{item.itemId=null, book.bookId=2}

EPL3: select book.bookId, item.itemId from example.model.contained.MediaOrder[books.book] as book full outer join example.model.contained.MediaOrder[items.item] as item on productId = bookId
{item.itemId=1, book.bookId=1}
{item.itemId=null, book.bookId=2}
{item.itemId=3, book.bookId=null}

EPL4: select count(*) from example.model.contained.MediaOrder[books.book] as book, example.model.contained.MediaOrder[items.item] as item where productId = bookId
{count(*)=1}
{count(*)=2}

EPL5: select count(*) from example.model.contained.MediaOrder[books.book] as book unidirectional, example.model.contained.MediaOrder[items.item] as item where productId = bookId
{count(*)=1}
{count(*)=1}
        前三个例子应该非常好理解,和普通连接操作没太大区别。4和5的区别在于unidirectional,4中不包含这个关键字时,发送两个同样的mediaOrder对象,聚合函数count是要累加的,而5有了这个关键字后,就将每次连接操作的结果都独立起来了。但是有一点要记住:如果Contained-Event来自于named window,那么连接操作就必须加上unidirectional关键字

        上面的三小节基本上就是Contained-Event的全部内容,官方文档上还有别的例子可以看看。不过说实话本人其实也没用过,但是存在即合理,所以咱们暂且先学习着,以后肯定能用到。

4. Updating an Insert Stream

       在之前讲解基础语法的时候,大家都接触了select、insert into、on delete、on update等操作,并且后两个是针对named window的。那有没有单独的update语法呢?有!为什么放到这一节才讲呢?我其实也是跟着文档来进行总结的,简直就是坑货嘛。今天先教大家,改日有空了我再移到前面和基础语法放到一起。

多说了几句废话。。。update语法简单,意义也简单,就是在事件即将被用于计算前,改变其自身的属性值,然后再将其用于后面的计算。语法如下:

update istream event_type [as stream_name]
set property_name = set_expression [, property_name = set_expression] [,...]
[where where_expression]
       istream表明更新的是新输入的事件(但是没有rstream,不知道这里为啥要指明这个关键字。。),event_type代表要更新的事件,set之后的property_name是要更新的事件属性,最后可以用where子句进行简单的过滤。

       因为事件会在计算前被更新,所以在事件还未到来之前,两类句子的创建顺序不管怎样都会使得update生效。但是如果有部分事件已经参与计算了,之后才创建update句子,那已经参与计算的事件就不会被更新了,之后的进入引擎的事件才会被更新。

       上面说的是不同于update句子的创建顺序导致的不同结果。如果有多个update句子作用于同一类事件,那么先创建的会先更新事件。但是如果在update句首用@Priority这个注解,那么更新事件的顺序是以优先级最高的最先更新。所以我建议大家在同时使用多个update句子的时候,最好使用优先级,因为你很可能搞不清楚所有update句子的创建顺序。

针对以上两点,我提供了一个简单的例子供大家参考。

package example;

import com.espertech.esper.client.Configuration;
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 7/31/14.
 */
class UpdateEvent implements Serializable {

	private int id;
	private String name;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String toString() {
		return "UpdateEvent{" + "id=" + id + ", name='" + name + '\'' + '}';
	}
}

class UpdateEventListener implements UpdateListener {

	public void update(EventBean[] newEvents, EventBean[] oldEvents) {
		if (newEvents != null) {
			for (int i = 0; i < newEvents.length; i++) {
				System.out.println("newEvent: " + newEvents[i].getUnderlying());
			}
		}
		if (oldEvents != null) {
			for (int i = 0; i < oldEvents.length; i++) {
				System.out.println("oldEvent: " + oldEvents[i].getUnderlying());
			}
		}
	}
}

public class UpdateEventTest {

	public static void main(String[] args) {
		/**
		 * config:
		 *
		 * <engine-settings>
		 *		<defaults>
		 *			<execution prioritized="true"/>
		 *		</defaults>
		 *	</engine-settings>
		 */
		Configuration config = new Configuration();
		config.configure("esper.examples.cfg.xml");
		EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider(config);
		EPAdministrator admin = epService.getEPAdministrator();
		EPRuntime runtime = epService.getEPRuntime();
		UpdateEventListener listener = new UpdateEventListener();

		UpdateEvent ue = new UpdateEvent();
		ue.setId(1);
		ue.setName("luonanqin");

		UpdateEvent ue2 = new UpdateEvent();
		ue2.setId(2);
		ue2.setName("qinnanluo");

		String updateEvent = UpdateEvent.class.getName();
		String select = "select * from " + updateEvent;
		String update1 = "@Priority(2)update istream " + updateEvent + " set name='qnoul' where id = 1";
		String update2 = "@Priority(1)update istream " + updateEvent + " set name='luonq' where id = 1";

		EPStatement stat1 = admin.createEPL(select);
		stat1.addListener(listener);
		System.out.println("select EPL: " + select);

		runtime.sendEvent(ue);
		runtime.sendEvent(ue2);
		System.out.println();

		EPStatement stat2 = admin.createEPL(update1);
		stat2.addListener(listener);
		System.out.println("update1 EPL: " + update1);

		runtime.sendEvent(ue);
		runtime.sendEvent(ue2);
		System.out.println();

		EPStatement stat3 = admin.createEPL(update2);
		stat3.addListener(listener);
		System.out.println("update2 EPL: " + update2);

		runtime.sendEvent(ue);
		runtime.sendEvent(ue2);
	}
}
执行结果(注释是我自己添加的,不属于执行结果):

select EPL: select * from example.UpdateEvent
newEvent: UpdateEvent{id=1, name='luonanqin'}
newEvent: UpdateEvent{id=2, name='qinnanluo'}

update1 EPL: @Priority(2)update istream example.UpdateEvent set name='qnoul' where id = 1
newEvent: UpdateEvent{id=1, name='qnoul'}		// update1 result
oldEvent: UpdateEvent{id=1, name='luonanqin'}   // update1 result
newEvent: UpdateEvent{id=1, name='qnoul'}		// select result
newEvent: UpdateEvent{id=2, name='qinnanluo'}   // select result

update2 EPL: @Priority(1)update istream example.UpdateEvent set name='luonq' where id = 1
newEvent: UpdateEvent{id=1, name='luonq'}		// update2 result
oldEvent: UpdateEvent{id=1, name='luonanqin'}	// update2 result
newEvent: UpdateEvent{id=1, name='qnoul'}		// update1 result
oldEvent: UpdateEvent{id=1, name='luonq'}		// update1 result
newEvent: UpdateEvent{id=1, name='qnoul'}		// select result
newEvent: UpdateEvent{id=2, name='qinnanluo'}	// select result
updateListener方法中,newEvents表示更新后的属性值,oldEvents表示更新前的属性值。

使用update语法有几点要注意:
1). 如果事件是POJO,那么要实现java.io.Serializable接口。因为引擎内部的update操作实际上是要先深复制原事件再更新拷贝后的事件,不会对原事件作出任何修改。
2). 设置属性的表达式不能用聚合函数
3). 如果事件是xml,update语法则不适用
4). update操作不可用于嵌套的事件



5. Controlling Event Delivery : The For Clause

       大家应该都注意到在此之前我们的UpdateListener在达到触发条件时,都是只触发一次,并且把满足这次触发的所有事件传给update方法。Esper对此提供了一个比较另类的触发方式——可设置条件对Listener进行多次触发,也就是这节所讲的For子句。各位可别把这个For子句想成for循环了,根本就是两码事。

For子句可应用在所有的Select子句之后,并且分为两类触发。一个是根据分组条件进行多次触发,即每一组触发一次。另一个是一个事件触发一次。对应的语法如下:

// 分组触发
... for grouped_delivery (group_expression [, group_expression] [,...])

// 每个事件都触发
... for discrete_delivery

针对上面两类触发,我提供了一个简单的示例。

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 7/31/14.
 */
class ForEvent {

	private String name;
	private int age;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public String toString() {
		return "ForEvent{" + "name='" + name + '\'' + ", age=" + age + '}';
	}
}

class ForListener implements UpdateListener {

	// 用于记录调用次数
	private int num = 1;

	public void update(EventBean[] newEvents, EventBean[] oldEvents) {
		System.out.println("invocation: " + num++);
		if (newEvents != null) {
			for (int i = 0; i < newEvents.length; i++) {
				System.out.println(newEvents[i].getUnderlying());
			}
		}
	}
}

public class ForTest {

	public static void main(String[] args) {
		EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider();
		EPAdministrator admin = epService.getEPAdministrator();
		EPRuntime runtime = epService.getEPRuntime();

		String forEvent = ForEvent.class.getName();
		String select = "select * from " + forEvent + ".win:length_batch(3)";
		String for1 = "select * from " + forEvent + ".win:length_batch(3) for grouped_delivery (age)";
		String for2 = "select * from " + forEvent + ".win:length_batch(3) for discrete_delivery";

		ForEvent fe1 = new ForEvent();
		fe1.setName("luo");
		fe1.setAge(1);

		ForEvent fe2 = new ForEvent();
		fe2.setName("nan");
		fe2.setAge(2);

		ForEvent fe3 = new ForEvent();
		fe3.setName("qin");
		fe3.setAge(1);

		EPStatement stat1 = admin.createEPL(select);
		stat1.addListener(new ForListener());
		System.out.println("select EPL1: " + select);

		System.out.println();
		runtime.sendEvent(fe1);
		runtime.sendEvent(fe2);
		runtime.sendEvent(fe3);
		stat1.destroy();
		System.out.println();

		EPStatement stat2 = admin.createEPL(for1);
		stat2.addListener(new ForListener());
		System.out.println("for EPL2: " + for1);

		System.out.println();
		runtime.sendEvent(fe1);
		runtime.sendEvent(fe2);
		runtime.sendEvent(fe3);
		stat2.destroy();
		System.out.println();

		EPStatement stat3 = admin.createEPL(for2);
		stat3.addListener(new ForListener());
		System.out.println("for EPL3: " + for2);

		System.out.println();
		runtime.sendEvent(fe1);
		runtime.sendEvent(fe2);
		runtime.sendEvent(fe3);
	}
}
执行结果:

select EPL1: select * from example.ForEvent.win:length_batch(3)

invocation: 1
ForEvent{name='luo', age=1}
ForEvent{name='nan', age=2}
ForEvent{name='qin', age=1}

for EPL2: select * from example.ForEvent.win:length_batch(3) for grouped_delivery (age)

invocation: 1
ForEvent{name='luo', age=1}
ForEvent{name='qin', age=1}
invocation: 2
ForEvent{name='nan', age=2}

for EPL3: select * from example.ForEvent.win:length_batch(3) for discrete_delivery

invocation: 1
ForEvent{name='luo', age=1}
invocation: 2
ForEvent{name='nan', age=2}
invocation: 3
ForEvent{name='qin', age=1}

可以看到第二个For子句中的分组条件是age,所以只触发了两次。既然这样,下面这个句子有问题吗:
select name from example.ForEvent.win:length_batch(3) for grouped_delivery (age)
select的内容是name,分组条件是age,Esper不允许这样做。这也是使用For子句时唯一要注意的地方,大家别忽略了。


        EPL的基本语法到这篇为止算是告一段落了,包括前面的八篇文章,都是要熟练掌握的。之后的内容是各位一直期待的Pattern篇,初步估计用3~4篇说完,太长了也怕你们看累了。^_^





 




相关内容