1. 概要

この記事では、Hibernate Searchの基本とその構成方法について説明し、いくつかの簡単なクエリを実装します。

2. 休止状態検索の基本

全文検索機能を実装する必要があるときはいつでも、すでに精通しているツールを使用することは常にプラスです。

ORMにHibernateとJPAをすでに使用している場合は、HibernateSearchからわずか1歩です。

Hibernate Searchは、Javaで記述された高性能で拡張可能な全文検索エンジンライブラリであるApacheLuceneを統合します。 これは、LuceneのパワーとHibernateおよびJPAのシンプルさを兼ね備えています。

簡単に言うと、ドメインクラスに注釈を追加するだけで、ツールがデータベース/インデックスの同期などを処理します。

Hibernate Searchは、Elasticsearchの統合も提供します。 ただし、まだ実験段階であるため、ここではLuceneに焦点を当てます。

3. 構成

3.1. Mavenの依存関係

開始する前に、まず必要な依存関係pom.xmlに追加する必要があります。

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-search-orm</artifactId>
    <version>5.8.2.Final</version>
</dependency>

簡単にするために、データベースとしてH2を使用します。

<dependency>
    <groupId>com.h2database</groupId> 
    <artifactId>h2</artifactId>
    <version>1.4.196</version>
</dependency>

3.2. 構成

Luceneがインデックスを格納する場所も指定する必要があります。

これは、プロパティhibernate.search.default.directory_providerを介して実行できます。

ファイルシステムを選択します。これは、このユースケースで最も簡単なオプションです。 その他のオプションは、公式ドキュメントにリストされています。 Filesystem-master / filesystem-slaveおよびinfinispanは、ノード間でインデックスを同期する必要があるクラスター化されたアプリケーションで注目に値します。

また、インデックスが保存されるデフォルトのベースディレクトリを定義する必要があります。

hibernate.search.default.directory_provider = filesystem
hibernate.search.default.indexBase = /data/index/default

4. モデルクラス

構成が完了すると、モデルを指定する準備が整います。

JPAアノテーション@Entityおよび@Tableに加えて、@Indexedアノテーションを追加する必要があります。エンティティProductにインデックスを付けることをHibernateSearchに通知します。

その後、@ Fieldアノテーションを追加して、必要な属性を検索可能として定義する必要があります。

@Entity
@Indexed
@Table(name = "product")
public class Product {

    @Id
    private int id;

    @Field(termVector = TermVector.YES)
    private String productName;

    @Field(termVector = TermVector.YES)
    private String description;

    @Field
    private int memory;

    // getters, setters, and constructors
}

termVector = TermVector.YES 属性は、後で「MoreLikeThis」クエリに必要になります。

5. Luceneインデックスの構築

実際のクエリを開始する前に、最初にインデックスを構築するためにLuceneをトリガーする必要があります

FullTextEntityManager fullTextEntityManager 
  = Search.getFullTextEntityManager(entityManager);
fullTextEntityManager.createIndexer().startAndWait();

この最初のビルドの後、HibernateSearchはインデックスを最新の状態に保つ処理を行います。 私。 e。 通常どおり、 EntityManager を使用してエンティティを作成、操作、および削除できます。

注:エンティティがLucene によって検出され、インデックスが作成される前に、データベースに完全にコミットされていることを確認する必要があります(ちなみに、これがに最初のテストデータをインポートする理由でもあります。サンプルコードテストケースは、@Commitで注釈が付けられた専用のJUnitテストケースで提供されます。

6. クエリの作成と実行

これで、最初のクエリを作成する準備が整いました。

次のセクションでは、クエリを準備して実行するための一般的なワークフローを示します。

その後、最も重要なクエリタイプのクエリ例をいくつか作成します。

6.1. クエリを作成および実行するための一般的なワークフロー

クエリの準備と実行は、一般的に4つのステップで構成されます

ステップ1では、JPA FullTextEntityManager を取得し、そこからQueryBuilderを取得する必要があります。

FullTextEntityManager fullTextEntityManager 
  = Search.getFullTextEntityManager(entityManager);

QueryBuilder queryBuilder = fullTextEntityManager.getSearchFactory() 
  .buildQueryBuilder()
  .forEntity(Product.class)
  .get();

ステップ2では、HibernateクエリDSLを介してLuceneクエリを作成します。

org.apache.lucene.search.Query query = queryBuilder
  .keyword()
  .onField("productName")
  .matching("iphone")
  .createQuery();

ステップ3では、LuceneクエリをHibernateクエリにラップします。

org.hibernate.search.jpa.FullTextQuery jpaQuery
  = fullTextEntityManager.createFullTextQuery(query, Product.class);

最後に、ステップ4でクエリを実行します。

List<Product> results = jpaQuery.getResultList();

:デフォルトでは、Luceneは結果を関連性でソートします。

手順1、3、および4は、すべてのクエリタイプで同じです。

以下では、ステップ2、iに焦点を当てます。 e。 さまざまなタイプのクエリを作成する方法。

6.2. キーワードクエリ

最も基本的な使用例は、特定の単語の検索です。

これは、前のセクションで実際に行ったことです。

Query keywordQuery = queryBuilder
  .keyword()
  .onField("productName")
  .matching("iphone")
  .createQuery();

ここで、 keyword()は、特定の1つの単語を検索することを指定し、 onField()は、Luceneに検索場所を指示し、 matching()は検索対象を指示します。

6.3. ファジークエリ

ファジークエリはキーワードクエリと同じように機能しますが、「ファジー」の制限を定義でき、それを超えるとLuceneは2つの用語を一致として受け入れるものとします。

withEditDistanceUpTo()により、用語が他の用語からどれだけ逸脱するかを定義できます。 これは0、1、および2に設定でき、デフォルト値は2です( note :この制限はLuceneの実装によるものです)。

withPrefixLength()により、あいまいさによって無視されるプレフィックスの長さを定義できます。

Query fuzzyQuery = queryBuilder
  .keyword()
  .fuzzy()
  .withEditDistanceUpTo(2)
  .withPrefixLength(0)
  .onField("productName")
  .matching("iPhaen")
  .createQuery();

6.4. ワイルドカードクエリ

Hibernate Searchを使用すると、ワイルドカードクエリを実行することもできます。 e。 単語の一部が不明なクエリ。

このために、単一の文字には「?」を使用でき、任意の文字シーケンスには「*」を使用できます。

Query wildcardQuery = queryBuilder
  .keyword()
  .wildcard()
  .onField("productName")
  .matching("Z*")
  .createQuery();

6.5. フレーズクエリ

複数の単語を検索する場合は、フレーズクエリを使用できます。 必要に応じてphrase()および withSlop()を使用して、で正確な文またはおおよその文を探すことができます。 スロップファクターは、文で許可される他の単語の数を定義します。

Query phraseQuery = queryBuilder
  .phrase()
  .withSlop(1)
  .onField("description")
  .sentence("with wireless charging")
  .createQuery();

6.6. 単純なクエリ文字列クエリ

以前のクエリタイプでは、クエリタイプを明示的に指定する必要がありました。

ユーザーにもう少し力を与えたい場合は、単純なクエリ文字列クエリを使用できます。これにより、ユーザーは実行時に独自のクエリを定義できます

次のクエリタイプがサポートされています。

  • ブール値(および「+」を使用、または「|」を使用、「-」を使用しない)
  • プレフィックス(プレフィックス*)
  • フレーズ(「いくつかのフレーズ」)
  • 優先順位(括弧を使用)
  • ファジー(fuzy〜2)
  • フレーズクエリのnear演算子(「いくつかのフレーズ」〜3)

次の例では、ファジー、フレーズ、ブールクエリを組み合わせます。

Query simpleQueryStringQuery = queryBuilder
  .simpleQueryString()
  .onFields("productName", "description")
  .matching("Aple~2 + \"iPhone X\" + (256 | 128)")
  .createQuery();

6.7. 範囲クエリ

範囲クエリは、指定された境界の間にある値を検索します。 これは、数値、日付、タイムスタンプ、および文字列に適用できます。

Query rangeQuery = queryBuilder
  .range()
  .onField("memory")
  .from(64).to(256)
  .createQuery();

6.8. このクエリのように

最後のクエリタイプは「MoreLikeThis 」–クエリです。 このために、エンティティを提供し、 Hibernate Searchは、それぞれが類似スコアを持つ類似エンティティのリストを返します。

前述のように、この場合、モデルクラスの termVector = TermVector.YES 属性が必要です。これは、インデックス作成中に各用語の頻度を格納するようにLuceneに指示します。

これに基づいて、類似度はクエリ実行時に計算されます。

Query moreLikeThisQuery = queryBuilder
  .moreLikeThis()
  .comparingField("productName").boostedTo(10f)
  .andField("description").boostedTo(1f)
  .toEntity(entity)
  .createQuery();
List<Object[]> results = (List<Object[]>) fullTextEntityManager
  .createFullTextQuery(moreLikeThisQuery, Product.class)
  .setProjection(ProjectionConstants.THIS, ProjectionConstants.SCORE)
  .getResultList();

6.9. 複数のフィールドを検索する

これまでは、 onField()を使用して、1つの属性を検索するためのクエリのみを作成していました。

ユースケースに応じて、2つ以上の属性を検索することもできます

Query luceneQuery = queryBuilder
  .keyword()
  .onFields("productName", "description")
  .matching(text)
  .createQuery();

さらに、個別に検索する各属性を指定できます、e。 g。 1つの属性のブーストを定義する場合:

Query moreLikeThisQuery = queryBuilder
  .moreLikeThis()
  .comparingField("productName").boostedTo(10f)
  .andField("description").boostedTo(1f)
  .toEntity(entity)
  .createQuery();

6.10. クエリの組み合わせ

最後に、Hibernate Searchは、さまざまな戦略を使用したクエリの組み合わせもサポートしています。

  • SHOULD:クエリには、サブクエリの一致する要素が含まれている必要があります
  • Must:クエリには、サブクエリの一致する要素が含まれている必要があります
  • 禁止事項:クエリにサブクエリの一致する要素が含まれていてはなりません

集計はブール値のAND、OR、NOT に似ています。ただし、名前は異なり、関連性にも影響を与えることを強調しています。

たとえば、2つのクエリ間の SHOULD は、ブール値の OR:に似ています。2つのクエリのいずれかが一致する場合、この一致が返されます。

ただし、両方のクエリが一致する場合、1つのクエリのみが一致する場合と比較して、一致の関連性は高くなります。

Query combinedQuery = queryBuilder
  .bool()
  .must(queryBuilder.keyword()
    .onField("productName").matching("apple")
    .createQuery())
  .must(queryBuilder.range()
    .onField("memory").from(64).to(256)
    .createQuery())
  .should(queryBuilder.phrase()
    .onField("description").sentence("face id")
    .createQuery())
  .must(queryBuilder.keyword()
    .onField("productName").matching("samsung")
    .createQuery())
  .not()
  .createQuery();

7. 結論

この記事では、Hibernate Searchの基本について説明し、最も重要なクエリタイプを実装する方法を示しました。 より高度なトピックは、公式ドキュメントにあります。

いつものように、例の完全なソースコードは、GitHubから入手できます。