随笔 - 19, 文章 - 93, 评论 - 17, 引用 - 0
数据加载中……

(七): Drools规则语言详解(下)

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  )
<!--[if !supportEmptyParas]--> <!--[endif]-->

这个例子同前一个例子有点类似。但是在这个例子中,我们将一个变量绑定到匹配规则引擎的 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 数字基本类型都可以用。

有效操作符:

<!--[if !supportLists]--> ·         <!--[endif]--> ==

<!--[if !supportLists]--> ·         <!--[endif]--> !=

<!--[if !supportLists]--> ·         <!--[endif]--> >

<!--[if !supportLists]--> ·         <!--[endif]--> <

<!--[if !supportLists]--> ·         <!--[endif]--> >=

<!--[if !supportLists]--> ·         <!--[endif]--> <=

Example 5.3 . Numeric Literal Constraint
Cheese( quantity  ==   5  )

Date

当前只对“ dd-mm-yyyy ”的日期格式提供默认支持。你可以通过指定 drools.dateformat 系统属性,来改变默认的日期格式。如果需要更多的控制,要用谓词约束( Predicate Constraint )。

有效操作符:

<!--[if !supportLists]--> ·         <!--[endif]--> ==

<!--[if !supportLists]--> ·         <!--[endif]--> !=

<!--[if !supportLists]--> ·         <!--[endif]--> >

<!--[if !supportLists]--> ·         <!--[endif]--> <

<!--[if !supportLists]--> ·         <!--[endif]--> >=

<!--[if !supportLists]--> ·         <!--[endif]--> <=

Example 5.4. Date Literal Constraint
Cheese( bestBefore  <   " 27-Oct-2007 "  )

String

可以使用任何有效的 Java String

有效操作符:

<!--[if !supportLists]--> ·         <!--[endif]--> ==

<!--[if !supportLists]--> ·         <!--[endif]--> !=

Example 5.5. String Literal Constraint
Cheese( type  ==   " stilton "  )

Boolean

只能用 true ”或“ false ”。 0 1 不能被识别,而且 Cheese(smelly) 也是不被允许的。

有效操作符:

<!--[if !supportLists]--> ·         <!--[endif]--> ==

<!--[if !supportLists]--> ·         <!--[endif]--> !=

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 Declarationindex position

Example 6.2. Query People over the age of 30

QueryResults 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" );
}

posted on 2006-12-03 09:56 BPM 阅读(1497) 评论(0)  编辑  收藏 所属分类: 规则引擎


只有注册用户登录后才能发表评论。


网站导航: