Use Hibernate Search with Hibernate ORM and Elasticsearch/OpenSearch

您有一个基于 Hibernate ORM 的应用程序?您想为用户提供功能齐全的全文搜索功能?您来对地方了。

You have a Hibernate ORM-based application? You want to provide a full-featured full-text search to your users? You’re at the right place.

通过本指南,您将了解如何使用 Hibernate Search,在 Elasticsearch 或 OpenSearch 集群中同步您的实体。我们还将探讨如何使用 Hibernate Search API 查询您的 Elasticsearch 或 OpenSearch 集群。

With this guide, you’ll learn how to synchronize your entities to an Elasticsearch or OpenSearch cluster in a heartbeat with Hibernate Search. We will also explore how you can query your Elasticsearch or OpenSearch cluster using the Hibernate Search API.

如果您想为非 Hibernate ORM 实体建立索引,请参见 this dedicated guide 代替。

If you want to index entities that are not Hibernate ORM entities, see this dedicated guide instead.

Prerequisites

Unresolved directive in hibernate-search-orm-elasticsearch.adoc - include::{includes}/prerequisites.adoc[]

Architecture

本指南中介绍的应用程序允许管理一个(简单的)库:您负责管理作者及其书籍。

The application described in this guide allows to manage a (simple) library: you manage authors and their books.

这些实体存储在 PostgreSQL 数据库中,并编入 Elasticsearch 集群的索引中。

The entities are stored in a PostgreSQL database and indexed in an Elasticsearch cluster.

Solution

我们建议您遵循接下来的部分中的说明,按部就班地创建应用程序。然而,您可以直接跳到完成的示例。

We recommend that you follow the instructions in the next sections and create the application step by step. However, you can go right to the completed example.

克隆 Git 存储库: git clone {quickstarts-clone-url},或下载 {quickstarts-archive-url}[存档]。

Clone the Git repository: git clone {quickstarts-clone-url}, or download an {quickstarts-archive-url}[archive].

该解决方法位于 hibernate-search-orm-elasticsearch-quickstart directory 中。

The solution is located in the hibernate-search-orm-elasticsearch-quickstart directory.

提供的解决方法包含一些附加元素,例如测试和测试基础设施。

The provided solution contains a few additional elements such as tests and testing infrastructure.

Creating the Maven project

首先,我们需要一个新项目。使用以下命令创建一个新项目:

First, we need a new project. Create a new project with the following command:

Unresolved directive in hibernate-search-orm-elasticsearch.adoc - include::{includes}/devtools/create-app.adoc[]

此命令生成一个 Maven 结构,该结构导入以下扩展:

This command generates a Maven structure importing the following extensions:

  • Hibernate ORM with Panache,

  • the PostgreSQL JDBC driver,

  • Hibernate Search + Elasticsearch,

  • Quarkus REST (formerly RESTEasy Reactive) and Jackson.

如果您已配置好 Quarkus 项目,则可以通过在项目基本目录中运行以下命令,将 hibernate-search-orm-elasticsearch 扩展添加到您的项目:

If you already have your Quarkus project configured, you can add the hibernate-search-orm-elasticsearch extension to your project by running the following command in your project base directory:

Unresolved directive in hibernate-search-orm-elasticsearch.adoc - include::{includes}/devtools/extension-add.adoc[]

这会将以下内容添加到您的 pom.xml

This will add the following to your pom.xml:

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-hibernate-search-orm-elasticsearch</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-hibernate-search-orm-elasticsearch")

Creating the bare entities

首先,让我们在 model 子包中创建我们的 Hibernate ORM 实体 BookAuthor

First, let’s create our Hibernate ORM entities Book and Author in the model subpackage.

package org.acme.hibernate.search.elasticsearch.model;

import java.util.List;
import java.util.Objects;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.OneToMany;

import io.quarkus.hibernate.orm.panache.PanacheEntity;

@Entity
public class Author extends PanacheEntity { (1)

    public String firstName;

    public String lastName;

    @OneToMany(mappedBy = "author", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER) (2)
    public List<Book> books;

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof Author)) {
            return false;
        }

        Author other = (Author) o;

        return Objects.equals(id, other.id);
    }

    @Override
    public int hashCode() {
        return 31;
    }
}
1 We are using Hibernate ORM with Panache, it is not mandatory.
2 We are loading these elements eagerly so that they are present in the JSON output. In a real world application, you should probably use a DTO approach.
package org.acme.hibernate.search.elasticsearch.model;

import java.util.Objects;

import jakarta.persistence.Entity;
import jakarta.persistence.ManyToOne;

import com.fasterxml.jackson.annotation.JsonIgnore;

import io.quarkus.hibernate.orm.panache.PanacheEntity;

@Entity
public class Book extends PanacheEntity {

    public String title;

    @ManyToOne
    @JsonIgnore 1
    public Author author;

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof Book)) {
            return false;
        }

        Book other = (Book) o;

        return Objects.equals(id, other.id);
    }

    @Override
    public int hashCode() {
        return 31;
    }
}
1 We mark this property with @JsonIgnore to avoid infinite loops when serializing with Jackson.

Initializing the REST service

虽然尚未为我们的 REST 服务设置所有内容,但我们可以使用我们需要的标准 CRUD 操作对其进行初始化。

While everything is not yet set up for our REST service, we can initialize it with the standard CRUD operations we will need.

创建 org.acme.hibernate.search.elasticsearch.LibraryResource 类:

Create the org.acme.hibernate.search.elasticsearch.LibraryResource class:

package org.acme.hibernate.search.elasticsearch;

import java.util.List;
import java.util.Optional;

import jakarta.enterprise.event.Observes;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.core.MediaType;

import org.acme.hibernate.search.elasticsearch.model.Author;
import org.acme.hibernate.search.elasticsearch.model.Book;
import org.hibernate.search.mapper.orm.session.SearchSession;
import org.jboss.resteasy.reactive.RestForm;
import org.jboss.resteasy.reactive.RestQuery;

import io.quarkus.runtime.StartupEvent;

@Path("/library")
public class LibraryResource {

    @PUT
    @Path("book")
    @Transactional
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    public void addBook(@RestForm String title, @RestForm Long authorId) {
        Author author = Author.findById(authorId);
        if (author == null) {
            return;
        }

        Book book = new Book();
        book.title = title;
        book.author = author;
        book.persist();

        author.books.add(book);
        author.persist();
    }

    @DELETE
    @Path("book/{id}")
    @Transactional
    public void deleteBook(Long id) {
        Book book = Book.findById(id);
        if (book != null) {
            book.author.books.remove(book);
            book.delete();
        }
    }

    @PUT
    @Path("author")
    @Transactional
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    public void addAuthor(@RestForm String firstName, @RestForm String lastName) {
        Author author = new Author();
        author.firstName = firstName;
        author.lastName = lastName;
        author.persist();
    }

    @POST
    @Path("author/{id}")
    @Transactional
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    public void updateAuthor(Long id, @RestForm String firstName, @RestForm String lastName) {
        Author author = Author.findById(id);
        if (author == null) {
            return;
        }
        author.firstName = firstName;
        author.lastName = lastName;
        author.persist();
    }

    @DELETE
    @Path("author/{id}")
    @Transactional
    public void deleteAuthor(Long id) {
        Author author = Author.findById(id);
        if (author != null) {
            author.delete();
        }
    }
}

这里没有特别之处:它只是在 REST 服务中使用 Panache 操作的传统 Hibernate ORM。

Nothing out of the ordinary here: it is just good old Hibernate ORM with Panache operations in a REST service.

事实上,有趣的部分是我们只需添加极少的元素,即可使我们的全文搜索应用程序正常工作。

In fact, the interesting part is that we will need to add very few elements to make our full text search application working.

Using Hibernate Search annotations

让我们回到我们的实体。

Let’s go back to our entities.

为它们启用全文搜索功能就像添加一些批注一样简单。

Enabling full text search capabilities for them is as simple as adding a few annotations.

我们再次编辑 Book 实体,包含此内容:

Let’s edit the Book entity again to include this content:

package org.acme.hibernate.search.elasticsearch.model;

import java.util.Objects;

import jakarta.persistence.Entity;
import jakarta.persistence.ManyToOne;

import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed;

import com.fasterxml.jackson.annotation.JsonIgnore;

import io.quarkus.hibernate.orm.panache.PanacheEntity;

@Entity
@Indexed (1)
public class Book extends PanacheEntity {

    @FullTextField(analyzer = "english") (2)
    public String title;

    @ManyToOne
    @JsonIgnore
    public Author author;

    // Preexisting equals()/hashCode() methods
}
1 First, let’s use the @Indexed annotation to register our Book entity as part of the full text index.
2 The @FullTextField annotation declares a field in the index specifically tailored for full text search. In particular, we have to define an analyzer to split and analyze the tokens (~ words) - more on this later.

既然已经对我们的书进行了索引,我们可以对作者进行相同的操作。

Now that our books are indexed, we can do the same for the authors.

打开 Author 类并包含以下内容。

Open the Author class and include the content below.

这里的情况非常相似:我们使用的是 @Indexed@FullTextField@KeywordField 批注。

Things are quite similar here: we use the @Indexed, @FullTextField and @KeywordField annotations.

不过也有一些差异/添加。我们来检查一下。

There are a few differences/additions though. Let’s check them out.

package org.acme.hibernate.search.elasticsearch.model;

import java.util.List;
import java.util.Objects;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.OneToMany;

import org.hibernate.search.engine.backend.types.Sortable;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.IndexedEmbedded;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.KeywordField;

import io.quarkus.hibernate.orm.panache.PanacheEntity;

@Entity
@Indexed
public class Author extends PanacheEntity {

    @FullTextField(analyzer = "name") (1)
    @KeywordField(name = "firstName_sort", sortable = Sortable.YES, normalizer = "sort") (2)
    public String firstName;

    @FullTextField(analyzer = "name")
    @KeywordField(name = "lastName_sort", sortable = Sortable.YES, normalizer = "sort")
    public String lastName;

    @OneToMany(mappedBy = "author", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
    @IndexedEmbedded (3)
    public List<Book> books;

    // Preexisting equals()/hashCode() methods
}
1 We use a @FullTextField similar to what we did for Book but you’ll notice that the analyzer is different - more on this later.
2 As you can see, we can define several fields for the same property. Here, we define a @KeywordField with a specific name. The main difference is that a keyword field is not tokenized (the string is kept as one single token) but can be normalized (i.e. filtered) - more on this later. This field is marked as sortable as our intention is to use it for sorting our authors.
3 The purpose of @IndexedEmbedded is to include the Book fields into the Author index. In this case, we just use the default configuration: all the fields of the associated Book entities are included in the index (i.e. the title field). The nice thing with @IndexedEmbedded is that it is able to automatically reindex an Author if one of its Book`s has been updated thanks to the bidirectional relation. `@IndexedEmbedded also supports nested documents (using the structure = NESTED attribute), but we don’t need it here. You can also specify the fields you want to embed in your parent index using the includePaths/excludePaths attributes if you don’t want them all.

Analyzers and normalizers

Introduction

分析是全文搜索中的重要部分:它定义了在建立索引或搜索查询时文本的处理方式。

Analysis is a big part of full text search: it defines how text will be processed when indexing or building search queries.

分析器的作用是将文本拆分为标记(~ 词)并过滤它们(例如,将它们全部添加为小写并移除重音)。

The role of analyzers is to split the text into tokens (~ words) and filter them (making it all lowercase and removing accents for instance).

标准化器是一种特殊类型的分析器,它将输入保持为单个标记。它尤其适用于对关键字进行排序或编制索引。

Normalizers are a special type of analyzers that keeps the input as a single token. It is especially useful for sorting or indexing keywords.

有很多捆绑分析器,但你也可以针对你自己的特定目的开发自己的分析器。

There are a lot of bundled analyzers, but you can also develop your own for your own specific purposes.

你可以在 Analysis section of the Elasticsearch documentation 中了解有关 Elasticsearch 分析框架的更多信息。

You can learn more about the Elasticsearch analysis framework in the Analysis section of the Elasticsearch documentation.

Defining the analyzers used

在将 Hibernate Search 批注添加到我们的实体时,我们定义了使用的分析器和标准化器。通常情况下:

When we added the Hibernate Search annotations to our entities, we defined the analyzers and normalizers used. Typically:

@FullTextField(analyzer = "english")
@FullTextField(analyzer = "name")
@KeywordField(name = "lastName_sort", sortable = Sortable.YES, normalizer = "sort")

我们使用:

We use:

  • an analyzer called name for person names,

  • an analyzer called english for book titles,

  • a normalizer called sort for our sort fields

但我们尚未设置它们。

but we haven’t set them up yet.

让我们看看你可以借助 Hibernate Search 如何实现它。

Let’s see how you can do it with Hibernate Search.

Setting up the analyzers

这是一项简单的任务,我们只需创建一个 ElasticsearchAnalysisConfigurer 的实现(稍后详细介绍,配置 Quarkus 以便使用它)。

It is an easy task, we just need to create an implementation of ElasticsearchAnalysisConfigurer (and configure Quarkus to use it, more on that later).

为了满足我们的要求,我们来创建以下实现:

To fulfill our requirements, let’s create the following implementation:

package org.acme.hibernate.search.elasticsearch.config;

import org.hibernate.search.backend.elasticsearch.analysis.ElasticsearchAnalysisConfigurationContext;
import org.hibernate.search.backend.elasticsearch.analysis.ElasticsearchAnalysisConfigurer;

import io.quarkus.hibernate.search.orm.elasticsearch.SearchExtension;

@SearchExtension (1)
public class AnalysisConfigurer implements ElasticsearchAnalysisConfigurer {

    @Override
    public void configure(ElasticsearchAnalysisConfigurationContext context) {
        context.analyzer("name").custom() (2)
                .tokenizer("standard")
                .tokenFilters("asciifolding", "lowercase");

        context.analyzer("english").custom() (3)
                .tokenizer("standard")
                .tokenFilters("asciifolding", "lowercase", "porter_stem");

        context.normalizer("sort").custom() (4)
                .tokenFilters("asciifolding", "lowercase");
    }
}
1 Annotate the configurer implementation with the @SearchExtension qualifier to tell Quarkus it should be used in the default persistence unit, for all Elasticsearch indexes (by default).[.iokays-translated-83411b1d7cf717720744ce01034174a9] 此注释还可以针对特定的持久化单元 (@SearchExtension(persistenceUnit = "nameOfYourPU"))、后端 (@SearchExtension(backend = "nameOfYourBackend"))、索引 (@SearchExtension(index = "nameOfYourIndex"))或它们的组合 (@SearchExtension(persistenceUnit = "nameOfYourPU", backend = "nameOfYourBackend", index = "nameOfYourIndex"))。

The annotation can also target a specific persistence unit (@SearchExtension(persistenceUnit = "nameOfYourPU")), backend (@SearchExtension(backend = "nameOfYourBackend")), index (@SearchExtension(index = "nameOfYourIndex")), or a combination of those (@SearchExtension(persistenceUnit = "nameOfYourPU", backend = "nameOfYourBackend", index = "nameOfYourIndex")). <1> This is a simple analyzer separating the words on spaces, removing any non-ASCII characters by its ASCII counterpart (and thus removing accents) and putting everything in lowercase. It is used in our examples for the author’s names. <1> We are a bit more aggressive with this one and we include some stemming: we will be able to search for mystery and get a result even if the indexed input contains mysteries. It is definitely too aggressive for person names, but it is perfect for the book titles. <1> Here is the normalizer used for sorting. Very similar to our first analyzer, except we don’t tokenize the words as we want one and only one token.

或者,如果出于某种原因你无法或不希望使用 @SearchExtension 为你的分析配置器添加注释,则只需使用 @Dependent @Named("myAnalysisConfigurer") 为其添加注释,然后从配置属性中引用它:

Alternatively, if for some reason you can’t or don’t want to annotate your analysis configurer with @SearchExtension, you can simply annotate it with @Dependent @Named("myAnalysisConfigurer") and then reference it from configuration properties:

quarkus.hibernate-search-orm.elasticsearch.analysis.configurer=bean:myAnalysisConfigurer

有关配置分析器的更多信息,请参阅 this section of the reference documentation

For more information about configuring analyzers, see this section of the reference documentation.

Adding full text capabilities to our REST service

在我们现有的 LibraryResource 中,我们只需要注入 SearchSession

In our existing LibraryResource, we just need to inject the SearchSession:

    @Inject
    SearchSession searchSession; (1)
1 Inject a Hibernate Search session, which relies on the EntityManager under the hood. Applications with multiple persistence units can use the CDI qualifier @io.quarkus.hibernate.orm.PersistenceUnit to select the right one: see CDI integration.

然后神奇的事情就发生了。当我们为实体添加注释时,我们使用户能够进行全文搜索;现在,我们可以通过添加以下方法(和一些 import)来使用 Hibernate Search DSL 查询索引:

And then the magic begins. When we added annotations to our entities, we made them available for full text search; we can now query the index using the Hibernate Search DSL, simply by adding the following method (and a few `import`s):

    @GET
    @Path("author/search")
    @Transactional (1)
    public List<Author> searchAuthors(@RestQuery String pattern, (2)
            @RestQuery Optional<Integer> size) {
        return searchSession.search(Author.class) (3)
                .where(f ->
                    pattern == null || pattern.trim().isEmpty() ?
                        f.matchAll() : (4)
                        f.simpleQueryString()
                                .fields("firstName", "lastName", "books.title").matching(pattern) (5)
                )
                .sort(f -> f.field("lastName_sort").then().field("firstName_sort")) (6)
                .fetchHits(size.orElse(20)); (7)
    }
1 Important point: we need a transactional context for this method.
2 Use the org.jboss.resteasy.reactive.RestQuery annotation type to avoid repeating the parameter name.
3 We indicate that we are searching for `Author`s.
4 We create a predicate: if the pattern is empty, we use a matchAll() predicate.
5 If we have a valid pattern, we create a simpleQueryString() predicate on the firstName, lastName and books.title fields matching our pattern.
6 We define the sort order of our results. Here we sort by last name, then by first name. Note that we use the specific fields we created for sorting.
7 Fetch the size top hits, 20 by default. Obviously, paging is also supported.

Hibernate Search DSL 支持 Elasticsearch 谓词的大部分子集(匹配、范围、嵌套、短语、空间…​)。欢迎使用自动完成来探索 DSL。

The Hibernate Search DSL supports a significant subset of the Elasticsearch predicates (match, range, nested, phrase, spatial…​). Feel free to explore the DSL using autocompletion.

如果这些还不够,则可以随时退回到 ` defining a predicate using JSON directly`。

When that’s not enough, you can always fall back to defining a predicate using JSON directly.

Automatic data initialization

为了演示,我们导入初始数据集。

For the purpose of this demonstration, let’s import an initial dataset.

让我们创建一个具有以下内容的 src/main/resources/import.sql 文件(我们将在配置中引用它):

Let’s create a src/main/resources/import.sql file with the following content (we’ll reference it in configuration later):

INSERT INTO author(id, firstname, lastname) VALUES (1, 'John', 'Irving');
INSERT INTO author(id, firstname, lastname) VALUES (2, 'Paul', 'Auster');
ALTER SEQUENCE author_seq RESTART WITH 3;

INSERT INTO book(id, title, author_id) VALUES (1, 'The World According to Garp', 1);
INSERT INTO book(id, title, author_id) VALUES (2, 'The Hotel New Hampshire', 1);
INSERT INTO book(id, title, author_id) VALUES (3, 'The Cider House Rules', 1);
INSERT INTO book(id, title, author_id) VALUES (4, 'A Prayer for Owen Meany', 1);
INSERT INTO book(id, title, author_id) VALUES (5, 'Last Night in Twisted River', 1);
INSERT INTO book(id, title, author_id) VALUES (6, 'In One Person', 1);
INSERT INTO book(id, title, author_id) VALUES (7, 'Avenue of Mysteries', 1);
INSERT INTO book(id, title, author_id) VALUES (8, 'The New York Trilogy', 2);
INSERT INTO book(id, title, author_id) VALUES (9, 'Mr. Vertigo', 2);
INSERT INTO book(id, title, author_id) VALUES (10, 'The Brooklyn Follies', 2);
INSERT INTO book(id, title, author_id) VALUES (11, 'Invisible', 2);
INSERT INTO book(id, title, author_id) VALUES (12, 'Sunset Park', 2);
INSERT INTO book(id, title, author_id) VALUES (13, '4 3 2 1', 2);
ALTER SEQUENCE book_seq RESTART WITH 14;

因为上述数据将在 Hibernate Search 不知情的情况下插入数据库中,所以它不会被索引,这与通过 Hibernate ORM 操作进行的即将到来的更新不同,即将到来的更新会自动同步到全文索引。

Because this data above will be inserted into the database without Hibernate Search’s knowledge, it won’t be indexed — unlike upcoming updates coming through Hibernate ORM operations, which will be synchronized automatically to the full text index.

在我们现有的 LibraryResource 中,让我们添加以下内容(以及一些 import)来索引该初始数据:

In our existing LibraryResource, let’s add the following content (and a few `import`s) to index that initial data:

如果你不手动导入数据库中的数据,则不需要这样做:只有在你更改索引配置(添加新字段、更改分析器的配置…​)且希望将新配置应用于现有数据时,才应该使用批量索引器。

If you don’t import data manually in the database, you don’t need this: the mass indexer should then only be used when you change your indexing configuration (adding a new field, changing an analyzer’s configuration…​) and you want the new configuration to be applied to your existing data.

    @Inject
    SearchMapping searchMapping; (1)

    void onStart(@Observes StartupEvent ev) throws InterruptedException { (2)
        // only reindex if we imported some content
        if (Book.count() > 0) {
            searchMapping.scope(Object.class) (3)
                    .massIndexer() (4)
                    .startAndWait(); (5)
        }
    }
1 Inject a Hibernate Search SearchMapping, which relies on the EntityManagerFactory under the hood. Applications with multiple persistence units can use the CDI qualifier @io.quarkus.hibernate.orm.PersistenceUnit to select the right one: see CDI integration.
2 Add a method that will get executed on application startup.
3 Create a "search scope" targeting all indexed entity types that extend Object — that is, every single indexed entity types (Author and Book).
4 Create an instance of Hibernate Search’s mass indexer, which allows indexing a lot of data efficiently (you can fine tune it for better performance).
5 Start the mass indexer and wait for it to finish.

Configuring the application

和往常一样,我们可以在 Quarkus 配置文件 application.properties 中配置所有内容。

As usual, we can configure everything in the Quarkus configuration file, application.properties.

编辑 src/main/resources/application.properties 并注入以下配置:

Edit src/main/resources/application.properties and inject the following configuration:

quarkus.ssl.native=false 1

quarkus.datasource.db-kind=postgresql 2

quarkus.hibernate-orm.sql-load-script=import.sql 3

quarkus.hibernate-search-orm.elasticsearch.version=8 4
quarkus.hibernate-search-orm.indexing.plan.synchronization.strategy=sync 5

%prod.quarkus.datasource.jdbc.url=jdbc:postgresql://localhost/quarkus_test 6
%prod.quarkus.datasource.username=quarkus_test
%prod.quarkus.datasource.password=quarkus_test
%prod.quarkus.hibernate-orm.database.generation=create
%prod.quarkus.hibernate-search-orm.elasticsearch.hosts=localhost:9200 6
1 We won’t use SSL, so we disable it to have a more compact native executable.
2 Let’s create a PostgreSQL datasource.
3 We load some initial data on startup (see Automatic data initialization).
4 We need to tell Hibernate Search about the version of Elasticsearch we will use. It is important because there are significant differences between Elasticsearch mapping syntax depending on the version. Since the mapping is created at build time to reduce startup time, Hibernate Search cannot connect to the cluster to automatically detect the version. Note that, for OpenSearch, you need to prefix the version with opensearch:; see OpenSearch compatibility.
5 This means that we wait for the entities to be searchable before considering a write complete. On a production setup, the write-sync default will provide better performance. Using sync is especially important when testing as you need the entities to be searchable immediately.
6 For development and tests, we rely on dev-services, which means Quarkus will start a PostgreSQL database and Elasticsearch cluster automatically. In production mode, however, we will want to start a PostgreSQL database and Elasticsearch cluster manually, which is why we provide Quarkus with this connection info in the prod profile (%prod. prefix).

因为我们依赖 Dev Services,所以在测试和开发模式下,在每次应用程序启动时,数据库和 Elasticsearch 模式将自动删除并重新创建(除非显式设置 <<`quarkus.hibernate-search-orm.schema-management.strategy`,quarkus-hibernate-search-orm-elasticsearch_quarkus-hibernate-search-orm-schema-management-strategy>>)。

Because we rely on dev-services, the database and Elasticsearch schema will automatically be dropped and re-created on each application startup in tests and dev mode (unless <<`quarkus.hibernate-search-orm.schema-management.strategy`,quarkus-hibernate-search-orm-elasticsearch_quarkus-hibernate-search-orm-schema-management-strategy>> is set explicitly).

如果由于某种原因无法使用 Dev Services,则必须设置以下属性以获取类似的行为:

If for some reason you cannot use Dev Services, you will have to set the following properties to get similar behavior:

%dev,test.quarkus.hibernate-orm.database.generation=drop-and-create
%dev,test.quarkus.hibernate-search-orm.schema-management.strategy=drop-and-create

另请参阅 <<`quarkus.hibernate-search-orm.schema-management.strategy`,quarkus-hibernate-search-orm-elasticsearch_quarkus-hibernate-search-orm-schema-management-strategy>>。

See also <<`quarkus.hibernate-search-orm.schema-management.strategy`,quarkus-hibernate-search-orm-elasticsearch_quarkus-hibernate-search-orm-schema-management-strategy>>.

有关 Hibernate Search ORM 扩展配置的更多信息,请参阅 Configuration Reference

For more information about configuration of the Hibernate Search ORM extension, refer to the configuration-reference.

Creating a frontend

现在,让我们添加一个简单的网页以与我们的 LibraryResource 进行交互。Quarkus 自动为位于 META-INF/resources 目录下的静态资源提供服务。在 src/main/resources/META-INF/resources 目录中,使用来自此 {quickstarts-blob-url}/hibernate-search-orm-elasticsearch-quickstart/src/main/resources/META-INF/resources/index.html[index.html] 文件的内容覆盖现有的 index.html 文件。

Now let’s add a simple web page to interact with our LibraryResource. Quarkus automatically serves static resources located under the META-INF/resources directory. In the src/main/resources/META-INF/resources directory, overwrite the existing index.html file with the content from this {quickstarts-blob-url}/hibernate-search-orm-elasticsearch-quickstart/src/main/resources/META-INF/resources/index.html[index.html] file.

Time to play with your application

你现在可以与你的 REST 服务进行交互:

You can now interact with your REST service:

  • start your Quarkus application with:include::{includes}/devtools/dev.adoc[]

  • open a browser to http://localhost:8080/

  • search for authors or book titles (we initialized some data for you)

  • create new authors and books and search for them too

如你所见,你的所有更新都自动同步到 Elasticsearch 集群。

As you can see, all your updates are automatically synchronized to the Elasticsearch cluster.

Building a native executable

您可以使用以下常用命令构建一个本机可执行文件:

You can build a native executable with the usual command:

Unresolved directive in hibernate-search-orm-elasticsearch.adoc - include::{includes}/devtools/build-native.adoc[]

与本机可执行文件编译一样,此操作会消耗大量内存。

As usual with native executable compilation, this operation consumes a lot of memory.

在构建本机可执行文件时,停止这两个容器可能更安全,完成后再重新启动它们。

It might be safer to stop the two containers while you are building the native executable and start them again once you are done.

运行本机可执行文件像执行 ./target/hibernate-search-orm-elasticsearch-quickstart-1.0.0-SNAPSHOT-runner 一样简单。

Running the native executable is as simple as executing ./target/hibernate-search-orm-elasticsearch-quickstart-1.0.0-SNAPSHOT-runner.

然后,您可以将浏览器指向 http://localhost:8080/ 并使用您的应用程序。

You can then point your browser to http://localhost:8080/ and use your application.

启动比平时慢一点:大部分是我们移除并重新创建数据库模式和 Elasticsearch 映射的缘故,每次都在启动时。我们还注入了一些数据并执行 mass 索引器。

The startup is a bit slower than usual: it is mostly due to us dropping and recreating the database schema and the Elasticsearch mapping every time at startup. We also inject some data and execute the mass indexer.

在实际生活中,很显然您不会在每次启动时都执行此操作。

In a real life application, it is obviously something you won’t do on every startup.

Dev Services (Configuration Free Datastores)

Quarkus 支持一项名为 Dev Services 的功能,允许您在没有任何配置的情况下启动各种容器。

Quarkus supports a feature called Dev Services that allows you to start various containers without any config.

Elasticsearch 的支持延伸到默认 Elasticsearch 连接。实际上,这意味着如果您尚未配置 quarkus.hibernate-search-orm.elasticsearch.hosts,Quarkus 会在执行测试或 dev mode 时自动启动 Elasticsearch 容器,并自动配置连接。

In the case of Elasticsearch this support extends to the default Elasticsearch connection. What that means practically, is that if you have not configured quarkus.hibernate-search-orm.elasticsearch.hosts, Quarkus will automatically start an Elasticsearch container when running tests or in dev mode, and automatically configure the connection.

在运行应用程序的生产版本时,Elasticsearch 连接需要正常配置,因此如果您想在 application.properties 中包含生产数据库配置,并且希望继续使用 Dev 服务,我们建议您使用 %prod. 配置文件来定义您的 Elasticsearch 设置。

When running the production version of the application, the Elasticsearch connection needs to be configured as normal, so if you want to include a production database config in your application.properties and continue to use Dev Services we recommend that you use the %prod. profile to define your Elasticsearch settings.

Elasticsearch 的 Dev 服务目前无法并发启动多个集群,因此它只适用于默认持久性单元的默认后端:命名持久性单元或命名后端无法利用 Elasticsearch 的 Dev 服务。

Dev Services for Elasticsearch is currently unable to start multiple clusters concurrently, so it only works with the default backend of the default persistence unit: named persistence units or named backends won’t be able to take advantage of Dev Services for Elasticsearch.

更多信息,你可以阅读 Dev Services for Elasticsearch guide

For more information you can read the Dev Services for Elasticsearch guide.

Programmatic mapping

如果无法添加 Hibernate Search 注解到实体中,则可以相反,以编程方式应用映射。编程方式映射通过 ProgrammaticMappingConfigurationContext 进行配置,可以通过映射配置器 (HibernateOrmSearchMappingConfigurer) 访问它。

If, for some reason, adding Hibernate Search annotations to entities is not possible, mapping can be applied programmatically instead. Programmatic mapping is configured through the ProgrammaticMappingConfigurationContext that is exposed via a mapping configurer (HibernateOrmSearchMappingConfigurer).

映射配置器 (HibernateOrmSearchMappingConfigurer) 的功能不仅仅是编程方式映射功能。它还允许 configuring annotation mapping, bridges, and more

A mapping configurer (HibernateOrmSearchMappingConfigurer) allows much more than just programmatic mapping capabilities. It also allows configuring annotation mapping, bridges, and more.

以下是对应用编程方式映射的映射配置器的一个示例:

Below is an example of a mapping configurer that applies programmatic mapping:

package org.acme.hibernate.search.elasticsearch.config;

import org.hibernate.search.mapper.orm.mapping.HibernateOrmMappingConfigurationContext;
import org.hibernate.search.mapper.orm.mapping.HibernateOrmSearchMappingConfigurer;
import org.hibernate.search.mapper.pojo.mapping.definition.programmatic.TypeMappingStep;

import io.quarkus.hibernate.search.orm.elasticsearch.SearchExtension;

@SearchExtension (1)
public class CustomMappingConfigurer implements HibernateOrmSearchMappingConfigurer {

	@Override
    public void configure(HibernateOrmMappingConfigurationContext context) {
        TypeMappingStep type = context.programmaticMapping()    (2)
            .type(SomeIndexedEntity.class);                     (3)
        type.indexed()                                          (4)
            .index(SomeIndexedEntity.INDEX_NAME);               (5)
        type.property("id").documentId();                       (6)
        type.property("text").fullTextField();                  (7)
    }
}
1 Annotate the configurer implementation with the @SearchExtension qualifier to tell Quarkus it should be used by Hibernate Search in the default persistence unit.[.iokays-translated-721c68a549f2055816428f235d168c74] 该注解还可以针对特定的持久性单元 (@SearchExtension(persistenceUnit = "nameOfYourPU"))。

The annotation can also target a specific persistence unit (@SearchExtension(persistenceUnit = "nameOfYourPU")). <1> Access the programmatic mapping context. <1> Create mapping step for the SomeIndexedEntity entity. <1> Define the SomeIndexedEntity entity as indexed. <1> Provide an index name to be used for the SomeIndexedEntity entity. <1> Define the document id property. <1> Define a full-text search field for the text property.

或者,如果您出于某种原因无法或不想使用 @SearchExtension 为映射配置程序添加注释,则可以简单地使用 @Dependent @Named("myMappingConfigurer") 为其添加注释,然后在配置属性中引用它:

Alternatively, if for some reason you can’t or don’t want to annotate your mapping configurer with @SearchExtension, you can simply annotate it with @Dependent @Named("myMappingConfigurer") and then reference it from configuration properties:

quarkus.hibernate-search-orm.mapping.configurer=bean:myMappingConfigurer

OpenSearch compatibility

Hibernate Search 与 ElasticsearchOpenSearch 都兼容,但它默认情况下会假定它正在与一个 Elasticsearch 集群工作。

Hibernate Search is compatible with both Elasticsearch and OpenSearch, but it assumes it is working with an Elasticsearch cluster by default.

要让 Hibernate Search 与一个 OpenSearch 集群一起工作,请执行以下操作: prefix the configured version with opensearch:

To have Hibernate Search work with an OpenSearch cluster instead, prefix the configured version with opensearch:, as shown below.

quarkus.hibernate-search-orm.elasticsearch.version=opensearch:2.16

所有其它配置选项和 API 与 Elasticsearch 中的配置选项和 API 完全相同。

All other configuration options and APIs are exactly the same as with Elasticsearch.

您可以在 this section of Hibernate Search’s reference documentation 中找到有关 Elasticsearch 的兼容发行版和版本的更多信息。

You can find more information about compatible distributions and versions of Elasticsearch in this section of Hibernate Search’s reference documentation.

Multiple persistence units

Configuring multiple persistence units

借助 Hibernate ORM 扩展,you can set up multiple persistence units,每个扩展都有自己的数据源和配置。

With the Hibernate ORM extension, you can set up multiple persistence units, each with its own datasource and configuration.

如果您确实声明了多个持久性单元,那么您还将为每个持久性单元单独配置 Hibernate Search。

If you do declare multiple persistence units, you will also configure Hibernate Search separately for each persistence unit.

quarkus.hibernate-search-orm. 命名空间根目录的属性定义了默认持久性单元。例如,以下代码片段定义了一个默认数据源和一个默认持久性单元,并为该持久性单元将 Elasticsearch 主机设置为 es1.mycompany.com:9200

The properties at the root of the quarkus.hibernate-search-orm. namespace define the default persistence unit. For instance, the following snippet defines a default datasource and a default persistence unit, and sets the Elasticsearch host for that persistence unit to es1.mycompany.com:9200.

quarkus.datasource.db-kind=h2
quarkus.datasource.jdbc.url=jdbc:h2:mem:default;DB_CLOSE_DELAY=-1

quarkus.hibernate-search-orm.elasticsearch.hosts=es1.mycompany.com:9200
quarkus.hibernate-search-orm.elasticsearch.version=8

使用基于 map 的方法,也可以配置命名持久性单元:

Using a map based approach, it is also possible to configure named persistence units:

quarkus.datasource."users".db-kind=h2 1
quarkus.datasource."users".jdbc.url=jdbc:h2:mem:users;DB_CLOSE_DELAY=-1

quarkus.datasource."inventory".db-kind=h2 2
quarkus.datasource."inventory".jdbc.url=jdbc:h2:mem:inventory;DB_CLOSE_DELAY=-1

quarkus.hibernate-orm."users".datasource=users 3
quarkus.hibernate-orm."users".packages=org.acme.model.user

quarkus.hibernate-orm."inventory".datasource=inventory 4
quarkus.hibernate-orm."inventory".packages=org.acme.model.inventory

quarkus.hibernate-search-orm."users".elasticsearch.hosts=es1.mycompany.com:9200 5
quarkus.hibernate-search-orm."users".elasticsearch.version=8

quarkus.hibernate-search-orm."inventory".elasticsearch.hosts=es2.mycompany.com:9200 6
quarkus.hibernate-search-orm."inventory".elasticsearch.version=8
1 Define a datasource named users.
2 Define a datasource named inventory.
3 Define a persistence unit called users pointing to the users datasource.
4 Define a persistence unit called inventory pointing to the inventory datasource.
5 Configure Hibernate Search for the users persistence unit, setting the Elasticsearch host for that persistence unit to es1.mycompany.com:9200.
6 Configure Hibernate Search for the inventory persistence unit, setting the Elasticsearch host for that persistence unit to es2.mycompany.com:9200.

Attaching model classes to persistence units

对于每个持久性单元,Hibernate 搜索将仅考虑附加到该持久性单元的已索引实体。实体通过 configuring the Hibernate ORM extension附加到持久性单元。

For each persistence unit, Hibernate Search will only consider indexed entities that are attached to that persistence unit. Entities are attached to a persistence unit by configuring the Hibernate ORM extension.

CDI integration

Injecting entry points

您可以使用 CDI 注入 Hibernate 搜索的主入口点 SearchSession`和 `SearchMapping

You can inject Hibernate Search’s main entry points, SearchSession and SearchMapping, using CDI:

@Inject
SearchSession searchSession;

这将注入默认持久性单元的 SearchSession

This will inject the SearchSession of the default persistence unit.

为了注入命名持久性单元(在本例中为 users)的 SearchSession,只需添加一个限定符:

To inject the SearchSession of a named persistence unit (users in our example), just add a qualifier:

@Inject
@PersistenceUnit("users") 1
SearchSession searchSession;
1 This is the @io.quarkus.hibernate.orm.PersistenceUnit annotation.

您可以使用完全相同的机制注入命名持久性单元的 SearchMapping

You can inject the SearchMapping of a named persistence unit using the exact same mechanism:

@Inject
@PersistenceUnit("users")
SearchMapping searchMapping;

Plugging in custom components

具有 Hibernate ORM 的 Hibernate 搜索的 Quarkus 扩展将自动将带有 `@SearchExtension`注释的组件注入到 Hibernate 搜索中。

The Quarkus extension for Hibernate Search with Hibernate ORM will automatically inject components annotated with @SearchExtension into Hibernate Search.

当有意义时,该注释可以选择针对特定的持久性单元 (@SearchExtension(persistenceUnit = "nameOfYourPU"))、后端 (@SearchExtension(backend = "nameOfYourBackend"))、索引 (@SearchExtension(index = "nameOfYourIndex")) 或它们的组合 (@SearchExtension(persistenceUnit = "nameOfYourPU", backend = "nameOfYourBackend", index = "nameOfYourIndex")) 进行。

The annotation can optionally target a specific persistence unit (@SearchExtension(persistenceUnit = "nameOfYourPU")), backend (@SearchExtension(backend = "nameOfYourBackend")), index (@SearchExtension(index = "nameOfYourIndex")), or a combination of those (@SearchExtension(persistenceUnit = "nameOfYourPU", backend = "nameOfYourBackend", index = "nameOfYourIndex")), when it makes sense for the type of the component being injected.

以下组件类型可使用此功能:

This feature is available for the following component types:

org.hibernate.search.engine.reporting.FailureHandler

A component that should be notified of any failure occurring in a background process (mainly index operations).

范围:每个持久性单元一个。

Scope: one per persistence unit.

请参阅 this section of the reference documentation获取更多信息。

org.hibernate.search.mapper.orm.mapping.HibernateOrmSearchMappingConfigurer

A component used to configure the Hibernate Search mapping, in particular programmatically.

范围:每个持久性单元一个或多个。

Scope: one or more per persistence unit.

请参阅 this section of this guide获取更多信息。

See programmatic-mapping for more information.

org.hibernate.search.mapper.pojo.work.IndexingPlanSynchronizationStrategy

A component used to configure how to synchronize between application threads and indexing.

范围:每个持久性单元一个。

Scope: one per persistence unit.

还可以通过 <<`quarkus.hibernate-search-orm.indexing.plan.synchronization.strategy`,quarkus-hibernate-search-orm-elasticsearch_quarkus-hibernate-search-orm-indexing-plan-synchronization-strategy>> 设置为内置实现。

Can also be set to built-in implementations through <<`quarkus.hibernate-search-orm.indexing.plan.synchronization.strategy`,quarkus-hibernate-search-orm-elasticsearch_quarkus-hibernate-search-orm-indexing-plan-synchronization-strategy>>.

请参阅 this section of the reference documentation获取更多信息。

org.hibernate.search.backend.elasticsearch.analysis.ElasticsearchAnalysisConfigurer

A component used to configure full text analysis (e.g. analyzers, normalizers).

范围:每个后端一个或多个。

Scope: one or more per backend.

请参阅 this section of this guide获取更多信息。

See analysis-configurer for more information.

org.hibernate.search.backend.elasticsearch.index.layout.IndexLayoutStrategy

A component used to configure the Elasticsearch layout: index names, index aliases, …​

范围:每个后端一个。

Scope: one per backend.

也可以通过 <<`quarkus.hibernate-search-orm.elasticsearch.layout.strategy`,quarkus-hibernate-search-orm-elasticsearch_quarkus-hibernate-search-orm-elasticsearch-layout-strategy>> 设置到内置实现。

Can also be set to built-in implementations through <<`quarkus.hibernate-search-orm.elasticsearch.layout.strategy`,quarkus-hibernate-search-orm-elasticsearch_quarkus-hibernate-search-orm-elasticsearch-layout-strategy>>.

更多信息,请参见 this section of the reference documentation

Offline startup

默认情况下,Hibernate Search 在启动时会向 Elasticsearch 集群发送一些请求。如果 Elasticsearch 集群在 Hibernate Search 启动时不必定启动并运行,则会导致启动失败。

By default, Hibernate Search sends a few requests to the Elasticsearch cluster on startup. If the Elasticsearch cluster is not necessarily up and running when Hibernate Search starts, this could cause a startup failure.

为了解决这个问题,您可以将 Hibernate Search 配置为在开始时不发送任何请求:

To address this, you can configure Hibernate Search to not send any request on startup:

  • Disable Elasticsearch version checks on startup by setting the configuration property <<`quarkus.hibernate-search-orm.elasticsearch.version-check.enabled`,quarkus-hibernate-search-orm-elasticsearch_quarkus-hibernate-search-orm-elasticsearch-version-check-enabled>> to false.

  • Disable schema management on startup by setting the configuration property <<`quarkus.hibernate-search-orm.schema-management.strategy`,quarkus-hibernate-search-orm-elasticsearch_quarkus-hibernate-search-orm-schema-management-strategy>> to none.

当然,即使进行了此项配置,Hibernate Search 仍然无法在 Elasticsearch 集群可访问前索引任何内容或运行搜索查询。

Of course, even with this configuration, Hibernate Search still won’t be able to index anything or run search queries until the Elasticsearch cluster becomes accessible.

如果您通过将 quarkus.hibernate-search-orm.schema-management.strategy 设置为 `none`来禁用自动模式创建,则必须在应用程序开始持久化/更新实体并执行搜索请求之前,在某个时间点手动创建该模式。

If you disable automatic schema creation by setting quarkus.hibernate-search-orm.schema-management.strategy to none, you will have to create the schema manually at some point before your application starts persisting/updating entities and executing search requests.

更多信息,请参见 this section of the reference documentation

Coordination through outbox polling

使用收件箱轮询的协调被视为预览版。

Coordination through outbox polling is considered preview.

preview 中,向后兼容性和生态环境中的交互性没有得到保证。具体的改进可能需要修改配置或 API,甚至存储格式,并且成为 stable 的计划正在进行中。欢迎在我们的 mailing list 中提供反馈,或作为我们在 GitHub issue tracker 中的问题。

In preview, backward compatibility and presence in the ecosystem is not guaranteed. Specific improvements might require changing configuration or APIs, or even storage formats, and plans to become stable are under way. Feedback is welcome on our mailing list or as issues in our GitHub issue tracker.

尽管在分布式应用程序中使用 Hibernate Search 和 Elasticsearch 在技术上是可行的,但它们在默认情况下会受到 a few limitations 的影响。

While it’s technically possible to use Hibernate Search and Elasticsearch in distributed applications, by default they suffer from a few limitations.

这些限制是因为 Hibernate Search 默认情况下并未在各个线程或应用程序节点之间协调。

These limitations are the result of Hibernate Search not coordinating between threads or application nodes by default.

为了排除这些限制,您可以 use the outbox-polling coordination strategy。此策略在数据库中创建了一个收件箱表,用于将实体更改事件推送到该表,并依靠后台处理器来消耗这些事件并执行索引编制。

In order to get rid of these limitations, you can use the outbox-polling coordination strategy. This strategy creates an outbox table in the database to push entity change events to, and relies on a background processor to consume these events and perform indexing.

要启用 outbox-polling 协调策略,需要一个附加的扩展:

To enable the outbox-polling coordination strategy, an additional extension is required:

Unresolved directive in hibernate-search-orm-elasticsearch.adoc - include::{includes}/devtools/extension-add.adoc[]

一旦有了该扩展,您将需要通过将 <<`quarkus.hibernate-search-orm.coordination.strategy`,quarkus-hibernate-search-orm-elasticsearch_quarkus-hibernate-search-orm-coordination-strategy>> 设置为 outbox-polling 显式选择 outbox-polling 策略。

Once the extension is there, you will need to explicitly select the outbox-polling strategy by setting <<`quarkus.hibernate-search-orm.coordination.strategy`,quarkus-hibernate-search-orm-elasticsearch_quarkus-hibernate-search-orm-coordination-strategy>> to outbox-polling.

最后,您需要确保 Hibernate Search(用来表示收件箱和代理人)添加的 Hibernate ORM 实体,在您的数据库中具有对应的表/序列:

Finally, you will need to make sure that the Hibernate ORM entities added by Hibernate Search (to represent the outbox and agents) have corresponding tables/sequences in your database:

数据库模式 Hibernate Search 期望进行 outbox 轮询协调可以通过以下配置属性进行自定义:

The database schema Hibernate Search will expect for outbox-polling coordination may be customized through the following configuration properties:

  • <<`quarkus.hibernate-search-orm.coordination.entity-mapping.agent.catalog`,quarkus-hibernate-search-orm-outbox-polling_quarkus-hibernate-search-orm-coordination-entity-mapping-agent-catalog>>

  • <<`quarkus.hibernate-search-orm.coordination.entity-mapping.agent.schema`,quarkus-hibernate-search-orm-outbox-polling_quarkus-hibernate-search-orm-coordination-entity-mapping-agent-schema>>

  • <<`quarkus.hibernate-search-orm.coordination.entity-mapping.agent.table`,quarkus-hibernate-search-orm-outbox-polling_quarkus-hibernate-search-orm-coordination-entity-mapping-agent-table>>

  • <<`quarkus.hibernate-search-orm.coordination.entity-mapping.agent.uuid-gen-strategy`,quarkus-hibernate-search-orm-outbox-polling_quarkus-hibernate-search-orm-coordination-entity-mapping-agent-uuid-gen-strategy>>

  • <<`quarkus.hibernate-search-orm.coordination.entity-mapping.agent.uuid-type`,quarkus-hibernate-search-orm-outbox-polling_quarkus-hibernate-search-orm-coordination-entity-mapping-agent-uuid-type>>

  • <<`quarkus.hibernate-search-orm.coordination.entity-mapping.outbox-event.catalog`,quarkus-hibernate-search-orm-outbox-polling_quarkus-hibernate-search-orm-coordination-entity-mapping-outbox-event-catalog>>

  • <<`quarkus.hibernate-search-orm.coordination.entity-mapping.outbox-event.schema`,quarkus-hibernate-search-orm-outbox-polling_quarkus-hibernate-search-orm-coordination-entity-mapping-outbox-event-schema>>

  • <<`quarkus.hibernate-search-orm.coordination.entity-mapping.outbox-event.table`,quarkus-hibernate-search-orm-outbox-polling_quarkus-hibernate-search-orm-coordination-entity-mapping-outbox-event-table>>

  • <<`quarkus.hibernate-search-orm.coordination.entity-mapping.outbox-event.uuid-gen-strategy`,quarkus-hibernate-search-orm-outbox-polling_quarkus-hibernate-search-orm-coordination-entity-mapping-outbox-event-uuid-gen-strategy>>

  • <<`quarkus.hibernate-search-orm.coordination.entity-mapping.outbox-event.uuid-type`,quarkus-hibernate-search-orm-outbox-polling_quarkus-hibernate-search-orm-coordination-entity-mapping-outbox-event-uuid-type>>

完成上述操作后,您就可以使用 Hibernate Search 和出站信箱了。无需更改任何代码,只需启动应用程序即可:它将自动检测到多个应用程序是否已连接到同一数据库,并相应地协调索引更新。

Once you are done with the above, you’re ready to use Hibernate Search with an outbox. Don’t change any code, and just start your application: it will automatically detect when multiple applications are connected to the same database, and coordinate the index updates accordingly.

使用 outbox-polling 协调策略时,Hibernate Search 的行为大多与不使用该策略时相同:应用程序代码(持久化实体、搜索等)无需进行任何更改。

Hibernate Search mostly behaves the same when using the outbox-polling coordination strategy as when not using it: application code (persisting entities, searching, etc.) should not require any change.

但是,有一个关键区别:索引更新必然是异步的;可以保证索引更新必定会发生,但不是立即发生。

However, there is one key difference: index updates are necessarily asynchronous; they are guaranteed to happen eventually, but not immediately.

这意味着配置属性 <<`quarkus.hibernate-search-orm.indexing.plan.synchronization.strategy`,quarkus-hibernate-search-orm-elasticsearch_quarkus-hibernate-search-orm-indexing-plan-synchronization-strategy>>在使用 outbox-polling 协调策略时无法设置:Hibernate Search 始终行为得仿佛该属性已设置为 write-sync(默认值)。

This means in particular that the configuration property <<`quarkus.hibernate-search-orm.indexing.plan.synchronization.strategy`,quarkus-hibernate-search-orm-elasticsearch_quarkus-hibernate-search-orm-indexing-plan-synchronization-strategy>> cannot be set when using the outbox-polling coordination strategy: Hibernate Search will always behave as if this property was set to write-sync (the default).

此行为与 Elasticsearch 的 near-real-time search 一致,即使在禁用协调后也是使用 Hibernate Search 的推荐方式。

This behavior is consistent with Elasticsearch’s near-real-time search and the recommended way of using Hibernate Search even when coordination is disabled.

有关 Hibernate Search 中协调的更多信息,请参阅 this section of the reference documentation

For more information about coordination in Hibernate Search, see this section of the reference documentation.

有关与协调相关的配置选项的更多信息,请参阅 Configuration of coordination with outbox polling

For more information about configuration options related to coordination, see Configuration of coordination with outbox polling.

[id="configuration-reference-aws"] AWS request signing

如果您需要使用 Amazon’s managed Elasticsearch service,您会发现它需要一种涉及请求签名的专有身份验证方法。

If you need to use Amazon’s managed Elasticsearch service, you will find it requires a proprietary authentication method involving request signing.

您可以通过向项目添加专用扩展并对其进行配置来启用 Hibernate Search 中的 AWS 请求签名。

You can enable AWS request signing in Hibernate Search by adding a dedicated extension to your project and configuring it.

Management endpoint

Hibernate Search 的管理端点被认为是预览版。

Hibernate Search’s management endpoint is considered preview.

preview 中,向后兼容性和生态环境中的交互性没有得到保证。具体的改进可能需要修改配置或 API,甚至存储格式,并且成为 stable 的计划正在进行中。欢迎在我们的 mailing list 中提供反馈,或作为我们在 GitHub issue tracker 中的问题。

In preview, backward compatibility and presence in the ecosystem is not guaranteed. Specific improvements might require changing configuration or APIs, or even storage formats, and plans to become stable are under way. Feedback is welcome on our mailing list or as issues in our GitHub issue tracker.

Hibernate Search 扩展通过 management interface 提供一个 HTTP 端点来重新索引您的数据。默认情况下,此端点不可用。可以通过如下所示的配置属性来启用它。

The Hibernate Search extension provides an HTTP endpoint to reindex your data through the management interface. By default, this endpoint is not available. It can be enabled through configuration properties as shown below.

quarkus.management.enabled=true 1
quarkus.hibernate-search-orm.management.enabled=true 2
1 Enable the management interface.
2 Enable Hibernate Search specific management endpoints.

启用管理端点之后,可以使用 /q/hibernate-search/reindex`重新索引数据,其中 `/q 是默认管理根路径,而 /hibernate-search 是默认 Hibernate Search 根管理路径。它 (/hibernate-search) 可以通过配置属性进行更改,如下所示。

Once the management endpoints are enabled, data can be re-indexed via /q/hibernate-search/reindex, where /q is the default management root path and /hibernate-search is the default Hibernate Search root management path. It (/hibernate-search) can be changed via configuration property as shown below.

quarkus.hibernate-search-orm.management.root-path=custom-root-path 1
1 Use a custom custom-root-path path for Hibernate Search’s management endpoint. If the default management root path is used then the reindex path becomes /q/custom-root-path/reindex.

此端点仅接受具有 application/json 内容类型的 POST 请求。如果提交空请求正文,将重新索引所有索引实体。如果仅需要重新索引实体子集或需要对底层批量索引器进行自定义配置,则该信息可以通过请求正文传递,如下所示。

This endpoint accepts POST requests with application/json content type only. All indexed entities will be re-indexed if an empty request body is submitted. If only a subset of entities must be re-indexed or if there is a need to have a custom configuration of the underlying mass indexer then this information can be passed through the request body as shown below.

{
  "filter": {
    "types": ["EntityName1", "EntityName2", "EntityName3", ...], 1
  },
  "massIndexer":{
    "typesToIndexInParallel": 1, 2
  }
}
1 An array of entity names that should be re-indexed. If unspecified or empty, all entity types will be re-indexed.
2 Sets the number of entity types to be indexed in parallel.

以下示例中提供了可能的过滤器和可用批量索引器配置的完整列表。

The full list of possible filters and available mass indexer configurations is presented in the example below.

{
  "filter": { 1
    "types": ["EntityName1", "EntityName2", "EntityName3", ...], 2
    "tenants": ["tenant1", "tenant2", ...] 3
  },
  "massIndexer":{ 4
    "typesToIndexInParallel": 1, 5
    "threadsToLoadObjects": 6,  6
    "batchSizeToLoadObjects": 10, 7
    "cacheMode": "IGNORE", 8
    "mergeSegmentsOnFinish": false, 9
    "mergeSegmentsAfterPurge": true, 10
    "dropAndCreateSchemaOnStart": false, 11
    "purgeAllOnStart": true, 12
    "idFetchSize": 100, 13
    "transactionTimeout": 100000, 14
  }
}
1 Filter object that allows to limit the scope of reindexing.
2 An array of entity names that should be re-indexed. If unspecified or empty, all entity types will be re-indexed.
3 An array of tenant ids, in case of multi-tenancy. If unspecified or empty, all tenants will be re-indexed.
4 Mass indexer configuration object.
5 Sets the number of entity types to be indexed in parallel.
6 Sets the number of threads to be used to load the root entities.
7 Sets the batch size used to load the root entities.
8 Sets the cache interaction mode for the data loading tasks.
9 Whether each index is merged into a single segment after indexing.
10 Whether each index is merged into a single segment after the initial index purge, just before indexing.
11 Whether the indexes and their schema (if they exist) should be dropped and re-created before indexing.
12 Whether all entities are removed from the indexes before indexing.
13 Specifies the fetch size to be used when loading primary keys if objects to be indexed.
14 Specifies the timeout of transactions for loading ids and entities to be re-indexed.[.iokays-translated-8b138ce29add5cb1cf1f5d0ba3a9c050] 请注意,JSON 中的所有属性都是可选的,并且仅应使用所需的属性。

Note all the properties in the JSON are optional, and only those that are needed should be used.

有关批量索引器配置的更详细信息,请参阅 corresponding section of the Hibernate Search reference documentation

For more detailed information on mass indexer configuration see the corresponding section of the Hibernate Search reference documentation.

提交重新索引请求将触发后台的索引。大范围索引进度将显示在应用程序日志中。出于测试的目的,了解索引何时完成可能很有用。将 wait_for=finished 查询参数添加到 URL 中将导致管理端点返回一个分块响应,该响应将报告索引何时开始以及何时结束。

Submitting the reindexing request will trigger indexing in the background. Mass indexing progress will appear in the application logs. For testing purposes, it might be useful to know when the indexing finished. Adding wait_for=finished query parameter to the URL will result in the management endpoint returning a chunked response that will report when the indexing starts and then when it is finished.

在使用多个持久性单元时,可通过 persistence_unit 查询参数提供要重新索引的持久性单元的名称:/q/hibernate-search/reindex?persistence_unit=non-default-persistence-unit

When working with multiple persistence units, the name of the persistence unit to reindex can be supplied through the persistence_unit query parameter: /q/hibernate-search/reindex?persistence_unit=non-default-persistence-unit.

Further reading

如果您有兴趣了解更多关于 Hibernate Search 的内容,Hibernate 团队发布了 an extensive reference documentation,以及列出 other relevant resources 的页面。

If you are interested in learning more about Hibernate Search, the Hibernate team publishes an extensive reference documentation, as well as a page listing other relevant resources.

FAQ

Why Elasticsearch only?

Hibernate Search 同时支持 Lucene 后台和 Elasticsearch 后台。

Hibernate Search supports both a Lucene backend and an Elasticsearch backend.

在 Quarkus 的背景下,为了构建可扩展的应用程序,我们认为后者更有意义。因此,我们集中精力于此。

In the context of Quarkus and to build scalable applications, we thought the latter would make more sense. Thus, we focused our efforts on it.

我们目前还没有计划在 Quarkus 中支持 Lucene 后台,尽管有一个问题在跟踪 Quarkiverse 中此类实现的进度: quarkiverse/quarkus-hibernate-search-extras#179

We don’t have plans to support the Lucene backend in Quarkus for now, though there is an issue tracking progress on such an implementation in the Quarkiverse: quarkiverse/quarkus-hibernate-search-extras#179.

Configuration Reference for Hibernate Search with Hibernate ORM

Main Configuration

Unresolved directive in hibernate-search-orm-elasticsearch.adoc - include::{generated-dir}/config/quarkus-hibernate-search-orm-elasticsearch.adoc[]

About bean references

首先,请注意在配置属性中引用 bean 是可选的,事实上是不鼓励的:可以通过用 `@SearchExtension`注释 bean 来实现相同的结果。有关更多信息,请参见 this section

First, be aware that referencing beans in configuration properties is optional and, in fact, discouraged: you can achieve the same results by annotating your beans with @SearchExtension. See plugging-in-custom-components for more information.

如果您真的想要在配置属性中使用字符串值来引用 bean,请知道该字符串已被解析;这里是最常见的格式:

If you really do want to reference beans using a string value in configuration properties know that string is parsed; here are the most common formats:

  • bean: followed by the name of a @Named CDI bean. For example bean:myBean.

  • class: followed by the fully-qualified name of a class, to be instantiated through CDI if it’s a CDI bean, or through its public, no-argument constructor otherwise. For example class:com.mycompany.MyClass.

  • An arbitrary string referencing a built-in implementation. Available values are detailed in the documentation of each configuration property, such as async/read-sync/write-sync/sync for <<`quarkus.hibernate-search-orm.indexing.plan.synchronization.strategy`,quarkus-hibernate-search-orm-elasticsearch_quarkus-hibernate-search-orm-indexing-plan-synchronization-strategy>>.

也接受其他格式,但仅适用于高级用例。有关更多信息,请参见 this section of Hibernate Search’s reference documentation

Other formats are also accepted, but are only useful for advanced use cases. See this section of Hibernate Search’s reference documentation for more information.

Configuration of coordination with outbox polling

这些配置属性需要附加扩展。请参见 Coordination through outbox polling

These configuration properties require an additional extension. See Coordination through outbox polling.

Unresolved directive in hibernate-search-orm-elasticsearch.adoc - include::{generated-dir}/config/quarkus-hibernate-search-orm-outbox-polling.adoc[]