第14章 Hibernate Search
14.1. Hibernate Search の使用
14.1.1. Hibernate Search について
Hibernate Search は、Hibernate アプリケーションに完全なテキスト検索機能を提供します。これは、全文、あいまい、位置情報検索などの SQL ベースのソリューションには適していないアプリケーションの検索に適しています。Hibernate Search は Apache Lucene を全文検索エンジンとして使用しますが、メンテナンスのオーバーヘッドを最小限に抑えるように設計されています。設定が完了したら、インデックス、クラスタリング、およびデータ同期は透過的に維持されるため、ビジネス要件を満たすことができます。
14.1.2. Hibernate Search を使用する最初の手順
アプリケーションの Hibernate Search の使用を開始するには、以下のトピックに従います。
- Hibernate Search を設定するには、JBoss EAP 『管理および設定ガイド』 の 『設定』 を参照してください。
14.1.3. Maven を使用した Hibernate Search の有効化
Maven プロジェクトで以下の設定を使用し、
hibernate-search-orm
依存関係を追加します。
<dependencyManagement> <dependencies> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-search-orm</artifactId> <version>4.6.0.Final-redhat-2</version> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-search-orm</artifactId> <scope>provided</scope> </dependency> </dependencies>
14.1.4. アノテーションの追加
ここでは、書籍の詳細を含むデータベースの例を見てみましょう。アプリケーションに Hibernate 管理クラス
example.Book
および example.Author
が含まれ、アプリケーションにフリーテキスト検索機能を追加して、書籍の検索を有効にする場合。
例14.1 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
を指定の解決が含まれる String
に変換する DateBridge
が含まれます。詳細は、「ブリッジ」 を参照してください。
これにより、
@IndexedEmbedded が残ります。
このアノテーションは、所有するエンティティーの一部として、関連付けられたエンティティー (@ ManyToMany
、@ * ToOne
、@ Embedded、および @ElementCollection
)
にインデックスを付けるために使用されます。これは、Lucene インデックスドキュメントがオブジェクト関係について不明なフラットデータ構造であるため必要になります。作成者の名前を確実に検索できるようにするには、名前を書籍の一部としてインデックス化する必要があります。@IndexedEmbedded
のほかに、インデックスに含める関連エンティティーのフィールドすべてを @Indexed
でマークする必要があります。詳細は、「埋め込みおよび関連オブジェクト」 を参照してください。
現在、これらの設定で十分です。エンティティーマッピングの詳細については、を参照してください。「エンティティーのマッピング」。
例14.2 Hibernate Search アノテーションの追加後のエンティティー
package example; ... @Entity @Indexed public class Book { @Id @GeneratedValue private Integer id; @Field(index=Index.YES, analyze=Analyze.YES, store=Store.NO) private String title; @Field(index=Index.YES, analyze=Analyze.YES, store=Store.NO) private String subtitle; @Field(index = Index.YES, analyze=Analyze.NO, store = Store.YES) @DateBridge(resolution = Resolution.DAY) private Date publicationDate; @IndexedEmbedded @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;
@Field
private String name;
public Author() {
}
// standard getters/setters follow here
...
}
14.1.5. インデックス化
Hibernate Search は、Hibernate Core を介して永続化、更新、または削除されるすべてのエンティティーを透過的にインデックス化します。ただし、データベースにすでに存在するデータの初期 Lucene インデックスを作成する必要があります。上記のプロパティーとアノテーションを追加したら、book の初期バッチインデックスをトリガーする時間になります。これは、以下のコードスニペットのいずれかを使用して実行できます (「インデックスの再構築」 を参照してください)。
例14.3 Hibernate セッションを使用してデータにインデックスを付ける
FullTextSession fullTextSession = org.hibernate.search.Search.getFullTextSession(session); fullTextSession.createIndexer().startAndWait();
例14.4 JPA を使用したデータのインデックス作成
EntityManager em = entityManagerFactory.createEntityManager(); FullTextEntityManager fullTextEntityManager = org.hibernate.search.jpa.Search.getFullTextEntityManager(em); fullTextEntityManager.createIndexer().startAndWait();
上記のコードを実行した後は、
/var/lucene/indexes/example.Book
に Lucene インデックスが表示されるはずです。Luke でこのインデックスを調べてください。HibernateSearch がどのように機能するかを理解するのに役立ちます。
14.1.6. 検索
検索を実行するには、Lucene API (「Lucene API を使用した Lucene クエリーの構築」) または Hibernate Search query DSL (「Lucene クエリーの構築」) のいずれかを使用して Lucene クエリーを作成します。
org.hibernate.Query
にクエリーをラップし、Hibernate API から必要な機能を取得します。以下のコードは、インデックスフィールドに対するクエリーを準備します。コードを実行すると、Book
の一覧が返されます。
例14.5 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();
例14.6 JPA を使用した検索の作成と実行
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();
14.1.7. アナライザー
インデックス化された Book エンティティーのタイトルが
Refactoring: Improving the Design of Existing Code
で、refactor
、refactors
、refactored
、refactoring
のクエリーに必要だとします。インデックス化および検索を行う際にワードステミングを適用する Lucene でアナライザークラスを選択します。Hibernate Search は、アナライザーを設定するためのいくつかの方法を提供します (を参照)。「デフォルトの Analyzer とクラスによる Analyzer」詳細については):
- 設定ファイルに
analyzer
プロパティーを設定します。指定されたクラスがデフォルトのアナライザーになります。 - エンティティーレベルで
アノテーションを設定します。@Analyzer
- フィールドレベルで
@
アノテーションを設定します。Analyzer
完全修飾クラス名または使用するアナライザーを指定するか、
@Analyzer
アノテーションとともに @AnalyzerDef
アノテーションで定義されているアナライザーを確認します。Solr アナライザーフレームワークとそのファクトリーは、後者のオプションに使用されます。ファクトリークラスの詳細は、Solr JavaDoc を参照するか、Solr Wiki (http://wiki.apache.org/solr/AnalyzersTokenizersTokenFilters) の該当するセクションを参照してください。
この例では、
StandardTokenizerFactory
が、LowerCaseFilterFactory
と SnowballPorterFilterFactory
の 2 つのフィルターファクトリーによって使用されています。トークンライザーは、英数字とハイフンで単語を分割しますが、メールアドレスとインターネットのホスト名は維持します。標準トークンは、これおよびその他の一般的な操作に適しています。小文字フィルターはトークンのすべての文字を小文字に変換し、snowball フィルターは言語固有のステミングを適用します。
Solr フレームワークを使用する場合は、任意の数のフィルターでトークンを使用します。
例14.7 @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
プロパティーのみに適用されます。アナライザーの定義はグローバルです。エンティティーのアナライザーを定義し、必要に応じて他のエンティティーの定義を再利用します。