Esper學習之六:EPL語法(二)

weixin_33711641發表於2013-09-22

        中秋三天,說閒也不閒,調調工作的程式碼,倒還解決不少問題。不過也是因為最近工作忙的緣故,Esper被我冷落不少日子了,趁著今天最後一天,趕緊寫一篇出來。

        從上一篇開始說EPL的語法,主要是關於註解的。今天來說說比較常用的語法,Select Clause和From Clause。這個兩個可以說是寫EPL必備,要想得到事件流的處理結果,基本上就靠他們倆了(Pattern除外)。今天的內容比較簡單,還請各位同學牢記,以免以後應用的時候花時間看文件或者我的文章。


Select Clause

1.查詢事件流的所有屬性及特定屬性

EPL的select和SQL的select很相近,SQL用*表示查詢表的所有欄位,而EPL用*表示查詢事件流的所有屬性值。SQL查詢某個欄位名,直接在select後跟欄位名就ok,EPL也是將要查詢的屬性名放在select之後。若查多個屬性值,則用逗號分割。和SQL一樣,EPL查詢屬性也可以設定別名。示例如下:

 

// EPL:查詢完整的User物件
select * from User
// 獲取User物件
User u = newEvent.getUnderlying();

// EPL:查詢User的name和id,id別名為i
select name, id as i from User
// 獲取name和id
String name = (String)newEvent.get("name");
int id = (Integer)newEvent.get("i");

這裡要注意,如果查詢的是一個完整物件,需要呼叫getUnderlying()方法,而get方法是針對確定的屬性名或者別名。另外*是不能設定別名的。

 


2.表示式

除了查詢完整物件和特定屬性,EPL還支援屬性值的計算,以計算後的值作為結果返回,並且也能設定別名。這個計算的式子就是表示式。例如:

 

// 計算長方形的面積(長乘以寬)
select length * width as area from Rectangle

除了簡單的加減乘除,還可以利用事件流物件的某個方法。例如:

 

 

// 計算長方形的面積(長乘以寬)
select r.getArea(r.length,r.width) as area from Rectangle as r

select r.getArea() as area from Rectangle as r

 

 

// Rectangle類
public class Rectangle
{
    private int length;
    private int width;

    /** 省略getter/setter方法 **/
    // 外部傳入引數計算面積
    public int getArea(int l, int w){
        return l*w;
    }

    // 計算該物件的面積
    public int getArea(){
        return length * width;
    }
}

 

如上所示,一個方法需要傳參,另一個方法不需要,但是他會利用當前事件的length和width來計算面積。而且要注意的是事件流需要設定別名才能使用其方法,如:r.getArea()

如果Rectangle類裡沒有計算面積的方法,但是提供了一個專門計算面積的靜態方法,表示式也可以直接引用。不過要事先載入這個包含方法的類。例如:

 

// 該類用於計算面積
public class ComputeArea{
	
	public static int getArea(int length, int width){
		return length*width;
	}
}

// 載入
epService.getEPAdministrator().getConfiguration().addImport(ComputeArea.class);
// 呼叫ComputeArea的getArea方法計算面積
select ComputeArea.getArea(length,width) from Rectangle

注意一定要是靜態方法,不然沒有例項化是沒法引用的。

 


3.多事件流的查詢

和SQL類似,EPL也可以同時對多個事件流進行查詢,但是必須對每個事件流設定別名。例如:

 

// 當老師的id和學生的id相同時,查詢學生的姓名和老師的姓名
select s.name, t.name from Student as s, Teacher as t where s.id=t.id

如果想查詢Student或者Teacher,則EPL改寫如下:

 

 

select s.* as st, t.* as tr from Student as s, Teacher as t where s.id=t.id

如果想要查詢的屬性只有存在於一個事件,那麼可以不用"別名.屬性名",但是最好還是帶上別名,萬一哪天另一個事件流多了一個一樣的屬性,那時候不需要修改EPL也可以使用。

 


4.insert和remove事件流

Esper對於事件流分輸入和移出兩種,分別對應監聽器的兩個引數newEvents和oldEvents,關於監聽器的內容可參看《Esper學習之三:程式模型 》。newEvents通常對應事件的計算結果,oldEvents可以理解過上一次計算結果。預設情況下,只有newEvents有值,oldEvents為null。如果需要檢視oldEvents,則需要使用一個引數。例如:

 

select rstream * from User

如果使用了該引數,則會將上一次計算結果放入newEvents內,而不是oldEvents(以前我還以為這是一個bug,後面發現手冊上官方明確就是newEvents,汗!)。並且無法獲得當前的計算結果

 

 

select irstream * from User

如果使用了該引數,則會將當前的計算結果放入newEvents內,上一次的計算結果放入oldEvents內。

 

 

select istream * from User
// 等同於
select * from User

如果使用了該引數,則會將當前的計算結果放入newEvents內,並且無法獲得上一次的計算結果。同時該引數也是預設引數,可不寫。

 

如果想修改預設引數,需要呼叫配置介面修改配置。


5.Distinct

distinct的用法和SQL一樣,放在需要修飾的屬性或者*前即可。例如:

 

select distinct * from User.win:time(3 sec)

 

 

6.查詢指定引擎的處理結果

除了上述所說的一些特點外,select還可以針對某個引擎進行查詢。因為引擎都有自己的URI,所以可以在select句子中增加URI標識來指定查詢哪一個引擎的事件處理情況。例如:

 

// 引擎URI為Processor
select Processor.MyEvent.myProperty from Processor.MyEvent

 

 

From Clause

1.語法介紹

From的語法不難,主要內容是針對事件流的處理。包括事件流過濾,事件流的維持等等。語法如下:

 

from stream_def [as name] [unidirectional] [retain-union | retain-intersection] [, stream_def [as stream_name]] [, ...]

// 事件流
event_stream_name [(filter_criteria)] [contained_selection] [.view_spec] [.view_spec] [...]

unidirectional,retain-union,retain-intersection,contained_selection,view_spec這幾個關鍵字因為涉及到view的知識,所以這裡沒法講解。待學完view之後再來回顧這幾個引數會很容易理解的。下面講講怎麼過濾事件流

 

2.事件流過濾

2.1.事件屬性過濾

事件流過濾通常情況都是對其中某個或多個屬性加以限制來達到過濾的目的。注意,過濾表示式是緊跟在事件流名稱之後而不是別名之後。例如:

 

// 只有age大於10的User物件才可查詢到name值
select name from User(age>10) as user
// 當name=“luonanqin”時,可獲得其age值
select age from User(name="luonanqin")

// 錯誤寫法
select name from User as user(age>10)

過濾表示式寫法多種多樣,可以用符號,又或者使用and,or,between等邏輯語言。例如:

 

 

// 查詢年齡大於15小於18的學生的姓名
select name from Student(age between 15 and 18)
// 等同於
select name from Student(age >= 15 and age <= 18)
// 等同於
select name from Student(age >= 15, age <= 18)

看以看到,過濾表示式寫法很多,並且多個表示式同時作用於一個事件流,用逗號連線即可。如果說滿足其中一個條件即可,則需要用or連線。

 

2.2.過濾範圍

剛才說到過濾表示式使用的符號很多,總結下來基本上有<, >, <=, >=, =, !=, between, in, not in, and, or, [ ], ( )。這裡主要說下between,in,( ),[ ]

between……and……

和SQL的between……and……意思一樣,是一個閉區間。比如說between 10 and 15,中文語義為10到15之間幷包含10和15.


( )

表示一個開區間,語法為(low:high)。如(10:15),表示10到15之間,並且不包含10和15


[ ]

表示一個閉區間,語法為[low:high]。如[10:15],表示10到15之間,並且包含10和15

( )和[ ]可以混合用。比如[10:15)或者(10:15]


in

配合( )和[ ]進行使用,表示值在某個範圍內。比如:

 

select name from User(age in [10:15))

相應的,not in表示不在此範圍內。

 

以上都是針對數字的例子,in和not in同樣可以作用於字串。比如:

 

select age from User(name in ('張三', '李四'))

 


2.3 靜態方法過濾

除了上面說的這些符號以外,類似於select子句中使用的靜態方法,過濾表示式中也可以使用,但是返回值必須為布林值,不然會報錯。例如:

 

// 判斷總數是否等於0
public class IsZero
{
	public static boolean isZero(int sum)
	{
		return sum==0;
	}
}
// 載入
epService.getEPAdministrator().getConfiguration().addImport(IsZero.class);
// 查詢沒有錢的使用者的name值(User包含name和money屬性)
select name from User(IsZero.isZero(money))

 

 

事件流的過濾並不能弄得很複雜,他有一下幾個限制:

1. 要過濾的屬性只能是數字和字串。

2. 過濾表示式中不能使用聚合函式。

3. “prev”和“prior”函式不能用於過濾表示式(暫且不考慮這是什麼)


       Select和From的基礎內容基本上就是上面所說的。當學過後面的章節之後,select和from可以寫得很複雜,才能支援更為複雜的業務需求。特別是學過view和一些event function之後,變化就更加多樣了。下一篇將講解別的Clause,敬請期待。

 

相關文章