第7章 Hibernate Search
7.1. Hibernate Search の使用 リンクのコピーリンクがクリップボードにコピーされました!
7.1.1. Hibernate Search について リンクのコピーリンクがクリップボードにコピーされました!
Hibernate Search は、Hibernate アプリケーションに完全なテキスト検索機能を提供します。これは、全文、あいまい、位置情報検索などの SQL ベースのソリューションには適していないアプリケーションの検索に適しています。Hibernate Search は Apache Lucene を全文検索エンジンとして使用しますが、メンテナンスのオーバーヘッドを最小限に抑えるように設計されています。設定が完了したら、インデックス、クラスタリング、およびデータ同期は透過的に維持されるため、ビジネス要件を満たすことができます。
以前のリリースの JBoss EAP には Hibernate 4.2 および Hibernate Search 4.6 が含まれていました。JBoss EAP 7 には Hibernate 5 と Hibernate Search 5.5 が含まれています。
Hibernate Search 5.5 は Java 7 と機能し、Lucene 5.3.x 上に構築されるようになりました。ネイティブ Lucene API を使用している場合は、必ずこのバージョンに合わせてください。
7.1.2. Hibernate Search の概要 リンクのコピーリンクがクリップボードにコピーされました!
Hibernate Search はインデックスコンポーネントとインデックス検索コンポーネントで構成されており、どちらも Apache Lucene によってサポートされます。エンティティーがデータベースから挿入、更新、または削除されるたびに、Hibernate Search は Hibernate イベントシステムからこのイベントを追跡し、インデックスの更新をスケジュール設定します。これらの更新はすべて、Apache Lucene API と直接対話せずに処理されます。その代わりに、基盤の Lucene インデックスとの対話は IndexManager で処理されます。デフォルトでは、IndexManager と Lucene インデックスの間に 1 対 1 の関係があります。IndexManager は、選択した back end、reader strategy および DirectoryProvider を含む特定のインデックス設定を抽象化します。
インデックスが作成されると、基盤の Lucene インフラストラクチャーを処理するのではなく、エンティティーを検索し、管理エンティティーのリストを返すことができます。同じ永続コンテキストが Hibernate と Hibernate Search 間で共有されます。FullTextSession クラスは Hibernate Session クラスの上に構築されるため、アプリケーションコードで HQL、Java Persistence クエリー言語 (JPQL)、またはネイティブクエリーと同じように統一されたorg.hibernate.Query または javax.persistence.Query API を使用できます。
トランザクションバッチモードは、Java Naming および Directory Interface ベースであるかに関わらず、すべての操作に推奨されます。
Java Naming and Directory Interface または Jakarta Transactions に関わらず、データベースと Hibernate Search の両方に対して、トランザクションで操作を実行することが推奨されます。
Hibernate Search は、アトミック会話として知られる Hibernate または EntityManager の長い会話パターンで完全に機能します。
7.1.3. ディレクトリープロバイダーについて リンクのコピーリンクがクリップボードにコピーされました!
Hibernate Search インフラストラクチャーの一部である Apache Lucene には、インデックスの保管にディレクトリーという概念があります。Hibernate Search は、Directory Provider 経由で Lucene Directory インスタンスの初期化および設定を処理します。
Directory_provider プロパティーは、インデックスを保存するために使用するディレクトリープロバイダーを指定します。デフォルトのファイルシステムディレクトリープロバイダーは filesystem で、ローカルファイルシステムを使用してインデックスを格納します。
7.1.4. Worker について リンクのコピーリンクがクリップボードにコピーされました!
Lucene インデックスの更新は、Hibernate Search Worker によって処理されます。これは、コンテキストが終了すると、すべてのエンティティーの変更を受け取り、コンテキストでキューに入れてそれらをキューに適用します。最も一般的なコンテキストはトランザクションですが、エンティティーの変更数または他のアプリケーションイベントの数に依存する可能性があります。
効率性を向上するために、対話はバッチ化され、通常はコンテキストの終了時に適用されます。トランザクション以外では、インデックスの更新操作は実際のデータベース操作の直後に実行されます。継続中のトランザクションの場合、インデックス更新操作はトランザクションのコミットフェーズに対してスケジュール設定され、トランザクションのロールバック時に破棄されます。worker は特定のバッチサイズ制限で設定でき、その後、インデックスはコンテキストに関係なく実行されます。
この手法では、インデックス更新を処理するのに以下のようなメリットがあります。
- パフォーマンス: 操作がバッチで実行されると、Lucene インデックスのパフォーマンスが向上しました。
- ACIDity: 実行されるワークはデータベーストランザクションによって実行されたものと同じスコーピングを持ち、トランザクションがコミットされた場合にのみ実行されます。これは、厳密な意味では ACID ではありませんが、ACID の動作では、ソースからいつでも再構築できるため、完全なテキスト検索インデックスにはあまり役に立ちません。
no scope と transactional という 2 つのバッチモードは、自動コミットとトランザクション動作と同等です。パフォーマンスの観点からは、transactional モードが推奨されます。スコープの選択は透過的に行われます。Hibernate Search はトランザクションの存在を検出し、スコーピングを調整します。
7.1.5. バックエンド設定と操作 リンクのコピーリンクがクリップボードにコピーされました!
7.1.5.1. バックエンド リンクのコピーリンクがクリップボードにコピーされました!
Hibernate Search は、さまざまなバックエンドを使用してワークのバッチ処理を行います。バックエンドは、設定オプション default.worker.backend に制限されません。このプロパティーは、バックエンド設定の一部である BackendQueueProcessor インターフェースの実装を指定します。Jakarta Messaging バックエンドなどのバックエンドを設定するには、追加の設定が必要です。
7.1.5.2. Lucene リンクのコピーリンクがクリップボードにコピーされました!
Lucene モードでは、ノードのすべてのインデックス更新は、ディレクトリープロバイダーを使用して同じノードから Lucene ディレクトリーに対して実行されます。このモードは、クラスター化されていない環境や、共有ディレクトリーストアを持つクラスター環境で使用します。
図7.1 Lucene バックエンド設定
Lucene モードは、ディレクトリーがロックストラテジーを管理するクラスター化またはクラスター化されたアプリケーションをターゲットに設定します。Lucene モードの主な利点は、Lucene クエリーの変更の単純化と即時表示です。Near Real Time (NRT) バックエンドは、クラスター化されていないインデックス設定や共有されていないインデックス設定の代替バックエンドです。
7.1.5.3. Jakarta Messaging リンクのコピーリンクがクリップボードにコピーされました!
ノードのインデックス更新は Jakarta Messaging キューに送信されます。一意のリーダーはキューを処理し、マスターインデックスを更新します。マスターインデックスはその後、スレーブコピーに定期的に複製され、マスターとスレーブのパターンを確立します。マスターは Lucene インデックスの更新を行います。スレーブは読み取りおよび書き込み操作を受け入れますが、ローカルインデックスコピーの読み取り操作を処理します。マスターは Lucene インデックスの更新のみを行います。更新操作にローカルの変更を適用するのは、マスターのみです。
図7.2 Jakarta Messaging Service バックエンドの設定
このモードは、スループットが重要で、インデックス更新の遅延が許容できるクラスター化された環境をターゲットとします。Jakarta Messaging プロバイダーは信頼性を確保し、スレーブを使用してローカルインデックスコピーを変更します。
7.1.6. リーダーストラテジー リンクのコピーリンクがクリップボードにコピーされました!
クエリーの実行時に、Hibernate Search はリーダーストラテジーを使用して Apache Lucene インデックスと対話します。頻繁な更新、多くの読み取り、非同期のインデックス更新など、アプリケーションのプロファイルに基づいてリーダーストラテジーを選択します。
7.1.6.3. カスタムリーダーストラテジー リンクのコピーリンクがクリップボードにコピーされました!
org.hibernate.search.reader.ReaderProvider の実装を使用して、カスタムリーダーストラテジーを作成できます。実装はスレッドセーフである必要があります。
7.2. Configuration (設定) リンクのコピーリンクがクリップボードにコピーされました!
7.2.1. 最小設定 リンクのコピーリンクがクリップボードにコピーされました!
Hibernate Search は、設定および操作の柔軟性を提供するように設計されており、ほとんどのユースケースに合わせてデフォルト値を慎重に選択しています。少なくとも、Directory Provider とそのプロパティーを設定する必要があります。デフォルトの Directory Provider は filesystem で 、インデックスストレージにローカルファイルシステムを使用します。利用可能な Directory Providers およびその設定の詳細は、DirectoryProvider Configuration を参照してください。
Hibernate を直接使用する場合は、DirectoryProvider などの設定を、設定ファイル (hibernate.properties または ) で設定する必要があります。Jakarta Persistence 経由で Hibernate を使用している場合、設定ファイルは hibernate.cfg.xmlpersistence.xml になります。
その他のリソース
- 利用可能な Directory Providers およびその設定の詳細は、DirectoryProvider Configuration を参照してください。
7.2.2. IndexManager の設定 リンクのコピーリンクがクリップボードにコピーされました!
Hibernate Search は、このインターフェースにいくつかの実装を提供します。
-
directory-based: LuceneDirectory抽象化を使用してインデックスファイルを管理するデフォルトの実装。 -
near-real-time: コミット時にディスクへの書き込みをフラッシュしないようにします。このインデックスマネージャーはDirectoryベースでもありますが、Lucene のほぼリアルタイムの NRT 機能を使用します。
デフォルト以外の IndexManager を指定するには、以下のプロパティーを指定します。
hibernate.search.[default|<indexname>].indexmanager = near-real-time
7.2.2.1. Directory-based リンクのコピーリンクがクリップボードにコピーされました!
Directory-based 実装はデフォルトの IndexManager 実装です。これは高度な設定が可能で、リーダーストラテジー、バックエンド、およびディレクトリープロバイダーに個別の設定を可能にします。
7.2.2.2. Near Real Time リンクのコピーリンクがクリップボードにコピーされました!
NRTIndexManager はデフォルトの IndexManager の拡張機能で、低レイテンシーインデックス書き込みに Lucene NRT、Near Real Time 機能を活用します。ただし、lucene 以外の代替のバックエンドの設定を無視し、Directory で排他的な書き込みロックを取得します。
IndexWriter は、低レイテンシーを提供するため、ディスクへの変更をすべてフラッシュしません。クエリーは、フラッシュされていないインデックスライターバッファーから更新された状態を読み取ることができます。ただし、これは、IndexWriter が強制終了したり、アプリケーションがクラッシュした場合に、インデックスを再構築する必要があるように更新が失われる可能性があることを意味します。
Near Real Time 設定は、上記のデメリットとマスターノードを個別に設定できるため、データが限定されたクラスター化されていない Web サイトに対して推奨されます。
7.2.2.3. Custom リンクのコピーリンクがクリップボードにコピーされました!
カスタム実装の完全修飾クラス名を指定して、カスタマイズされた IndexManager を設定します。以下のように、実装に no-argument コンストラクターを設定します。
[default|<indexname>].indexmanager = my.corp.myapp.CustomIndexManager
カスタムインデックスマネージャーの実装には、デフォルトの実装と同じコンポーネントは必要ありません。たとえば、Directory インターフェースを公開しないリモートインデックスサービスに委譲します 。
7.2.3. DirectoryProvider の設定 リンクのコピーリンクがクリップボードにコピーされました!
DirectoryProvider は Lucene Directory に関する Hibernate Search の抽象化で、基盤の Lucene リソースの設定および初期化を処理します。Directory Providers and Their Properties では Hibernate Search で利用可能なディレクトリープロバイダーの一覧と、対応するオプションを表示しています。
インデックス化された各エンティティーは Lucene インデックスに関連付けられます (複数のエンティティーが同じインデックスを共有している場合を除きます)。インデックスの名前は、@Indexed アノテーションの index プロパティーによって指定されます。Index プロパティーが指定されていない場合は、インデックスされたクラスの完全修飾名が名前として使用されます (推奨)。
DirectoryProvider および追加オプションは、プレフィックス hibernate.search.<indexname> を使用して設定できます。名前 default (hibernate.search.default) は予約されており、すべてのインデックスに適用されるプロパティーを定義するために使用できます。Configuring Directory Providers では、hibernate.search.default.directory_provider を使用してデフォルトのディレクトリープロバイダーをファイルシステムに設定する方法を示しています。その後に、hibernate.search.default.indexBase がインデックスのデフォルトのベースディレクトリーを設定します。その結果、エンティティー Status のインデックスが /usr/lucene/indexes/org.hibernate.example.Status に作成されます。
ただし、Rule エンティティーのインデックスは、メモリー内ディレクトリーを使用します。これは、このエンティティーのデフォルトのディレクトリープロバイダーが hibernate.search.Rules.directory_provider プロパティーによって上書きされるためです。
最後に、Action エンティティーは 、hibernate.search.Actions.directory_provider で指定されるカスタムディレクトリープロバイダー CustomDirectoryProvider を使用します。
インデックス名の指定
package org.hibernate.example;
@Indexed
public class Status { ... }
@Indexed(index="Rules")
public class Rule { ... }
@Indexed(index="Actions")
public class Action { ... }
ディレクトリープロバイダーの設定
hibernate.search.default.directory_provider = filesystem
hibernate.search.default.indexBase=/usr/lucene/indexes
hibernate.search.Rules.directory_provider = ram
hibernate.search.Actions.directory_provider = com.acme.hibernate.CustomDirectoryProvider
上記の設定スキームを使用すると、ディレクトリープロバイダーやベースディレクトリーなどの一般的なルールを簡単に定義でき、これらのデフォルトをインデックスごとに後で上書きできます。
ディレクトリープロバイダーおよびそれらのプロパティー
- ram
- None
- Filesystem
ファイルシステムベースのディレクトリー使用されるディレクトリーは <indexBase> /< indexName > です。
- indexBase: ベースディレクトリー
- indexName: @Indexed.index を上書き (シャード化されたインデックスに便利です)
- locking_strategy: オプション。LockFactory Configuration を参照してください。
-
filesystem_access_type: この
DirectoryProviderで使用されるFSDirectory実装の正確なタイプを判別できます。許可される値はauto(デフォルト値。Windows 以外のシステムではNIOFSDirectory、Windwos ではSimpleFSDirectory)、simple (SimpleFSDirectory)、nio (NIOFSDirectory)、mmap (MMapDirectory)を選択します。この設定を変更する前に、これらのディレクトリー実装に関する Java ドキュメントを参照してください。NIOFSDirectoryまたはMMapDirectoryは、パフォーマンスを大幅に向上させる可能性がありますが、問題もあります。
filesystem-masterファイルシステムベースのディレクトリー
filesystemと類似しています。また、定期的にインデックスをソースディレクトリー(コピーディレクトリー) にコピーします。更新期間に推奨される値は (最低 50%)、情報をコピーする時間 (デフォルトは 3600 秒 - 60 分) です。
コピーは、増分コピーメカニズムをベースにしているため、コピーの平均時間が短縮されることに注意してください。
DirectoryProvider は通常、Jakarta Messaging バックエンドクラスターのマスターノードで使用されます。
buffer_size_on_copy最適化は、ご使用のオペレーティングシステムと利用可能な RAM によって異なります。多くユーザーは 16 から 64MB までの値を使用して良好な結果を報告しています。- indexBase: ベースディレクトリー
- indexName: @Indexed.index を上書き (シャード化されたインデックスに便利です)
- sourceBase: ソース (コピー) のベースディレクトリー。
-
source: ソースディレクトリーの接尾辞 (
デフォルトは @Indexed.index)。実際のソースのディレクトリー名は<sourceBase>/<source>です。 - refresh: 更新の間隔 (秒単位) です (コピーは更新の秒数ごとに行われます)。次の refresh 期間が経過してもコピーが進行中であると、次のコピー操作はスキップされます。
- buffer_size_on_copy: 単一の低レベルのコピー命令で移動するメガージの量。デフォルトは 16MB です。
- locking_strategy: オプション。LockFactory Configuration を参照してください。
-
filesystem_access_type: この
DirectoryProviderで使用されるFSDirectory実装の正確なタイプを判別できます。許可される値はauto(デフォルト値。Windows 以外のシステムではNIOFSDirectory、Windwos ではSimpleFSDirectory)、simple (SimpleFSDirectory)、nio (NIOFSDirectory)、mmap (MMapDirectory)を選択します。この設定を変更する前に、これらのディレクトリー実装に関する Java ドキュメントを参照してください。NIOFSDirectoryまたはMMapDirectoryではパフォーマンスが大幅に向上しますが、注意が必要な問題もあります。
filesystem-slaveファイルシステムベースのディレクトリー
filesystemと似ていますが、マスターバージョン (ソース) を定期的に取得します。ロックおよび一貫性のない検索結果を避けるため、2 つのローカルコピーが維持されます。更新期間に推奨される値は (最低 50%)、情報をコピーする時間 (デフォルトは 3600 秒 - 60 分) です。
コピーは、増分コピーメカニズムをベースにしているため、コピーの平均時間が短縮されることに注意してください。refresh 期間が経過してもコピーが進行中であると、次のコピー操作はスキップされます。
DirectoryProvider は通常、Jakarta Messaging バックエンドを使用するスレーブノードに使用されます。
buffer_size_on_copy最適化は、ご使用のオペレーティングシステムと利用可能な RAM によって異なります。多くユーザーは 16 から 64MB までの値を使用して良好な結果を報告しています。- indexBase: ベースディレクトリー
- indexName: @Indexed.index を上書き (シャード化されたインデックスに便利です)
- sourceBase: ソース (コピー) のベースディレクトリー。
-
source: ソースディレクトリーの接尾辞 (
デフォルトは @Indexed.index)。実際のソースのディレクトリー名は<sourceBase>/<source>です。 - refresh: 更新の間隔 (秒単位) です (コピーは更新の秒数ごとに行われます)。
- buffer_size_on_copy: 単一の低レベルのコピー命令で移動するメガージの量。デフォルトは 16MB です。
- locking_strategy: オプション。LockFactory Configuration を参照してください。
- retry_marker_lookup: オプションで、デフォルトは 0 です。Hibernate Search がソースディレクトリーのマーカーファイルをチェックする回数を定義します。試行ごとに 5 秒待機します。
-
retry_initialize_period: オプション。再試行初期化機能を有効にするために整数値を秒単位で設定します。スレーブがマスターインデックスを検出できない場合、アプリケーションがバックグラウンドで起動しないようにせずに再試行します。インデックスの初期化前に実行された FULLTEXT クエリーはブロックされず、空の結果が返されます。オプションを有効にしない、または明示的にゼロに設定すると、再試行タイマーのスケジュール設定ではなく、例外により失敗します。無効なインデックスなしでアプリケーションが起動しないようにし、初期化のタイムアウトを制御するには、代わりに
retry_marker_lookupを参照してください。 -
filesystem_access_type: この
DirectoryProviderで使用されるFSDirectory実装の正確なタイプを判別できます。許可される値は auto (デフォルト値。Windows 以外のシステムではNIOFSDirectory、Windwos ではSimpleFSDirectory)、simple (SimpleFSDirectory)、nio (NIOFSDirectory)、mmap (MMapDirectory)を選択します。この設定を変更する前に、これらのディレクトリー実装に関する Java ドキュメントを参照してください。NIOFSDirectoryまたはMMapDirectoryはパフォーマンスを大幅に向上させる可能性がありますが、問題にも認識する必要があります。
ビルトインディレクトリープロバイダーがニーズに適さない場合は、org.hibernate.store.DirectoryProvider インターフェースを実装して独自のディレクトリープロバイダーを作成することができます。この場合、プロバイダーの完全修飾クラス名を directory_provider プロパティーに渡します。接頭辞 hibernate.search.<indexname> を使用して追加のプロパティーを渡すことができます。
7.2.4. Worker 設定 リンクのコピーリンクがクリップボードにコピーされました!
workder 設定では、Hibernate Search が Lucene と対話する方法を詳細化することができます。複数のアーキテクチャーコンポーネントと可能な拡張ポイントが存在します。詳しく見てみましょう。
ワーカー設定を使用して、Infinispan クエリーが Lucene と対話する方法を詳細化します。この設定には、いくつかのアーキテクチャーコンポーネントおよび可能な拡張ポイントを使用できます。
まず、Worker があります。Worker インターフェースの実装は、すべてのエンティティーの変更を受け取り、コンテキストによってそれらをキューに登録し、コンテキストの終了時に適用します。特に ORM との接続で最も直感的なコンテキストはトランザクションです。このため、はデフォルトで TransactionalWorker を使用してトランザクションごとにすべての変更をスコープします。ただし、エンティティーの変更数やその他のアプリケーションライフサイクルイベントなどによってコンテキストが異なるシナリオを想定できます。
| プロパティー | 説明 |
|---|---|
|
|
使用する |
|
|
接頭辞 |
|
|
コンテキストごとにバッチ処理されるインデックス操作の最大数を定義します。制限に達すると、コンテキストが終了していなくてもインデックスがトリガーされます。このプロパティーは、 |
コンテキストが終了すると、インデックスの変更を準備して適用します。これは、新規スレッド内で同期または非同期に実行できます。同期更新には、常にインデックスがデータベースと同期しているという利点があります。一方、非同期のアップデートは、ユーザーの応答時間を最小限に抑えるのに役立ちます。欠点は、データベースとインデックスの状態間で不一致が生じる可能性があることです。
以下のオプションはインデックスごとに異なる場合があります。実際には、indexName 接頭辞が必要になるか、default を使用してすべてのインデックスのデフォルト値を設定する必要があります。
| プロパティー | 説明 |
|---|---|
|
|
|
|
| バックエンドは、スレッドプールを使用して、同じトランザクションコンテキスト (またはバッチ) から更新を並行して適用することができます。デフォルト値は 1 です。トランザクションごとに多数の操作がある場合は、大きな値を試すことができます。 |
|
| スレッドプールが不足している場合は、ワークキューの最大数を定義します。非同期実行のみに便利です。デフォルトは infinite です。制限に達すると、ワークはメインスレッドによって行われます。 |
現在、いずれの実行モードであっても、すべての作業は同じ仮想マシン内で行われます。単一仮想マシンの作業合計量は変更されていません。常に、より適切なアプローチ (つまり委任) があります。hibernate.search.default.worker.backend を設定すると、インデックス作業を別のサーバーに送信できます。このオプションも、インデックスごとに異なる方法で設定できます。
| プロパティー | 説明 |
|---|---|
|
|
|
- Java Messaging サービスのバックエンド設定
| プロパティー | 説明 |
|
| 必要に応じて InitialContext を開始する Java Naming および Directory Interface プロパティーを定義します。Java Naming and Directory Interface は、Java Messaging サービスのバックエンドによってのみ使用されます。 |
|
|
Java Messaging Service バックエンドには必須です。Java Messaging Service 接続ファクトリーを (Red Hat JBoss Enterprise Application Platform のデフォルトでは |
|
| Java Messaging Service バックエンドには必須です。Java Messaging Service キューを検索する Java Naming and Directory Interface 名を定義します。キューはワークメッセージをポストするために使用されます。 |
おそらく、表示されるプロパティーの一部は関連付けられるため、プロパティー値のすべての組み合わせが適切であるとは限りません。実際には、機能以外の設定を行うことができます。これは、特に、ここに示されるインターフェースの独自の実装を提供する場合が該当します。独自の Worker または BackendQueueProcessor 実装を作成する前に、既存のコードを調査してください。
7.2.4.1. Jakarta Messaging マスター/スレーブバックエンド リンクのコピーリンクがクリップボードにコピーされました!
このセクションでは、マスター/スレーブの Hibernate Search アーキテクチャーの設定方法について説明します。
図7.3 Jakarta Messaging バックエンドの設定
7.2.4.2. スレーブノード リンクのコピーリンクがクリップボードにコピーされました!
すべてのインデックス更新操作は Jakarta Messaging キューに送信されます。インデックスクエリー操作は、ローカルインデックスコピーで実行されます。
Jakarta Messaging スレーブの設定
### slave configuration
## DirectoryProvider
# (remote) master location
hibernate.search.default.sourceBase = /mnt/mastervolume/lucenedirs/mastercopy
# local copy location
hibernate.search.default.indexBase = /Users/prod/lucenedirs
# refresh every half hour
hibernate.search.default.refresh = 1800
# appropriate directory provider
hibernate.search.default.directory_provider = filesystem-slave
## Back-end configuration
hibernate.search.default.worker.backend = jms
hibernate.search.default.worker.jms.connection_factory = /ConnectionFactory
hibernate.search.default.worker.jms.queue = queue/hibernatesearch
#optional jndi configuration (check your Jakarta Messaging provider for more information)
## Optional asynchronous execution strategy
# hibernate.search.default.worker.execution = async
# hibernate.search.default.worker.thread_pool.size = 2
# hibernate.search.default.worker.buffer_queue.max = 50
ファイルシステムローカルコピーは、より高速な検索結果を得るために推奨されます。
7.2.4.3. マスターノード リンクのコピーリンクがクリップボードにコピーされました!
すべてのインデックス更新操作は Jakarta Messaging キューから取得され、実行されます。マスターインデックスは定期的にコピーされます。
Jakarta Messaging キューのインデックス更新操作は実行され、マスターインデックスは定期的にコピーされます。
Jakarta Messaging Service Master 設定
### master configuration
## DirectoryProvider
# (remote) master location where information is copied to
hibernate.search.default.sourceBase = /mnt/mastervolume/lucenedirs/mastercopy
# local master location
hibernate.search.default.indexBase = /Users/prod/lucenedirs
# refresh every half hour
hibernate.search.default.refresh = 1800
# appropriate directory provider
hibernate.search.default.directory_provider = filesystem-master
## Back-end configuration
#Back-end is the default for Lucene
Hibernate Search フレームワークの設定に加えて、Jakarta Messaging を介してインデックスを処理するようメッセージ駆動 Bean を作成し、設定する必要があります。
メッセージ駆動型 Bean がインデックスキューを処理する
@MessageDriven(activationConfig = {
@ActivationConfigProperty(propertyName="destinationType",
propertyValue="javax.jms.Queue"),
@ActivationConfigProperty(propertyName="destination",
propertyValue="queue/hibernatesearch"),
@ActivationConfigProperty(propertyName="DLQMaxResent", propertyValue="1")
} )
public class MDBSearchController extends AbstractJMSHibernateSearchController
implements MessageListener {
@PersistenceContext EntityManager em;
//method retrieving the appropriate session
protected Session getSession() {
return (Session) em.getDelegate();
}
//potentially close the session opened in #getSession(), not needed here
protected void cleanSessionIfNeeded(Session session)
}
}
この例では、Hibernate Search ソースコードで利用可能な抽象 Jakarta Messaging コントローラークラスから継承し、Jakarta EE MDB を実装します。この実装は例として提供されており、Jakarta EE 以外のメッセージ駆動 Bean を利用するように調整できます。
7.2.5. Lucene インデックスのチューニング リンクのコピーリンクがクリップボードにコピーされました!
7.2.5.1. Lucene インデックスのパフォーマンスチューニング リンクのコピーリンクがクリップボードにコピーされました!
Hibernate Search は、mergeFactor、maxMergeDocs、maxBufferedDocs など、基礎となる Lucene IndexWriter に渡される一連のパラメーターを指定することで Lucence インデクスパフォーマンスを調整するのに使用されます。これらのパラメーターは、インデックスベースまたはシャードごとに、すべてのインデックスに適用されるデフォルト値として指定します。
各種ユースケースに合わせて調整できる低レベルの IndexWriter 設定がいくつかあります。これらのパラメーターは、indexwriter キーワードでグループ化されます。
hibernate.search.[default|<indexname>].indexwriter.<parameter_name>
特定のシャード設定の indexwriter 値に値が設定されていないと、Hibernate Search は index セクションをチェックしてから default セクションをチェックします。
以下の表の設定により、Animal インデックスの 2 つ目のシャードにこれらの設定が適用されます。
-
max_merge_docs= 10 -
merge_factor= 20 -
ram_buffer_size= 64MB -
term_index_interval= Lucene default
他のすべての値は、Lucene で定義されたデフォルトを使用します。
デフォルトでは、すべての値は Lucene の独自のデフォルトのままにします。Indexing Performance and Behavior Properties に一覧表示される値は、使用している Lucene のバージョンに応じて異なります。上記の値はバージョン 2.4 に相対的です。
Hibernate Search の以前のバージョンには batch および transaction プロパティーの概念がありました。バックエンドは常に同じ設定を使用して機能するため、これは変わりました。
| プロパティー | 説明 | デフォルト値 |
|---|---|---|
|
|
他のプロセスが同じインデックスに書き込む必要がない場合は |
|
|
|
各インデックスには、インデックスに適用される更新が含まれる個別の「pipeline」があります。このキューが満杯になると、ブロック操作になります。 |
|
|
| バッファーインメモリー削除の条件が適用され、フラッシュされるまでに最低限必要な削除用語を決定します。時点でバッファーされたドキュメントがメモリーにある場合、ドキュメントはマージされ、新しいセグメントが作成されます。 | Disabled (RAM 使用率によるフラッシュ) |
|
| インデックス作成中にメモリーでバッファーされたドキュメントの量を制御します。RAM が大きいほど消費されます。 | Disabled (RAM 使用率によるフラッシュ) |
|
| セグメント内で許容されるドキュメントの最大数を定義します。値を小さくすると、頻繁に変更されるインデックスでのパフォーマンスが向上します。値を大きくすると、インデックスが頻繁に変更されない場合に検索パフォーマンスが向上します。 | Unlimited (Integer.MAX_VALUE) |
|
| セグメントマージの頻度とサイズを制御します。 挿入時にセグメントインデックスをマージする頻度を決定します。値を小さくすると、インデックス処理時に使用される RAM が少なくなり、最適化されていないインデックスの検索速度が速くなりますが、インデックスの速度は遅くなります。値が大きいと、インデックス時により多くの RAM が使用され、最適化されていないインデックスの検索速度が遅くなるため、インデックスがより速くなります。そのため、大きな値 (> 10) はバッチインデックスの作成に最も適しており、対話的に維持されるインデックスに小さい値 (< 10) が適しています。この値は 2 未満にしないでください。 | 10 |
|
|
セグメントマージの頻度とサイズを制御します。このサイズより小さいセグメント (MB 単位) は常に次のセグメントマージ操作の対象となります。このパラメーターを高く設定しすぎると、マージ操作が頻繁に行われない場合でも、処理にコストがかかる可能性があります。 | 0 MB (実際 ~1K) |
|
| セグメントマージの頻度とサイズを制御します。 このサイズより大きいセグメント (MB 単位) は、大きなセグメントにマージされることはありません。 これにより、メモリー要件が減り、一部のマージ操作が最適な検索速度で回避されます。インデックスの最適化時にこの値は無視されます。
| 無制限 |
|
| セグメントマージの頻度とサイズを制御します。
このサイズよりも大きい (MB 単位) セグメントは、インデックスの最適化中にも大きなセグメントではマージされません (
| 無制限 |
|
| セグメントマージの頻度とサイズを制御します。
|
|
|
| ドキュメントバッファー専用の RAM の容量 (MB 単位) を制御します。Max_buffered_docs を一緒に使用すると、いずれのイベントに対してもフラッシュが発生します。 通常、インデックスのパフォーマンスを上げるには、ドキュメント数ではなく、RAM 使用率によってフラッシュし、RAM バッファーをできるだけ大きく使用するのが最良の方法です。 | 16 MB |
|
| インデックス化された期間の間隔を設定します。 値が大きくなると、IndexReader では使用されるメモリーが少なくなります。しかし、ランダムアクセスが遅くなります。値が小さいと、IndexReader の方がより多くのメモリーが使用され、用語へのランダムアクセスが速くなります。詳細は、Lucene ドキュメントを参照してください。 | 128 |
|
|
複合ファイル形式を使用すると、使用するファイル記述子が少なくなります。欠点は、インデックス作成にかかる時間と一時ディスク容量が多いことです。インデックス処理時間を短縮するために、このパラメーターを
ブール値のパラメーター。 | true |
|
| すべてのエンティティーの変更に Lucene インデックスの更新が必要であるわけではありません。更新されたエンティティープロパティー (dirty プロパティー) すべてがインデックス化されない場合、Hibernate Search はインデックス変更プロセスを省略します。
各更新イベントで呼び出す必要があるカスタムの
この最適化は、
ブール値のパラメーター。 | true |
blackhole バックエンドは 、インデックスのボトルネックを特定するツールとしてのみ、実稼働環境で使用することは意図されていません。
7.2.5.2. Lucene IndexWriter リンクのコピーリンクがクリップボードにコピーされました!
各種ユースケースに合わせて調整できる低レベルの IndexWriter 設定がいくつかあります。これらのパラメーターは、indexwriter キーワードでグループ化されます。
default.<indexname>.indexwriter.<parameter_name>
シャード設定で indexwriter の値が設定されていない場合、Hibernate Search は index セクションを参照し、デフォルトのセクションを探します。
7.2.5.3. パフォーマンスオプションの設定 リンクのコピーリンクがクリップボードにコピーされました!
以下の設定では、これらの設定が Animal インデックスの 2 つ目のシャードに適用されます。
パフォーマンスオプションの設定例
default.Animals.2.indexwriter.max_merge_docs = 10
default.Animals.2.indexwriter.merge_factor = 20
default.Animals.2.indexwriter.term_index_interval = default
default.indexwriter.max_merge_docs = 100
default.indexwriter.ram_buffer_size = 64
-
max_merge_docs= 10 -
merge_factor= 20 -
ram_buffer_size= 64MB -
term_index_interval= Lucene default
他のすべての値は、Lucene で定義されたデフォルトを使用します。
Lucene のデフォルト値が Hibernate Search のデフォルト設定です。したがって、以下の表に記載する値は、使用される Lucene のバージョンによって異なります。上記の値はバージョン 2.4 に相対的です。Lucene インデックスのパフォーマンスに関する詳細は、Lucene のドキュメントを参照してください。
バックエンドは常に同じ設定を使用して動作します。
| プロパティー | 説明 | デフォルト値 |
|---|---|---|
|
|
他のプロセスが同じインデックスに書き込む必要がない場合は |
|
|
|
各インデックスには、インデックスに適用される更新が含まれる個別の「pipeline」があります。このキューが満杯になると、ブロック操作になります。 |
|
|
| バッファーインメモリー削除の条件が適用され、フラッシュされるまでに最低限必要な削除用語を決定します。時点でバッファーされたドキュメントがメモリーにある場合、ドキュメントはマージされ、新しいセグメントが作成されます。 | Disabled (RAM 使用率によるフラッシュ) |
|
| インデックス作成中にメモリーでバッファーされたドキュメントの量を制御します。RAM が大きいほど消費されます。 | Disabled (RAM 使用率によるフラッシュ) |
|
| セグメント内で許容されるドキュメントの最大数を定義します。値を小さくすると、頻繁に変更されるインデックスでのパフォーマンスが向上します。値を大きくすると、インデックスが頻繁に変更されない場合に検索パフォーマンスが向上します。 | Unlimited (Integer.MAX_VALUE) |
|
| セグメントマージの頻度とサイズを制御します。 挿入時にセグメントインデックスをマージする頻度を決定します。値を小さくすると、インデックス処理時に使用される RAM が少なくなり、最適化されていないインデックスの検索速度が速くなりますが、インデックスの速度は遅くなります。値が大きいと、インデックス時により多くの RAM が使用され、最適化されていないインデックスの検索速度が遅くなるため、インデックスがより速くなります。そのため、大きな値 (> 10) はバッチインデックスの作成に最も適しており、対話的に維持されるインデックスに小さい値 (< 10) が適しています。この値は 2 未満にしないでください。 | 10 |
|
| セグメントマージの頻度とサイズを制御します。 このサイズより小さいセグメント (MB 単位) は常に次のセグメントマージ操作の対象となります。 このパラメーターを高く設定しすぎると、マージ操作が頻繁に行われない場合でも、処理にコストがかかる可能性があります。
| 0 MB (実際 ~1K) |
|
| セグメントマージの頻度とサイズを制御します。 このサイズより大きいセグメント (MB 単位) は、大きなセグメントにマージされることはありません。 これにより、メモリー要件が減り、一部のマージ操作が最適な検索速度で回避されます。インデックスの最適化時にこの値は無視されます。
| 無制限 |
|
| セグメントマージの頻度とサイズを制御します。
このサイズよりも大きい (MB 単位) セグメントは、インデックスの最適化中にも大きなセグメントではマージされません (
| 無制限 |
|
| セグメントマージの頻度とサイズを制御します。
|
|
|
| ドキュメントバッファー専用の RAM の容量 (MB 単位) を制御します。Max_buffered_docs を一緒に使用すると、いずれのイベントに対してもフラッシュが発生します。 通常、インデックスのパフォーマンスを上げるには、ドキュメント数ではなく、RAM 使用率によってフラッシュし、RAM バッファーをできるだけ大きく使用するのが最良の方法です。 | 16 MB |
|
| インデックス化された期間の間隔を設定します。 大きな値を設定すると、IndexReader が使用するメモリーは少なくなりますが、ランダムアクセスの速度が遅くなります。値を小さくすると、IndexReader によりより多くのメモリーが使用され、用語へのランダムアクセスが速くなります。詳細は、Lucene ドキュメントを参照してください。 | 128 |
|
|
複合ファイル形式を使用すると、使用するファイル記述子が少なくなります。欠点は、インデックス作成にかかる時間と一時ディスク容量が多いことです。インデックス処理時間を短縮するために、このパラメーターを
ブール値のパラメーター。 | true |
|
| すべてのエンティティーの変更に Lucene インデックスの更新が必要であるわけではありません。更新されたエンティティープロパティー (dirty プロパティー) すべてがインデックス化されない場合、Hibernate Search はインデックス変更プロセスを省略します。
各更新イベントで呼び出す必要があるカスタムの
この最適化は、
ブール値のパラメーター。 | true |
7.2.5.4. インデックス速度の調整 リンクのコピーリンクがクリップボードにコピーされました!
アーキテクチャーが許可する場合、インデックスの書き込み効率を向上させるために default.exclusive_index_use=true のままにします。
インデックスの速度を調整する場合は、最初にオブジェクトの読み込みの最適化に焦点を合わせ、次にインデックスプロセスのチューニングの基準として達成するタイミングを使用することが推奨されます。blackhole をワーカーバックエンドとして設定し、インデックスルーチンを開始します。このバックエンドは、Hibernate Search を無効にしません。インデックスに必要な変更セットが生成されますが、それらをインデックスにフラッシュするのではなく破棄します。hibernate.search.indexing_strategy を manual に設定するのとは対照的に、blackhole を使用すると、関連するエンティティーも再インデックス化されるため、データベースがより多くのデータを読み込む可能性があります。
hibernate.search.[default|<indexname>].worker.backend blackhole
blackhole バックエンドは 、インデックスのボトルネックを特定する診断ツールとしてのみ、実稼働環境で使用することは意図されていません。
7.2.5.5. コントロールセグメントサイズ リンクのコピーリンクがクリップボードにコピーされました!
以下のオプションは、作成されるセグメントの最大サイズを設定します。
-
merge_max_size -
merge_max_optimize_size -
merge_calibrate_by_deletes
コントロールセグメントサイズ
//to be fairly confident no files grow above 15MB, use:
hibernate.search.default.indexwriter.ram_buffer_size = 10
hibernate.search.default.indexwriter.merge_max_optimize_size = 7
hibernate.search.default.indexwriter.merge_max_size = 7
マージセグメントが 2 つの大きなセグメントに結合するため、マージ操作の max_size はハード制限セグメントサイズの半分未満に設定します。
新しいセグメントは、当初は予想よりも大きくなる場合がありますが、セグメントが ram_buffer_sizeよりも大きく作成されることは決してありません。このしきい値は予測としてチェックされます。
7.2.6. LockFactory 設定 リンクのコピーリンクがクリップボードにコピーされました!
Lucene ディレクトリーは、Hibernate Search によって管理される各インデックスの LockingFactory を介してカスタムロックストラテジーで設定できます。
一部のロックストラテジーには、ファイルシステムレベルのロックが必要で、RAM ベースのインデックスで使用できます。このストラテジーを使用する場合は、IndexBase 設定オプションを指定して、ロックマーカーファイルを保存するファイルシステムの場所を指定する必要があります。
ロックファクトリーを選択するには、hibernate.search.<index>.locking_strategy オプションを以下のオプションのいずれかに設定します。
- ˆsimple
- native
- single
- none
| 名前 | クラス | 説明 |
|---|---|---|
|
LockFactory Configuration | org.apache.lucene.store.SimpleFSLockFactory | Java の File API に基づく安全な実装では、マーカーファイルを作成してインデックスの使用をマークします。 何らかの理由でアプリケーションを強制終了する必要がある場合には、このファイルを削除してから再起動する必要があります。 |
|
| org.apache.lucene.store.NativeFSLockFactory |
この実装では、NFS で既知の問題があるため、ネットワーク共有で使用しないでください。
|
|
| org.apache.lucene.store.SingleInstanceLockFactory | この LockFactory はファイルマーカーを使用しませんが、メモリーに保持される Java オブジェクトロックであるため、インデックスが他のプロセスで共有されないことが確実な場合にのみ使用できます。
これは、 |
|
| org.apache.lucene.store.NoLockFactory | このインデックスへの変更は、ロックによって調整されません。 |
以下は、ロックストラテジーの設定例です。
hibernate.search.default.locking_strategy = simple
hibernate.search.Animals.locking_strategy = native
hibernate.search.Books.locking_strategy = org.custom.components.MyLockingFactory
7.2.7. インデックス形式の互換性 リンクのコピーリンクがクリップボードにコピーされました!
Hibernate Search では現在、アプリケーションを新しいバージョンへの移植を容易にする後方互換性の API やツールは提供されていません。API は、インデックスの書き込みおよび検索に Apache Lucene を使用します。インデックス形式の更新が必要になる場合があります。この場合、Lucene が古い形式を読み取れない場合は、データのインデックスを再作成する必要があります。
インデックス形式を更新する前にインデックスをバックアップします。
Hibernate Search は、hibernate.search.lucene_version 設定プロパティーを公開します。このプロパティーは、古いバージョンの Lucene に定義されている動作に準拠するように Analyzers およびその他の Lucene クラスに指示します。lucene-core.jar に含まれる org.apache.lucene.util.Version も参照してください。このオプションを指定しないと、Hibernate Search はバージョンのデフォルトを使用するよう Lucene に指示します。使用されるバージョンは、アップグレード時に自動的に変更されないように設定に明示的に定義することが推奨されます。アップグレード後、必要に応じて設定値を明示的に更新できます。
Lucene 3.0 の Created Index と互換性を持たせるようにアナライザーを強制する
hibernate.search.lucene_version = LUCENE_30
設定した SearchFactory はグローバルであり、関連するパラメーターが含まれるすべての Lucene API に影響します。Lucene が使用され、Hibernate Search がバイパスされる場合には、一貫した結果を得るために同じ値を適用してください。
7.3. アプリケーションの Hibernate Search リンクのコピーリンクがクリップボードにコピーされました!
7.3.1. Hibernate Search を使用する最初の手順 リンクのコピーリンクがクリップボードにコピーされました!
アプリケーションの Hibernate Search の使用を開始するには、以下のトピックに従います。
7.3.2. Maven を使用した Hibernate Search の有効化 リンクのコピーリンクがクリップボードにコピーされました!
Maven プロジェクトで以下の設定を使用し、hibernate-search-orm 依存関係を追加します。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-search-orm</artifactId>
<version>5.5.1.Final-redhat-1</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-search-orm</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
7.3.3. アノテーションの追加 リンクのコピーリンクがクリップボードにコピーされました!
ここでは、書籍の詳細を含むデータベースの例を見てみましょう。アプリケーションに Hibernate 管理クラス example.Book および example.Author が含まれ、アプリケーションにフリーテキスト検索機能を追加して、書籍の検索を有効にする場合。
例: Hibernate Search 固有のアノテーションを追加する前のエンティティーブックおよび作成者
package example;
...
@Entity
public class Book {
@Id
@GeneratedValue
private Integer id;
private String title;
private String subtitle;
@ManyToMany
private Set<Author> authors = new HashSet<Author>();
private Date publicationDate;
public Book() {}
// standard getters/setters follow here
...
}
package example;
...
@Entity
public class Author {
@Id
@GeneratedValue
private Integer id;
private String name;
public Author() {}
// standard getters/setters follow here
...
}
これを行うには、Book クラスおよび Author クラスにアノテーションをいくつか追加する必要があります。最初のアノテーション @Indexed は Book にインデックス可能 (indexable) のマークを付けます。デザイン上、Hibernate Search は未認証の ID をインデックスに保存し、特定のエンティティーのインデックスの一意性を確保します。@DocumentId は、この目的に使用するプロパティーをマークし、ほとんどの場合はデータベースのプライマリーキーと同じです。@DocumentId アノテーションは、@Id アノテーションが存在する場合は任意です。
次に、検索可能にするフィールドには、そのようにマークを付ける必要があります。この例では、title と subtitle で始まります。両方に、@Field アノテーションを付けます。パラメーター index=Index.YES は、テキストが確実にインデックス化されるようにします。一方、analyze=Analyze.YES は、デフォルトの Lucene アナライザーを使用してテキストが分析されるようにします。通常、分析とは個別の単語に文章を分け、'a' や `the'.などの一般的な単語を除外できる可能性があることを意味します。アナライザーについてもう少し後ほど説明します。@Field で指定する第 3 のパラメーター store=Store.NO は、実際のデータがインデックスに保存されないようにします。このデータがインデックスに保存されているかどうかや、そのデータを検索する機能がないかどうかです。Lucene の観点からは、インデックスが作成されてからデータを保持する必要はありません。これを保存する利点は、projections を介してそれを取得することです。
Hibernate Search は projections なしで、クエリー基準に一致するエンティティーのデータベース識別子を見つけるためにデフォルトで Lucene クエリーを実行し、これらの識別子を使用してデータベースから管理オブジェクトを取得します。projection については、状況に応じてまたは対照的に決定する必要があります。管理オブジェクトを返す一方で、オブジェクト配列のみを返すため、デフォルトの動作が推奨されます。Index=Index.YES, analyze=Analyze.YES および store=Store.NO はこれらのパラメーターのデフォルト値であるため、省略できることに注意してください。
まだ説明されていない別のアノテーションは @DateBridge です。このアノテーションは、Hibernate Search の組み込みフィールドブリッジのいずれかになります。Lucene インデックスは文字列ベースです。このため、Hibernate Search はインデックス化されたフィールドの値タイプを文字列 (またはその逆) に変換する必要があります。事前定義されたブリッジの範囲が提供されます。これには、java.util.Date を指定の解決が含まれる文字列に変換する DateBridge が含まれます。詳細は Bridges を参照してください。
これにより、@IndexedEmbedded が残されます。このアノテーションは、所有するエンティティーの一部として関連するエンティティー (@ManyToMany、Gatewaytoone、@Embedded、および @ElementCollection) をインデックス化するために使用されます。これは、Lucene インデックスドキュメントがオブジェクト関係について不明なフラットデータ構造であるため必要になります。作成者の名前を確実に検索できるようにするには、名前を書籍の一部としてインデックス化する必要があります。@IndexedEmbedded のほかに、インデックスに含める関連エンティティーのフィールドすべてを @Indexed でマークする必要があります。詳細は、Embedded and Associated Objects を参照してください。
現在、これらの設定で十分です。エンティティーマッピングの詳細は、Mapping an Entity を参照してください。
例: Hibernate Search アノテーションの追加後のエンティティー
package example;
...
@Entity
public class Book {
@Id
@GeneratedValue
private Integer id;
private String title;
private String subtitle;
@Field(index = Index.YES, analyze=Analyze.NO, store = Store.YES)
@DateBridge(resolution = Resolution.DAY)
private Date publicationDate;
@ManyToMany
private Set<Author> authors = new HashSet<Author>();
public Book() {
}
// standard getters/setters follow here
...
}
package example;
...
@Entity
public class Author {
@Id
@GeneratedValue
private Integer id;
private String name;
public Author() {
}
// standard getters/setters follow here
...
}
7.3.4. インデックス化 リンクのコピーリンクがクリップボードにコピーされました!
Hibernate Search は、Hibernate Core を介して永続化、更新、または削除されるすべてのエンティティーを透過的にインデックス化します。ただし、データベースにすでに存在するデータの初期 Lucene インデックスを作成する必要があります。上記のプロパティーとアノテーションを追加したら、book の初期バッチインデックスをトリガーする時間になります。これは、以下のコードスニペットのいずれかを使用して実行できます (参照)。
例: Hibernate Session を使用したデータのインデックス化
FullTextSession fullTextSession = org.hibernate.search.Search.getFullTextSession(session);
fullTextSession.createIndexer().startAndWait();
例: Jakarta Persistence を使用したデータのインデックス化
EntityManager em = entityManagerFactory.createEntityManager();
FullTextEntityManager fullTextEntityManager = org.hibernate.search.jpa.Search.getFullTextEntityManager(em);
fullTextEntityManager.createIndexer().startAndWait();
上記のコードを実行した後は、/var/lucene/indexes/example.Book に Lucene インデックスが表示されるはずです。このインデックスを Luke で検査すると Hibernate Search の仕組みを理解するのに役立ちます。
7.3.5. 検索 リンクのコピーリンクがクリップボードにコピーされました!
検索を実行するには、Lucene API または Hibernate Search query DSL のいずれかを使用して Lucene クエリーを作成します。org.hibernate.Query にクエリーをラップし、Hibernate API から必要な機能を取得します。以下のコードは、インデックスフィールドに対するクエリーを準備します。コードを実行すると、書籍の一覧が返されます。
例: Hibernate Search セッションを使用した検索の作成および実行
FullTextSession fullTextSession = Search.getFullTextSession(session);
Transaction tx = fullTextSession.beginTransaction();
// create native Lucene query using the query DSL
// alternatively you can write the Lucene query using the Lucene query parser
// or the Lucene programmatic API. The Hibernate Search DSL is recommended though
QueryBuilder qb = fullTextSession.getSearchFactory()
.buildQueryBuilder().forEntity( Book.class ).get();
org.apache.lucene.search.Query query = qb
.keyword()
.onFields("title", "subtitle", "authors.name", "publicationDate")
.matching("Java rocks!")
.createQuery();
// wrap Lucene query in a org.hibernate.Query
org.hibernate.Query hibQuery =
fullTextSession.createFullTextQuery(query, Book.class);
// execute search
List result = hibQuery.list();
tx.commit();
session.close();
例: Jakarta Persistence を使用した検索の作成および実行
EntityManager em = entityManagerFactory.createEntityManager();
FullTextEntityManager fullTextEntityManager =
org.hibernate.search.jpa.Search.getFullTextEntityManager(em);
em.getTransaction().begin();
// create native Lucene query using the query DSL
// alternatively you can write the Lucene query using the Lucene query parser
// or the Lucene programmatic API. The Hibernate Search DSL is recommended though
QueryBuilder qb = fullTextEntityManager.getSearchFactory()
.buildQueryBuilder().forEntity( Book.class ).get();
org.apache.lucene.search.Query query = qb
.keyword()
.onFields("title", "subtitle", "authors.name", "publicationDate")
.matching("Java rocks!")
.createQuery();
// wrap Lucene query in a javax.persistence.Query
javax.persistence.Query persistenceQuery =
fullTextEntityManager.createFullTextQuery(query, Book.class);
// execute search
List result = persistenceQuery.getResultList();
em.getTransaction().commit();
em.close();
7.3.6. アナライザー リンクのコピーリンクがクリップボードにコピーされました!
インデックス化された Book エンティティーのタイトルが Refactoring: Improving the Design of Existing Code で、refactor、refactors、refactored、refactoring のクエリーに必要だとします。インデックス化および検索を行う際にワードステミングを適用する Lucene でアナライザークラスを選択します。Hibernate Search は、アナライザーを設定する方法を複数提供します (詳細はDefault Analyzer and Analyzer by Class 参照してください)。
-
設定ファイルに
analyzerプロパティーを設定します。指定されたクラスがデフォルトのアナライザーになります。 -
エンティティーレベルで
@Analyzerアノテーションを設定します。 -
フィールドレベルで
@Analyzerアノテーションを設定します。
完全修飾クラス名または使用するアナライザーを指定するか、@Analyzer アノテーションとともに @AnalyzerDef アノテーションで定義されているアナライザーを確認します。Solr アナライザーフレームワークとそのファクトリーは、後者のオプションに使用されます。ファクトリークラスの詳細は、Solr JavaDoc を参照するか、Solr Wiki の該当するセクションを参照してください。
この例では、StandardTokenizerFactory が、LowerCaseFilterFactory と SnowballPorterFilterFactory の 2 つのフィルターファクトリーによって使用されています。トークンライザーは、英数字とハイフンで単語を分割しますが、メールアドレスとインターネットのホスト名は維持します。標準トークンは、これおよびその他の一般的な操作に適しています。小文字フィルターはトークンのすべての文字を小文字に変換し、snowball フィルターは言語固有のステミングを適用します。
Solr フレームワークを使用する場合は、任意の数のフィルターでトークンを使用します。
例: @AnalyzerDef および Solr Framework を使用した Analyzer の定義および使用
@Indexed
@AnalyzerDef(
name = "customanalyzer",
tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class),
filters = {
@TokenFilterDef(factory = LowerCaseFilterFactory.class),
@TokenFilterDef(factory = SnowballPorterFilterFactory.class,
params = { @Parameter(name = "language", value = "English") })
})
public class Book implements Serializable {
@Field
@Analyzer(definition = "customanalyzer")
private String title;
@Field
@Analyzer(definition = "customanalyzer")
private String subtitle;
@IndexedEmbedded
private Set authors = new HashSet();
@Field(index = Index.YES, analyze = Analyze.NO, store = Store.YES)
@DateBridge(resolution = Resolution.DAY)
private Date publicationDate;
public Book() {
}
// standard getters/setters follow here
...
}
@AnalyzerDef を使用してアナライザーを定義し、@Analyzer を使用してエンティティーおよびプロパティーに適用します。この例では、customanalyzer は定義されますが、エンティティーには適用されません。アナライザーは title および subtitle プロパティーのみに適用されます。アナライザーの定義はグローバルです。エンティティーのアナライザーを定義し、必要に応じて他のエンティティーの定義を再利用します。
7.4. インデックス構造へのエンティティーのマッピング リンクのコピーリンクがクリップボードにコピーされました!
7.4.1. エンティティーのマッピング リンクのコピーリンクがクリップボードにコピーされました!
エンティティーのインデックス化に必要なすべてのメタデータ情報はアノテーションで記述されるため、XML マッピングファイルは必要ありません。基本的な Hibernate 設定には Hibernate マッピングファイルも使用できますが、Hibernate Search 固有の設定はアノテーションで表現する必要があります。
7.4.1.1. 基本的なマッピング リンクのコピーリンクがクリップボードにコピーされました!
エンティティーのマッピングに最も一般的に使用されるアノテーションから始めましょう。
Lucene ベースの Query API は、以下の共通アノテーションを使用してエンティティーをマッピングします。
- @Indexed
- @Field
- @NumericField
- @Id
7.4.1.2. @Indexed リンクのコピーリンクがクリップボードにコピーされました!
ほとんどの場合、永続クラスをインデックス化可能として宣言する必要があります。これは、クラスに @Indexed アノテーションを付けることで行われます (@Indexed アノテーションが付いていないすべてのエンティティーはインデックスプロセスによって無視されます)。
@Entity
@Indexed
public class Essay {
...
}
オプションで @Indexed アノテーションの index 属性を指定して、インデックスのデフォルト名を変更できます。
7.4.1.3. @Field リンクのコピーリンクがクリップボードにコピーされました!
エンティティーのプロパティー(または属性) ごとに、インデックス化方法を記述する機能があります。デフォルト (アノテーションなし) は、インデックスプロセスによってプロパティーが無視されることを意味します。
Hibernate Search 5 以前は、@NumericField で明示的に要求された場合に限り、数値フィールドエンコーディングが選択されていました。Hibernate Search 5 では、このエンコーディングは数値タイプに自動的に選択されます。数値エンコーディングを回避するには、非数値フィールドブリッジ @Field.bridge または @FieldBridge を明示的に指定できます。パッケージ org.hibernate.search.bridge.builtin には、org.hibernate.search.bridge.builtin.IntegerBridge などの数を文字列としてエンコードする一連のブリッジが含まれます。
@Field はプロパティーをインデックスとして宣言し、以下の属性のいずれかを設定してインデックスプロセスの複数の側面を設定できます。
-
name: プロパティーがどの名前下で、Lucene Document に保存されるべきかを説明します。デフォルト値はプロパティー名です (続く JavaBeans 規則)。 -
store: プロパティーが Lucene インデックスに保存されているかどうかを示します。Store.YESの値 (インデックスに多くの領域が必要ですが projection を許可) を保存するか、これを圧縮方式Store.COMPRESSで保存、あるいは、Store.NOを回避することができます (デフォルト値)。プロパティーが保存されると、Lucene ドキュメントから元の値を取得できます。これは、要素がインデックス化されるかどうかに関連しません。 index: プロパティーがインデックス化されるかどうかを示します。異なる値はIndex.NOで、これはインデックス化されず、クエリーおよびIndex.YESによって見つからりません。これは、要素がインデックス化され、検索可能であることを意味します。デフォルト値はIndex.YESです。Index.NOは、プロパティーの検索が不可能であるものの、利用できる必要がある場合に便利です。注記Index.NOをAnalyze.YESまたはNorms.YESと組み合わせると有用なわけではありません。これは、analyzeとnormsと解析のプロパティーのインデックス作成が必要ないためです。analyze: プロパティーが分析されたかどうか (Analyze.YES) または (Analyze.NO) を判断します。デフォルト値はAnalyze.YESです。注記プロパティーを分析するかどうかは、要素をそのまま0検索する場合と、含まれる単語で検索するかによって異なります。テキストフィールドを分析することは理にかなっていますが、日付フィールドは分析しません。
注記ソートに使用されるフィールドは、分析できません。
-
norms: インデックス時間の改善情報を保存する必要があるかどうか (Norms.YES) または (Norms.NO) を示します。これを保存しないと、大量のメモリーを節約できますが、インデックスの時間が改善する情報は提供されません。デフォルト値はNorms.YESです。 termVector: 用語と周波数のペア (term-frequency) のコレクションについて説明しています。この属性により、インデックス作成中にドキュメント内にベクターを保存することができます。デフォルト値はTermVector.NOです。この属性の異なる値は次のとおりです。
Expand 値 定義 TermVector.YES
各ドキュメントのTerm Vectors を保存します。これにより、同期されたアレイが作成され、これらはドキュメント用語が含まれ、他は用語の周波数が含まれます。
TermVector.NO
Term Vector は保存しないでください。
TermVector.WITH_OFFSETS
Term Verctor およびトークンオフセット情報を保存します。これは TermVector.YES と同様で、用語の開始および終了オフセット位置情報が含まれます。
TermVector.WITH_POSITIONS
Term Verctor およびトークン位置情報を保存します。これは TermVector.YES と同じですが、ドキュメント内の各用語の特徴も含まれます。
TermVector.WITH_POSITION_OFFSETS
Term Vector 、トークンの位置、およびオフセット情報を格納します。これは、YES、WITH_OFFSETS、および WITH_POSITIONS の組み合わせです。
indexNullAs: デフォルトの null 値ごとに無視され、インデックスは作成されません。ただし、indexNullAsを使用すると、null値のトークンとして挿入される文字列を指定できます。デフォルトでは、この値はField.DO_NOT_INDEX_NULLに設定され、null値がインデックス化されないことを示します。この値をField.DEFAULT_NULL_TOKENに設定すると、デフォルトのnullトークンが使用されます。このデフォルトのnullトークンは、hibernate.search.default_null_tokenを使用して設定に指定できます。このプロパティーを設定せず、Field.DEFAULT_NULL_TOKENを指定すると、文字列 "null" がデフォルトとして使用されます。注記indexNullAsパラメーターを使用する場合は、検索クエリーで同じトークンを使用してnull値を検索することが重要です。また、この機能は、非分析フィールド (analyze.NO) でのみ使用することが推奨されます。警告カスタムの FieldBridge または TwoWayFieldBridge を実装する場合、開発者は null 値のインデックス化を処理することになります (LuceneOptions.indexRulesAs() の Java ドキュメントを参照)。
7.4.1.4. @NumericField リンクのコピーリンクがクリップボードにコピーされました!
@Field には @NumericField というコンパニオンアノテーションがあり、@Field または @DocumentId と同じスコープで指定できます。このプロパティーは、Integer、Long、Float、および LastName プロパティーに指定できます。インデックスの作成時に、値は Trie 構造を使用してインデックス化されます。プロパティーを数字フィールドとしてインデックス化すると、標準的な @Field プロパティーに対して同じクエリーを実行するよりも、効率的な範囲クエリーとソートが可能になります。@NumericField アノテーションは以下のパラメーターを受け入れます。
| 値 | 定義 |
|---|---|
| forField | (オプション) 数値としてインデックス化される関連 @Field の名前を指定します。@Field 宣言を超えるプロパティーが含まれる場合にのみ必須となります。 |
| precisionStep | (オプション) インデックスに Trie 構造を格納する方法を変更します。precisionStes を小さくすると、ディスク領域の使用率が高くなり、範囲やソートのクエリーが速くなります。値が大きいほど使用領域が少なくなり、クエリーのパフォーマンスは通常の @Fields の範囲のクエリーに近づくことになります。デフォルト値は 4 です。 |
@NumericField は、LOCATION、Long、Integer、および Float のみに対応しています。他の数値タイプには Lucene で同様の機能を活用できないため、残りのタイプはデフォルトまたはカスタムの TwoWayFieldBridge で文字列エンコーディングを使用する必要があります。
タイプ変換中に概算を処理できると仮定した場合は、カスタムの NumericFieldBridge を使用することができます。
例: カスタムの NumericFieldBridge の定義
public class BigDecimalNumericFieldBridge extends NumericFieldBridge {
private static final BigDecimal storeFactor = BigDecimal.valueOf(100);
@Override
public void set(String name, Object value, Document document, LuceneOptions luceneOptions) {
if ( value != null ) {
BigDecimal decimalValue = (BigDecimal) value;
Long indexedValue = Long.valueOf( decimalValue.multiply( storeFactor ).longValue() );
luceneOptions.addNumericFieldToDocument( name, indexedValue, document );
}
}
@Override
public Object get(String name, Document document) {
String fromLucene = document.get( name );
BigDecimal storedBigDecimal = new BigDecimal( fromLucene );
return storedBigDecimal.divide( storeFactor );
}
}
7.4.1.5. @Id リンクのコピーリンクがクリップボードにコピーされました!
最後に、エンティティーの id (identifier) プロパティーは、特定のエンティティーのインデックスを一意に保つために Hibernate Search で使用される特別なプロパティーです。設計上、id は保存する必要があり、トークン化しないでください。プロパティーをインデックス識別子としてマークするには、@DocumentId アノテーションを使用します。Jakarta Persistence を使用し、@Id を指定すると @DocumentId を省略できます。選択したエンティティー ID は、ドキュメント識別子として使用されます。
Infinispan Query はエンティティーの id プロパティーを使用して、インデックスが一意に識別されるようにします。設計上、ID は保存されます。トークンに変換しないでください。プロパティーにインデックス ID のマークを付けるには、@DocumentId アノテーションを使用します。
例: インデックス化されたプロパティーの指定
@Entity
@Indexed
public class Essay {
...
@Id
@DocumentId
public Long getId() { return id; }
@Field(name="Abstract", store=Store.YES)
public String getSummary() { return summary; }
@Lob
@Field
public String getText() { return text; }
@Field @NumericField( precisionStep = 6)
public float getGrade() { return grade; }
}
上記の例では、id、Abstract、text、grade のフィールドを持つインデックスを定義します。デフォルトでは、JavaBean 仕様にしたがってフィールド名は大文字では表示されないことに注意してください。Grade フィールドは、デフォルトよりも若干精度の高いステップで数字としてアノテーションが付けられます。
7.4.1.6. 複数回のプロパティーのマッピング リンクのコピーリンクがクリップボードにコピーされました!
若干異なるインデックスストラテジーで、インデックスごとにプロパティーを複数回マップする必要がある場合があります。たとえば、フィールド別にクエリーを並び替えるには、フィールドを分析解除する必要があります。このプロパティーの単語で検索し、これをソートするには、分析後と未分析のときにインデックス付けする必要があります。これは、@Fields を使用することで可能です。
例: @Fields を使用した複数時間のプロパティーのマップ
@Entity
@Indexed(index = "Book" )
public class Book {
@Fields( {
@Field,
@Field(name = "summary_forSort", analyze = Analyze.NO, store = Store.YES)
} )
public String getSummary() {
return summary;
}
...
}
この例では、フィールの summary は 2 回インデックス化されます。これはトークン化方式の summary、非トークン化方式の summary_forSort で行われます。
7.4.1.7. 埋め込みおよび関連オブジェクト リンクのコピーリンクがクリップボードにコピーされました!
関連オブジェクトおよび組み込みオブジェクトは、ルートエンティティーインデックスの一部としてインデックス化できます。これは、関連するオブジェクトのプロパティーに基づいて特定のエンティティーを検索する場合に役に立ちます。関連する都市が Atlanta である場所を返すことを目的としています (Lucene クエリーパーサー言語で、address.city:Atlanta に変換されます)。場所フィールドは、Place インデックスでインデックス化されます。Placement インデックスドキュメントには、クエリー可能な address.id、address.street、address.city フィールドも含まれます。
例: 関連付けのインデックス化
@Entity
@Indexed
public class Place {
@Id
@GeneratedValue
@DocumentId
private Long id;
@Field
private String name;
@OneToOne( cascade = { CascadeType.PERSIST, CascadeType.REMOVE } )
@IndexedEmbedded
private Address address;
....
}
@Entity
public class Address {
@Id
@GeneratedValue
private Long id;
@Field
private String street;
@Field
private String city;
@ContainedIn
@OneToMany(mappedBy="address")
private Set<Place> places;
...
}
@IndexedEmbedded 技術を使用すると、データは Lucene インデックスで非正規化されるため、Hibernate Search は Place オブジェクトのすべての変更と、インデックスを最新の状態に保つため Address オブジェクトの変更を認識する必要があります。Lucene ドキュメントが Address の変更時に更新されるようにするには、双方向関係の反対側に @ContainedIn のマークを付けます。
@ContainedIn はエンティティーを参照する関連付けや、組み込み (コレクション) オブジェクトを参照する関連付けで役立ちます。
この例を展開するために、以下の例では @IndexedEmbedded のネスト化を示しています。
例: @IndexedEmbedded および @ContainedIn のネスト化された使用方法
@Entity
@Indexed
public class Place {
@Id
@GeneratedValue
@DocumentId
private Long id;
@Field
private String name;
@OneToOne( cascade = { CascadeType.PERSIST, CascadeType.REMOVE } )
@IndexedEmbedded
private Address address;
....
}
@Entity
public class Address {
@Id
@GeneratedValue
private Long id;
@Field
private String street;
@Field
private String city;
@IndexedEmbedded(depth = 1, prefix = "ownedBy_")
private Owner ownedBy;
@ContainedIn
@OneToMany(mappedBy="address")
private Set<Place> places;
...
}
@Embeddable
public class Owner {
@Field
private String name;
...
}
@*ToMany, @*ToOne および @Embedded 属性は、@IndexedEmbedded アノテーションを付けることができます。その後、関連クラスの属性が主なエンティティーインデックスに追加されます。インデックスには以下のフィールドが含まれます。
- id
- name
- address.street
- address.city
- address.ownedBy_name
デフォルトの接頭辞は propertyName.で、従来のオブジェクトナビゲーション規則に従います。ownedBy プロパティーに示されるように、prefix 属性を使用して上書きできます。
接頭辞を空の文字列に設定することはできません。
depth プロパティーは、オブジェクトグラフにクラス (インスタンスではない) の cyclic 依存関係が含まれるときに必要になります。たとえば、Owner が Place をポイントする場合です。Hibernate Search は、予想される深さに達すると (またはオブジェクトグラフの境界に到達する)、インデックス化された組み込み属性を含まなくなります。自己参照を持つクラスは、cyclic 依存関係の例です。この例では、depth が 1 に設定されているため、Owner の @IndexedEmbedded 属性は無視されます。
オブジェクト関連付けに @IndexedEmbedded を使用すると、以下のようなクエリーを表現できます (Lucene のクエリー構文を使用)。
名前に JBoss が含まれ、住所の都市が Atlanta の場所を返します。Lucene クエリーでは、以下のようになります。
+name:jboss +address.city:atlanta名前に JBoss が含まれ、所有者名に Joe が含まれる場所を返します。Lucene クエリーでは、以下のようになります。
+name:jboss +address.ownedBy_name:joe
この動作は、より効率的な方法 (データの重複が犠牲となる) でのリレーショナルジョイン操作の操作に似ています。初期状態の Lucene インデックスには関連付けの概念がないため、join 操作が存在しないことに注意してください。これは、完全なテキストインデックスの速度と機能が充実した状態で、リレーショナルデータベースを維持するのに役立ちます。
関連付けられたオブジェクトは、@Indexed にすることができます (ただし、必須ではありません)。
@IndexedEmbedded がエンティティーを参照する場合、関連付けには指向性が必要で、反対側にはアノテーション @ContainedIn を付ける必要があります (前述の例を参照)。これがない場合、Hibernate Search は関連エンティティーの更新時にルートインデックスを更新することはできません (この例では、関連付けられた Address インスタンスの更新時に Place インデックスドキュメントを更新する必要があります)。
@IndexedEmbedded アノテーションが付けられたオブジェクトタイプは、Hibernate および Hibernate Search によってターゲットに設定されたオブジェクトタイプではない場合があります。これは、インターフェースが実装の代わりに使用される場合にとくに当てはまります。このため、targetElement パラメーターを使用して、Hibernate Search がターゲットとするオブジェクトタイプを上書きできます。
例: @IndexedEmbedded の targetElement プロパティーの使用
@Entity
@Indexed
public class Address {
@Id
@GeneratedValue
@DocumentId
private Long id;
@Field
private String street;
@IndexedEmbedded(depth = 1, prefix = "ownedBy_", )
@Target(Owner.class)
private Person ownedBy;
...
}
@Embeddable
public class Owner implements Person { ... }
7.4.1.8. 特定のパスへのオブジェクト埋め込みの制限 リンクのコピーリンクがクリップボードにコピーされました!
@IndexedEmbedded アノテーションは、属性 includePaths も提供します。これは、デプスの代わりとして使用することも、属性と組み合わせることもできます。
デプスのみを使用すると、埋め込み型のインデックス設定されたフィールドはすべて、同じデプスで再帰的に追加されます。これにより、他のフィールドもすべて追加せずに特定のパスのみを選択することが困難になります。これは必須ではありません。
不要な読み込みおよびインデックスエンティティーを回避するには、必要なパスを正確に指定することができます。通常のアプリケーションでは、パスごとに異なるデプスが必要な場合や、以下の例のようにパスを明示的に指定する必要がある場合があります。
例: @IndexedEmbedded の includePaths プロパティーの使用
@Entity
@Indexed
public class Person {
@Id
public int getId() {
return id;
}
@Field
public String getName() {
return name;
}
@Field
public String getSurname() {
return surname;
}
@OneToMany
@IndexedEmbedded(includePaths = { "name" })
public Set<Person> getParents() {
return parents;
}
@ContainedIn
@ManyToOne
public Human getChild() {
return child;
}
...//other fields omitted
上記の例のようにマッピングを使用すると、名前やサムネーム、name や surname、親の name で Person の検索wお行うことができます。親の surname をインデックス化しないため、親の surname を検索することはできません。ただし、インデックス作成を迅速化し、スペースを節約して、全体的なパフォーマンスを向上させることができます。
@IndexedEmbeddedincludePaths には、通常はデプスの制限された値を指定するのに加え、指定されたパスが含まれます。IncludePaths を使用し、デプスを未定義のままにすると、depth=0 を設定するのと同等の動作になります。含まれるパスのみがインデックス化されます。
例: @IndexedEmbedded の includePaths プロパティーの使用
@Entity
@Indexed
public class Human {
@Id
public int getId() {
return id;
}
@Field
public String getName() {
return name;
}
@Field
public String getSurname() {
return surname;
}
@OneToMany
@IndexedEmbedded(depth = 2, includePaths = { "parents.parents.name" })
public Set<Human> getParents() {
return parents;
}
@ContainedIn
@ManyToOne
public Human getChild() {
return child;
}
...//other fields omitted
上記の例では、すべての人間 (human) の名前と surname 属性にインデックスが付けられます。また、depth 属性が原因で、親の名前と surname (姓) も再帰的に 2 行目にインデックス化されます。人が直接、自身の親、または親の名前で検索することができます。第 2 レベル以外では、姓 (surname) ではなく、もう 1 レベル (名前のみ) をインデックス化します。
これにより、インデックスに以下のフィールドが生成されます。
-
id: プライマリーキーとして -
_hibernate_class: エンティティータイプを保存 -
name: 直接フィールドとして -
surname: 直接フィールドとして -
parents.name: デプス 1 の埋め込みフィールドとして -
parents.surname: デプス 1 の埋め込みフィールドとして -
parents.parents.name: デプス 2 の埋め込みフィールドとして -
parents.parents.surname: デプス 2 の埋め込みフィールドとして -
parents.parents.parents.name: includePaths で指定される追加パスとして最初のparentsはフィールド名から推測され、残りのパスは includePaths の属性です。
インデックス化されたパスを明示的に制御することは、必要なクエリーを最初に定義してアプリケーションを設計する場合に容易になる可能性があります。この時点では、どのフィールドが必要かを正確に把握している可能性もあります。その他のどのフィールドがユースケースを実装する必要はありません。
7.4.2. Boosting リンクのコピーリンクがクリップボードにコピーされました!
Lucene には boosting の概念があります。そのため、特定のドキュメントやフィールドに対して、他のドキュメントまたはフィールドよりも重要性を高くするか、低いものにすることができます。Lucene は、インデックスと検索時間の改善を区別します。以下のセクションでは、Hibernate Search を使用してインデックス時間を改善する方法を説明します。
7.4.2.1. 静的インデックスの時間の改善 リンクのコピーリンクがクリップボードにコピーされました!
インデックス化されたクラスまたはプロパティーの静的なブースト値を定義するには、@Boost アノテーションを使用できます。このアノテーションは、@Field 内で使用することも、メソッドまたはクラスレベルで直接指定することもできます。
例: @Boost を使用するさまざまな方法
@Entity
@Indexed
public class Essay {
...
@Id
@DocumentId
public Long getId() { return id; }
@Field(name="Abstract", store=Store.YES, boost=@Boost(2f))
@Boost(1.5f)
public String getSummary() { return summary; }
@Lob
@Field(boost=@Boost(1.2f))
public String getText() { return text; }
@Field
public String getISBN() { return isbn; }
}
上記の例では、Essay が検索一覧の最上位に到達する可能性は 1.7 で乗算されます。summary フィールドは、isbn フィールドに比べて 3.0 重要になります (2 * 1.5。これは、isbn フィールドよりも @Field.boost および @Boost が累積的であるためです)。Text フィールドは isbn フィールドよりも 1.2 倍重要になります。この説明は、最も厳格な条件では間違っていますが、あらゆる実用的な目的において、実用性に十分に近い点に注意してください。
7.4.2.2. 動的インデックス時間の改善 リンクのコピーリンクがクリップボードにコピーされました!
Static Index Time Boosting (静的インデックスの時間の改善)で使用される @Boost アノテーションは、ランタイム時にインデックス化されたエンティティーの状態に依存しない静的なブーディング係数を定義します。ただし、改善要因がエンティティーの実際の状態に依存する可能性があるユースケースがあります。この場合は、@DynamicBoost アノテーションと付随するカスタム BoostStrategy を使用できます。
例: 動的ブースト
public enum PersonType {
NORMAL,
VIP
}
@Entity
@Indexed
@DynamicBoost(impl = VIPBoostStrategy.class)
public class Person {
private PersonType type;
// ....
}
public class VIPBoostStrategy implements BoostStrategy {
public float defineBoost(Object value) {
Person person = ( Person ) value;
if ( person.getType().equals( PersonType.VIP ) ) {
return 2.0f;
}
else {
return 1.0f;
}
}
}
上記の例では、動的ブーストは、インデックス処理時に使用される BoostStrategy インターフェースの実装として VIPBoostStrategy を指定するクラスレベルで定義されます。@DynamicBoost は、クラスまたはフィールドレベルのいずれかで配置できます。アノテーションの配置に応じて、エンティティー全体が defineBoost メソッドに渡されるか、アノテーションが付いたフィールド/プロパティー値のみに渡されます。渡されたオブジェクトを正しいタイプにキャストするのはユーザー自身です。この例では、VIP ユーザーのすべてのインデックス化された値が通常の人の値と同じくらい重要になります。
指定された BoostStrategy 実装は、パブリックの no-arg コンストラクターを定義する必要があります。
当然ながら、エンティティーで @Boost と @DynamicBoost DynamicBoost アノテーションを混在させることができます。定義されたすべてのブースター要素は累積的です。
7.4.3. 分析 リンクのコピーリンクがクリップボードにコピーされました!
Analysis、テキストを 単一の用語 (単語) に変換するプロセスであり、フルテキスト検索エンジンの主な機能として見なされます。Lucene は、Analyzers の概念を使用してこのプロセスを制御します。以下のセクションでは、Hibernate Search がアナライザーを設定するために提供する複数の方法について説明します。
7.4.3.1. デフォルトの Analyzer とクラスによる Analyzer リンクのコピーリンクがクリップボードにコピーされました!
トークン化されたフィールドのインデックス化に使用されるデフォルトのアナライザークラスは、hibernate.search.analyzer プロパティーで設定できます。このプロパティーのデフォルト値は org.apache.lucene.analysis.standard.StandardAnalyzer です。
また、エンティティーやプロパティー、さらには @Field ごとにアナライザークラスを定義することもできます (複数のフィールドが単一のプロパティーからインデックス化される場合に便利です)。
例: @Analyzer の異なる使用
@Entity
@Indexed
@Analyzer(impl = EntityAnalyzer.class)
public class MyEntity {
@Id
@GeneratedValue
@DocumentId
private Integer id;
@Field
private String name;
@Field
@Analyzer(impl = PropertyAnalyzer.class)
private String summary;
@Field(analyzer = @Analyzer(impl = FieldAnalyzer.class)
private String body;
...
}
この例では、EntityAnalyzer を使用して、PropertyAnalyzer と FieldAnalyzer でインデックス化される summary と body を除き トークン化されたプロパティー (name) をインデックス化します。
同じエンティティーで異なるアナライザーを組み合わせることは多くの場合で適切ではありません。特にクエリー全体に同じパーサーを使用する QueryParser を使用している場合、クエリーの構築がより複雑になり、結果の予測が難しくなります。経験上、特定のフィールドでは、同じアナライザーを使用してインデックス付けとクエリーを実行する必要があります。
7.4.3.2. Named Analyzers リンクのコピーリンクがクリップボードにコピーされました!
アナライザーの処理は非常に複雑になる可能性があります。このため、Hibernate Search にはアナライザ定義の概念が導入されました。アナライザ定義は、@Analyzer 宣言の多くで再利用でき、以下で構成されています。
- a name: 定義を参照するために使用される一意な文字列
- a list of char filters: 各文字型フィルターは、トークン化の前に入力文字を事前処理します。文字型フィルターでは、文字の追加、変更、削除ができます。一般的な使用方法として、文字を正規化する方法があります。
- a tokenizer: 入力ストリームを個別の単語にトークン化します。
- a list of filters: 各フィルターは、単語の削除や変更を行い、トークンライザーが提供するストリームに単語を追加することさえあります。
タスクの分離: 文字型フィルターのリストとトークンライザー、およびフィルターの一覧が続きます。これにより、個々のコンポーネントを簡単に再利用でき、(Lego など) 非常に柔軟な方法でカスタマイズされたアナライザーを構築することができます。一般的に、文字型フィルターは文字入力で一部の事前処理を実行し、Tokenizer は、文字入力を TokenFilters によってさらに処理されるトークンに変換してトークン処理を開始します。Hibernate Search は Solr Analyzer フレームワークを使用してこのインフラストラクチャーをサポートします。
以下に記載されている具体的な例を見てみましょう。まず、文字型フィルターはファクトリーによって定義されます。この例では、マッピング文字型フィルターが使用され、マッピングファイルに指定されたルールに基づいて入力内の文字が置き換えられます。次にトークナイザーを定義します。この例では、標準トークナイザーを使用します。最後に同じように重要に、のフィルターの一覧がファクトリーによって定義されます。この例では、StopFilter フィルターは、専用の用語プロパティーファイルを読み取ります。フィルターはケースを無視することも予想されます。
例: @AnalyzerDef および Solr フレームワーク
@AnalyzerDef(name="customanalyzer",
charFilters = {
@CharFilterDef(factory = MappingCharFilterFactory.class, params = {
@Parameter(name = "mapping",
value = "org/hibernate/search/test/analyzer/solr/mapping-chars.properties")
})
},
tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class),
filters = {
@TokenFilterDef(factory = ISOLatin1AccentFilterFactory.class),
@TokenFilterDef(factory = LowerCaseFilterFactory.class),
@TokenFilterDef(factory = StopFilterFactory.class, params = {
@Parameter(name="words",
value= "org/hibernate/search/test/analyzer/solr/stoplist.properties" ),
@Parameter(name="ignoreCase", value="true")
})
})
public class Team {
...
}
フィルターと文字型フィルターは、@AnalyzerDef アノテーションで定義された順序で適用されます。順序は重要です。
一部のトークナイザー、トークンフィルター、または文字型フィルターは、設定ファイルやメタデータファイルなどのリソースを読み込みます。これは、ストップフィルターと同意フィルターの例になります。リソース文字セットが仮想マシンのデフォルトを使用していない場合は、resource_charset パラメーターを追加して明示的に指定できます。
例: 特定の文字セットを使用したプロパティーファイルの読み込み
@AnalyzerDef(name="customanalyzer",
charFilters = {
@CharFilterDef(factory = MappingCharFilterFactory.class, params = {
@Parameter(name = "mapping",
value = "org/hibernate/search/test/analyzer/solr/mapping-chars.properties")
})
},
tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class),
filters = {
@TokenFilterDef(factory = ISOLatin1AccentFilterFactory.class),
@TokenFilterDef(factory = LowerCaseFilterFactory.class),
@TokenFilterDef(factory = StopFilterFactory.class, params = {
@Parameter(name="words",
value= "org/hibernate/search/test/analyzer/solr/stoplist.properties" ),
@Parameter(name="resource_charset", value = "UTF-16BE"),
@Parameter(name="ignoreCase", value="true")
})
})
public class Team {
...
}
定義が完了すると、以下の例のように @Analyzer 宣言でアナライザーの定義を再利用できます。
例: 名前でのアナライザーの参照
@Entity
@Indexed
@AnalyzerDef(name="customanalyzer", ... )
public class Team {
@Id
@DocumentId
@GeneratedValue
private Integer id;
@Field
private String name;
@Field
private String location;
@Field
@Analyzer(definition = "customanalyzer")
private String description;
}
@AnalyzerDef で宣言された Analyzer インスタンスは、SearchFactory の名前でも利用できます.これは、クエリーを構築する際に非常に便利です。
Analyzer analyzer = fullTextSession.getSearchFactory().getAnalyzer("customanalyzer");
クエリー内のフィールドは、共通の「言語」を伝えるためにフィールドのインデックス作成に使用するものと同じアナライザーで分析する必要があります。クエリーとインデックス作成プロセス間で同じトークンが再利用されます。このルールには例外がありますが、ほとんどの場合に当てはまります。実際に何を実行しているのかが分からない限り、それに従ってください。
7.4.3.3. 利用可能なアナライザー リンクのコピーリンクがクリップボードにコピーされました!
Solr および Lucene には、多くの便利なデフォルトの文字型フィルター、トークンサイザー、およびフィルターがあります。文字型フィルターファクトリー、トークン化ファクトリー、およびフィルターファクトリーの完全な一覧は、http://wiki.apache.org/solr/AnalyzersTokenizersTokenFilters にあります。それらのいくつかをチェックしてください。
| ファクトリー | 説明 | パラメーター |
|---|---|---|
| MappingCharFilterFactory | リソースファイルで指定されたマッピングに基づいて、1 文字または複数の文字を 1 文字または複数の文字に置き換えます。 |
|
| HTMLStripCharFilterFactory | HTML 標準のタグを削除し、テキストを保持します。 | none |
| ファクトリー | 説明 | パラメーター |
|---|---|---|
| StandardTokenizerFactory | Lucene StandardTokenizer の使用 | none |
| HTMLStripCharFilterFactory | HTML タグを削除し、テキストを保持して StandardTokenizer に渡します。 | none |
| PatternTokenizerFactory | 指定された正規表現パターンでテキストを区切ります。 | pattern: トークン化に使用する正規表現 group: トークンに抽出するパターングループを示します。 |
| ファクトリー | 説明 | パラメーター |
|---|---|---|
| StandardFilterFactory | 略語および単語からドットを削除する | none |
| LowerCaseFilterFactory | すべての単語を小文字にします | none |
| StopFilterFactory | ストップワードの一覧に一致する単語 (トークン) を削除します。 | words: ストップワードを含むリソースファイルを参照します。 ignoreCase: ストップワードを比較する際に大文字と小文字が無視される必要がある場合は true、そうでない場合は false を設定します。 |
| SnowballPorterFilterFactory | 特定の言語で、単語を語根に減らします (例: protect、protects、protection は同じ語根を共有)。このようなフィルターを使用すると、関連する単語を検索できます。 |
|
IDE で org.apache.lucene.analysis.TokenizerFactory and org.apache.lucene.analysis.TokenFilterFactory のすべての実装を確認して、利用可能な実装を確認することが推奨されます。
7.4.3.4. 動的アナライザーの選択 リンクのコピーリンクがクリップボードにコピーされました!
現時点で、アナライザーを指定する方法はすべて静的でした。ただし、インデックスを作成するエンティティーの現在の状態 (たとえば多言語アプリケーション) に応じてアナライザーを選択すると便利なユースケースがあります。たとえば、BlogEntry クラスの場合、アナライザーはエントリーの言語プロパティーに依存する可能性があります。このプロパティーによっては、実際のテキストにインデックスを付けるために正しい言語固有のスチーマーを選択する必要があります。
この動的アナライザーを有効にするために、Hibernate Search で AnalyzerDiscriminator アノテーションが導入されました。以下の例は、このアノテーションの使用方法を示しています。
例: @AnalyzerDiscriminator の使用
@Entity
@Indexed
@AnalyzerDefs({
@AnalyzerDef(name = "en",
tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class),
filters = {
@TokenFilterDef(factory = LowerCaseFilterFactory.class),
@TokenFilterDef(factory = EnglishPorterFilterFactory.class
)
}),
@AnalyzerDef(name = "de",
tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class),
filters = {
@TokenFilterDef(factory = LowerCaseFilterFactory.class),
@TokenFilterDef(factory = GermanStemFilterFactory.class)
})
})
public class BlogEntry {
@Id
@GeneratedValue
@DocumentId
private Integer id;
@Field
@AnalyzerDiscriminator(impl = LanguageDiscriminator.class)
private String language;
@Field
private String text;
private Set<BlogEntry> references;
// standard getter/setter
...
}
public class LanguageDiscriminator implements Discriminator {
public String getAnalyzerDefinitionName(Object value, Object entity, String field) {
if ( value == null || !( entity instanceof BlogEntry ) ) {
return null;
}
return (String) value;
}
}
@AnalyzerDiscriminator を使用するための前提条件は、動的に使用されるすべてのアナライザーが @AnalyzerDef 定義で事前定義されることです 。このような場合、クラスまたはアナライザーを動的に選択するエンティティーの特定のプロパティーに @AnalyzerDiscriminator アノテーションを配置することができます。AnalyzerDiscriminator の impl パラメーターを使用すると、Dicriminator インターフェースの具体的な実装を指定できます。このインターフェースの実装は、ユーザー自身が提供する必要があります。実装が必要な唯一の方法は getAnalyzerDefinitionName() で、これは Lucene ドキュメントに追加された各フィールドに対して呼び出されます。インデックスを取得するエンティティーもインターフェースメソッドに渡されます。value パラメーターは、AnalyzerDiscriminator がクラスレベルではなくプロパティーレベルに配置されている場合にのみ設定されます。この場合、値はこのプロパティーの現在の値を表します。
Discriminator インターフェースの実装では、既存のアナライザー定義の名前を返し、デフォルトのアナライザーが上書きされない場合は null を返します。上記の例では、言語パラメーターは @AnalyzerDefs で指定された名前に一致する 'de' または 'en' であることを仮定しています 。
7.4.3.5. アナライザーの取得 リンクのコピーリンクがクリップボードにコピーされました!
複数のアナライザーがドメインモデルで使用される場合、スチーミングや概算などの利点を活かすために、アナライザーを取得することができます。この場合、同じアナライザーを使用してクエリーを作成します。または、正しいアナライザーを自動的に選択する Hibernate Search クエリー DSL を使用します。以下を参照してください。
Lucene プログラム API または Lucene クエリーパーサーのいずれを使用している場合も、特定のエンティティーのスコープ分析を取得することができます。スコープ指定のアナライザーは、インデックス化されたフィールドに応じて適切なアナライザーを適用するアナライザーです。複数のアナライザーを特定のエンティティーに定義でき、それぞれが個別のフィールドで作業することに注意してください。スコープ指定のアナライザーは、すべてのアナライザーをコンテキスト認識のアナライザーに統合します。理論はビットが複雑であるように見えますが、クエリーで正しいアナライザーを使用することは非常に簡単です。
子エンティティーにプログラムによるマッピングを使用する場合は、子エンティティーによって定義されるフィールドのみを表示できます。親エンティティーから継承されたフィールドまたはメソッド (@MappedSuperclass でアノテーション付け) は設定できません。親エンティティーから継承したプロパティーを設定するには、子エンティティーのプロパティーを上書きするか、親エンティティーのプログラムによるマッピングを作成します。これは、子エンティティーで定義されていない場合に親エンティティーのフィールドとメソッドにアノテーションを付けることができないアノテーションの使用に影響します。
例: 完全テキストクエリーの構築時のスコープ付きテナントの使用
org.apache.lucene.queryParser.QueryParser parser = new QueryParser(
"title",
fullTextSession.getSearchFactory().getAnalyzer( Song.class )
);
org.apache.lucene.search.Query luceneQuery =
parser.parse( "title:sky Or title_stemmed:diamond" );
org.hibernate.Query fullTextQuery =
fullTextSession.createFullTextQuery( luceneQuery, Song.class );
List result = fullTextQuery.list(); //return a list of managed objects
上記の例では、song タイトルが 2 つのフィールドでインデックス化されています。title フィールドでは標準アナライザーが使用され、title_stemmed フィールドには、スチーミングアナライザーが使用されます。検索ファクトリーによって提供されるアナライザーを使用すると、クエリーはターゲットフィールドに応じて適切なアナライザーを使用します。
searchFactory.getAnalyzer(String) を使用して定義名で @AnalyzerDef を介して定義したアナライザーを取得することもできます。
7.4.4. ブリッジ リンクのコピーリンクがクリップボードにコピーされました!
エンティティーの基本的なマッピングを検討する際に、最も重要なファクトは無視されていました。Lucene では、すべてのインデックスフィールドは文字列として表現する必要があります。@Field アノテーションが付けられたエンティティープロパティーはすべて、インデックス化される文字列に変換する必要があります。これまでに言及していない理由は、Hibernate Search のほとんどのプロパティーでは、組み込みブリッジのセットにより翻訳ジョブが実行されるためです。ただし、場合によっては、翻訳プロセスをより細かく制御する必要があります。
7.4.4.1. ビルトインブリッジ リンクのコピーリンクがクリップボードにコピーされました!
Hibernate Search には、Java プロパティータイプとその完全なテキスト表現間の組み込みブリッジのセットがバンドルされています。
- null
-
デフォルトの
null要素ごとにインデックス化されません。Lucene は null 要素をサポートしません。ただし、状況によっては、null値を表すカスタムトークンを追加すると便利です。詳細はを参照してください。 - java.lang.String
- 文字列は short、Short、integer、Integer、long、Long、float、Float、double としてインデックス化されます。
- Double、BigInteger、BigDecimal
数値は文字列表現に変換されます。数値は Lucene (範囲指定のクエリーで使用される) によって追加されず、パディングする必要があることに注意してください。
注記Range クエリーの使用には欠点があります。別の方法は、結果クエリーを適切な範囲に対してフィルターするフィルタークエリーを使用することです。Hibernate Search は、カスタム Custom Bridges で説明されているように、カスタム StringBridge を使用できます。
- java.util.Date
日付は yyyyMMddHHmmssSSS として GMT 時間 (EST 2006 年 11 月 7 日 4:03 PM 12 秒の場合は 200611072203012) として保存されます。内部形式にこだわる必要はありません。TermRangeQuery を使用する場合は、グリニッジ標準時 (GMT) で日付を示す必要があることを把握しておくことが重要です。
通常は、ミリ秒単位までの日付を保存する必要はありません。
@DateBridgeは、インデックス に保存する適切な分解能を定義します ((@DateBridge(resolution=Resolution.DAY))。日付のパターンは、それに応じて切り捨てられます。
@Entity
@Indexed
public class Meeting {
@Field(analyze=Analyze.NO)
private Date date;
...
分解能が MILLISECOND 未満の日付は、@DocumentId にはできません。
デフォルトの Date ブリッジは Lucene の DateTools を使用して、文字列から文字列に変換します。これは、すべての日付が GMT 時間で表されることを意味します。固定タイムゾーンに日付を保存する必要がある場合は、カスタムの日付ブリッジを実装する必要があります。日付のインデックス作成および検索に関するアプリケーションの要件を理解している。
- java.net.URI、java.net.URL
- URI および URL は文字列表現に変換されます。
- java.lang.Class
- クラスは完全修飾クラス名に変換されます。スレッドコンテキストクラスローダーは、クラスがリハイドレートされる際に使用されます。
7.4.4.2. カスタムブリッジ リンクのコピーリンクがクリップボードにコピーされました!
Hibernate Search の組み込みブリッジは一部のプロパティータイプに対応していない場合や、ブリッジが使用する String 表現が要件を満たさない場合があります。以下では、この問題に対する複数の解決策を説明します。
7.4.4.2.1. StringBridge リンクのコピーリンクがクリップボードにコピーされました!
最も単純なカスタムソリューションは、Hibernate Search に予想される Object を String ブリッジに実装を提供することです。これを実行するには、org.hibernate.search.bridge.StringBridge インターフェースを実装する必要があります。すべての実装は同時に使用されるため、スレッドセーフである必要があります。
例: カスタム StringBridge 実装
/**
* Padding Integer bridge.
* All numbers will be padded with 0 to match 5 digits
*
* @author Emmanuel Bernard
*/
public class PaddedIntegerBridge implements StringBridge {
private int PADDING = 5;
public String objectToString(Object object) {
String rawInteger = ( (Integer) object ).toString();
if (rawInteger.length() > PADDING)
throw new IllegalArgumentException( "Try to pad on a number too big" );
StringBuilder paddedInteger = new StringBuilder( );
for ( int padIndex = rawInteger.length() ; padIndex < PADDING ; padIndex++ ) {
paddedInteger.append('0');
}
return paddedInteger.append( rawInteger ).toString();
}
}
前の例で定義された文字列ブリッジでは、@FieldBridge アノテーションにより、どのプロパティーまたはフィールドもこのブリッジを使用できます。
@FieldBridge(impl = PaddedIntegerBridge.class)
private Integer length;
7.4.4.2.2. パラメーター化されたブリッジ リンクのコピーリンクがクリップボードにコピーされました!
また、パラメーターをブリッジ実装に渡すと柔軟性が向上します。以下の例は ParameterizedBridge インターフェースを実装し、パラメーターは @FieldBridge アノテーションを介して渡されます。
例: ブリッジ実装にパラメーターを渡す
public class PaddedIntegerBridge implements StringBridge, ParameterizedBridge {
public static String PADDING_PROPERTY = "padding";
private int padding = 5; //default
public void setParameterValues(Map<String,String> parameters) {
String padding = parameters.get( PADDING_PROPERTY );
if (padding != null) this.padding = Integer.parseInt( padding );
}
public String objectToString(Object object) {
String rawInteger = ( (Integer) object ).toString();
if (rawInteger.length() > padding)
throw new IllegalArgumentException( "Try to pad on a number too big" );
StringBuilder paddedInteger = new StringBuilder( );
for ( int padIndex = rawInteger.length() ; padIndex < padding ; padIndex++ ) {
paddedInteger.append('0');
}
return paddedInteger.append( rawInteger ).toString();
}
}
//property
@FieldBridge(impl = PaddedIntegerBridge.class,
params = @Parameter(name="padding", value="10")
)
private Integer length;
ParameterizedBridge インターフェースは、StringBridge、TwoWayStringBridge、FieldBridge 実装で実装できます。
すべての実装はスレッドセーフである必要がありますが、パラメーターは初期化時に設定されるため、この段階で特別な注意をする必要はありません。
7.4.4.2.3. Type Aware Bridge リンクのコピーリンクがクリップボードにコピーされました!
これは、ブリッジが適用されるタイプを取得すると便利です。
- field/getter-level ブリッジのプロパティーの戻りタイプ。
- クラスレベルのブリッジのクラスタイプ。
例として、カスタム方式で列挙に対応するブリッジがありますが、実際の列挙型にアクセスする必要があります。AppliedOnTypeAwareBridge を実装するブリッジは、挿入時にブリッジが適用されるタイプを取得します。パラメーターと同様に、インジェクトされるタイプには、スレッドセーフティーに関する特別な作業は必要ありません。
7.4.4.2.4. Two-Way Bridge リンクのコピーリンクがクリップボードにコピーされました!
id プロパティー (@DocumentId アノテーション付き) でブリッジ実装を使用する必要がある場合は、TwoWayStringBridge という名前の拡張バージョン StringBridge を使用する必要があります。Hibernate Search は、識別子の文字列表現を読み取り、そこからオブジェクトを生成する必要があります。@FieldBridge アノテーションの使用方法には違いがありません。
例: id プロパティーに使用できる TwoWayStringBridge の実装
public class PaddedIntegerBridge implements TwoWayStringBridge, ParameterizedBridge {
public static String PADDING_PROPERTY = "padding";
private int padding = 5; //default
public void setParameterValues(Map parameters) {
Object padding = parameters.get( PADDING_PROPERTY );
if (padding != null) this.padding = (Integer) padding;
}
public String objectToString(Object object) {
String rawInteger = ( (Integer) object ).toString();
if (rawInteger.length() > padding)
throw new IllegalArgumentException( "Try to pad on a number too big" );
StringBuilder paddedInteger = new StringBuilder( );
for ( int padIndex = rawInteger.length() ; padIndex < padding ; padIndex++ ) {
paddedInteger.append('0');
}
return paddedInteger.append( rawInteger ).toString();
}
public Object stringToObject(String stringValue) {
return new Integer(stringValue);
}
}
//id property
@DocumentId
@FieldBridge(impl = PaddedIntegerBridge.class,
params = @Parameter(name="padding", value="10")
private Integer id;
双方向プロセスがべき等 (例: object = stringToObject( objectToString( object ) ) ) であることが重要です。
7.4.4.2.5. FieldBridge リンクのコピーリンクがクリップボードにコピーされました!
場合によっては、プロパティーを Lucene インデックスにマッピングするときに、単純なオブジェクトから文字列への変換が必要になります。可能な限り柔軟性を持たせるために、FieldBridge としてブリッジを実装することもできます。このインターフェースは、プロパティーの値を提供し、Lucene ドキュメント内で希望する方法でマッピングできるようにします。たとえば、プロパティーを異なるドキュメントフィールドに保存できます。インターフェースの概念は Hibernate UserTypes と非常に似ています。
例: FieldBridge インターフェースの実装
/**
* Store the date in 3 different fields - year, month, day - to ease Range Query per
* year, month or day (eg get all the elements of December for the last 5 years).
* @author Emmanuel Bernard
*/
public class DateSplitBridge implements FieldBridge {
private final static TimeZone GMT = TimeZone.getTimeZone("GMT");
public void set(String name, Object value, Document document, LuceneOptions luceneOptions) {
Date date = (Date) value;
Calendar cal = GregorianCalendar.getInstance(GMT);
cal.setTime(date);
int year = cal.get(Calendar.YEAR);
int month = cal.get(Calendar.MONTH) + 1;
int day = cal.get(Calendar.DAY_OF_MONTH);
// set year
luceneOptions.addFieldToDocument(
name + ".year",
String.valueOf( year ),
document );
// set month and pad it if needed
luceneOptions.addFieldToDocument(
name + ".month",
month < 10 ? "0" : "" + String.valueOf( month ),
document );
// set day and pad it if needed
luceneOptions.addFieldToDocument(
name + ".day",
day < 10 ? "0" : "" + String.valueOf( day ),
document );
}
}
//property
@FieldBridge(impl = DateSplitBridge.class)
private Date date;
上記の例では、フィールドはドキュメントに直接追加されません。代わりに、追加が LuceneOptions ヘルパーシステムに委任されます。このヘルパーは、Store や TermVector などの @Fieldで選択したオプションを適用するか、選択した @Boost 値を適用します。COMPRESS 実装の複雑性をカプセル化することは特に便利です。ドキュメントにフィールドを追加するために LuceneOptions に委譲することが推奨されますが、必要であれば、ドキュメントを直接編集して、LuceneOptions を無視しても問題ありません。
LuceneOptions などのクラスは、アプリケーションを Lucene API の変更から保護し、コードを簡素化するために作成されます。可能な場合はこれを使用しますが、さらに柔軟性が必要な場合は、使用しなくても問題ありません。
7.4.4.2.6. ClassBridge リンクのコピーリンクがクリップボードにコピーされました!
特定のエンティティーの複数のプロパティーを組み合わせて、これを特定の方法で Lucene インデックスにインデックスを設定すると便利です。@ClassBridge および @ClassBridges アノテーションは、プロパティーレベルではなくクラスレベルで定義できます。この場合、カスタムフィールドブリッジ実装は、特定のプロパティーの代わりに、エンティティーインスタンスを値のパラメーターとして受信します。以下の例では示していませんが、@ClassBridge は Basic Mapping セクションで説明されている termVector 属性をサポートします。
例: クラスブリッジの実装
@Entity
@Indexed
(name="branchnetwork",
store=Store.YES,
impl = CatFieldsClassBridge.class,
params = @Parameter( name="sepChar", value=" " ) )
public class Department {
private int id;
private String network;
private String branchHead;
private String branch;
private Integer maxEmployees
...
}
public class CatFieldsClassBridge implements FieldBridge, ParameterizedBridge {
private String sepChar;
public void setParameterValues(Map parameters) {
this.sepChar = (String) parameters.get( "sepChar" );
}
public void set( String name, Object value, Document document, LuceneOptions luceneOptions) {
// In this particular class the name of the new field was passed
// from the name field of the ClassBridge Annotation. This is not
// a requirement. It just works that way in this instance. The
// actual name could be supplied by hard coding it below.
Department dep = (Department) value;
String fieldValue1 = dep.getBranch();
if ( fieldValue1 == null ) {
fieldValue1 = "";
}
String fieldValue2 = dep.getNetwork();
if ( fieldValue2 == null ) {
fieldValue2 = "";
}
String fieldValue = fieldValue1 + sepChar + fieldValue2;
Field field = new Field( name, fieldValue, luceneOptions.getStore(),
luceneOptions.getIndex(), luceneOptions.getTermVector() );
field.setBoost( luceneOptions.getBoost() );
document.add( field );
}
}
この例では、特定の CatMissionClassBridge が department インスタンスに適用され、フィールドブリッジはブランチとネットワークの両方を連結し、この連結をインデックス化します。
7.5. Hibernate Search を使用した Lucene クエリーの実行 リンクのコピーリンクがクリップボードにコピーされました!
手順
Hibernate Search は Lucene クエリーを実行し、InfinispanHibernate セッションによって管理されるドメインオブジェクトを取得できます。この検索は、Hibernate パラダイムを離れることなく Lucene の機能を提供し、HQL、基準クエリー、ネイティブ SQL クエリーなど、Hibernate の従来の検索メカニズムに別の特性を与えます。
クエリーの準備と実行は、以下の 4 つの手順で構成されます。
- FullTextSession の作成
- Hibernate QueryHibernate Search クエリー DSL (推奨) または Lucene Query API を使用した Lucene クエリーの作成
- org.hibernate.Query を使用した Lucene クエリーのラップ
- サンプルの list() または scroll() を呼び出して検索を実行すると、検索が実行されます。
クエリー機能にアクセスするには、FullTextSession を使用します。この検索固有のセッションは、クエリーおよびインデックス機能を提供するために通常の org.hibernate.Session をラップします。
例: FullTextSession の作成
Session session = sessionFactory.openSession();
...
FullTextSession fullTextSession = Search.getFullTextSession(session);
FullTextSession を使用して、Hibernate Search クエリー DSL またはネイティブの Lucene クエリーのいずれかでフルテキストクエリーを構築します。
Hibernate Search クエリー DSL を使用する場合は、以下のコードを使用します。
final QueryBuilder b = fullTextSession.getSearchFactory().buildQueryBuilder().forEntity( Myth.class ).get();
org.apache.lucene.search.Query luceneQuery =
b.keyword()
.onField("history").boostedTo(3)
.matching("storm")
.createQuery();
org.hibernate.Query fullTextQuery = fullTextSession.createFullTextQuery( luceneQuery );
List result = fullTextQuery.list(); //return a list of managed objects
または、Lucene クエリーパーサーまたは Lucene プログラム API を使用して Lucene クエリーを書き込みます。
例: QueryParser を使用した Lucene クエリーの作成
SearchFactory searchFactory = fullTextSession.getSearchFactory();
org.apache.lucene.queryParser.QueryParser parser =
new QueryParser("title", searchFactory.getAnalyzer(Myth.class) );
try {
org.apache.lucene.search.Query luceneQuery = parser.parse( "history:storm^3" );
}
catch (ParseException e) {
//handle parsing failure
}
org.hibernate.Query fullTextQuery = fullTextSession.createFullTextQuery(luceneQuery);
List result = fullTextQuery.list(); //return a list of managed objects
Lucene クエリーに構築された Hibernate クエリーは org.hibernate.Query です。このクエリーは、HQL (Hibernate Query Language)、Native、および Criteria などの他の Hibernate クエリー機能と同じパラダイムに残ります。クエリーで list()、uniqueResult()、repeat()、および scroll () などのメソッドを使用します。
Hibernate Jakarta Persistence では、同じ拡張機能を利用できます。
例: Jakarta Persistence を使用した検索クエリーの作成
EntityManager em = entityManagerFactory.createEntityManager();
FullTextEntityManager fullTextEntityManager =
org.hibernate.search.jpa.Search.getFullTextEntityManager(em);
...
final QueryBuilder b = fullTextEntityManager.getSearchFactory()
.buildQueryBuilder().forEntity( Myth.class ).get();
org.apache.lucene.search.Query luceneQuery =
b.keyword()
.onField("history").boostedTo(3)
.matching("storm")
.createQuery();
javax.persistence.Query fullTextQuery = fullTextEntityManager.createFullTextQuery( luceneQuery );
List result = fullTextQuery.getResultList(); //return a list of managed objects
これらの例では、Hibernate API が使われています。FullTextQuery の取得方法を調整すると、同じ例を Jakarta Persistence で記述することもできます。
7.5.1. クエリーの構築 リンクのコピーリンクがクリップボードにコピーされました!
Hibernate Search クエリーは Lucene クエリーにビルドされるため、ユーザーはすべての Lucene クエリータイプを使用できます。クエリーを構築すると、Hibernate Search はクエリー操作 API として org.hibernate.Query を使用して追加のクエリー処理を行います。
7.5.1.1. Lucene API を使用した Lucene クエリーの構築 リンクのコピーリンクがクリップボードにコピーされました!
Lucene API では、クエリーパーサー(簡単なクエリー) または Lucene プログラム API (複雑なクエリー) のいずれかを使用します。Lucene クエリーの構築は、Hibernate Search ドキュメントの範囲外です。詳細は、オンラインの Lucene ドキュメント、またはLucene in Action または Hibernate Search in Action を参照してください。
7.5.1.2. Lucene クエリーの構築 リンクのコピーリンクがクリップボードにコピーされました!
Lucene プログラム API は、フルテキストクエリーを有効にします。ただし、Lucene プログラム API を使用する場合は、パラメーターを同等の文字列に変換する必要があります。また、正しいアナライザーを正しいフィールドに適用する必要もあります。たとえば、ngram アナライザーは、特定の単語に対するトークンとして複数の ngrams を使用するため、そのように検索する必要があります。このタスクには QueryBuilder を使用することが推奨されます。
Hibernate Search のクエリー API は変動しており、以下の主要な特徴があります。
- メソッド名は英語です。そのため、API 操作は、一連の英語のフレーズおよび命令として読み取り、理解することができます。
- IDE オートコンプリートを使用します。これは、現在の入力プレフィックスの完了を容易にし、ユーザーが適切なオプションを選択できるようにします。
- 多くの場合、チェーンメソッドパターンを使用します。
- API 操作を簡単に使用でき、読み取ることができます。
API を使用するには、まず、指定の indexedentitytype に割り当てられるクエリービルダーを作成します。この QueryBuilder は、使用するアナライザーと、適用するフィールドブリッジを認識します。複数の QueryBuilder (クエリーのルートに関連するエンティティータイプごとに 1 つ) を作成できます。QueryBuilder は SearchFactory から派生します。
QueryBuilder mythQB = searchFactory.buildQueryBuilder().forEntity( Myth.class ).get();
特定のフィールドに使用されるアナライザーも上書きできます。
QueryBuilder mythQB = searchFactory.buildQueryBuilder()
.forEntity( Myth.class )
.overridesForField("history","stem_analyzer_definition")
.get();
クエリービルダーを使用して Lucene クエリーをビルドできるようになりました。Lucene プログラム API を使用してアセンブルされた Lucene のクエリーパーサーまたはクエリーオブジェクトを使用して生成されたカスタマイズされたクエリーは、Hibernate Search DSL とともに使用されます。
7.5.1.3. キーワードのクエリー リンクのコピーリンクがクリップボードにコピーされました!
以下の例は、特定の単語を検索する方法を示しています。
Query luceneQuery = mythQB.keyword().onField("history").matching("storm").createQuery();
| Parameter | 説明 |
|---|---|
| keyword() | 特定の単語を検索するには、このパラメーターを使用します。 |
| onField() | このパラメーターを使用して、単語を検索する lucene フィールドを指定します。 |
| matching() | このパラメーターを使用して、検索文字列の一致を指定します。 |
| createQuery() | Lucene クエリーオブジェクトを作成します。 |
-
「storm」という値が
historyFieldBridge から渡されます。これは、数値または日付が必要な場合に便利です。 -
フィールドブリッジの値は、
historyフィールドインデックス化に使用されるアナライザーに渡されます。これにより、クエリーはインデックス (小文字、ngram、スチミングなど) よりも、同じ用語変換を使用します。分析プロセスで指定の単語が複数生成されると、ブールクエリーがSHOULD論理 (おおよそOR論理) とともに使用されます。
タイプ文字列ではないプロパティーを検索します。
@Indexed
public class Myth {
@Field(analyze = Analyze.NO)
@DateBridge(resolution = Resolution.YEAR)
public Date getCreationDate() { return creationDate; }
public Date setCreationDate(Date creationDate) { this.creationDate = creationDate; }
private Date creationDate;
...
}
Date birthdate = ...;
Query luceneQuery = mythQb.keyword().onField("creationDate").matching(birthdate).createQuery();
プレーンの Lucene では、Date オブジェクトは文字列表現 (この場合は年) に変換する必要がありました。
この変換は、FieldBridge に objectToString メソッド (およびすべての組み込み FieldBridge 実装) がある場合、すべてのオブジェクトに対して機能します。
以下の例では、ngram アナライザーを使用するフィールドを検索します。ngram アナライザーは単語の ngrams インデックスを連続させるため、ユーザーの誤字を防ぎます。たとえば、hibernate という単語の 3 グラムは hib、ibe、ber、ern、rna、nat、ate です。
@AnalyzerDef(name = "ngram",
tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class ),
filters = {
@TokenFilterDef(factory = StandardFilterFactory.class),
@TokenFilterDef(factory = LowerCaseFilterFactory.class),
@TokenFilterDef(factory = StopFilterFactory.class),
@TokenFilterDef(factory = NGramFilterFactory.class,
params = {
@Parameter(name = "minGramSize", value = "3"),
@Parameter(name = "maxGramSize", value = "3") } )
}
)
public class Myth {
@Field(analyzer=@Analyzer(definition="ngram")
public String getName() { return name; }
public String setName(String name) { this.name = name; }
private String name;
...
}
Date birthdate = ...;
Query luceneQuery = mythQb.keyword().onField("name").matching("Sisiphus")
.createQuery();
一致する単語「Sisiphus」は小文字になり、3 つのグラム (sis、isi、sip、iph、phu、hus) に分けられます。これらの各 ngram はクエリーの一部になります。ユーザーは、Sysiphus myth ( y) を見つけることができます。ユーザーに透過的に実行されます。
ユーザーが特定のフィールドでフィールドブリッジまたはアナライザーを使用しない場合は、ignoreAnalyzer() 関数または ignoreFieldBridge() 関数を呼び出しできます。
同一フィールドで複数の使用可能な単語を検索するには、それらすべてを一致するシーケンスに追加します。
//search document with storm or lightning in their history
Query luceneQuery =
mythQB.keyword().onField("history").matching("storm lightning").createQuery();
複数のフィールドで同じ単語を検索するには、onField メソッドを使用します。
Query luceneQuery = mythQB
.keyword()
.onFields("history","description","name")
.matching("storm")
.createQuery();
場合によっては、同じ用語を検索する場合でも、あるフィールドを別のフィールドとは異なる方法で処理する必要があります。その場合は、andField() メソッドを使用します。
Query luceneQuery = mythQB.keyword()
.onField("history")
.andField("name")
.boostedTo(5)
.andField("description")
.matching("storm")
.createQuery();
上記の例では、フィールド名のみが 5 に改善されています。
7.5.1.4. Fuzzy クエリー リンクのコピーリンクがクリップボードにコピーされました!
Levenshtein 距離アルゴリズムに基づく fuzzy クエリーを実行するには、keyword クエリーから始め、fuzzy フラグを追加します。
Query luceneQuery = mythQB
.keyword()
.fuzzy()
.withThreshold( .8f )
.withPrefixLength( 1 )
.onField("history")
.matching("starm")
.createQuery();
threshold は、両方の用語で照合が考慮される制限です。0 から 1 までの小数で、デフォルト値は 0.5 です。prefixLength は、「fuzzyness」で無視される接頭辞の長さです。デフォルト値は 0 ですが、多数の異なる用語を含むインデックスにはゼロ以外の値が推奨されます。
7.5.1.5. ワイルドカードクエリー リンクのコピーリンクがクリップボードにコピーされました!
ワイルドカードクエリーは、単語の一部のみが認識される状況で役に立ちます。? は単一文字で、* は複数文字を表します。パフォーマンス維持のために、クエリーは ? または * で開始しないことが推奨されます。
Query luceneQuery = mythQB
.keyword()
.wildcard()
.onField("history")
.matching("sto*")
.createQuery();
ワイルドカードクエリーは、一致する用語にアナライザーを適用しません。経験のある * または ? のリスクが高すぎます。
7.5.1.6. フレーズクエリー リンクのコピーリンクがクリップボードにコピーされました!
これまで、単語または単語セットを見てきましたが、ユーザーは正確な単語または概算した単語を検索することもできます。これを行うには、phrase() を使用します。
Query luceneQuery = mythQB
.phrase()
.onField("history")
.sentence("Thou shalt not kill")
.createQuery();
おおよその文は、slop 係数を追加することで検索できます。slop 係数は、文内で許可される他の単語の数を表します。これは、within または near 演算子のように機能します。
Query luceneQuery = mythQB
.phrase()
.withSlop(3)
.onField("history")
.sentence("Thou kill")
.createQuery();
7.5.1.7. 範囲クエリー リンクのコピーリンクがクリップボードにコピーされました!
範囲クエリーは、指定された範囲内 (含まれるかどうか) または特定の範囲を下回る、もしくは上回る値を検索します。
//look for 0 <= starred < 3
Query luceneQuery = mythQB
.range()
.onField("starred")
.from(0).to(3).excludeLimit()
.createQuery();
//look for myths strictly BC
Date beforeChrist = ...;
Query luceneQuery = mythQB
.range()
.onField("creationDate")
.below(beforeChrist).excludeLimit()
.createQuery();
7.5.1.8. クエリーの統合 リンクのコピーリンクがクリップボードにコピーされました!
クエリーを組み合わせてより複雑なクエリーを作成できます。以下の集計演算子を使用できます。
-
SHOULD: クエリーには、サブクエリーの一致する要素が含まれる必要があります。 -
MUST: クエリーには、サブクエリーの一致する要素が含まれる必要があります。 -
MUST NOT: クエリーには、サブクエリーの一致する要素を含めないでください。
サブクエリーは、ブール値クエリー自体を含む任意の Lucene クエリーにすることができます。
例: SHOULD クエリー
//look for popular myths that are preferably urban
Query luceneQuery = mythQB
.bool()
.should( mythQB.keyword().onField("description").matching("urban").createQuery() )
.must( mythQB.range().onField("starred").above(4).createQuery() )
.createQuery();
例: MUST クエリー
//look for popular urban myths
Query luceneQuery = mythQB
.bool()
.must( mythQB.keyword().onField("description").matching("urban").createQuery() )
.must( mythQB.range().onField("starred").above(4).createQuery() )
.createQuery();
例: MUST NOT クエリー
//look for popular modern myths that are not urban
Date twentiethCentury = ...;
Query luceneQuery = mythQB
.bool()
.must( mythQB.keyword().onField("description").matching("urban").createQuery() )
.not()
.must( mythQB.range().onField("starred").above(4).createQuery() )
.must( mythQB
.range()
.onField("creationDate")
.above(twentiethCentury)
.createQuery() )
.createQuery();
7.5.1.9. クエリーオプション リンクのコピーリンクがクリップボードにコピーされました!
Hibernate Search クエリー DSL は使いやすく、読みやすいクエリー API です。Lucene クエリーを受け入れて生成すると、DSL で対応していないクエリータイプを組み込むことができます。
以下は、クエリータイプおよびフィールドのクエリーオプションの要約です。
- boostedTo (クエリータイプおよびフィールド上) は、クエリー全体または特定のフィールドを指定された係数に改善します。
- constantScore (クエリー上) では、クエリーに一致するすべての結果の、定数スコアがブーストと等しくなります。
- filteredBy(Filter) (クエリー上) は、Filter インスタンスを使用してクエリー結果をフィルターします。
- ignoreAnalyzer (フィールド上) は、このフィールドを処理するときにアナライザーを無視します。
- ignoreFieldBridge (フィールド上) は、このフィールドを処理するときにフィールドブリッジを無視します。
例: クエリーオプションの組み合わせ
Query luceneQuery = mythQB
.bool()
.should( mythQB.keyword().onField("description").matching("urban").createQuery() )
.should( mythQB
.keyword()
.onField("name")
.boostedTo(3)
.ignoreAnalyzer()
.matching("urban").createQuery() )
.must( mythQB
.range()
.boostedTo(5).withConstantScore()
.onField("starred").above(4).createQuery() )
.createQuery();
7.5.1.10. Hibernate Search クエリーの構築 リンクのコピーリンクがクリップボードにコピーされました!
7.5.1.10.1. 一般性 リンクのコピーリンクがクリップボードにコピーされました!
Lucene クエリーの構築後に、Hibernate クエリー内にラップします。クエリーはインデックス化されたすべてのエンティティーを検索し、明示的に設定しない限り、インデックス化されたクラスのすべてのタイプを返します。
例: Hibernate クエリーでの Lucene クエリーのラップ
FullTextSession fullTextSession = Search.getFullTextSession( session );
org.hibernate.Query fullTextQuery = fullTextSession.createFullTextQuery( luceneQuery );
パフォーマンスを改善するために、返されたタイプを以下のように制限します。
例: エンティティータイプによる検索結果のフィルター
fullTextQuery = fullTextSession
.createFullTextQuery( luceneQuery, Customer.class );
// or
fullTextQuery = fullTextSession
.createFullTextQuery( luceneQuery, Item.class, Actor.class );
次の例の最初の部分は、一致する Customers のみを返します。同じ例の次の部分は、一致する Actors と item を返します。タイプ制限はポリモーフィックです。そのため、2 つのサブクラス Sales man と、ベースクラス Person の Customer は、結果タイプに基づいてフィルタリングする Person.class を指定します。
7.5.1.10.2. ページネーション リンクのコピーリンクがクリップボードにコピーされました!
パフォーマンスの低下を回避するには、クエリーごとに返されたオブジェクトの数を制限することが推奨されます。あるページから別のページに移動するユーザーは、非常に一般的なユースケースです。ページネーションを定義する方法は、プレーンの HQL または基準クエリーでのページネーションの定義と似ています。
例: 検索クエリーのページネーションの定義
org.hibernate.Query fullTextQuery =
fullTextSession.createFullTextQuery( luceneQuery, Customer.class );
fullTextQuery.setFirstResult(15); //start from the 15th element
fullTextQuery.setMaxResults(10); //return 10 elements
fulltextQuery.getResultSize() によるページネーションに関係なく、一致する要素の合計数を取得できます。
7.5.1.10.3. ソート リンクのコピーリンクがクリップボードにコピーされました!
Apache Lucene には、柔軟で強力な結果ソートメカニズムが含まれています。デフォルトの並び替えは関連により行われ、さまざまなユースケースに適しています。Lucene Sort オブジェクトを使用し、他のプロパティーでソートするようにソートメカニズムを変更できます。
例: Lucene ソートの指定
org.hibernate.search.FullTextQuery query = s.createFullTextQuery( query, Book.class );
org.apache.lucene.search.Sort sort = new Sort(
new SortField("title", SortField.STRING));
List results = query.list();
ソートに使用されるフィールドは、トークン化できません。トークン化についての詳細は、@Field を参照してください 。
7.5.1.10.4. ストラテジーの取得 リンクのコピーリンクがクリップボードにコピーされました!
Hibernate Search は、戻り値の型が単一のクラスに制限されている場合に、単一のクエリーを使用してオブジェクトをロードします。Hibernate Search は、ドメインモデルで定義された静的なフェッチストラテジーによって制限されます。以下のように、特定のユースケースのフェッチストラテジーを微調整すると便利です。
例: クエリーでの FetchMode の指定
Criteria criteria =
s.createCriteria( Book.class ).setFetchMode( "authors", FetchMode.JOIN );
s.createFullTextQuery( luceneQuery ).setCriteriaQuery( criteria );
この例では、クエリーは LuceneQuery に一致するすべての Books を返します。Authors コレクションは、SQL の外部結合を使用して同じクエリーからロードされます。
条件クエリー定義では、タイプは提供された基準クエリーに基づいて推測されます。そのため、返されたエンティティータイプを制限する必要はありません。
フェッチモードは、唯一の調整可能なプロパティーです。getResultSize() は制限のある Criteria とともに使用された場合に SearchException をスローするため、Criteria クエリーで制限 (完全な句) を使用しないでください。
複数のエンティティーが予想される場合は、setGatewayQuery を使用しないでください。
7.5.1.10.5. プロジェクション リンクのコピーリンクがクリップボードにコピーされました!
場合によっては、プロパティーの小さなサブセットのみが必要となります。Hibernate Search を使用して、以下のようにプロパティーのサブセットを取得します。
Hibernate Search は Lucene インデックスからプロパティーを抽出し、それらをオブジェクト表現に変換し、Object[] のリストを返します。プロジェクションは、データベースのラウンドトリップが長くなるのを防ぎます。ただし、以下の制限があります。
-
予測されるプロパティーはインデックス (
@Field(store=Store.YES)) に保存され、インデックスサイズが増えます。 予測されるプロパティーは、org.hibernate.search.bridge.TwoWayFieldBridge または
org.hibernate.search.bridge.TwoWayStringBridgeを実装するFieldBridgeを使用する必要があり、後者はより単純なバージョンになります。注記Hibernate Search の組み込みタイプはすべて双方向です。
- インデックス化されたエンティティーまたはその埋め込み関連の簡単なプロパティーのみを展開できます。したがって、埋め込みエンティティー全体を展開できません。
- @IndexedEmbedded でインデックス化されるコレクションやマップで機能しません。
Lucene は、クエリー結果に関するメタデータ情報を提供します。インジェクト定数を使用してメタデータを取得します。
例: メタデータの取得へのプロジェクションの使用
org.hibernate.search.FullTextQuery query =
s.createFullTextQuery( luceneQuery, Book.class );
query.;
List results = query.list();
Object[] firstResult = (Object[]) results.get(0);
float score = firstResult[0];
Book book = firstResult[1];
String authorName = firstResult[2];
フィールドは、以下のプロジェクションと組み合わせることができます。
- FullTextQuery.THIS: 初期化され管理されたエンティティーを返します (展開されていないクエリーが実行される場合)。
- FullTextQuery.DOCUMENT: 展開されるオブジェクトに関連する Lucene ドキュメントを返します。
- FullTextQuery.OBJECT_CLASS: インデックス化されたエンティティーのクラスを返します。
- FullTextQuery.SCORE: クエリーのドキュメントスコアを返します。スコアは、あるクエリーの結果を別のクエリーに対する比較には便利ですが、異なるクエリーの結果を比較する場合に有用ではありません。
- FullTextQuery.ID: 予測されるオブジェクトの ID プロパティー値。
- FullTextQuery.DOCUMENT_ID: Lucene ドキュメント ID。この値を Lucene ドキュメント ID として使用すると、異なる 2 つの IndexReader を開くたびに変更される可能性があります。
- FullTextQuery.explanation: 指定のクエリーの一致するオブジェクト/ドキュメントの Lucene Explanation オブジェクトを返します。これは、大量のデータを取得するのには適していません。通常、実行の説明は、一致する要素ごとに Lucene クエリー全体を実行することを意味します。そのため、展開が推奨されます。
7.5.1.10.6. オブジェクト初期化ストラテジーのカスタマイズ リンクのコピーリンクがクリップボードにコピーされました!
デフォルトでは、Hibernate Search は最適なストラテジーを使用して、完全なテキストクエリーに一致するエンティティーを初期化します。必要なエンティティーを取得するためにクエリーを実行します。このアプローチでは、取得したエンティティーが永続コンテキスト (セッション) または 2 次レベルキャッシュにほとんど存在しないデータベースのストライプが最小限に抑えられます。
2 次キャッシュにエンティティーが存在する場合は、データベースオブジェクトを取得する前に、Hibernate Search が強制的にキャッシュを調べます。
例: クエリーを使用する前の 2 次キャッシュのチェック
FullTextQuery query = session.createFullTextQuery(luceneQuery, User.class);
query.initializeObjectWith(
ObjectLookupMethod.SECOND_LEVEL_CACHE,
DatabaseRetrievalMethod.QUERY
);
ObjectLookupMethod は、オブジェクトをデータベースから取得せずに簡単にアクセスできるかどうかを確認するストラテジーを定義します。その他のオプションは以下のとおりです。
-
ObjectLookupMethod.PERSISTENCE_CONTEXTは、一致する多くのエンティティーが永続コンテキストにすでにロードされている場合に使用されます (Session または EntityManager にロードされている場合)。 -
ObjectLookupMethod.SECOND_LEVEL_CACHEは永続コンテキストをチェックし、2 次キャッシュを確認します。
2 次キャッシュで検索するには、以下を設定します。
- 2 次キャッシュを正しく設定およびアクティブ化します。
- 関連するエンティティーの 2 次キャッシュを有効にします。これは、@Cacheable などのアノテーションを使用してを行います。
-
Session、EntityManager、または Query のいずれかの 2 次キャッシュ読み取りアクセスを有効にします。Hibernate ネイティブ API では
CacheMode.NORMALを使用し、Jakarta Persistence ではCacheRetrieveMode.USEを使用します。
2 次キャッシュ実装が Infinispan でない場合、ObjectLookupMethod.SECOND_LEVEL_CACHE は使用しないでください。他の 2 次レベルのキャッシュプロバイダーはこのオペレーションを効率的に実装しません。
DatabaseRetrievalMethod を使用して、以下のようにデータベースからオブジェクトを読み込む方法をカスタマイズします。
- QUERY (デフォルト) はクエリーのセットを使用して、複数のオブジェクトを各バッチに読み込みます。このアプローチが推奨されます。
-
find_BY_ID は
Session.getまたはEntityManager.findセマンティックを使用して一度にオブジェクトをロードします。これは、Hibernate Core がエンティティーをバッチでロードできるようにする、エンティティーにバッチサイズが設定されている場合に推奨されます。
7.5.1.10.7. クエリー時間の制限 リンクのコピーリンクがクリップボードにコピーされました!
Hibernate Guide で、以下のようにクエリーにかかる時間を制限します。
- 制限を指定して受信する際に例外を発生させます。
- 時間制限が発生したときに取得する結果の数に制限します。
7.5.1.10.8. 時間制限の例外発生 リンクのコピーリンクがクリップボードにコピーされました!
クエリー使用する時間が定義した時間を超える場合は、QueryTimeoutException が発生します (プログラム API に応じた、org.hibernate.QueryTimeoutException または javax.persistence.QueryTimeoutException)。
ネイティブの Hibernate API を使用する際に制限を定義するには、以下のいずれかの方法を使用します。
例: クエリー実行でのタイムアウトの定義
Query luceneQuery = ...;
FullTextQuery query = fullTextSession.createFullTextQuery(luceneQuery, User.class);
//define the timeout in seconds
query.setTimeout(5);
//alternatively, define the timeout in any given time unit
query.setTimeout(450, TimeUnit.MILLISECONDS);
try {
query.list();
}
catch (org.hibernate.QueryTimeoutException e) {
//do something, too slow
}
getResultSize()、iterate()、および scroll() は、メソッド呼び出しの終了までタイムアウトを受け入れます。その結果、Iterable または ScrollableResults は、タイムアウトを無視します。また、explain() はこのタイムアウト期間を受け入れません。この方法は、デバッグに使用され、クエリーのパフォーマンス低下の原因をチェックします。
以下は、Jakarta Persistence を使用した実行時間を制限する標準的な方法です。
例: クエリー実行でのタイムアウトの定義
Query luceneQuery = ...;
FullTextQuery query = fullTextEM.createFullTextQuery(luceneQuery, User.class);
//define the timeout in milliseconds
query.setHint( "javax.persistence.query.timeout", 450 );
try {
query.getResultList();
}
catch (javax.persistence.QueryTimeoutException e) {
//do something, too slow
}
サンプルコードは、クエリーが指定の結果量で停止することを保証しません。
7.5.2. 結果の取得 リンクのコピーリンクがクリップボードにコピーされました!
Hibernate クエリーの構築後、HQL または Criteria クエリーと同じように実行されます。同じ準仮想化とオブジェクトセマンティックが Lucene クエリーに適用され、list()、uniqueResult()、iterate()、scroll() などの一般的な操作を使用できます。
7.5.2.1. パフォーマンスに関する考慮事項 リンクのコピーリンクがクリップボードにコピーされました!
妥当な数の結果 (たとえば、ページネーションの使用) が想定され、それらすべてで動作することが予想される場合は、list() または uniqueResult() が推奨されます。list() は、エンティティー batch-size が正しく設定されている場合に最適に機能します。list()、uniqueResult()、iterate()を使用する場合は、Hibernate Search が Lucene Hits 要素 (ページネーション内) をすべて処理する必要があることに注意してください。
Lucene ドキュメントの負荷を最小限に抑える必要がある場合には、scroll() の方が適しています。完了したら、Lucene リソースを保持するため、Scrollable Mission オブジェクトを閉じることを忘れないでください。スクロールを使用することが予測されても、オブジェクトを一括して読み込む必要がある場合は、query.setFetchSize() を使用できます。オブジェクトにアクセスし、読み込まれていない場合、Hibernate Search は次の fetchSize オブジェクトをパスに読み込みます。
ページネーションが、スクロールよりも好まれます。
7.5.2.2. 結果サイズ リンクのコピーリンクがクリップボードにコピーされました!
一致するドキュメントの合計数を把握しておくと役に立つ場合があります。
- Google 検索で提供された、全体的な検索結果機能を提供たとえば、"約 888,000,000 件のうちの 1-10」のようになります。
- 高速なページネーションナビゲーションを実装する
- クエリーがゼロを返すか、十分な結果がない場合に概算を追加する複数ステップの検索エンジンを実装するには、以下を実行します。
当然ながら、一致するドキュメントをすべて取得することはできません。Hibernate Search を使用すると、ページネーションパラメーターに関係なく、一致するドキュメントの合計数を取得できます。さらに注意深く、単一のオブジェクト負荷をトリガーせずに一致する要素の数を取得できます。
例: クエリーの結果サイズの決定
org.hibernate.search.FullTextQuery query =
s.createFullTextQuery( luceneQuery, Book.class );
//return the number of matching books without loading a single one
assert 3245 == ;
org.hibernate.search.FullTextQuery query =
s.createFullTextQuery( luceneQuery, Book.class );
query.setMaxResult(10);
List results = query.list();
//return the total number of matching books regardless of pagination
assert 3245 == ;
Google と同様に、インデックスがデータベースと完全に更新されていない場合は、結果の数は概算されます (例: 非同期クラスター)。
7.5.2.3. ResultTransformer リンクのコピーリンクがクリップボードにコピーされました!
プロジェクション結果はオブジェクト配列として返されます。オブジェクトに使用されるデータ構造がアプリケーションの要件と一致しない場合は、ResultTransformer を適用します。ResultTransformer は、クエリーの実行後に必要なデータ構造を構築します。
例: プロジェクトでの ResultTransformer の使用
org.hibernate.search.FullTextQuery query =
s.createFullTextQuery( luceneQuery, Book.class );
query.setProjection( "title", "mainAuthor.name" );
query.setResultTransformer( new StaticAliasToBeanResultTransformer( BookView.class, "title", "author" ) );
List<BookView> results = (List<BookView>) query.list();
for(BookView view : results) {
log.info( "Book: " + view.getTitle() + ", " + view.getAuthor() );
}
ResultTransformer 実装の例は、ibernate Core codebase を参照してください。
7.5.2.4. 結果について リンクのコピーリンクがクリップボードにコピーされました!
クエリーの結果が適切でない場合、Luke ツールは結果を理解する際に役立ちます。ただし、Hibernate Search を使用すると、所定の結果 (特定のクエリー内) の Lucene Explanation オブジェクトにアクセスできます。このクラスは Lucene ユーザーに非常に高度なものとみなされますが、オブジェクトの性質をよく理解することができます。特定の結果について Explanation オブジェクトにアクセスするには、以下のいずれかの方法があります。
-
fullTextQuery.explain(int)メソッドを使用します。 - プロジェクションの使用
最初の方法は、ドキュメント ID をパラメーターとして取り、Explanation オブジェクトを返します。ドキュメント ID は、インジェクトと FullTextQuery.DOCUMENT_ID 定数を使用して取得できます。
ドキュメント ID はエンティティー ID に関連しません。これらの概念を混同しないように注意してください。
次の方法では、FullTextQuery.EXPLANATION 定数を使用して Explanation オブジェクトをプロジェクトします。
例: プロジェクトを使用した Lucene の説明オブジェクトの取得
FullTextQuery ftQuery = s.createFullTextQuery( luceneQuery, Dvd.class )
.setProjection(
FullTextQuery.DOCUMENT_ID,
,
FullTextQuery.THIS );
@SuppressWarnings("unchecked") List<Object[]> results = ftQuery.list();
for (Object[] result : results) {
Explanation e = (Explanation) result[1];
display( e.toString() );
}
Explanation オブジェクトは、Lucene クエリーを再度実行するときのように、必要とされる場合にのみ使用してください。
7.5.2.5. フィルター リンクのコピーリンクがクリップボードにコピーされました!
Apache Lucene には、カスタムフィルタープロセスに応じてクエリーの結果をフィルタリングできる強力な機能があります。これは、特にフィルターをキャッシュして再利用できるため、追加のデータ制限を適用する非常に強力な方法です。ユースケースには以下が含まれます。
- セキュリティー
- 一時的なデータ (例: 先月のデータのみ表示)
- 予測フィルター (例: 検索は所定カテゴリーに限定される)
Hibernate Search は、透過的にキャッシュされるパラメーター可能な名前付きフィルターの概念を導入することで、この概念をさらにプッシュします。Hibernate Core フィルターの概念を熟知しているユーザーにとって、API は非常に似ています。
例: クエリーのフルテキストフィルターの有効化
fullTextQuery = s.createFullTextQuery( query, Driver.class );
fullTextQuery.enableFullTextFilter("bestDriver");
fullTextQuery.enableFullTextFilter("security").setParameter( "login", "andre" );
fullTextQuery.list(); //returns only best drivers where andre has credentials
この例では、クエリーの上に複数のフィルターを有効化しています。フィルターはいくつでも有効または無効にできます。
宣言フィルターは @FullTextFilterDef アノテーションを使用して実行されます。このアノテーションは、フィルターが後に適用されるクエリーに関係なく、@Indexed エンティティーに設定できます。これは、フィルター定義がグローバルであり、名前が一意である必要があることを意味します。同じ名前を持つ @FullTextFilterDef アノテーションが定義される場合は、SearchException が発生します。名前付きの各フィルターは、実際のフィルター実装を指定する必要があります。
例: フィルターの定義および実装
@FullTextFilterDefs( {
@FullTextFilterDef(name = "bestDriver", impl = BestDriversFilter.class),
@FullTextFilterDef(name = "security", impl = SecurityFilterFactory.class)
})
public class Driver { ... }
public class BestDriversFilter extends org.apache.lucene.search.Filter {
public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
OpenBitSet bitSet = new OpenBitSet( reader.maxDoc() );
TermDocs termDocs = reader.termDocs( new Term( "score", "5" ) );
while ( termDocs.next() ) {
bitSet.set( termDocs.doc() );
}
return bitSet;
}
}
BestDriversFilter は簡単な Lucene フィルターの例です。これにより、スコアが 5 のドライバーに設定された結果が減少します。この例では、指定したフィルターは org.apache.lucene.search.Filter を直接実装し、no-arg コンストラクターが含まれます。
フィルターの作成に追加のステップが必要な場合や、使用するフィルターに no-arg コンストラクターがない場合は、ファクトリーパターンを使用できます。
例: ファクトリーパターンを使用したフィルターの作成
@FullTextFilterDef(name = "bestDriver", impl = BestDriversFilterFactory.class)
public class Driver { ... }
public class BestDriversFilterFactory {
@Factory
public Filter getFilter() {
//some additional steps to cache the filter results per IndexReader
Filter bestDriversFilter = new BestDriversFilter();
return new CachingWrapperFilter(bestDriversFilter);
}
}
Hibernate Search は @Factory アノテーションが付けられたメソッドを検索し、そのメソッドを使用してフィルターインスタンスを作成します。ファクトリーには no-arg コンストラクターが必要です。
Infinispan Query は @Factory アノテーションが付けられたメソッドを使用してフィルターインスタンスを構築します。ファクトリーには引数コンストラクターは使えません。
名前付きフィルターを使用すると、パラメーターをフィルターに渡すことができます。たとえば、セキュリティーフィルターを適用すると、適用するセキュリティーレベルが分かります。
例: 定義されたフィルターにパラメーターを渡す
fullTextQuery = s.createFullTextQuery( query, Driver.class );
fullTextQuery.enableFullTextFilter("security").setParameter( "level", 5 );
各パラメーター名は、フィルターや、ターゲットとなる名前付きフィルター定義のフィルターまたはフィルターファクトリーのいずれかに関連するセッターを持つ必要があります。
例: フィルター実装におけるパラメーターの使用
public class SecurityFilterFactory {
private Integer level;
/**
* injected parameter
*/
public void setLevel(Integer level) {
this.level = level;
}
@Key public FilterKey getKey() {
StandardFilterKey key = new StandardFilterKey();
key.addParameter( level );
return key;
}
@Factory
public Filter getFilter() {
Query query = new TermQuery( new Term("level", level.toString() ) );
return new CachingWrapperFilter( new QueryWrapperFilter(query) );
}
}
@Key アノテーションが付けられたメソッドは FilterKey オブジェクトを返すことに注意してください。返されたオブジェクトには特別なコントラクトを持ちます。キーオブジェクトは equals() / hashCode() を実装し、指定される Filter タイプが同一で、パラメーターのセットが同じである場合にのみ、両方の鍵が同じになるようにする必要があります。つまり、鍵の生成元となるフィルターが交換可能な場合、あるいは交換可能である場合にのみ、両方のフィルター鍵が同等になります。キーオブジェクトは、キャッシュメカニズムのキーとして使用されます。
@Key メソッドは以下の場合にのみ必要です。
- フィルターキャッシングシステムが有効になっている (デフォルトでは有効)。
- フィルターにはパラメーターがあります。
多くの場合、StandardFilterKey 実装を使用すれば十分です。これは、equals() / hashCode() 実装をそれぞれのパラメーター equals および hashcode メソッドに委譲します。
定義されたフィルターがデフォルトのキャッシュされ、キャッシュは、必要に応じてハード参照とソフト参照の組み合わせを使用してメモリーの破損を可能にします。ハード参照キャッシュは、最近使用されたフィルターを追跡し、必要に応じて SoftReferences に最も使用されるフィルターを変換します。ハード参照キャッシュの制限に達すると、追加のフィルターが SoftReferences としてキャッシュされます。ハード参照キャッシュのサイズを調整するには、hibernate.search.filter.cache_strategy.size (デフォルトは 128 に設定) を使用します。フィルターキャッシングの高度な使用のために、独自の FilterCachingStrategy を実装します。classname は hibernate.search.filter.cache_strategy によって定義されます。
このフィルターキャッシュメカニズムは、実際のフィルター結果をキャッシュするのと混同しないようにしてください。Lucene では、CachingWrapperFilter を中心に IndexReader を使用してフィルターをラッピングすることが一般的です。ラッパーは、getDocIdSet(IndexReader reader) メソッドから返される DocIdSet をキャッシュして、高価な再構築を防ぎます。リーダーは、開いた時点のインデックスの状態を効果的に表示するため、計算した DocIdSet が同じ IndexReader インスタンスに対してのみキャッシュ可能であることを示すことが重要です。ドキュメントリストは、開いている IndexReader 内では変更できません。ただし、別の、新しい IndexReader インスタンスの場合は、(別のインデックスから、または単にインデックスが変更されたため) 異なるファイルのセットで動作する可能性があるため、キャッシュされた DocIdSet を再計算する必要があります。
また、Hibernate Search はキャッシングのこの側面にも役立ちます。@FullTextFilterDef の cache フラグは、デフォルトでは FilterCacheModeType.INSTANCE_AND_DOCIDSETRESULTS に設定されています。これにより、フィルターインスタンスを自動的にキャッシュし、CachingWrapperFilter の Hibernate 固有の実装に指定されたフィルターをラップします。このクラスの Lucene のバージョンとは対照的に、SoftReferences はハード参照数とともに使用されます (フィルターキャッシュについて参照)。ハード参照数は、hibernate.search.filter.cache_docidresults.size (デフォルトは 5 に設定) を使用して調整できます。ラッピング動作は、the@FullTextFilterDef.cache パラメーターを使用して制御できます。このパラメーターには、以下の異なる値があります。
| 値 | 定義 |
|---|---|
| FilterCacheModeType.NONE | フィルターインスタンスがなく、Hibernate Search によって結果がキャッシュされません。フィルター呼び出しごとに、新しいフィルターインスタンスが作成されます。この設定は、データセットまたはメモリーが制限される環境を迅速に変更する際に役に立つことがあります。 |
| FilterCacheModeType.INSTANCE_ONLY | フィルターインスタンスは、同時に Filter.getDocIdSet() 呼び出しでキャッシュされ、再利用されます。DocIdSet の結果はキャッシュされません。この設定は、フィルターが固有のキャッシングメカニズムを使用するか、アプリケーション固有のイベントにより DocIdSet をキャッシュする必要がない場合に動的にフィルターの結果が変更される場合に役立ちます。 |
| FilterCacheModeType.INSTANCE_AND_DOCIDSETRESULTS | フィルターインスタンスと DocIdSet 両方の結果がキャッシュされます。これがデフォルト値になります。 |
フィルターは以下の状況でキャッシュする必要があります。
- システムはターゲットエンティティーインデックスを頻繁に更新しません (つまり、IndexReader は頻繁に再利用されます)。
- Filter の DocIdSet の計算には、クエリーの実行にかかった時間と比較して負荷がかかります。
7.5.2.6. シャード化された環境でのフィルターの使用 リンクのコピーリンクがクリップボードにコピーされました!
シャード化された環境では、利用可能なシャードのサブセットでクエリーを実行することができます。これを行う方法は 2 つあります。
インデックスシャードのサブセットのクエリー
- フィルター設定に応じて IndexManager のサブセットを選択するシャード化ストラテジーを作成します。
- クエリー時にフィルターをアクティブにします。
例: インデックスシャードのサブセットのクエリー
この例では、customer フィルターがアクティブな場合は、クエリーが特定の顧客シャードに対して実行されます。
public class CustomerShardingStrategy implements IndexShardingStrategy {
// stored IndexManagers in an array indexed by customerID
private IndexManager[] indexManagers;
public void initialize(Properties properties, IndexManager[] indexManagers) {
this.indexManagers = indexManagers;
}
public IndexManager[] getIndexManagersForAllShards() {
return indexManagers;
}
public IndexManager getIndexManagerForAddition(
Class<?> entity, Serializable id, String idInString, Document document) {
Integer customerID = Integer.parseInt(document.getFieldable("customerID").stringValue());
return indexManagers[customerID];
}
public IndexManager[] getIndexManagersForDeletion(
Class<?> entity, Serializable id, String idInString) {
return getIndexManagersForAllShards();
}
/**
* Optimization; don't search ALL shards and union the results; in this case, we
* can be certain that all the data for a particular customer Filter is in a single
* shard; simply return that shard by customerID.
*/
public IndexManager[] getIndexManagersForQuery(
FullTextFilterImplementor[] filters) {
FullTextFilter filter = getCustomerFilter(filters, "customer");
if (filter == null) {
return getIndexManagersForAllShards();
}
else {
return new IndexManager[] { indexManagers[Integer.parseInt(
filter.getParameter("customerID").toString())] };
}
}
private FullTextFilter getCustomerFilter(FullTextFilterImplementor[] filters, String name) {
for (FullTextFilterImplementor filter: filters) {
if (filter.getName().equals(name)) return filter;
}
return null;
}
}
この例では、custom という名前のフィルターがある場合、この顧客専用のシャードのみがクエリーされ、それ以外の場合はすべてのシャードが返されます。所定のシャード化ストラテジーは、単一または複数のフィルターに対応し、それらのパラメーターに依存します。
次のステップでは、クエリー時にフィルターをアクティブにします。フィルターは、クエリーの後に Lucene 結果をフィルターする (定義されている) 通常のフィルターですが、シャード化ストラテジーにのみ渡される特殊フィルターを使用することができます (その他は無視されます)。
この機能を使用するには、フィルターの宣言時に ShardSensitiveOnlyFilter クラスを指定します。
@Indexed
@FullTextFilterDef(name="customer", impl=ShardSensitiveOnlyFilter.class)
public class Customer {
...
}
FullTextQuery query = ftEm.createFullTextQuery(luceneQuery, Customer.class);
query.enableFulltextFilter("customer").setParameter("CustomerID", 5);
@SuppressWarnings("unchecked")
List<Customer> results = query.getResultList();
ShardSensitiveOnlyFilter を使用して Lucene フィルターを実装する必要はありません。シャード化された環境のクエリーを加速するには、これらのフィルターに対応するフィルターおよびシャード化ストラテジーを使用することが推奨されます。
7.5.3. ファセット リンクのコピーリンクがクリップボードにコピーされました!
ファセット検索は、クエリーの結果を複数のカテゴリーに分割できる技術です。この分類には、各カテゴリーのヒット数の計算や、これらのファイン (カテゴリー) に基づいて検索結果をさらに制限する機能が含まれます。以下の例は、ファセットの例を示しています。ページのメイン部分に表示される検索結果が表示されます。ただし、左側のナビゲーションバーには、Programming、Computer Science、Databases、Software、Web Development、Networking、Home Computing のサブカテゴリーを持つ Computers & Internet カテゴリーが表示されています。各サブカテゴリーについて、主要な検索条件に合致し、それぞれのサブカテゴリーに属する Book の数が表示されます。Computers & Internet のカテゴリーの区分は、特定の検索ファセットです。その他には、平均的なカスタマーレビューなどが挙げられます。
ファセット検索は、クエリーの結果をカテゴリーに分割します。この分類は、各カテゴリーのヒット数の計算を含み、これらのファセット (カテゴリー) に基づいて検索結果をさらに制限します。以下の例では、ファセット検索結果がメインページに表示されます。
左側のナビゲーションバーには、カテゴリーとサブカテゴリーが表示されます。各サブカテゴリーについて、Book の数は主要な検索条件と一致し、それぞれのサブカテゴリーに属します。この Computers & Internet カテゴリーの区分は、特定の検索ファセットです。もう 1 つの例は、平均的なカスタマーレビューです。
例: Amazon での Hibernate Search の検索
Hibernate Search の QueryBuilder クラスおよび FullTextQuery クラスは、ファセット API へのエントリーポイントです。前者は要求を作成し、後者は FacetManager にアクセスします。FacetManager はクエリーにファセット要求を適用し、検索結果を絞り込むために既存のクエリーに追加されるブックマークを選択します。この例では、以下の例のように Cd エンティティーを使用します。
例: エンティティー Cd
@Indexed
public class Cd {
private int id;
@Fields( {
@Field,
@Field(name = "name_un_analyzed", analyze = Analyze.NO)
})
private String name;
@Field(analyze = Analyze.NO)
@NumericField
private int price;
Field(analyze = Analyze.NO)
@DateBridge(resolution = Resolution.YEAR)
private Date releaseYear;
@Field(analyze = Analyze.NO)
private String label;
// setter/getter
...
Hibernate Search 5.2 よりも前のバージョンでは、@Facet アノテーションを明示的に使用する必要がなくなりました。Hibernate Search 5.2 では、Lucene のネイティブファセッティング API を使用するために必要になりました。
7.5.3.1. ファセット要求の作成 リンクのコピーリンクがクリップボードにコピーされました!
ファセット検索に対する最初のステップは、FacetingRequest を作成することです。現時点では、2 種類のセッティング要求がサポートされています。最初のタイプは discrete faceting と呼ばれ、次のタイプは range faceting 要求と呼ばれます。個別のファセットリクエストの場合は、ファセット (分類) を行うインデックスフィールドと、適用するファセットオプションを指定します。個別のファセッティング要求の例は、以下の例で確認できます。
例: 個別のファセット要求の作成
QueryBuilder builder = fullTextSession.getSearchFactory()
.buildQueryBuilder()
.forEntity( Cd.class )
.get();
FacetingRequest labelFacetingRequest = builder.facet()
.name( "labelFaceting" )
.onField( "label")
.discrete()
.orderedBy( FacetSortOrder.COUNT_DESC )
.includeZeroCounts( false )
.maxFacetCount( 1 )
.createFacetingRequest();
このファセットリクエストを実行すると、インデックス設定されたlabel フィールドの個別値ごとに Facet インスタンスが作成されます。Facet インスタンスは、元のクエリー結果内でこの特定のフィールドの値が発生する頻度を含む実際のフィールド値を記録します。orderdBy、includeZeroCounts および maxFacetCount は任意のオプションのパラメーターで、すべてのファセット要求に適用できます。ordersBy では、作成されたブックマークが返される順序を指定できます。デフォルトは FacetSortOrder.COUNT_DESC ですが、フィールドの値または範囲の指定順序でソートすることもできます。includeZeroCount は、結果内に含まれるブックマーク数 (デフォルトでは 0) を判断し、maxFacetCount により、返されるワイルドカードの最大数を制限できます。
現時点では、ファセッティングを適用するためにインデックス化されたフィールドを満たす必要のあるいくつかの前提条件があります。インデックス付きプロパティーは String、Date、または Number および null の値を持つものは使用しないでください。さらに、プロパティーは Analyze.NO でインデックス化する必要があり、数値プロパティー @NumericField を指定する場合は指定する必要があります。
一定の範囲のファセットリクエストの作成は、ブックマークするフィールド値の範囲を指定する必要がある点以外は非常に似ています。一定の範囲のファセットリクエストは、複数の異なるレート範囲が指定されている場合に以下で確認できます。below および above は 1 度のみ指定できますが、from - to は必要なだけ指定できます。各範囲の境界は、excludeLimit で、範囲に含まれるかどうかを指定することもできます。
例: 範囲ファセット要求の作成
QueryBuilder builder = fullTextSession.getSearchFactory()
.buildQueryBuilder()
.forEntity( Cd.class )
.get();
FacetingRequest priceFacetingRequest = builder.facet()
.name( "priceFaceting" )
.onField( "price" )
.range()
.below( 1000 )
.from( 1001 ).to( 1500 )
.above( 1500 ).excludeLimit()
.createFacetingRequest();
7.5.3.2. ファセット要求の適用 リンクのコピーリンクがクリップボードにコピーされました!
ファセット要求は、FullTextQuery クラスを介して取得できる FacetManager クラスでクエリーに適用されます。
ファセットリクエストはいくつでも有効にでき、ファセット要求名を指定して getFacets() で後から取得できます。また、名前を指定してファセット要求を無効にできる disableFaceting() メソッドもあります。
ファセット要求は、FullTextQuery から取得できる FacetManager を使用してクエリーに適用できます。
例: ファセット要求の適用
// create a fulltext query
Query luceneQuery = builder.all().createQuery(); // match all query
FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery( luceneQuery, Cd.class );
// retrieve facet manager and apply faceting request
FacetManager facetManager = fullTextQuery.getFacetManager();
facetManager.enableFaceting( priceFacetingRequest );
// get the list of Cds
List<Cd> cds = fullTextQuery.list();
...
// retrieve the faceting results
List<Facet> facets = facetManager.getFacets( "priceFaceting" );
...
getFacets() を使用して、ファセットリクエスト名を指定することで、複数のファセット名を取得できます。
disableFaceting() メソッドは、名前を指定してブックマーク要求を無効にします。
7.5.3.3. クエリー結果の制限 リンクのコピーリンクがクリップボードにコピーされました!
最後でも重要ですが、「ドルダウン」機能を実装するために、元のクエリーに追加の基準として返されたすべての Facets を適用できます。そのためには、FacetSelection を使用できます。FacetSelections は FacetManager 経由で利用可能で、クエリー基準としてファセットを選択できます。(selectFacets)、ファセットの制限を削除して、すべてのファセット制限 (deselectFacets) を削除し、現在選択しているすべてのファセット (getSelectedFacets) を取得します。以下のスニペットは例になります。
// create a fulltext query
Query luceneQuery = builder.all().createQuery(); // match all query
FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery( luceneQuery, clazz );
// retrieve facet manager and apply faceting request
FacetManager facetManager = fullTextQuery.getFacetManager();
facetManager.enableFaceting( priceFacetingRequest );
// get the list of Cd
List<Cd> cds = fullTextQuery.list();
assertTrue(cds.size() == 10);
// retrieve the faceting results
List<Facet> facets = facetManager.getFacets( "priceFaceting" );
assertTrue(facets.get(0).getCount() == 2)
// apply first facet as additional search criteria
facetManager.getFacetGroup( "priceFaceting" ).selectFacets( facets.get( 0 ) );
// re-execute the query
cds = fullTextQuery.list();
assertTrue(cds.size() == 2);
7.5.4. クエリープロセスの最適化 リンクのコピーリンクがクリップボードにコピーされました!
クエリーのパフォーマンスは、以下の基準に依存します。
- Lucene クエリー。
- 読み込まれたオブジェクト数: ページネーション (常時) またはインデックス処理 (必要な場合) を使用します。
- Hibernate Search が Lucene リーダーと対話する方法: 適切なリーダーストラテジーを定義します。
- インデックスから頻繁に抽出された値をキャッシュします。詳細は、Caching Index Values: FieldCacheを参照してください。
7.5.4.1. インデックス値のキャッシュ: FieldCache リンクのコピーリンクがクリップボードにコピーされました!
Lucene インデックスの主な機能は、クエリーへの一致を識別することです。クエリーが実行された後に、結果が分析され、有用な情報が抽出される必要があります。通常、Hibernate Search はクラスタイプとプライマリーキーを抽出する必要があります。
インデックスから必要な値を抽出するには、パフォーマンス負荷がかかります。パフォーマンス負荷が極めて低く、気付かない場合もありますが、キャッシュを行うのに役立つ場合もあります。
この要件は、使用されている Projections によって異なります。これは、クラスタイプがクエリーコンテキストまたは他の方法で推測される可能性があるため、クラスタイプが不要なためです。
@CacheFromIndex アノテーションを使用すると、Hibernate Search に必要なメインのメタデータフィールドのキャッシュをさまざまな方法で試すことができます。
import static org.hibernate.search.annotations.FieldCacheType.CLASS;
import static org.hibernate.search.annotations.FieldCacheType.ID;
@Indexed
@CacheFromIndex( { CLASS, ID } )
public class Essay {
...
このアノテーションを使用してクラスタイプと ID をキャッシュできます。
CLASS: Hibernate Search は Lucene FieldCache を使用して、インデックスからクラスタイプの抽出のパフォーマンスを改善します。この値はデフォルトで有効になっており、@CacheFromIndex アノテーションを指定しない場合に Hibernate Search が適用されます。
-
ID: プライマリー識別子はキャッシュを使用します。これにより、パフォーマンスが最も高いクエリーが提供されますが、消費するメモリーが多くなり、パフォーマンスが低下する可能性があります。
ウォームアップ後のパフォーマンスおよびメモリー消費の影響を測定します (一部のクエリーの実行)。パフォーマンスは、フィールドキャッシュを有効にすることによって改善される可能性がありますが、常に改善されるわけではありません。
FieldCache の使用は、以下の点を考慮してください。
- メモリー使用量: このキャッシュには、かなりメモリーがハングします。通常、CLASS キャッシュの要件は ID キャッシュの要件よりも低くなります。
- インデックスウォームアップ: フィールドキャッシュを使用する場合、新しいインデックスまたはセグメントの最初のクエリーは、キャッシュが有効になっていない場合よりも遅くなります。
一部のクエリーでは、クラスタイプは全く不要です。その場合、CLASS フィールドキャッシュを有効にしても使用されない可能性があります。たとえば、単一クラスをターゲットとしている場合は、返される値はすべてそのタイプのものになります (これは各クエリー実行時に評価されます)。
ID FieldCache を使用するには、ターゲットエンティティーの ID が TwoWayFieldBridge (すべてのブリッジの構築として) を使用し、特定のクエリーに読み込まれるすべてのタイプが id のフィールド名を使用し、同じタイプの ID が割り当てられている必要があります (これは各クエリー実行時に評価されます)。
7.6. 手動によるインデックスの変更 リンクのコピーリンクがクリップボードにコピーされました!
Hibernate Core が変更をデータベースに適用すると、Hibernate Search はこれらの変更を検出し、インデックスを自動的に更新します (eventListener が無効でない場合)。バックアップが復元したり、データが影響を受ける場合のように、Hibernate を使用せずにデータベースに変更が加えられることがあります。このような場合、Hibernate Search は Manual Index API を公開し、インデックスから単一のエンティティーを明示的に更新または削除したり、データベース全体のインデックスを再構築したり、特定のタイプへのすべての参照を削除したりします。
これらのメソッドはすべて Lucene Index のみに影響し、変更は適用されません。
7.6.1. インデックスへのインスタンスの追加 リンクのコピーリンクがクリップボードにコピーされました!
FullTextSession.index(T エンティティー) を使用すると、特定のオブジェクトインスタンスを直接インデックスに追加または更新できます。このエンティティーがすでにインデックス化されている場合は、インデックスが更新されます。インデックスへの変更は、トランザクションコミット時にのみ適用されます。
FullTextSession.index(T エンティティー) を使用してオブジェクトまたはインスタンスを直接インデックスに追加します。このインデックスは、エンティティーがインデックス化されると更新されます。Infinispan Query は、トランザクションのコミット中に変更をインデックスに適用します。
例: FullTextSession.index(T エンティティー) を使用したエンティティーのインデックス作成
FullTextSession fullTextSession = Search.getFullTextSession(session);
Transaction tx = fullTextSession.beginTransaction();
Object customer = fullTextSession.load( Customer.class, 8 );
fullTextSession.index(customer);
tx.commit(); //index only updated at commit time
1 つのタイプのすべてのインスタンスを追加する場合や、すべてのインデックスタイプのインスタンスを追加する場合には、MassIndexer を使用することが推奨されます。詳細はを参照してください。
MassIndexer を使用して、タイプ (またはすべてのインデックス型) のすべてのインスタンスを追加します。詳細は、Using a MassIndexer を参照してください。
7.6.2. インデックスからのインスタンスの削除 リンクのコピーリンクがクリップボードにコピーされました!
データベースからエンティティーまたは特定タイプのすべてのエンティティーを物理的に削除しなくても、特定のタイプの単一のエンティティーまたはすべてのエンティティーを Lucene インデックスから削除できます。この操作はパージと呼ばれ、FullTextSession を介しても実行されます。
パージ操作では、データベースから物理的に削除せずに、単一つのエンティティーまたは特定タイプのすべてのエンティティーを Lucene インデックスから削除できます。この操作は FullTextSession を使用して実行されます。
例: インデックスからエンティティーの特定のインスタンスを削除
FullTextSession fullTextSession = Search.getFullTextSession(session);
Transaction tx = fullTextSession.beginTransaction();
for (Customer customer : customers) {
fullTextSession.purgeAll( Customer.class );
//optionally optimize the index
//fullTextSession.getSearchFactory().optimize( Customer.class );
tx.commit(); //index is updated at commit time
このような操作の後にインデックスを最適化することが推奨されます。
メソッド index、purge、および purgeAll は FullTextEntityManager でも使用できます。
すべての手動のインデックスメソッド (index、urge、および purgeAll) は、データベースではなくインデックスにのみ影響し、トランザクションであっても、トランザクションが正常にコミットされるまで適用されません。または flushToIndexes を利用します。
7.6.3. インデックスの再構築 リンクのコピーリンクがクリップボードにコピーされました!
エンティティーマッピングをインデックスに変更する場合は、インデックス全体を更新する必要があります。たとえば、別のアナライザーを使用して既存のフィールドにインデックスを付ける場合は、影響を受けるタイプのインデックスを再構築する必要があります。また、データベースが置き換えられた場合 (バックアップから復元した場合や、レガシーシステムからインポートした場合など) は、既存データからインデックスを再構築できます。Hibernate Search は以下から選択する主要なストラテジーを提供します。
インデクサーのエンティティーマッピングを変更する場合は、インデックス全体を更新する必要があります。たとえば、既存のフィールドを異なるアナライザーを使用してインデックス化する場合、インデックスは影響を受けるタイプに対して再構築する必要があります。
さらに、バックアップから復元するか、レガシーシステムからインポートすることによりデータベースが置き換えられる場合、インデックスは既存データから再構築する必要があります。Infinispan Query は、以下の主要なストラテジーを提供します。
-
FullTextSession.index()をすべてのエンティティーで使用しながら、FullTextSession.flushToIndexes()を定期的に使用。 -
MassIndexerを使用。
7.6.3.1. FlushToIndexes() の使用 リンクのコピーリンクがクリップボードにコピーされました!
このストラテジーは、既存のインデックスを削除してから、FullTextSession.purgeAll() および FullTextSession.index() を使用してすべてのエンティティーをインデックスに戻すことで構成されますが、メモリーと効率の制約があります。効率を最大限に高めるためにも、Hibernate Search はインデックス操作をバッチ処理し、コミット時に実行します。大量のデータをインデックス化する場合は、トランザクションがコミットされるまですべてのドキュメントがキューに保存されるため、メモリー消費について注意する必要があります。キューを定期的に空にしない場合は、OutOfMemoryException が発生する可能性があります。これには fullTextSession.flushToIndexes() を使用します。FullTextSession.flushToIndexes() が呼び出されるたびに (またはトランザクションがコミットされると)、バッチキューが処理され、すべてのインデックスの変更が適用されます。フラッシュ後は、変更をロールバックできないことに注意してください。
例: index() および flushToIndexes() を使用したインデックス再構築
fullTextSession.setFlushMode(FlushMode.MANUAL);
fullTextSession.setCacheMode(CacheMode.IGNORE);
transaction = fullTextSession.beginTransaction();
//Scrollable results will avoid loading too many objects in memory
ScrollableResults results = fullTextSession.createCriteria( Email.class )
.setFetchSize(BATCH_SIZE)
.scroll( ScrollMode.FORWARD_ONLY );
int index = 0;
while( results.next() ) {
index++;
fullTextSession.index( results.get(0) ); //index each element
if (index % BATCH_SIZE == 0) {
fullTextSession.flushToIndexes(); //apply changes to indexes
fullTextSession.clear(); //free memory since the queue is processed
}
}
transaction.commit();
この明示的な API が優先され、より優れた制御が提供されるため、hibernate.search.default.worker.batch_size が非推奨となりました。
アプリケーションがメモリーが不足しないように、バッチサイズを使用するようにしてください.大規模なバッチサイズオブジェクトの方がデータベースより高速ですが、より多くのメモリーが必要になります。
7.6.3.2. MassIndexer の使用 リンクのコピーリンクがクリップボードにコピーされました!
Hibernate Search の MassIndexer は、複数の並列スレッドを使用してインデックスを再ビルドします。オプションで、リロードする必要のあるエンティティーを選択するか、またはすべてのエンティティーのインデックスを変更できます。このアプローチは、パフォーマンスを最大化するために最適化されていますが、アプリケーションをメンテナンスモードに設定する必要があります。MassIndexer がビジーな場合、インデックスのクエリーは推奨されません。
例: MassIndexer を使用したインデックスの再構築
fullTextSession.createIndexer().startAndWait();
これにより、インデックスが再構築され、インデックスが削除されてから、データベースからすべてのエンティティーが再読み込みされます。簡単に使用できますが、プロセスのスピードを上げるために調整を行うことが推奨されます。
MassIndexer の進行中は、インデックスの内容未定義になります。MassIndexer が機能している間にクエリーを実行すると、一部の結果が失われる可能性が高くなります。
例: Tuned MassIndexer の使用
fullTextSession
.createIndexer( User.class )
.batchSizeToLoadObjects( 25 )
.cacheMode( CacheMode.NORMAL )
.threadsToLoadObjects( 12 )
.idFetchSize( 150 )
.progressMonitor( monitor ) //a MassIndexerProgressMonitor implementation
.startAndWait();
これにより、すべての User インスタンス (およびフラグ) のインデックスが再構築され、クエリーごとに 25 個のオブジェクトのバッチを使用して User インスタンスをロードするために、12 個の並列スレッドが作成されます。また、これら 12 個のスレッドが Lucene ドキュメントを出力するには、インデックス化された埋め込み関係およびカスタム FieldBridges または ClassBridges を処理する必要もあります。スレッドは、変換プロセス中に追加属性のレイジーローディングをトリガーします。そのため、並行して機能しているスレッドは多く必要になります。実際のインデックス書き込みで稼働しているスレッドの数は、各インデックスのバックエンド設定によって定義されます。
Cachemode を CacheMode.IGNORE (デフォルト) のままにすることが推奨されます。これは、ほとんどの場合でキャッシュが不要な追加オーバーヘッドになるためです。メインのエンティティーがインデックスに含まれる列挙のようなデータに関連する場合は、パフォーマンスを向上させる可能性があるため、データに応じて他の CacheMode を有効にすると便利です。
最適なパフォーマンスを実現するために理想的なスレッド数は、全体的なアーキテクチャー、データベース設計、およびデータ値によって大きく異なります。すべての内部スレッドグループには意味のある名前が付けられているため、スレッドダンプ多くのの診断ツールで簡単に識別できます。
MassIndexer はトランザクションを認識しないため、開始したり、コミットしたりする必要はありません。これはトランザクション処理ではないため、ユーザーが処理中にシステムを使用するようにすることは推奨されません。これは、ユーザーが結果を見つけられず、システム負荷が高すぎる可能性があるためです。
インデックス処理にかかる時間やメモリー消費量に影響を与える他のパラメーターには、以下が含まれます。
-
hibernate.search.[default|<indexname>].exclusive_index_use -
hibernate.search.[default|<indexname>].indexwriter.max_buffered_docs -
hibernate.search.[default|<indexname>].indexwriter.max_merge_docs -
hibernate.search.[default|<indexname>].indexwriter.merge_factor -
hibernate.search.[default|<indexname>].indexwriter.merge_min_size -
hibernate.search.[default|<indexname>].indexwriter.merge_max_size -
hibernate.search.[default|<indexname>].indexwriter.merge_max_optimize_size -
hibernate.search.[default|<indexname>].indexwriter.merge_calibrate_by_deletes -
hibernate.search.[default|<indexname>].indexwriter.ram_buffer_size -
hibernate.search.[default|<indexname>].indexwriter.term_index_interval
以前のバージョンにも max_field_length がありましたが、これは Lucene から削除されました。LimitTokenCountAnalyzer を使用すると、同様の効果を得ることができます。
すべての .indexwriter パラメーターは Lucene 固有で、Hibernate Search はこれらのパラメーターを渡します。
MassIndexer は、前方のみのスクロール可能な結果を使用して、読み込まれるプライマリーキーを繰り返し処理しますが、MySQL の JDBC ドライバーはメモリーのすべての値を読み込みます。この「最適化」を回避するには、idFetchSize を Integer.MIN_VALUE に設定します。
7.7. インデックスの最適化 リンクのコピーリンクがクリップボードにコピーされました!
時折、Lucene インデックスを最適化する必要があります。このプロセスは基本的にはデフラグです。最適化がトリガーされるまで、Lucene は削除されたドキュメントのみをマークします。物理的には適用されません。最適化プロセス中に削除が適用され、Lucene ディレクトリー内のファイル数にも影響を及ぼします。
Lucene インデックスを最適化すると検索が高速になりますが、インデックス化 (更新) パフォーマンスには影響はありません。最適化中に検索を実行できますが、おそらく遅くなる可能性があります。インデックスの更新はすべて停止します。最適化のスケジュール設定が推奨されます。
Lucene インデックスを最適化すると検索が高速になりますが、インデックス更新のパフォーマンスには影響しません。検索は、最適化プロセス中に実行できますが、期待よりも遅くなります。すべてのインデックスの更新は、最適化中に保留されます。そのため、最適化のスケジュール設定を行うことが推奨されます。
- アイドル状態のシステムの場合、または検索が頻繁に行われない場合
- 多数のインデックス変更が適用された後。
MassIndexer は、デフォルトで、開始時と処理終了時にインデックスを最適化します。MassIndexer.optimizeAfterPurge および MassIndexer.optimizeOnFinish を使用して、このデフォルト動作を変更します。詳細は、Using a MassIndexer を参照してください。
7.7.1. 自動最適化 リンクのコピーリンクがクリップボードにコピーされました!
Hibernate Search は、以下のいずれかの後にインデックスを自動的に最適化できます。
Infinispan のクエリーは、以下の後にインデックスを自動的に最適化します。
- 一定量の操作 (挿入または削除)。
- 一定量のトランザクション。
インデックスの自動最適化の設定は、グローバルまたはインデックスごとに定義できます。
例: 自動最適化パラメーターの定義
hibernate.search.default.optimizer.operation_limit.max = 1000
hibernate.search.default.optimizer.transaction_limit.max = 100
hibernate.search.Animal.optimizer.transaction_limit.max = 50
最適化は、以下のいずれかの時点で Animal インデックスに対してトリガーされます。
-
追加および削除の数は
1000に到達します。 -
トランザクション数が
50に達すると、hibernate.search.Animal.optimizer.transaction_limit.maxがhibernate.search.default.optimizer.transaction_limit.maxよりも優先されます。
これらのパラメーターが定義されていないと、最適化は自動的に処理されません。
OptimizerStrategy のデフォルト実装は、org.hibernate.search.store.optimization.OptimizerStrategy を実装して上書きし、optimizer.implementation プロパティーを実装の完全修飾名に設定して上書きできます。この実装は、インターフェースを実装し、パブリッククラスであり、引数なしでパブリックコンストラクターを持つ必要があります。
例: カスタム OptimizerStrategy の読み込み
hibernate.search.default.optimizer.implementation = com.acme.worlddomination.SmartOptimizer
hibernate.search.default.optimizer.SomeOption = CustomConfigurationValue
hibernate.search.humans.optimizer.implementation = default
キーワード default を使用すると、Hibernate Search のデフォルト実装を選択できます。.optimizer キーセパレーター以降のすべてのプロパティーは、起動時に実装の初期化メソッドに渡されます。
7.7.2. 手動の最適化 リンクのコピーリンクがクリップボードにコピーされました!
SearchFactory を使用して、Hibernate Search からプログラムで Lucene インデックスを最適化 (デフラグ) できます。
例: プログラムのインデックスの最適化
FullTextSession fullTextSession = Search.getFullTextSession(regularSession);
SearchFactory searchFactory = fullTextSession.getSearchFactory();
searchFactory.optimize(Order.class);
// or
searchFactory.optimize();
最初の例は、Orders を保持している Lucene インデックスを最適化し、次の例はすべてのインデックスを最適化します。
searchFactory.optimize() は Jakarta Messaging バックエンドには影響しません。マスターノードで最適化操作を適用する必要があります。
SearchFactory.optimize() は Jakarta Message バックエンドに影響を与えないため、マスターノードに適用されます。
7.7.3. 最適化の調整 リンクのコピーリンクがクリップボードにコピーされました!
Apache Lucene には、最適化の実行方法に影響を与えるいくつかのパラメーターがあります。Hibernate Search はこれらのパラメーターを公開します。
その他のインデックス最適化パラメーターには、以下が含まれます。
-
hibernate.search.[default|<indexname>].indexwriter.max_buffered_docs -
hibernate.search.[default|<indexname>].indexwriter.max_merge_docs -
hibernate.search.[default|<indexname>].indexwriter.merge_factor -
hibernate.search.[default|<indexname>].indexwriter.ram_buffer_size -
hibernate.search.[default|<indexname>].indexwriter.term_index_interval
7.8. 高度な機能 リンクのコピーリンクがクリップボードにコピーされました!
7.8.1. SearchFactory へのアクセス リンクのコピーリンクがクリップボードにコピーされました!
SearchFactory オブジェクトは、Hibernate Search の基礎となる Lucene リソースを追跡します。これは、Lucene にネイティブにアクセスする便利な方法です。SearchFactory は FullTextSession からアクセスできます。
例: SearchFactory へのアクセス
FullTextSession fullTextSession = Search.getFullTextSession(regularSession);
SearchFactory searchFactory = fullTextSession.getSearchFactory();
7.8.2. IndexReader の使用 リンクのコピーリンクがクリップボードにコピーされました!
Lucene のクエリーは IndexReader で実行されます。Hibernate Search は、パフォーマンスを最大化するためにインデックスリーダーをキャッシュするか、更新された IndexReader 軽減 I/O 操作を取得するために他の効率的なストラテジーを提供する場合があります。コードはこれらのキャッシュされたリソースにアクセスできますが、要件がいくつかあります。
例: IndexReader のアクセス
IndexReader reader = searchFactory.getIndexReaderAccessor().open(Order.class);
try {
//perform read-only operations on the reader
}
finally {
searchFactory.getIndexReaderAccessor().close(reader);
}
この例では、SearchFactory は (シャード化ストラテジーに関連して) このエンティティーのクエリーに必要なインデックスを判別します。各インデックスで設定された ReaderProvider を使用して、関連するすべてのインデックスの上に複合 IndexReader を返します。この IndexReader は複数のクライアント間で共有されるため、以下のルールに従う必要があります。
- IndexReader.close() を呼び出さず、必要に応じて readerProvider.closeReader(reader) を finally ブロックで使用します。
- 変更操作には、この IndexReader を使用しないでください (読み取り専用の IndexReader であり、このような試行によって例外が発生します)。
これらのルール以外に、特にネイティブな Lucene クエリーを行うため、IndexReader を自由に使用できます。共有 IndexReaders を使用すると、たとえばファイルシステムから直接開くよりも、ほとんどのクエリーがより効率的になります。
open(Class…types) メソッドの代替として、open(String…indexNames) を使用して、1 つ以上のインデックス名で渡すことができます。このストラテジーを使用して、シャードが使用される場合に、インデックスタイプに対してインデックスのサブセットを選択することもできます。
例: インデックス名による IndexReader へのアクセス
IndexReader reader = searchFactory.getIndexReaderAccessor().open("Products.1", "Products.3");
7.8.3. Lucene ディレクトリーへのアクセス リンクのコピーリンクがクリップボードにコピーされました!
Directory は、インデックスストレージを表すために Lucene で使用される最も一般的な抽象化です。Hibernate Search は Lucene Directory と直接対話することはありませんが、これらの対話は IndexManager 経由で抽象化されます。インデックスが必ずしもディレクトリーによって実装される必要はありません。
インデックスが Directory として表示され、アクセスする必要がある場合は、IndexManager からディレクトリーへの参照を取得できます。IndexManager を DirectoryBasedIndexManager にキャストし、getDirectoryProvider().getDirectory() を使用して基礎となる Directory への参照を取得します。これは推奨されていません.代わりに IndexReader を使用することが推奨されます。
7.8.4. シャード化インデックス リンクのコピーリンクがクリップボードにコピーされました!
場合によっては、特定のエンティティーのインデックス付きデータを複数の Lucene インデックスに分割 (シャード) すると役に立つことがあります。
シャード化は、欠点が短所を上回った場合にのみ実装する必要があります。単一の検索用にシャードをすべて開く必要があるため、シャード化されたインデックスの検索は一般的に遅くなります。
シャード化のユースケースを以下に示します。
- 単一のインデックスが大きいと、インデックスの更新時間は遅くなります。
- 一般的な検索は、データが顧客、地域、またはアプリケーションによってセグメント化された場合など、インデックスのサブセットのみに一致します。
デフォルトでは、シャードの数が設定されていないとシャード化は有効になりません。これには、hibernate.search.<indexName>.sharding_strategy.nbr_of_shards プロパティーを使用します。
例: インデックスのシャード化
この例では、5 つのシャードが有効にされています。
hibernate.search.<indexName>.sharding_strategy.nbr_of_shards = 5
データをサブインデックスに分割するには、IndexShardingStrategy を使用します。デフォルトのシャード化ストラテジーは、(FieldBridge によって生成された)ID 文字列表現のハッシュ値に従ってデータを分割します。これにより、シャードが大幅に分散されます。カスタムの IndexShardingStrategy を実装して、デフォルトのストラテジーを置き換えることができます。カスタムストラテジーを使用するには、hibernate.search.<indexName>.sharding_strategy プロパティーを設定する必要があります。
例: カスタムシャードストラテジーの指定
hibernate.search.<indexName>.sharding_strategy = my.shardingstrategy.Implementation
IndexShardingStrategy プロパティーでは、クエリーを実行するシャードを選択して検索を最適化することもできます。フィルターをアクティベートすることで、シャード化ストラテジーはクエリー (IndexShardingStrategy.getIndexManagersForQuery) に回答するために使用されるシャードのサブセットを選択できるため、クエリーの実行が速くなります。
各シャードには独立した IndexManager があるため、異なるディレクトリープロバイダーおよびバックエンド設定を使用するように設定できます。以下の例の Animal エンティティーの IndexManager インデックス名は Animal.0 から Animal.4 です。つまり、各シャードには所有するインデックス名の後に . (ドット) とそのインデックス番号が設定されます。
例: エンティティー解析のシャード化設定
hibernate.search.default.indexBase = /usr/lucene/indexes
hibernate.search.Animal.sharding_strategy.nbr_of_shards = 5
hibernate.search.Animal.directory_provider = filesystem
hibernate.search.Animal.0.indexName = Animal00
hibernate.search.Animal.3.indexBase = /usr/lucene/sharded
hibernate.search.Animal.3.indexName = Animal03
上記の例では、設定はデフォルトの id 文字列ハッシュストラテジーを使用し、シャードは Animal インデックスを 5 つのサブインデックスに使用しています。すべてのサブインデックスはファイルシステムのインスタンスで、各サブインデックスが保存されるディレクトリーは、以下のようになります。
-
sub-index 0:
/usr/lucene/indexes/Animal00(共有 indexBase、indexName を上書き) -
sub-index 1:
/usr/lucene/indexes/Animal.1(共有 indexBase、デフォルトの indexName) -
sub-index 2:
/usr/lucene/indexes/Animal.2(共有 indexBase、デフォルトの indexName) -
sub-index 3:
/usr/lucene/shared/Animal03(上書きされた indexBase、上書きされた indexName) -
sub-index 4:
/usr/lucene/indexes/Animal.4(共有 indexBase、デフォルトの indexName)
IndexShardingStrategy を実装する場合は、任意のフィールドを使用してシャード化の選択を判断できます。deletion、purge、purgeAll などの操作を処理するには、すべてのフィールド値またはプライマリー識別子を読み取れずにインデックスを返す必要があることもあります。このような場合、すべてのインデックスが返されるため、削除操作は、削除されるドキュメントが含まれる可能性のあるすべてのインデックスに伝播されます。
7.8.5. Lucene のスコアリングカスタマイズ リンクのコピーリンクがクリップボードにコピーされました!
Lucene を使用するとユーザーは、org.apache.lucene.search.Similarity を拡張して、そのフラグ式をカスタマイズできます。このクラスで定義された抽象メソッドは、以下の式の係数と一致し、ドキュメント d のクエリー q のスコアを計算します。
org.apache.lucene.search.Similarity を拡張して、Lucene の診断式をカスタマイズします。抽象メソッドは、以下のようにドキュメント d のクエリー q のスコアを計算するために使用される式と一致します。
*score(q,d) = coord(q,d) · queryNorm(q) · ∑ ~t in q~ ( tf(t in d) ·
idf(t) ^2^ · t.getBoost() · norm(t,d) )*
| ファクター | 説明 |
|---|---|
| tf(t ind) | ドキュメント (d) の用語 (t) の周波数係数。 |
| idf(t) | 用語の頻度に関する記録。 |
| coord(q,d) | 指定されたドキュメントのクエリー用語がいくつあるかに基づくスコア要因。 |
| queryNorm(q) | クエリー間でスコアを設定するために使用される正規化の要素。 |
| t.getBoost() | フィールドブースト。 |
| norm(t,d) | いくつかの (インデックス時間) ブーストおよび長さ要素をカプセル化します。 |
この式の詳細は、本書の範囲外です。詳細は、Similarity の Java ドキュメントを参照してください。
Hibernate Search では、Lucene の類似性の計算を修正する方法を利用できます。
最初に、プロパティー hibernate.search.matchity を使用して、Similarity 実装の完全に指定されたクラス名を指定すると、デフォルトの類似性を設定できます。デフォルト値は org.apache.lucene.search.DefaultSimilarity です。
また、similarity プロパティーを設定して特定のインデックスに使用される類似性を上書きすることもできます。
hibernate.search.default.similarity = my.custom.Similarity
最後に、@Similarity アノテーションを使用してクラスレベルのデフォルトの類似性を上書きできます。
@Entity
@Indexed
@Similarity(impl = DummySimilarity.class)
public class Book {
...
}
たとえば、ドキュメントに用語が表示される頻度は重要ではないと仮定します。用語が 1 回出現したドキュメントは、複数回見つかったドキュメントと同様にスコア付けされる必要があります。この場合、メソッド tf(float freq) のカスタム実装は 1.0 を返します。
2 つのエンティティーが同じインデックスを共有する場合、同じ Similarity 実装を宣言する必要があります。同じクラス階層のクラスは常にインデックスを共有するため、シミュリティー実装の上書きはできません。
同様に、インデックス設定とクラスレベルの設定で類似性を定義することは、競合が発生するため意味がありません。このような設定は拒否されます。
7.8.6. 例外処理の設定 リンクのコピーリンクがクリップボードにコピーされました!
Hibernate Search では、インデックスプロセスでの例外の処理方法を設定できます。設定が指定されていない場合、デフォルトでは例外がログ出力に記録されます。以下のように例外ロギングメカニズムを明示的に宣言できます。
hibernate.search.error_handler = log
デフォルトの例外処理は、同期インデックスと非同期インデックスの両方で行われます。Hibernate Search は、デフォルトのエラー処理実装を上書きする簡単なメカニズムを提供します。
独自の実装を指定するには、handle(ErrorContext context) メソッドを提供する ErrorHandler インターフェースを実装する必要があります。ErrorContext は、プライマリー LuceneWork インスタンス、LuceneWork、およびプライマリー例外により処理できなかった後続の LuceneWork インスタンスへの参照を提供します。
public interface ErrorContext {
List<LuceneWork> getFailingOperations();
LuceneWork getOperationAtFault();
Throwable getThrowable();
boolean hasErrors();
}
このエラーハンドラーを Hibernate Search に登録するには、設定プロパティーで ErrorHandler 実装の完全修飾クラス名を宣言する必要があります。
hibernate.search.error_handler = CustomerErrorHandler
7.8.7. Hibernate Search の無効化 リンクのコピーリンクがクリップボードにコピーされました!
Hibernate Search は必要に応じて部分的にまたは完全に無効にできます。Hibernate Search のインデックスは、たとえばインデックスが読み取り専用の場合や、自動ではなく手動でインデックスを実行したい場合など、無効にすることができます。Hibernate Search を完全に無効にして、インデックス設定や検索を阻止することもできます。
- インデックスの無効化
Hibernate Search インデックスを無効にするには、
indexing_strategy設定オプションをmanualに変更して JBoss EAP を再起動します。hibernate.search.indexing_strategy = manual- Hibernate Search を完全に無効にする
Hibernate Search を完全に無効にするには、
autoregister_listeners設定オプションをfalseに変更してすべてのリスナーを無効にし、JBoss EAP を再起動します。hibernate.search.autoregister_listeners = false
7.9. モニタリング リンクのコピーリンクがクリップボードにコピーされました!
Hibernate Search は、SearchFactory.getStatistics() を経由した Statistics オブジェクトへのアクセスを提供します。たとえば、インデックス付けされるクラスや、インデックスに含まれるエンティティーの数を判別できます。この情報は、常に利用できます。ただし、設定でhibernate.search.generate_statistics プロパティーを指定すると、合計および平均の Lucene クエリーおよびオブジェクトの読み込みタイミングを収集することもできます。
Jakarta Management を使用した統計へのアクセス
Jakarta Management による統計へのアクセスを有効にするには、hibernate.search.jmx_enabled プロパティーを true に設定します。これにより、StatisticsInfoMBean Bean が自動的に登録され、Statistics オブジェクトを使用した統計へのアクセスが提供されます。設定によっては、IndexingProgressMonitorMBean Bean も登録できます。
インデックスのモニタリング
マスインデックサー API を使用する場合は、IndexingProgressMonitorMBean Bean を使用してインデックス作成の進捗をモニターできます。Bean は、インデックスが進行中の Jakarta Management にのみバインドされます。
JConsole を使用して Jakarta Management bean にリモートでアクセスするには、システムプロパティー com.sun.management.jmxremote を trueに設定します。