drools -規則語法


文章結構

1. 基礎api

2. FACT對象

3. 規則

4. 函數

1. 基礎api

在 Drools 當中,規則的編譯與運行要通過Drools 提供的各種API 來實現,這些API 總體來講可以分為三類:規則編譯、規則收集和規則的執行。完成這些工作的API 主要有KnowledgeBuilder、KnowledgeBase、StatefulKnowledgeSession、StatelessKnowledgeSession、、等,它們起到了對規則文件進行收集、編譯、查錯、插入fact、設置global、執行規則或規則流等作用

1.1 KnowledgeBuilder

KnowledgeBuilder 在業務代碼當中收集已經編寫好的規則, 然后對這些規則文件進行編譯, 最終產生一批編譯好的規則包(KnowledgePackage)給其它的應用程序使用。KnowledgeBuilder 在編譯規則的時候可以通過其提供的hasErrors()方法得到編譯規則過程中發現規則是否有錯誤,如果有的話通過其提供的getErrors()方法將錯誤打印出來,以幫助我們找到規則當中的錯誤信息。

1.2 KnowledgeBase

KnowledgeBase 是 Drools 提供的用來收集應用當中知識(knowledge)定義的知識庫對象,在一個KnowledgeBase 當中可以包含普通的規則(rule)、規則流(rule flow)、函數定義(function)、用戶自定義對象(type model)等。KnowledgeBase 本身不包含任何業務數據對象(fact 對象,后面有相應章節着重介紹fact 對象),業務對象都是插入到由KnowledgeBase產生的兩種類型的session 對象當中(StatefulKnowledgeSession 和StatelessKnowledgeSession,后面會有對應的章節對這兩種類型的對象進行介紹),通過session 對象可以觸發規則執行或開始一個規則流執行。

1.3. StatefulKnowledgeSessions

StatefulKnowledgeSession 對象是一種最常用的與規則引擎進行交互的方式,它可以與規則引擎建立一個持續的交互通道,在推理計算的過程當中可能會多次觸發同一數據集。在用戶的代碼當中,最后使用完StatefulKnowledgeSession 對象之后,一定要調用其dispose()方法以釋放相關內存資源。

1.4. StateLessKnowledgeSession

StatelessKnowledgeSession 的作用與StatefulKnowledgeSession 相仿,它們都是用來接收業務數據、執行規則的。事實上,StatelessKnowledgeSession 對StatefulKnowledgeSession 做了包裝,使得在使用StatelessKnowledgeSession 對象時不需要再調用dispose()方法釋放內存資源了。

在drools 6.x以后這些API 都整合到kie API 中了

2. FACT對象

Fact 是指在Drools 規則應用當中,將一個普通的JavaBean 插入到規則的WorkingMemory當中后的對象。規則可以對Fact 對象進行任意的讀寫操作,當一個JavaBean 插入到WorkingMemory 當中變成Fact 之后,Fact 對象不是對原來的JavaBean 對象進行Clon,而是原來JavaBean 對象的引用。

3. 規則

3.1規則文件

在 Drools 當中,一個標准的規則文件就是一個以“.drl”結尾的文本文件,由於它是一個標准的文本文件,所以可以通過一些記事本工具對其進行打開、查看和編輯。

規則是放在規則文件當中的,一個規則文件可以存放多個規則,除此之外,在規則文件當中還可以存放用戶自定義的函數、數據對象及自定義查詢等相關在規則當中可能會用到的一些對象。常用的有:package package-name、imports、globals、functions、queries、rules

對於一個規則文件而言,首先聲明package 是必須的,除package 之外,其它對象在規則文件中的順序是任意的,也就是說在規則文件當中必須要有一個package 聲明,同時package 聲明必須要放在規則文件的第一行。在Drools 的規則文件當中package 對於規則文件中規則的管理只限於邏輯上的管理,,而不管其在物理上的位置如何,這點是規則與Java 文件的package 的區別。對於同一package 下的用戶自定義函數、自定義的查詢等,不管這些函數與查詢是否在同一個規則文件里面,在規則里面是可以直接使用的,這點和Java 的同一package 里的Java類調用是一樣的。

3.2 規則語言

rule "name"
attributes
    when
        LHS
    Then
        RHS
end
   
   
  
  
          

一個規則通常包括三個部分:屬性部分(attribute)、條件部分(LHS)和結果部分(RHS)。對於一個完整的規則來說,這三個部分都是可選的,也就是說如下 所示的規則是合法的:

rule "name"
when
then
end
   
   
  
  
          

3.3 條件部分

條件部分又被稱之為Left Hand Side,簡稱為LHS,下文當中,如果沒有特別指出,那么所說的LHS 均指規則的條件部分,在一個規則當中when 與then 中間的部分就是LHS 部分。在LHS 當中,可以包含0~n 個條件,如果LHS 部分沒空的話,那么引擎會自動添加一個eval(true)的條件,由於該條件總是返回true,所以LHS 為空的規則總是返回true。LHS 部分是由一個或多個條件組成,條件又稱之為pattern(匹配模式),多個pattern之間用可以使用and 或or 來進行連接,同時還可以使用小括號來確定pattern 的優先級。
一個pattern 的語法如下:

[綁定變量名:]Object([field 約束])
   
   
  
  
          

對於一個pattern 來說“綁定變量名”是可選的,如果在當前規則的LHS 部分的其它的pattern 要用到這個對象,那么可以通過為該對象設定一個綁定變量名來實現對其引用,對於綁定變量的命名,通常的作法是為其添加一個“$”符號作為前綴,這樣可以很好的與Fact的屬性區別開來;綁定變量不僅可以用在對象上,也可以用在對象的屬性上面,命名方法與對象的命名方法相同;“field 約束”是指當前對象里相關字段的條件限制,示例如下:

rule "rule1"
when
$customer:Customer(age>20,gender==’male’)
Order(customer==$customer,price>1000)
then
<action>…
End
   
   
  
  
          

此段規則的含義為:的規則就包含兩個pattern,第一個pattern 有三個約束,分別是:對象類型必須是Cutomer;同時Cutomer 的age 要大於20 且gender 要是male;第二個pattern 也有三個約束,分別是:對象類型必須是Order,同時Order 對應的Cutomer 必須是前面的那個Customer 且當前這個Order 的price 要大於1000。在這兩個pattern 沒有符號連接,在Drools當中在pattern 中沒有連接符號,那么就用and 來作為默認連接,所以在該規則的LHS 部分中兩個pattern 只有都滿足了才會返回true。默認情況下,每行可以用“;”來作為結束符(和Java 的結束一樣),當然行尾也可以不加“;”結尾。

3.3.1約束連接

對於對象內部的多個約束的連接,可以采用“&&”(and)、“||”(or)和“,”(and)來實現,“&&”(and)、“||”(or)和“,”這三個連接符號如果沒有用小括號來顯示的定義優先級的話,那么它們的執行順序是:“&&”(and)、“||”(or)和“,” “&&”優先級最高,表面上看“,”與“&&”具有相同的含義,但是有一點需要注意,“,”與“&&”和“||”不能混合使用,也就是說在有“&&”或“||”出現的LHS 當中,是不可以有“,”連接符出
現的,反之亦然。

3.3.2 比較操作符

在當中共提供了十二種類型的比較操作符,分別是:>、>=、<、<=、= =、!=、contains、not contains、memberof、not memberof、matches、not matches;在這十二種類型的比較操作符當中,前六個是比較常見也是用的比較多的比較操作符,着重對后六種類型的比較操作符進行介紹。

3.3.2.1 contains

比較操作符contains 是用來檢查一個Fact 對象的某個字段(該字段要是一個Collection或是一個Array 類型的對象)是否包含一個指定的對象。

when
$order:Order();
$customer:Customer(age >20, orders contains $order);
then
System.out.println($customer.getName());
End
   
   
  
  
          

contains 只能用於對象的某個Collection/Array 類型的字段與另外一個值進行比較,作為比較的值可以是一個靜態的值,也可以是一個變量(綁定變量或者是一個global 對象)。

3.3.2.2 not contains

not contains 作用與contains 作用相反,not contains 是用來判斷一個Fact 對象的某個字段(Collection/Array 類型)是不是不包含一個指定的對象,和contains 比較符相同,它也只能用在對象的field 當中。

3.3.2.3 memberOf

memberOf 是用來判斷某個Fact 對象的某個字段是否在一個集合(Collection/Array)當中,用法與contains 有些類似,但也有不同,memberOf 的語法如下:Object(fieldName memberOf value[Collection/Array])可以看到memberOf 中集合類型的數據是作為被比較項的,集合類型的數據對象位於memberOf 操作符后面,同時在用memberOf 比較操作符時被比較項一定要是一個變量(綁定變量或者是一個global 對象),而不能是一個靜態值。

3.3.2.4 not memberOf

該操作符與memberOf 作用洽洽相反,是用來判斷Fact 對象當中某個字段值是不是中某個集合(Collection/Array)當中,同時被比較的集合對象只能是一個變量(綁定變量或global對象)。

3.3.2.5 matches

matches 是用來對某個Fact 的字段與標准的Java 正則表達式進行相似匹配,被比較的字符串可以是一個標准的Java 正則表達式,但有一點需要注意,那就是正則表達式字符串當中不用考慮“\”的轉義問題

when
$customer:Customer(name matches "李.*");
then
System.out.println($customer.getName());
end
   
   
  
  
          

該規則是用來查找所有Customer 對象的name 屬性是不是以“李”字開頭,如果滿足這一條件那么就將該Customer 對象的name 屬性打印出來。

3.3.2.6 not matches

與matches 作用相反,是用來將某個Fact 的字段與一個Java 標准正則表達式進行匹配,看是不是能與正則表達式匹配。not matches 使用語法如下:

Object(fieldname not matches “正則表達式”)
   
   
  
  
          

3.4 結果部分

結果部分又被稱之為Right Hand Side,簡稱為RHS,在一個規則當中then 后面部分就是RHS,只有在LHS 的所有條件都滿足時RHS 部分才會執行。

RHS 部分是規則真正要做事情的部分,可以將因條件滿足而要觸發的動作寫在該部分當中,在RHS 當中可以使用LHS 部分當中定義的綁定變量名、設置的全局變量、或者是直接編寫Java 代碼(對於要用到的Java 類,需要在規則文件當中用import 將類導入后方能使用,這點和Java 文件的編寫規則相同)。

在規則當中LHS 就是用來放置條件的,所以在RHS 當中雖然可以直接編寫Java 代碼,但不建議在代碼當中有條件判斷,如果需要條件判斷,那么請重新考慮將其放在LHS 當中,否則就違背了使用規則的初衷。

在 Drools 當中,在RHS 里面,提供了一些對當前Working Memory 實現快速操作的宏函數或宏對象,比如insert/insertLogical、update 和retract 就可以實現對當前Working Memory中的Fact 對象進行新增、刪除或者是修改.

3.4.1 insert

insert(new Object());
   
   
  
  
          

一旦調用insert 宏函數,那么Drools 會重新與所有的規則再重新匹配一次,對於沒有設置no-loop 屬性為true 的規則,如果條件滿足,不管其之前是否執行過都會再執行一次,這個特性不僅存在於insert 宏函數上,后面介紹的update、retract 宏函數同樣具有該特性,所以在某些情況下因考慮不周調用insert、update 或retract容易發生死循環,這點大家需要注意.

when
eval(true);
then
Customer cus=new Customer();
cus.setName("張三");
insert(cus);
end
   
   
  
  
          

3.4.2 update

update 函數意義與其名稱一樣,用來實現對當前Working Memory 當中的Fact 進行更新,update 宏函數的作用與StatefulSession 對象的update 方法的作用基本相同,都是用來告訴當前的Working Memory 該Fact 對象已經發生了變化。

when
$customer:Customer(name=="張三",age<10);
then
$customer.setAge($customer.getAge()+1);
update($customer);
System.out.println("----------"+$customer.getName());
End
   
   
  
  
          

3.4.3 retract

retract 用來將Working Memory 當中某個Fact 對象從Working Memory 當中刪除,下面就通過一個例子來說明retract 宏函數的用法。

retract($customer);
   
   
  
  
          

3.4.4 modify

modify 是一個表達式塊,它可以快速實現對Fact 對象多個屬性進行修改,修改完成后會自動更新到當前的Working Memory 當中。它的基本語法格式如下:

modify(fact-expression){
<修改Fact 屬性的表達式>[,<修改Fact 屬性的表達式>*]
}
   
   
  
  
          

示例

when
$customer:Customer(name=="張三",age==20);
then
System.out.println("modify before customer id:"+$customer.getId()+";age:"+$customer.getAge());
modify($customer){
setId("super man"),
setAge(30)
}
End
   
   
  
  
          

這里有一點需要注意,那就是和insert、update、retract 對Working Memory 的操作一樣,一旦使用了modify 塊對某個Fact 的屬性進行了修改,那么會導致引擎重新檢查所有規則是否匹配條件,而不管其之前是否執行過。

3.5 屬性部分

規則屬性是用來控制規則執行的重要工具,在規則的屬性共有13 個,它們分別是:activation-group、agenda-group、
auto-focus、date-effective、date-expires、dialect、duration、enabled、lock-on-active、no-loop、ruleflow-group、salience、when,這些屬性分別適用於不同的場景,下面我們就來分別介紹這些屬性的含義及用法。

3.5.1 salience

用來設置規則執行的優先級,salience 屬性的值是一個數字,數字越大執行優先級越高,同時它的值可以是一個負數。默認情況下,規則的ssalience 默認值為0,所以如果我們不手動設置規則的salience 屬性,那么它的執行順序是隨機(但是一般都是按照加載順序。)

rule "rule1"
salience 1
when
eval(true)
then
System.out.println("rule1");
End
   
   
  
  
          

3.5.2 no-loop

no-loop 屬性的作用是用來控制已經執行過的規則在條件再次滿足時是否再次執行。默認情況下規則的no-loop屬性的值為false,如果no-loop 屬性值為true,那么就表示該規則只會被引擎檢查一次,

no-loop 是 對於自己規則的操作引起重新匹配,只執行一次。由於其他規則的操作引起的重新匹配會執行一次。所以如果有兩個
規則都是一直滿足條件,且then 中有update 操作(如下例子)。那么將會進入死循環,此時應該使用 lock-on-active 屬性保證只匹配一次。

rule "rule1"
salience 1
no-loop true
when
$customer:Customer(name=="張三")
then
update($customer);
System.out.println("customer name:"+$customer.getName());
End
   
   
  
  
          

3.5.3 date-effective

控制規則只有在到達后才會觸發。只有當系統時間>=date-effective 設置的時間值時,規則才會觸發執行,否則執行將不執行。在沒有設置該屬性的情況下,規則隨時可以觸發,沒有這種限制。

date-effective 可接受的日期格式為“dd-MMM-yyyy”,例如2009 年9 月25 日
如果您的操作系統為中文的,那么應該寫成“25-Sep-2009”;如果是英文操作系統“25-九月-2009”

rule "rule1"
date-effective " 25-九月-2009"
when
eval(true);
then
System.out.println("rule1 is execution!");
End
   
   
  
  
          

3.5.4 date-expires

該屬性的作用與date-effective 屬性恰恰相反, date-expires 的作用是用來設置規則的有效期。如果date-expires 的值大於系統時間,那么規則就執行,否則就不執行。

具體用法與date-effective 屬性相同。

3.5.5 enabled

enabled 屬性比較簡單,它是用來定義一個規則是否可用的。該屬性的值是一個布爾值,默認該屬性的值為true,表示規則是可用的,如果手工為一個規則添加一個enabled 屬性,並且設置其enabled 屬性值為false,那么引擎就不會執行該規則。

3.5.6 dialect

該屬性用來定義規則當中要使用的語言類型,目前Drools 版本當中支持兩種類型的語言:mvel 和java,默認情況下,如果沒有手工設置規則的dialect,那么使用的java 語言。

3.5.7 duration

如果設置了該屬性,那么規則將在該屬性指定的值之后在另外一個線程里觸發。該屬性對應的值為一個長整型,單位是毫秒.

rule "rule1"
duration 3000
when
eval(true)
then
System.out.println("rule thread id:"+Thread.currentThread().getId());
end
   
   
  
  
          

表示該規則將在3000 毫秒之后在另外一個線程里觸發。

5.8 lock-on-active

確認規則只執行一次。 將lock-on-action 屬性的值設置為true,可能避免因某些Fact 對象被修改而使已經執行過的規則再次被激活執行。lock-on-active 是no-loop 的增強版屬性。lock-on-active 屬性默認值為false。

5.9 activation-group

該屬性的作用是將若干個規則划分成一個組,用一個字符串來給這個組命名,這樣在執行的時候,具有相同activation-group 屬性的規則中只要有一個會被執行,其它的規則都將不再執行。

在一組具有相同activation-group 屬性的規則當中,只有一個規則會被執行,其它規則都將不會被執行。當然對於具有相同activation-group 屬性的規則當中究竟哪一個會先執行,則可以用類似salience 之類屬性來實現。

rule "rule1"
activation-group "test"
when
eval(true)
then
System.out.println("rule1 execute");
end

rule "rule 2"
activation-group "test"
when
eval(true)
then
System.out.println("rule2 execute");
End
   
   
  
  
          

rule1 和rule2 這兩個規則因為具體相同名稱的activation-group 屬性,所以它們只有一個會被執行。

3.5.10 agenda-group

Agenda Group 是用來在Agenda 的基礎之上,對現在的規則進行再次分組,具體的分組方法可以采用為規則添加agenda-group 屬性來實現。
agenda-group 屬性的值也是一個字符串,通過這個字符串,可以將規則分為若干個Agenda Group,默認情況下,引擎在調用這些設置了agenda-group 屬性的規則的時候需要顯示的指定某個Agenda Group 得到Focus(焦點),這樣位於該Agenda Group 當中的規則才會觸發執行,否則將不執行。

rule "rule1"
agenda-group "001"
when
eval(true)
then
System.out.println("rule1 execute");
end

rule "rule 2"
agenda-group "002"
when
eval(true)
then
System.out.println("rule2 execute");
End
   
   
  
  
          

java 代碼

//getSession 獲取KieSession 的方法自己寫的。
 KieSession ks = getSession();
 //設置agenda-group 的auto-focus 使其執行           ks.getAgenda().getAgendaGroup("group1").setFocus();

   
   
  
  
          

3.5.11 auto-focus

前面我們也提到auto-focus 屬性,它的作用是用來在已設置了agenda-group 的規則上設置該規則是否可以自動獨取Focus,如果該屬性設置為true,那么在引擎執行時,就不需要顯示的為某個Agenda Group 設置Focus,否則需要。

對於規則的執行的控制,還可以使用Agenda Filter 來實現。在Drools 當中,提供了一個名為org.drools.runtime.rule.AgendaFilter 的Agenda Filter 接口,用戶可以實現該接口,通過規則當中的某些屬性來控制規則要不要執行。org.drools.runtime.rule.AgendaFilter 接口只有一個方法需要實現,方法體如下:

public boolean accept(Activation activation);
   
   
  
  
          

在該方法當中提供了一個Activation 參數,通過該參數我們可以得到當前正在執行的規則對象或其它一些屬性,該方法要返回一個布爾值,該布爾值就決定了要不要執行當前這個規則,返回true 就執行規則,否則就不執行。

在引擎執行規則的時候,我們希望使用規則名來對要執行的規則做一個過濾,此時就可以通過AgendaFilter 來實現,示例代碼既為我們實現的一個AgendaFilter 類源碼。

import org.drools.runtime.rule.Activation;
import org.drools.runtime.rule.AgendaFilter;
public class TestAgendaFilter implements AgendaFilter {
private String startName;
public TestAgendaFilter(String startName){
this.startName=startName;
}
public boolean accept(Activation activation) {
String ruleName=activation.getRule().getName();
if(ruleName.startsWith(this.startName)){
return true;
}else{
return false;
}
}
}
   
   
  
  
          

從實現類中可以看到,我們采用的過濾方法是規則名的前綴,通過Activation 得到當前的Rule 對象,然后得到當前規則的name,再用這個name 與給定的name 前綴進行比較,如果相同就返回true,否則就返回false。

java 中調用

            TestAgendaFilter filter= new TestAgendaFilter("activa");
           int count = ks.fireAllRules(filter);
   
   
  
  
          

3.5.12 ruleflow-group

在使用規則流的時候要用到ruleflow-group 屬性,該屬性的值為一個字符串,作用是用來將規則划分為一個個的組,然后在規則流當中通過使用ruleflow-group 屬性的值,從而使用對應的規則。

4. 函數

函數是定義在規則文件當中一代碼塊,作用是將在規則文件當中若干個規則都會用到的業務操作封裝起來,實現業務代碼的復用,減少規則編寫的工作量。函數的編寫位置可以是規則文件當中package 聲明后的任何地方,Drools 當中函數聲明。

function void/Object functionName(Type arg...) {
/*函數體的業務代碼*/
}
   
   
  
  
          

Drools 當中的函數以function 標記開頭,如果函數體沒有返回值,那么function 后面就是void,如果有返回值這里的void 要換成對應的返回值對象,接下來就是函數的名稱函數名稱的定義可以參考Java 類當中方法的命名原則,對於一個函數可以有若干個輸入參數,所以函數名后面的括號當中可以定義若干個輸入參數。

Drools 為我們提供了一個特殊的import 語句:import function,通過該import語句,可以實現將一個Java 類中靜態方法引入到一個規則文件當中,使得該文件當中的規則可以像使用普通的Drools 函數一樣來使用Java 類中某個靜態方法。

import function test.RuleTools.printInfo;
   
   
  
  
          

通過使用import function 關鍵字,將test.RuleTools 類中靜態方法printInfo 引入到當前規則文件中。

調用方式

RuleTools.printInfo(...
原文地址:https://blog.csdn.net/u012373815/article/details/53872025


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM