Esper学习之十五:Pattern(二),esper学习pattern


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



       上一篇开始了新一轮语法——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之后会有详细说明。

       以上面的例子来说,我们会监听所有进入引擎的FollowedEvent事件,并等待匹配规则的follow事件。但是如果满足条件的事件一直不发生,那么之前的等待事件就会一直存于引擎内,这势必会引起内存溢出,或者我们有某种需求,即只保留某一部分的事件用来等待匹配的follow事件。Esper为此提供了进阶的Followed-by语法:
lhs_expression -[limit_expression]> rhs_expression
       lhs_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到达后就直接触发了。

关于限制左边事件的内容,有些是通过配置完成。比如说针对所有的pattern语句都限制,不必每次都写在句子中。这个在之后的配置章节会有讲解。

2.Every
这个操作符想必大家已经不陌生了。例子也看了这么多,顾名思义也能想到它代表的就是“每一个”的意思。实际上他表示的是,为操作符后的事件或者子pattern表达式建立一个监听实例,只要满足触发条件这个实例就到此结束。比如:

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。

我之所以先讲Followed-by,就是因为every和->的不同优先级会把各位弄晕,所以先让大家把->搞清楚再来看各种组合情况。

我把文档里的一个优先级例子放在这里专门讲解下,完整例子我就不写了。

假设事件传入引擎的顺序是这样的:

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

3. Every-Distinct
Every-Distinct和Every基本一样,唯一的区别是Every-Distinct会根据事件的某些属性过滤掉重复的,避免触发监听器。其余用法和Every相同。具体语法如下:

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实例被销毁。

语法讲解就这么多,以下几个点需要各位注意:
1).语法后面跟的子表达式如果返回false,pattern也会被重置。这一点下面的例子会说明。
2).无论多少个事件的属性参与过滤,其事件名必须设置别名,以“别名.属性名”的方式写在圆括号内。
3).事件别名一定要在子表达式中定义,否则语法错误。比如:a=A -> every-distinct(a.aprop) b=B


总结上面的所有内容,我写了个完整的例子供大家参考。

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时监听器被触发。


       Pattern的操作符本篇只说了3个,内容虽然不多,但是大家一定要掌握好,特别是操作符之间的优先级,理解不透的话很容易出现意想不到的结果。下篇会接着讲几个我自己都很少用到操作符,敬请期待……




hannah montana第二季第十五集开头miley唱的是什歌?

one in a million(万分之一)
歌词:
~How did I get here
我怎麼到这里的
I turned around and there you were
当我转身后还有你
Didn't think twice or rationalize
不用想第二次或者把它合理化
But somehow I knew
但我不知道为什麼就能了解
That there was more than just chemistry
我们之间渐渐出现的感情
I mean I know you were kind of in to me
我指的是我知道你开始在我心中形成的地位
But I figured it's just to good to be true
但对我来说那实在太美好到难以成真了

I said pinch me where the catch this time
这次我的心真的被抓住了
Can't find single cloud in the sky
就如同不会在天空中找到一片孤单的云
Help me before I get used to this guy
请在我习惯某个人之前帮助我吧

*They say that good things take time
大家总说好的事需要花时间
But really great things happen in a blink of an eye
但真正棒的事却发生在眨眼瞬间
Thought the chances to meet someone like you were a million to one
虽然遇见像你一样的人的机会是一百万分之一
I can believe it
我相信
Your one in a million
你就是那一百万分之一的机会
All this time I was looking for love
这次我找到我的真爱了
Trying to make things work
试著让事情行的通吧
They weren't good enough till I thought I'm through
它从不够好直到我相信我突破了
Said I'm done
相信我做到了
Then stumbled into the arms on the one
然后在某一方面失足而依靠你

You're making me laugh about the silliest stuff
你总是用你最愚蠢的行为让我开心
Said that I'm your diamond in the rough
说我是你未琢磨过的钻石
When I'm mad at you
当我对你生气时
You come with your velvet touch
你会给我最温柔的感动
Can't believe that I'm so lucky
不敢相信我这麼幸运
I have never felt so happy
我从未感到如此开心
Every time I see that sparkle in your eye
在我每一次看见你眼中的光芒时

repeat*

下载地址:
......余下全文>>
 


相关内容