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 Decision 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>();
public void setName(String name) {
this.name = name;
notifyModification();
}
public void addChild(Child child) {
children.add(child);
// No need to call `notifyModification()` here
}
}