Pengenalan Pencarian Hibernate

1. Gambaran keseluruhan

Dalam artikel ini, kami akan membincangkan asas-asas Pencarian Hibernate, cara mengkonfigurasinya, dan kami akan melaksanakan beberapa pertanyaan mudah.

2. Asas Pencarian Hibernate

Bila-bila masa kita mesti melaksanakan fungsi carian teks penuh, menggunakan alat yang sudah kita mahir selalu menjadi kelebihan.

Sekiranya kita sudah menggunakan Hibernate dan JPA untuk ORM, kita hanya tinggal satu langkah lagi dari Hibernate Search.

Hibernate Search mengintegrasikan Apache Lucene, sebuah pustaka enjin carian teks penuh berprestasi tinggi dan boleh dikembangkan yang ditulis di Java . Ini menggabungkan kekuatan Lucene dengan kesederhanaan Hibernate dan JPA.

Ringkasnya, kita hanya perlu menambahkan beberapa anotasi tambahan ke kelas domain kita, dan alat ini akan menguruskan perkara-perkara seperti penyegerakan pangkalan data / indeks.

Hibernate Search juga menyediakan penyatuan Elasticsearch; namun, kerana masih dalam peringkat eksperimen, kami akan memberi tumpuan kepada Lucene di sini.

3. Konfigurasi

3.1. Ketergantungan Maven

Sebelum memulakan, pertama kita perlu menambahkan kebergantungan yang diperlukan pada pom.xml kami :

 org.hibernate hibernate-search-orm 5.8.2.Final 

Demi kesederhanaan, kami akan menggunakan H2 sebagai pangkalan data kami:

 com.h2database h2 1.4.196 

3.2. Konfigurasi

Kita juga harus menentukan di mana Lucene harus menyimpan indeks.

Ini boleh dilakukan melalui hibernate.search.default.directory_provider milik .

Kami akan memilih sistem fail , yang merupakan pilihan paling mudah untuk kes penggunaan kami. Lebih banyak pilihan disenaraikan dalam dokumentasi rasmi. Filesystem-master / filesystem-slave dan infinispan penting untuk aplikasi berkelompok, di mana indeks harus diselaraskan antara nod.

Kita juga harus menentukan direktori asas lalai di mana indeks akan disimpan:

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

4. Kelas Model

Selepas konfigurasi, kami kini bersedia untuk menentukan model kami.

Di atas anotasi JPA @Entity dan @Table , kita harus menambahkan anotasi @Indexed . Ia memberitahu Hibernate Search bahawa entiti Produk harus diindeks.

Selepas itu, kita harus menentukan atribut yang diperlukan sebagai dicari dengan menambahkan anotasi @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 }

The termVector = TermVector.YES atribut diperlukan untuk "Lagi Like This" Pertanyaan kemudian.

5. Membina Indeks Lucene

Sebelum memulakan pertanyaan sebenar, kita harus mencetuskan Lucene untuk membina indeks pada mulanya :

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

Selepas binaan awal ini, Hibernate Search akan menjaga indeksnya terkini . I. e. kita boleh membuat, memanipulasi dan menghapus entiti melalui EntityManager seperti biasa.

Catatan: kita harus memastikan bahawa entiti benar-benar komited pada pangkalan data sebelum dapat ditemui dan diindeks oleh Lucene (omong-omong, ini juga menjadi sebab mengapa import data ujian awal dalam kes ujian kod contoh kami datang dalam JUnit khusus kes ujian, dianotasi dengan @Commit ).

6. Membina dan Melaksanakan Pertanyaan

Sekarang, kami bersedia untuk membuat pertanyaan pertama kami.

Pada bahagian berikut, kami akan menunjukkan aliran kerja umum untuk menyiapkan dan melaksanakan pertanyaan.

Selepas itu, kami akan membuat beberapa contoh pertanyaan untuk jenis pertanyaan yang paling penting.

6.1. Aliran Kerja Umum untuk Membuat dan Melaksanakan Pertanyaan

Menyiapkan dan melaksanakan pertanyaan secara umum terdiri daripada empat langkah :

Pada langkah 1, kita harus mendapatkan JPA FullTextEntityManager dan dari itu QueryBuilder :

FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager); QueryBuilder queryBuilder = fullTextEntityManager.getSearchFactory() .buildQueryBuilder() .forEntity(Product.class) .get();

Pada langkah 2, kami akan membuat pertanyaan Lucene melalui pertanyaan Hibernate DSL:

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

Pada langkah 3, kami akan membungkus pertanyaan Lucene menjadi pertanyaan Hibernate:

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

Akhirnya, pada langkah 4 kami akan melaksanakan pertanyaan:

List results = jpaQuery.getResultList();

Nota : secara lalai, Lucene menyusun keputusan mengikut kesesuaian.

Langkah 1, 3 dan 4 adalah sama untuk semua jenis pertanyaan.

Berikut ini, kita akan memfokus pada langkah 2, iaitu bagaimana membuat pelbagai jenis pertanyaan.

6.2. Pertanyaan Kata Kunci

Kes penggunaan paling asas adalah mencari kata tertentu .

Inilah yang sebenarnya telah kita lakukan di bahagian sebelumnya:

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

Here, keyword() specifies that we are looking for one specific word, onField() tells Lucene where to look and matching() what to look for.

6.3. Fuzzy Queries

Fuzzy queries are working like keyword queries, except that we can define a limit of “fuzziness”, above which Lucene shall accept the two terms as matching.

By withEditDistanceUpTo(), we can define how much a term may deviate from the other. It can be set to 0, 1, and 2, whereby the default value is 2 (note: this limitation is coming from the Lucene's implementation).

By withPrefixLength(), we can define the length of the prefix which shall be ignored by the fuzziness:

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

6.4. Wildcard Queries

Hibernate Search also enables us to execute wildcard queries, i. e. queries for which a part of a word is unknown.

For this, we can use “?” for a single character, and “*” for any character sequence:

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

6.5. Phrase Queries

If we want to search for more than one word, we can use phrase queries. We can either look for exact or for approximate sentences, using phrase() and withSlop(), if necessary. The slop factor defines the number of other words permitted in the sentence:

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

6.6. Simple Query String Queries

With the previous query types, we had to specify the query type explicitly.

If we want to give some more power to the user, we can use simple query string queries: by that, he can define his own queries at runtime.

The following query types are supported:

  • boolean (AND using “+”, OR using “|”, NOT using “-“)
  • prefix (prefix*)
  • phrase (“some phrase”)
  • precedence (using parentheses)
  • fuzzy (fuzy~2)
  • near operator for phrase queries (“some phrase”~3)

The following example would combine fuzzy, phrase and boolean queries:

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

6.7. Range Queries

Range queries search for avalue in between given boundaries. This can be applied to numbers, dates, timestamps, and strings:

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

6.8. More Like This Queries

Our last query type is the “More Like This” – query. For this, we provide an entity, and Hibernate Search returns a list with similar entities, each with a similarity score.

As mentioned before, the termVector = TermVector.YES attribute in our model class is required for this case: it tells Lucene to store the frequency for each term during indexing.

Based on this, the similarity will be calculated at query execution time:

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

6.9. Searching More Than One Field

Until now, we only created queries for searching one attribute, using onField().

Depending on the use case, we can also search two or more attributes:

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

Moreover, we can specify each attribute to be searched separately, e. g. if we want to define a boost for one attribute:

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

6.10. Combining Queries

Finally, Hibernate Search also supports combining queries using various strategies:

  • SHOULD: the query should contain the matching elements of the subquery
  • MUST: the query must contain the matching elements of the subquery
  • MUST NOT: the query must not contain the matching elements of the subquery

The aggregations are similar to the boolean ones AND, OR and NOT. However, the names are different to emphasize that they also have an impact on the relevance.

Sebagai contoh, HARUS di antara dua pertanyaan serupa dengan boolean ATAU: jika salah satu daripada dua pertanyaan mempunyai kecocokan, padanan ini akan dikembalikan.

Walau bagaimanapun, jika kedua-dua pertanyaan sesuai, perlawanan akan mempunyai kaitan yang lebih tinggi berbanding jika hanya satu pertanyaan yang sepadan:

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. Kesimpulannya

Dalam artikel ini, kami membincangkan asas-asas Pencarian Hibernate dan menunjukkan cara melaksanakan jenis pertanyaan yang paling penting. Topik yang lebih maju boleh didapati sebagai dokumentasi rasmi.

Seperti biasa, kod sumber penuh contoh terdapat di GitHub.