一、Accumulate语法分析
accumulate( <source pattern>; <functions> [;<constraints>] )
例如,计算给定传感器的最低、最高和平均温度读数的规则,如果最低温度低于20C,而平均温度超过70C,则会发出警报,可以用累加写成:
rule "Raise alarm"
when
$s : Sensor()
accumulate( Reading( sensor == $s, $temp : temperature );
$min : min( $temp ),
$max : max( $temp ),
$avg : average( $temp );
$min < 20, $avg > 70 )
then
// raise the alarm
end
在上面的例子中,min、max和average是累加函数,将计算每个传感器所有读数上的最小、最大和平均温度值。
二、Drools附带的内置的累加功能,包括:
-
average
-
min
-
max
-
count
-
sum
-
variance
-
standardDeviation
-
collectList
-
collectSet
三、替代语法:带有返回类型的单个函数
累加语法随着时间的推移而发展,其目标是变得更加紧凑和富有表现力。不过,Drools仍然支持以前的语法以实现向后兼容性。
如果规则在给定的累加上使用单个累加函数,则可以为result对象添加一个模式,并使用“from”关键字将其链接到累加结果。例如:对100美元以上的订单给予10%折扣的规则可以这样写:
rule "Apply 10% discount to orders over US$ 100,00"
when
$order : Order()
$total : Number( doubleValue > 100 )
from accumulate( OrderItem( order == $order, $value : value ),
sum( $value ) )
then
// apply discount to $order
end
在上面的例子中,累加元素只使用一个函数(sum),因此规则作者选择显式地为累加函数(Number)的结果类型编写一个模式,并在其中编写约束。在使用这种语法时,与前面介绍的紧凑语法相比,没有任何问题,只是有点冗长。还请注意,不允许在同一个累加语句中同时使用返回类型和函数绑定。
执行编译时检查是为了确保使用“from”关键字的模式可以从使用的累加函数的结果中分配。
在上面的例子中,“$total”被绑定到collect sum()函数返回的结果。
但是,另一个例子是,如果累加函数的结果是一个集合,那么“from”仍然绑定到单个结果,并且它不会迭代:
rule "Person names"
when
$x : Object() from accumulate(MyPerson( $val : name );
collectList( $val ) )
then
// $x is a List
end
绑定的“$x: Object()”是列表本身,由使用的collectList累加函数返回。
这是一个需要强调的重要区别,因为“from”关键字也可以单独用于累加,以遍历集合的元素:
rule "Iterate the numbers"
when
$xs : List()
$x : Integer() from $xs
then
// $x matches and binds to each Integer in the collection
end
尽管出于向后兼容性的目的,仍然支持这种语法,但是出于这个和其他原因,我们鼓励规则作者使用而不是使用累加CE首选语法,以避免任何潜在的陷阱,如下面的示例所述。
四、使用内联自定义代码进行累积(推荐)
语法:
<result pattern> from accumulate( <source pattern>,
init( <init code> ),
action( <action code> ),
reverse( <reverse code> ),
result( <result expression> ) )
每个要素的含义如下:
<srouce pattern>:源模式是一个常规模式,引擎将尝试匹配每个源对象。
<init code>:这是所选方言中的一个语义代码块,在遍历源对象之前,将对每个元组执行一次。
<action code>:这是所选方言中的一个语义代码块,将为每个源对象执行。
<reverse code>:这是所选方言中可选的语义代码块,如果存在,将为不再匹配源模式的每个源对象执行。这个代码块的目的是撤销在块中所做的任何计算,这样引擎就可以在修改或删除源对象时进行递减计算,极大地提高了这些操作的性能。
<result expression>:这是在所选方言中的一个语义表达式,在遍历所有源对象之后执行。
<result pattern>:这是一个规则模式,引擎试图匹配从返回的对象。如果匹配,则累加条件元素的计算结果为true,然后引擎继续计算规则中的下一个CE。如果不匹配,累加CE的值将为false,引擎将停止为该规则计算CEs。
例子:
rule "Apply 10% discount to orders over US$ 100,00"
when
$order : Order()
$total : Number( doubleValue > 100 )
from accumulate( OrderItem( order == $order, $value : value ),
init( double total = 0; ),
action( total += $value; ),
reverse( total -= $value; ),
result( total ) )
then
// apply discount to $order
end
在上面的示例中,对于工作内存中的每个订单,引擎将执行初始化总变量为零的init代码。然后,它将遍历该订单的所有OrderItem对象,为每个对象执行操作(在本例中,它将把所有项的值加到total变量中)。在遍历所有OrderItem对象之后,它将返回与结果表达式对应的值(在上面的示例中,是变量total的值)。最后,引擎将尝试将结果与数字模式匹配,如果double值大于100,则规则将触发。
本例使用Java作为语义方言,因此,请注意在init、action和reverse代码块中必须使用分号作为语句分隔符。结果是一个表达式,因此,它不承认“;”。如果用户使用任何其他方言,他必须遵守该方言的特定语法。
如前所述,反向代码是可选的,但强烈建议用户编写它,以便从更新和删除性能的改进中获益。
rule "Accumulate using custom objects"
when
$person : Person( $likes : likes )
$cheesery : Cheesery( totalAmount > 100 )
from accumulate( $cheese : Cheese( type == $likes ),
init( Cheesery cheesery = new Cheesery(); ),
action( cheesery.addCheese( $cheese ); ),
reverse( cheesery.removeCheese( $cheese ); ),
result( cheesery ) );
then
// do something
end
五、实战案例
(1)获取10分钟订单数超过100的订单信息
rule "获取10分钟订单数超过100的订单信息"
when
$t1: OrderInfo()
$list: List(size > 99) from accumulate(
$t2: OrderInfo(
this != $t1
this after[ 0s, 10m ] $t1
),
init(List list = new ArrayList();),
action(list.add($t2);)
result(list)
)
then
// do something
end
(2)10分钟出现3次错误
rule "10分钟出现3次错误"
when
$count: Number(intValue >= 3) from accumulate(
$temp: LogInfo(type == "error") over window:time( 10m ),
count($temp)
)
then
// do something
end
(3)1分钟定时统计出现的错误数
rule "1分钟定时统计出现的错误数"
timer(cron: 0 0/1 * * * ?)
when
$count: Number() from accumulate(
$temp: LogInfo(type == "error") over window:time( 1m ),
count($temp)
)
then
// do something
end
【一起学习】