目录

1、介绍

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

2、语法结构

3、模式例子

3.1 单个对象匹配

rule "工作内存中只要有Person对象就执行,存在多个执行多次"
when Person()
then
System.out.println("工作内存中存在Person对象");
end

3.2 匹配任何对象

rule "只要工作内存中有对象,都会匹配到"
when Object()
then
System.out.println("只要工作内存中有对象,都会匹配到");
end

3.3 带条件匹配

rule "匹配年龄小于20岁的"
when
Person(age < 20) // 等驾与getAge() < 20,推荐使用属性的写法
then
System.out.println("匹配年龄小于20岁的");
end

3.3.1 注意事项

1、匹配的条件结果需要是 true或者false

2、Person(age < 20)Person(getAge() < 20) 是等价的,但是推荐第一种写法

3、Person(age < 20)默认会调用getAge()方法,如果该方法不存在则会调用age()方法,如果还不存在,则抛出异常。

4、Drools engine 会缓存调用期间的匹配结果以提高效率,因此我们的getter方法,不要有状态。

3.4 嵌套属性的匹配

3.4.1 访问单个嵌套属性

rule "嵌套属性的访问"
when
Person(car.name == "宝马")
then
System.out.println("嵌套属性的访问");
end

3.4.2 访问多个嵌套属性

rule "嵌套属性的访问-02"
when
Person( age < 20 && car.name == "宝马" && car.color == null)
then
System.out.println("嵌套属性的访问-02");
end

3.4.3 属性分组

.( <constraints> ) 将这些属性访问器分组到嵌套对象,以获得更易读的规则

rule "嵌套属性的访问-03"
when
Person(age < 20 , car.(name == "宝马" || color != null)) // 属性分组访问
then
System.out.println("嵌套属性的访问-03");
end

3.4.4 强制类型转换

在嵌套模式中,我们可以使用 <type>#<subtype>语法强制转换为子类型并使父类型的 getter 用于子类型。

rule "嵌套属性的访问-强制类型转换"
when
Person(age < 20 , car#BMWCar.name == "宝马") // 强制类型转换
then
System.out.println("嵌套属性的访问-强制类型转换");
end

注意看上方的car#BMWCar,这个是将car转换成BMWCar类型来使用。

3.4.5 注意事项

在有状态的kie session中,需要谨慎的使用嵌套属性。因为 Drools engine 的工作内存不知道任何嵌套值,也不会检测它们何时更改。

3.5 调用java方法约束

rule "调用java方法约束"
when
Person(!isChild())
then
System.out.println("调用java方法约束");
end

3.5.1 注意实现

isChild()方法不应该修改fact的状态,因为drools引擎为了提高工作效率,会将调用期间的结果进行缓存,如果修改了状态,可能将会导致匹配的结果不准。

3.6 多个字段约束

rule "多个字段约束"
when
Person((name != null && age < 20) && car != null) // isChild 方法中需要有状态的更改
then
System.out.println("多个字段约束");
end

3.7 顶级字段约束

Person(name != null , age < 20 , car != null)
Person((name != null && age < 20) && car != null)

上面2种写法是一样的。

3.7.1 注意事项

1、在顶级字段约束中,的性能是要高于&&的。

2、&&优先于||&&||两者都优先,

3、不可在复合表达式中嵌入,,比如:Person((name != null , age < 20) , car != null)这是错误的写法,需要将,换成&&符号。

3.8 日期类型的使用

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()) );
end

4、在模式和约束中使用绑定变量

rule "使用绑定变量"
when
$p: Person($age: age)
then
System.err.println("使用绑定变量 " + $p.getName() + ": " + $age);
end

后期我们可以使用$p$age$p表示当前规则运行时,工作内存中匹配到的Person对象,$age表示匹配到这个对象的age属性,一般绑定变量以$开头,和fact的属性区分开。

4.1 字段约束中绑定变量不好的写法

rule "使用绑定变量-不好的写法"
when
Person($age: age * 2 < 100)
then
System.err.println("使用绑定变量-不好的写法 " + ": " + $age);
end

这样写不清晰,而且执行效率不高。

4.2 字段约束中绑定变量好的写法

rule "使用绑定变量-推荐的写法"
when
Person( age * 2 < 100, $age: age) // 这样写更清晰,运行效率更高
then
System.err.println("使用绑定变量-推荐的写法 " + ": " + $age);
end

4.3 约束绑定只考虑后面的第一个原子表达式

Person( $age1: (age * 2))

Person( $age2: age * 2)

的结果是不一样的,$age1的结果是$age2的结果的2倍。

5、支持的操作符

5.1 .() 分组属性

使用 .() 运算符将属性访问器分组到嵌套对象

Person(age < 20 , car.(name == "宝马" , color == null ))
Person(age < 20 , car.name == "宝马" , car.color == null )

以上2种写法是同一个意思

5.2 # 类型转换

在嵌套模式中,我们可以使用 <type>#<subtype>语法强制转换为子类型并使父类型的 getter 用于子类型。

Person(car#BMWCar.brand == "BMW")

car#BMWCar指的是将car转换成BMWCar类型。

5.3 !. 嵌套属性null安全

Person(car!.name == "宝马") // 如果此时 car 为null,则使用 car!.name 是不会报错的

当我们的属性存在嵌套的时候,使用!.可以避免空指针异常。

5.4 [] 操作List或Map

1、List操作-按照索引访问

Person(hobbyList[0] == "打篮球")

2、map操作-按照键操作

Person(map["key1"] == "value1")

Person(map["key1"] == "value1")中的这个mapPerson的一个字段是map

5.5 <, <=, >, >=,==, !=,&&,||

这些操作符和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))是相等的。

5.6 matches, not matches 正则匹配

  1. 用来判断给定的字段是否匹配执行的正则表达式。
  2. 正则表达式可以是一个给定的字符串,也可以是从变量中动态获取。
  3. 转义需要使用\\
 Person(name matches hobbyList[2] && car.name not matches "^奥迪") // 正则表达式可以是动态来的

5.7 contains, not contains集合或字符串是否包含什么

contains:包含;not contains:不包含。

1、验证一个ArrayCollection是否包含某个指定字段的值(可以是常量也可以是变量)。

2、也可以是String类型的字段是否包含某个值(可以是常量也可以是变量)。

Person(
hobbyList contains "打篮球" && hobbyList not contains "打橄榄球"
&&。hobbyList not contains name &&
name contains "张" && name not contains car.name
)

hobbyList:List类型的字段。

namecar.name:String类型的字段。

从上方的例子中可以看到:

hobbyList contains "打篮球":"打篮球"是一个常量字符串。

hobbyList not contains nam:"name"是一个动态变量,从Person中获取。

为了向后兼容,excludes运算符和not contains的作用一致。

5.8 memberOf, not memberOf字段是否是某个集合的一员

验证某个字段是否是ArrayCollection的一员。ArrayCollection必须是可变的。

Person("打篮球" memberOf hobbyList && "篮球" not memberOf hobbyList
&& name not memberOf hobbyList)

5.9 str验证字段是否以什么开头或结尾

  1. 验证指定的字符串是以什么开头str[startsWith]
  2. 验证指定的字符串是以什么结尾str[endsWith]
  3. 验证指定字符串的长度str[length]
  4. 查看这个类org.drools.core.base.evaluators.StrEvaluatorDefinition
Person(
name str[startsWith] "张" && name str[endsWith] "三" &&
name str[length] 2 && "张三" str[startsWith] "张"
)

5.10 in not in

判断某个值是否在某一组值中

Person(
$name: name &&
name in ($name, "李四") &&
"打篮球" in ("打篮球", "踢足球") &&
car.name not in ("打篮球", $name)
)

6、运算符的优先级

下表列出了 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

7、DRL支持的规则条件元素(关键字)

drl中支持的规则条件元素比较多,此处讲解部分关键字字的用法。

7.1 and

  1. 使用and可以将条件分组为逻辑组合。
  2. and支持中缀和前缀方式。
  3. 可以使用()明确的进行分组。
  4. 默认情况下是and
// 规则 and-01 and-02 and-03 是同一个意思,工作内存中需要同时存在Person和Order对象
rule "and-01"
when
Person() and Order()
then
System.out.println("and-01");
end rule "and-02"
when
(and Person() Order())
then
System.out.println("and-02");
end rule "and-03"
when
Person()
Order()
then
System.out.println("and-03");
end

7.2 or

or也支持好几种写法,此处列出一种写法。和java中的or用法一致

rule "or-01"
when
$p: Person() or Order() // 规则内存中只要存在Pereson或Order对象就会执行,如果都存在,那么可能会执行多次。如果只想执行一次,可以看下exists的用法
then
System.out.println("or-01");
end

7.3 exists

与工作内存中的Fact进行匹配,只会在第一次匹配时触发,不会触发多次,如果和多个模式一起使用,则需要使用()

简单理解: 假设我工作内存中一次插入了5个Person对象,如果exists匹配到了,那么只会执行一次,不会执行5次。

rule "exists"
when
exists (Person() or Order()) // 单个: exists Person() 多个:需要()分割
then
System.out.println("exists 工作内存中同时存在多个Person()对象和Order()对象,该规则也只执行一次");
end

7.4 not

规则内存中不存在这个对象时,触发规则。

比如: 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

7.5 from

使用它来指定模式的数据源。 这使 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

如果PersonhobbyList是一个比较大的集合,那么推荐将hobbyList这个插入到kie session中,来提高性能。

和lock-on-active一起使用的解决办法

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:

  1. Avoid using the 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.
  2. Place the variable used in the modify() block as the last sentence in your rule condition.
  3. Avoid using the lock-on-active rule attribute when you can explicitly manage how rules within the same ruleflow group place activations on one another.

form子句后在跟一个模式的解决办法

包含 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
// Actions
end // Use `from` in this way instead:
rule R
when
$l : List()
(String() from $l)
(String() or Number())
then
// Actions
end

7.6 entry-point

使用它来定义与模式的数据源相对应的入口点或事件流。 此元素通常与 from 条件元素一起使用。 您可以为事件声明一个入口点,以便 Drools 引擎仅使用来自该入口点的数据来评估规则。 您可以通过在 DRL 规则中引用它来隐式声明一个入口点,或者在您的 Java 应用程序中显式声明它。

drl文件

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());
end

Order()从上方的规则文件中可以,这个Order()对象是从order-entry-point这个地方来的。而不是别的地方来的。

Java文件

// order-entry-point 这个是 drl 文件中定义的
EntryPoint entryPoint = kieSession.getEntryPoint("order-entry-point");
entryPoint.insert(new Order(2001L, 10000L));

8、完整项目

https://gitee.com/huan1993/spring-cloud-parent/tree/master/drools/drools-drl-when

9、参考地址

1、https://docs.drools.org/7.69.0.Final/drools-docs/html_single/index.html#drl-rules-WHEN-con_drl-rules

drools中的条件 when的更多相关文章

  1. drools中then部分的写法

    目录 1.背景 2.支持的方法 2.1 insert 插入对象到工作内存中 2.1.1 需求 2.1.2 drl文件编写 2.1.3 部分java代码编写 2.1.4 运行结果 2.1.5 结论 2. ...

  2. MongoDB官方C#驱动中查询条件Query用法

    Query.All("name", "a", "b");//通过多个元素来匹配数组 Query.And(Query.EQ("nam ...

  3. JScript中的条件注释详解(转载自网络)

    JScript中的条件注释详解-转载 这篇文章主要介绍了JScript中的条件注释详解,本文讲解了@cc_on.@if.@set.@_win32.@_win16.@_mac等条件注释语句及可用于条件编 ...

  4. 在SQL存储过程中给条件变量加上单引号

    在SQL存储过程中给条件变量加上单引号,不加语句就会出问题,以下就是在存储过程中将条件where设置成了动态变化的,给where赋完值再和前面的语句拼接,再execute(SQL) ), )), )+ ...

  5. HTML在IE中的条件注释

    HTML在IE中的条件注释 HTML的条件注释在IE5中被首次引入,直到IE9.一直都是简单地判定用户浏览器(IE,非IE,IE版本)的一种手段,而在IE10的标准模式下,条件注释功能被停止支持(兼容 ...

  6. sql 语句中使用条件判断case then else end

    sql 语句中使用条件判断case then else end范例: SELECT les.[nLessonNo] FROM BS_Lesson AS les WHERE les.[sClassCod ...

  7. js中的条件语句

    //js中的条件语句 ; //example1 单分支语句 ){ console.log("你已经不年轻了!"); }else{ console.log("你依然很有活力 ...

  8. 在VS.NET中根据条件设置不同的MainForm

    在VS.NET中有时候需要根据不同的条件设置不同的MainForm,例如:在程序第一次运行时候打开设置基本信息或服务器信息窗口等. 1.找到VS.NET中设置MainForm的窗口: 2.在编辑窗口中 ...

  9. SQL中on条件与where条件的区别(转载)

    数据库在通过连接两张或多张表来返回记录时,都会生成一张中间的临时表,然后再将这张临时表返回给用户. 在使用left jion时,on和where条件的区别如下: 1. on条件是在生成临时表时使用的条 ...

随机推荐

  1. 微信小程序调研

    小程序入口 微信发现,小程序 公众号主体查看小程序 好友分享,群分享 公众号自定义菜单跳转 APP页面跳转 第三方服务 附近的小程序 扫普通链接二维码打开小程序 需要后台开启功能,开启后,用户在微信& ...

  2. 初识JavaScript EventLoop

    Event Loop指的是计算机系统的一种运行机制.JavaScript采用此机制解决单线程引发相关问题 在浏览器中的web应用会涉及到.JavaScript引擎.WebAPI.Event Loop. ...

  3. Python窗口学习之搜索框美化

    初学tkinter,感觉这个插件虽然是做界面的,但是没有html,也没有android那么人性化 既没有画圆角长方形的办法也没有添加透明按钮的办法(可能是我没找到) 所以自己用canvas画了两个扇形 ...

  4. 将百度地图Demo抽取出来安到自己的程序中

    今日所学: 使用百度地图ADK实现手机定位 [Android]使用百度.高德.腾讯地图SDK获取定位数据与屏幕截图分享到QQ_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili 代码获取SHA1 (2 ...

  5. Configuration类的理解

    Configuration类主要用来读取配置文件,启动Hibernate,并负责Hibernate的配置信息.一个应用程序只创建一个Configuration. 在Hibernate启动过程中,Con ...

  6. jupyter notebook 调用.py文件

    方法1.利用 %run xx.py 直接运行得出结果. 方法2:利用 %load xx.py 载入代码再点击Run运行,这种方法的好处是可以方便修改代码. 说明: Jupyter Notebook中以 ...

  7. Docker操作容器2

    Docker操作容器1:https://blog.csdn.net/Kevinnsm/article/details/ 1.如何更改docker容器中的配置文件(如nginx容器中的nginx.con ...

  8. LC-76

    给你一个字符串 s .一个字符串 t .返回 s 中涵盖 t 所有字符的最小子串.如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" . 注意: 对于 t 中重复字符 ...

  9. MongoDB 集群-主从复制(一主二从)

    MongoDB 集群-主从复制(一主二从) 官方文档 https://docs.mongodb.com/manual/tutorial/deploy-replica-set/ https://docs ...

  10. iOS全埋点解决方案-UITableView和UICollectionView点击事件

    前言 在 $AppClick 事件采集中,还有两个比较特殊的控件: UITableView •UICollectionView 这两个控件的点击事件,一般指的是点击 UITableViewCell 和 ...