一、Drools规则引擎的中级语法
Package
-
package为规则包逻辑路径,当然在之前的文章中有提到过,为了管理方便及很好的阅读性最好和物理路径一致。
package必须要放在规则文件的首行且在package的范围内还可以有import、global、funcation、query、rule、EOF。如果你觉得在每个规则文件中都去编写包路径,那么你可以这么做<kbase name="rules" packages="com.dr.drools" > <ksession name="hellodrools"/> </kbase>
Global 全局变量
-
全局变量是由
global class name组成,这可以使我们能够更好的为规则提供数据,而且global不同于Fact对象被再次激活而影响到数据的变化。package com.dr.drools; dialect "mvel" global java.lang.Integer count rule "Global" when then count = 10; System.out.println(count); end说明:
-
全局变量是一个公共的资源,如果你的规则中修改了全局变量的值,那并不会给其他规则造成影响因为这就像是
ThreadLoacl线程副本一样,你只是修改了你规则中的这份副本值。通常这都是在定义常量和包装类时。 -
当我们定义全局变量为集合类或者
javaBean时,在RHS 部分修改了全局变量的值时是会更改全局变量的数据,如果我们的规则相当多时,出现问题并不是我所想的那样。 -
如果想在代码中设置全局变量
KieServices kieServices = KieServices.Factory.get(); KieContainer kieContainer = kieServices.getKieClasspathContainer(); KieSession kieSession = kieContainer.newKieSession("hellodrools"); kieSession.setGlobal("globalName","value值");
-
Query 查询
- 从结构图中可以看到以
query开始end结束,name为查询名称,括号内表示查询参数,可以是多个并且多个以逗号隔开。因为查询主要是设置查询条件故只在LHS中。
示例:
query "use query"(int $age)
$u: User(age == $age)
end
Function 函数
- 函数和
Java的函数是类似的,只不过函数的开头需要以function开始,同时函数可以是带参,也可以是有返回值的 - 如果你在规则文件外面编写了一个静态方法,你可以使用
import进行导入 - 需要注意的是:在
when中不能单独使用函数,必需使用eval()关键字进行引用
import function com.dr.drools.ImportFunction.outStaticFunction;
rule "Global"
when
eval(test1)
then
end
// 当然也可以放在rule的上面
function void test1(){
System.out.println("自定义函数");
}
Declare 声明
-
如同声明
JavaBean一样,但却比JavaBean更简单,除了声明新的类型以外还可以对Fact对象中的元数据进行声明import java.util.Date declare Person name: String time: Date endrule "Global" when $p: Person(name == "zhangsan") then System.out.println($p); end -
除此之外我们还可以使用
extends来继承在规则文件以外声明的类declare MyImportFuntion extends com.dr.drools.ImportFunction address: String end
最核心的When
When 条件
- LHS部分是我们用来判断和约束哪些规则能够满足条件,故此在大多数开发中都是在编写LHS中约束条件,下面将介绍约束条件的关键字。
-
in和not in-
其用法类似我们在编写
sql语句时所使用的in和not inrule "Global" when $p: Person(name in ("张三","李四","王五")) then System.out.println($p); end
-
-
eval-
条件元素可以使用任何语义代码,只要保证返回的结果是
Boolean值即可,但值得注意的是eval所带来的性能却不是很好rule "Global" when eval(true) then System.out.println("只要保证eval(boolean)"); end
-
-
not-
not 用来判断某个值不存在于
Fact工作内存中rule "Global" when not User() // 判断工作内存中没有User实例 then end
-
-
exists-
相信对数据库熟悉的朋友应该对这个关键字不陌生,是的,这就是用来判断工作内存是否存在某个值
rule "Global" when exists User() // 判断工作内存有没有User实例 then end
-
-
forall-
同
eval一样的条件元素,但是他们的区别在于forall会匹配工作内存中所有满足的条件为true是才会成立。rule "Global" when forall($u: User(name == "张三" && age != null)) then end
-
-
from-
from关键字是具有很强大的功能,他可以收集你指定的任意数据,而且还可以遍历集合中的数据,不仅如此它还要获取到全局变量。值得注意的是: 当你使用了lock-on-active属性时,集合只会遍历一次rule "Global" when $u: UserList($p: Person) $p: Person(name == "小张") from $u then System.out.println("使用from关键字"); end
-
-
collect-
收集对于
collect来说是最好的命名了,它更多的是结合from来使用,比如我们可以收集好数据以后放入一个集合中,除此之外collect关键字还可以嵌套使用。import java.util.ArrayList rule "Global" when ArrayList(size() > 3) from collect (User(age > 18)) // 收集用户年龄大于18的数量大于3 then System.out.println("使用from 和 collect 关键字结合使用"); end
-
-
accumulate-
这也是一个条件元素,我想这是所有条件元素最灵活的一个了。
accumulate可以遍历集合并且还会返回一个结果。当然一些内置函数在这里也是非常好使的,比如:sum、min、max、count、average、collectList()、collectSet()
-
> 有点晚了所以直接从官网贴了这张图,本想自己手动画一下的,等后面在补吧。
-
从图中我们可以看到在括号内可以发现,
accumulate除了条件匹配以外还可以使用fromcollect进行再次收集,而且还可以再次使用accumulate。同时我们也可以看到右侧又有两种处理的方式,方式一我们可以自定义函数,方式二 我们可以进行我们想做的任何动作。 -
方式一:
- 如果你想要自定义函数那么你可以实现
AccumnlateFunction重写里面的方法即可,除此之外你在规则要使用到自定义的函数还需要在import关键字后面添加accumulate关键字来进行标识
- 如果你想要自定义函数那么你可以实现
-
方式二:
-
init😴- 我们可以在这里定义初始化值
-
action☕️- 在这里我们可以进行编写逻辑代码,类似循环体,当then中的操作修改元数据时,action会再次被激活
-
reverse🚻- 这是一个可选的参数,网上也看到许多不同的说话,这个代码块中不会和
action做任何计算,而是当工作内存fact被减一或者删除时才会被触发,一般可以用来递减操作,从而避免引擎再次对action进行操作来提升系统的性能。
- 这是一个可选的参数,网上也看到许多不同的说话,这个代码块中不会和
-
result🛀- 对结果进行返回
rule "accumulate" when $total: Number() from accumulate( User($a: age), init(Integer val = 0;), action( if($a > 18){ val = $a; } ), result(val) ) then end -
then 结果
-
then是对结果进行处理的部分,是对工作内存Fact 对象进行数据处理的具体体现。在结果部分应该尽量不要有逻辑判断,RHS这部分就要对工作内存进行插入、修改、删除。Update()更新工作内存中的数据,并且重新激活规则进行条件匹配。Insert()新增Fact 对象到工作内存中。Delete()删除工作内存中的Fact 对象
-
当然修改工作内存中的数据还有另外的一种写法,
modify
rule "modify"
when
$u: User(username == "张三")
then
modify($u){
setAge(28),
setUsername("李四")
}
end
rule "update"
when
$u: User(username == "张三")
then
$u.setAge(38);
$u.setUsername("李四");
update($u)
end
halt
- 在那条规则中设置了该函数即运行到该规则后就不在执行了
rule "halt"
when
eval(true)
then
System.out.println("只运行到该规则就结束");
drools.halt();
end
- 除此以外还有
drools内置的一些函数
rule "halt"
when
eval(true)
then
System.out.println("只运行到该规则就结束");
drools.halt();
drools.getWorkingMemory(); // 返回工作内存对象
drools.getRule(); // 返回规则名称
drools.setFocus("议程组"); // 将焦点放入议程组中
drools.getTuple(); // 返回当前规则匹配的数组
end
二、Kmodule 配置文件
KieBase属性:
<?xml version="1.0" encoding="UTF-8" ?>
<kmodule xmlns="http://www.drools.org/xsd/kmodule">
<kbase name="rules" packages="com.dr.drools" >
<ksession name="hellodrools"/>
</kbase>
</kmodule>
- name 必填,类型为字符串,注册到KieContainer 容器中。
- package 可选,类型字符串,规则包路径,如同Mybatis 的包扫描一样。
- includes 可选,类型字符串,包含另一个或多个规则,功能类似继承。
- default 可选,类型布尔值,Kmodule 文件中是可以存在多个kieBase ,当存在多个时你可以指定某一个为默认的KieBase
- equalsBehavior 可选,将新的Fact 对象添加到工作内存中。
- eventProcessingMode 可选,模式:Cloud / Stream ,当以云模式编译时KieBase 将事件视为正常事实,而在流模式下可以对其进行事件推理,一般在Workbench 中使用。
- declaractiveAgenda 可选,Disabled / enabled 议程是否启用
Ksession属性:
- name 必填,类型字符串,用来指定会话名称且唯一
- type 可选,stateful / stateless 设置状态为有状态或无状态,默认无状态
- default 可选,布尔值,是否默认
- clockType 可选,realtime / defeasible 定义该会话使用的belief System 类型
下篇将从整合Spring开始