Drools規則語言詳解(下)
5. Rule
Figure?5.1.?rule
Rule
結構是最重要的結構。
Rule
使用了形如“
IF
”
something
“
THEN
”
action
(當然,我們的關鍵字是“
when
”和“
then
”)的形式。
一個規則在一個
package
中必須要有唯一的名字。如果一個名字中含有空格,那就需要將名字放在雙引號中(最好總是使用雙引號)。
Attribute
是可選的(最好是每行只有一個
Attribute
)。
規則的
LHS
跟在“
when
”關鍵字的后面(最好是另起一行),同樣
RHS
要跟在“
then
”關鍵字后面(最好也另起一行)。規則以關鍵字“
end
”結束。規則不能嵌套。
5.1 Left Hand Side
Left Hand Side
其實就是規則的條件部分。
LHS
對應的
rail-road
圖如下,我們在后面會做進一步解釋:
Figure?5.2.?Left Hand Side

Figure?5.3.?pattern
5.2 Right Hand Side
Right Hand Side
(
RHS
)就是規則的結果(
consequence
)或者動作(
action
)部分。
RHS
的目的是
retract
或
add facts
到
WorkingMemory
中,還有針對你的
application
的動作。實際上,
RHS
是當規則激發(
fire
)時執行的代碼塊。
在
RHS
中,你可以使用幾個方便的
method
來改變
WorkingMemory
:
“
modify(obj)
”:告訴引擎一個對象已經發生變化,規則必須重新匹配(
obj
對象必須是出現在
LHS
中的對象);
“
assert(new
Something())
”:將一個新的
Something
對象加入
WorkingMemory
;
“
assertLogical(new
Something())
”:與
assert
方法類似。但是,當沒有
fact
支持當前激發規則的真實性的時候,這個新對象會自動被
retract
,
“
retract(obj)
”:從
WorkingMemory
中移除一個對象。
這些方法都是宏指令,提供了到
KnowledgeHelper
實例的快捷方式(參考
KnowledgeHelper
接口)。
KnowledgeHelper
接口可以在
RHS
代碼塊中調用,通過變量“
drools
”。如果你在
assert
進引擎的
JavaBean
中加入“
Property
Change Listener
”,在對象發生變化的時候,你就不用調用“
modify
”方法。
5.3 Rule Attributes
Figure?5.4.?rule attributes
5.3.1 no-loop
默認值:
false
類型:
boolean
當在
rule
的
RHS
中修改了一個
fact
,這可能引起這個
rule
再次被
activate
,引起遞歸。將
no-loop
設為
true
,就可以防止這個
rule
的
Activation
的再次被創建。
5.3.2 salience
默認值:
0
類型:
int
每個
rule
都可以設置一個
salience
整數值,默認為
0
,可以設為正整數或負整數。
Salience
是優先級的一種形式。當處于
Activation
隊列中時,擁有高
salience
值的
rule
將具有更高的優先級。
5.3.3 agenda-group
默認值:
MAIN
類型:
String
Agenda group
允許用戶對
Agenda
進行分組,以提供更多的執行控制。只有具有焦點的組中的
Activation
才會被激發(
fire
)。
5.3.4 auto-focus
默認值:
false
類型:
boolean
當一個規則被
activate
(即
Activation
被創建了),如果這個
rule
的
auto-focus
值為
true
并且這個
rule
的
agenda-group
沒有焦點,此時這個
Activation
會被給予焦點,允許這個
Activation
有
fire
的潛在可能。
5.3.5 activation-group
默認值:
N/A
類型:
String
當處于同一個
activation-group
中的第一個
Activation fire
后,這個
activation-group
中其余剩下的
Activation
都不會被
fire
。
5.3.6 duration
默認值:沒有默認值
類型:
long
5.4 Column
Figure?5.5.?Column
Example?5.1.?Column
Cheese(?)
Cheese(?type?
==
?
"
stilton
"
,?price?
<
?
10
?)
一個
Column
由一個類的一個或多個域約束構成。第一個例子沒有約束,它將匹配
WorkingMemory
中所有的
Cheese
實例。第二個例子對于一個
Cheese
對象有兩個字面約束(
Literal Constraints
),它們被用“,”號隔開,意味著“
and
”。
Figure?5.6.?Bound Column
Example?5.2.?Bound Column
cheapStilton?:?Cheese(?type?
==
?
"
stilton
"
,?price?
<
?
10
?)
這個例子同前一個例子有點類似。但是在這個例子中,我們將一個變量綁定到匹配規則引擎的
Cheese
實例上。這意味著,你可以在另一個條件中使用
cheapStilton
,或者在
rule
的
RHS
中。
5.4.1 Field Constraints
Field Constraints
使規則引擎可以從
WorkingMemory
中挑選出合適的
Fact
對象。一個
Fact
的“
Field
”必須符合
JavaBean
規范,提供了訪問
field
的
getter
方法。你可以使用
field
的名字直接訪問
field
,或者使用完整的方法名(省略括號)。
例如,以我們的
Chess
類為例,下面是等價的:
Cheese(type
= = …)
和
Cheese(getType = = …)
。這意味著,你可以使用不太嚴格遵守
JavaBean
規范對象。盡管如此,你要保證
accessor
方法是不帶參數的,以保證它不會改變對象的狀態。
注意:如果一個
field
使用原始類型(
primitive type
),
Drools
將會把它們自動裝箱成相應的對象(即使你使用
java 1.4
),但是在
java 1.4
下卻不能自動拆箱。總的來說,盡量在
rule
所使用的類中,使用非原始類型的域。如果是使用
java 5
,就可以比較隨意了,因為編譯器會幫你執行自動裝拆箱。
5.4.1.1 Operators
Figure?5.7.?Operators
有效的操作符是同域類型相關的。例如,對于日期域,“
<
”意味著“之前”。“
matches
”只適用于
String
域,“
contains
”和“
excludes
”只適用于
Collection
類型域。
5.4.1.2
字面值約束(
Literal Constraints
)
最簡單的域約束就是字面值約束,允許用戶將一個
field
約束于一個已知值。
注意:你可以檢查域是否為
null
,使用
= =
或
!=
操作符和字面值‘
null
’關鍵字。如,
Cheese(type
!= null)
。字面值約束,特別是“
= =
”操作符,提供了非常快的執行速度,因為可以使用散列法來提高性能。
Figure?5.8.?Literal Constraints
Numeric
所有標準的
Java
數字基本類型都可以用。
有效操作符:
·????????
==
·????????
!=
·????????
>
·????????
<
·????????
>=
·????????
<=
Example?5.3 .?Numeric Literal Constraint
Cheese(?quantity?
==
?
5
?)
Date
當前只對“
dd-mm-yyyy
”的日期格式提供默認支持。你可以通過指定
drools.dateformat
系統屬性,來改變默認的日期格式。如果需要更多的控制,要用謂詞約束(
Predicate Constraint
)。
有效操作符:
·????????
==
·????????
!=
·????????
>
·????????
<
·????????
>=
·????????
<=
Example?5.4.?Date Literal Constraint
Cheese(?bestBefore?
<
?
"
27-Oct-2007
"
?)
String
可以使用任何有效的
Java String
。
有效操作符:
·????????
==
·????????
!=
Example?5.5.?String Literal Constraint
Cheese(?type?
==
?
"
stilton
"
?)
Boolean
只能用
“
true
”或“
false
”。
0
和
1
不能被識別,而且
Cheese(smelly)
也是不被允許的。
有效操作符:
·????????
==
·????????
!=
Example?5.6?Boolean Literal Constraint
Cheese(?smelly?
=
?
=
?
true
?)
Matches Operator
Matches
操作符后面可以跟任何有效的
Java
正則表達式。
Example?5.7.?Regular Expression Constraint
Cheese(?type?matches?
"
(Buffulo)?\\S*Mozerella
"
?)
Contains Operator and Excludes Operator
“
contains
”和“
excludes
”可以用來檢查一個
Collection
域是否含有一個對象。
Example?5.8.?Literal Cosntraints with
Collections
CheeseCounter(?cheeses?contains?
"
stilton
"
?)
CheeseCounter(?cheeses?excludes?
"
chedder
"
?)
5.4.1.3 Bound Variable Constraint
可以將
Facts
和它們的
Fields
附給一個
Bound Variable
,然后在后續的
Field Constraints
中使用這些
Bound Variable
。一個
Bound Variable
被稱為聲明(
Declaration
)。
Declaration
并不能和“
matches
”操作符合用,但卻可以和“
contains
”操作符合用。
Example?5.9.?Bound Field using '==' operator
Person(?likes?:?favouriteCheese?)
Cheese(?type?
==
?likes?)
在上面的例子中,“
likes
”就是我們的
Bound Variable
,即
Declaration
。它被綁定到了任何正在匹配的
Person
實例的
favouriteCheese
域上,并且用來在下一個
Column
中約束
Cheese
的
type
域。可以使用所有有效的
Java
變量名,包括字符“
$
”。“
$
”經常可以幫助你區分
Declaration
和
field
。下面的例子將一個
Declaration
綁定到匹配的實例上,并且使用了“
contains
”操作符。注意:
Declaratino
的第一個字符用了“
$
”:
Example?5.10?Bound Fact using 'contains'
operator
$stilton?:?Cheese(?type?
==
?
"
stilton
"
?)
Cheesery(?cheeses?contains?$stilton?)
5.4.1.4 Predicate Constraints
Figure?5.9.?Predicate expression
Predicate
表達式可以使用任何有效的
Java
邏輯表達式。先前的
Bound
Declaration
可以用在表達式中。
下面的例子將會找出所有男性比女性大
2
歲的
pairs of
male/femal people
:
Example?5.11. Predicate Constraints
Person(?girlAge?:?age,?sex?
=
?
=
?
"
F
"
?)
Person(?boyAge?:?age?
->
?(?girlAge.intValue()?
+
?
2
?
==
?boyAge.intValue()?),?sex?
=
?
=
?
"
M
"
?)
5.4.1.5 Return Value Constraints
Figure?5.10.?Return Value expression
一個
Retrurn Value
表達式可以使用任何有效的
Java
表達式,只要它返回一個對象,不能返回原始數據類型。如果返回值是原始數據類型,要先進行裝箱。先前的
Bound Declaration
也可以使用在表達式中。
下面的例子跟上一節的例子一樣,也將會找出所有男性比女性大
2
歲的
pairs of
male/femal people
。注意:這里我們不用綁定
boyAge
,增加了可讀性:
Example?5.12.?Return Value Constraints
Person(?girlAge?:?age,?sex?
=
?
=
?
"
F
"
?)
Person(?age?
=
?
=
?(?
new
?Integer(girlAge.intValue()?
+
?
2
)?),?sex?
=
?
=
?
"
M
"
?)
5.5 Conditional Elements
Conditional
Elements
用來連接一個或多個
Columns
。
5.5.1
“
and
”
Figure?5.11.?and
Example?5.13. ?And
Cheese(?cheeseType?:?type?)?
&&
?Person(?favouriteCheese?
==
?cheeseType?)
Cheese(?cheeseType?:?type?)?and?Person(?favouriteCheese?
==
?cheeseType?)
5.5.2
“
or
”
Figure?5.12.?or
Example?5.14.?or
Person(?sex?
==
?
"
f
"
,?age?
>
?
60
?)?
||
?Person(?sex?
==
?
"
m
"
,?age?
>
?
65
?)
Person(?sex?
==
?
"
f
"
,?age?
>
?
60
?)?or?Person(?sex?
==
?
"
m
"
,?age?
>
?
65
?)
Figure?5.13.?or with binding
Example?5.15.?or with binding
pensioner?:?Person(?sex?
==
?
"
f
"
,?age?
>
?
60
?)?
||
?pensioner?:?Person(?sex?
==
?
"
m
"
,?age?
>
?
65
?)?
pensioner?:?(?Person(?sex?
==
?
"
f
"
,?age?
>
?
60
?)?or?Person(?sex?
==
?
"
m
"
,?age?
>
?
65
?)?)
“
or
”
Conditional
Element
的使用會導致多條
rule
的產生,稱為
sub rules
。上面的例子將在內部產生兩條規則。這兩條規則會在
WorkingMemory
中各自獨立的工作,也就是它們都能進行
match
,
activate
和
fire
。當對一個“
or
”
Conditional
Element
使用變量綁定時,要特別小心,錯誤的使用將產生完全不可預期的結果。
可以將“
OR
”
Conditional Element
理解成產生兩條規則的快捷方式。因此可以很容易理解,當“
OR
”
Conditional Element
兩邊都為真時,這樣的一條規則將可能產生多個
activation
。
5.5.3
“
eval
”
Figure?5.14 .?eval
Eval is essentially a catch all which
allows any semantic code (that returns a primitive boolean) to be executed.
在表達式中可以引用在
LHS
中出現的變量,和在
rule package
中的
Functions
。一個
Eval
應該是
LHS
中的最后一個
Conditional Element
。在一個
rule
中,你可以有多個
eval
。
Eval
不能被索引,因此不能像
Field Constraints
那樣被優化。盡管如此,當
Functions
的返回值一直在變化時,應該使用
Eval
,因為這在
Field
Constraints
中時不允許的。如果規則中的其他條件都匹配,一個
eval
每次都要被檢查。(現在還不理解到底
eval
要怎么用?)
Example?5.16.?eval
p1?:?Parameter()?
p2?:?Parameter()
eval(?p1.getList().containsKey(p2.getItem())?)
eval(?isValid(p1,?p2)?)?
//
this?is?how?you?call?a?function?in?the?LHS?-?a?function?called?
//
"isValid"
5.5.4
“
not
”
Figure?5.15.?not
“
not
”是一階邏輯的存在量詞(
first order logic’s
Existential Quantifier
)
,
用來檢查
WorkingMemory
中某對象的非存在性。現在,只有
Columns
可以放在
not
中,但是將來的版本會支持“
and
”和“
or
”。
Example?5.17.?No Buses
not?Bus()
Example?5.18.?No red Buses
not?Bus(color?
==
?
"
red
"
)
not?(?Bus(color?
==
?
"
red
"
,?number?
=
?
=
?
42
)?)?
//
brackets?are?optional
5.5.5
“
exists
”
Figure?5.16.?exists
“
exists
”
是一階邏輯的存在量詞(
first order logic’s Existential Quantifier
),用來檢查
WorkingMemory
中某對象的存在性。可以將“
exists
”理解為“至少有一個”(
at least one…
)。它的意義不同于只有
Column
本身,“
Column
”本身可以理解為“對于每一個
…
”(
for each of …
)。如果你對一個
Column
使用了“
exists
”,那么規則將只
activate
一次,而不管
WorkingMeomry
中有多少個數據匹配了那個條件。
現在,只有
Columns
可以放在“
exists
”中,但是將來的版本會支持“
and
”和“
or
”。
Example?5.19.?At least one Bus
exists?Bus()
Example?5.20.?At least one red Bus
exists?Bus(color?
==
?
"
red
"
)
5.5.6
“
group
”
Figure?5.17.?group
Group
的作用相當于代數學中的“()”,顯式的指明操作的順序。
5.6
再談自動裝箱和原始類型
Java 5
支持在原始類型與其對應包裝類之間的裝拆箱。盡管如此,因為要讓
drools
能夠在
J2SE 1.4
下運行,我們不能依靠
J2SE
。因此,
drools
自己實現了自動裝箱。被引用(即被
Bound Variable
綁定)的
Field
將被自動進行裝箱(如果它們本身就是
object
,就不會有任何變化)。盡管如此,必須要注意的是,他們并不會被自動拆箱。
還有一點要注意的,就是對于
ReturnValue
Constraints
,返回值的代碼必須返回一個對象,而不能是一個原始類型。
6
.
Query
Figure?6.1 .?query
一個
query
只包含了一個
rule
的
LHS
結構(你不用指定“
when
”或“
then
”)。這是查詢
WorkingMemory
中匹配條件的
Facts
的簡單的方法。
要得到結果,要使用
WorkingMemory.getQueryResults(“name”)
方法,其中“
name
”就是
query
的名字。
Query
的名字在
RuleBase
中是全局的,所以,
do not add queries of the
same name to different packages for the same RuleBase
。
下面的例子創建了一個簡單的
query
來查詢所有年齡大于
30
的人:
Example?6.1.?Query People over the age of 30
?
query?"people?over?the?age?of?30"?
????person?:?Person(?age?>?30?)
end
我們通過一個標準的循環來迭代一個返回的QueryResults對象。每一次的iterate將返回一個QueryResult對象。我們可以用QueryResult對象的get()方法來訪問每個Column,通過傳入Bound Declaration或index position。
Example?6.2.?Query People over the age of 30QueryResults?results?=?workingMemory.getQueryResults(?"people?over?the?age?of?30"?);
System.out.println(?"we?have?"?+?results.size()?+?"?people?over?the?age??of?30"?);
System.out.println(?"These?people?are?are?over?30:"?);
for?(?Iterator?it?=?results.iterator;?it.hasNext();?)?{
????QueryResult?result?=?(?QueryResult?)?it.next();
????Person?person?=?(?Person?)?result.get(?"person"?);
????System.out.println(?person.getName()?+?"\n"?);
}