Hibernate Search リファレンスガイド
JBoss Enterprise Application Platform 5 向け
エディッション 5.1.2
概要
第1章 はじめに
1.1. システム要求
Java ランタイム | JDK または JRE バージョン 5 以上。Java Runtime for Windows/Linux/Solaris はここからダウンロードできます。 |
Hibernate Search | Hibernate Search ディストリビューションの lib ディレクトリにある hibernate-search.jar とすべてのランタイム依存関係。必要な依存関係を理解するには、lib ディレクトリにある README.txt を参照してください。 |
Hibernate Core | この手順は Hibernate 3.3.x に対してテストされました。hibernate-core.jar とディストリビューションの lib ディレクトリにある推移的な依存関係が必要です。最小ラインタイム要件を調べるには、lib ディレクトリにある README.txt を参照してください。 |
Hibernate Annotations | Hibernate Search は Hibernate Annotations なしで使用できますが、以下の手順では基本的なエンティティ設定 (@Entity, @Id, @OneToMany,...) のために Hibernate Annotations を使用します。設定のこの部分は xml またはコードでも記述できます。ただし、Hibernate Search 自体は、別の設定が存在しない独自のアノテーションセット (@Indexed, @DocumentId, @Field,...) を持ちます。チュートリアルは Hibernate Annotations のバージョン 3.4.x に対してテストされました。 |
1.2. Maven の使用
pom.xml
または settings.xml
の repositories セクションに追加します。
例1.1 settings.xml
への JBoss maven レポジトリの追加
<repository> <id>repository.jboss.org</id> <name>JBoss Maven Repository</name> <url>http://repository.jboss.org/maven2</url> <layout>default</layout> </repository>
例1.2 Hibernate Search 用の Maven 依存関係
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-search</artifactId> <version>3.1.0.GA</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-annotations</artifactId> <version>3.4.0.GA</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>3.4.0.GA</version> </dependency> <dependency> <groupId>org.apache.solr</groupId> <artifactId>solr-common</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>org.apache.solr</groupId> <artifactId>solr-core</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-snowball</artifactId> <version>2.4.0</version> </dependency>
lucene-snowball
依存関係は、Lucene のスノーボールステマーを使用する場合に必要です。
1.3. 設定
hibernate.properties
または hibernate.cfg.xml
で実行できます。JPA から Hibernate を使用している場合は、プロパティを persistence.xml
に追加することもできます。標準的な使用では、ほとんどのプロパティが適切なデフォルト値を提供します。サンプルの persistence.xml
設定は以下のようになります。
例1.3
、hibernate.properties
、または hibernate.cfg.xml
persistence.xml
に追加される基本的な設定オプション
... <property name="hibernate.search.default.directory_provider" value="org.hibernate.search.store.FSDirectoryProvider"/> <property name="hibernate.search.default.indexBase" value="/var/lucene/indexes"/> ...
DirectoryProvider
を Hibernate Search に指示する必要があります。これは、hibernate.search.default.directory_provider
プロパティを設定することにより達成できます。Apache Lucene には インデックスファイルを格納する Directory
の概念があります。Hibernate Search は DirectoryProvider
を使用して Lucene Directory
インスタンスの初期化と設定を処理します。このチュートリアルでは、FSDirectoryProvider
という名前の DirectoryProvider
のサブクラスを使用します。これにより、Hibernate Search によって作成された Lucene インデックスを物理的に検査できます (たとえば、Luke を使用)。稼働する設定がある場合は、他のディレクトリプロバイダを実験できます (「Directory 構成」 を参照)。ディレクトリプロバイダの次は、 hibernate.search.default.indexBase
を使用してすべてのインデックスに対してデフォルトのルートディレクトリを指定する必要があります。
example.Book
と example.Author
が含まれ、データベースに含まれた書籍を検索するためにフリーテキスト検索機能をアプリケーションに追加したいとします。
例1.4 Hibernate Search 固有のアノテーションを追加する前のエンティティ Book と Author の例
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
をインデックス化可能としてマークします。設計により、Hibernate Search は特定のエンティティのインデックスの単一性を確保するためにインデックスに非トークン化 id を格納する必要があります。@DocumentId
は、このために使用するプロパティをマークし、ほとんどの場合はデータベース一次キーと同じです。実際には、Hibernate Search の 3.1.0 リリース以降、@DocumentId
はオプションです (@Id
アノテーションが存在します)。
title
と subtitle
を @Field
でアノテートします。パラメータ index=Index.TOKENIZED
により、テキストはデフォルトの Lucene アナライザを使用してトークン化されます。通常は、トークン化とは文を個々の単語に分割し、場合によっては 'a'
や 'the
' などの共通の単語を除外することを意味します。アナライザについては、後ほど詳しく説明します。 @Field
、 store=Store.NO
内で指定する2 つ目のパラメータは、実際のデータがインデックスに格納されないようにします。このデータがインデックスに格納されるかどうかは、その検索機能と関係ありません。Lucene の観点から、インデックスの作成後はデータを保持する必要がありません。これを格納する利点は、プロジェクション (「プロジェクション」) を使用して取得できることです。
Store.NO
が推奨されます (プロジェクションはオブジェクトアレイのみ返します)。
Book
クラスのアノテートに戻ってみましょう。まだ説明してないアノテーションは @DateBridge
です。このアノテーションは、Hibernate Search のビルトインフィールドブリッジの 1 つです。Lucene インデックスは純粋に文字列ベースです。このため、Hibernate Search はインデックス化されたフィールドのデータタイプを文字列に変換する必要があります (また、文字列をフィールドのデータタイプに変換する必要があります)。事前に定義されたさまざまなブリッジ (java.util.Date
を指定されたレゾリューションを持つ String
に変換する DateBridge
を含む) が提供されます。詳細については、「プロパティ/フィールドのブリッジ」 を参照してください。
@IndexedEmbedded.
が残りました。このアノテーションは関連付けられたエンティティ (@ManyToMany
、@*ToOne
、および @Embedded
) を所有エンティティの一部としてインデックス化するために使用されます。これは、Lucene インデックスドキュメントがオブジェクト関係について何も知らないフラットなデータ構造であるため必要です。著者名が検索可能になるように、著者名は書籍自体の一部としてインデックス化する必要があります。また、@IndexedEmbedded
上で、@Indexed
を使用してインデックスに含める関連付けられたエンティティのすべてのフィールドをマークする必要があります。詳細については、「組込みおよび関連付けられたオブジェクト」 を参照してください。
例1.5 Hibernate Search アノテーション追加後のサンプルエンティティ
package example; ... @Entity @Indexed public class Book { @Id @GeneratedValue @DocumentId private Integer id; @Field(index=Index.TOKENIZED, store=Store.NO) private String title; @Field(index=Index.TOKENIZED, store=Store.NO) private String subtitle; @IndexedEmbedded @ManyToMany private Set<Author> authors = new HashSet<Author>(); @Field(index = Index.UN_TOKENIZED, store = Store.YES) @DateBridge(resolution = Resolution.DAY) private Date publicationDate; public Book() { } // standard getters/setters follow here ... }
package example;
...
@Entity
public class Author {
@Id
@GeneratedValue
private Integer id;
@Field(index=Index.TOKENIZED, store=Store.NO)
private String name;
public Author() {
}
// standard getters/setters follow here
...
}
1.4. インデックス化
例1.6 Hibernate Session を使用してデータをインデックス化
FullTextSession fullTextSession = Search.getFullTextSession(session);
Transaction tx = fullTextSession.beginTransaction();
List books = session.createQuery("from Book as book").list();
for (Book book : books) {
fullTextSession.index(book);
}
tx.commit(); //index is written at commit time
例1.7 JPA を使用してデータをインデックス化
EntityManager em = entityManagerFactory.createEntityManager();
FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(em);
em.getTransaction().begin();
List books = em.createQuery("select book from Book as book").getResultList();
for (Book book : books) {
fullTextEntityManager.index(book);
}
em.getTransaction().commit();
em.close();
/var/lucene/indexes/example.Book
下にあるはずです。Luke を使用してこのインデックスを確認します。これにより、Hibernate Search がどのように動作するかを理解できます。
1.5. 検索
Book
のリストを返します。
例1.8 Hibernate Session を使用して検索を作成および実行
FullTextSession fullTextSession = Search.getFullTextSession(session); Transaction tx = fullTextSession.beginTransaction(); // create native Lucene query String[] fields = new String[]{"title", "subtitle", "authors.name", "publicationDate"}; MultiFieldQueryParser parser = new MultiFieldQueryParser(fields, new StandardAnalyzer()); org.apache.lucene.search.Query query = parser.parse( "Java rocks!" ); // 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();
例1.9 JPA を使用して検索を作成および実行
EntityManager em = entityManagerFactory.createEntityManager(); FullTextEntityManager fullTextEntityManager = org.hibernate.search.jpa.Search.getFullTextEntityManager(em); em.getTransaction().begin(); // create native Lucene query String[] fields = new String[]{"title", "subtitle", "authors.name", "publicationDate"}; MultiFieldQueryParser parser = new MultiFieldQueryParser(fields, new StandardAnalyzer()); org.apache.lucene.search.Query query = parser.parse( "Java rocks!" ); // 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();
1.6. アナライザ
- 設定ファイルでの
hibernate.search.analyzer
プロパティの設定。指定されたクラスはデフォルトのアナライザです。 - エンティティレベルでの
アノテーションの設定。@Analyzer
- フィールドレベルでの
@
アノテーションの設定。Analyzer
@Analyzer
アノテーションを使用する場合は、使用するアナライザの完全修飾クラス名を指定したり、@AnalyzerDef
アノテーションにより定義されたアナライザ定義を参照したりできます。アナライザ定義を参照する場合は、ファクトリを使用する Solr アナライザフレームワークが使用されます。利用可能なファクトリクラスの詳細については、Solr JavaDoc または Solr Wiki の対応するセクションを参照してください。選択されたファクトリに応じて、Solr 依存関係の上部に追加のライブラリが必要になることがあります。たとえば、PhoneticFilterFactory
は commons-codec に依存します。
StandardTokenizerFactory
が使用され、その後に LowerCaseFilterFactory
と SnowballPorterFilterFactory
の 2 つのフィルタファクトリが続きます。標準的なトークナイザは単語を句読点とハイフンで分割します (電子メールアドレスとインターネットホスト名はそのまま保持されます)。これは優れた汎用的なトークナイザです。小文字のフィルタは各トークンの文字を小文字にし、スノーボールフィルタは言語固有のステミングを適用します。
例1.10 @AnalyzerDef
および Solr フレームワークを使用してアナライザを定義および使用
package example; ... @Entity @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 { @Id @GeneratedValue @DocumentId private Integer id; @Field(index=Index.TOKENIZED, store=Store.NO) @Analyzer(definition = "customanalyzer") private String title; @Field(index=Index.TOKENIZED, store=Store.NO) @Analyzer(definition = "customanalyzer") private String subtitle; @IndexedEmbedded @ManyToMany private Set<Author> authors = new HashSet<Author>(); @Field(index = Index.UN_TOKENIZED, store = Store.YES) @DateBridge(resolution = Resolution.DAY) private Date publicationDate; public Book() { } // standard getters/setters follow here ... }
1.7. 次の作業
例1.11 maven アーキタイプを使用してチュートリアルソースを作成
mvn archetype:create \ -DarchetypeGroupId=org.hibernate \ -DarchetypeArtifactId=hibernate-search-quickstart \ -DarchetypeVersion=3.1.0.GA \ -DgroupId=my.company -DartifactId=quickstart
第2章 アーキテクチャ
2.1. 概要
DirectoryProvider
に関する概念があります。 ディレクトリプロバイダは特定の Lucene Directory
タイプを管理します。 ディレクトリプロバイダを設定してディレクトリターゲットを調整することができます (「Directory 構成」 を参照)。
FullTextSession
は Hibernate Session 上に構築されるため、アプリケーションコードは統一された org.hibernate.Query
または javax.persistence.Query
を HQL、JPA-QL、 またはネイティブのクエリが行うのと全く同じ方法で使用できます。
- パフォーマンス: バッチで操作を実行したとき Lucene のインデックス化は向上します。
- ACIDity: 実行される作業はデータベーストランザクションにより実行される作業と同じ範囲を持ち、トランザクションがコミットされた場合にのみ実行されます。これは、厳密には ACID ではありませんが、ACID の動作は完全テキスト検索インデックスにはほとんど役に立ちません (インデックスをソースからいつでも再構築できるため)。
注記
2.2. バックエンド
2.2.1. バックエンドタイプ
2.2.1.1. Lucene
2.2.1.2. JMS
注記
hibernate-dev@lists.jboss.org
までご連絡ください。
2.2.2. 作業の実行
2.2.2.1. 同期
2.2.2.2. 非同期
2.3. リーダー方針
2.3.1. 共有
IndexReader
が最新の場合に、Hibernate Search が特定の Lucene に対して複数のクエリとスレッドで同じ IndexReader
を共有します。IndexReader
が最新でない場合は、新しい IndexReader
がオープンされ、提供されます。各 IndexReader
は複数の SegmentReader
から構成されます。この方針は最後のオープン後に変更または作成されたセグメントのみを再オープンし、以前のインスタンスからすでにロードされたセグメントを共有します。この方針はデフォルトです。
shared
です。
2.3.2. 非共有
IndexReader
がオープンされます。IndexReader
のオープンやウォームアップは比較的コストが高い操作であるため、この方針は最も効率的とはいえません。
not-shared
です。
2.3.3. カスタム
org.hibernate.search.reader.ReaderProvider
を実装することにより、アプリケーションのニーズを満たす独自のリーダー方針を記述できます。この実装はスレッドセーフである必要があります。
第3章 設定
3.1. Directory 構成
Directory
の表記が存在します。Directory
実装はカスタマイズできますが、Lucene はファイルシステム (FSDirectoryProvider
) とメモリ内 (RAMDirectoryProvider
) 実装でバンドルされます。DirectoryProvider
は Lucene Directory
を Hibernate Search により抽象化し、基礎となる Lucene リソースの設定と初期化を処理します。表3.1「ビルトインの Directory Provider 一覧」 は Hibernate Search でバンドルされるディレクトリプロバイダのリストを示します。
クラス | 説明 | プロパティ |
---|---|---|
org.hibernate.search.store.RAMDirectoryProvider | メモリベースのディレクトリ。ディレクトリは @Indexed.index エレメントにより一意に (同じデプロイメント単位で) 識別されます。 | none |
org.hibernate.search.store.FSDirectoryProvider | ファイルシステムベースのディレクトリ。使用されるディレクトリは <indexBase>/< indexName > です。 | indexBase : ベースディレクトリ
indexName : @Indexed.index をオーバーライドします (共有されたインデックスの場合に役に立ちます)。
locking_strategy : オプション。「LockFactory の設定」 を参照してください。
|
org.hibernate.search.store.FSMasterDirectoryProvider |
ファイルシステムベースのディレクトリ。FSDirectoryProvider と同様。インデックスを定期的にソースディレクトリ (コピーディレクトリとも呼ばれます) にコピーします。
更新期間の推奨値は情報をコピーする時間よりも大きい (最低) 50%です (デフォルトの 3600 秒 - 60 分)。
コピーは平均コピー時間を短縮する差分コピーメカニズムに基づいています。
DirectoryProvider は通常、JMS バックエンドクラスタのマスターノードで使用されます。
buffer_size_on_copy の最適化は、オペレーティングシステムと利用可能な RAM に依存します。16 〜 64MB の値を使用した場合に良い結果が得られると報告したユーザーがほとんどです。
| indexBase : ベースのディレクトリ
indexName : @Indexed.index をオーバーライドします (共有されたインデックスの場合に役に立ちます)。
sourceBase : ソース (コピー) ベースディレクトリ
source : ソースディレクトリのサフィックス (デフォルト値は @Indexed.index )。実際のソースディレクトリ名は <sourceBase>/<source> です。
refresh : 秒単位の更新期間 (更新期間ごとにコピーが行われます)
buffer_size_on_copy : 単一ローレベルコピー命令で移動する MegaBytes の量。デフォルト値は 16MB です。
locking_strategy : オプション。「LockFactory の設定」 を参照してください。
|
org.hibernate.search.store.FSSlaveDirectoryProvider |
ファイルシステムベースのディレクトリ。FSDirectoryProvider と似ていますが通常でマスターバージョン (ソース) を取得します。ロックと不整合な検索結果を回避するために、2 つのローカルコピーが保持されます。
更新期間の推奨値は情報をコピーする時間よりも大きい (最低) 50%です (デフォルトの 3600 秒 - 60 分)。
コピーは平均コピー時間を短縮する差分コピーメカニズムに基づいています。
DirectoryProvider は通常、JMS バックエンドを使用したスレーブノードで使用されます。
buffer_size_on_copy の最適化は、オペレーティングシステムと利用可能な RAM に依存します。16 〜 64MB の値を使用した場合に良い結果が得られると報告したユーザーがほとんどです。
| indexBase : ベースのディレクトリ
indexName : @Indexed.index をオーバーライドします (共有されたインデックスの場合に役に立ちます)。
sourceBase : ソース (コピー) ベースディレクトリ
source : ソースディレクトリのサフィックス (デフォルト値は @Indexed.index )。実際のソースディレクトリ名は <sourceBase>/<source> です。
refresh : 秒単位の更新期間 (更新期間ごとにコピーが行われます)
buffer_size_on_copy : 単一ローレベルコピー命令で移動する MegaBytes の量。デフォルト値は 16MB です。
locking_strategy : オプション。「LockFactory の設定」 を参照してください。
|
org.hibernate.store.DirectoryProvider
インターフェースを実装することで独自のディレクトリプロバイダを記述することができます。
hibernate.search.
indexname でプレフィックスされるプロパティでインデックスを設定することができます。 全インデックスに継承されるデフォルトのプロパティはプレフィックスの hibernate.search.default.
を使用して定義することができます。
hibernate.search.indexname.directory_provider
を使用します。
例3.1 ディレクトリプロバイダの設定
hibernate.search.default.directory_provider org.hibernate.search.store.FSDirectoryProvider hibernate.search.default.indexBase=/usr/lucene/indexes hibernate.search.Rules.directory_provider org.hibernate.search.store.RAMDirectoryProvider
例3.2 @Indexed
の index
パラメータを使用したインデックス名の指定
@Indexed(index="Status") public class Status { ... } @Indexed(index="Rules") public class Rule { ... }
/usr/lucene/indexes/Status
内にファイルシステムディレクトリを作成し、 Rule エンティティがインデックス化される Rules
という名前のインメモリディレクトリを使用します。
DirectoryProvider
を記述すると、 この設定メカニズムを利用することもできます。
3.2. インデックスの分割
IndexShardingStrategy
により、データは異なるサブインデックスに分割されます。デフォルトでは、分割の数が設定されない限り、分割方針は有効になりません。分割の数を設定するには、次のプロパティを使用します。
例3.3 特定のインデックスに対して nbr_of_shards を指定することによりインデックスの分割を有効化
hibernate.search.<indexName>.sharding_strategy.nbr_of_shards 5
IndexShardingStrategy
を実装し、以下のプロパティを設定することにより変えることができます。
例3.4 カスタム分割方針の指定
hibernate.search.<indexName>.sharding_strategy my.shardingstrategy.Implementation
<indexName>.0
〜 <indexName>.4
です。つまり、各分割は独自のインデックスの名前の後に .
(ドット) とインデックス番号が付いた名前を持ちます。
例3.5 サンプルのエンティティAnimal
に対する分割設定の構成
hibernate.search.default.indexBase /usr/lucene/indexes hibernate.search.Animal.sharding_strategy.nbr_of_shards 5 hibernate.search.Animal.directory_provider org.hibernate.search.store.FSDirectoryProvider hibernate.search.Animal.0.indexName Animal00 hibernate.search.Animal.3.indexBase /usr/lucene/sharded hibernate.search.Animal.3.indexName Animal03
FSDirectoryProvider
インスタンスであり、各サブインデックスが格納されるディレクトリは以下のとおりです。
- サブインデックス 0 用: /usr/lucene/indexes/Animal00 (共有された indexBase、上書きされた indexName)
- サブインデックス 1 用: /usr/lucene/indexes/Animal.1 (共有された indexBase、デフォルトの indexName)
- サブインデックス 2 用: /usr/lucene/indexes/Animal.2 (共有された indexBase、デフォルトの indexName)
- サブインデックス 3 用: /usr/lucene/shared/Animal03 (上書きされた indexBase、上書きされた indexName)
- サブインデックス 4 用: /usr/lucene/indexes/Animal.4 (共有された indexBase、デフォルトの indexName)
3.3. インデックスの共有 (同じディレクトリに 2 つのエンティティ)
注記
- 基礎となるディレクトリプロバイダが同じ物理インデックスディレクトリを参照するよう設定します。実際には、プロパティ
hibernate.search.[fully qualified entity name].indexName
に同じ値を設定します。例として、Furniture
とAnimal
エンティティに対して同じインデックス (ディレクトリ) を使用します。例 “Animal” の両方のエンティティに対してindexName
を設定します。両方のエンティティは Animal ディレクトリに格納されます。hibernate.search.org.hibernate.search.test.shards.Furniture.indexName = Aninal hibernate.search.org.hibernate.search.test.shards.Animal.indexName = Aninal
- 同じ値にマージする、エンティティの
@Indexed
アノテーションのindex
属性を設定します。Animal
のすべてのインスタンスとともに再びFurniture
インスタンスをAnimal
インデックスでインデックス化する場合は、Animal
クラスとFurniture
クラスの両方で@Indexed(index=”Animal”)
を指定します。
3.4. ワーカーの設定
プロパティ | 説明 |
hibernate.search.worker.backend | Apache Lucene バックエンドと JMS バックエンドはデフォルトでサポートされます。デフォルト値は lucene です。jms もサポートされます。 |
hibernate.search.worker.execution | 同期および非同期実行をサポートします。デフォルト値は です。async もサポートされます。 |
hibernate.search.worker.thread_pool.size | プール内のスレッド数を定義します。非同期実行の場合のみ役に立ちます。デフォルト値は 1 です。 |
hibernate.search.worker.buffer_queue.max | スレッドポールが枯渇した場合に作業キューの最大数を定義します。非同期実行の場合のみ役に立ちます。デフォルト値は無限です。制限に達したら、作業は主なスレッドにより実行されます。 |
hibernate.search.worker.jndi.* | InitialContext を初期化する JNDI プロパティを定義します (必要な場合)。JNDI は JMS バックエンドのみにより使用されます。 |
hibernate.search.worker.jms.connection_factory | JMS バックエンドには必須。JMS 接続ファクトリを検索する JNDI 名を定義します (JBoss AS ではデフォルトで /ConnectionFactory ) |
hibernate.search.worker.jms.queue | JMS バックエンドには必須。JMS キューを検索する JNDI 名を定義します。このキューは作業メッセージを転記するために使用されます。 |
3.5. JMS マスター/スレーブの設定
3.5.1. スレーブノード
例3.6 JMS スレーブの設定
### 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 = org.hibernate.search.store.FSSlaveDirectoryProvider ## Backend configuration hibernate.search.worker.backend = jms hibernate.search.worker.jms.connection_factory = /ConnectionFactory hibernate.search.worker.jms.queue = queue/hibernatesearch #optional jndi configuration (check your JMS provider for more information) ## Optional asynchronous execution strategy # hibernate.search.worker.execution = async # hibernate.search.worker.thread_pool.size = 2 # hibernate.search.worker.buffer_queue.max = 50
3.5.2. マスターノード
例3.7 JMS マスターの設定
### 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 = org.hibernate.search.store.FSMasterDirectoryProvider ## Backend configuration #Backend is the default lucene one
例3.8 インデックスキューを処理するメッセージ駆動 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) } }
getSession()
と cleanSessionIfNeeded()
の詳細については、 AbstractJMSHibernateSearchController
の javadoc を参照してください。
3.6. リーダー方針の設定
shared
: 複数のクエリでインデックスリーダーを共有します。この方針は最も効率的です。not-shared
: 各クエリのインデックスリーダーを作成します。
shared
です。これは調整できます。
hibernate.search.reader.strategy = not-shared
not-shared
方針に追加します。
hibernate.search.reader.strategy = my.corp.myapp.CustomReaderProvider
my.corp.myapp.CustomReaderProvider
はカスタム方針実装です。
3.7. Hibernate Search および自動インデックスを有効化
3.7.1. Hibernate Search の有効化
hibernate.search.autoregister_listeners
を false に設定します。リスナーが有効な場合は、エンティティがインデックス化されない場合であってもパフォーマンスの影響はありません。
FullTextIndexEventListener
を追加します。
例3.9 FullTextIndexEventListener
を設定して Hibernate Search を明示的に有効にする
<hibernate-configuration> <session-factory> ... <event type="post-update"/> <listener class="org.hibernate.search.event.FullTextIndexEventListener"/> </event> <event type="post-insert"/> <listener class="org.hibernate.search.event.FullTextIndexEventListener"/> </event> <event type="post-delete"/> <listener class="org.hibernate.search.event.FullTextIndexEventListener"/> </event> <event type="post-collection-recreate"/> <listener class="org.hibernate.search.event.FullTextIndexEventListener"/> </event> <event type="post-collection-remove"/> <listener class="org.hibernate.search.event.FullTextIndexEventListener"/> </event> <event type="post-collection-update"/> <listener class="org.hibernate.search.event.FullTextIndexEventListener"/> </event> </session-factory> </hibernate-configuration>
3.7.2. 自動インデックス化
hibernate.search.indexing_strategy manual
注記
3.8. Lucene インデックス化パフォーマンスのチューニング
IndexWriter
に渡されるパラメータセット (mergeFactor
、maxMergeDocs
、maxBufferedDocs
など) を指定することにより Lucene インデックス化のパフォーマンスをチューニングできます。これらのパラメータは、すべてのインデックスに適用するデフォルト値、1 つのインデックごとのデフォルト値、または 1 つの分割ごとのデフォルト値として指定できます。
transaction
キーワードによりグループ化されます。
hibernate.search.[default|<indexname>].indexwriter.transaction.<parameter_name>インデックス化が
FullTextSession.index()
(6章手動インデックス化 を参照) を使用して行われた場合、使用されるプロパティは batch
キーワードによりグループ化されます。
hibernate.search.[default|<indexname>].indexwriter.batch.<parameter_name>
.batch
プロパティが明示的に設定されない限り、値はデフォルトで .transaction
プロパティに設定されます。特定の分割設定の .batch
値に対して値が設定されない場合、Hibernate Search は最初にインデックスセクション、次にデフォルトセクションを参照し、その後以下の順序で .transaction
を検索します。
hibernate.search.Animals.2.indexwriter.transaction.max_merge_docs 10 hibernate.search.Animals.2.indexwriter.transaction.merge_factor 20 hibernate.search.default.indexwriter.batch.max_merge_docs 100この設定は、Animals インデックスの共有された 2 つ目のものに適用される設定になります。
transaction.max_merge_docs
= 10batch.max_merge_docs
= 100transaction.merge_factor
= 20batch.merge_factor
= 20
2.4
に相対的です。Lucene インデックス化のパフォーマンスの詳細については、Lucene ドキュメンテーションを参照してください。
プロパティ | 説明 | デフォルト値 |
---|---|---|
hibernate.search.[default|<indexname>].indexwriter.[transaction|batch].max_buffered_delete_terms |
バッファ化されたメモリ内の削除タームが適用され、フラッシュされる前に必要な削除タームの最小数を決定します。メモリ内にバッファ化されたドキュメントが存在する場合、ドキュメントはマージされ、新しいセグメントが作成されます。
| 無効 (RAM の使用によりフラッシュ) |
hibernate.search.[default|<indexname>].indexwriter.[transaction|batch].max_buffered_docs |
インデックス化の間にメモリ内にバッファ化されたドキュメントの量を制御します。量が多くなると、消費される RAM も多くなります。
| 無効 (RAM の使用によりフラッシュ) |
hibernate.search.[default|<indexname>].indexwriter.[transaction|batch].max_field_length |
1 つのフィールドに対してインデックス化するタームの最大数。これにより、インデックス化に必要なメモリ量が制限され、データが非常に大きい場合にメモリ不足によりインデックス化プロセスがクラッシュしなくなります。この設定は、異なるタームの数ではなく稼働しているタームの数を参照します。
これにより大きいドキュメントが若干切り捨てられます (ドキュメントにあるすべてのタームがインデックスから除外されます)。ソースドキュメントが大きいことがわかっている場合は、期待されるサイズを収めることができるようこの値を十分に大きく設定してください。Integer.MAX_VALUE に設定した場合、唯一の制限はメモリです。ただし、OutOfMemoryError が発生します。
この値を
transaction ではなく batch で設定する場合は、インデックス化モードに応じてインデックスの異なるデータ (および結果) を取得できます。
| 10000 |
hibernate.search.[default|<indexname>].indexwriter.[transaction|batch].max_merge_docs |
セグメントで許可されるドキュメントの最大数を定義します。大きい値はバッチインデックス化と高速な検索に最適です。小さい値はトランザクションインデックス化に最適です。
| 無制限 (Integer.MAX_VALUE) |
hibernate.search.[default|<indexname>].indexwriter.[transaction|batch].merge_factor |
セグメントマージの頻度とサイズを制御します。
挿入が行われたときにセグメントインデックスをマージする頻度を決定します。値を小さくすると、インデックス化の間に使用される RAM の量が少なくなり、最適化されないインデックスの検索が高速になりますが、インデックス化の処理に時間がかかります。値を大きくすると、インデックス化の間に使用される RAM の量が多くなり、最適化されないインデックスの検索が低速になり、インデックス化が高速なります。したがって、バッチインデックス作成には大きい値 (> 10) が最適であり、対話的に保持するインデックスには小さい値 (< 10) が最適です。この値は 2 よりも小さくする必要があります。
| 10 |
hibernate.search.[default|<indexname>].indexwriter.[transaction|batch].ram_buffer_size |
ドキュメントバッファ専用の RAM の量 (MB 単位) を制御します。max_buffered_docs とともに使用すると、最初に発生したイベントに対してフラッシュが実行されます。
一般的に、高速なインデックス化パフォーマンスを実現するには、ドキュメント数の代わりに RAM の使用量によりフラッシュし、できるだけ大きい RAM バッファを使用することが最適です。
| 16 MB |
hibernate.search.[default|<indexname>].indexwriter.[transaction|batch].term_index_interval |
上級者向け: インデックス化されたターム間の間隔を設定します。
値を大きくすると、IndexReader により使用されるメモリ量は小さくなりますが、タームへのランダムアクセスが低速になります。値を小さくすると、IndexReader により使用されるメモリ量は大きくなり、タームへのランダムアクセスが高速になります。詳細については、Lucene ドキュメンテーションを参照してください。
| 128 |
hibernate.search.[default|<indexname>].indexwriter.[transaction|batch].use_compound_file | 複合ファイル形式を使用する利点は使用されるファイル記述子が少なくなることです。欠点はインデックス化に時間がかかり、より多くの一時ディスク領域が必要になることです。インデックス化の時間を短縮するために、このパラメータを false に設定できますが、mergeFactor も大きい場合は、ファイル記述子が足りなくなることがあります。
ブール値パラメータの場合は、"
true " または "false " を使用します。このオプションのデフォルト値は true です。
| true |
3.9. LockFactory の設定
hibernate.search.<index>.locking_strategy
オプションを simple
、native
、single
、または none
のいずれかに設定するか、あるいはorg.hibernate.search.store.LockFactoryFactory
の実装の完全修飾名に設定します。このインターフェイスを実装することにより、カスタム org.apache.lucene.store.LockFactory
を提供できます。
name | クラス | 説明 |
---|---|---|
simple | org.apache.lucene.store.SimpleFSLockFactory |
Java のファイル API をベースにした安全な実装。マーカーファイルを作成することにより、インデックスの使用をマークします。
何らかの理由でアプリケーションを終了する必要がある場合は、アプリケーションを再起動する前にこのファイルを削除する必要があります。
これは、
FSDirectoryProvider 、FSMasterDirectoryProvider 、および FSSlaveDirectoryProvider のデフォルトの実装です。
|
native | org.apache.lucene.store.NativeFSLockFactory | simple と同様に、これもマーカーファイルを作成することによりインデックスの使用をマークします。ただし、アプリケーションがクラッシュした場合であってもロックがクリーンアップされるようにネイティブの OS ファイルロックが使用されます。
この実装には NFS に関する既知の問題が存在します。
|
single | org.apache.lucene.store.SingleInstanceLockFactory |
この LockFactory はファイルマーカーを使用しませんが、メモリ内で保持される Java オブジェクトロックです。したがって、インデックスが他のプロセスにより共有されない場合のみ使用できます。
これは、
RAMDirectoryProvider のデフォルトの実装です。
|
none | 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
第4章 インデックス構造にエンティティをマッピング
4.1. エンティティのマッピング
4.1.1. 基本的なマッピング
@Indexed
アノテーションを付与してこれを行います (@Indexed
アノテーションが付与されていないエンティティはすべてインデックス化のプロセスで無視されることになる)。
例4.1 @Indexed
アノテーションを使用してクラスのインデックス化を可能にする
@Entity
@Indexed(index="indexes/essays")
public class Essay {
...
}
index
属性は Lucene ディレクトリ名が何になるかを Hibernate に指示します (通常はファイルシステム上のディレクトリ)。設定ファイルで hibernate.search.default.indexBase
プロパティを使用して、すべての Lucene インデックスにベースディレクトリを定義することが推奨されます。または、hibernate.search.<index>.indexBase
(<index>
はインデックス化されたエンティティの完全修飾クラス名) を指定してインデックス化された各エンティティに対してベースディレクトリを指定できます。各エンティティインスタンスは特定のインデックス (つまり Directory) の内側の Lucene Document
で表されます。
@Field
はプロパティをインデックス化されたものとして宣言します。Lucene ドキュメントにエレメントをインデックス化する場合はどのようにインデックス化するかを指定することができます。
name
: どの名前でプロパティが Lucene Document に格納されるかを指定します。 デフォルト値はそのプロパティ名です (JavaBeans 規則に準拠)。store
: プロパティが Lucene インデックスに格納されるかされないかを指定します。Store.YES
の値 (インデックス内の領域消費が多いが、プロジェクションを許可。詳細については、「プロジェクション」 を参照)、 圧縮形式で格納するStore.COMPRESS
(CPU の消費が多い)、 またはまったく格納しないようにするStore.NO
(デフォルトの値) のいずれかにセットできます。 プロパティが格納される場合はその元の値を Lucene Document から検索できます。これはエレメントがインデックス化されるか否かとは関係ありません。- index: どのようにエレメントのインデックス化を行うかと情報ストアのタイプを指定します。
Index.NO
(インデックス化なし、 クエリでは見つけられない)、Index.TOKENIZED
(プロパティの処理にアナライザを使用する)、Index.UN_TOKENISED
(アナライザ事前処理なし)、Index.NO_NORM
(正常化データを格納しない)などの値があります。デフォルト値はTOKENIZED
です。 - termVector: 期間と頻度のペアのコレクションを定義します。この属性により、インデックス化の間に期間ベクタが格納され、ドキュメント内で期間ベクタを利用できるようになります。デフォルト値は TermVector.NO です。この属性の他の値は次のとおりです。
値 定義 TermVector.YES 各ドキュメントの期間ベクタを格納します。これにより、同期化された 2 つのアレイが生成されます。1 つはドキュメント期間を含み、もう 1 つは期間の頻度を含みます。 TermVector.NO 期間ベクタは格納しないでください。 TermVector.WITH_OFFSETS 期間ベクタとトークンオフセット情報を格納します。これは、TermVector.YES と同じです。また、期間の開始/終了オフセット位置情報も含まれます。 TermVector.WITH_POSITIONS 期間ベクタおよびトークン位置情報を格納します。これは、TermVector.YES と同じです。また、ドキュメント内の各期間の順序の位置が含まれます。 TermVector.WITH_POSITIONS_OFFSETS 期間ベクタ、トークン位置、およびオフセット情報を格納します。これは、YES、WITH_OFFSETS、および WITH_POSITIONS の組み合わせです。
注記
@DocumentId
アノテーションを使用します。Hibernate Annotations を使用し、@Id を指定した場合は、@DocumentId を省略できます。選択されたエンティティ id はドキュメント id としても使用されます。
例4.2 インデックス化されたエンティティに @DocumentId
ad @Field
アノテーションを追加
@Entity @Indexed(index="indexes/essays") public class Essay { ... @Id @DocumentId public Long getId() { return id; } @Field(name="Abstract", index=Index.TOKENIZED, store=Store.YES) public String getSummary() { return summary; } @Lob @Field(index=Index.TOKENIZED) public String getText() { return text; } }
@DocumentId
ad @Field
アノテーションを追加」 では、id
、Abstract
、text
の 3 つのフィールドでインデックスを定義します。デフォルトではフィールド名は JavaBean の仕様に従い小文字に変換されます。
4.1.2. プロパティを複数回マッピング
UN_TOKENIZED
である必要があります。このプロパティのワードで検索し、ソートする場合は、プロパティを 2 回インデックス化する必要があります (1 回はトークン化、もう 1 回はトークン化解除)。@Fields を使用すると、この目的を達成できます。
例4.3 @Fields を使用してプロパティを複数回マップ
@Entity @Indexed(index = "Book" ) public class Book { @Fields( { @Field(index = Index.TOKENIZED), @Field(name = "summary_forSort", index = Index.UN_TOKENIZED, store = Store.YES) } ) public String getSummary() { return summary; } ... }
summary
は 2 回インデックス化されます(1 回はトークン化の方法で summary
として、もう 1 回は非トークン化の方法で summary_forSort
として)。@Field は @Fields が使用された場合に役に立つ 2 つの属性をサポートします。
- アナライザ: プロパティごとではなくフィールドごとに @Analyzer アノテーションを定義します。
- ブリッジ: プロパティごとではなくフィールドごとに @FieldBridge アノテーションを定義します。
4.1.3. 組込みおよび関連付けられたオブジェクト
address.city:Atlanta
に変換されます)。
例4.4 関係をインデックス化するために @IndexedEmbedded を使用
@Entity @Indexed public class Place { @Id @GeneratedValue @DocumentId private Long id; @Field( index = Index.TOKENIZED ) private String name; @OneToOne( cascade = { CascadeType.PERSIST, CascadeType.REMOVE } ) @IndexedEmbedded private Address address; .... } @Entity public class Address { @Id @GeneratedValue private Long id; @Field(index=Index.TOKENIZED) private String street; @Field(index=Index.TOKENIZED) private String city; @ContainedIn @OneToMany(mappedBy="address") private Set<Place> places; ... }
Place
インデックスでインデックス化されます。Place
インデックスドキュメントには、フィールド address.id
、address.street
、および address.city
(問い合わせることが可能) が含まれます。これは、@IndexedEmbedded
アノテーションにより有効化されます。
@IndexedEmbedded
テクニックを使用した場合、データは Lucene インデックスで非正規化されるため、Hibernate Search はインデックスを最新の状態に保つために Place
オブジェクトと Address
オブジェクトの変更を認識する必要があります。Address
が変更したときに Place
Lucene ドキュメントが更新されるように、@ContainedIn
との双方向の関係の一方をマークする必要があります。
@ContainedIn
は、組込みオブジェクト (オブジェクトコレクション) ではなくエンティティを参照する関係の場合のみ役に立ちます。
例4.5 @IndexedEmbedded
と @ContainedIn
のネストでの使用
@Entity @Indexed public class Place { @Id @GeneratedValue @DocumentId private Long id; @Field( index = Index.TOKENIZED ) private String name; @OneToOne( cascade = { CascadeType.PERSIST, CascadeType.REMOVE } ) @IndexedEmbedded private Address address; .... } @Entity public class Address { @Id @GeneratedValue private Long id; @Field(index=Index.TOKENIZED) private String street; @Field(index=Index.TOKENIZED) private String city; @IndexedEmbedded(depth = 1, prefix = "ownedBy_") private Owner ownedBy; @ContainedIn @OneToMany(mappedBy="address") private Set<Place> places; ... } @Embeddable public class Owner { @Field(index = Index.TOKENIZED) private String name; ... }
@*ToMany, @*ToOne
および @Embedded
属性は、 @IndexedEmbedded
を使用してアノテートできます。関連付けられたクラスの属性は、主なエンティティインデックスに追加されます。前の例では、インデックスに以下のフィールドが含まれます。
- id
- name
- address.street
- address.city
- addess.ownedBy_name
propertyName.
となります。これは、ownedBy
プロパティで示されたように prefix
属性を使用してオーバーライドできます。
注記
depth
プロパティが必要です。たとえば、Owner
が Place
を参照する場合、Hibernate Search は期待された深さに達した後 (または、オブジェクトグラフ境界に達した後) にインデックス化された組込み属性を含めることを止めます。自己参照を持つクラスは周期的な依存関係の例です。例では、depth
が 1 に設定されているため、Owner (存在する場合) の @IndexedEmbedded
属性は無視されます。
@IndexedEmbedded
を使用すると、以下のようにクエリを記述できます。
- 名前に JBoss が含まれ、住所の都市が Atlanta である場所を返します。Lucene クエリでは、以下のようになります。
+name:jboss +address.city:atlanta
- 名前に JBoss が含まれ、所有者の名前に Joe が含まれる場所を返します。Lucene クエリでは、以下のようになります。
+name:jboss +address.orderBy_name:joe
注記
@Indexed
である場合があります (ただし、これに限定されません)。
@ContainedIn
でアノテートする必要があります (前の例で示されたように)。これに該当しない場合、Hibernate Search は関連付けられたエンティティが更新されたときにルートインデックスを更新できません (例では、関連付けられた Address
インスタンスが更新されたときに、Place
インデックスドキュメントを更新する必要があります)。
@IndexedEmbedded
でアノテートされたオブジェクトタイプが Hibernate と Hibernate Search が対象とするオブジェクトタイプではないことがあります。これは、実装の代わりにインターフェイスが使用される場合に特に当てはまります。このため、Hibernate Search が対象とするオブジェクトタイプは targetElement
パラメータを使用してオーバーライドできます。
例4.6 @IndexedEmbedded
の targetElement
プロパティの使用
@Entity
@Indexed
public class Address {
@Id
@GeneratedValue
@DocumentId
private Long id;
@Field(index= Index.TOKENIZED)
private String street;
@IndexedEmbedded(depth = 1, prefix = "ownedBy_", targetElement = Owner.class)
@Target(Owner.class)
private Person ownedBy;
...
}
@Embeddable
public class Owner implements Person { ... }
4.1.4. ブーストファクタ
@Boost
を @Field、メソッド、またはクラスレベルで使用することができます。
例4.7 ブーストファクタを使用してインデックス化エレメントの重みをさまざまな方法で増やす
@Entity @Indexed(index="indexes/essays") @Boost(1.7f) public class Essay { ... @Id @DocumentId public Long getId() { return id; } @Field(name="Abstract", index=Index.TOKENIZED, store=Store.YES, boost=@Boost(2f)) @Boost(1.5f) public String getSummary() { return summary; } @Lob @Field(index=Index.TOKENIZED, 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 - @Field.boost
とプロパティの @Boost
は累積されます) 重要になります。text
フィールドは isbn
よりも 1.2 場合重要になります。 この説明は実際には誤りですが、 説明としてはわかりやすく現実に近いものとなります。 Otis Gospodnetic および Erik Hatcher の 『Lucene In Action』 または Lucene 関連のドキュメントを確認してください。
4.1.5. 動的ブーストファクタ
@Boost
アノテーションは、ランタイム時にインデックス化されたエンティティの状態から無関係の静的ブーストファクタを定義します。ただし、ブーストファクタが実際のエンティティの状態によって異なる使用事例もあります。この場合は、付随のカスタムの BoostStrategy
とともに @DynamicBoost
アノテーションを使用できます。
例4.8 動的ブーストの例
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
メソッドに渡されるか、アノテートされたフィールド/プロパティの値だけか異なります。渡されたオブジェクトを正しい種類にキャストするのはユーザー次第です。例では、重要人物が持つインデックス値はすべて、通常の人の値と比べ 2 倍重要になります。
注記
BoostStrategy
の実装は、引数なしのパブリックコンストラクタを定義します。
@Boost
と @DynamicBoost
アノテーションを適合することができます。すべての定義されたブーストファクタは、「ブーストファクタ」 に記載のとおり累積されます。
4.1.6. アナライザ
hibernate.search.analyzer
プロパティから設定することができます。 このプロパティのデフォルト値は org.apache.lucene.analysis.standard.StandardAnalyzer
です。
例4.9 アナライザを指定するさまざまな方法
@Entity @Indexed @Analyzer(impl = EntityAnalyzer.class) public class MyEntity { @Id @GeneratedValue @DocumentId private Integer id; @Field(index = Index.TOKENIZED) private String name; @Field(index = Index.TOKENIZED) @Analyzer(impl = PropertyAnalyzer.class) private String summary; @Field(index = Index.TOKENIZED, analyzer = @Analyzer(impl = FieldAnalyzer.class) private String body; ... }
name
など) をインデックス化するために EntityAnalyzer
が使用されます (それぞれ PropertyAnalyzer
と FieldAnalyzer
でインデックス化される summary
と body
を除く)。
重要
4.1.6.1. アナライザ定義
@Analyzer
宣言によって再利用できます。アナライザ定義は以下のものから構成されます。
- 名前: 定義を参照するために使用される一意の文字列
- トークナイザ: 個々のワードに入力ストリームをトークン化する
- フィルタのリスト: 各フィルタはトークナイザにより提供されたストリームに対してワードを削除、変更、または場合によっては追加する
Tokenizer
は入力された文字をトークン (さらに TokenFilter
によって処理される) に変換することによって分析プロセスを開始します。Hibernate Search は、Solr アナライザフレームワークを使用してこのインフラストラクチャをサポートします。アナライザ定義を使用するためにクラスパスに solr-core.jar and
solr-common.jar
を追加してください。スノーボールステマーも使用する場合は、lucene-snowball.jar.
も含めてください。他の Solr アナライザはさらに多くのライブラリに依存することがあります。たとえば、PhoneticFilterFactory
は commons-codec に依存します。Hibernate Search のディストリビューションは、これらの依存関係を lib
ディレクトリで提供します。
例4.10 @AnalyzerDef
および Solr フレームワーク
@AnalyzerDef(name="customanalyzer", 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
アノテーションで定義された順序で適用されます。この順序についてよく考えてください。
@Analyzer
宣言により実装クラスを宣言するのではなく定義名を使用して再利用できます。
例4.11 名前によるアナライザの参照
@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
により宣言されたアナライザインスタンスは SearchFactory
の名前で利用できます。
Analyzer analyzer = fullTextSession.getSearchFactory().getAnalyzer("customanalyzer");
4.1.6.2. 利用可能なアナライザ
ファクトリ | 説明 | パラメータ |
---|---|---|
StandardTokenizerFactory | Lucene 標準トークナイザを使用する | none |
HTMLStripStandardTokenizerFactory | HTML タグを削除し、テキストを保持し、StandardTokenizer に渡す | none |
ファクトリ | 説明 | パラメータ |
---|---|---|
StandardFilterFactory | 頭字語とワードからドットを削除する | none |
LowerCaseFilterFactory | 小文字のワード | none |
StopFilterFactory | ストップワードのリストに一致するワード (トークン) を削除する | words : ストップワードを含むリソースファイルを参照する
ignoreCase: ストップワードを比較するときに
case を無視する場合は true、その他の場合は false
|
SnowballPorterFilterFactory | ワードを特定の言語の語幹に減らす (たとえば、protect、protects、protection は同じ語幹を共有する)。このようなフィルタを使用すると、関連ワードに一致する検索ができる。 | language : Danish、Dutch、English、Finnish、French、German、Italian、Norwegian、Portuguese、Russian、Spanish、Swedish
|
ISOLatin1AccentFilterFactory | フランス語のような言語のアクセントを削除する | none |
org.apache.solr.analysis.TokenizerFactory
、org.apache.solr.analysis.TokenFilterFactory
のすべての実装をチェックして利用可能な実装を確認することをお薦めします。
4.1.6.3. アナライザ判別子 (実験段階)
BlogEntry
クラスの場合、アナライザは、エンティティの言語プロパティに依存することがあります。このプロパティに応じて、実際のテキストをインデックス化するために適切な言語固有ステマーを選択する必要があります。
AnalyzerDiscriminator
アノテーションが導入されました。以下の例は、このアノテーションの使用方法を示しています。
例4.12 アナライザを選択するための @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 getAnanyzerDefinitionName(Object value, Object entity, String field) { if ( value == null || !( entity instanceof Article ) ) { return null; } return (String) value; } }
@AnalyzerDiscriminator
を使用する前提条件は、使用されるすべてのアナライザが @AnalyzerDef
定義で事前に定義されていることです。これに該当する場合は、@AnalyzerDiscriminator
アノテーションをクラス、またはアナライザを動的に選択するエンティティの特定のプロパティに配置できます。AnalyzerDiscriminator
の impl
パラメータを使用して、Discriminator
インターフェイスの具体的な実装を指定します。実装する必要がある唯一のメソッドは、Lucene ドキュメントに追加される各フィールドに対して呼び出される getAnanyzerDefinitionName()
です。インデックス化されるエンティティは、インターフェイスメソッドにも渡されます。value
パラメータは、AnalyzerDiscriminator
がクラスレベルではなくプロパティレベルに配置された場合にのみ設定されます。この場合、値はこのプロパティ現在の値を表します。
Discriminator
インターフェイスの実装は、アナライザを動的に設定する場合は既存のアナライザ定義の名前、デフォルトのアナライザをオーバーライドしない場合は null
を返す必要があります。示された例では、言語パラメータが @AnalyzerDef
で指定された名前に一致する 'de' または 'en' のいずれかであることを前提とします。
注記
@AnalyzerDiscriminator
は現在まだ実験段階にあり、API は変わる可能性があります。この機能の利便性と使用感についてコミュニティからのフィードバックを募集しています。
4.1.6.4. アナライザの取得
注記
例4.13 完全テキストクエリの構築時にスコープ設定されたアナライザを使用
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
title
で使用され、ステミングアナライザはフィールド title_stemmed
で使用されます。検索ファクトリにより提供されたアナライザを使用することにより、クエリは対象となるフィールドに応じて適切なアナライザを使用します。
searchFactory.getAnalyzer(String)
を使用して定義により取得できます。
4.2. プロパティ/フィールドのブリッジ
@Field
でアノテートされたすべてのエンティティプロパティは String 形式でインデックス化する必要があります。プロパティのほとんどでは、Hibernate Search がビルトインのブリッジセットで変換作業を行ってくれます。 ただし、 変換プロセスを細かく制御する必要がある場合もあります。
4.2.1. ビルトインのブリッジ
- null
- null エレメントはインデックス化されません。 Lucene は null エレメントに対応しないため意味がありません。
- java.lang.String
- String はそのままインデックス化されます。
- short、 Short、 integer、 Integer、 long、 Long、 float、 Float、 double、 Double、 BigInteger、 BigDecimal
- 数値はその文字列表現に変換されます。 Lucene はそのままでは数値を比較できないため (つまり、 対象のクエリ内で使用できない)、 パディイングする必要があります。
注記
Range クエリの使用は議論の余地があり欠点もあるため、 代替となる手段として Filter クエリの使用があります。 これは結果となるクエリを適切な範囲にフィルタします。Hibernate Search はパッディングのメカニズムをサポートします。 - java.util.Date
- 日付は GMT 時間で yyyyMMddHHmmssSSS のように格納されます (2006 年 11 月 7 日の 4:03PM 12 ミリ秒 EST の場合は 200611072203012)。 内部形式は気にする必要はありません。 重要なのは DateRange クエリを使用する場合にその日付が GMT 時間で表現されるということを認識しておくことです。通常、 日付をミリ秒まで格納する必要がありません。
@DateBridge
はインデックスに格納しようとしている適切なリゾリューションを定義します (@DateBridge(resolution=Resolution.DAY)
@Entity @Indexed public class Meeting { @Field(index=Index.UN_TOKENIZED) @DateBridge(resolution=Resolution.MINUTE) private Date date; ...
警告
MILLISECOND
より低い単位の日付は@DocumentId
にはなれません。 - java.net.URI、java.net.URL
- URI と URL は文字列形式に変換されます。
- java.lang.Class
- クラスは完全修飾クラス名に変換されます。スレッドコンテキストクラスローダーは、クラスがハイドレートされたときに使用されます。
4.2.2. カスタムのブリッジ
4.2.2.1. StringBridge
Object
と String
のブリッジの実装を提供します。これを行うには org.hibernate.search.bridge.StringBridge
インターフェースを実装する必要があります。すべての実装は同時に使用されるためスレッドセーフである必要があります。
例4.14 独自の 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;
ParameterizedBridge
インターフェースを実装し、 そのパラメータは @FieldBridge
アノテーションで渡されます。
例4.15 ブリッジ実装にパラメータを渡す
public class PaddedIntegerBridge implements StringBridge, 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(); } } //property @FieldBridge(impl = PaddedIntegerBridge.class, params = @Parameter(name="padding", value="10") ) private Integer length;
ParameterizedBridge
インターフェースは StringBridge
、 TwoWayStringBridge
、 FieldBridge
の実装で実装可能です。
@DocumentId
アノテーションを付与)、 若干拡張した TwoWayStringBridge
という名前の StringBridge
バージョンを使用する必要があります。 Hibernate Search は識別子の文字列表現を読み取りそこからオブジェクトを生成する必要があります。 @FieldBridge
アノテーションの使用方法に違いはありません。
例4.16 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;
4.2.2.2. FieldBridge
FieldBridge
として実装することもできます。 このインターフェースによりプロパティ値が提供され、Lucene Document
で行いたい方法でマッピングすることができます。 このインターフェースは概念的には Hibernate UserType
に非常によく似ています。
例4.17 特定のプロパティを複数のドキュメントフィールドに格納するため 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 Field field = new Field(name + ".year", String.valueOf(year), luceneOptions.getStore(), luceneOptions.getIndex(), luceneOptions.getTermVector()); field.setBoost(luceneOptions.getBoost()); document.add(field); // set month and pad it if needed field = new Field(name + ".month", month < 10 ? "0" : "" + String.valueOf(month), luceneOptions.getStore(), luceneOptions.getIndex(), luceneOptions.getTermVector()); field.setBoost(luceneOptions.getBoost()); document.add(field); // set day and pad it if needed field = new Field(name + ".day", day < 10 ? "0" : "" + String.valueOf(day), luceneOptions.getStore(), luceneOptions.getIndex(), luceneOptions.getTermVector()); field.setBoost(luceneOptions.getBoost()); document.add(field); } } //property @FieldBridge(impl = DateSplitBridge.class) private Date date;
4.2.2.3. ClassBridge
@ClassBridge
アノテーションと @ClassBridge
アノテーションは (プロパティレベルではなく) クラスレベルで定義できます。この場合、カスタムフィールドブリッジ実装は、値パラメータとして特定のプロパティではなくエンティティインスタンスを受け取ります。この例では示されていませんが、@ClassBridge
は項 「基本的なマッピング」 で説明された termVector
属性をサポートします。
例4.18 クラスブリッジの実装
@Entity @Indexed @ClassBridge(name="branchnetwork", index=Index.TOKENIZED, 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 ); } }
CatFieldsClassBridge
が department
インスタンスに適用され、フィールドブリッジはブランチとネットワークの両方を連結し、その連結をインデックス化します。
4.3. 独自の id の提供
警告
4.3.1. ProvidedId アノテーション
例4.19 独自の id の提供
@ProvidedId (bridge = org.my.own.package.MyCustomBridge) @Indexed public class MyClass{ @Field String MyString; ... }
第5章 クエリ
FullTextSession
の作成- Lucene クエリの作成
org.hibernate.Query
を使用した Lucene クエリのラップlist()
やscroll()
などを呼び出して検索を実行
FullTextSession
を使用する必要があります。この検索固有のセッションは、通常の org.hibernate.Session
をラップしてクエリ機能とインデックス機能を提供します。
例5.1 FullTextSession の作成
Session session = sessionFactory.openSession(); ... FullTextSession fullTextSession = Search.getFullTextSession(session);
例5.2 Lucene クエリの作成
org.apache.lucene.queryParser.QueryParser parser =
new QueryParser("title", new StopAnalyzer() );
org.apache.lucene.search.Query luceneQuery = parser.parse( "summary:Festina Or brand:Seiko" );
org.hibernate.Query fullTextQuery = fullTextSession.createFullTextQuery( luceneQuery );
List result = fullTextQuery.list(); //return a list of managed objects
org.hibernate.Query
です。つまり、他の Hibernate クエリ機能 (HQL、Native、または Criteria) と同じパラダイムに属しています。通常の list()
、uniqueResult()
、iterate()
、および scroll()
メソッドを使用できます。
例5.3 JPA API を使用した検索クエリの作成
EntityManager em = entityManagerFactory.createEntityManager();
FullTextEntityManager fullTextEntityManager =
org.hibernate.hibernate.search.jpa.Search.getFullTextEntityManager(em);
...
org.apache.lucene.queryParser.QueryParser parser =
new QueryParser("title", new StopAnalyzer() );
org.apache.lucene.search.Query luceneQuery = parser.parse( "summary:Festina Or brand:Seiko" );
javax.persistence.Query fullTextQuery = fullTextEntityManager.createFullTextQuery( luceneQuery );
List result = fullTextQuery.getResultList(); //return a list of managed objects
FullTextQuery
が取得される方法を調整することによって簡単に書き換えることができます。
5.1. クエリの構築
org.hibernate.Query
を主なクエリ操作 API として使用してクエリ処理をラップします。
5.1.1. Lucene クエリの構築
5.1.2. Hibernate Search クエリの構築
5.1.2.1. 概要
例5.4 Hibernate Query への Lucene クエリのラップ
FullTextSession fullTextSession = Search.getFullTextSession( session ); org.hibernate.Query fullTextQuery = fullTextSession.createFullTextQuery( luceneQuery );
例5.5 エンティティタイプによる検索結果のフィルタリング
org.hibernate.Query fullTextQuery = fullTextSession.createFullTextQuery( luceneQuery, Customer.class ); // or fullTextQuery = fullTextSession.createFullTextQuery( luceneQuery, Item.class, Actor.class );
Customer
だけを返し、2 つ目の例は一致する Actor
と Item
を返します。タイプの制限は完全にポリモーフィックです。つまり、基本クラス Person
の、2 つのインデックス化されたサブクラスSalesman
と Customer
が存在する場合、結果タイプに基づいてフィルタするには Person.class
を指定します。
5.1.2.2. ページ処理
例5.6 検索クエリのページ処理の定義
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()
を使用すると、ページ処理に関係なく一致するエレメントの合計数を取得できます。
5.1.2.3. ソート
例5.7 結果をソートするための Lucene Sort
の指定
org.hibernate.search.FullTextQuery query = s.createFullTextQuery( query, Book.class );
org.apache.lucene.search.Sort sort = new Sort(new SortField("title"));
query.setSort(sort);
List results = query.list();
FullTextQuery
インタフェースが org.hibernate.Query
のサブインタフェースであることに気づかれるかもしれません。ソートに使用されるフィールドはトークン化しないよう注意してください。
5.1.2.4. フェッチ方針
例5.8 クエリでの FetchMode
の指定
Criteria criteria = s.createCriteria( Book.class ).setFetchMode( "authors", FetchMode.JOIN ); s.createFullTextQuery( luceneQuery ).setCriteriaQuery( criteria );
setCriteriaQuery
を使用できません。
5.1.2.5. プロジェクション
例5.9 完全なドメインオブジェクトを返す代わりにプロジェクションを使用
org.hibernate.search.FullTextQuery query = s.createFullTextQuery( luceneQuery, Book.class );
query.setProjection( "id", "summary", "body", "mainAuthor.name" );
List results = query.list();
Object[] firstResult = (Object[]) results.get(0);
Integer id = (Integer) firstResult[0];
String summary = (String) firstResult[1];
String body = (String) firstResult[2];
String authorName = (String) firstResult[3];
Object[]
のリストが返されます)。プロジェクションにより、潜在的なデータベースラウンドトリップ (クエリの応答時間が重要な場合に有用) が回避されますが、いくつかの制限が存在します。
- 予測されたプロパティはインデックス (
@Field(store=Store.YES)
) に格納する必要があります (これによりインデックスサイズが増加します)。 - 予測されたプロパティは、
org.hibernate.search.bridge.TwoWayFieldBridge
またはorg.hibernate.search.bridge.TwoWayStringBridge
(単純なバージョン) を実装するFieldBridge
を使用する必要があります。すべての Hibernate Search 組込みタイプは両方向です。 - インデックス化されたエンティティまたは組み込まれた関係の単純なプロパティのみを予測できます。つまり、組み込まれたエンティティ全体を予測できません。
- プロジェクションは、
@IndeedEmbedded
を使用してインデックス化されたコレクションまたはマップに対して機能しません。
例5.10 メタデータを取得するためにプロジェクションを使用
org.hibernate.search.FullTextQuery query = s.createFullTextQuery( luceneQuery, Book.class );
query.setProjection( FullTextQuery.SCORE, FullTextQuery.THIS, "mainAuthor.name" );
List results = query.list();
Object[] firstResult = (Object[]) results.get(0);
float score = (Float) firstResult[0];
Book book = (Book) firstResult[1];
String authorName = (String) firstResult[2];
- FullTextQuery.THIS: 初期化および管理されたエンティティを返します (予測されないクエリの場合と同様)。
- FullTextQuery.DOCUMENT: 予測されたオブジェクトに関連する Lucene Document を返します。
- FullTextQuery.OBJECT_CLASS: インデックス化されたエンティティのクラスを返します。
- FullTextQuery.SCORE: クエリのドキュメンテーションスコアを返します。スコアはある結果とクエリの他の結果を比較するのに役に立ちしますが、異なるクエリの結果を比較する場合は役に立ちません。
- FullTextQuery.ID: 予測されたオブジェクトの id プロパティ値。
- FullTextQuery.DOCUMENT_ID: Lucene ドキュメント id。Lucene ドキュメント id は 2 つの異なる IndexReader のオープン間で変わることがあることに注意してください (この機能は実験段階にあります)。
- FullTextQuery.EXPLANATION: 該当するクエリの一致するオブジェクト/ドキュメントに対する Lucene Explanation オブジェクトを返します。たくさんのデータを取得する場合は使用しないでください。通常、Explanation を返すコストは、一致するエレメントごとに Lucene クエリ全体を実行するコストと同じです。
5.2. 結果の取得
list()
、uniqueResult()
、iterate()
、scroll()
などのすべての共通の操作が利用可能です。
5.2.1. パフォーマンスに関する考慮事項
list()
または uniqueResult()
が推奨されます。エンティティ batch-size
が適切に設定された場合は、list()
が最適です。list()
、uniqueResult()
、および iterate()
を使用する場合、Hibernate Search はすべての Lucene Hits エレメントを (ページ処理内で) 処理する必要があることに注意してください。
scroll()
が適しています。作業を行ったら ScrollableResults
オブジェクトをクローズすることを忘れないでください (クローズしないと、Lucene リソースが保持されます)。scroll,
を使用し、オブジェクトをバッチでロードする場合は、query.setFetchSize()
を使用できます。オブジェクトがアクセスされ、まだロードされていない場合、Hibernate Search は 1 つのパスで次の fetchSize
をロードします。
5.2.2. 結果サイズ
- Google のような機能用 (約 888,000,000 のうちの 1-10)
- 高速なページ処理ナビゲーションの実装
- マルチステップ検索エンジンの実装 (制限されたクエリが結果をまったく返さない、または十分な結果を返さない場合に近似値を追加)
例5.11 クエリの結果サイズの決定
org.hibernate.search.FullTextQuery query = s.createFullTextQuery( luceneQuery, Book.class ); assert 3245 == query.getResultSize(); //return the number of matching books without loading a single one org.hibernate.search.FullTextQuery query = s.createFullTextQuery( luceneQuery, Book.class ); query.setMaxResults(10); List results = query.list(); assert 3245 == query.getResultSize(); //return the total number of matching books regardless of pagination
注記
5.2.3. ResultTransformer
ResultTransformer
操作ポストクエリを適用できます。
例5.12 プロジェクションとともに ResultTransformer を使用
org.hibernate.search.FullTextQuery query = s.createFullTextQuery( luceneQuery, Book.class );
query.setProjection( "title", "mainAuthor.name" );
query.setResultTransformer( new AliasToBeanResultTransformer( BookView.class ) );
List<BookView> results = (List<BookView>) query.list();
for(BookView view : results) {
log.info( "Book: " + view.getTitle() + ", " + view.getAuthor() );
}
ResultTransformer
実装のサンプルは Hibernate Core コードベースにあります。
5.2.4. 結果の理解
Explanation
オブジェクトへのアクセスを提供します (該当するクエリで)。このクラスは Lucene ユーザーにとっては非常に高度なものですが、オブジェクトのスコアを理解するのに役に立ちます。該当する結果の Explanation オブジェクトにアクセスするには 2 つの方法があります。
fullTextQuery.explain(int)
メソッドを使用- プロジェクションを使用
FullTextQuery.DOCUMENT_ID
定数を使用して取得できます。
警告
FullTextQuery.EXPLANATION
定数を使用して Explanation
オブジェクトを予測します。
例5.13 プロジェクションを使用して Lucene Explanation オブジェクトを取得
FullTextQuery ftQuery = s.createFullTextQuery( luceneQuery, Dvd.class )
.setProjection( FullTextQuery.DOCUMENT_ID, FullTextQuery.EXPLANATION, FullTextQuery.THIS );
@SuppressWarnings("unchecked") List<Object[]> results = ftQuery.list();
for (Object[] result : results) {
Explanation e = (Explanation) result[1];
System.out.println( e.toString() );
}
5.3. フィルタ
- セキュリティ
- 一時データ (先月のデータのみを表示するなど)
- 生成フィルタ (該当するカテゴリに制限される検索など)
- その他
例5.14 該当するクエリに対して完全テキストフィルタを有効化
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
エンティティに対するものです。これは、フィルタ定義がグローバルであり、名前が一意である必要があることを暗黙的に示しています。SearchException
は同じ名前を持つ 2 つの異なる @FullTextFilterDef
アノテーションが定義された場合にスローされます。各名前付きフィルタは実際のフィルタ実装を指定する必要があります。
例5.15 フィルタの定義と実装
@Entity @Indexed @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
は、スコアが 5 のドライバに結果セットを削減する単純な Lucene フィルタの例です。この例では、指定されたフィルタが org.apache.lucene.search.Filter
を直接実装し、引数を持たないコンストラクタを含みます。
例5.16 ファクトリパターンを使用したフィルタの作成
@Entity
@Indexed
@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);
}
}
@Factory
によりアノテートされたメソッドを探し、それを使用してフィルタインスタンスを構築します。ファクトリは引数を持たないコンストラクタを持つ必要があります。JBoss Seam に精通しているユーザーにとって、これはコンポーネントファクトリパターンに似ていますが、アノテーションは異なります。
例5.17 定義されたフィルタへのパラメータの引き渡し
fullTextQuery = s.createFullTextQuery( query, Driver.class );
fullTextQuery.enableFullTextFilter("security").setParameter( "level", 5 );
例5.18 実際のフィルタ実装でのパラメータの使用
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
オブジェクトを返すことに注意してください。返されたオブジェクトは特別なコントラクトを持ちます。キーオブジェクトは、該当する Filter
タイプが同じであり、パラメータのセットが同じである場合にのみ 2 つのキーが同じになるよう equals()
/ hashcode()
を実装する必要があります。つまり、2 つのフィルタキーは、キーが生成されるフィルタを交換できる場合にのみ同じになります。キーオブジェクトはキャッシュメカニズムでキーとして使用されます。
@Key
メソッドは以下の場合にのみ必要です。
- フィルタキャッシュシステムを有効にした場合 (デフォルトで有効)
- フィルタがパラメータを持っている場合
StandardFilterKey
実装を使用するだけで十分です。これにより、equals()
/ hashcode()
実装が同じ各パラメータとハードコードメソッドに委譲されます。
SoftReferences
に変換します。ハード参照キャッシュの制限に達したら、追加のフィルタは SoftReferences
としてキャッシュされます。ハード参照キャッシュのサイズを調整するために、hibernate.search.filter.cache_strategy.size
(デフォルトで 128) を使用します。フィルタキャッシュの上級ユーザーの場合は、独自の FilterCachingStrategy
を実装できます。クラス名は hibernate.search.filter.cache_strategy
により定義されます。
CachingWrapperFilter
の周りで IndexReader
を使用してフィルタをラップすることは一般的です。コストが高井再計算を回避するために、ラッパーは getDocIdSet(IndexReader reader)
メソッドから返された DocIdSet
をキャッシュします。計算された DocIdSet
は同じ IndexReader
インスタンスに対してのみキャッシュ可能であることに注意してください (リーダーはインスタンスが開かれたときのインデックスの状態を表すため)。ドキュメントリストは開かれた IndexReader
内で変更できません。ただし、キャッシュされた DocIdSet
を再計算する必要があるため、異なる/新しい IndexReader
インスタンスは、Document
の異なるセット上で動作します (異なるインデックスから、または単にインデックスが変更されたため)。
@FullTextFilterDef
の cache
フラグは FilterCacheModeType.INSTANCE_AND_DOCIDSETRESULTS
に設定されます。この結果、フィルタインスタンスが自動的にキャッシュされ、CachingWrapperFilter
の Hibernate 固有の実装の周りに指定されたフィルタがラップされます (org.hibernate.search.filter.CachingWrapperFilter
)。このクラス SoftReference
の Lucene のバージョンは、ハード参照数とともに使用されます (フィルタキャッシュの説明を参照)。ハード参照数は hibernate.search.filter.cache_docidresults.size
(デフォルト値は 5) を使用して調整できます。ラップの動作は @FullTextFilterDef.cache
パラメータを使用して制御できます。このパラメータには 3 つの異なる値が存在します。
値 | 定義 |
---|---|
FilterCacheModeType.NONE | Hibernate Search によりフィルタインスタンスと結果がキャッシュされません。各フィルタコールごとに、新しいフィルタインスタンスが作成されます。この設定はすぐに変わるデータセットやメモリの制約が強い環境に役に立つことがあります。 |
FilterCacheModeType.INSTANCE_ONLY | フィルタインスタンスはキャッシュされ、同時 Filter.getDocIdSet() コールで再利用されます。DocIdSet の結果はキャッシュされません。この設定は、フィルタが独自のキャッシュメカニズムを使用したり、フィルタの結果がアプリケーション固有のイベントのため動的に変化したりする場合 (両方の場合に DocIdSet キャッシュは不必要になります) に役に立ちます。 |
FilterCacheModeType.INSTANCE_AND_DOCIDSETRESULTS | フィルタインスタンスと DocIdSet 結果の両方はキャッシュされます。これはデフォルト値です。 |
- システムが対象のエンティティインデックスを頻繁に更新しない (つまり、IndexReader が頻繁に再利用される)
- フィルタの DocIdSet を計算するのにコストがかかる (クエリを実行するのにかかる時間と比較)
5.4. クエリプロセスの最適化
- Lucene クエリ自体。この主題に関する資料を参照
- ロードされたオブジェクトの数。ページ処理をまたはプロジェクション (必要な場合) を使用
- Hibernate Search が Lucene リーダーとどのように対話するか。適切な 「リーダー方針」 を定義
5.5. ネイティブ Lucene クエリ
第6章 手動インデックス化
6.1. インデックス化
FullTextSession
.index()
を使用して実行できます。
例6.1 FullTextSession.index()
を使用したエンティティのインデックス化
FullTextSession fullTextSession = Search.getFullTextSession(session);
Transaction tx = fullTextSession.beginTransaction();
for (Customer customer : customers) {
fullTextSession.index(customer);
}
tx.commit(); //index are written at commit time
OutOfMemoryException
が発生することがあります。この例外を回避するには、fullTextSession.flushToIndexes()
を使用します。fullTextSession.flushToIndexes()
が呼び出されるたびに (または、トランザクションがコミットされる場合)、バッチキューが処理され (メモリが解放される) すべてのインデックスの変更が適用されます。破棄された変更はロールバックできないことに注意してください。
注記
hibernate.search.worker.batch_size
は廃止され、より適切に制御できるこの明示的な API に取って代わりました。
hibernate.search.[default|<indexname>].indexwriter.[batch|transaction].max_buffered_docs
hibernate.search.[default|<indexname>].indexwriter.[batch|transaction].max_field_length
hibernate.search.[default|<indexname>].indexwriter.[batch|transaction].max_merge_docs
hibernate.search.[default|<indexname>].indexwriter.[batch|transaction].merge_factor
hibernate.search.[default|<indexname>].indexwriter.[batch|transaction].ram_buffer_size
hibernate.search.[default|<indexname>].indexwriter.[batch|transaction].term_index_interval
例6.2 特定のクラスを効率的にインデックス化 (インデックスの (再) 初期化に有用)
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(); //clear since the queue is processed } } transaction.commit();
6.2. パージ
FullTextSession
から実行されます。
例6.3 インデックスあkらエンティティの特定のインスタンスをパージ
FullTextSession fullTextSession = Search.getFullTextSession(session);
Transaction tx = fullTextSession.beginTransaction();
for (Customer customer : customers) {
fullTextSession.purge( Customer.class, customer.getId() );
}
tx.commit(); //index are written at commit time
purgeAll
メソッドを使用できます。この操作はパラメータおよびすべてのサブタイプとして渡されたタイプのすべてのエンティティを削除します。
例6.4 インデックスからエンティティのすべてのインスタンスをパージ
FullTextSession fullTextSession = Search.getFullTextSession(session);
Transaction tx = fullTextSession.beginTransaction();
fullTextSession.purgeAll( Customer.class );
//optionally optimize the index
//fullTextSession.getSearchFactory().optimize( Customer.class );
tx.commit(); //index are written at commit time
注記
index
、purge
、および purgeAll
はFullTextEntityManager
でも利用可能です。
第7章 インデックスの最適化
- アイドル状態のシステム上、または検索の頻度が低い場合
- 大量のインデックス変更後
7.1. 自動最適化
- 特定の数の操作 (挿入、削除)
- または特定の数のトランザクション
例7.1 自動最適化パラメータの定義
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
よりも優先されます)
7.2. 手動最適化
SearchFactory
を使用して、Lucene インデックスをプログラムで最適化 (デフラグ) できます。
例7.2 プログラムによるインデックス最適化
FullTextSession fullTextSession = Search.getFullTextSession(regularSession); SearchFactory searchFactory = fullTextSession.getSearchFactory(); searchFactory.optimize(Order.class); // or searchFactory.optimize();
Order
を保持する Lucene インデックスを最適化します。2 つ例では、すべてのインデックスを最適化します。
注記
searchFactory.optimize()
は JMS バックエンドに影響を与えます。マスターノードで最適化操作を適用する必要があります。
7.3. 最適化の調整
hibernate.search.[default|<indexname>].indexwriter.[batch|transaction].max_buffered_docs
hibernate.search.[default|<indexname>].indexwriter.[batch|transaction].max_field_length
hibernate.search.[default|<indexname>].indexwriter.[batch|transaction].max_merge_docs
hibernate.search.[default|<indexname>].indexwriter.[batch|transaction].merge_factor
hibernate.search.[default|<indexname>].indexwriter.[batch|transaction].ram_buffer_size
hibernate.search.[default|<indexname>].indexwriter.[batch|transaction].term_index_interval
第8章 高度な機能
8.1. SearchFactory
SearchFactory
オブジェクトは Hibernate Search の基礎となる Lucene リソースを追跡します。また、このオブジェクトを使用することにより、Lucene にネイティブな方法で簡単にアクセスできます。SearchFactory
は FullTextSession
からアクセスできます。
例8.1 SearchFactory
へのアクセス
FullTextSession fullTextSession = Search.getFullTextSession(regularSession); SearchFactory searchFactory = fullTextSession.getSearchFactory();
8.2. Lucene ディレクトリへのアクセス
SearchFactory
はインデックス化されたクラスごとに DirectoryProvider
を追跡します。クラスが基礎となる同じインデックスディレクトリを共有する場合は、1 つのディレクトリプロバイダをインデックス化された複数のクラスで共有できます。通常は当てはまりませんが、インデックスが共有された場合、特定のエンティティは複数の DirectoryProvider
を持つことができます (「インデックスの分割」 を参照)。
例8.2 Lucene Directory
へのアクセス
DirectoryProvider[] provider = searchFactory.getDirectoryProviders(Order.class); org.apache.lucene.store.Directory directory = provider[0].getDirectory();
Order
情報を格納する lucene インデックスを参照します。取得された Lucene ディレクトリはクローズしないでください (これは Hibernate Search の責任です)。
8.3. IndexReader の使用
IndexReader
で実行されます。Hibernate Search はパフォーマンスを最大化するためにすべてのインデックスリーダーをキャッシュします。コードはこのキャッシュされたリソースにアクセスできますが、 "good citizen" ルールに従う必要があります。
例8.3 IndexReader
へのアクセス
DirectoryProvider orderProvider = searchFactory.getDirectoryProviders(Order.class)[0]; DirectoryProvider clientProvider = searchFactory.getDirectoryProviders(Client.class)[0]; ReaderProvider readerProvider = searchFactory.getReaderProvider(); IndexReader reader = readerProvider.openReader(orderProvider, clientProvider); try { //do read-only operations on the reader } finally { readerProvider.closeReader(reader); }
IndexReader
は複数のクライアントで共有されるため、以下のルールに従う必要があります。
- indexReader.close() は呼び出さず、常に readerProvider.closeReader(reader) を呼び出します (理想的には Finally Block で)。
- 変更操作にはこの
IndexReader
を使用しないでください (例外が発生します)。読み取り/書き込みインデックスリーダーを使用する場合は、Lucene Directory オブジェクトからオープンします。
IndexReader
を使用すると、ほとんどのクエリが効率的になります。
8.4. Lucene のスコア式のカスタマイズ
org.apache.lucene.search.Similarity
を拡張することによりスコア式をカスタマイズできます。このクラスで定義された抽象メソッドはドキュメント d のクエリ q のスコアを計算する以下の式の係数に一致します。
係数 | 説明 |
---|---|
tf(t ind) | ドキュメント (d) のターム (t)のターム頻度係数。 |
idf(t) | ドキュメントのタームの頻度を反転。 |
coord(q,d) | 指定されたドキュメントで見付かったクエリタームの数に基づいたスコア係数。 |
queryNorm(q) | クエリ間のスコアを比較可能にするために使用する正規化係数。 |
t.getBoost() | フィールドブースト。 |
norm(t,d) | いくつかの (インデックス化時間) ブーストおよび長さ係数をカプセル化。 |
Similarity
の Javadoc を参照してください。
hibernate.search.similarity
を使用して Similarity
実装の完全修飾クラス名を指定してデフォルトの近似値を設定できます。デフォルト値は org.apache.lucene.search.DefaultSimilarity
です。また、@Similarity
アノテーションを使用してクラスレベルでデフォルトの近似値をオーバーライドできます。
@Entity
@Indexed
@Similarity(impl = DummySimilarity.class)
public class Book {
...
}
例として、ドキュメントにタームが現れる頻度は重要でないこととします。タームが 1 つのドキュメントのスコアは、複数のタームがあるドキュメントのスコアと同じになる必要があります。この場合、メソッド tf(float freq)
のカスタム実装は 1.0 を返す必要があります。
付録A 改訂履歴
改訂履歴 | ||||
---|---|---|---|---|
改訂 5.1.2-3.400 | 2013-10-30 | |||
| ||||
改訂 5.1.2-3 | 2012-07-18 | |||
| ||||
改訂 5.1.2-100 | Thu Dec 8 2011 | |||
| ||||
改訂 5.1.1-100 | Mon Jul 18 2011 | |||
| ||||
改訂 5.1.0-100 | Wed Sep 15 2010 | |||
|