14.8. DRL (WHEN)中的规则条件

DRL 规则的 when 部分(也称为规则的 Left Hand Side (LHS) )包含执行操作必须满足的条件。条件由一系列声明 模式和限制 组成,根据软件包中的可用数据对象,带有可选 绑定 和支持的规则条件元素(关键字)。例如,如果某个公司需要 loan applicants 的时间超过 21 年,那么 "Underage" 规则的 when 条件将是 Applicant (age < 21)

注意

DRL 使用 when 而不是 if ,因为是其他执行流的一部分,该流通常会在特定时间点上检查条件。相反, 表示条件评估不仅限于特定的评估序列或时间点,而是随时发生。每当满足条件时,都会执行操作。

如果 when 部分为空,则条件被视为 true,并且首先在决策引擎中执行 fireAllRules () 调用时,将执行 部分中的操作。如果要使用规则设置决策引擎状态,这非常有用。

以下示例规则使用空条件在每次执行规则时插入事实:

没有条件的规则示例

rule "Always insert applicant"
  when
    // Empty
  then   // Actions to be executed once
    insert( new Applicant() );
end

// The rule is internally rewritten in the following way:

rule "Always insert applicant"
  when
    eval( true )
  then
    insert( new Applicant() );
end

如果规则条件使用多个模式,且没有定义的关键字(如 ),则默认组合是 :

没有关键字组合的规则示例

rule "Underage"
  when
    application : LoanApplication()
    Applicant( age < 21 )
  then
    // Actions
end

// The rule is internally rewritten in the following way:

rule "Underage"
  when
    application : LoanApplication()
    and Applicant( age < 21 )
  then
    // Actions
end

14.8.1. 模式和约束

DRL 规则条件中的 模式是 决策引擎要匹配的分段。模式可能与插入到决策引擎工作内存的每个事实匹配。模式也可以包含 约束,以进一步定义要匹配的事实。

在最简单的形式中,没有限制,模式与给定类型的事实匹配。在以下示例中,type 是 Person,因此模式将与决策引擎工作内存中的所有 Person 对象匹配:

单个事实类型的模式示例

Person()

这个类型不需要是一些事实对象的实际类。模式可以引用超级类甚至接口,可能与许多不同类的事实匹配。例如,以下模式与决策引擎工作内存中的所有对象匹配:

所有对象的模式示例

Object() // Matches all objects in the working memory

组成约束的特征的括号,如个人年龄的以下约束:

带有约束的模式示例

Person( age == 50 )

约束 是返回 truefalse 的表达式。DRL 中的模式限制基本上是带有一些改进的 Java 表达式,如属性访问,以及一些区别,如 ==!=equals ()!equals () 语义(而不是常见的语义)。

任何 192.168.1.0/24 属性都可以直接从模式限制访问。bean 属性在内部使用没有参数且返回一些标准站 getter 来公开。例如,age 属性写为 DRL 中的 年龄,而不是 getter getAge ()

带有 sVirt 属性的 DRL 约束语法

Person( age == 50 )

// This is the same as the following getter format:

Person( getAge() == 50 )

Red Hat Process Automation Manager 使用标准 JDK Introspector 类来实现此映射,因此它遵循标准站规格。为获得最佳决策引擎性能,请使用属性访问格式,如 年龄,而不是明确使用 getters,如 getAge ()

警告

不要使用属性访问器来更改对象的状态,这可能会影响规则,因为决策引擎会缓存调用之间匹配的结果,以便提高效率。

例如,不要使用以下方法使用属性访问器:

public int getAge() {
    age++; // Do not do this.
    return age;
}
public int getAge() {
    Date now = DateUtil.now(); // Do not do this.
    return DateUtil.differenceInYears(now, birthday);
}

遵循第二个示例,插入一个事实,它将当前日期嵌套在工作内存中,并根据需要更新 fireAllRules () 之间的事实。

但是,如果无法找到属性的 getter,编译器将使用属性名称作为回退方法名称,且没有参数:

如果没有找到对象,回退方法

Person( age == 50 )

// If `Person.getAge()` does not exist, the compiler uses the following syntax:

Person( age() == 50 )

您还可以以模式嵌套访问属性,如下例所示。嵌套属性由决策引擎索引。

带有嵌套属性访问的模式示例

Person( address.houseNumber == 50 )

// This is the same as the following format:

Person( getAddress().getHouseNumber() == 50 )

警告

在有状态 KIE 会话中,请小心使用嵌套的访问器,因为决策引擎的工作内存不知道任何嵌套的值,且不会检测何时更改。考虑不可变的值,而其任何父引用都会插入到工作内存中,或者,如果要修改嵌套值,请将所有外部事实标记为更新。在上例中,当 houseNumber 属性有变化时,所有具有该地址的 Person 必须标记为更新。

您可以使用任何返回 布尔值 的 Java 表达式作为模式括号内的约束。Java 表达式可以与其他表达式增强混合,如属性访问:

带有使用属性访问和 Java 表达式的约束的模式示例

Person( age == 50 )

您可以使用括号更改评估优先级,如在任何逻辑或数学表达式中一样:

约束评估顺序示例

Person( age > 100 && ( age % 10 == 0 ) )

您还可以在约束中重复使用 Java 方法,如下例所示:

使用可重复使用的 Java 方法的限制示例

Person( Math.round( weight / ( height * height ) ) < 25.0 )

警告

不要使用限制来更改对象的状态,这可能会影响规则,因为决策引擎会缓存调用之间匹配的结果,以提高效率。在规则条件中对事实执行的任何方法都必须是只读的方法。此外,事实的状态不应在规则调用之间更改,除非这些事实在每次更改时在工作内存中被标记为更新。

例如,不要使用以下方法使用模式约束:

Person( incrementAndGetAge() == 10 ) // Do not do this.
Person( System.currentTimeMillis() % 1000 == 0 ) // Do not do this.

标准 Java 操作器优先级适用于 DRL 中的约束运算符,DRL 运算符遵循标准的 Java 语义,但 ==!= 运算符除外。

== 运算符使用 null-safe equals () 语义,而不是常见的语义。例如,模式 Person (firstName == "John")java.util.Objects.equals (person.getFirstName (), "John"), 和 "John" 不为空,其模式也类似于 "John".equals (person.getFirstName ())

!= 运算符使用 null-safe !equals () 语义,而不是常见的语义。例如,模式 Person (firstName != "John")!java.util.Objects.equals (person.getFirstName (), "John") 类似。

如果约束的字段和约束值是不同的类型,则决策引擎将使用类型 coercion 来解决冲突并减少编译错误。例如,如果 "ten" 作为数字评估器中的字符串提供,则会出现编译错误,而 "10" 则合并到数字 10 中。在 coercion 中,字段类型总是优先于值类型:

带有 coerced 值的约束示例

Person( age == "10" ) // "10" is coerced to 10

对于限制组,您可以使用逗号限制 使用隐式 和连接 语义:

具有多个约束的模式示例

// Person is at least 50 years old and weighs at least 80 kilograms:
Person( age > 50, weight > 80 )

// Person is at least 50 years old, weighs at least 80 kilograms, and is taller than 2 meters:
Person( age > 50, weight > 80, height > 2 )

注意

虽然 && 和,但运算符具有相同的语义,但它们可以通过不同的优先级来解决。 && 运算符在 || 操作器前,以及 &&|| 运算符都放在 , 运算符 前。在顶级约束中使用逗号运算符来优化决策引擎性能和人类可读性。

您不能将逗号运算符嵌入到复合约束表达式中,如括号中:

复合约束表达式中错误使用的逗号示例

// Do not use the following format:
Person( ( age > 50, weight > 80 ) || height > 2 )

// Use the following format instead:
Person( ( age > 50 && weight > 80 ) || height > 2 )

14.8.2. 在模式和限制中绑定变量

您可以将变量绑定到模式和约束,以引用规则其他部分中的匹配对象。通过您在数据模型中注解事实的方式,绑定变量可帮助您更有效地定义规则或更加一致。要在规则中的变量和字段之间更容易区分,请为变量使用标准格式 $variable,特别是在复杂的规则中。此惯例很有用,但在 DRL 中不需要。

例如,以下 DRL 规则将变量 $p 用于带有 Person 事实的模式:

带有绑定变量的模式

rule "simple rule"
  when
    $p : Person()
  then
    System.out.println( "Person " + $p );
end

同样,您还可以将变量绑定到模式约束中的属性,如下例所示:

// Two persons of the same age:
Person( $firstAge : age ) // Binding
Person( age == $firstAge ) // Constraint expression
注意

确保为更清晰、更有效的规则定义分隔约束绑定和约束表达式。虽然支持混合绑定和表达式,但它们可能会复杂模式并影响评估效率。

// Do not use the following format:
Person( $age : age * 2 < 100 )

// Use the following format instead:
Person( age * 2 < 100, $age : age )

决策引擎不支持绑定到同一声明,但支持多个属性的 取消 参数。虽然位置参数始终使用 unification 处理,但命名参数存在联合符号 :=

以下示例模式在两个 Person 事实之间统一 age 属性:

带有未配置的模式示例

Person( $age := age )
Person( $age := age )

取消声明第一次发生的绑定,并限制为绑定字段的同一值,以进行序列发生。

14.8.3. 嵌套约束和内联多播

在某些情况下,您可能需要访问嵌套对象的多个属性,如下例所示:

访问多个属性的模式示例

Person( name == "mark", address.city == "london", address.country == "uk" )

您可以将这些属性访问器分组到使用语法 . (<constraints>) 嵌套的对象,以便更易读的规则,如下例所示:

带有分组限制的模式示例

Person( name == "mark", address.( city == "london", country == "uk") )

注意

period 前缀 . 将嵌套对象约束与方法调用区分开。

当您以模式处理嵌套对象时,您可以使用语法 < type>114<subtype > 广播到子类型,并使父类型中的 getters 可供子类型使用。您可以使用对象名称或完全限定类名称,您可以转换为一个或多个子类型,如下例所示:

将内联转换为子类型的模式示例

// Inline casting with subtype name:
Person( name == "mark", address#LongAddress.country == "uk" )

// Inline casting with fully qualified class name:
Person( name == "mark", address#org.domain.LongAddress.country == "uk" )

// Multiple inline casts:
Person( name == "mark", address#LongAddress.country#DetailedCountry.population > 10000000 )

这些示例模式 cast AddressLongAddress,以及上一次示例中的 Detailsed Country,在每个示例中,父 getters 可供子类型使用。

您可以使用 instanceof 运算符来推断在后续使用带有模式的指定类型的结果,如下例所示:

Person( name == "mark", address instanceof LongAddress, address.country == "uk" )

如果无法进行内联多播(例如,如果 instanceof 返回 false),则评估被视为 false

14.8.4. 约束中的日期字面

默认情况下,决策引擎支持日期格式 dd-mmm-yyyy。您可以通过提供系统属性 drools.dateformat="dd-mmm-yyyy hh:mm" 的其它格式掩码来自定义日期格式,包括时间格式掩码。您还可以使用 drools.defaultlanguagedrools.defaultcountry 系统属性更改语言区域设置来自定义日期格式(例如,Thailand 的区域设置被设置为 drools.defaultlanguage=thdrools.defaultcountry=TH)。

带有日期字面限制的模式示例

Person( bornBefore < "27-Oct-2009" )

14.8.5. DRL 模式约束支持的 Operator

DRL 在模式约束中支持 Operator 的标准 Java 语义,但有一些例外和一些额外的运算符在 DRL 中是唯一的。以下列表总结了 DRL 约束中不同于标准 Java 语义或 DRL 约束中唯一的 Operator。

.(), #

使用 . () 运算符将属性 accessors 分组到嵌套对象,并使用 guestfish 运算符,将子类型转换为嵌套对象中的子类型。遍历子类型使来自父类型的 getters 可供子类型使用。您可以使用对象名称或完全限定类名称,您可以转换为一个或多个子类型。

带有嵌套对象的模式示例

// Ungrouped property accessors:
Person( name == "mark", address.city == "london", address.country == "uk" )

// Grouped property accessors:
Person( name == "mark", address.( city == "london", country == "uk") )

注意

period 前缀 . 将嵌套对象约束与方法调用区分开。

将内联转换为子类型的模式示例

// Inline casting with subtype name:
Person( name == "mark", address#LongAddress.country == "uk" )

// Inline casting with fully qualified class name:
Person( name == "mark", address#org.domain.LongAddress.country == "uk" )

// Multiple inline casts:
Person( name == "mark", address#LongAddress.country#DetailedCountry.population > 10000000 )

!.

使用此操作器以 null-safe 的方式解引用属性。! 左侧的值。 运算符不能为空(解析为 != null),才能为模式匹配提供正结果。

带有 null-safe 解引用的限制示例

Person( $streetName : address!.street )

// This is internally rewritten in the following way:

Person( address != null, $streetName : address.street )

[]

使用此操作器根据 index 或键的 Map 值访问 List 值。

带有 ListMap 访问权限的约束示例

// The following format is the same as `childList(0).getAge() == 18`:
Person(childList[0].age == 18)

// The following format is the same as `credentialMap.get("jdoe").isValid()`:
Person(credentialMap["jdoe"].valid)

<, <=, >, >=

在具有自然排序的属性中使用这些运算符。例如,对于 Date 字段,&lt ; 运算符在 之前 表示,对于 String 字段,运算符在 之前按字母顺序 表示。这些属性仅适用于相似的属性。

operator 之前 的限制示例

Person( birthDate < $otherBirthDate )

Person( firstName < $otherFirstName )

==, !=

在约束中将这些运算符用作 equals ()!equals () 方法,而不是常见的 相同 语义。

带有 null-safe 相等的约束示例

Person( firstName == "John" )

// This is similar to the following formats:

java.util.Objects.equals(person.getFirstName(), "John")
"John".equals(person.getFirstName())

带有 null-safe 不匹配的限制示例

Person( firstName != "John" )

// This is similar to the following format:

!java.util.Objects.equals(person.getFirstName(), "John")

&&, ||

使用这些运算符创建缩写组合关系条件,该条件对字段添加多个限制。您可以使用括号 () 对限制进行分组,以创建递归语法模式。

带有缩写组合关系的限制示例

// Simple abbreviated combined relation condition using a single `&&`:
Person(age > 30 && < 40)

// Complex abbreviated combined relation using groupings:
Person(age ((> 30 && < 40) || (> 20 && < 25)))

// Mixing abbreviated combined relation with constraint connectives:
Person(age > 30 && < 40 || location == "london")

匹配不匹配

使用这些运算符来指示字段匹配或不匹配指定的 Java 正则表达式。通常,正则表达式是一个字符串字面,但解析为有效正则表达式的变量也支持。这些操作器仅适用于 String 属性。如果您使用 null 值匹配的,则生成的评估始终为 false。如果您使用 null 值不匹配,则生成的评估始终为 true。与 Java 一样,您写为 字符串 字面的正则表达式必须使用双反斜杠 \\ 来转义。

要匹配或不匹配正则表达式的约束示例

Person( country matches "(USA)?\\S*UK" )

Person( country not matches "(USA)?\\S*UK" )

包含不包含

使用这些运算符来验证是 Array 还是集合包含或不包含指定的值的字段。这些运算符适用于 ArrayCollection 属性,但您也可以使用这些运算符代替 String.contains ()!String.contains () 约束检查。

包含且不包含 集合的限制示例

// Collection with a specified field:
FamilyTree( countries contains "UK" )

FamilyTree( countries not contains "UK" )


// Collection with a variable:
FamilyTree( countries contains $var )

FamilyTree( countries not contains $var )

包含 且不包含 String 字面的限制示例

// Sting literal with a specified field:
Person( fullName contains "Jr" )

Person( fullName not contains "Jr" )


// String literal with a variable:
Person( fullName contains $var )

Person( fullName not contains $var )

注意

为向后兼容,excludes 运算符是不受支持的 synonym,不包含

memberOf,not memberOf

使用这些运算符来验证字段是否为一个数组的成员,还是定义为变量的集合的成员。 ArrayCollection 必须是变量。

带有 memberOf 而不是 memberOf 的限制示例

FamilyTree( person memberOf $europeanDescendants )

FamilyTree( person not memberOf $europeanDescendants )

soundslike

使用此运算符验证词语是否具有几乎相同的声音,使用英语探测与给定值(与 match 运算符类似)。此 operator 使用 Soundex 算法。

带有 类似声音的约束示例

// Match firstName "Jon" or "John":
Person( firstName soundslike "John" )

str

使用此运算符验证 字符串 是否以或以指定的值开头或结尾的字段。您还可以使用此操作器验证 String 的长度。

带有 str的约束示例

// Verify what the String starts with:
Message( routingValue str[startsWith] "R1" )

// Verify what the String ends with:
Message( routingValue str[endsWith] "R2" )

// Verify the length of the String:
Message( routingValue str[length] 17 )

在 中,not in

使用这些运算符指定多个可能的值来匹配约束(复合值限制)。只有 in 和不支持在 operator 中支持复合值限制 的功能。这些运算符的第二个操作对象必须是以逗号分隔的值列表,用括号括起。您可以将值作为变量、字面、返回值或合格的标识符提供。这些运算符内部使用操作符 ==!= 作为多个限制的列表。

带有 innotin的限制示例

Person( $color : favoriteColor )
Color( type in ( "red", "blue", $color ) )

Person( $color : favoriteColor )
Color( type notin ( "red", "blue", $color ) )

14.8.6. DRL 模式约束中的 Operator 优先级

DRL 支持适用约束运算符的标准 Java 操作器优先级,但有一些例外和一些额外的运算符在 DRL 中是唯一的。下表在适用的情况下列出了 DRL operator 优先级,从最高到最低的优先级:

表 14.2. DRL 模式约束中的 Operator 优先级

Operator 类型Operator备注

嵌套或空安全属性访问

., .(), !.

不支持标准 Java 语义

列表映射 访问

[]

不支持标准 Java 语义

约束绑定

:

不支持标准 Java 语义

multiplicative

*, /%

 

additive

+, -

 

移动

>>, >>>, <<

 

关系

& lt; , &lt;= , & gt; , & gt;= , instanceof

 

相等

== !=

使用 equals ()!equals () 语义,而不是标准的 Java 相同的 语义

非短路 AND

&

 

非短路 exclusive OR

^

 

非短路包含 OR

|

 

logical AND

&&

 

logical OR

||

 

ternary

? :

 

以逗号分隔的 AND

,

不支持标准 Java 语义

14.8.7. DRL 中支持的规则条件元素(关键字)

DRL 支持以下规则条件元素(关键字),您可以与您在 DRL 规则条件中定义的模式一起使用:

和

使用它来将条件组件分组到逻辑组合中。在fix prefix 中被支持,并被支持。您可以使用括号 () 显式分组模式。默认情况下,所有列出的模式都会与 合并,如果没有指定。

带有 and的模式示例

//Infix `and`:
Color( colorType : type ) and Person( favoriteColor == colorType )

//Infix `and` with grouping:
(Color( colorType : type ) and (Person( favoriteColor == colorType ) or Person( favoriteColor == colorType ))

// Prefix `and`:
(and Color( colorType : type ) Person( favoriteColor == colorType ))

// Default implicit `and`:
Color( colorType : type )
Person( favoriteColor == colorType )

注意

不要使用带有 and 关键字的前导 声明绑定 (例如,您可以使用 )。声明一次只能引用单个事实,如果您使用声明绑定和,那么当 满足时,它将同时匹配事实,并导致错误。

滥用 的示例

// Causes compile error:
$person : (Person( name == "Romeo" ) and Person( name == "Juliet"))

或者

使用此选项将条件组件分组到逻辑取消中。在fix 和 prefix 中不被支持。您可以使用括号 () 显式分组模式。您还可以使用模式 绑定或,但每个模式都必须单独绑定。

带有 or的模式示例

//Infix `or`:
Color( colorType : type ) or Person( favoriteColor == colorType )

//Infix `or` with grouping:
(Color( colorType : type ) or (Person( favoriteColor == colorType ) and Person( favoriteColor == colorType ))

// Prefix `or`:
(or Color( colorType : type ) Person( favoriteColor == colorType ))

带有 模式绑定的模式示例

pensioner : (Person( sex == "f", age > 60 ) or Person( sex == "m", age > 65 ))

(or pensioner : Person( sex == "f", age > 60 )
    pensioner : Person( sex == "m", age > 65 ))

or condition 元素的行为与 connective || 运算符不同,用于字段限制和限制。决策引擎不会直接解释 or 元素,而是使用逻辑转换来使用或作为多个子规则重写规则。此过程最终会生成具有单个 根节点的规则,以及每个 condition 元素的一个子规则。每个子规则都像任何常规规则一样激活并执行,子规则之间没有特殊行为或交互。

因此,当两个或者多个 disjunction 术语满足时,请考虑 or condition 元素生成两个或多个类似规则的快捷方式。

exists

使用它来指定必须存在的事实和约束。这个选项仅针对第一个匹配项触发,而不在以后的匹配项上触发。如果您将此元素与多种模式一起使用,请将模式与括号 () 括起来。

存在模式示例

exists Person( firstName == "John")

exists (Person( firstName == "John", age == 42 ))

exists (Person( firstName == "John" ) and
        Person( lastName == "Doe" ))

not

使用它来指定不能存在的事实和约束。如果您将此元素与多种模式一起使用,请将模式与括号 () 括起来。

没有的模式示例

not Person( firstName == "John")

not (Person( firstName == "John", age == 42 ))

not (Person( firstName == "John" ) and
     Person( lastName == "Doe" ))

forall

使用此选项来验证与第一个模式匹配的所有事实是否与所有剩余的模式匹配。当满足 forall 结构时,该规则将评估为 true。此元素是一个范围分隔符,因此它可以使用任何之前绑定的变量,但其中没有变量绑定可在它之外使用。

带有 forall的规则示例

rule "All full-time employees have red ID badges"
  when
    forall( $emp : Employee( type == "fulltime" )
                   Employee( this == $emp, badgeColor = "red" ) )
  then
    // True, all full-time employees have red ID badges.
end

在本例中,规则选择类型为 "fulltime" 的所有 Employee 对象。对于与此模式匹配的每个事实,该规则会评估遵循的模式(从属颜色),如果匹配,该规则将评估为 true

要说明决策引擎工作内存中给定类型的所有事实必须与一组限制匹配,您可以使用单一模式来方便使用。

带有 forall 和单一模式的规则示例

rule "All full-time employees have red ID badges"
  when
    forall( Employee( badgeColor = "red" ) )
  then
    // True, all full-time employees have red ID badges.
end

您可以将 forall 构造使用多个模式,或者嵌套它们与其他 condition 元素一起,例如在 not 元素构造中。

带有 forall 和多个模式的规则示例

rule "All employees have health and dental care programs"
  when
    forall( $emp : Employee()
            HealthCare( employee == $emp )
            DentalCare( employee == $emp )
          )
  then
    // True, all employees have health and dental care.
end

带有 forall 而不是t 的规则示例

rule "Not all employees have health and dental care"
  when
    not ( forall( $emp : Employee()
                  HealthCare( employee == $emp )
                  DentalCare( employee == $emp ) )
        )
  then
    // True, not all employees have health and dental care.
end

注意

all (p1 p2 p2 p3 …​) 的格式等同于 not (p1 而不是(和 p2 p3 …​)

from

使用它来为模式指定数据源。这可让决策引擎超过工作内存中的数据。数据源可以是绑定变量上的子字段,也可以是方法调用的结果。用于定义对象源的表达式是遵循常规 MVEL 语法的任何表达式。因此,ca 元素可让您轻松使用对象属性导航、执行方法调用和访问映射和集合元素。

使用 from 和 pattern 绑定的规则示例

rule "Validate zipcode"
  when
    Person( $personAddress : address )
    Address( zipcode == "23920W" ) from $personAddress
  then
    // Zip code is okay.
end

带有 from 和图形表示法的规则示例

rule "Validate zipcode"
  when
    $p : Person()
    $a : Address( zipcode == "23920W" ) from $p.address
  then
    // Zip code is okay.
end

迭代所有对象的规则示例

rule "Apply 10% discount to all items over US$ 100 in an order"
  when
    $order : Order()
    $item  : OrderItem( value > 100 ) from $order.items
  then
    // Apply discount to `$item`.
end

注意

对于大量对象集合,而不是添加带有决定引擎经常迭代的大型图形的对象,直接将集合添加到 KIE 会话中,然后在条件中加入集合,如下例所示:

when
  $order : Order()
  OrderItem( value > 100, order == $order )

带有 fromlock-on-active 规则属性的规则示例

rule "Assign people in North Carolina (NC) to sales region 1"
  ruleflow-group "test"
  lock-on-active true
  when
    $p : Person()
    $a : Address( state == "NC" ) from $p.address
  then
    modify ($p) {} // Assign the person to sales region 1.
end

rule "Apply a discount to people in the city of Raleigh"
  ruleflow-group "test"
  lock-on-active true
  when
    $p : Person()
    $a : Address( city == "Raleigh" ) from $p.address
  then
    modify ($p) {} // Apply discount to the person.
end

重要

使用带有 lock-on-active 规则属性的 from 可能会导致规则没有被执行。您可以使用以下方法之一解决这个问题:

  • 当您可以将所有事实插入到决策引擎的工作内存中时,避免使用 from 元素,或者在约束表达式中使用嵌套对象引用。
  • modify () 块中使用的变量设置为规则条件中最后一个句子。
  • 当您可以明确管理同一 ruleflow 组中的规则时,避免使用 lock-on-active 规则属性。

包含 from 子句的模式不能后跟以括号开头的另一个模式。这个限制的原因是 DRL 解析器从表达式读取 " from $l (String ()或 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

entry-point

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

来自 entry-point的规则示例

rule "Authorize withdrawal"
  when
    WithdrawRequest( $ai : accountId, $am : amount ) from entry-point "ATM Stream"
    CheckingAccount( accountId == $ai, balance > $am )
  then
    // Authorize withdrawal.
end

带有 EntryPoint 对象的 Java 应用程序代码示例和插入的事实

import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.rule.EntryPoint;

// Create your KIE base and KIE session as usual:
KieSession session = ...

// Create a reference to the entry point:
EntryPoint atmStream = session.getEntryPoint("ATM Stream");

// Start inserting your facts into the entry point:
atmStream.insert(aWithdrawRequest);

collect

使用它来定义规则可作为条件的一部分使用的对象集合。规则从指定源或决策引擎的工作内存获取集合。collect 元素的结果模式可以是实现 java.util.Collection 接口的任何聚合类,并提供默认的 no-arg 公共构造器。您可以使用 Java 集合,如 ListLinkedListHashSet,或者您自己的类。如果在条件中的 collect 元素之前绑定变量,您可以使用变量限制源和结果模式。但是,collect 元素内所做的任何绑定都不可用,可在它之外使用。

带有 collect的规则示例

import java.util.List

rule "Raise priority when system has more than three pending alarms"
  when
    $system : System()
    $alarms : List( size >= 3 )
              from collect( Alarm( system == $system, status == 'pending' ) )
  then
    // Raise priority because `$system` has three or more `$alarms` pending.
end

在本例中,该规则评估每个给定系统决策引擎工作内存中的所有待处理警报,并将它们分组到列表中。如果为给定系统找到三个或更多警告,则执行该规则。

您还可以使用与元素嵌套的 collect 元素,如下例所示:

中收集 和嵌套 的规则示例

import java.util.LinkedList;

rule "Send a message to all parents"
  when
    $town : Town( name == 'Paris' )
    $mothers : LinkedList()
               from collect( Person( children > 0 )
                             from $town.getPeople()
                           )
  then
    // Send a message to all parents.
end

累积

使用它来迭代对象集合,对每个元素执行自定义操作,并返回一个或多个结果对象(如果约束评估为 true)。这个元素是 collect condition 元素的更灵活且强大的形式。您可以在 累积 条件中使用预定义的功能,或根据需要实施自定义功能。您还可以在规则条件中使用缩写缩写 cc 来 累积

使用以下格式在规则中定义 累积 条件:

累积格式的首选格式

accumulate( <source pattern>; <functions> [;<constraints>] )

注意

虽然决策引擎支持 累积 元素的替代格式来实现向后兼容,但对于规则和应用程序中获得最佳性能,这种格式是首选的。

决策引擎支持以下预定义的 累积 功能。这些功能接受任何表达式作为输入。

  • 平均
  • 分钟
  • max
  • æ•°é‡�
  • sum
  • collectList
  • collectSet

在以下示例规则中,minmaxaverage 是在计算每个传感器的所有读取值的最小、最大值和平均温度值的功能:

计算温度值的累积规则示例

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

以下示例规则使用测量 的平均 函数来按照顺序计算所有项目的平均原率:

计算平均 profit 的规则示例

rule "Average profit"
  when
    $order : Order()
    accumulate( OrderItem( order == $order, $cost : cost, $price : price );
                $avgProfit : average( 1 - $cost / $price ) )
  then
    // Average profit for `$order` is `$avgProfit`.
end

要在 累积 条件中使用自定义特定于域的功能,请创建一个 Java 类来实现 org.kie.api.runtime.rule.AccumulateFunction 接口。例如,以下 Java 类定义了 AverageData 函数的自定义实现:

带有 平均 功能的自定义实现的 Java 类示例

// An implementation of an accumulator capable of calculating average values

public class AverageAccumulateFunction implements org.kie.api.runtime.rule.AccumulateFunction<AverageAccumulateFunction.AverageData> {

    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {

    }

    public void writeExternal(ObjectOutput out) throws IOException {

    }

    public static class AverageData implements Externalizable {
        public int    count = 0;
        public double total = 0;

        public AverageData() {}

        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
            count   = in.readInt();
            total   = in.readDouble();
        }

        public void writeExternal(ObjectOutput out) throws IOException {
            out.writeInt(count);
            out.writeDouble(total);
        }

    }

    /* (non-Javadoc)
     * @see org.kie.api.runtime.rule.AccumulateFunction#createContext()
     */
    public AverageData createContext() {
        return new AverageData();
    }

    /* (non-Javadoc)
     * @see org.kie.api.runtime.rule.AccumulateFunction#init(java.io.Serializable)
     */
    public void init(AverageData context) {
        context.count = 0;
        context.total = 0;
    }

    /* (non-Javadoc)
     * @see org.kie.api.runtime.rule.AccumulateFunction#accumulate(java.io.Serializable, java.lang.Object)
     */
    public void accumulate(AverageData context,
                           Object value) {
        context.count++;
        context.total += ((Number) value).doubleValue();
    }

    /* (non-Javadoc)
     * @see org.kie.api.runtime.rule.AccumulateFunction#reverse(java.io.Serializable, java.lang.Object)
     */
    public void reverse(AverageData context, Object value) {
        context.count--;
        context.total -= ((Number) value).doubleValue();
    }

    /* (non-Javadoc)
     * @see org.kie.api.runtime.rule.AccumulateFunction#getResult(java.io.Serializable)
     */
    public Object getResult(AverageData context) {
        return new Double( context.count == 0 ? 0 : context.total / context.count );
    }

    /* (non-Javadoc)
     * @see org.kie.api.runtime.rule.AccumulateFunction#supportsReverse()
     */
    public boolean supportsReverse() {
        return true;
    }

    /* (non-Javadoc)
     * @see org.kie.api.runtime.rule.AccumulateFunction#getResultType()
     */
    public Class< ? > getResultType() {
        return Number.class;
    }

}

要在 DRL 规则中使用自定义功能,请使用 import 累积语句导入 函数:

导入自定义功能的格式

import accumulate <class_name> <function_name>

带有导入 平均 功能的规则示例

import accumulate AverageAccumulateFunction.AverageData average

rule "Average profit"
  when
    $order : Order()
    accumulate( OrderItem( order == $order, $cost : cost, $price : price );
                $avgProfit : average( 1 - $cost / $price ) )
  then
    // Average profit for `$order` is `$avgProfit`.
end

14.8.8. OOPath 语法以及 DRL 规则条件中的对象图形

OOPath 是 XPath 的面向对象的语法扩展,旨在浏览 DRL 规则条件限制中的对象图形。OOPath 使用 XPath 中的紧凑表示法来导航相关元素,同时处理集合和过滤约束,对对象图形特别有用。

当事实的字段是一个集合时,您可以使用 from condition 元素(关键字)与集合中的所有项目绑定和原因。如果您需要在规则条件约束中浏览对象图表,则对 from condition 元素的广泛使用会导致详细和重复的语法,如下例所示:

从中浏览对象图形的规则示例

rule "Find all grades for Big Data exam"
  when
    $student: Student( $plan: plan )
    $exam: Exam( course == "Big Data" ) from $plan.exams
    $grade: Grade() from $exam.grades
  then
    // Actions
end

在本例中,域模型包含一个带有正在研究的 Plan 的 Tailoring 对象。此计划 可以有零个或多个研究 实例,并会包括零个或多个 Grade 实例在这种情况下,只有图形的根对象需要处于决策引擎的工作内存中,才能使此规则设置正常工作。

作为使用声明中广泛使用的更多替代方案,您可以使用缩写 OOPath 语法,如下例所示:

使用 OOPath 语法浏览对象图形的规则示例

rule "Find all grades for Big Data exam"
  when
    Student( $grade: /plan/exams[course == "Big Data"]/grades )
  then
    // Actions
end

通常,OOPath 表达式的核心 grammar 以扩展 Backus-Naur 格式(EBNF)标记定义:

OOPath 表达式的 EBNF 表示法

OOPExpr = [ID ( ":" | ":=" )] ( "/" | "?/" ) OOPSegment { ( "/" | "?/" | "." ) OOPSegment } ;
OOPSegment = ID ["#" ID] ["[" ( Number | Constraints ) "]"]

实际上,OOPath 表达式具有以下功能和功能:

  • 以正斜杠 / 或问问题标记开始,如果它是一个非主动 OOPath 表达式(本节后面介绍),则以正斜杠 / 或正斜杠 ?/ 开头。
  • 可以使用句点 . 操作符解引用对象的单个属性。
  • 可以使用正斜杠 / 运算符解引用对象的多个属性。如果返回集合,表达式会迭代集合中的值。
  • 可以过滤掉不满足一个或多个限制的对象。约束在方括号之间写成 predicate 表达式,如下例所示:

    将约束作为 predicate 表达式

    Student( $grade: /plan/exams[ course == "Big Data" ]/grades )

  • 可将遍历的对象缩减到通用集合中声明的类的子类。后续限制也可以安全地访问该子类中声明的属性,如下例所示。不是此内联 cast 中指定的类实例的对象会自动过滤掉。

    带有下游对象的约束

    Student( $grade: /plan/exams#AdvancedExam[ course == "Big Data", level > 3 ]/grades )

  • 可以引用当前迭代图形前遍历的图形的对象。例如,以下 OOPath 表达式仅匹配通过测试的平均等级:

    带有后引用对象的约束

    Student( $grade: /plan/exams/grades[ result > ../averageResult ] )

  • 可以是另一个 OOPath 表达式,如下例所示:

    递归约束表达式

    Student( $exam: /plan/exams[ /grades[ result > 20 ] ] )

  • 可以通过其方括号 [] 间的索引访问对象,如下例所示。要遵循 Java 规范,OOPath 索引基于 0,而 XPath 索引则基于 1。

    按索引访问对象的限制

    Student( $grade: /plan/exams[0]/grades )

OOPath 表达式可以是 reactive 或 non-reactive。决策引擎不会响应涉及在评估 OOPath 表达式期间遍历的深度嵌套对象的更新。

要使这些对象重新主动更改,请修改对象来扩展类 org.drools.core.phreak.ReactiveObject。修改对象以扩展 ReactiveObject 类后,domain 对象调用继承的方法 notifyModification,以在更新其中一个字段时通知决策引擎,如下例所示:

通知决策引擎迁移到其他课程的对象方法示例

public void setCourse(String course) {
        this.course = course;
        notifyModification(this);
        }

使用以下对应的 OOPath 表达式,当评估移到其他课程时,会重新执行该规则,并重新计算与该规则匹配的等级列表:

"Big Data" 规则中的 OOPath 表达式示例

Student( $grade: /plan/exams[ course == "Big Data" ]/grades )

您还可以使用 ?/ 分隔符而不是 / 分隔符来只对 OOPath 表达式的一个子端口禁用重新活动,如下例所示:

部分非主动的 OOPath 表达式示例

Student( $grade: /plan/exams[ course == "Big Data" ]?/grades )

通过本例,决策引擎会对评估所做的更改做出反应,或者如果为计划添加新的等级,则不会对计划进行了评估。

如果 OOPath 部分不是主动的,则 OOPath 表达式的所有剩余部分也会变为非活跃状态。例如,以下 OOPath 表达式完全非活跃:

完全非主动的 OOPath 表达式示例

Student( $grade: ?/plan/exams[ course == "Big Data" ]/grades )

因此,您不能在同一 OOPath 表达式中使用 ?/ 分隔符一次。例如,以下表达式会导致编译错误:

带有重复非活动标记的 OOPath 表达式示例

Student( $grade: /plan?/exams[ course == "Big Data" ]?/grades )

启用 OOPath 表达式的另一种方法是为 Red Hat Process Automation Manager 中的 ListSet 接口使用专用实现。这些实现是 ReactiveListReactiveSet 类。一个 ReactiveCollection 类也可用。该实施还支持通过 IteratorListIterator 类执行可变操作。

以下示例类使用这些类来配置 OOPath 表达式重新活动:

配置 OOPath 表达式的 Java 类示例

public class School extends AbstractReactiveObject {
    private String name;
    private final List<Child> children = new ReactiveList<Child>(); 1

    public void setName(String name) {
        this.name = name;
        notifyModification(); 2
    }

    public void addChild(Child child) {
        children.add(child); 3
        // No need to call `notifyModification()` here
    }
  }

1
使用 ReactiveList 实例对标准 Java List 实例进行重新主动支持。
2
在重新主动支持中更改时,使用所需的 notifyModification () 方法。
3
children 字段是一个 ReactiveList 实例,因此不需要 notifyModification () 方法调用。通知会自动处理,与通过 child 字段执行的所有其他变异操作一样。