目录

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. canvas系列教程03-柱状图项目1

    弄了一堆线方块和函数,感觉挺玄乎,然并卵.我们直接写个项目看看. canvas一个比较拽的应用就是图表和游戏,我们从浅入深,先玩图表,图表我们今天说一个最简单柱状图. 柱状图很多人用百度的echars ...

  2. jboss7学习3-jboss安装 访问(外网)添加用户

    一.下载安装 1.下载地址: http://www.jboss.org/jbossas/downloads ,下载Certified Java EE 6 Full Profile版本. 2.解压 jb ...

  3. in a frame because it set 'X-Frame-Options' to 'sameorigin'

    不是所有网站都给  iframe嵌套的,  有的网站设置了  禁止嵌套!!! 浏览器会依据X-Frame-Options的值来控制iframe框架的页面是否允许加载显示出来, 看你们公司这么设置了!! ...

  4. ES7中前端异步特性:async、await。

    在最新的ES7(ES2017)中提出的前端异步特性:async.await. 什么是async.await? async顾名思义是"异步"的意思,async用于声明一个函数是异步的 ...

  5. 接口和抽象类的区别(不讲废话,干货满满,JDK1.8最新整理)

    接口和抽象类的区别(不讲废话,干货满满,JDK1.8最新整理) 1.抽象类 以下说辞可能不太准确,但是会让你醍醐灌顶 抽象类是把一些具有共同属性(包括行为)的东西抽象出来,比如: 小狗有身高,体重,颜 ...

  6. 攻防世界——stegano

    分析 1. 一个pdf,里边都是英文. 打开pdf "ctrl + F",检查flag 然活这里边直接告诉你,flag不在这里,一般都这么说了那就是真的不在了. 2. txt打开, ...

  7. 在容器使用stress指令进行负载压测

    安装stressstress是一个linux下的压力测试工具,专门为那些想要测试自己的系统,完全高负荷和监督这些设备运行的用户 在容器中安装docker容器压测工具 stress #先安装一些基础工具 ...

  8. 1.Markdown语法

    Markdown学习 一.标题:(# +标题名字) 标题 三级标题 四级标题 二.字体 (空格内容前后的空格删掉) Hello,World! **粗体** Hello,World! *斜体* Hell ...

  9. Java学习day8

    今天学习了package,import,final,static和多态 package可以理解为文件夹,因为有些类可能重名,如果在同一个目录下就无法正常实现,所有需要有不同的包来装对应的类 Java出 ...

  10. 计算属性、侦听属性、局部与全局组件使用、组件通信(父子互传)、ref属性、动态组件和keep-alive、插槽

    今日内容概要 计算属性 侦听属性 局部组件和全局组件 组件通信之父传子 组件通信之子传父 ref属性(组件间通信) 动态组件和keep-alive 插槽 内容详细 1.计算属性 # 插值的普通函数,只 ...