第21章 Infinispan Query DSL
21.1. Infinispan Query DSL
Infinispan Query DSL はキャッシュをクエリーするための統一された方法を提供します。これは、ライブラリーモードでインデックス化されたクエリーとインデックスのないクエリーの両方に使用でき、リモートクエリー (Hot Rod Java クライアントを経由) にも使用できます。Infinispan Query DSL では、Lucene ネイティブクエリー API や Hibernate Search クエリー API に依存せずにクエリーを実行できます。
Infinispan Query DSL では、インデックスのないクエリーは JBoss Data Grid リモートおよび埋め込みモードでのみ利用できます。インデックスのないクエリーには設定されたインデックス (「Infinispan Query DSL ベースのクエリー」を参照) は必要ありません。Hibenrate Search や Lucene ベースの API はインデックスのないクエリーを使用できません。
21.2. Infinispan Query DSL を用いたクエリーの作成
新しいクエリー API は org.infinispan.query.dsl パッケージにあります。クエリーは、Search.getQueryFactory() を使用して取得される QueryFactory インスタンスを利用して作成されます。各 QueryFactory インスタンスは 1 つのキャッシュインスタンスにバインドされ、複数の並列クエリーを作成する場合に使用できるステートレスでスレッドセーフなオブジェクトです。
Infinispan Query DSL は以下の手順にしたがってクエリーを実行します。
-
クエリーは
from(Class entityType)メソッドを呼び出して作成されます。これは、特定のキャッシュから指定のエンティティークラスのクエリーを作成するQueryBuilderオブジェクトを返します。 -
QueryBuilderは、DSL メソッドを呼び出して指定される検索基準と設定を累積します。また、構築を完了するQueryBuilder.build()メソッドを呼び出してQueryオブジェクトを構築するために使用されます。QueryBuilderオブジェクトを使用して、入れ子のクエリー以外の複数のクエリーを同時に構築することはできませんが、後で再使用することができます。 -
Queryオブジェクトのlist()メソッドを呼び出してクエリーを実行し、結果を取得します。実行するとQueryオブジェクトは再使用できなくなります。新しい結果を取得する必要がある場合は、QueryBuilder.build()を呼び出して新しいインスタンスを取得します。
クエリーは単一のエンティティー型を対象とし、単一キャッシュの内容全体で評価されます。複数キャッシュでのクエリーの実行や、複数のエンティティー型を対象とするクエリーの作成はサポートされません。
21.3. Infinispan Query DSL ベースのクエリーの有効化
ライブラリーモードでは、Infinispan Query DSL ベースのクエリーの実行は Lucene ベースの API クエリーの実行とほぼ同じです。前提条件は次のとおりです。
- Infinispan Query に必要なすべてのライブラリーがクラスパスにある。詳細は 『Administration and Configuration Guide』を参照してください。
- インデックス化が有効で、キャッシュに対して設定されている (任意)。詳細は、『Administration and Configuration Guide』を参照してください。
- アノテーションが付いた POJO キャッシュ値 (任意)。インデックス化が有効になっていない場合は POJO アノテーションは必要なく、設定されている場合は無視されます。インデックス化が有効になっていない場合、Hibernate Search のアノテーションが付いたフィールドのみではなく、JavaBeans の慣例にしたがうフィールドすべてが検索可能になります。
21.4. Infinispan Query DSL ベースのクエリーの実行
Infinispan Query DSL ベースのクエリーが有効になったら、DSL ベースのクエリーを実行するために Search から QueryFactory を取得します。
キャッシュの QueryFactory の取得
ライブラリーモードで、以下のように QueryFactory を取得します。
QueryFactory qf = org.infinispan.query.Search.getQueryFactory(cache)
DSL ベースのクエリーの構築
import org.infinispan.query.Search;
import org.infinispan.query.dsl.QueryFactory;
import org.infinispan.query.dsl.Query;
QueryFactory qf = Search.getQueryFactory(cache);
Query q = qf.from(User.class)
.having("name").eq("John")
.build();
List list = q.list();
assertEquals(1, list.size());
assertEquals("John", list.get(0).getName());
assertEquals("Doe", list.get(0).getSurname());
リモートクエリーをリモートクライアントサーバーモードで使用する場合、Search は org.infinispan.client.hotrod パッケージにあります。詳細は 「Hot Rod Java クライアント経由のリモートクエリーの実行」の例を参照してください。
複数の条件 (サブ条件を含む) をブール値演算子と組み合わせることも可能です。
複数条件の組み合わせ
Query q = qf.from(User.class)
.having("name").eq("John")
.and().having("surname").eq("Doe")
.and().not(qf.having("address.street").like("%Tanzania%")
.or().having("address.postCode").in("TZ13", "TZ22"))
.build();
このクエリー API は、ユーザーに Lucene クエリーオブジェクト構築の詳細内容を公開しないことで、クエリーの作成方法を簡素化します。また、リモート Hot Rod クライアントが利用できる利点もあります。
以下の例は、Book エンティティーのクエリーの書き方を示しています。
Book エンティティーのクエリー
import org.infinispan.query.Search;
import org.infinispan.query.dsl.*;
// get the DSL query factory, to be used for constructing the Query object:
QueryFactory qf = Search.getQueryFactory(cache);
// create a query for all the books that have a title which contains the word "engine":
Query query = qf.from(Book.class)
.having("title").like("%engine%")
.build();
// get the results
List<Book> list = query.list();
21.5. 射影クエリー
多くの場合で、完全なドメインオブジェクトを返す必要はなく、アプリケーションには属性の小さなサブセットのみが必要になります。射影クエリーでは、属性 (または属性のパス) の特定のサブセットを返すことができます。射影クエリーが使用されると、Query.list() はドメインエンティティー全体 (List<Object>) を返さずに、アレイの各エントリーが射影された属性に対応する List<Object[]> を返します。
射影クエリーを定義するには、以下の例のようにクエリーの構築時に select(…) メソッドを使用します。
書名と発行年の取得
// Match all books that have the word "engine" in their title or description
// and return only their title and publication year.
Query query = queryFactory.from(Book.class)
.select(Expression.property("title"), Expression.property("publicationYear"))
.having("title").like("%engine%")
.or().having("description").like("%engine%")
.build();
// results.get(0)[0] contains the first matching entry's title
// results.get(0)[1] contains the first matching entry's publication year
List<Object[]> results = query.list();
21.6. グループ化および集約操作
Infinispan Query DSL には、グループ化フィールドのセットを基にしてクエリー結果をグループ化する機能や、値のセットに集約関数を適用して各グループからの結果の集約を構築する機能があります。グループ化と集約は、射影クエリーとのみ使用できます。
グループ化フィールドのセットは、groupBy(field) メソッドを複数回呼び出して指定されます。グループ化フィールドの順番は関係ありません。
射影で選択されたすべての非グループ化フィールドは、以下に説明するグループ化関数の 1 つを使用して集約する必要があります。
著者による本のグループ化とその冊数
Query query = queryFactory.from(Book.class)
.select(Expression.property("author"), Expression.count("title"))
.having("title").like("%engine%")
.groupBy("author")
.build();
// results.get(0)[0] will contain the first matching entry's author
// results.get(0)[1] will contain the first matching entry's title
List<Object[]> results = query.list();
集約操作
以下の集約操作を指定のフィールドで実行できます。
-
avg()-Numberのセットの平均を算出し、Doubleとして表します。null 以外の値がない場合、結果は null になります。 -
count()- null でない行の数をLongとして返します。null 以外の値がない場合、結果は 0 になります。 max()- 指定のフィールドで見つかった最も大きな値と、適用されたフィールドと同じ戻り値の型を返します。null 以外の値がない場合は、結果は null になります。注記指定のフィールドの値は、
Comparable型である必要があります。そうでないとIllegalStateExceptionが発生します。min()- 指定のフィールドで見つかった最も小さな値と、適用されたフィールドと同じ戻り値の型を返します。null 以外の値がない場合は、結果は null になります。注記指定のフィールドの値は、
Comparable型である必要があります。そうでないとIllegalStateExceptionが発生します。sum()-Numberのセットの合計を算出し、返します。戻り値の型は指定のフィールド型によります。null で以外の値がない場合、結果は null になります。以下の表は、指定のフィールドを基にした戻り値の型を表しています。
表21.1 戻り値の型の合計
フィールド型 戻り値の型 Integral (
BigInteger以外)LongFloating Point
DoubleBigIntegerBigIntegerBigDecimalBigDecimal
射影クエリーの特別なケース
射影クエリーでは、以下のケースは特別なユースケースとなります。
- 選択されたフィールドがすべて集約され、グループ化に何も使用されないことが適切である射影クエリー。この場合、集約はグループごとに算出されず、グローバルに算出されます。
- フィールドのグループ化を集約で使用できる。これは、集約が単一のデータポイント上で算出され、値が現在のグループに属する退化したケースになります。
- グループ化フィールドのみを選択し、集約フィールドは選択しないことが適切であるクエリー。
グループ化および集約クエリーの評価
通常のクエリーのように、集約クエリーにフィルター条件を含めることができます。これは、任意でグループ化操作の前後に実行できます。
groupBy メソッドの呼び出し前に指定されたすべてのフィルター条件は、グループ化操作の実行前に直接キャッシュエントリーに適用されます。これらのフィルター条件は、クエリーされたエンティティー型のプロパティーを参照することがあり、後でグループ化に使用されるデータセットを制限するためのものです。
groupBy メソッドの呼び出し後に指定されたすべてのフィルター条件は、グループ化操作によって生じる射影に適用されます。これらのフィルター条件は、 groupBy によって指定されたフィールドまたは集約フィールドを参照できます。select 句に指定されていない集約フィールドの参照は許可されますが、非集約フィールドや非グループ化フィールドの参照は禁止されます。このフェーズをフィルターすると、プロパティーを基にしてグループの数が削減されます。
順序付けも通常のクエリーと同様に指定できます。順序付け操作はグループ化操作の後に実行され、前述のとおりグループ化後のフィルターによって許可されるフィールドを参照できます。
21.7. 名前付きパラメーターの使用
各リクエストに新しいクエリーを作成する代わりに、各実行で置き換えられるパラメーターを含むことが可能です。これにより、クエリーを 1 度に定義でき、必要時にクエリーの変数を調整できます。
having(…) の比較演算子の右側に Expression.param(…) 演算子を使用すると、クエリーの作成時パラメーターが定義されます。
名前付きパラメーターの定義
import org.infinispan.query.Search;
import org.infinispan.query.dsl.*;
[...]
QueryFactory queryFactory = Search.getQueryFactory(cache);
// Defining a query to search for various authors
Query query = queryFactory.from(Book.class)
.select("title")
.having("author").eq(Expression.param("authorName"))
.build()
[...]
名前付きパラメーターの値設定
デフォルトでは、宣言されたパラメーターはすべて null となり、クエリーの実行前に定義されたすべてのパラメーターを null でない値に更新する必要があります。パラメーターの宣言後、クエリーで新しい値を指定して setParameter(parameterName, value) または setParameters(parameterMap) を呼び出すと値を更新することができます。また、クエリーを再構築する必要はありません。新しいパラメーターが定義された後に再度実行することができます。
パラメーターを個別に更新
[...]
query.setParameter("authorName","Smith");
// Rerun the query and update the results
resultList = query.list();
[...]
パラメーターのマップとして更新
[...]
parameterMap.put("authorName","Smith");
query.setParameters(parameterMap);
// Rerun the query and update the results
resultList = query.list();
[...]

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.