第4章 ルール言語
4.1. 概要
DRL.g 内のルール言語に対する Antlr3 グラマーを参照することもできますが、必須ではありません。Rule Workbench を使用する場合、コンテンツアシスタントはルール構造の大部分に対応します。例えば、「ru」を入力し、ctrl キーとスペースキーを同時に押すと、ルール構造が構築されます。
4.1.1. ルールファイル
.drl 拡張子を持つファイルです。DRL ファイルには複数のルール、クエリ、および関数を含めることができ、またルールやクエリによって割り当てられ使用されるインポート、グローバルおよび属性などの一部のリソース宣言も含めることができます。 複数のルールファイルにまたがってルールを分散することもできます。この場合、拡張子 .rule が推奨されますが必須ではありません。ファイルにまたがってルールを分散すると、大量のルールを管理する場合に役立ちます。DRL ファイルは簡単なテキストファイルです。
package package-name imports globals functions queries rules
4.1.2. ルールの構造
rule "name"
attributes
when
LHS
then
RHS
end
4.2. キーワード
| true | false | accumulate |
| collect | from | null |
| over | then | when |
| lock-on-active | date-effective | date-expires |
| no-loop | auto-focus | activation-group |
| agenda-group | ruleflow-group | entry-point |
| duration | package | import |
| dialect | salience | enabled |
| attributes | rule | extend |
| template | query | declare |
| function | global | eval |
| not | in | or |
| and | exists | forall |
| action | reverse | result |
| end | init |
notSomething() や accumulateSomething() のようにメソッド名に使用することが可能です。
Holiday( `when` == "july" )
rule "validate holiday by eval"
dialect "mvel"
when
h1 : Holiday( )
eval( h1.when == "july" )
then
System.out.println(h1.name + ":" + h1.when);
end
rule "validate holiday"
dialect "mvel"
when
h1 : Holiday( `when` == "july" )
then
System.out.println(h1.name + ":" + h1.when);
end
4.3. コメント
rule engine によって無視されるテキストの部分のことです。セマンティックコードブロック 内にコメントがある場合以外は、コメントが検出されると、ルールの右側のように無視されます。コメントには 単一行コメント と 複数行コメント の 2 種類があります。

図4.1 単一行コメント
# または // のいずれかを使用して単一行コメントを作成します。パーサーは行のコメント記号後の内容をすべて無視します。
rule "Testing Comments"
when
# this is a single line comment
// this is also a single line comment
eval( true ) # this is a comment in the same line of a pattern
then
// this is a comment inside a semantic code block
# this is another comment in a semantic code block
end

図4.2 複数行コメント
rule "Test Multi-line Comments"
when
/* this is a multi-line comment
in the left hand side of a rule */
eval( true )
then
/* and this is a multi-line comment
in the right hand side of a rule */
end
4.4. エラーメッセージ

図4.3 エラーメッセージの形式
- 最初のブロック
- この領域はエラーコードを特定します。
- 2 番目のブロック
- この領域には行および列の情報が表示されます。
- 3 番目のブロック
- この行行には問題を説明するテキストが表示されます。
- 4 番目のブロック
- 最初のコンテキストです。通常、エラーが発生したルール、関数、テンプレート、またはクエリを示します (このブロックは必須ではありません)。
- 5 番目のブロック
- エラーが発生したパターンを特定します (このブロックは必須ではありません)。
4.4.1. 101: No viable alternative
rule one
when
exists Foo()
exits Bar()
then
end
[ERR 101] Line 4:4 no viable alternative at input 'exits' in rule one
exits != exists であるため、実際は有効ではありません。
package org.drools;
rule
when
Object()
then
System.out.println("A RHS");
end
[ERR 101] Line 3:2 no viable alternative at input 'WHEN'
rule simple_rule
when
Student( name == "Andy )
then
end
[ERR 101] Line 0:-1 no viable alternative at input '<eof>' in rule simple_rule in pattern Student
注記
0:-1 の位置を生成する場合があります。この場合、引用符、アポストロフィ、および括弧がすべて閉じられているか確認してください。
4.4.2. 102: Mismatched input
rule simple_rule
when
foo3 : Bar(
[ERR 102] Line 0:-1 mismatched input '<eof>' expecting ')' in rule simple_rule in pattern Bar
注記
0:-1 位置は parser がソースの最後に到達したことを意味します。
package org.drools;
rule "Avoid NPE on wrong syntax"
when
not(Cheese((type=="stilton",price==10)||(type=="brie",price==15))
from $cheeseList)
then
System.out.println("OK");
end
[ERR 102] Line 5:36 mismatched input ',' expecting ')' in rule
"Avoid NPE on wrong syntax" in pattern Cheese
[ERR 101] Line 5:57 no viable alternative at input 'type' in
rule "Avoid NPE on wrong syntax"
[ERR 102] Line 5:106 mismatched input ')' expecting 'then' in
rule "Avoid NPE on wrong syntax",) を && 演算子に置き換えます。
注記
4.4.3. 103: Failed predicate
false として評価されたことを意味します。通常、これらのセマンティック述語は「ソフト」キーワードを識別するために使用されます。次の例でも同様です。
package nesting;
dialect "mvel"
import org.drools.Person
import org.drools.Address
fdsfdsfds
rule "test something"
when
p: Person( name=="Michael" )
then
p.name = "other";
System.out.println(p.name);
end
[ERR 103] Line 7:0 rule 'rule_key' failed predicate:
{(validateIdentifierKey(DroolsSoftKeywords.RULE))}? in rulefdsfdsfds は無効なテキストで、parser はそれをソフトキーワードの rule として識別できません。
注記
102: Mismatched input と大変よく似ていますが、通常はソフトキーワードが関係します。
4.4.4. 104: Trailing semi-colon not allowed
rule simple_rule
when
eval(abc();)
then
end
[ERR 104] Line 3:4 trailing semi-colon not allowed in rule simple_rule
4.4.5. 105: Early Exit
recognizer がどの代替とも一致しない文法 (grammar) のサブルールを検出すると発生します。これは、parser が出口のないブランチに入ったことを意味します。この状態を表す例は次のようになります。
template test_error
aa s 11;
end
[ERR 105] Line 2:2 required (...)+ loop did not match anything at input 'aa' in template test_error
4.5. パッケージ

図4.4 package
注記
4.5.1. import

図4.5 import
java.lang パッケージより自動的にクラスをインポートします。
4.5.2. global

図4.6 global
- ルールファイルにグローバル変数を宣言し、ルールで使用します。例は次の通りです。
global java.util.List myGlobalList; rule "Using a global" when eval( true ) then myGlobalList.add( "Hello World" ); end - ワーキングメモリーにグローバル値を設定します。ワーキングメモリーにファクトをアサートする前にすべてのグローバル値を設定することが推奨されます。例は次の通りです。
List list = new ArrayList(); WorkingMemory wm = rulebase.newStatefulSession(); wm.setGlobal( "myGlobalList", list );
4.6. 関数

図4.7 関数
helper クラス以上のことは実行できませんが (実際にコンパイラーが「背後」で helper クラスを生成します)、ロジックをすべて 1 つの場所に集められることが主な利点となります。また、必要に応じて関数を変更することもできます (これには良い面と悪い面の両方があります)。
function String hello(String name) {
return "Hello "+name+"!";
}
注記
function キーワードが使用されます。関数にとってパラメーターは通常のメソッドと同様です (また、必要でない場合はパラメーターを使用する必要はありません)。戻り値 は通常のメソッドと同様です。
Foo.hello() のように helper クラスで静的メソッドを使用することもできます。JBoss Rules では 関数インポート を使用できます。次のコード例は使用方法を表しています。
import function my.package.Foo.hello
rule "using a static function"
when
eval( true )
then
System.out.println( hello( "Bob" ) );
end
4.7. タイプ宣言

図4.8 meta_data

図4.9 type_declaration
- 新しいタイプの宣言: JBoss Rules はそのままの状態でプレーン POJO をファクトとして動作します。Java などの下位言語でモデルを作成することを懸念せずに直接モデルをルールエンジンへ定義したい時があります。また、ドメインモデルが既に構築されている状態で、主に理由付け処理中に使用される追加エンティティーを用いてこのモデルを補完したい場合や補完する必要がある場合もあります。
- メタデータの宣言: ファクトに、関連するメタ情報があることがあります。メタ情報の例には、ファクト属性によって表されず、そのファクトタイプのすべてのインスタンス間で一貫するあらゆる種類のデータが含まれます。メタ情報はエンジンによってランタイム時にクエリされ、理由付け処理で使用されることがあります。
4.7.1. 新しいタイプの宣言
例4.1 新しいファクトタイプの宣言: Address
declare Address number : int streetName : String city : String end
例4.2 新しいファクトタイプ Person の宣言
declare Person name : String dateOfBirth : java.util.Date address : Address end
java.util.Date タイプで、address は以前に定義されたファクトタイプ Address です。
例4.3 import を使用して完全修飾クラス名の使用を省略する
import java.util.Date declare Person name : String dateOfBirth : Date address : Address end
例4.4 以前の Person ファクトタイプ宣言に対して生成された Java クラス
public class Person implements Serializable { private String name; private java.util.Date dateOfBirth; private Address address; // getters and setters // equals/hashCode // toString }
例4.5 ルールで宣言されたタイプの使用
rule "Using a declared Type" when $p : Person( name == "Bob" ) then System.out.println( "The name of the person is "+ ) // lets insert Mark, that is Bob's mate Person mark = new Person(); mark.setName("Mark"); insert( mark ); end
4.7.2. メタデータの宣言
@matadata_key( metadata_value )
例4.6 任意のメタデータ属性に宣言
@author( Bob )
例4.7 ファクトタイプと属性に対するメタデータ属性の宣言
import java.util.Date declare Person @author( Bob ) @dateOfCreation( 01-Feb-2009 ) name : String @key @maxLength( 30 ) dateOfBirth : Date address : Address end
4.7.3. 既存タイプに対するメタデータの宣言
例4.8 既存のタイプに対するメタデータの宣言
import org.drools.examples.Person declare Person @author( Bob ) @dateOfCreation( 01-Feb-2009 ) end
例4.9 完全修飾クラス名を使用したメタデータの宣言
declare org.drools.examples.Person @author( Bob ) @dateOfCreation( 01-Feb-2009 ) end
4.7.4. アプリケーションコードからの宣言されたタイプへのアクセス
例4.10 org.drools.examples パッケージでのタイプの宣言
package org.drools.examples import java.util.Date declare Person name : String dateOfBirth : Date address : Address end
例4.11 API を介した宣言されたファクトタイプの処理
// get a reference to a knowledge base with a declared type:
KnowledgeBase kbase = ...
// get the declared FactType
FactType personType = kbase.getFactType( "org.drools.examples",
"Person" );
// handle the type as necessary:
// create instances:
Object bob = personType.newInstance();
// set attributes values
personType.set( bob,
"name",
"Bob" );
personType.set( bob,
"age",
42 );
// insert fact into a session
StatefulKnowledgeSession ksession = ...
ksession.insert( bob );
ksession.fireAllRules();
// read attributes
String name = personType.get( bob, "name" );
int age = personType.get( bob, "age" );
4.8. ルール

図4.10 ルール
例4.12 ルール構文の概要
rule "<name>"
<attribute>*
when
<conditional element>*
then
<action>*
end例4.13 単純なルール
rule "Approve if not rejected"
salience -100
agenda-group "approval"
when
not Rejection()
p : Policy(approved == false, policyState:status)
exists Driver(age > 25)
Process(status == policyState)
then
log("APPROVED: due to no objections.");
p.setApproved(true);
end注記
4.8.1. ルール属性
Ruleflow など複雑なサブシステムの一部であるものもあります。JBoss Rules を活用するには、本項を読んで各属性をよく理解するとよいでしょう。

図4.11 ルール属性
表4.1 ルール属性
| 属性 | デフォルト値 | タイプ | コメント |
|---|---|---|---|
| no-loop |
false
| ブール値 |
ルールの結果がファクトを変更すると、ルールが再度アクティベートされることがあり、再帰が行われる原因となります。no-loop を
true に設定して、現在のデータセットに対する activation の作成が無視されるようにします。
|
| ruleflow-group |
N/A
| 文字列 |
ルールフロー 機能を使用して、ルールの実行を制御します (同じ ruleflow-group 識別子によってアセンブルされたルールはグループがアクティブな時のみ実行されます)。
|
| lock-on-active |
false
| ブール値 |
uleflow-group がアクティブになるか、agenda-group がフォーカスを受けると、lock-on-active が true に設定されたそのグループ内のルールがアクティブでなくなります。更新元に関係なく、一致するルールのアクティベーションは破棄されます。これは、変更がルール自体のみによって引き起こされるわけではないため、no-loop の強いバージョンになります。これは、ファクトを変更する複数のルールが存在し、ルールの再照合や再実行を希望しない場合の計算ルールに最適です。ruleflow-group がアクティブでなくなるか、agenda-group がフォーカスを失った時のみ、lock-on-active が true に設定されたルールがアジェンダに配置されるアクティベーションに対して再び有効になります。
|
| salience |
0
| 整数 |
各ルールは整数を割り当てることができる salience 属性を持ちます。デフォルトはゼロです。salience は優先度の形式で、
activation queue で順序付けされる時に大きい値を持つルールに高い優先度が割り当てられます。
|
| agenda-group |
MAIN
| 文字列 |
アジェンダグループを用いるとアジェンダを分割できるため、実行制御を強化できます。フォーカスを取得したアジェンダグループのルールのみ実行が許可されます。
|
| auto-focus |
false
| ブール値 | auto-focus 値が true のルールがアクティベートされた場合や、ルールの agenda グループがフォーカスを取得していない場合、フォーカスがグループに与えられ、ルールを実行できるようになります。
|
| activation-group |
N/A
| 文字列 |
この文字列は同じ
activation グループに属するルールを識別します。このようなグループのルールはお互いを排他的に実行します。つまり、実行する activation グループの最初のルールは他のルールのアクティベーションをキャンセルし、実行を阻止します。
注記
これは以前 Xor グループと呼ばれていましたが、厳密には Xor の定義を満たしていません。
|
| dialect |
パッケージの指定通り
|
文字列で、可能な値は
java と mvel
|
この属性を使用して、左側または右側のいずれかにあるコード式に使用される言語を指定します。現在、Java と MVFLEX 式言語 の 2 つのダイアレクトを使用できます (ダイアレクトはパッケージレベルで指定できますが、この属性を使用すると、ルールのパッケージ定義を上書きできます)。
|
| date-effective |
N/A
|
日付と時間の定義が含まれる文字列
|
この属性で設定されたタイムスタンプよりも現在の日時が後であることを示す場合にのみルールをアクティベートできます。
|
| date-expires |
N/A
|
日付と時間の定義が含まれる文字列
|
この属性で設定されたタイムスタンプよりも現在の日時が後であるとルールをアクティベートできません。
|
| duration |
デフォルト値なし
|
long
|
この属性を使用すると、指定された期間後に属性が
true のままであった場合にルールが実行されます。
|
rule "my rule" salience 42 agenda-group "number 1" when ...
4.8.2. タイマーとカレンダー
例4.14 timer 属性の使用例
timer ( int: <initial delay> <repeat interval>? ) timer ( int: 30s ) timer ( int: 30s 5m ) timer ( cron: <cron expression> ) timer ( cron:* 0/15 * * * ? )
例4.15 cron の例
rule "Send SMS every 15 minutes"
timer (cron:* 0/15 * * * ?)
when
$a : Alarm( on == true )
then
channels[ "sms" ].insert( new Sms( $a.mobileNumber, "The alarm is still on" ));
end例4.16 クオーツ式カレンダーの適応
Calendar weekDayCal = QuartzHelper.quartzCalendarAdapter(org.quartz.Calendar quartzCal)
例4.17 カレンダーの登録
ksession.getCalendars().set( "week day", weekDayCal );
例4.18 カレンダーとタイマーを一緒に使用
rule "weekdays are high priority"
calendars "weekday"
timer (int:0 1h)
when
Alarm()
then
send( "priority high - we have an alarm” );
end
rule "weekend are low priority"
calendars "weekend"
timer (int:0 4h)
when
Alarm()
then
send( "priority low - we have an alarm” );
end4.8.3. 左側条件要素
eval(true) と書き換えられます。そのため、ルールの条件は常に true のままになります)。
working memory セッションが作成された時に一度だけアクティベートされます。

図4.12 左側
conditional element のないルールになります。
rule "no CEs"
when
then
<action>*
end
rule "no CEs"
when
eval( true )
then
<action>*
end
and で、ルールの左側に複数の完全に無関係なパターンがある場合に暗示されます。
注記
or パターンとは異なり、and は 主導宣言バインディング を持つことができません。これは、宣言は 1 つのファクトへのみ参照でき、 and が満たされると複数のファクトを照合するため、どのファクトへバインドするのか分からなくなるためです。
4.8.3.1. パターン
conditional element の最も重要な型です。以下のエンティティー関係図はパターンの制約を構成するさまざまな部分の概要と、これらの部分がどのように連携するかを表しています。各部分の詳細は、図やコード例と共に本項の後半で取り上げます。

図4.13 パターンエンティティー関係図

図4.14 パターン
Cheese であるため、パターンは working memory の各 Cheese オブジェクトに対して照合されます。
注記
Cheese( )
$c などのパターンバインディング変数を使用して一致するオブジェクトを参照します。$ プレフィックスの使用は任意ですが、このプレフィックスを使用すると変数をフィールドを区別しやすくなるため、複雑なルールに対処するする場合に便利です。
$c : Cheese( )
,、&&、|| のいずれかを使用して制約を区切ります。ただし、これらの記号は若干ことなる機能を持つことに注意してください。

図4.15 制約

図4.16 制約

図4.17 constraintGroup
,) は制約グループを区切るために使用されます。これは暗黙的で接続的な意味論を持ちます。
# Cheese type is stilton and price < 10 and age is mature. Cheese( type == "stilton", price < 10, age == "mature" )
type == "stilton"の通り、型は stilton です。price < 10の通り、価格 (price) は 10 未満です。age == "mature"の通り、熟成度 (age) は mature です。
&& と || を用いると、グループは複数の制約を持つことができます。例は次の通りです。
// Cheese type is "stilton" and price < 10, and age is mature Cheese( type == "stilton" && price < 10, age == "mature" ) // Cheese type is "stilton" or price < 10, and age is mature Cheese( type == "stilton" || price < 10, age == "mature" )
- &&
- ||
- ,
# Cheese type is stilton and ( price is less than 20 or age is mature ). Cheese( type == "stilton" && ( price < 20 || age == "mature" ) )
|| は && の前に評価されるようになります。
&& と , は同じ意味論を持ちますが、異なる優先度で解決されることに注意してください。そのため、複合制約式に , を組み込むことはできません。
// invalid as ',' cannot be embedded in an expression: Cheese( ( type == "stilton", price < 10 ) || age == "mature" ) // valid as '&&' can be embedded in an expression: Cheese( ( type == "stilton" && price < 10 ) || age == "mature")
4.8.3.1.1. フィールド制約

図4.18 fieldConstraint
- 単一値制限
- 複合値制限
- 複数制限

図4.19 制限
4.8.3.1.2. ファクトとしての Java Bean
getXXX メソッドか isXXX メソッドを使用してフィールドが公開されます。この場合、これらのメソッドは引数を取りませんが、何らかの値を返します。
getType は type としてアクセスされ、JBoss Rules は標準的な Java Development Kit の Introspector クラスを使用してこのマッピング処理を実行します)。
Cheese クラスの例では、Cheese(type == "brie") パターンは getType() メソッドを Cheese インスタンスへ適用します。フィールド名が見つからない場合、コンパイラーは最終的に引数のないメソッドとして名前を使用します。そのため、Cheese(toString == "cheddar") 制約により toString() が呼び出されます。この場合、大文字と小文字を正しく区別したメソッドの完全名を使用しますが、括弧は省略します。必ず、アクセスされるメソッドがパラメーターを取らず、これらのメソッドはルールに影響する方法でオブジェクトの状態を変更しない accessors であるようにしてください (パフォーマンス上の理由で、呼び出し間の照合の結果を rule engine がキャッシュすることに注意してください)。
4.8.3.1.3. 値
- リテラル
- qualifiedIdentifiers (列挙値)
- 変数
- returnValues

図4.20 リテラル

図4.21 qualifiedIdentifier

図4.22 変数

図4.23 returnValue
== と != を使用します。リテラルの null キーワード (Cheese(type != null) のように使用) は、evaluator が例外をスローしない場合、値が null であると true を返します。
evaluator に ten を文字列として提供すると、例外が発生しますが、10 を提供すると数値の 10 に強制します (強制は常に値型ではなくフィールド型を優先します)。
4.8.3.1.4. 単一値制限

図4.24 singleValueRestriction
4.8.3.1.5. 演算子

図4.25 演算子
== および != 演算子はすべての型で有効です。型値が順序付けされている時は他のリレーショナル演算子を使用することもできます。データフィールドでは < は 「前」を意味します。 matches と not matches のペアは文字列フィールドへのみ適用し、contains と not contains には Collection 型のフィールドが必要になります (evaluator とフィールドに対して正しい値を強制しようとします)。
matches演算子- これは有効な Java 正規表現があるフィールドと一致します。正規表現は通常は文字列リテラルですが、有効な正規表現を解決する変数を使用することもできます。
重要
Java とは異なり、文字列リテラルとして書かれる正規表現内にエスケープは必要ありません。Cheese( type matches "(Buffalo)?\S*Mozarella" )
not matches演算子- この演算子は文字列が正規表現と一致しない時に
trueを返します。matches演算子でも同じルールが適用されます。使用例は次の通りです。Cheese( type not matches "(Buffulo)?\S*Mozerella" )
contains演算子- この演算子を使用して、 collection または array フィールドに指定された値が含まれていることを確認します。
CheeseCounter( cheeses contains "stilton" ) // contains with a String literal CheeseCounter( cheeses contains $var ) // contains with a variable
not contains演算子- この演算子を使用して、collection または array フィールドに指定された値がないことを確認します。
CheeseCounter( cheeses not contains "cheddar" ) // not contains with a String literal CheeseCounter( cheeses not contains $var ) // not contains with a variable
注記
後方互換性を維持するため、excludes演算子もサポートされています。この演算子はnot containsと同等です。 memberOf演算子- この演算子を使用してフィールドが collection または array のメンバーであるかどうかを確認します。コレクションは変数である必要があります。
CheeseCounter( cheese memberOf $matureCheeses )
not memberOf演算子- この演算子を使用して、フィールドが collection または array のメンバーでないかどうかを確認します。コレクションは変数である必要があります。
CheeseCounter( cheese not memberOf $matureCheeses )
soundslike演算子- この演算子は
matchesと似ていますが、言葉が指定値と似た発音であるかどうかを確認します。Soundex アルゴリズムを使用し、英語の発音を基にして言葉の発音を確認します。// match cheese "fubar" or "foobar" Cheese( name soundslike 'foobar' )
4.8.3.1.6. リテラル制限

図4.26 literalRestriction
- 数値
- 標準的な Java 数値プリミティブがすべてサポートされます。
Cheese( quantity == 5 )
- 日付
- デフォルトの日付形式は
dd-mmm-yyyyです。これを変更するには、drools.dateformat プロパティーの別の日付形式マスクを提供します (制御を強化する必要がある場合は、inline-eval制約を使用します)。Cheese( bestBefore < "27-Oct-2013" )
- 文字列
- 有効な Java 文字列を使用します。
Cheese( type == "stilton" )
- ブール値
trueかfalseのみを使用できます。0 と 1 は使用できません。単一のブール値フィールド (例Cheese( smelly )) は許可されません。ブール値リテラルの比較対象となる必要があります。Cheese( smelly == true )
- 修飾識別子
- Java Development Kit 1.4 および 1.5 形式の
enumsはサポートされますが、1.5 は JDK 5 の環境でのみ使用可能です。Cheese( smelly == SomeClass.TRUE )
4.8.3.1.7. バインドされた変数制限

図4.27 variableRestriction
fied constraints で使用することが可能です。バインドされた変数は 宣言 と呼ばれます。制約されるフィールドの型によって有効な演算子が決定されます。可能な場合、強制が試行されます。パフォーマンスを高速化するには、== 演算子を使用して変数制限をバインドします。
Person( likes : favouriteCheese ) Cheese( type == likes )
Person インスタンスの favouriteCheese フィールドへ likes 変数がバインドされます (次のパターンで Cheese の型を制約します)。有効な Java 変数名を使用することができ、フィールドと宣言を区別するために使用される $ プレフィックスを使用できます。
$stilton の宣言を表しています。contains 演算子と共に使用されます (任意の $ の使用方法に注目してください)。
$stilton : Cheese( type == "stilton" ) Cheesery( cheeses contains $stilton )
4.8.3.1.8. 戻り値制限

図4.28 returnValueRestriction
Person( girlAge : age, sex == "F" ) Person( age == ( girlAge + 2) ), sex == 'M' )
4.8.3.1.9. 複合値制限
in および not in evaluators のみがサポートしています)。
evaluators は実際は「シンタクティックシュガー」で、!= および == 演算子を使用して複数制限のリストとして内部で書き直されます。

図4.29 compoundValueRestriction
Person( $cheese : favouriteCheese ) Cheese( type in ( "stilton", "cheddar", $cheese )
4.8.3.1.10. 複数制限
&& または || 分離記号を使用)。括弧を用いたグループ化は許可され、結果的に再帰的な構文パターンになります。

図4.30 multiRestriction

図4.31 restrictionGroup
// Simple multi restriction using a single &&
Person( age > 30 && < 40 )
// Complex multi restriction using groupings of multi restrictions
Person( age ( (> 30 && < 40) ||
(> 20 && < 25) ) )
// Mixing muti restrictions with constraint connectives
Person( age > 30 && < 40 || location == "london" )
4.8.3.1.11. インライン評価制約

図4.32 インライン評価式
注記
age 変数は auto-vivification によって 2 つ目のパターンで自動作成されます。
Person( girlAge : age, sex = "F" ) Person( eval( age == girlAge + 2 ), sex = 'M' )
4.8.3.1.12. ネストされたアクセサー
inline-eval 制約として書き換えられます)。
警告
working memory はネストされた値を認識せず、ネストされた値がいつ変更するかも認識しないため、nested accessors を使用する時は十分注意してください。親参照が working memory に存在する間は常に不変の値として見なしてください。
modify とその ブロックセッター を使用し、ネストされたアクセサーの割り当てを書くことができます。この場合、ルートの親オブジェクトを必要に応じて取り消したり挿入したりすることができます (Nested accessors は演算子記号のどちらの側でも使用できます)。
// Find a pet older than its owners first-born child $p : Person( ) Pet( owner == $p, age > $p.children[0].age )
inline eval として書き換えられます。
// Find a pet older than its owners first-born child $p : Person( ) Pet( owner == $p, eval( age > $p.children[0].age ) )
警告
nested accessors は direct field accesses よりもパフォーマンスへの影響がかなり大きいため、注意して使用してください。
4.8.3.2. and 条件要素
and conditional element を使用して他の conditional elements を論理積にグループ化します。
root element は暗黙的な and プレフィックスです。これは指定する必要はありません。JBoss Rules は and をプレフィックスとインフィックスの両方としてサポートしますが、暗黙的なグループ化により混乱を避けることができるため、プレフィックスの使用が推奨されます。

図4.33 prefixAnd
(and Cheese( cheeseType : type ) Person( favouriteCheese == cheeseType ) )
when Cheese( cheeseType : type ) Person( favouriteCheese == cheeseType )
and インフィックスと括弧による明示的なグループ化がサポートされます。
注記
and の代わりに && 記号を使用できます。この記号は廃止されましたが、レガシーサポートの理由で現在でも使用可能です。

図4.34 infixAnd
//infixAnd Cheese( cheeseType : type ) and Person( favouriteCheese == cheeseType ) //infixAnd with grouping ( Cheese( cheeseType : type ) and ( Person( favouriteCheese == cheeseType ) or Person( favouriteCheese == cheeseType ) )
4.8.3.3. or 条件要素
or conditional element を使用して他の conditional element を論理和にグループ化します。
注記
or をプレフィックスまたはインフィックスとして使用できますが、暗黙的なグループ化により混乱を避けることができるため、プレフィックスの使用が推奨されます。
conditional element の挙動は、フィールド制約や制限に対する結合記号 || とは異なります。engine は実際には or を理解しません。複数の異なる論理変換を使用することにより、or を使用するルールは複数のサブルールとして書き換えられます。この結果、ルールは単一の root node である or と、各 conditional elements に対する 1 つのサブルールを持ちます。各サブルールは通常のルールと同様にアクティベートおよび実行できます。特別な挙動や対話はありませんが、それが新しい開発者が混乱する原因となることがあります。

図4.35 prefixOr
(or Person( sex == "f", age > 60 ) Person( sex == "m", age > 65 )
or インフィックスと括弧による暗示的なグループ化がサポートされます。
注記
or の代わりに || 記号を使用できます。この記号は廃止されましたが、レガシーサポートの理由で現在でも使用可能です。

図4.36 infixOr
//infixOr Cheese( cheeseType : type ) or Person( favouriteCheese == cheeseType ) //infixOr with grouping ( Cheese( cheeseType : type ) or ( Person( favouriteCheese == cheeseType ) and Person( favouriteCheese == cheeseType ) )
or を用いて pattern binding を使用することもできます。これは、結果となる各サブルールがパターンにバインドされることを意味します。次の例のように、eponymous 変数を使用して各パターンを別々にバインドする必要があります。
(or pensioner : Person( sex == "f", age > 60 ) pensioner : Person( sex == "m", age > 65 ) )
or を使用すると、可能な結果ごとに 1 つサブルールが作成されます。上記の単純な例では、2 つのルールが生成されます。これらのルールは working memory 内で独立して機能するため、両方とも照合、アクティベートおよび実行することが可能です。ショートカットはありません。
or を 2 つ以上の同様のルールを生成する方法と考えると分かりやすいかもしれません。2 つ以上の項の論理和が true の場合、単一のルールが複数のアクティベーションを持つことがあります。
4.8.3.4. eval 条件要素

図4.37 eval
eval conditional element はプリミティブなブール値を返す意味論コードを実行できるようにする「catch-all」です。このコードはルールの左側にバインドされた変数か、ルールパッケージの関数を参照できます。
警告
eval を使用しすぎないようにしてください。ルールの宣言性が低下し、engine のパフォーマンスが低下することがあります。eval はパターンのどこにでも使用できますが、ルールの左側で最後の条件要素として追加することが推奨されます。
Evals に索引を付けることはできません。そのため、フィールド制約 ほど効率的ではありません。しかし、時間とともに変化する値を返す関数として使用することに適しています (フィールド制約にはこの機能はありません)。
p1 : Parameter() p2 : Parameter() eval( p1.getList().containsKey(p2.getItem()) ) // call function isValid in the LHS eval( isValid(p1, p2) )
4.8.3.5. not 条件要素

図4.38 not
not conditional element は、1 階論理の 非存在数量詞 (non-existential quantifier) です。この目的は、working memory に存在しないものを確認することです。
not キーワードは、括弧で囲まれた conditional element の前に指定する必要があります (最も単純なユースケースでは、括弧を省略することが可能です)。
not Bus()
// Brackets are optional: not Bus(color == "red") // Brackets are optional: not ( Bus(color == "red", number == 42) ) // "not" with nested infix and - two patterns, // brackets are requires: not ( Bus(color == "red") and Bus(color == "blue") )
4.8.3.6. exists 条件要素

図4.39 exists
exists conditional element は 1 階論理 の 存在数量詞 (existential quantifier) です。この目的は、working memory に存在するものを確認することです。exists を「少なくとも 1 つ存在する」という意味で考えるプログラマーもいます (「それぞれに対する」のように独自のパターンを持つこととは異なります)。
exists が使用されると、その条件と一致する working memory のデータの量に関係なく、ルールは一度だけアクティベートされます。存在のみが非常に重要となるため、バインディングは確立されません。
exists キーワードは、適用する conditional elements の前に指定する必要があります。conditional elements は括弧で囲まれている必要があります (以下のような最も単純な単一のパターンでは、括弧を省略することもできます)。
exists Bus()
exists Bus(color == "red") // brackets are optional: exists ( Bus(color == "red", number == 42) ) // "exists" with nested infix and, // brackets are required: exists ( Bus(color == "red") and Bus(color == "blue") )
4.8.3.7. conditional elements

図4.40 forall
forall conditional element は JBoss Rules の 1 階論理のサポートを完全にします。最初のパターンに一致するすべてのファクトが、残りの各パターンにも一致する場合、forall は true として評価します。例は次の通りです。
rule "All English buses are red"
when
forall( $bus : Bus( type == 'english')
Bus( this == $bus, color = 'red' ) )
then
# all english buses are red
end
english の各 Bus オブジェクトを選択します。そして、このパターンに一致する各ファクトに対し、後続のパターンが評価されます。後続のパターンにも一致する場合、forall conditional element は true として評価します。
rule "All Buses are Red"
when
forall( Bus( color == 'red' ) )
then
# all asserted Bus facts are red
end
rule "all employees have health and dental care programs"
when
forall( $emp : Employee()
HealthCare( employee == $emp )
DentalCare( employee == $emp )
)
then
# all employees have health and dental care
end
not などの他の conditional elements 内にネストできます。
重要
rule "not all employees have health and dental care"
when
not ( forall( $emp : Employee()
HealthCare( employee == $emp )
DentalCare( employee == $emp ) )
)
then
# not all employees have health and dental care
end
注記
not( forall( p1 p2 p3...)) は次のコードと同等です。
not(p1 and not(and p2 p3...))
重要
forall は 範囲の区切り文字 であることに注意してください。そのため、以前にバインドされた変数を使用できますが、内部でバインドされた変数は外部で使用することはできません。
4.8.3.8. from 条件要素

図4.41 from
from conditional element を使用して、左側のパターンによって一致されるデータの任意のソースを指定します。これを行うことにより、 engine が working memory で発見されなかったデータを推論できます。データソースはバインドされた変数上のサブフィールドや、メソッド呼び出しの結果であることがあります。
rule "validate zipcode"
when
Person( $personAddress : address )
Address( zipcode == "23920W") from $personAddress
then
# zip code is ok
end
rule "validate zipcode"
when
$p : Person( )
$a : Address( zipcode == "23920W") from $p.address
then
# zip code is ok
end
from を使用してオブジェクトのコレクションを返すこともできます。この場合、from がコレクションの各オブジェクトを繰り返し、個別に照合しようとします。以下は、注文の各商品を 10 % 割引するためのルールが含まれる例になります。
rule "apply 10% discount to all items over $ 100,00 in an order"
when
$order : Order()
$item : OrderItem( value > 100 ) from $order.items
then
# apply discount to $item
end
from を lock-on-active ルール属性と共に使用する場合、予期しない結果がもたらされることがあるため特に注意してください。次のように若干変更が加えられている前述の例を考慮してください。
rule "Assign people in Queensland (QLD) to sales region 1"
ruleflow-group "test"
lock-on-active true
when
$p : Person( )
$a : Address( state == "QLD") from $p.address
then
modify ($p) {} #Assign person to sales region 1 in a modify block
end
rule "Apply a discount to people in the city of Brisbane"
ruleflow-group "test"
lock-on-active true
when
$p : Person( )
$a : Address( city == "Brisbane") from $p.address
then
modify ($p) {} #Apply discount to person in a modify block
end
from を使用すると目的に関係なく、評価される度に新しいファクトが返されます)。
- 前述のパターンを使用する必要性について検討します。これは異なるルールフローグループ全体で多くのルールが存在するためです。ルールが
working memoryおよび該当するルールフローのダウンストリームにある他のルールを変更し、必要性を再評価しなければならない場合、modifyを使用することが重要となります。しかし、同じルールフローグループの他のルールがお互いにアクティベーションを再帰的に配置しないようにしてください。この場合、ルールがルール自体を再帰的にアクティベートできないようになるため、no-loop 属性では効果がありません。したがって、lock-on-active を使用してください。 - この問題に対応する方法はいくつかあります。
- すべてのファクトを
working memoryにアサートできる場合にfromを使用しないようにするか、制約式でネストされたオブジェクト参照を使用します (下記を参照)。 modify blockで使用するために割り当てられた変数を、左側の条件の最後にある文として配置します。- 同じルールフローグループ内のルールがお互いにアクティベーションを配置する方法を明示的に管理できる場合、lock-on-active を使用しないようにします。
この中で推奨される解決法は、working memoryへすべてのファクトを直接アサートできる場合にfromの使用を最小限に抑える方法になります。上記の例では、PersonおよびAddressインスタンスの両方をworking memoryにアサートすることが可能です。グラフは比較的単純であるため、次のようにルールを変更するとさらに簡単です。rule "Assign people in Queensland (QLD) to sales region 1" ruleflow-group "test" lock-on-active true when $p : Person(address.state == "QLD" ) then modify ($p) {} #Assign person to sales region 1 in a modify block end rule "Apply a discount to people in the city of Brisbane" ruleflow-group "test" lock-on-active true when $p : Person(address.city == "Brisbane" ) then modify ($p) {} #Apply discount to person in a modify block end - 両方のルールは想定通り実行されるようになりますが、この方法で常にネストされたファクトへアクセスできるわけではありません。
Personが 1 つまたは複数のAddressesを保持し、存在数量詞 を使用して特定条件を満たす住所を最低でも 1 つ person に一致させたい例を考えてみましょう。この場合、コレクションを推論するためfromを使用する必要があります。これを実現する方法は複数ありますが、すべてで lock-on-active 使用時の問題が発生するわけではありません。たとえば、次のようにfromを使用すると、両方のルールが想定通りに実行されます。rule "Assign people in Queensland (QLD) to sales region 1" ruleflow-group "test" lock-on-active true when $p : Person($addresses : addresses) exists (Address(state == "QLD") from $addresses) then modify ($p) {} #Assign person to sales region 1 in a modify block end rule "Apply a discount to people in the city of Brisbane" ruleflow-group "test" lock-on-active true when $p : Person($addresses : addresses) exists (Address(city == "Brisbane") from $addresses) then modify ($p) {} #Apply discount to person in a modify block endしかし、若干異なる方法では問題が発生します。rule "Assign people in Queensland (QLD) to sales region 1" ruleflow-group "test" lock-on-active true when $assessment : Assessment() $p : Person() $addresses : List() from $p.addresses exists (Address( state == "QLD") from $addresses) then modify ($assessment) {} #Modify assessment in a modify block end rule "Apply a discount to people in the city of Brisbane" ruleflow-group "test" lock-on-active true when $assessment : Assessment() $p : Person() $addresses : List() from $p.addresses exists (Address( city == "Brisbane") from $addresses) then modify ($assessment) {} #Modify assessment in a modify block endこの場合、fromを使用すると $addresses が返されます。またこの例では、可能な解決法を示す新しいオブジェクトassessmentが導入されます。$addresses 変数が移動され、各ルールの最後の条件となると、両方のルールは想定通り実行されます。上記の例は、ルールのアクティベーションを損失せずにfromを lock-on-active と組み合わせて使用する方法を表していますが、左側の条件の配置順序に依存するという欠点があります。またこれにより、問題を起こす可能性のある条件を追跡する必要があるため、ルールの作成が極めて複雑になります。 - 代わりに、ファクトをさらに
working memoryへアサートする方がよいでしょう。この場合、人の住所がworking memoryへアサートされると、fromを使用する必要がなくなります。
注記
working memory にアサートするのは現実的ではなく、他の方法を使用する必要がある場合があります。
working memory が変更された時にお互いを再帰的にアクティベートしないようにする条件が各ルールに含まれるようにして、同じルールフローグループ内のルールがお互いをアクティベートするよう、直接管理する方法もあります。たとえば、前述の例の場合、割引が既に適用されているか確認し、適用されている場合はルールがアクティベートしないようにする条件をルールに追加できます。
4.8.3.9. collect 条件要素

図4.42 collect
collect conditional element を使用して、指定のソースまたは working memory のいずれかより取得されたオブジェクトのコレクションをルールが推定するようにします。
注記
import java.util.ArrayList
rule "Raise priority if system has more than 3 pending alarms"
when
$system : System()
$alarms : ArrayList( size >= 3 )
from collect( Alarm( system == $system, status == 'pending' ) )
then
# Raise priority, because system $system has
# 3 or more alarms pending. The pending alarms
# are $alarms.
end
working memory で該当する各システムの保留のアラームを探し、ArrayLists でグループ化します。1 つのシステムに対して 3 つ以上のアラームが見つかった場合にルールが実行されます。
collect の結果パターンは、 java.util.Collection インターフェースを実装し、引数のないデフォルトの公開コンストラクターを提供する「具体的」なクラスになります。これは java.util.Collection を実装し、これらの条件を満たす限り、ArrayList、LinkedList、HashSet、またはカスタムクラスなどの Java 使用できることを意味します。
注記
collect conditional element の前にバインドされた変数は、ソースと結果パターン両方のスコープ内にあります。このような変数を使用してこれらのパターンを制約します。しかし、collect はバインディングの scope delimiter であるため、内部のバインディングは外部では使用できません。
collect はネストされた from conditional elements を許可します。そのため、以下は collect の有効な使用例になります。
import java.util.LinkedList;
rule "Send a message to all mothers"
when
$town : Town( name == 'Paris' )
$mothers : LinkedList()
from collect(
Person( gender == 'F', children > 0 )
from $town.getPeople()
)
then
# send a message to all mothers
end
4.8.3.10. accumulate 条件要素

図4.43 accumulate
accumulate conditional element は collect よりも柔軟で強力な条件要素です。ルールはオブジェクトのコレクションにまたがって繰り返しでき、各要素に対してカスタムのアクションを実行できます。終了すると結果オブジェクトを返します。
accumulate conditional element の一般的な構文は次の通りです。
<result pattern> from accumulate(<source pattern>,
init( <init code> ),
action( <action code> ),
reverse( <reverse code> ),
result( <result expression> ) )
<source pattern>:engineが各ソースオブジェクトとの一致を試みる通常のパターンです。<init code>: 選択されたダイアレクトのコードにある意味論ブロックです。ソースオブジェクトで繰り返しする前に各タプルに対して一度実行されます。<action code>: 各ソースオブジェクトに対して実行される選択されたオブジェクトにあるコードの意味論ブロックです。<reverse code>: 選択されたダイアレクトにあるコードの任意の意味論ブロックです。存在する場合、ソースパターンと一致しなくなった各ソースオブジェクトに対して実行されます。ソースオブジェクトが変更または取り消された時にengineが減分計算を行えるよう、<action code>ブロックで実行された計算を元に戻すことが目的です。このような操作のパフォーマンスを大幅に向上します。<result expression>: すべてのソースオブジェクトが繰り返し処理された後に実行される選択されたダイアレクトの意味論の式です。<result pattern>:engineが<result expression>から返されたオブジェクトと一致しようとする通常のパターンです。一致する場合、accumulate条件要素がtrueとして評価し、engineはルールにある次の条件要素を評価します。一致しない場合、accumulate条件要素はfalseとして評価し、ルールの条件要素を評価することを停止します。
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
engineはworking memoryの各orderに対する init のコード を実行します。これは、 total 変数をゼロに初期化します。- 次に、該当する注文に対する
OrderItemオブジェクトすべてで繰り返し処理を行い、各オブジェクトに対してアクションを実行します (この場合、全商品の合計値が計算され、total変数に置かれます)。 result expressionに対応する値が返されます (total変数の値)。- エンジンは結果を
Numberパターンに一致しようとします。double の値が 100 よりも大きい場合にルールが実行されます。
重要
init、action、および reverse コードブロックではセミコロンをステートメントの区切り文字として使用しなければならないことに注意してください。結果は式であり、; は許可されません。他のダイアレクトを使用する場合は、常に特定の構文に従ってください。
重要
reverse code は任意ですが、update および retract の使用時にパフォーマンスが向上するため、Red Hat は reverse code の使用を強く推奨します。
accumulate conditional element を使用してソースオブジェクト上のアクションを実行します。次項の例は、カスタムオブジェクトをインスタンス化し、値を投入します。
4.8.3.10.1. Accumulate 関数
accumulate conditional element は非常に強力な CE ですが、累積関数 と呼ばれる事前定義された関数を使用する場合、特に簡単に使用することができます。累積関数は accumulate とほぼ同様に動作しますが、accumulate conditional element すべてにカスタムコードを明示的に記述する代わりに、一般的な操作に対して事前定義されたコードを使用できることが異なります。
accumulate 関数を用いて、次のように注文に割引を適用するルールをプログラミングできることを表しています。
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 は accumulate 関数です。名前の通り、各 OrderItem の $value の合計を計算し、結果を返します。
accumulate 関数が内蔵されています。
averageminmaxcountsum
average 関数を使用してルールを記述します。
rule "Average profit"
when
$order : Order()
$profit : Number()
from accumulate( OrderItem( order == $order, $cost : cost, $price : price )
average( 1 - $cost / $price ) )
then
# average profit for $order is $profit
end
accumulate 関数はすべて プラグ可能 です。そのため、必要な場合はカスタマイズされたドメイン固有の関数を比較的簡単に engine へ追加することが可能です。追加後、ルールは制限なしでこれらの関数を使用できます。新しい accumulate 関数を実装するには、以下の手順に従います。
org.drools.base.acumulators.AccumulateFunctionインターフェースを実装する Java クラスを作成します。- 設定ファイルに行を追加するか、システムプロパティーを設定し、
engineに新しい関数について知らせます。次の例は、average関数の実装を示しています。/* * Copyright 2007 JBoss Inc * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Created on Jun 21, 2007 */ package org.drools.base.accumulators; /** * An implementation of an accumulator capable of calculating average values * * @author etirelli * */ public class AverageAccumulateFunction implements AccumulateFunction { protected static class AverageData { public int count = 0; public double total = 0; } /* (non-Javadoc) * @see org.drools.base.accumulators.AccumulateFunction#createContext() */ public Object createContext() { return new AverageData(); } /* (non-Javadoc) * @see org.drools.base.accumulators.AccumulateFunction#init(java.lang.Object) */ public void init(Object context) throws Exception { AverageData data = (AverageData) context; data.count = 0; data.total = 0; } /* (non-Javadoc) * @see org.drools.base.accumulators.AccumulateFunction#accumulate(java.lang.Object, * java.lang.Object) */ public void accumulate(Object context, Object value) { AverageData data = (AverageData) context; data.count++; data.total += ((Number) value).doubleValue(); } /* (non-Javadoc) * @see org.drools.base.accumulators.AccumulateFunction#reverse(java.lang.Object, * java.lang.Object) */ public void reverse(Object context, Object value) throws Exception { AverageData data = (AverageData) context; data.count--; data.total -= ((Number) value).doubleValue(); } /* (non-Javadoc) * @see org.drools.base.accumulators.AccumulateFunction#getResult(java.lang.Object) */ public Object getResult(Object context) throws Exception { AverageData data = (AverageData) context; return new Double( data.count == 0 ? 0 : data.total / data.count ); } /* (non-Javadoc) * @see org.drools.base.accumulators.AccumulateFunction#supportsReverse() */ public boolean supportsReverse() { return true; } }統合の作業はすべてengineによって実行されるため、コードは非常に簡単です。 - 機能を
engineへプラグするには、configurationファイルへ追加します。drools.accumulate.function.average = org.drools.base.accumulators.AverageAccumulateFunction重要
必ずdrools.accumulate.function.プレフィックスを使用してください。averageは関数がルールファイルでどのように使用されるかを制御します。org.drools.base.accumulators.AverageAccumulateFunctionは関数の動作を実装するクラスの完全修飾名です。
4.8.4. 右側
4.8.4.1. 使用法
重要
working memory のデータを挿入、取り消し、または変更します。これを行うには、最初に working memory を参照せずに working memory を変更する次の 便宜的なメソッド (convenience mehtod) を利用します。
update(object, handle)を使用して、オブジェクト (左側にあるものにバインドされている) が変更されたため、ルールの「再検討」が必要になる可能性があることをengineに伝えます。update(object)を使用して、ナレッジヘルパー が必要なfacthandleをルックアップするようにします。これは、渡されたオブジェクトの ID チェックを使用して行われます (Java bean にproperty change listenersを提供する場合、engineに挿入するため、オブジェクト変更時にupdate()を呼び出す必要はありません)。insert(new Something ())を使用して、新しく作成されたオブジェクトをworking memoryに置きます。insertLogical(new Something())はinsertと似ていますが、現在実行されているルールの真実をサポートするファクトがこれ以上存在しない場合に、オブジェクトが自動的に取り消される点が異なります。retract(handle)を使用してworking memoryからオブジェクトを削除します。
convenience methods は、実際には Knowledge Helper インスタンスへショートカットを提供するマクロにすぎません。これを行うことにより、rules ファイルから working memory へのアクセスが可能になります。
drools.halt()を使用して、ルールの実行を即座に終了します。これは、現在のセッションがfireUntilHalt()で開始された時点へ制御を戻すために行います。insert(Object o)、update(Object o)、およびretract(Object o)メソッドを呼び出すこともできます (頻繁に使用されるため、オブジェクト参照なしで呼び出すことが可能です)。drools.getWorkingMemory()を使用してworking memoryオブジェクトを返します。drools.setFocus( String s)を使用して、指定されたagenda groupにフォーカスを設定します。drools.getRule().getName()を使用してルール名を返します。drools.getTuple()を使用して現在実行しているルールに一致する タプル を返します。drools.getActivation()は対応するactivationを返します (これらの呼び出しはデバッグの処理に便利です)。
Knowlege Runtime アプリケーションプログラミングインターフェースは、KnowledgeContext 型の事前定義された変数 kcontext を介して公開されます。この getKnowledgeRuntime() メソッドが KnowledgeRuntime 型のオブジェクトを送信し、多数のメソッドへのアクセスを提供します。これらのメソッドは、右側の論理をコーディングする時に便利です。
kcontext.getKnowledgeRuntime().halt()呼び出しを使用して、ルールの実行を即座に終了します。getAgenda()アクセサーを使用して、このセッションのagendaへの参照を返します。これは、さまざまなアクティベーション、アジェンダ、およびルールフローグループへのアクセスを提供します。以下ようなアジェンダグループのアクティベーションは比較的一般的な使用例になります。// give focus to the agenda group CleanUp kcontext.getKnowledgeRuntime().getAgenda().getAgendaGroup( "CleanUp" ).setFocus();
注記
drools.setFocus( "CleanUp" )を使用して上記を実現することも可能です。- クエリを実行するには、
getQueryResults(String query)を呼び出します。その後、「クエリ」 の通り、結果を処理することが可能です。 working memoryとagendaevent listenersを追加および削除できるevent managementに対応するためのメソッドがあります。getKnowledgeBase()メソッドを使用してKnowledgeBaseオブジェクトを返します。このオブジェクトはシステムのバックボーンで、現在のセッションの発信元です。setGlobal(...)、getGlobal(...)、およびgetGlobals()を用いてグローバルを管理します。getEnvironment()を使用してrun-timeの 環境 を返します (これはオペレーティングシステムの環境と同様です)。
4.8.4.2. modify ステートメント
fact の更新を実行する構造化された方法を提供する言語拡張です。更新操作と、オブジェクトのフィールドを変更する複数の setter 呼び出しを組み合わせます。構文スキーマは次の通りです。
modify ( <fact-expression> ) {
<expression> [ , <expression> ]*
}
重要
<fact-expression> は ファクトオブジェクト参照 を生成しなければなりません。必ず、ブロックの式リストが該当するオブジェクトの setter 呼び出しで構成されるようにしてください (これらは、コンパイラーによって自動的に事前終了される通常のオブジェクト参照を用いずに記述されます)。
rule "modify stilton"
when
$stilton : Cheese(type == "stilton")
then
modify( $stilton ){
setPrice( 20 ),
setAge( "overripe" )
}
end
4.9. クエリ

図4.44 query
KnowledgeBase に対してグローバルであるため、同じ名前のクエリを同じ RuleBase の異なるナレッジパッケージに追加しないでください。
ksession.getQueryResults("name") を使用し、"name" はクエリ名に置き換えます。これは、クエリと一致したオブジェクトを読み出せるクエリ結果のリストを返します。
例4.19 年齢が 30 を超える人を問い合わせるクエリー
query "people over the age of 30"
person : Person( age > 30 )
end例4.20 年齢が x を超え、y に住む人を問い合わせるクエリー
query "people over the age of x" (int x, String y)
person : Person( age > x, location == y )
endQueryResultsRow で、タプルの各列へアクセスするために使用します。これらの列は、バインドされた宣言名または索引の位置よりアクセスできます。
例4.21 年齢が 30 を超える人を問い合わせるクエリー
QueryResults results = ksession.getQueryResults( "people over the age of 30" );
System.out.println( "we have " + results.size() + " people over the age of 30" );
System.out.println( "These people are are over 30:" );
for ( QueryResultsRow row : results ) {
Person person = ( Person ) row.get( "person" );
System.out.println( person.getName() + "
" );
}4.10. ドメイン固有言語
4.10.1. ドメイン固有言語の使用
- ルールの作成と、
engineが操作するドメインオブジェクトを区別するレイヤーとして機能します。これは、ドメインの専門家 (ビジネスアナリストなど) によってルールが読まれ、検証される必要がある場合に便利です。DSL は実装の詳細を隠し、ルール論理に集中します。 - DSL センテンスは、ルールで繰り返し使用される条件要素と結果アクションのテンプレートとしても機能します。
- DSL の実行時、ルールエンジンは影響を受けません。DSL はコンパイル時の機能であり、特別なパーサーとトランスフォーマーを必要とします。
4.10.2. ドメイン固有言語の作成
- ドメイン固有言語の作成には、技術者とドメインの専門家の協力が必要となります。
- 最初に、大きさと複雑さを把握するために、アプリケーションが必要なルールの例を記述します。
- ルールにおける類似のステートメントと繰り返しのステートメントを特定し、変数の部分をパラメーターとしてマーク付けします。
- 言語が開発されたら、ルールをテストします。
- アプリケーションのデータモデルがデータ型をファクトとして表す場合、ルールの記述は通常容易になります。
- 行の最初に大なり記号 (">") を付けて条件要素とアクションを DRL 形式のままにすると、初期設計段階で条件とアクションに関する実装の決定を延期することができます (これはデバッグのステートメントを挿入する際にも便利です)。
- 既存の DSL 定義を再使用したり、既存の条件または結果エントリーにパラメーターを追加したりしてルールを記述することが可能です。
- DSL エントリーの数をできるだけ少なくするようにしてください。パラメーターを使用すると、似たようなルールパターンや制約に同じ DSL センテンスを適用することができます。
4.10.3. ドメイン固有言語の管理
[when]Something is {color}=Something(color=="{color}")[when]キーワードは、式のスコープを示します (ルールの LHS (左側) または RHS (右側) に有効であるかどうかなど)。- 括弧で囲まれたキーワードの後の部分は、ルールで使用する式になります。通常は自然言語の式ですが、自然言語以外の式でも可能です。
- 最初の等号
=の右部分は、式のルール言語へのマッピングになります。この文字列の形式は、宛先、RHS、または LHS によって異なります。LHS の場合、通常の LHS 構文に基づいた用語となり、RHS の場合は Java ステートメントとなることがあります。
{color} など)。次に、括弧で囲まれた名前がマッピングの右側にある場合に、これらのキャプチャーから取得された値が補間されます。最後に、DSL ルールファイルの行にある式全体と一致する値を、補間された文字列が置き換えます。
? を使用して先行する文字が任意であることを示すことができます。これにより、DSL の自然言語のフレーズの変異を解消できます。ただし、これらの式は正規表現のパターンであるため、Java のパターン構文の マジック 文字はすべてバックスラッシュ (\) でエスケープする必要があります。
注記
[when]This is "{something}" and "{another}"=Something(something=="{something}", another=="{another}")
[when]This is {also} valid=Another(something=="{also}"){ および }) を使用する必要があります。波括弧をそのまま式や右側の置換テキスト内で使用したい場合は、前にバックスラッシュ (\) を付けてエスケープする必要があります。
[then]do something= if (foo) \{ doSomething(); \}注記
{ および }) を DSL 定義の置換文字列で表示する場合は、バックスラッシュ (\) でエスケープする必要があります。
例4.22 DSL マッピングエントリー
#This is a comment to be ignored.
[when]There is a Person with name of "{name}"=Person(name=="{name}")
[when]Person is at least {age} years old and lives in "{location}"=Person(age >= {age}, location=="{location}")
[then]Log "{message}"=System.out.println("{message}");
[when]And = and例4.23 DSL マッピングエントリーの拡張
There is a Person with name of "kitty"
==> Person(name="kitty")
Person is at least 42 years old and lives in "Atlanta"
==> Person(age > 42, location="Atlanta")
Log "boo"
==> System.out.println("boo");
There is a Person with name of "Bob" and Person is at least 30 years old and lives in "Atlanta"
==> Person(name="kitty") and Person(age > 30, location="Atlanta")4.10.4. ファクトへの制約の追加
-) を DSL 式の先頭に追加することでパターンに制約を追加することができます。式がハイフンで始まる場合、式はフィールド制約と見なされ、その前にある最後のパターン行に追加されます。
Cheese クラスに type、price、age、および country のフィールドがある場合、次のように左側の条件の一部を通常の DRL ファイルに表現することが可能です。
例4.24 DRL の LHS 条件
Cheese(age < 5, price == 20, type=="stilton", country=="ch")
例4.25 制約の追加
[when]There is a Cheese with=Cheese()
[when]- age is less than {age}=age<{age}
[when]- type is '{type}'=type=='{type}'
[when]- country equal to '{country}'=country=='{country}'例4.26 制約の記述
There is a Cheese with
- age is less than 42
- type is 'stilton'- で始まる行を見つけ、制約として先行のパターンに追加し、必要な場合にコンマを挿入します。 例4.26「制約の記述」 の例では、結果となる DRL は次のようになります。
Cheese(age<42, type=='stilton')
[when][]is less than or equal to=<= [when][]is less than=< [when][]is greater than or equal to=>= [when][]is greater than=> [when][]is equal to=== [when][]equals=== [when][]There is a Cheese with=Cheese()
注記
注記
- を押した後に Ctrl+Space を押してこのリストから項目を選択します。
[when][org.drools.Cheese]- age is less than {age} のように変更します。同じことを、上記の例の全項目に対して行います。
[org.drools.Cheese] コードは、文が直上の主要な制約にのみ適用されることを表しています (この場合は There is a Cheese with)。
Cheese というクラスが存在し、Content Assistance を用いた方法で追加される場合、com.yourcompany.Something オブジェクトスコープでマーク付けされた項目のみが有効となり、リストに表示されます。これは完全に任意となります。
4.10.5. DSL および DSLR 参照
#または//始まる行はコメントとして扱われます (先頭に空白文字がある場合とない場合の両方)。#/で始まるコメント行は、デバッグオプションを要求する単語に対してスキャンされます。- 左角括弧
[で始まる行は DSL エントリー定義の最初の行と見なされます。 - 他の行はすべて先行する DSL エントリー定義に付加され、行端が空白文字に置き換えられます。
- スコープの定義
[condition]または[when][consequence]または[then][keyword](ruleまたはendなど)。キーワードは、グローバルであるかどうか (DSLR ファイル全体で認識されるなど)、エントリーのスコープを示します。
- Java クラス名として記述されたタイプ定義は括弧で囲まれます。次の部分が左括弧で始まる場合を除き、この部分は任意となります。括弧の中が空の場合は無効になります。
- DSL 式は (Java) 正規表現で構成され、任意数の組み込み 点数定義 を持ち、等号
=で終了します。変数定義は波括弧{および}で囲まれます。変数定義は変数名と 2 つの任意のアタッチメントで構成され、コロン:で区切られます。アタッチメントが 1 つある場合、変数に割り当てられるテキストを一致する正規表現となります。2 つのアタッチメントがある場合、最初のアタッチメントは GUI エディターのヒントで、2 つ目が正規表現になります。正規表現で magic である文字を式の中で文字通り使用する場合、その文字の前にバックスラッシュ\を付けてエスケープする必要があることに注意してください。 - 区切り等号の後にある行の残りの部分は、正規表現に一致するすべての DSLR テキストの置換テキストになります。これには、波括弧で囲まれた変数名など、変数参照が含まれるようにすることもできます。任意で、変数名の後に感嘆符 (
!) と変換関数を使用することもできます。置換文字列内で波括弧{および}を文字通り使用する場合、括弧の前にバックスラッシュ\を付けてエスケープする必要があることに注意してください。
#/ で始まるコメント行を使用して選択的に有効にすることができます。このコメント行には下表の単語が 1 つ以上含まれることがあります。結果となる出力は標準出力に書き込まれます。
表4.2 DSL 拡張のデバッグオプション
| 単語 | 説明 |
|---|---|
| result | 結果となる DRL テキストを行番号と共に出力します。 |
| steps | 条件および結果行の各拡張ステップを出力します。 |
| keyword | スコープ keyword を持つすべての DSL エントリーの内部表現をダンプします。 |
| when | スコープ when または * を持つすべての DSL エントリーの内部表現をダンプします。 |
| then | スコープ then または * を持つすべての DSL エントリーの内部表現をダンプします。 |
| usage | すべての DSL エントリーの使用統計を表示します。 |
# Comment: DSL examples
#/ debug: display result and usage
# keyword definition: replaces "regula" by "rule"
[keyword][]regula=rule
# conditional element: "T" or "t", "a" or "an", convert matched word
[when][][Tt]here is an? {entity:\w+}=
${entity!lc}: ${entity!ucfirst} ()
# consequence statement: convert matched word, literal braces
[then][]update {entity:\w+}=modify( ${entity!lc} )\{ \}
4.10.6. DSLR ファイルのトランスフォーメーション
- テキストがメモリーへ読み込まれます。
- 各
keywordエントリーがテキスト全体に適用されます。最初に、空白文字シーケンスを任意数の空白文字と一致するパターンに置き換え、変数定義をデフォルトの".*?または定義で提供される正規表現より作成されたキャプチャーに置き換え、キーワード定義からの正規表現が変更されます。次に、DSLR テキストにて、変更された正規表現と一致する文字列が徹底的に検索されます。さらに、変数キャプチャーに対応する一致する文字列のサブ文字列が抽出され、対応する置換テキストの変数参照を置き換えます。DSLR テキストの一致する文字列はこのテキストによって置き換えられます。 whenとthenの間にある DSLR テキストは、均一に行ごとに検索されます。thenとendの間にある DSLR テキストは、均一に行ごとに処理されます。行では、行のセクションに関係する各 DSL エントリーが、DSL ファイルに記述されている順番通りに取得されます。正規表現の部分は変更されます。空白文字は任意数の空白文字と一致するパターンによって置き換えられ、正規表現を持つ変数定義はこの正規表現を持つキャプチャーによって置き換えられます (デフォルトは.*?になります)。結果となる正規表現が行のすべてまたは一部と一致すると、一致した部分が適切に変更された置換テキストによって置き換えられます。変数参照を正規表現のキャプチャーに対応するテキストに置き換えることで置換テキストが変更されます。このテキストは、変数参照に提供される文字列変換関数に従って変更することができます。同じエントリーで定義されない変数を命名する変数参照がある場合、その名前の変数にバインドされた値をエキスパンダーが置換します (現在のルールの前にある行の 1 つに定義されている場合)。- 条件の DSLR 行がハイフンで始まる場合、拡張された結果は最後の行に挿入されます。最後の行にはパターン CE (例: 型名の後に左右の括弧が続く) が含まれる必要があります。括弧内が空白である場合、拡張された行 (有効な制約が含まれている) が挿入されます。これ以外の場合、コンマ (
,) がその前に挿入されます。結果の DSLR 行がハイフンで始まる場合、拡張された結果は最後の行に挿入されます。最後の行にはmodifyステートメントが含まれるはずで、左右の波括弧{および}で終わります。括弧内が空白である場合、拡張された行 (有効なメソッド呼び出しが含まれる) が挿入されます。これ以外の場合、コンマ (,) がその前に挿入されます。
注記
accumulate など) にテキストを挿入することは できません。最初の挿入のみ実行されることはあります (eval など)。
4.10.7. 文字列トランスフォメーション関数
表4.3 文字列トランスフォーメーション関数
| 名前 | 説明 |
|---|---|
| uc | すべての文字を大文字に変換します。 |
| lc | すべての文字を小文字に変換します。 |
| ucfirst | 最初の文字を大文字に変換し、他の文字はすべて小文字に変換します。 |
| num | すべての数字と - を文字列から抽出します。元の文字列の最後にある 2 つの数字の前に . または , がある場合、少数点が対応する場所に挿入されます。 |
| a?b/c | 文字列と文字列 a を比較し、同等であれば b と置き換え、同等でなければ c と置き換えます。ただし、c は別の a、b、c の 3 つ組である可能性もあるため、構造全体が実際にはトランスレーションテーブルになります。 |
例4.27 DSL 文字列トランスフォーメーション関数
# definitions for conditions
[when][]There is an? {entity}=${entity!lc}: {entity!ucfirst}()
[when][]- with an? {attr} greater than {amount}={attr} <= {amount!num}
[when][]- with a {what} {attr}={attr} {what!positive?>0/negative?%lt;0/zero?==0/ERROR}.dsl が拡張子して使用されます。これは、ResourceType.DSL で Knowledge Builder へ渡されます。DSL 定義を使用するファイルでは、.dslr を拡張子として使用すべきです。Knowledge Builder は ResourceType.DSLR を想定します。しかし、 IDE はファイル拡張子に依存してルールファイルを正しく認識し、ルールファイルと動作します。
KnowledgeBuilder kBuilder = new KnowledgeBuilder(); Resource dsl = ResourceFactory.newClassPathResource( dslPath, getClass() ); kBuilder.add( dsl, ResourceType.DSL ); Resource dslr = ResourceFactory.newClassPathResource( dslrPath, getClass() ); kBuilder.add( dslr, ResourceType.DSLR );
4.10.8. BRMS および IDE におけるドメイン固有言語
重要
{token} へのみデータの入力が許可されます)。
drools-ant タスクを使用するか、「XML ルール言語」 のようにコードを組み込みます。
4.11. XML ルール言語
4.11.1. XML を使用する場合
4.11.2. XML 形式
<?xml version="1.0" encoding="UTF-8"?>
<package name="com.sample"
xmlns="http://drools.org/drools-4.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xs:schemaLocation="http://drools.org/drools-4.0 drools-4.0.xsd">
<import name="java.util.HashMap" />
<import name="org.drools.*" />
<global identifier="x" type="com.sample.X" />
<global identifier="yada" type="com.sample.Yada" />
<function return-type="void" name="myFunc">
<parameter identifier="foo" type="Bar" />
<parameter identifier="bada" type="Bing" />
<body>System.out.println("hello world");</body>
</function>
<rule name="simple_rule">
<rule-attribute name="salience" value="10" />
<rule-attribute name="no-loop" value="true" />
<rule-attribute name="agenda-group" value="agenda-group" />
<rule-attribute name="activation-group" value="activation-group" />
<lhs>
<pattern identifier="foo2" object-type="Bar" >
<or-constraint-connective>
<and-constraint-connective>
<field-constraint field-name="a">
<or-restriction-connective>
<and-restriction-connective>
<literal-restriction evaluator=">" value="60" />
<literal-restriction evaluator="<" value="70" />
</and-restriction-connective>
<and-restriction-connective>
<literal-restriction evaluator="<" value="50" />
<literal-restriction evaluator=">" value="55" />
</and-restriction-connective>
</or-restriction-connective>
</field-constraint>
<field-constraint field-name="a3">
<literal-restriction evaluator="==" value="black" />
</field-constraint>
</and-constraint-connective>
<and-constraint-connective>
<field-constraint field-name="a">
<literal-restriction evaluator="==" value="40" />
</field-constraint>
<field-constraint field-name="a3">
<literal-restriction evaluator="==" value="pink" />
</field-constraint>
</and-constraint-connective>
<and-constraint-connective>
<field-constraint field-name="a">
<literal-restriction evaluator="==" value="12"/>
</field-constraint>
<field-constraint field-name="a3">
<or-restriction-connective>
<literal-restriction evaluator="==" value="yellow"/>
<literal-restriction evaluator="==" value="blue" />
</or-restriction-connective>
</field-constraint>
</and-constraint-connective>
</or-constraint-connective>
</pattern>
<not>
<pattern object-type="Person">
<field-constraint field-name="likes">
<variable-restriction evaluator="==" identifier="type"/>
</field-constraint>
</pattern>
<exists>
<pattern object-type="Person">
<field-constraint field-name="likes">
<variable-restriction evaluator="==" identifier="type"/>
</field-constraint>
</pattern>
</exists>
</not>
<or-conditional-element>
<pattern identifier="foo3" object-type="Bar" >
<field-constraint field-name="a">
<or-restriction-connective>
<literal-restriction evaluator="==" value="3" />
<literal-restriction evaluator="==" value="4" />
</or-restriction-connective>
</field-constraint>
<field-constraint field-name="a3">
<literal-restriction evaluator="==" value="hello" />
</field-constraint>
<field-constraint field-name="a4">
<literal-restriction evaluator="==" value="null" />
</field-constraint>
</pattern>
<pattern identifier="foo4" object-type="Bar" >
<field-binding field-name="a" identifier="a4" />
<field-constraint field-name="a">
<literal-restriction evaluator="!=" value="4" />
<literal-restriction evaluator="!=" value="5" />
</field-constraint>
</pattern>
</or-conditional-element>
<pattern identifier="foo5" object-type="Bar" >
<field-constraint field-name="b">
<or-restriction-connective>
<return-value-restriction evaluator="==" >
a4 + 1
</return-value-restriction>
<variable-restriction evaluator=">" identifier="a4" />
<qualified-identifier-restriction evaluator="==">
org.drools.Bar.BAR_ENUM_VALUE
</qualified-identifier-restriction>
</or-restriction-connective>
</field-constraint>
</pattern>
<pattern identifier="foo6" object-type="Bar" >
<field-binding field-name="a" identifier="a4" />
<field-constraint field-name="b">
<literal-restriction evaluator="==" value="6" />
</field-constraint>
</pattern>
</lhs>
<rhs>
if ( a == b ) {
assert( foo3 );
} else {
retract( foo4 );
}
System.out.println( a4 );
</rhs>
</rule>
</package>import 要素はルールで使用したい型をインポートします。
global 要素はルールで参照できるグローバルオブジェクトを定義します。
function にはルールで使用される関数のための関数宣言が含まれます。戻り型、一意の名前、およびパラメーターをコードのスニペットのボディーに指定する必要があります。
例4.28 rule 要素の詳細
<rule name="simple_rule">
<rule-attribute name="salience" value="10" />
<rule-attribute name="no-loop" value="true" />
<rule-attribute name="agenda-group" value="agenda-group" />
<rule-attribute name="activation-group" value="activation-group" />
<lhs>
<pattern identifier="cheese" object-type="Cheese">
<from>
<accumulate>
<pattern object-type="Person"></pattern>
<init>
int total = 0;
</init>
<action>
total += $cheese.getPrice();
</action>
<result>
new Integer( total ) );
</result>
</accumulate>
</from>
</pattern>
<pattern identifier="max" object-type="Number">
<from>
<accumulate>
<pattern identifier="cheese" object-type="Cheese"></pattern>
<external-function evaluator="max" expression="$price"/>
</accumulate>
</from>
</pattern>
</lhs>
<rhs>
list1.add( $cheese );
</rhs>
</rule>4.11.3. 形式 (XML と DRL) 間の自動転換
XmlDumper - XML のエキスポート。 DrlDumper - DRL のエキスポート DrlParser - DRL の読み取り。 XmlPackageReader - XML の読み取り。

Where did the comment section go?
Red Hat's documentation publication system recently went through an upgrade to enable speedier, more mobile-friendly content. We decided to re-evaluate our commenting platform to ensure that it meets your expectations and serves as an optimal feedback mechanism. During this redesign, we invite your input on providing feedback on Red Hat documentation via the discussion platform.