此篇文章主要记录一下 drools 中的模式(patterns)和约束(constraints)以及when中条件的写法。

rule "工作内存中只要有Person对象就执行,存在多个执行多次" when Person() then System.out.println("工作内存中存在Person对象");endrule "只要工作内存中有对象,都会匹配到" when Object() then System.out.println("只要工作内存中有对象,都会匹配到");endrule "匹配年龄小于20岁的" when Person(age < 20) // 等驾与getAge() < 20,推荐使用属性的写法 then System.out.println("匹配年龄小于20岁的");end1、匹配的条件结果需要是 true或者false。
2、Person(age < 20) 和 Person(getAge() < 20) 是等价的,但是推荐第一种写法。
3、Person(age < 20)默认会调用getAge()方法,如果该方法不存在则会调用age()方法,如果还不存在,则抛出异常。
4、Drools engine 会缓存调用期间的匹配结果以提高效率,因此我们的getter方法,不要有状态。
rule "嵌套属性的访问" when Person(car.name == "宝马") then System.out.println("嵌套属性的访问");endrule "嵌套属性的访问-02" when Person( age < 20 && car.name == "宝马" && car.color == null) then System.out.println("嵌套属性的访问-02");end.( <constraints> ) 将这些属性访问器分组到嵌套对象,以获得更易读的规则
rule "嵌套属性的访问-03" when Person(age < 20 , car.(name == "宝马" || color != null)) // 属性分组访问 then System.out.println("嵌套属性的访问-03");end在嵌套模式中,我们可以使用 <type>#<subtype>语法强制转换为子类型并使父类型的 getter 用于子类型。
rule "嵌套属性的访问-强制类型转换" when Person(age < 20 , car#BMWCar.name == "宝马") // 强制类型转换 then System.out.println("嵌套属性的访问-强制类型转换");end注意看上方的car#BMWCar,这个是将car转换成BMWCar类型来使用。
在有状态的kie session中,需要谨慎的使用嵌套属性。因为 Drools engine 的工作内存不知道任何嵌套值,也不会检测它们何时更改。
rule "调用java方法约束" when Person(!isChild()) then System.out.println("调用java方法约束");endisChild()方法不应该修改fact的状态,因为drools引擎为了提高工作效率,会将调用期间的结果进行缓存,如果修改了状态,可能将会导致匹配的结果不准。
rule "多个字段约束" when Person((name != null && age < 20) && car != null) // isChild 方法中需要有状态的更改 then System.out.println("多个字段约束");endPerson(name != null , age < 20 , car != null)Person((name != null && age < 20) && car != null)上面2种写法是一样的。
1、在顶级字段约束中,的性能是要高于&&的。
2、&&优先于||,&&和||两者都优先,。
3、不可在复合表达式中嵌入,,比如:Person((name != null , age < 20) , car != null)这是错误的写法,需要将,换成&&符号。
在drools中默认的日期格式为dd-mmm-yyyy,此处我们通过设置系统变量drools.dateformat修改成yyyy-MM-dd HH:mm:ss格式。
rule "日期类型的使用" when $p: Person(registerDate < '2022-05-24 12:12:12' ) // 日期格式比较,System.setProperty("drools.dateformat","yyyy-MM-dd HH:mm:ss"); then System.err.println("日期类型的使用 注册时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format($p.getRegisterDate()) );endrule "使用绑定变量" when $p: Person($age: age) then System.err.println("使用绑定变量 " + $p.getName() + ": " + $age);end后期我们可以使用$p和$age,$p表示当前规则运行时,工作内存中匹配到的Person对象,$age表示匹配到这个对象的age属性,一般绑定变量以$开头,和fact的属性区分开。
rule "使用绑定变量-不好的写法" when Person($age: age * 2 < 100) then System.err.println("使用绑定变量-不好的写法 " + ": " + $age);end这样写不清晰,而且执行效率不高。
rule "使用绑定变量-推荐的写法" when Person( age * 2 < 100, $age: age) // 这样写更清晰,运行效率更高 then System.err.println("使用绑定变量-推荐的写法 " + ": " + $age);endPerson( $age1: (age * 2)) 和
Person( $age2: age * 2)的结果是不一样的,$age1的结果是$age2的结果的2倍。
使用 .() 运算符将属性访问器分组到嵌套对象
Person(age < 20 , car.(name == "宝马" , color == null ))Person(age < 20 , car.name == "宝马" , car.color == null )以上2种写法是同一个意思
在嵌套模式中,我们可以使用 <type>#<subtype>语法强制转换为子类型并使父类型的 getter 用于子类型。
Person(car#BMWCar.brand == "BMW")car#BMWCar指的是将car转换成BMWCar类型。
Person(car!.name == "宝马") // 如果此时 car 为null,则使用 car!.name 是不会报错的当我们的属性存在嵌套的时候,使用!.可以避免空指针异常。
1、List操作-按照索引访问
Person(hobbyList[0] == "打篮球")2、map操作-按照键操作
Person(map["key1"] == "value1")Person(map["key1"] == "value1")中的这个map是Person的一个字段是map
这些操作符和Java中的用法一致。<, <=, >, >= 这些操作符,如果是用在Date类型的字段,则<表示before,对于String类型的字段,则按照自然顺序排序比较
Person(age ((> 30 && < 40) || (>= 18 && <= 25)) && car != null && registerDate < '2022-12-12 12:12:12')Person(age >= 18 && age <= 25)和Person(age (>= 18 && <= 25))是相等的。
给定的字段是否匹配执行的正则表达式。正则表达式可以是一个给定的字符串,也可以是从变量中动态获取。\\。 Person(name matches hobbyList[2] && car.name not matches "^奥迪") // 正则表达式可以是动态来的contains:包含;not contains:不包含。
1、验证一个Array或Collection是否包含某个指定字段的值(可以是常量也可以是变量)。
2、也可以是String类型的字段是否包含某个值(可以是常量也可以是变量)。
Person( hobbyList contains "打篮球" && hobbyList not contains "打橄榄球" &&。hobbyList not contains name && name contains "张" && name not contains car.name)hobbyList:List类型的字段。name或car.name:String类型的字段。
从上方的例子中可以看到: hobbyList contains "打篮球":"打篮球"是一个常量字符串。hobbyList not contains nam:"name"是一个动态变量,从Person中获取。
为了向后兼容,excludes运算符和not contains的作用一致。
验证某个字段是否是Array或Collection的一员。Array或Collection必须是可变的。
Person("打篮球" memberOf hobbyList && "篮球" not memberOf hobbyList && name not memberOf hobbyList)str[startsWith]。str[endsWith]。str[length]。org.drools.core.base.evaluators.StrEvaluatorDefinitionPerson( name str[startsWith] "张" && name str[endsWith] "三" && name str[length] 2 && "张三" str[startsWith] "张")判断某个值是否在某一组值中
Person( $name: name && name in ($name, "李四") && "打篮球" in ("打篮球", "踢足球") && car.name not in ("打篮球", $name))下表列出了 DRL 运算符从高到低的优先级。
| Operator type | Operators | Notes |
|---|---|---|
| Nested or null-safe property access | ., .(), !. | Not standard Java semantics |
List or Map access | [] | Not standard Java semantics |
| Constraint binding | : | Not standard Java semantics |
| Multiplicative | *, /% | |
| Additive | +, - | |
| Shift | >>, >>>, << | |
| Relational | <, <=, >, >=, instanceof | |
| Equality | == != | Uses equals() and !equals() semantics, not standard Java same and not same semantics |
Non-short-circuiting AND | & | |
Non-short-circuiting exclusive OR | ^ | |
Non-short-circuiting inclusive OR | | | |
Logical AND | && | |
Logical OR | | | |
| Ternary | ? : | |
Comma-separated AND | , | Not standard Java semantics |
drl中支持的规则条件元素比较多,此处讲解部分关键字字的用法。
and可以将条件分组为逻辑组合。and支持中缀和前缀方式。()明确的进行分组。and// 规则 and-01 and-02 and-03 是同一个意思,工作内存中需要同时存在Person和Order对象rule "and-01" when Person() and Order() then System.out.println("and-01");endrule "and-02" when (and Person() Order()) then System.out.println("and-02");endrule "and-03" when Person() Order() then System.out.println("and-03");endor也支持好几种写法,此处列出一种写法。和java中的or用法一致
rule "or-01" when $p: Person() or Order() // 规则内存中只要存在Pereson或Order对象就会执行,如果都存在,那么可能会执行多次。如果只想执行一次,可以看下exists的用法 then System.out.println("or-01");end与工作内存中的Fact进行匹配,只会在第一次匹配时触发,不会触发多次,如果和多个模式一起使用,则需要使用()。
简单理解: 假设我工作内存中一次插入了5个Person对象,如果exists匹配到了,那么只会执行一次,不会执行5次。
rule "exists" when exists (Person() or Order()) // 单个: exists Person() 多个:需要()分割 then System.out.println("exists 工作内存中同时存在多个Person()对象和Order()对象,该规则也只执行一次");end规则内存中不存在这个对象时,触发规则。
比如: not Person() 表示规则内存中没有Person这个Fact对象时触发。
rule "not-02" when not (Person(name == "李四") or Order(orderId == 1000)) then System.out.println("not-02,规则内存中不存在Person#name==李四或Order#orderId=1000 时触发");end使用它来指定模式的数据源。 这使 Drools 引擎能够对不在工作内存中的数据进行推理。 数据源可以是绑定变量的子字段,也可以是方法调用的结果。 用于定义对象源的表达式是任何遵循常规 MVEL 语法的表达式。 因此,from 元素使您能够轻松地使用对象属性导航、执行方法调用以及访问映射和集合元素。
rule "from" when $p: Person($hobbyList: hobbyList) $hobby: String() from $hobbyList then System.out.println("如果$hobby有多个,那么此处可能执行多次"); System.out.println("from: person: " + $p.getName() + " 的 hobby is: " +$hobby);end如果Person的hobbyList是一个比较大的集合,那么推荐将hobbyList这个插入到kie session中,来提高性能。
Using from with lock-on-active rule attribute can result in rules not being executed. You can address this issue in one of the following ways:
from element when you can insert all facts into the working memory of the Drools engine or use nested object references in your constraint expressions.modify() block as the last sentence in your rule condition.lock-on-active rule attribute when you can explicitly manage how rules within the same ruleflow group place activations on one another.包含 from 子句的模式后面不能跟以括号开头的另一个模式。 此限制的原因是 DRL 解析器将 from 表达式读取为“来自 $l (String() or Number())”,它无法将此表达式与函数调用区分开来。 最简单的解决方法是将 from 子句括在括号中,如以下示例所示:
// Do not use `from` in this way:rule R when $l : List() String() from $l (String() or Number()) then // Actionsend// Use `from` in this way instead:rule R when $l : List() (String() from $l) (String() or Number()) then // Actionsend使用它来定义与模式的数据源相对应的入口点或事件流。 此元素通常与 from 条件元素一起使用。 您可以为事件声明一个入口点,以便 Drools 引擎仅使用来自该入口点的数据来评估规则。 您可以通过在 DRL 规则中引用它来隐式声明一个入口点,或者在您的 Java 应用程序中显式声明它。
rule "entry-point" when $o: Order() from entry-point "order-entry-point" // 这个地方的数据是从 order-entry-point 中来的,kieSession.getEntryPoint("order-entry-point"); $p: Person() // 这个地方的数据是通过kieSession.insert 来的 then System.err.println("entry-point" + $p.getName() + ": " + $o.getOrderId());endOrder()从上方的规则文件中可以,这个Order()对象是从order-entry-point这个地方来的。而不是别的地方来的。
// order-entry-point 这个是 drl 文件中定义的EntryPoint entryPoint = kieSession.getEntryPoint("order-entry-point");entryPoint.insert(new Order(2001L, 10000L));https://gitee.com/huan1993/spring-cloud-parent/tree/master/drools/drools-drl-when
1、https://docs.drools.org/7.69.0.Final/drools-docs/html_single/index.html#drl-rules-WHEN-con_drl-rules