16.8. DRL のルール条件 (WHEN)

DRL ルールの when 部分 (ルールの左辺 (LHS)とも言う) には、アクションを実行するのに満たされなければならない条件が含まれます。条件は、パッケージ内で使用可能なデータオブジェクトに基づいて、指定された一連の パターン および 制約、オプションの バインディング およびサポートされるルール条件要素 (キーワード) で設定されます。たとえば、銀行のローンの申し込みに年齢制限 (21 歳以上) が必要な場合、"Underage" ルールの when 条件は Applicant( age < 21 ) となります。

注記

DRL は if ではなく when を使用します。これは、if が一般に手続き型の実行フローの一部であり、条件が特定の時点でチェックされるためです。一方、when は、条件の評価が特定の評価シーケンスや時点に限定されず、いつでも継続的に行われることを示しています。条件が満たされるたびに、これらのアクションが実施されます。

when セクションを空にすると、条件は true であると見なされ、デシジョンエンジンで fireAllRules() 呼び出しが最初に実施された場合に、then セクションのアクションが実行されます。これは、デシジョンエンジンのステートを設定するルールを使用する場合に便利です。

以下のルール例では、空の条件を使用して、ルールが実行するたびにファクトを挿入します。

条件のないルール例

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

ルールの条件が、定義されたキーワード接続詞 (andor、または not など) なしで複数のパターンを使用する場合、デフォルトの接続詞は and になります。

キーワード接続詞なしのルールの例

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

16.8.1. パターンと制約

DRL ルール条件の パターン は、デシジョンエンジンによって照合されるセグメントです。パターンは、デシジョンエンジンのワーキングメモリーに挿入される各ファクトと一致する可能性があります。パターンには 制約 を含めることもでき、これにより一致するファクトをより詳細に定義できます。

制約のない最も単純なフォームでは、パターンは指定されたタイプのファクトに一致します。以下の例ではタイプが Person であるため、このパターンはデシジョンエンジンのワーキングメモリーのすべての Person オブジェクトに一致します。

ファクトタイプが 1 つの場合のパターン例

Person()

このタイプは、ファクトオブジェクトの実際のクラスである必要はありません。パターンは、複数の異なるクラスのファクトと一致する可能性のあるスーパーユーザーやインターフェイスも参照できます。たとえば、以下のパターンは、デシジョンエンジンのワーキングメモリーにあるすべてのオブジェクトと一致します。

すべてのオブジェクトの場合のパターン例

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

パターンの括弧は制約を囲みます (以下のユーザーの年齢に関する制約など)。

制約のあるパターンの例

Person( age == 50 )

制約は、true または false を返す式です。DRL 内のパターンの制約は、基本的にはプロパティーのアクセスなどの拡張が設定された Java の式ですが、== および != に対する equals() および !equals() セマンティクスなど、若干の違いがあります (通常の same および not same セマンティクスではありません)。

JavaBean プロパティーはパターンの制約から直接アクセスできます。Bean プロパティーは、引数を使用せずに何かを返す標準の JavaBeans の getter を使用して内部的に公開されます。たとえば、age プロパティーは、DRL で getter の getAge() ではなく、age として記述されます。

JavaBeans プロパティーを使用した DRL 制約構文

Person( age == 50 )

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

Person( getAge() == 50 )

Red Hat Process Automation Manager は標準の JDK leavingspector クラスを使用してこのマッピングを行うため、標準の JavaBeans 仕様に従います。デシジョンエンジンのパフォーマンスの最適化には、getAge() のように getter を明示的に使用するのではなく、age のようなプロパティーアクセスの形式を使用します。

警告

デシジョンエンジンは効率化のために呼び出し間で一致した結果をキャッシュするため、プロパティーアクセサーを使用してルールに影響を与える可能性がある方法でオブジェクトの状態を変更しないでください。

たとえば、プロパティーアクセサーを以下のように使用しないでください。

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);
}

2 番目の例に従う代わりに、ワーキングメモリーに現在の日付をラップするファクトを挿入し、必要に応じてそのファクトを 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 プロパティーが変更された場合は、この Address が指定された Person は更新済みとしてマークされる必要があります。

パターンの括弧内では boolean 値を制約として返す任意の 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.

DRL 内の制約演算子には、標準の Java 演算子の優先順位が適用されます。DRL 演算子は、== および != 演算子を除き、標準の Java セマンティクスに従います。

== 演算子は、通常の same セマンティクスではなく、null 安全な equals() セマンティクスを使用します。たとえば、Person( firstName == "John" ) というパターンは java.util.Objects.equals(person.getFirstName(), "John") と同様であり、"John" は null でないため、このパターンは "John".equals(person.getFirstName()) にも似ています。

!= 演算子は、通常の not same セマンティクスではなく null 安全な !equals() セマンティクスを使用します。たとえば、Person( firstName != "John" ) というパターンは、!java.util.Objects.equals(person.getFirstName(), "John") に似ています。

フィールドと制約の値が異なるタイプの場合、デシジョンエンジンは型強制 (type coercion) を使用して競合を解決し、コンパイルエラーを減らします。たとえば、"ten" が数値エバリュエーターで文字列として指定される場合は、コンパイルエラーが発生しますが、"10" は数値 10 に型強制されます。型強制では、フィールドのタイプは常に値のタイプより優先されます。

型強制された値を使用する制約の例

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

制約のグループの場合は、コンマ区切り (,) を使って、暗黙的な and の接続的なセマンティクスを使用することができます。

複数の制約があるパターンの例

// 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 )

16.8.2. パターンと制約でバインドされた変数

パターンおよび制約に変数をバインドして、ルールの他の部分で一致するオブジェクトを参照することができます。バインドされた変数は、ルールをより効率的に、かつデータモデルでのファクトへのアノテーションの付け方と一貫した方法で定義するのに役立ちます。(とくに複雑なルールの場合に) ルール内で変数とフィールドを簡単に区別するには、変数に対して標準の形式である $variable を使用します。この規則は便利ですが、DRL で必須ではありません。

たとえば、以下の DRL ルールでは、Person ファクトが指定されたパターンに対して変数 $p が使用されています。

バインドされた変数が使用されているパターン

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 )

デシジョンエンジンは同じ宣言に対するバインディングをサポートしませんが、複数のプロパティー間での引数の ユニフィケーション をサポートします。位置引数は、常にユニフィケーションで常に処理され、名前付き引数の場合はユニフィケーション記号 := が使用されます。

以下のパターンの例では、2 つの Person ファクト間で age プロパティーを統合します。

ユニフィケーションが使用されるパターンの例

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

ユニフィケーションは、シーケンスオカレンスのバインドされたフィールドの同じ値に対して、最初のオカレンスと制約のバインディングを宣言します。

16.8.3. ネストされた制約とインラインキャスト

以下の例のように、ネストされたオブジェクトの複数のプロパティーにアクセスしなければならない場合があります。

複数のプロパティーにアクセスするパターンの例

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

以下の例のように、これらのプロパティーのアクセサーを、.( <constraints> ) という構文を使用してネストされたオブジェクトに対してグループ化することで、ルールを読みやすくすることができます。

グループ化された制約を使用したパターンの例

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

注記

ピリオドのプリフィックス . は、メソッド呼び出しとネストされたオブジェクト制約を区別します。

パターンでネストされたオブジェクトを使用する場合は、構文 <type>#<subtype> を使用してサブタイプにキャストし、親タイプの getter をサブタイプに対して利用可能にします。以下の例のように、オブジェクト名または完全修飾クラス名のいずれかを使用して、1 つまたは複数のサブタイプにキャストできます。

サブタイプへのインラインキャストを使用したパターンの例

// 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 )

これらのパターン例では、AddressLongAddress に、さらに最後の例にある DetailedCountry にキャストし、各ケースのサブタイプで親の getter を利用可能にします。

以下の例のように、instanceof 演算子を使用して、パターンを使用した後続のフィールドで指定されたタイプの結果を推測できます。

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

インラインキャストが使用できない場合 (たとえば instanceoffalse を返す場合)、評価は false と見なされます。

16.8.4. 制約内の日付リテラル

デフォルトで、デシジョンエンジンは dd-mmm-yyyy という日付形式をサポートします。この日付形式 (必要に応じて時間形式マスクを含む) は、システムプロパティー drools.dateformat="dd-mmm-yyyy hh:mm" を使用して、別の形式マスクを指定することによってカスタマイズすることができます。日付形式は、drools.defaultlanguage および drools.defaultcountry システムプロパティーを使用し、言語ロケールを変更することによってカスタマイズすることもできます (たとえば、タイのロケールは drools.defaultlanguage=th および drools.defaultcountry=TH と設定します)。

日付のリテラル制限を使用したパターンの例

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

16.8.5. DRL のパターン制約でサポートされている演算子

DRL では、パターン制約の演算子で標準の Java セマンティクスがサポートされていますが、いくつかの例外があり、追加となる DRL 固有の演算子もいくつかあります。以下の一覧は、標準の Java セマンティクスとは異なる方法で処理される DRL の制約の演算子や DRL の制約に固有の演算子をまとめています。

.(), #

.() 演算子を使用すると、プロパティーのアクセサーをネストされたオブジェクトにグループ化でき、# 演算子を使用すると、ネストされたオブジェクトのサブタイプにキャストできます。サブタイプにキャストすることで、親タイプの getter をサブタイプに対して使用できるようになります。オブジェクト名または完全修飾クラス名のいずれかを使用でき、1 つまたは複数のサブタイプにキャストできます。

ネストされたオブジェクトが使用されるパターンの例

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

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

注記

ピリオドのプリフィックス . は、メソッド呼び出しとネストされたオブジェクト制約を区別します。

サブタイプへのインラインキャストを使用したパターンの例

// 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 安全な方法でプロパティーを逆参照します。パターンマッチングの適切な結果を得るには、!. 演算子の左側の値を null にすることはできません (!= null と解釈される)。

null 安全な逆参照を使用した制約の例

Person( $streetName : address!.street )

// This is internally rewritten in the following way:

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

[]

この演算子を使用して、インデックスで List 値にアクセスするか、またはキーで Map 値にアクセスします。

List および Map アクセスを使用する制約の例

// 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 フィールドでは を意味し、String フィールドでは アルファベット順で前 であることを意味します。これらのプロパティーは、比較可能なプロパティーにのみ適用されます。

before 演算子を使用した制約の例

Person( birthDate < $otherBirthDate )

Person( firstName < $otherFirstName )

==, !=

制約では、これらの演算子を、通常の same および not same セマンティクスではなく、equals() および !equals() メソッドとして使用します。

null 安全な等価性を使用する制約の例

Person( firstName == "John" )

// This is similar to the following formats:

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

null 安全な非等価性を使用する制約の例

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")

matches, not matches

これらの演算子を使用して、指定された Java 正規表現にフィールドが一致するか、または一致しないかを示します。一般に、正規表現は String リテラルですが、有効な正規表現に解決される変数もサポートされます。これらの演算子は String プロパティーのみに適用されます。null 値に対して matches を使用する場合は、結果の評価が常に false になります。null 値に対して not matches を使用する場合は、結果の評価が常に true になります。Java の場合のように、String リテラルとして記述された正規表現は二重のバックスラッシュ \\ を使用してエスケープする必要があります。

正規表現と一致する制約または一致しない制約の例

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

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

contains, not contains

これらの演算子を使用して、フィールドの Array または Collection が指定された値を含むか、または含まないかを検証します。これらの演算子は Array プロパティーまたは Collection プロパティーに適用されますが、これらの演算子を String.contains() および !String.contains() の制約チェックの代わりとして使用することもできます。

コレクションに対して contains および not 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 リテラルに対して contains および not contains が使用された制約の例

// 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 演算子は not contains の同義語としてサポートされます。

memberOf, not memberOf

これらの演算子を使用して、フィールドが変数として定義されている Array または Collection のメンバーであるかどうかを検証します。Array または Collection は変数でなければなりません。

コレクションと memberOf および not memberOf を使用する制約の例

FamilyTree( person memberOf $europeanDescendants )

FamilyTree( person not memberOf $europeanDescendants )

soundslike

この演算子を使用して、ある単語を英語で発音した場合に、指定された値と発音がほぼ同じであるかどうかを検証します (matches 演算子に類似)。この演算子は Soundex アルゴリズムを使用します。

soundslike を使用した制約の例

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

str

この演算子を使用して、String であるフィールドが指定された値で開始しているか、または終了しているかを検証します。この演算子を使用して、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 )

innotin

これらの演算子を使用して、制約の中で一致する可能性がある複数の値を指定します (複合値の制約)。複合値の制約についての機能をサポートするのは in 演算子および not in 演算子のみです。これらの演算子の 2 番目のオペランドは、括弧で囲み、コンマ区切り値の一覧で指定する必要があります。値は変数、リテラル、戻り値、または修飾された識別子として指定できます。これらの演算子は、== または != 演算子を使用し、複数の制約のリストとして内部に再書き込みされます。

in および notin を使用した制約の例

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

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

16.8.6. DRL のパターン制約における演算子の優先順位

DRL では、適用可能な制約演算子の場合は標準的な Java 演算子の優先順位をサポートしていますが、一部の例外と、DRL に固有の追加の演算子がいくつかあります。以下の表には、適用可能な DRL 演算子を優先順位の高いものから低いものの順で記載しています。

表16.2 DRL のパターン制約における演算子の優先順位

演算子のタイプ演算子注記

ネストされているか、null 安全なプロパティーアクセス

..()!.

標準の Java セマンティクスではない

List または Map アクセス

[]

標準の Java セマンティクスではない

制約のバインディング

:

標準の Java セマンティクスではない

乗法

*/%

 

加法

+-

 

シフト

>>>>><<

 

リレーショナル

<<=>>=instanceof

 

等価性

== !=

標準の Java の same および not same セマンティクスではなく、equals() および !equals() を使用

非短絡 (Non-short-circuiting) AND

&

 

非短絡の排他的 OR

^

 

非短絡の包含的 OR

|

 

論理 AND

&&

 

論理 OR

||

 

三項

? :

 

コンマ区切り AND

,

標準の Java セマンティクスではない

16.8.7. DRL でサポートされるルール条件要素 (キーワード)

DRL では、DRL のルール条件で定義するパターンで使用できる以下のルール条件要素 (キーワード) がサポートされます。

and

条件コンポーネントを論理積に分類します。インフィックスおよびプリフィックスの and がサポートされます。括弧 () を使用することにより、パターンを明示的にグループ化できます。デフォルトでは、結合演算子を指定しないと、リストされているパターンがすべて and で結合されます。

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 キーワードを使用しないでください (or などは使用できます)。宣言が参照できるのは一度に 1 つのファクトのみであり、and と宣言のバインディングを使用すると、and が満たされた場合にこの要素が両方のファクトと一致してしまうため、エラーが発生します。

and の誤った使用例

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

または

条件コンポーネントを論理和にグループ化します。インフィックスおよびプリフィックスの or がサポートされます。括弧 () を使用することにより、パターンを明示的にグループ化できます。or と共にパターンバインディングを使用することもできますが、各パターンは個別にバインディングする必要があります。

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 ))

or とパターンのバインディングを使用したパターンの例

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 条件要素の動作は、制約や、フィールド制約の制限を対象とした接続演算子 ( ||) とは異なります。デシジョンエンジンは or 要素を直接解釈しませんが、論理変換を使用して、or が使用されているルールを複数のサブルールに書き換えます。このプロセスにより、最終的には、ルートノードおよび各条件要素のサブルールとして、1 つの or を使用するルールが生成されます。サブルール間での操作や、特別な動作なしに、各サブルールは通常のルールと同様に有効にされ、実行されます。

したがって、or 条件要素については複数の類似したルールを生成するための近道であり、複数の論理和が true である場合は、複数のアクティベーションが作成される可能性があることに留意してください。

exists

存在している必要のあるファクトおよび制約を指定します。このオプションは、最初に一致したものだけが適用され、その後一致するものは無視されます。この要素を複数のパターンで使用する場合は、これらのパターンを括弧 () で囲みます。

exists を使用したパターンの例

exists Person( firstName == "John")

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

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

not

存在していてはならないファクトと制約を指定します。この要素を複数のパターンで使用する場合は、これらのパターンを括弧 () で囲みます。

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 を使用できます。

forall と 1 つのパターンを使用するルールの例

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 設定を使用するか、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

forallnot を使用するルールの例

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

注記

forall( p1 p2 p3 …​) の形式は not( p1 and not( and p2 p3 …​ ) ) と等価です。

from

これを使用してパターンのデータソースを指定します。これにより、デシジョンエンジンがワーキングメモリーにないデータに対して推論できるようになります。データソースには、バインドされた変数のサブフィールド、またはメソッド呼び出しの結果を指定できます。オブジェクトソースの定義に使用される式として、通常の MVEL 構文に準拠する任意の式を使用できます。このため、from 要素により、オブジェクトプロパティーのナビゲーションを使用して、メソッド呼び出しを実行し、マップとコレクション要素にアクセスすることが簡単にできます。

from およびパターンのバインディングを使用するルールの例

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

すべてのオブジェクトに対して反復処理される from のルールの例

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 )

from および lock-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

重要

fromlock-on-active のルール属性を同時に使用すると、ルールが実行しなくなります。この問題に対しては、以下のいずれかの方法で対処できます。

  • すべてのファクトをデシジョンエンジンのワーキングメモリーに挿入したり、制約式でネストされたオブジェクト参照を使用したりする場合は、from 要素は使用しないでください。
  • ルール条件の最後の文として、modify() ブロックで使用される変数を配置します。
  • 同じルールフローグループ内のルールがアクティベーションを相互に組み込む方法を明示的に管理できる場合は、lock-on-active ルール属性を使用しないでください。

from 句を含むパターンの後に、括弧から始まる別のパターンを使用することはできません。この制限がある理由は、DRL パーサーが from 式を "from $l (String() or Number())" として読み取り、この式を関数呼び出しと区別できないためです。この最も単純な回避策は、以下の例に示すように、from 句を括弧でラップする方法です。

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 条件要素と共に使用します。イベントのエントリーポイントを宣言し、デシジョンエンジンがそのエントリーポイントからのデータのみを使用してルールを評価することが可能です。エントリーポイントは、DRL ルールで参照することで暗黙的に宣言することも、Java アプリケーションで明示的に宣言することもできます。

from 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 インターフェイスを実装し、デフォルトの引数を持たないパブリックコンストラクターを指定する任意の具象クラスを使用できます。ListLinkedList、および HashSet のような Java コレクションを使用することも、独自のクラスを使用することもできます。条件内で 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

この例では、ルールは指定された各システムのデシジョンエンジンのワーキングメモリーの保留中のすべてのアラームを評価し、それらを List にグループ化します。指定されたシステムについての 3 つ以上のアラームが見つかると、ルールが実行します。

以下の例のように、ネストされた from 要素と共に collect 要素を使用することもできます。

collect とネストされた from を使用するルールの例

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

accumulate

オブジェクトのコレクションを反復処理し、各要素に対してカスタムアクションを実行し、結果オブジェクトを返します (制約が true に評価される場合)。この要素は、collect 条件要素のより柔軟性が高い、強化された形式です。accumulate 条件で事前に定義した関数を使用するか、必要に応じてカスタム関数を実装できます。また、ルール条件で accumulate の短縮形である acc を使用することもできます。

以下の形式を使用して、ルールに accumulate 条件を定義します。

accumulate の推奨形式

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

注記

デシジョンエンジンは下位互換性を確保するために accumulate 要素の代替形式をサポートしますが、この形式は、ルールとアプリケーションの最適なパフォーマンスという点ではより適しています。

デシジョンエンジンは、以下の事前に定義された accumulate 関数をサポートします。これらの関数は、任意の式を入力として受け入れます。

  • average
  • min
  • max
  • count
  • sum
  • collectList
  • collectSet

以下のルールの例では、minmax、および averageaccumulate 関数であり、各センサーの読み取り値での最低、最高、および平均の温度値を算出します。

温度値を算出する accumulate を使用したルールの例

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

以下のルールの例では、accumulate を指定した average 関数を使用して、ある注文のすべてのアイテムの平均収益を計算します。

平均収益を計算する accumulate を使用したルールの例

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

accumulate の条件でカスタムかつドメイン固有の関数を使用するには、org.kie.api.runtime.rule.AccumulateFunction インターフェイスを実装する Java クラスを作成します。たとえば、以下の Java クラスは AverageData 関数のカスタム実装を定義します。

average 関数がカスタムで実装された 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 accumulate ステートメントを使用してその関数をインポートします。

カスタム関数をインポートするための形式

import accumulate <class_name> <function_name>

インポートされた average 関数を使用するルールの例

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

16.8.8. DRL ルール条件内でオブジェクトのグラフが使用される OOPath 構文

OOPath は、DRL ルールの条件の制約でオブジェクトのグラフを参照するために設計された XPath のオブジェクト指向構文の拡張です。OOPath は、コレクションおよびフィルター制約を処理する間に XPath からのコンパクト表記を使用して関連要素を移動します。また、OOPath は特にオブジェクトグラフの場合に役に立ちます。

ファクトのフィールドがコレクションである場合は、from 条件要素 (キーワード) を使用してバインドし、そのコレクションのすべての項目を 1 つずつ判断することができます。ルール条件制約でオブジェクトのグラフを参照する必要がある場合は、from 条件要素を過度に使用すると、以下の例のように冗長かつ繰り返しの多い構文になります。

from を使用してオブジェクトのグラフを参照するルールの例

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

この例では、ドメインモデルには Student オブジェクトと学習の Plan が含まれています。Plan には、ゼロ以上の Exam インスタンスを指定でき、Exam にはゼロ以上の Grade インスタンスを指定できます。このルール設定が機能するためにデシジョンエンジンのワーキングメモリーに存在する必要があるのは、グラフのルートオブジェクト (この例では Student) のみです。

from ステートメントを使用するより効率的な別の方法として、以下の例のように短縮された OOPath 構文を使用できます。

OOPath 構文でオブジェクトのグラフを参照するルールの例

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

通常、OOPath 式の中核となる文法は、以下のように拡張された Backus-Naur form (EBNF) 表記で定義されます。

OOPath 式の EBNF 表記

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

OOPath 式の実際の特性および機能は以下のとおりです。

  • 非リアクティブな OOPath 式の場合は、スラッシュ / または疑問符とスラッシュ ?/ で始まります (このセクションで後述します)。
  • オブジェクトの単一プロパティーを、ピリオド . 演算子を使用して逆参照できます。
  • オブジェクトの複数のプロパティーをスラッシュ / 演算子を使用して逆参照できます。コレクションが返される場合、この式はコレクションの値を反復処理します。
  • 1 つまたは複数の制約を満たさない、走査されたオブジェクトをフィルターで除外できます。この制約は、以下の例のように、角括弧内の述語式として記述されます。

    述語式としての制約

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

  • 汎用コレクションで宣言されたクラスのサブクラスに走査されたオブジェクトをダウンキャストできます。以下の例に示すように、後続の制約も、このサブクラスで宣言されたプロパティーにのみ安全にアクセスすることができます。このインラインキャストで指定されたクラスのインスタンスではないオブジェクトは、自動的にフィルターで除外されます。

    ダウンキャストオブジェクトが使用される制約

    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 式は、リアクティブまたは非リアクティブにできます。デシジョンエンジンは、深くネスト化されており、OOPath 式の評価中に走査されたオブジェクトを含む更新には反応しません。

これらのオブジェクトが変更に反応するようにするには、org.drools.core.phreak.ReactiveObject クラスを拡張するようにオブジェクトを変更します。オブジェクトを変更して ReactiveObject クラスを拡張すると、ドメインオブジェクトはいずれかのフィールドが更新されている場合に、以下のように継承されたメソッド 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 式のリアクティビティーを有効にするもう 1 つの方法は、Red Hat Process Automation Manager で List インターフェイスおよび Set インターフェイスの専用の実装を使用することです。これらの実装は、ReactiveList クラスおよび ReactiveSet クラスです。また、ReactiveCollection クラスも使用できます。これらの実装により、Iterator クラスおよび ListIterator クラスを使用した可変操作の実行もリアクティブにサポートされます。

以下のクラスの例では、これらのクラスを使用して 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
標準の Java List インスタンスに対するリアクティブサポートのために、ReactiveList インスタンスを使用します。
2
フィールドがリアクティブなサポートで変更された場合は、必須の notifyModification() メソッドを使用します。
3
children フィールドは ReactiveList のインスタンスであるため、notifyModification() メソッド呼び出しは必要ありません。通知は、children フィールドで実行される他のすべての変更操作と同様に、自動的に処理されます。