14.2. 埋め込みクエリー


Data Grid をライブラリーとして使用すると、埋め込みクエリーを使用できます。protobuf マッピングは不要であり、インデックス作成と検索の両方が Java オブジェクト上で実行されます。ライブラリーモードでは、Lucene クエリーを直接実行し、利用可能なすべての Query API を使用できます。また、柔軟なインデックス設定により、レイテンシーを最小限に抑えることができます。

14.2.1. 簡単な例

books と呼ばれる Data Grid キャッシュに Book インスタンスを保存します。Book インスタンスはインデックス化されるため、キャッシュのインデックスを有効にし、Data Grid が インデックスを自動的に設定 できるようにします。

Data Grid の設定:

infinispan.xml

<infinispan>
    <cache-container>
        <transport cluster="infinispan-cluster"/>
        <distributed-cache name="books">
            <indexing index="PRIMARY_OWNER" auto-config="true"/>
        </distributed-cache>
    </cache-container>
</infinispan>

キャッシュを取得します。

import org.infinispan.Cache;
import org.infinispan.manager.DefaultCacheManager;
import org.infinispan.manager.EmbeddedCacheManager;

EmbeddedCacheManager manager = new DefaultCacheManager("infinispan.xml");
Cache<String, Book> cache = manager.getCache("books");

Book は、次の例のように定義されます。インデックスを作成するプロパティーを選択する必要があります。プロパティーごとに、Hibernate Search プロジェクトで定義されたアノテーションを使用して高度なインデックスオプションを任意で選択できます。

Book.java

import org.hibernate.search.annotations.*;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

//Values you want to index need to be annotated with @Indexed, then you pick which fields and how they are to be indexed:
@Indexed
public class Book {
   @Field String title;
   @Field String description;
   @Field @DateBridge(resolution=Resolution.YEAR) Date publicationYear;
   @IndexedEmbedded Set<Author> authors = new HashSet<Author>();
}

Author.java

public class Author {
   @Field String name;
   @Field String surname;
   // hashCode() and equals() omitted
}

Data Grid Cache に複数の Book インスタンスを保存したとすると、次の例のように、一致するフィールドを検索できます。

Lucene クエリーの使用:

// get the search manager from the cache:
SearchManager searchManager = org.infinispan.query.Search.getSearchManager(cache);

// create any standard Lucene query, via Lucene's QueryParser or any other means:
org.apache.lucene.search.Query fullTextQuery = //any Apache Lucene Query

// convert the Lucene query to a CacheQuery:
CacheQuery cacheQuery = searchManager.getQuery( fullTextQuery );

// get the results:
List<Object> found = cacheQuery.list();

Lucene クエリーは、"title:infinispan AND authors.name:sanne" などのテキスト形式でクエリーを解析するか、Hibernate Search によって提供されるクエリービルダーを使用して作成されます。

// get the search manager from the cache:
SearchManager searchManager = org.infinispan.query.Search.getSearchManager( cache );

// you could make the queries via Lucene APIs, or use some helpers:
QueryBuilder queryBuilder = searchManager.buildQueryBuilderForClass(Book.class).get();

// the queryBuilder has a nice fluent API which guides you through all options.
// this has some knowledge about your object, for example which Analyzers
// need to be applied, but the output is a fairly standard Lucene Query.
org.apache.lucene.search.Query luceneQuery = queryBuilder.phrase()
                  .onField("description")
                  .andField("title")
                  .sentence("a book on highly scalable query engines")
                  .createQuery();

// the query API itself accepts any Lucene Query, and on top of that
// you can restrict the result to selected class types:
CacheQuery query = searchManager.getQuery(luceneQuery, Book.class);

// and there are your results!
List objectList = query.list();

for (Object book : objectList) {
      System.out.println(book);
}

list() とは別に、結果をストリーミングするか、ページネーションを使用できます。

Lucene またはフルテキスト機能を必要としない検索で、ほとんどの場合集計と完全一致に関するものである場合は、Data Grid Query DSL API を使用できます。

import org.infinispan.query.dsl.QueryFactory;
import org.infinispan.query.dsl.Query;
import org.infinispan.query.Search;

// get the query factory:
QueryFactory queryFactory = Search.getQueryFactory(cache);

Query q = queryFactory.from(Book.class)
            .having("author.surname").eq("King")
            .build();

List<Book> list = q.list();

最後に、Ickle クエリーを直接使用して、1 つ以上の述語で Lucene 構文を可能にします。

import org.infinispan.query.dsl.QueryFactory;
import org.infinispan.query.dsl.Query;

// get the query factory:
QueryFactory queryFactory = Search.getQueryFactory(cache);


Query q = queryFactory.create("from Book b where b.author.name = 'Stephen' and " +
                "b.description : (+'dark' -'tower')");

List<Book> list = q.list();

14.2.2. インデックス化

Data Grid のインデックス作成はキャッシュごとに行われ、デフォルトではキャッシュはインデックス化されません。インデックスを有効にすることは必須ではありませんが、インデックスを使用したクエリーのパフォーマンスは大幅に高くなります。一方、インデックスを有効にするとクラスターの書き込みスループットに悪影響を及ぼす可能性があるため、一部のストラテジーの クエリーパフォーマンスガイド を確認して、キャッシュタイプとユースケースに応じてこの影響を最小限に抑えるようにしてください。

14.2.2.1. 設定

14.2.2.1.1. 一般的な形式

XML によるインデックス作成を有効にするには、<indexing> 要素と index (インデックスモード) をキャッシュ設定に追加し、オプションで追加のプロパティーを渡す必要があります。

<infinispan>
   <cache-container default-cache="default">
      <replicated-cache name="default">
         <indexing index="ALL">
            <property name="property.name">some value</property>
         </indexing>
      </replicated-cache>
   </cache-container>
</infinispan>

プログラマティック

import org.infinispan.configuration.cache.*;

ConfigurationBuilder cacheCfg = ...
cacheCfg.indexing().index(Index.ALL)
      .addProperty("property name", "propery value")
14.2.2.1.2. Index names

index 要素内の各プロパティーの前には、org.infinispan.sample.Car というインデックス名が付けられます。directory_providerlocal-heap です。

  ...
  <indexing index="ALL">
      <property name="org.infinispan.sample.Car.directory_provider">local-heap</property>
  </indexing>
...
</infinispan>
cacheCfg.indexing()
   .index(Index.ALL)
      .addProperty("org.infinispan.sample.Car.directory_provider", "local-heap")

Data Grid は、キャッシュに存在する各エンティティーのインデックスを作成し、これらのインデックスを個別に設定できます。@Indexed アノテーションが付けられたクラスの場合、アノテーションの name 引数で上書きされない限り、インデックス名は完全修飾クラス名になります。

以下のスニペットでは、すべてのエンティティーのデフォルトストレージは infinispan ですが、Boat インスタンスは boatIndex という名前のインデックスの local-heap に保存されます。Airplane エンティティーも local-heap に保存されます。他のエンティティーのインデックスは、default で接頭辞が付けられたプロパティーで設定されます。

package org.infinispan.sample;

@Indexed(name = "boatIndex")
public class Boat {

}

@Indexed
public class Airplane {

}
    ...
    <indexing index="ALL">
        <property name="default.directory_provider">infinispan</property>
        <property name="boatIndex.directory_provider">local-heap</property>
        <property name="org.infinispan.sample.Airplane.directory_provider">
            ram
        </property>
    </indexing>
    ...
</infinispan>
14.2.2.1.3. インデックス化されたエンティティーの指定

Data Grid は、キャッシュ内の異なるエンティティータイプのインデックスを自動的に認識し、管理できます。Data Grid の今後のバージョンではこの機能が削除されるため、インデックス化されるタイプを宣言することが推奨されます(完全修飾クラス名で一覧表示します)。これは、xml を介して実行できます。

<infinispan>
   <cache-container default-cache="default">
      <replicated-cache name="default">
         <indexing index="ALL">
            <indexed-entities>
                <indexed-entity>com.acme.query.test.Car</indexed-entity>
                <indexed-entity>com.acme.query.test.Truck</indexed-entity>
            </indexed-entities>
         </indexing>
      </replicated-cache>
   </cache-container>
</infinispan>

プログラムを使用する場合

 cacheCfg.indexing()
      .index(Index.ALL)
       .addIndexedEntity(Car.class)
       .addIndexedEntity(Truck.class)

サーバーモードでは、indexed-entities 要素に一覧表示されているクラス名は、JBoss Modules モジュール識別子、スロット名、および完全修飾クラス名で設定される拡張クラス名を使用し、これら 3 つのコンポーネントは ':' 文字 (例: "com.acme.my-module-with-entity-classes:my-slot:com.acme.query.test.Car") を使用する必要があります。エンティティークラスは参照モジュールに配置する必要があります。このモジュールは、サーバーの modules フォルダーにデプロイされたユーザー提供モジュールか、deployments フォルダーにデプロイされたプレーン jar のいずれかです。問題のモジュールはキャッシュの自動依存関係となるため、最終的に再デプロイするとキャッシュが再起動されます。

注記

サーバーの場合のみ、extended クラス名の使用要件に従い、プレーンクラス名を使用すると、誤った ClassLoader が使用されているため、クラスがないために解決に失敗します(Data Grid の内部クラスパスが使用されます)。

14.2.2.2. インデックスモード

Data Grid ノードは通常、ローカルとリモートの 2 つのソースからデータを受け取ります。ローカルは、同じ JVM のマップ API を使用してデータを操作するクライアントに変換されます。リモートデータは、レプリケーションまたはリバランス時に他の Data Grid ノードから提供されます。

インデックスモードの設定は、クラスターがインデックス化されるビューのノードから定義します。

値:

  • all: すべてのデータがインデックス化され、ローカル、およびリモートになります。
  • LOCAL: ローカルデータのみがインデックス化されます。
  • PRIMARY_OWNER: ローカルまたはリモートの作成元に関係なく、ノードがプライマリー所有者であるキーを含むエントリーのみがインデックス化されます。
  • NONE: データがインデックス化されません。インデックスを設定しないのと同等です。

14.2.2.3. インデックスマネージャー

インデックスマネージャーは、Data Grid クエリーの central コンポーネントで、Lucene の IndexReaderIndexWriter などの複数のクエリーコンポーネントのインデックス設定、配布、および内部ライフサイクルを行います。各インデックスマネージャーは Directory Provider に関連付けられており、インデックスの物理ストレージを定義します。

インデックスの分散に関して、Data Grid は共有インデックスまたは非共有インデックスで設定できます。

14.2.2.4. 共有インデックス

共有インデックスは、特定のキャッシュに対して単一の分散されたクラスター全体のインデックスです。主な利点は、インデックスがすべてのノードから表示され、インデックスがローカルであるかのようにクエリーできることです。すべてのメンバーにクエリーを ブロードキャスト して結果を集約する必要がないことです。欠点は、Lucene がインデックスに同時に書き込むことができないため、ロック取得の連携を適切な共有インデックス対応インデックスマネージャーが行う必要があることです。いずれの場合も、クラスター単位で 1 つの書き込みロックがあると、書き込みが非常に多い場合に一定レベルの競合が発生する可能性があります。

Data Grid は、Data Grid Directory Provider を利用する共有インデックスをサポートします。これは、インデックスを InfinispanIndexManager と呼ばれる個別のキャッシュセットに保存します。

14.2.2.4.1. インデックスモードの影響

共有インデックスは冗長なインデックス作成につながるため、ALL インデックスモードは使用しないでください。クラスタ全体で1つのインデックスが存在するため、Cache API を介して挿入されたときにインデックスが作成され、データグリッドが別のノードに複製したときに別のインデックスが作成されます。ALL モードは通常、各ノードで完全な インデックスレプリカを作成するために、非共有インデックス に関連付けます。

14.2.2.4.2. InfinispanIndexManager

このインデックスマネージャーは Data Grid Directory Provider を使用し、共有インデックスの作成に適しています。この設定では、インデックスモードを LOCAL に設定する必要があります。

設定:

<distributed-cache name="default" >
    <indexing index="PRIMARY_OWNER">
        <property name="default.indexmanager">org.infinispan.query.indexmanager.InfinispanIndexManager</property>
        <!-- Optional: tailor each cache used internally by the InfinispanIndexManager -->
        <property name="default.locking_cachename">LuceneIndexesLocking_custom</property>
        <property name="default.data_cachename">LuceneIndexesData_custom</property>
        <property name="default.metadata_cachename">LuceneIndexesMetadata_custom</property>
    </indexing>
</distributed-cache>

<!-- Optional -->
<replicated-cache name="LuceneIndexesLocking_custom">
    <indexing index="NONE" />
    <!-- extra configuration -->
</replicated-cache>

<!-- Optional -->
<replicated-cache name="LuceneIndexesMetadata_custom">
    <indexing index="NONE" />
    <!-- extra configuration -->
</replicated-cache>

<!-- Optional -->
<distributed-cache name="LuceneIndexesData_custom">
    <indexing index="NONE" />
    <!-- extra configuration -->
</distributed-cache>

インデックスは、デフォルトで LuceneIndexesDataLuceneIndexesMetadata、および LuceneIndexesLocking と呼ばれるクラスター化されたキャッシュのセットに保存されます。

LuceneIndexesLocking キャッシュは Lucene ロックを保存するために使用されます。これは非常に小さなキャッシュであり、エンティティー (インデックス) ごとに 1 つのエントリーが含まれます。

LuceneIndexesMetadata キャッシュは、名前、チャンク、サイズなど、インデックスの一部である論理ファイルに関する情報を格納するために使用されます。また、サイズが小さくなります。

LuceneIndexesData キャッシュは、ほとんどのインデックスが配置されます。これは他の 2 つのインデックスよりもはるかに大きくなりますが、Lucene の効率的な保存技術により、キャッシュ自体のデータよりも小さくなければなりません。

これらの 3 つのケースの設定を再定義する必要はありません。Data Grid は適切なデフォルトを選択します。それらを再定義する理由は、特定のシナリオにおけるパフォーマンスチューニングや、キャッシュストアを設定して永続化させる理由です。

クラスターの 2 つ以上のノードが同時にインデックスへの書き込みを試みたときにインデックスが破損するのを回避するために、InfinispanIndexManager はクラスター内のマスター (JGroups コーディネーター) を内部で選択し、すべてのインデックスがこのマスターに機能します。

14.2.2.5. 共有されていないインデックス

共有されていないインデックスは、各ノードで独立したインデックスです。この設定は、各ノードにすべてのクラスターデータがあるレプリケートされたキャッシュに特に利点があります。そのため、すべてのインデックスを保持することができ、クエリー時のネットワークレイテンシーがゼロで最適なクエリーパフォーマンスが得られます。もう 1 つの利点は、インデックスは各ノードにローカルであり、書き込み中に競合が発生するため、各ノードはクラスター全体ではなく、独自のインデックスロックの対象であることから、競合が少なくなります。

各ノードは部分的なインデックスを保持する可能性があるため、正しい検索結果を取得するために、リンク#query_clustered_query_api[broadcast] クエリーが必要になる場合があります。これにより、レイテンシーを追加できます。ただし、キャッシュが REPL の場合、ブロードキャストは必要ありません。各ノードはインデックスの完全なローカルコピーを保持し、ローカルインデックスを利用する最適な速度でクエリーが実行されます。

Data Grid には、directory-basednear-real-time の 2 つのインデックスマネージャーが非共有インデックスに適しています。ストレージでは、共有されていないインデックスは ram、filesystem、または Data Grid のローカルキャッシュに配置できます。

14.2.2.5.1. インデックスモードの影響

directory-based および near-real-time のインデックスマネージャーは、異なる インデックスモード に関連付けることができます。これにより、インデックスディストリビューションが異なります。

REPL キャッシュは ALL インデックスモードと組み合わせて、各ノードのクラスター全体のインデックスの完全なコピーになります。このモードでは、ネットワークのレイテンシーなしにクエリーを効果的にローカルにすることができます。これは、REPL キャッシュをインデックス化するのに推奨されるモードであり、REPL キャッシュが検出されると auto-config によって選択されます。ALL モードは、DIST キャッシュでは使用できません。

REPL または DIST キャッシュと LOCAL インデックスモードを組み合わせると、各ノードが同じ JVM から挿入されたデータのみをインデックス化するため、インデックスの分散が不均一になります。正しいクエリー結果を取得するには、ブロードキャスト クエリーを使用する必要があります。

REPL または DIST キャッシュを PRIMARY_OWNER と組み合わせるには、ブロードキャストクエリーも必要です。LOCAL モードとは異なり、各ノードのインデックスには、キーが一貫したハッシュに従ってノードが所有するインデックス化されたエントリーが含まれ、ノード間でより均等に配分されたインデックスが作成されます。

14.2.2.5.2. ディレクトリーベースのインデックスマネージャー

これは、インデックスマネージャーが設定されていない場合に使用されるデフォルトのインデックスマネージャーです。directory-based のインデックスマネージャーは、ローカルの lucene ディレクトリーがサポートするインデックスを管理するために使用されます。ramfilesystem、およびクラスター化されていない infinispan ストレージをサポートします。

ファイルシステムストレージ

これはデフォルトのストレージであり、インデックスマネージャーの設定が省略される場合に使用されます。インデックスは MMapDirectory を使用してファイルシステムに保存されます。ローカルインデックスに推奨されるストレージです。インデックスはディスク上で永続化されますが、Lucene によってマップされたメモリーを取得するため、適切なクエリーパフォーマンスが提供されます。

設定:

<replicated-cache name="myCache">
   <indexing index="ALL">
      <!-- Optional: define base folder for indexes -->
      <property name="default.indexBase">${java.io.tmpdir}/baseDir</property>
   </indexing>
</replicated-cache>

Data Grid は、キャッシュに存在するエンティティー(インデックス)ごとに default.indexBase の下に異なるフォルダーを作成します。

RAM ストレージ

インデックスは Lucene RAMDirectory を使用してメモリーに保存されます。大規模なインデックスや同時の状況では推奨されません。Ram に保存されているインデックスは永続的ではないため、クラスターのシャットダウン後に 再インデックス が必要になります。設定:

<replicated-cache name="myCache">
   <indexing index="ALL">
      <property name="default.directory_provider">local-heap</property>
   </indexing>
</replicated-cache>

Data Grid ストレージ

Data Grid ストレージは、インデックスをキャッシュのセットに保存する Data Grid Lucene ディレクトリーを利用します。これらのキャッシュは、たとえば、キャッシュストアを追加して、メモリー以外の別の場所でインデックスを永続化するなどして、他の Data Grid キャッシュと同様に設定できます。共有されていないインデックスで Data Grid ストレージを使用するには、インデックスに LOCAL キャッシュを使用する必要があります。

<replicated-cache name="default">
    <indexing index="ALL">
        <property name="default.locking_cachename">LuceneIndexesLocking_custom</property>
        <property name="default.data_cachename">LuceneIndexesData_custom</property>
        <property name="default.metadata_cachename">LuceneIndexesMetadata_custom</property>
    </indexing>
</replicated-cache>

<local-cache name="LuceneIndexesLocking_custom">
    <indexing index="NONE" />
</local-cache>

<local-cache name="LuceneIndexesMetadata_custom">
    <indexing index="NONE" />
</local-cache>

<local-cache name="LuceneIndexesData_custom">
    <indexing index="NONE" />
</local-cache>
14.2.2.5.3. Near-real-time インデックスマネージャー

directory-based のインデックスマネージャーと同様ですが、Lucene の Near-Real-Time 機能を利用します。基礎となるストアにインデックスをフラッシュする頻度が少ないため、directory-based よりも書き込みパフォーマンスが向上します。欠点は、シャットダウンが適切に行われない場合に、フラッシュされていないインデックスの変更が失われることです。local-heapfilesystem、および local infinispan ストレージと共に使用できます。異なるストレージタイプの設定は、directory-based のインデックスマネージャーと同じです。

ram を使用する例:

<replicated-cache name="default">
    <indexing index="ALL">
        <property name="default.indexmanager">near-real-time</property>
        <property name="default.directory_provider">local-heap</property>
    </indexing>
</replicated-cache>

filesystem を使用した例:

<replicated-cache name="default">
    <indexing index="ALL">
        <property name="default.indexmanager">near-real-time</property>
    </indexing>
</replicated-cache>

14.2.2.6. 外部インデックス

Data Grid 自体によって管理されている共有インデックスと非共有インデックスを持つこと以外に、サードパーティーの検索エンジンにインデックスをオフロードすることができます。現在、Data Grid は Elasticsearch を外部インデックスストレージとしてサポートします。

14.2.2.6.1. Elasticsearch IndexManager (実験的)

このインデックスマネージャーは、すべてのインデックスを外部 Elasticsearch サーバーに転送します。これは実験的な統合であり、一部の機能は利用できません。たとえば、@IndexedEmbedded アノテーションの indexNullAs現在サポートされていません

設定:

<indexing index="PRIMARY_OWNER">
    <property name="default.indexmanager">elasticsearch</property>
    <property name="default.elasticsearch.host">link:http://elasticHost:9200</property>
    <!-- other elasticsearch configurations -->
</indexing>

Data Grid は Elasticsearch を単一の共有インデックスとみなすため、インデックスモードは LOCAL に設定する必要があります。設定プロパティーの完全な説明など、Elasticsearch 統合の詳細は、Hibernate Search マニュアル を参照してください。

14.2.2.7. 自動設定

属性 auto-config は、キャッシュタイプに基づいてインデックスを設定する簡単な方法を提供します。レプリケートされたキャッシュとローカルキャッシュの場合、インデックスは、他のプロセスと共有されず、ディスク上で永続化されるように設定されます。また、オブジェクトがインデックス化されてから、検索に使用可能な時点 (ほぼリアルタイム) の間に最小遅延が発生するように設定されます。

<local-cache name="default">
    <indexing index="PRIMARY_OWNER" auto-config="true"/>
</local-cache>
注記

auto-config を介して追加されたプロパティーを再定義したり、新しいプロパティーを追加したりできるため、高度なチューニングが可能になります。

auto 設定は、レプリケートされたキャッシュ、およびローカルキャッシュに以下のプロパティーを追加します。

プロパティー名valuedescription

default.directory_provider

Filesystem

ファイルシステムベースのインデックス。詳細は、Hibernate Search のドキュメント を参照してください。

default.exclusive_index_use

true

排他モードでのインデックス化操作が可能で、Hibernate Search による書き込みの最適化が可能になります。

default.indexmanager

Near Real Time

Lucene の near real ti me機能を利用します。つまり、インデックス化されたオブジェクトは検索にすぐに利用できます。

default.reader.strategy

shared

複数のクエリーでインデックスリーダーを再利用するため、再度開くのを防ぎます。

分散キャッシュの場合、auto-config は Data Grid 自体のインデックスを設定し、インデックス化操作がインデックスに書き込む単一のノードに送信されるマスター/スレーブメカニズムとして内部的に処理されます。

分散キャッシュの自動設定プロパティーは次のとおりです。

プロパティー名valuedescription

default.directory_provider

infinispan

Data Grid に保存されているインデックス。詳細は、Hibernate Search のドキュメント を参照してください。

default.exclusive_index_use

true

排他モードでのインデックス化操作が可能で、Hibernate Search による書き込みの最適化が可能になります。

default.indexmanager

org.infinispan.query.indexmanager.InfinispanIndexManager

Data Grid クラスターの単一ノードへのインデックス書き込みを委譲します。

default.reader.strategy

shared

複数のクエリーでインデックスリーダーを再利用し、再度開くのを防ぎます。

14.2.2.8. 再インデックス化

場合によっては、Cache に保存されているデータから Lucene インデックスを再構築する必要がある場合があります。Analyzers はインデックスの記述方法に影響を与えるため、タイプでインデックスの定義を変更する場合や、一部の Analyzer パラメーターを変更する場合はインデックスを再構築する必要があります。また、一部のシステム管理エラーによって破棄された場合は、インデックスを再構築する必要がある場合があります。インデックスを再構築するには、MassIndexer への参照を取得して開始するには、グリッド内のすべてのデータを再処理する必要があるため、時間がかかる場合があります。

// Blocking execution
SearchManager searchManager = Search.getSearchManager(cache);
searchManager.getMassIndexer().start();

// Non blocking execution
CompletableFuture<Void> future = searchManager.getMassIndexer().startAsync();
ヒント

これは、org.infinispan:type=Query,manager="{name-of-cache-manager}",cache="{name-of-cache}",component=MassIndexer 配下に登録されている MassIndexer MBeanstart JMX 操作としても利用できます。

14.2.2.9. マッピングエンティティー

Data Grid は、エンティティーレベルでインデックス作成の詳細な設定を定義するため Hibernate Search の API に依存します。この設定には、アノテーションが付けられたフィールド、使用するアナライザー、ネストされたオブジェクトのマッピング方法などが含まれます。詳細なドキュメントは the Hibernate Search manual を参照してください。

14.2.2.9.1. @DocumentId

Hibernate Search とは異なり、@DocumentId を使用してフィールドを識別子としてマーク付けすると、Data Grid は値を保存するために使用されるキーになります。すべての @Indexed オブジェクトの識別子は、値を保存するために使用されるキーになります。@Transformable、カスタム型、およびカスタム FieldBridge 実装の組み合わせを使用して、キーのインデックス化方法をカスタマイズできます。

14.2.2.9.2. @Transformable keys

各値のキーはインデックス化する必要があり、キーインスタンスを String で変換する必要があります。Data Grid には、共通のプリミティブをエンコードするためのデフォルトの変換ルーチンが含まれていますが、カスタムキーを使用するには org.infinispan.query.Transformer の実装を提供する必要があります。

アノテーションを使用したキートランスフォーマーの登録

キークラスに org.infinispan.query.Transformable のアノテーションを付け、カスタムトランスフォーマー実装が自動的に選択されます。

@Transformable(transformer = CustomTransformer.class)
public class CustomKey {
   ...
}

public class CustomTransformer implements Transformer {
   @Override
   public Object fromString(String s) {
      ...
      return new CustomKey(...);
   }

   @Override
   public String toString(Object customType) {
      CustomKey ck = (CustomKey) customType;
      return ...
   }
}

キャッシュインデックス設定を介したキートランスフォーマーの登録

埋め込みおよびサーバー設定の両方で、key-transformers xml 要素を使用できます。

<replicated-cache name="test">
    <indexing index="ALL" auto-config="true">
        <key-transformers>
            <key-transformer key="com.mycompany.CustomKey" transformer="com.mycompany.CustomTransformer"/>
        </key-transformers>
    </indexing>
</replicated-cache>

または、Java 設定 API (組み込みモード) を使用して同じ効果を得ることができます。

   ConfigurationBuilder builder = ...
   builder.indexing().autoConfig(true)
         .addKeyTransformer(CustomKey.class, CustomTransformer.class);

実行時のプログラムによるトランスフォーマーの登録

この手法を使用して、カスタムキータイプにアノテーションを付ける必要も、キャッシュインデックス設定にトランスフォーマーを追加する必要もなく、代わりに org.infinispan.query.spi.SearchManagerImplementor.registerKeyTransformer(Class<?>, Class<? extends Transformer>) を呼び出すことで、実行時に SearchManagerImplementor に追加することができます。

org.infinispan.query.spi.SearchManagerImplementor manager = Search.getSearchManager(cache).unwrap(SearchManagerImplementor.class);
manager.registerKeyTransformer(keyClass, keyTransformerClass);
注記

10.0 以降、このアプローチは非推奨になりました。これは、新しく起動したノードが初期状態遷移でキャッシュエントリーを受信し、必要なキートランスフォーマーがまだ登録されていないため (キャッシュが完全に起動した後にのみ登録可能)、それらをインデックス化できない可能性があるためです。他の利用可能なアプローチ (設定およびアノテーション) を使用してキートランスフォーマーを登録すると、望ましくない状況を回避できます。

14.2.2.9.3. プログラムによるマッピング

アノテーションを使用してエンティティーをインデックスにマップする代わりに、プログラムで設定することもできます。

次の例では、グリッドに格納され、クラスにアノテーションを付けなくても 2 つのプロパティーで検索可能にするオブジェクト Author をマップします。

import org.apache.lucene.search.Query;
import org.hibernate.search.cfg.Environment;
import org.hibernate.search.cfg.SearchMapping;
import org.hibernate.search.query.dsl.QueryBuilder;
import org.infinispan.Cache;
import org.infinispan.configuration.cache.Configuration;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.configuration.cache.Index;
import org.infinispan.manager.DefaultCacheManager;
import org.infinispan.query.CacheQuery;
import org.infinispan.query.Search;
import org.infinispan.query.SearchManager;

import java.io.IOException;
import java.lang.annotation.ElementType;
import java.util.Properties;

SearchMapping mapping = new SearchMapping();
mapping.entity(Author.class).indexed()
       .property("name", ElementType.METHOD).field()
       .property("surname", ElementType.METHOD).field();

Properties properties = new Properties();
properties.put(Environment.MODEL_MAPPING, mapping);
properties.put("hibernate.search.[other options]", "[...]");

Configuration infinispanConfiguration = new ConfigurationBuilder()
        .indexing().index(Index.NONE)
        .withProperties(properties)
        .build();

DefaultCacheManager cacheManager = new DefaultCacheManager(infinispanConfiguration);

Cache<Long, Author> cache = cacheManager.getCache();
SearchManager sm = Search.getSearchManager(cache);

Author author = new Author(1, "Manik", "Surtani");
cache.put(author.getId(), author);

QueryBuilder qb = sm.buildQueryBuilderForClass(Author.class).get();
Query q = qb.keyword().onField("name").matching("Manik").createQuery();
CacheQuery cq = sm.getQuery(q, Author.class);
assert cq.getResultSize() == 1;

14.2.3. API のクエリー

以下を使用して Data Grid にクエリーを実行できます。

  • Lucene または Hibernate Search クエリー。Data Grid は、Lucene クエリーを生成する Hibernate Search DSL を公開します。Lucene クエリーを単一ノードで実行するか、Data Grid クラスターの複数のノードにクエリーをブロードキャストできます。
  • フルテキスト拡張を含むカスタム文字列ベースのクエリー言語である Ickle クエリー。

14.2.3.1. Hibernate Search

インデックスを設定するために Hibernate Search アノテーションをサポートする以外に、他の Hibernate Search API を使用してキャッシュをクエリーすることもできます。

14.2.3.1.1. Lucene クエリーの実行

Lucene クエリーを直接実行するには、CacheQuery でこれを作成してラップするだけです。

import org.apache.lucene.search.Query;
import org.infinispan.query.CacheQuery;
import org.infinispan.query.Search;
import org.infinispan.query.SearchManager;


SearchManager searchManager = Search.getSearchManager(cache);
Query query = searchManager.buildQueryBuilderForClass(Book.class).get()
            .keyword().wildcard().onField("description").matching("*test*").createQuery();
CacheQuery<Book> cacheQuery = searchManager.getQuery(query);
14.2.3.1.2. Hibernate Search DSL の使用

Hibernate Search DSL を使用して Lucene クエリーを作成できます。以下に例を示します。

import org.infinispan.query.Search;
import org.infinispan.query.SearchManager;
import org.apache.lucene.search.Query;

Cache<String, Book> cache = ...

SearchManager searchManager = Search.getSearchManager(cache);

Query luceneQuery = searchManager
                         .buildQueryBuilderForClass(Book.class).get()
                         .range().onField("year").from(2005).to(2010)
                         .createQuery();

List<Object> results = searchManager.getQuery(luceneQuery).list();

この DSL のクエリー機能の詳細は、Hibernate Search のマニュアル の関連のセクションを参照してください。

14.2.3.1.4. 空間クエリー

Data Grid は Spatial Queries もサポートしているため、完全テキストを距離、地理、地理コーディネートに基づく制限と組み合わせることができます。

この例では、@Latitude および @Longitude とともに検索されるエンティティーで @Spatial アノテーションを使用することから開始します。

@Indexed
@Spatial
public class Restaurant {

      @Latitude
      private Double latitude;

      @Longitude
      private Double longitude;

      @Field(store = Store.YES)
      String name;

      // Getters, Setters and other members omitted

   }

空間クエリーを実行するには、Hibernate Search DSL を使用できます。

// Cache is configured as indexed
Cache<String, Restaurant> cache = ...

// Obtain the SearchManager
Searchmanager searchManager = Search.getSearchManager(cache);

// Build the Lucene Spatial Query
Query query = Search.getSearchManager(cache).buildQueryBuilderForClass(Restaurant.class).get()
          .spatial()
            .within( 2, Unit.KM )
              .ofLatitude( centerLatitude )
              .andLongitude( centerLongitude )
            .createQuery();

// Wrap in a cache Query
CacheQuery<Restaurant> cacheQuery = searchManager.getQuery(query);

List<Restaurant> nearBy = cacheQuery.list();

詳細については、Hibernate Search のマニュアル を参照してください。

14.2.3.1.5. IndexedQueryMode

インデックス化されたクエリーのクエリーモードを指定できます。IndexedQueryMode.BROADCAST を使用すると、クラスターの各ノードにクエリーをブロードキャストし、結果を取得してから呼び出し元に戻すことができます。各ノードのローカルインデックスには、インデックス化されたデータのサブセットのみがあるため、共有されていないインデックス と併用する場合に適しています。

IndexedQueryMode.FETCH は呼び出し元でクエリーを実行します。クラスター全体のデータのすべてのインデックスがローカルで利用可能な場合、パフォーマンスが最適になります。それ以外の場合は、このクエリーモードにはリモートノードからインデックスデータの取得が含まれる場合があります。

IndexedQueryMode は、Ickle クエリーおよび Lucene クエリーでサポートされます(Query DSL ではサポートされません)。

以下に例を示します。

CacheQuery<Person> broadcastQuery = Search.getSearchManager(cache).getQuery(new MatchAllDocsQuery(), IndexedQueryMode.BROADCAST);

List<Person> result = broadcastQuery.list();

14.2.3.2. Data Grid Query DSL

注記

Query DSL (QueryBuilder および関連インターフェイス)は非推奨となり、次のメジャーバージョンで削除されます。代わりに Ickle クエリーを使用してください。

Data Grid は、Lucene および Hibernate Search に依存しない独自のクエリー DSL を提供します。基礎となるクエリーおよびインデックスメカニズムからクエリー API を切り離すと、Lucene だけでなく、同じ統一されたクエリー API を使用できるように、今後も新しい代替エンジンを導入できます。インデックスと検索の現在の実装は Hibernate Search および Lucene をベースとしているため、この章で説明するすべてのインデックス関連側面は引き続き適用されます。

新しい API は、Lucene クエリーオブジェクトを構築する低レベルの詳細にユーザーを公開せず、リモートの Hot Rod クライアントで使用できるという利点があります。ただし、詳細を分解する前に、最初に前の例から Book エンティティーのクエリーを書き込む簡単な例を見てみましょう。

Data Grid のクエリー DSL を使用したクエリーの例

import org.infinispan.query.dsl.*;

// get the DSL query factory from the cache, to be used for constructing the Query object:
QueryFactory qf = org.infinispan.query.Search.getQueryFactory(cache);

// create a query for all the books that have a title which contains "engine":
org.infinispan.query.dsl.Query query = qf.from(Book.class)
      .having("title").like("%engine%")
      .build();

// get the results:
List<Book> list = query.list();

API は org.infinispan.query.dsl パッケージにあります。クエリーは、キャッシュごとの SearchManager から取得した QueryFactory インスタンスを使用して作成されます。各 QueryFactory インスタンスは SearchManager と同じ Cache インスタンスにバインドされますが、それ以外の場合は、複数のクエリーを並行して作成するために使用できるステートレスおよびスレッドセーフオブジェクトになります。

クエリーの作成は from(Class entityType) メソッドの呼び出しで始まります。このメソッドは、指定のキャッシュから特定されたエンティティークラスにターゲットとするクエリーを作成する QueryBuilder オブジェクトを返します。

注記

クエリーは常に単一のエンティティータイプをターゲットにし、単一のキャッシュの内容に対して評価されます。複数のキャッシュでクエリーを実行したり、複数のエンティティータイプ (結合) を対象とするクエリーを作成したりすることは、サポートされていません。

QueryBuilder は、DSL メソッドの呼び出しによって指定された検索条件や設定を蓄積し、最終的に QueryBuilder.build() メソッドの呼び出しによって Query オブジェクトをビルドし、構築を完了させます。ステートフルオブジェクトであるため、(ネスト されたクエリーを除く) 複数のクエリーを同時に作成することはできませんが、後で再利用できます。

注記

この QueryBuilder は Hibernate Search とは異なりますが、同様の目的があるため、同じ名前になります。あいまいさを防ぐために、まもなく名前を変更することを検討しています。

クエリーの実行と結果のフェッチは、Queryオブジェクトの list() メソッドを呼び出すのと同じくらい簡単です。実行すると、Query オブジェクトは再利用できません。新しい結果を取得するためにこれを再実行する必要がある場合は、QueryBuilder.build() を呼び出して新しいインスタンスを取得する必要があります。

14.2.3.2.1. Operator のフィルターリング

クエリーの構築は、複数の条件を設定する階層的なプロセスで、この階層に従って説明するのが最適です。

クエリー条件の最も単純な形式は、ゼロ以上の引数を受け入れるフィルター Operator に応じて、エンティティー属性の値を制限することです。エンティティー属性は、利用可能なすべての Operator を公開する中間コンテキストオブジェクト(FilterConditionEndContext)を返すクエリービルダーの having(String attributePath) メソッドを呼び出して指定されます。FilterConditionEndContext で定義された各メソッドは、2 つの引数を持つ between と引数を持たない isNull を除いて、引数を受け取る演算子です。引数はクエリーの作成時に静的に評価されるため、SQL の相関サブクエリーと同様の機能を探している場合は、現在利用できません。

// a single query criterion
QueryBuilder qb = ...
qb.having("title").eq("Hibernate Search in Action");
表14.1 FilterConditionEndContext は、以下のフィルターリング Operator を公開します。
フィルター引数説明

in

コレクション値

左のオペランドが引数として指定された値のコレクションからの要素のいずれかと等しいことを確認します。

in

オブジェクト値

左側のオペランドが、値として指定された値のリスト (固定) のいずれかと同じであることを確認します。

contains

オブジェクト値

左側の引数 (配列またはコレクションとして想定される) に指定の要素が含まれていることを確認します。

containsAll

コレクション値

左側の引数 (配列またはコレクションとして想定される) に、指定されたコレクションのすべての要素を任意の順序で含まれていることを確認します。

containsAll

オブジェクト値

左側の引数 (配列またはコレクションとして想定される) に、指定したすべての要素を任意の順序で含まれていることを確認します。

containsAny

コレクション値

左側の引数 (配列またはコレクションとして想定される) に、指定されたコレクションの要素が含まれていることを確認します。

containsAny

オブジェクト値

左側の引数 (配列またはコレクションとして想定される) に指定の要素が含まれていることを確認します。

isNull

 

左側の引数が null であることを確認します。

like

文字列のパターン

(文字列として想定される) 左側の引数が、JPA ルールに準拠するワイルドカードパターンと一致することを確認します。

eq

オブジェクト値

左側の引数が指定の値と同じであることを確認します。

equal

オブジェクト値

eq のエイリアス。

gt

オブジェクト値

左側の引数が指定の値よりも大きいことを確認します。

gte

オブジェクト値

左側の引数が指定の値以上であることを確認します。

lt

オブジェクト値

左側の引数が指定の値未満であることを確認します。

lte

オブジェクト値

左側の引数が指定の値以下であることを確認します。

between

Object from、Object to

左側の引数が指定された範囲の制限の間にあることを確認します。

クエリ構築には、適切な順序で行わなければならない、正確に 1 回 で完了しなければならない、2 回行うとエラーになる、といったメソッド呼び出しの多段階の連鎖が必要であることに注意することが重要です。以下の例は無効であり、各ケースによって基準が無視される (あいまいなケース) または例外が出力されます (より深刻なもの)。

// Incomplete construction. This query does not have any filter on "title" attribute yet,
// although the author may have intended to add one.
QueryBuilder qb1 = ...
qb1.having("title");
Query q1 = qb1.build(); // consequently, this query matches all Book instances regardless of title!

// Duplicated completion. This results in an exception at run-time.
// Maybe the author intended to connect two conditions with a boolean operator,
// but this does NOT actually happen here.
QueryBuilder qb2 = ...
qb2.having("title").like("%Data Grid%");
qb2.having("description").like("%clustering%");   // will throw java.lang.IllegalStateException: Sentence already started. Cannot use 'having(..)' again.
Query q2 = qb2.build();
14.2.3.2.2. 埋め込みエンティティーの属性に基づくフィルタリング

having メソッドは、埋め込みエンティティー 属性を参照するためのドット区切りの属性パスも受け付けますので、以下は有効なクエリーとなります。

// match all books that have an author named "Manik"
Query query = queryFactory.from(Book.class)
      .having("author.name").eq("Manik")
      .build();

属性パスの各部分は、対応するエンティティーまたは埋め込みエンティティークラスの既存のインデックス付き属性を参照する必要があります。複数のレベルの埋め込みが可能です。

14.2.3.2.3. ブール値の条件

以下の例では、複数の属性条件を論理結合 (and) および非結合 (or) 演算子と組み合わせて、より複雑な条件を作成する方法を示しています。ブール値演算子のよく知られている Operator の優先順位ルールはここで適用されるため、構築中に DSL メソッド呼び出しの順序は無関係です。ここで、or が最初に呼び出された場合でも、and Operator の優先順位は or よりも高くなります。

// match all books that have "Data Grid" in their title
// or have an author named "Manik" and their description contains "clustering"
Query query = queryFactory.from(Book.class)
  .having("title").like("%Data Grid%")
  .or().having("author.name").eq("Manik")
  .and().having("description").like("%clustering%")
  .build();

ブール値の否定は、論理演算子の中で最も優先され、次の単純な属性条件にのみ適用される not 演算子で実現されます。

// match all books that do not have "Data Grid" in their title and are authored by "Manik"
Query query = queryFactory.from(Book.class)
  .not().having("title").like("%Data Grid%")
  .and().having("author.name").eq("Manik")
  .build();
14.2.3.2.4. ネストされた条件

論理演算子の優先順位の変更は、ネストされたフィルター条件で行います。論理演算子を使用すると、以前示される 2 つの単純な属性条件を接続できますが、同じクエリーファクトリーで作成された後続の複雑な条件で単純な属性条件を接続することもできます。

// match all books that have an author named "Manik" and their title contains
// "Data Grid" or their description contains "clustering"
Query query = queryFactory.from(Book.class)
  .having("author.name").eq("Manik")
  .and(queryFactory.having("title").like("%Data Grid%")
          .or().having("description").like("%clustering%"))
  .build();
14.2.3.2.5. プロジェクション

一部のユースケースでは、属性のごく一部のみがアプリケーションによって実際に使用されている場合、特にドメインエンティティーにエンティティーが埋め込まれている場合、ドメインオブジェクト全体を返すのはやり過ぎです。クエリー言語を使用すると、プロジェクションを返す属性 (または属性パス) のサブセットを指定できます。展開が使用される場合、Query.list() はドメインエンティティー全体を返しませんが、Object[]List (プロジェクト化された属性に対応する配列) を返します。

// match all books that have "Data Grid" in their title or description
// and return only their title and publication year
Query query = queryFactory.from(Book.class)
  .select("title", "publicationYear")
  .having("title").like("%Data Grid%")
  .or().having("description").like("%Data Grid%"))
  .build();
14.2.3.2.6. ソート

1 つ以上の属性または属性パスに基づいて結果の順序は、属性パスとソート方向を受け入れる QueryBuilder.orderBy( ) メソッドで行われます。複数の並べ替え基準を指定すると、orderBy メソッドの呼び出し順序が優先順位を決定します。ただし、複数の並べ替え基準は、各属性の個別のソート操作のシーケンスではなく、指定された属性のタプルで動作します。

// match all books that have "Data Grid" in their title or description
// and return them sorted by the publication year and title
Query query = queryFactory.from(Book.class)
  .orderBy("publicationYear", SortOrder.DESC)
  .orderBy("title", SortOrder.ASC)
  .having("title").like("%Data Grid%")
  .or().having("description").like("%Data Grid%"))
  .build();
14.2.3.2.7. ページネーション

QueryBuildermaxResults プロパティーを設定することにより、返される結果の数を制限できます。これは、結果セットのページネーションを実現するために startOffset の設定と併用できます。

// match all books that have "clustering" in their title
// sorted by publication year and title
// and return 3'rd page of 10 results
Query query = queryFactory.from(Book.class)
  .orderBy("publicationYear", SortOrder.DESC)
  .orderBy("title", SortOrder.ASC)
  .startOffset(20)
  .maxResults(10)
  .having("title").like("%clustering%")
  .build();
注記

フェッチされる結果が maxResults に制限されている場合でも、Query.getResultSize() を呼び出すことで、一致する結果の合計数を見つけることができます。

14.2.3.2.8. グループ化およびアグリゲーション

Data Grid には、グループ化フィールドのセットに従ってクエリー結果をグループ化し、各グループに分類される値のセットに集計関数を適用することにより、各グループからの結果の集計を構築する機能があります。グループ化および集計は、プロジェクションクエリーにのみ適用できます。サポートされる集約は avg、sum、count、max、min です。グループ化フィールドのセットは groupBy (field) メソッドで指定され、複数回呼び出すことができます。グループフィールドの定義に使用される順序は関係ありません。プロジェクションで選択されたすべてのフィールドは、グループ化フィールドであるか、以下で説明するグループ化関数の 1 つを使用して集約される必要があります。Projection フィールドは集約され、同時にグループ化に使用できます。グループ化フィールドのみを選択し、集計フィールドは選択しないクエリーは有効です。

例: ブックマークは作成者別にグループ化し、それらをカウントします。

Query query = queryFactory.from(Book.class)
    .select(Expression.property("author"), Expression.count("title"))
    .having("title").like("%engine%")
    .groupBy("author")
    .build();
注記

選択したすべてのフィールドに集計関数が適用され、グループ化にフィールドが使用されないプロジェクションクエリーが許可されます。この場合、集計は、単一のグローバルグループが存在するかのようにグローバルに計算されます。

14.2.3.2.9. 集約

avg、sum、count、max、min の集約関数をフィールドに適用できます。

  • avg() - 一連の数字の平均を計算します。許可される値は、java.lang.Number のプリミティブ番号およびインスタンスです。結果は java.lang.Double で表されます。null 以外の値がない場合、結果は代わりに null になります。
  • count()- null 以外の行の数をカウントし、java.lang.Long を返します。null 以外の値がない場合、結果は代わりに 0 になります。
  • max() - 見つかった最も大きな値を返します。許可される値は java.lang.Comparable のインスタンスである必要があります。null 以外の値がない場合、結果は代わりに null になります。
  • min() - 見つかった最小値を返します。許可される値は java.lang.Comparable のインスタンスである必要があります。null 以外の値がない場合、結果は代わりに null になります。
  • sum() - 数字のセットの合計を計算します。null 以外の値がない場合、結果は代わりに null になります。以下の表は、指定のフィールドに基づいて返されるタイプを示しています。
表14.2 テーブル合計戻り値のタイプ
フィールドタイプ戻り値のタイプ

Integral (BigInteger 以外)

Long

Float または Double

double

BigInteger

BigInteger

BigDecimal

BigDecimal

14.2.3.2.10. グループ化および集計を使用したクエリーの評価

集計クエリーには、通常のクエリーのようにフィルター条件を含めることができます。フィルターリングは、グループ化操作の前後の 2 つのステージで実行できます。グループ化操作の実行前に groupBy() メソッドを起動する前に定義されたフィルター条件はすべて、キャッシュエントリーに直接 (最終的な展開ではなく) キャッシュエントリーに適用されます。これらのフィルター条件は、照会されたエンティティータイプの任意のフィールドを参照し、グループ化ステージの入力となるデータセットを制限することを目的としています。groupBy メソッドの呼び出し後に定義されたフィルター条件はすべて、展開およびグループ化操作の結果が展開されます。このフィルター条件は、groupBy フィールドまたは集約されたフィールドのいずれかを参照できます。select 句で指定されていない集約フィールドを参照することは許可されています。ただし、非集計フィールドと非グループ化フィールドを参照することは禁止されています。このフェーズでフィルターリングすると、プロパティーに基づいてグループの数が減ります。通常のクエリーと同様にソートを指定することもできます。順序付け操作は、グループ化操作後に実行され、groupBy フィールドまたは集約されたフィールドのいずれかを参照できます。

14.2.3.2.11. 名前付きクエリーパラメーターの使用

実行ごとに新しい Query オブジェクトを作成する代わりに、実行前に実際の値に置き換えることができる名前付きパラメーターをクエリーに含めることができます。これにより、クエリーを 1 度定義し、複数回効率的に実行できます。パラメーターは、Operator の右側でのみ使用でき、通常の定数値ではなく、org.infinispan.query.dsl.Expression.param(String paramName) メソッドによって生成されたオブジェクトを Operator に提供することで、クエリーの作成時に定義されます。パラメーターが定義されたら、以下の例に示すように Query.setParameter(parameterName, value) または Query.setParameters(parameterMap) のいずれかを呼び出すことで設定できます。

import org.infinispan.query.Search;
import org.infinispan.query.dsl.*;
[...]

QueryFactory queryFactory = Search.getQueryFactory(cache);
// Defining a query to search for various authors and publication years
Query query = queryFactory.from(Book.class)
    .select("title")
    .having("author").eq(Expression.param("authorName"))
    .and()
    .having("publicationYear").eq(Expression.param("publicationYear"))
    .build();

// Set actual parameter values
query.setParameter("authorName", "Doe");
query.setParameter("publicationYear", 2010);

// Execute the query
List<Book> found = query.list();

または、実際のパラメーター値のマップを指定して、複数のパラメーターを一度に設定することもできます。

複数の名前付きパラメーターを一度に設定する

import java.util.Map;
import java.util.HashMap;

[...]

Map<String, Object> parameterMap = new HashMap<>();
parameterMap.put("authorName", "Doe");
parameterMap.put("publicationYear", 2010);

query.setParameters(parameterMap);

注記

クエリーの解析、検証、および実行計画の作業の大部分は、パラメーターでのクエリーの最初の実行時に実行されます。この作業は後続の実行時には繰り返し行われないため、クエリーパラメーターではなく定数値を使用した同様のクエリーの場合よりもパフォーマンスが向上します。

14.2.3.2.12. その他のクエリー DSL サンプル

Query DSL API の使用を調べる最善の方法は、テストスイートを確認することです。QueryDslConditionsTest は簡単な例です。

14.2.3.3. Ickle

Ickle クエリー言語を使用して、ライブラリーおよびリモートクライアント/サーバーモードの両方でリレーショナルおよびフルテキストクエリーを作成します。

Ickle は文字列ベースであり、以下の特徴があります。

  • Java クラスをクエリーし、Protocol Buffers をサポートします。
  • クエリーは単一のエンティティータイプをターゲットにすることができます。
  • クエリーは、コレクションを含む埋め込みオブジェクトのプロパティーをフィルタリングできます。
  • プロジェクション、集計、ソート、名前付きパラメーターをサポートします。
  • インデックス付きおよびインデックスなしの実行をサポートします。
  • 複雑なブール式をサポートします。
  • フルテキストクエリーをサポートします。
  • user.age > sqrt(user.shoeSize+3) などの式の計算をサポートしません。
  • 結合をサポートしません。
  • サブクエリーをサポートしません。
  • さまざまな Data Grid API でサポートされています。Query が受け入れるたびに、継続的なクエリーやリスナーのイベントフィルターに QueryBuilder が生成されます。

API を使用するには、まず QueryFactory をキャッシュに取得してから、.create() メソッドを呼び出し、クエリーで使用する文字列を渡します。たとえば、以下のようになります。

QueryFactory qf = Search.getQueryFactory(remoteCache);
Query q = qf.create("from sample_bank_account.Transaction where amount > 20");

Ickle を使用する場合、フルテキスト演算子で使用されるすべてのフィールドは、Indexed および Analysed の両方である必要があります。

14.2.3.3.1. Ickle クエリー言語パーサー構文

Ickle クエリー言語のパーサー構文には、いくつかの重要なルールがあります。

  • 空白は重要ではありません。
  • フィールド名ではワイルドカードはサポートされません。
  • デフォルトのフィールドがないため、フィールド名またはパスは必ず指定する必要があります。
  • && および || は、フルテキストと JPA 述語の両方で、AND または OR の代わりに使用できます。
  • !NOT の代わりに使用できます。
  • 足りないブール値 Operator は OR として解釈されます。
  • 文字列の用語は、一重引用符または二重引用符で囲む必要があります。
  • ファジー性とブースティングは任意の順序で受け入れられず、常にファジー性が最初になります。
  • <> の代わりに != が許可されます。
  • ブーディングは、>>=< Operator には適用できません。同じ結果を達成するために範囲を使用することができます。
14.2.3.3.2. Fuzzy クエリー

ファジークエリー add ~ を整数とともに実行するには、用語の後に使用される用語からの距離を表します。たとえば、以下のようになります。

Query fuzzyQuery = qf.create("from sample_bank_account.Transaction where description : 'cofee'~2");
14.2.3.3.3. 範囲クエリー

以下の例に示すように、範囲クエリーを実行するには、中括弧のペア内で指定の境界を定義します。

Query rangeQuery = qf.create("from sample_bank_account.Transaction where amount : [20 to 50]");
14.2.3.3.4. フレーズクエリー

次の例に示すように、単語のグループは引用符で囲むことで検索できます。

Query q = qf.create("from sample_bank_account.Transaction where description : 'bus fare'");
14.2.3.3.5. 近接クエリー

特定の距離内で 2 つの用語を検索して近接クエリーを実行するには、フレーズの後に距離とともに ~ を追加します。たとえば、以下の例では、キャンセル と fee という単語が 3 個以上ありません。

Query proximityQuery = qf.create("from sample_bank_account.Transaction where description : 'canceling fee'~3 ");
14.2.3.3.6. ワイルドカードクエリー

単一文字およびマルチ文字のワイルドカード検索の両方を実行できます。

  • 単一文字のワイルドカード検索は ? 文字で使用できます。
  • マルチ文字のワイルドカード検索は * 文字で使用できます。

テキストを検索するか、テストするには、以下の単一文字のワイルドカード検索を使用します。

Query wildcardQuery = qf.create("from sample_bank_account.Transaction where description : 'te?t'");

test、tests、または tester を検索するには、以下のマルチ文字のワイルドカード検索を使用します。

Query wildcardQuery = qf.create("from sample_bank_account.Transaction where description : 'test*'");
14.2.3.3.7. 正規表現クエリー

正規表現クエリーは、/ 間のパターンを指定することで実行できます。Ickle は Lucene の正規表現構文を使用しているため、単語 moat または boat を検索するには、以下を使用できます。

Query regExpQuery = qf.create("from sample_library.Book  where title : /[mb]oat/");
14.2.3.3.8. クエリーのブースト

用語は、指定のクエリーにおける耐障害性を高めるために ^ を追加し、条件を強化できます。たとえば、ビールとビールとの関連性が 3 倍高いビールとワインを含むタイトルを検索するには、次のように使用できます。

Query boostedQuery = qf.create("from sample_library.Book where title : beer^3 OR wine");

14.2.3.4. 継続的なクエリー

継続的なクエリーにより、アプリケーションはクエリーフィルターに現在一致したエントリーを受信するリスナーを登録し、さらにキャッシュ操作の結果としてクエリーされたデータセットへの変更を継続的に通知できます。これには、セットに結合された値の着信一致、更新された一致、変更されて引き続き一致する一致値、およびセットを離れた値の発信一致が含まれます。継続的なクエリーを使用することにより、アプリケーションは、変更を検出するために同じクエリーを繰り返し実行する代わりに、イベントの安定したストリームを受信し、リソースがより効率的に使用されるようになります。たとえば、以下のユースケースすべてで、継続的なクエリーを使用できます。

  • Person エンティティーに age プロパティーがあり、ユーザーアプリケーションによって更新される 18〜25 歳の人を返す。
  • $2000 を超えるすべてのトランザクションを返す。
  • F1 レーサーのラップスピードが 1:45.00 秒未満だったすべての時間を返す (キャッシュにラップエントリーが含まれていて、レース中にラップがライブ入力されていると仮定)。
14.2.3.4.1. 連続クエリー実行

継続的クエリーは、以下の場合に通知されるリスナーを使用します。

  • エントリーは、Join イベントによって表される指定のクエリーの一致を開始します。
  • 一致するエントリーが更新され、Update event によって表されるクエリーの一致が継続されます。
  • エントリーは、Leave イベントで表されるクエリーの一致を停止します。

クライアントが継続的なクエリーリスナーを登録すると、すぐにクエリーに一致する結果の受信を開始します (上記のように Join イベントとして受信された)。さらに、通常は作成、変更、削除、または有効期限イベントを生成するキャッシュ操作の結果として、他のエントリーが Join イベントとしてクエリーの一致を開始したとき、または Leave イベントとしてクエリーの一致を停止したときに、後続の通知を受信します。更新されたキャッシュエントリーは、操作の前後でエントリーがクエリーフィルターに一致する場合、Update イベントを生成します。要約すると、リスナーが JoinUpdate、または Leave イベントを受信するかどうかを決定するために使用されるロジックは次のとおりです。

  1. 古い値と新しい値の両方に対するクエリーが false と評価された場合、イベントは抑制されます。
  2. 古い値に対するクエリーが false と評価され、新しい値に対するクエリーが true と評価された場合、Join イベントが送信されます。
  3. 古い値と新しい値の両方のクエリーが true と評価されると、Update イベントが送信されます。
  4. 古い値に対するクエリーが true と評価され、新しい値に対するクエリーが false と評価された場合、Leave イベントが送信されます。
  5. 古い値に対するクエリーが true と評価され、エントリーが削除または期限切れになると、Leave イベントが送信されます。
注記

継続的なクエリーは、グループ化、集計、およびソート操作を除き、Query DSL の完全な機能を使用できます。

14.2.3.4.2. 継続的なクエリーの実行

継続的なクエリーを作成するには、最初に Query オブジェクトを作成して開始します。これは、クエリー DSL セクション で説明されています。次に、キャッシュの ContinuousQuery (org.infinispan.query.api.continuous.ContinuousQuery) オブジェクトを取得し、クエリーと継続的なクエリーリスナー (org.infinispan.query.api.continuous.ContinuousQueryListener) を登録する必要があります。キャッシュに関連付けられた ContinuousQuery オブジェクトは、リモートモードで実行されている場合は静的メソッド org.infinispan.client.hotrod.Search.getContinuousQuery(RemoteCache<K, V> cache) を、組み込みモードで実行されている場合は org.infinispan.query.Search.getContinuousQuery(Cache<K, V> cache) を呼び出して取得することができます。リスナーを作成したら、ContinuousQuery の addContinuousQueryListener メソッドを使用して登録できます。

continuousQuery.addContinuousQueryListener(query, listener);

以下の例は、埋め込みモードの単純な継続的なクエリーのユースケースを示しています。

継続的クエリーの登録

import org.infinispan.query.api.continuous.ContinuousQuery;
import org.infinispan.query.api.continuous.ContinuousQueryListener;
import org.infinispan.query.Search;
import org.infinispan.query.dsl.QueryFactory;
import org.infinispan.query.dsl.Query;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

[...]

// We have a cache of Persons
Cache<Integer, Person> cache = ...

// We begin by creating a ContinuousQuery instance on the cache
ContinuousQuery<Integer, Person> continuousQuery = Search.getContinuousQuery(cache);

// Define our query. In this case we will be looking for any Person instances under 21 years of age.
QueryFactory queryFactory = Search.getQueryFactory(cache);
Query query = queryFactory.from(Person.class)
    .having("age").lt(21)
    .build();

final Map<Integer, Person> matches = new ConcurrentHashMap<Integer, Person>();

// Define the ContinuousQueryListener
ContinuousQueryListener<Integer, Person> listener = new ContinuousQueryListener<Integer, Person>() {
    @Override
    public void resultJoining(Integer key, Person value) {
        matches.put(key, value);
    }

    @Override
    public void resultUpdated(Integer key, Person value) {
        // we do not process this event
    }

    @Override
    public void resultLeaving(Integer key) {
        matches.remove(key);
    }
};

// Add the listener and the query
continuousQuery.addContinuousQueryListener(query, listener);

[...]

// Remove the listener to stop receiving notifications
continuousQuery.removeContinuousQueryListener(listener);

21 歳未満の Person インスタンスがキャッシュに追加されると、リスナーによって受信され、 matches マップに配置されます。これらのエントリーがキャッシュから削除されるか、年齢が 21 歳以上に変更されると、それらは matches から削除されます。

14.2.3.4.3. 継続的なクエリーの削除

クエリーのそれ以上の実行を停止するには、リスナーを単に削除します。

continuousQuery.removeContinuousQueryListener(listener);
14.2.3.4.4. 継続的なクエリーのパフォーマンスに関する注意

継続的なクエリーは、アプリケーションに一定の更新ストリームを提供するように設計されており、特に幅広いクエリーに対して非常に多くのイベントが生成される可能性があります。イベントごとに新規の一時的なメモリー割り当てが行われます。この動作によりメモリーが不足し、クエリーが適切に設計されていない場合、OutOfMemoryErrors(特にリモートモード) が発生する可能性があります。このような問題を防ぐために、一致するエントリーの数と各一致のサイズの両方の観点から、各クエリーが必要な最小限の情報をキャプチャーし (プロジェクションを使用して興味深いプロパティーをキャプチャできます)、各 ContinuousQueryListener が受信したすべてのイベントをブロックせずにすばやく処理し、リッスンするキャッシュから一致する新しいイベントの生成につながるアクションの実行を回避するように設計されていることが強く推奨されます。

Red Hat logoGithubRedditYoutubeTwitter

詳細情報

試用、購入および販売

コミュニティー

Red Hat ドキュメントについて

Red Hat をお使いのお客様が、信頼できるコンテンツが含まれている製品やサービスを活用することで、イノベーションを行い、目標を達成できるようにします。

多様性を受け入れるオープンソースの強化

Red Hat では、コード、ドキュメント、Web プロパティーにおける配慮に欠ける用語の置き換えに取り組んでいます。このような変更は、段階的に実施される予定です。詳細情報: Red Hat ブログ.

会社概要

Red Hat は、企業がコアとなるデータセンターからネットワークエッジに至るまで、各種プラットフォームや環境全体で作業を簡素化できるように、強化されたソリューションを提供しています。

© 2024 Red Hat, Inc.