14.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. endforallとnotを使用するルールの例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. endfromとグラフ表記を使用するルールの例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重要fromとlock-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. endEntryPoint オブジェクトが使用され、ファクトが挿入された 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インターフェイスを実装し、デフォルトの引数を持たないパブリックコンストラクターを指定する任意の具象クラスを使用できます。List、LinkedList、および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. endaccumulateオブジェクトのコレクションを反復処理し、各要素に対してカスタムアクションを実行し、結果オブジェクトを返します (制約が
trueに評価される場合)。この要素は、collect条件要素のより柔軟性が高い、強化された形式です。accumulate条件で事前に定義した関数を使用するか、必要に応じてカスタム関数を実装できます。また、ルール条件でaccumulateの短縮形であるaccを使用することもできます。以下の形式を使用して、ルールに
accumulate条件を定義します。accumulateの推奨形式accumulate( <source pattern>; <functions> [;<constraints>] )注記デシジョンエンジンは下位互換性を確保するために
accumulate要素の代替形式をサポートしますが、この形式は、ルールとアプリケーションの最適なパフォーマンスという点ではより適しています。デシジョンエンジンは、以下の事前に定義された
accumulate関数をサポートします。これらの関数は、任意の式を入力として受け入れます。-
average -
min -
max -
count -
sum -
collectList -
collectSet
以下のルールの例では、
min、max、およびaverageはaccumulate関数であり、各センサーの読み取り値での最低、最高、および平均の温度値を算出します。温度値を算出する
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`. endaccumulateの条件でカスタムかつドメイン固有の関数を使用するには、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-