Use Hibernate Search in Standalone mode with Elasticsearch/OpenSearch
你有一个 Quarkus 应用程序吗?你想为用户提供全功能的全文搜索吗?你来对地方了。
You have a Quarkus 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 index entities into 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 Hibernate ORM entities, see this dedicated guide instead.
Prerequisites
Unresolved directive in hibernate-search-standalone-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.
实体存储并索引在 Elasticsearch 集群中。
The entities are stored 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-standalone-elasticsearch-quickstart
directory 。
The solution is located in the hibernate-search-standalone-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-standalone-elasticsearch.adoc - include::{includes}/devtools/create-app.adoc[]
此命令生成一个 Maven 结构,该结构导入以下扩展:
This command generates a Maven structure importing the following extensions:
-
Hibernate Search Standalone + Elasticsearch,
-
Quarkus REST (formerly RESTEasy Reactive) and Jackson.
如果你已经配置了 Quarkus 项目,你可以通过在项目基本目录中运行以下命令将 hibernate-search-standalone-elasticsearch
扩展添加到项目:
If you already have your Quarkus project configured, you can add the hibernate-search-standalone-elasticsearch
extension
to your project by running the following command in your project base directory:
Unresolved directive in hibernate-search-standalone-elasticsearch.adoc - include::{includes}/devtools/extension-add.adoc[]
这会将以下内容添加到您的 pom.xml
:
This will add the following to your pom.xml
:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-search-standalone-elasticsearch</artifactId>
</dependency>
implementation("io.quarkus:quarkus-hibernate-search-standalone-elasticsearch")
Creating the bare classes
首先,让我们在 Book
和 Author
子包中创建我们的 model
类。
First, let’s create our Book
and Author
classes in the model
subpackage.
package org.acme.hibernate.search.elasticsearch.model;
import java.util.List;
import java.util.Objects;
public class Author {
public UUID id; (1)
public String firstName;
public String lastName;
public List<Book> books;
public Author(UUID id, String firstName, String lastName, List<Book> books) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.books = books;
}
}
1 | We’re using public fields here,
because it’s shorter and there is no expectation of encapsulation on what is essentially a data class.[.iokays-translated-76d58a2eef34742417882b911298c811]
但是,如果你更喜欢使用带有 getter/setter 的私有字段,那完全没问题,只要 getter/etter 遵循 JavaBeans 命名规范 (getSomething() /isSomething() /setSomething(…) ),它们就会完美地工作。 |
However, if you prefer using private fields with getters/setters,
that’s totally fine and will work perfectly as long as the getters/setters follow the JavaBeans naming convention
(getSomething()
/isSomething()
/setSomething(…)
).
package org.acme.hibernate.search.elasticsearch.model;
import java.util.Objects;
public class Book {
public UUID id;
public String title;
public Book(UUID id, String title) {
this.id = id;
this.title = title;
}
}
Using Hibernate Search annotations
为我们的类启用全文本搜索功能仅需添加一些注释即可。
Enabling full text search capabilities for our classes is as simple as adding a few annotations.
让我们编辑 Author
实体来包括此内容:
Let’s edit the Author
entity to include this content:
package org.acme.hibernate.search.elasticsearch.model;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import org.hibernate.search.engine.backend.types.Sortable;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.DocumentId;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.IdProjection;
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 org.hibernate.search.mapper.pojo.mapping.definition.annotation.ProjectionConstructor;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.SearchEntity;
@SearchEntity (1)
@Indexed (2)
public class Author {
@DocumentId (3)
public UUID id;
@FullTextField(analyzer = "name") (4)
@KeywordField(name = "firstName_sort", sortable = Sortable.YES, normalizer = "sort") (5)
public String firstName;
@FullTextField(analyzer = "name")
@KeywordField(name = "lastName_sort", sortable = Sortable.YES, normalizer = "sort")
public String lastName;
@IndexedEmbedded (6)
public List<Book> books = new ArrayList<>();
public Author(UUID id, String firstName, String lastName) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
}
@ProjectionConstructor (7)
public Author(@IdProjection UUID id, String firstName, String lastName, List<Book> books) {
this( id, firstName, lastName );
this.books = books;
}
}
1 | First, let’s mark the Author type as an entity type.
In short, this implies the Author type it has its own, distinct lifecycle (not tied to another type),
and that every `BookAuthor instance carries an immutable, unique identifier. |
2 | Then, let’s use the @Indexed annotation to register our Author entity as part of the full text index. |
3 | And let’s end the mandatory configuration by defining a document identifier. |
4 | 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. |
5 | 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. |
6 | 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 instances are included in the index (i.e. the title field).
@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. |
7 | We mark a (single) constructor as a @ProjectionConstructor ,
so that an Author instance can be reconstructed from the content of the index. |
现在我们的作者已经编入索引,我们将需要映射书籍,这样这个 @IndexedEmbedded
注释实际上就会嵌入 something。
Now that our authors are indexed, we will want to map books,
so that this @IndexedEmbedded
annotation actually embeds something.
打开 Book
类,并包含下面的内容。
Open the Book
class and include the content below.
package org.acme.hibernate.search.elasticsearch.model;
import java.util.Objects;
import java.util.UUID;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.KeywordField;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.ProjectionConstructor;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.SearchEntity;
@SearchEntity (1)
public class Book {
@KeywordField (2)
public UUID id;
@FullTextField(analyzer = "english") (3)
public String title;
@ProjectionConstructor (4)
public Book(UUID id, String title) {
this.id = id;
this.title = title;
}
}
1 | We also mark the Book type as an entity type,
but we don’t use @Indexed , because we decided we don’t need a dedicated index for books. |
2 | We index the book’s ID, so it can be projected (see below). |
3 | We use a @FullTextField similar to what we did for Author but you’ll notice that the analyzer is different - more on this later. |
4 | Like Author , we mark a constructor as a @ProjectionConstructor ,
so that a Book instance can be reconstructed from the content of the index. |
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.standalone.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 Hibernate Search Standalone, 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.
有关配置分析器的更多信息,请参阅 this section of the reference documentation。
For more information about configuring analyzers, see this section of the reference documentation.
Implementing the REST service
创建 org.acme.hibernate.search.elasticsearch.LibraryResource
类:
Create the org.acme.hibernate.search.elasticsearch.LibraryResource
class:
package org.acme.hibernate.search.elasticsearch;
import java.util.ArrayList;
import java.util.UUID;
import jakarta.inject.Inject;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.NotFoundException;
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.pojo.standalone.mapping.SearchMapping;
import org.hibernate.search.mapper.pojo.standalone.session.SearchSession;
import org.jboss.resteasy.reactive.RestForm;
import org.jboss.resteasy.reactive.RestPath;
@Path("/library")
public class LibraryResource {
@Inject
SearchMapping searchMapping; (1)
@PUT
@Path("author")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public void addAuthor(@RestForm String firstName, @RestForm String lastName) {
try (var searchSession = searchMapping.createSession()) { (2)
Author author = new Author(UUID.randomUUID(), firstName, lastName, new ArrayList<>());
searchSession.indexingPlan().add(author); (3)
}
}
@GET
@Path("author/{id}")
public Author getAuthor(@RestPath UUID id) {
try (var searchSession = searchMapping.createSession()) {
return getAuthor(searchSession, id);
}
}
private Author getAuthor(SearchSession searchSession, UUID id) {
return searchSession.search(Author.class) (4)
.where(f -> f.id().matching(id))
.fetchSingleHit()
.orElseThrow(NotFoundException::new);
}
@POST
@Path("author/{id}")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public void updateAuthor(@RestPath UUID id, @RestForm String firstName, @RestForm String lastName) {
try (var searchSession = searchMapping.createSession()) {
Author author = getAuthor(searchSession, id); (5)
author.firstName = firstName;
author.lastName = lastName;
searchSession.indexingPlan().addOrUpdate(author); (5)
}
}
@DELETE
@Path("author/{id}")
public void deleteAuthor(@RestPath UUID id) {
try (var searchSession = searchMapping.createSession()) {
searchSession.indexingPlan().purge(Author.class, id, null); (6)
}
}
@PUT
@Path("author/{authorId}/book/")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public void addBook(@RestPath UUID authorId, @RestForm String title) {
try (var searchSession = searchMapping.createSession()) {
Author author = getAuthor(searchSession, authorId); (7)
author.books.add(new Book(authorId, title));
searchSession.indexingPlan().addOrUpdate(author);
}
}
@DELETE
@Path("author/{authorId}/book/{bookId}")
public void deleteBook(@RestPath UUID authorId, @RestPath UUID bookId) {
try (var searchSession = searchMapping.createSession()) {
Author author = getAuthor(searchSession, authorId); (7)
author.books.removeIf(book -> book.id.equals(bookId));
searchSession.indexingPlan().addOrUpdate(author);
}
}
}
1 | Inject a Hibernate Search mapping, the main entry point to Hibernate Search APIs. |
2 | Create a Hibernate Search session, which allows executing operations on the indexes. |
3 | To index a new Author, retrieve the session’s indexing plan and call add , passing the author instance in argument. |
4 | To retrieve an Author from the index, execute a simple search — more on search later — by identifier. |
5 | To update an Author, retrieve it from the index, apply changes,
retrieve the session’s indexing plan and call addOrUpdate , passing the author instance in argument. |
6 | To delete an Author by identifier, retrieve the session’s indexing plan
and call purge , passing the author class and identifier in argument. |
7 | Since books are "owned" by authors (they are duplicated for each author and their lifecycle is bound to their author’s), adding/deleting a book is simply an update to the author. |
这里没有什么开创性:仅仅是 REST 服务中几个 CRUD 操作,使用 Hibernate Search API。
Nothing groundbreaking here: just a few CRUD operations in a REST service, using Hibernate Search APIs.
有趣的部分涉及添加搜索端点。在我们的 LibraryResource
中,我们只需要添加以下方法(和一些 import
):
The interesting part comes with the addition of a search endpoint.
In our LibraryResource
, we just need to add the following method (and a few `import`s):
@GET
@Path("author/search")
public List<Author> searchAuthors(@RestQuery String pattern, (1)
@RestQuery Optional<Integer> size) {
try (var searchSession = searchMapping.createSession()) { (2)
return searchSession.search(Author.class) (3)
.where(f -> pattern == null || pattern.isBlank()
? 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 | Use the org.jboss.resteasy.reactive.RestQuery annotation type to avoid repeating the parameter name. |
2 | Create a Hibernate Search session, which allows executing operations on the indexes. |
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.
让我们在 LibraryResource
中添加一些方法:
Let’s add a few methods in LibraryResource
:
void onStart(@Observes StartupEvent ev) { (1)
// Index some test data if nothing exists
try (var searchSession = searchMapping.createSession()) {
if (0 < searchSession.search(Author.class) (2)
.where(f -> f.matchAll())
.fetchTotalHitCount()) {
return;
}
for (Author author : initialDataSet()) { (3)
searchSession.indexingPlan().add(author); (4)
}
}
}
private List<Author> initialDataSet() {
return List.of(
new Author(UUID.randomUUID(), "John", "Irving",
List.of(
new Book(UUID.randomUUID(), "The World According to Garp"),
new Book(UUID.randomUUID(), "The Hotel New Hampshire"),
new Book(UUID.randomUUID(), "The Cider House Rules"),
new Book(UUID.randomUUID(), "A Prayer for Owen Meany"),
new Book(UUID.randomUUID(), "Last Night in Twisted River"),
new Book(UUID.randomUUID(), "In One Person"),
new Book(UUID.randomUUID(), "Avenue of Mysteries"))),
new Author(UUID.randomUUID(), "Paul", "Auster",
List.of(
new Book(UUID.randomUUID(), "The New York Trilogy"),
new Book(UUID.randomUUID(), "Mr. Vertigo"),
new Book(UUID.randomUUID(), "The Brooklyn Follies"),
new Book(UUID.randomUUID(), "Invisible"),
new Book(UUID.randomUUID(), "Sunset Park"),
new Book(UUID.randomUUID(), "4 3 2 1"))));
}
1 | Add a method that will get executed on application startup. |
2 | Check whether there already is data in the index — if not, bail out. |
3 | Generate the initial dataset. |
4 | For each author, add it to the index. |
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.hibernate-search-standalone.mapping.structure=document 2
quarkus.hibernate-search-standalone.elasticsearch.version=8 3
quarkus.hibernate-search-standalone.indexing.plan.synchronization.strategy=sync 4
%prod.quarkus.hibernate-search-standalone.elasticsearch.hosts=localhost:9200 5
1 | We won’t use SSL, so we disable it to have a more compact native executable. |
2 | We need to tell Hibernate Search about the structure of our entities.[.iokays-translated-ceb715a8da8d7cae4d6494eef329e432] 在此应用程序中,我们将索引实体(作者)视为“文档”的根:作者“拥有”它通过关联引用的书籍,cannot 可以独立地更新作者。 |
In this application we consider an indexed entity (the author) is the root of a "document": the author "owns" books it references through associations, which cannot be updated independently of the author.
另请参阅 <<`quarkus.hibernate-search-standalone.mapping.structure`,quarkus-hibernate-search-standalone-elasticsearch_quarkus-hibernate-search-standalone-mapping-structure>>,了解其他选项和更多详细信息。
See <<`quarkus.hibernate-search-standalone.mapping.structure`,quarkus-hibernate-search-standalone-elasticsearch_quarkus-hibernate-search-standalone-mapping-structure>> for other options and more details.
<1> We need to tell Hibernate Search about the version of Elasticsearch we will use.[.iokays-translated-595c66012b9811e735c1488ae0bf5cb7]
这很重要,因为不同版本之间的 Elasticsearch 映射语法之间存在很大差异。由于映射是在构建时创建的以减少启动时间,因此 Hibernate Search 无法连接到该集群以自动检测该版本。请注意,对于 OpenSearch,您需要用 opensearch:
为版本加前缀;请参见 OpenSearch compatibility。
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.
<1> 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.
<1> For development and tests, we rely on dev-services,
which means Quarkus will start an Elasticsearch cluster automatically.
In production mode, however,
we will want to start an 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-standalone.schema-management.strategy`,quarkus-hibernate-search-standalone-elasticsearch_quarkus-hibernate-search-standalone-schema-management-strategy>>)。 Because we rely on dev-services, the Elasticsearch schema will automatically be dropped and re-created on each application startup in tests and dev mode (unless <<`quarkus.hibernate-search-standalone.schema-management.strategy`,quarkus-hibernate-search-standalone-elasticsearch_quarkus-hibernate-search-standalone-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:
另请参阅 <<`quarkus.hibernate-search-standalone.schema-management.strategy`,quarkus-hibernate-search-standalone-elasticsearch_quarkus-hibernate-search-standalone-schema-management-strategy>>。 See also <<`quarkus.hibernate-search-standalone.schema-management.strategy`,quarkus-hibernate-search-standalone-elasticsearch_quarkus-hibernate-search-standalone-schema-management-strategy>>. |
如需了解有关 Hibernate Search Standalone 扩展配置的更多信息,请参阅 Configuration Reference。 |
For more information about configuration of the Hibernate Search Standalone extension, refer to the configuration-reference. |
Creating a frontend
现在,让我们添加一个简单的网页与我们的 LibraryResource
进行交互。Quarkus 会自动处理位于 META-INF/resources
目录下的静态资源。在 src/main/resources/META-INF/resources
目录中,使用此 index.html 文件中的内容覆盖现有的 index.html
文件:{quickstarts-blob-url}/hibernate-search-standalone-elasticsearch-quickstart/src/main/resources/META-INF/resources/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-standalone-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-standalone-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-standalone-elasticsearch-quickstart-1.0.0-SNAPSHOT-runner
一样简单。
Running it is as simple as executing ./target/hibernate-search-standalone-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 映射所致。我们还索引一些初始数据。 The startup is a bit slower than usual: it is mostly due to us dropping and recreating the Elasticsearch mapping every time at startup. We also index some initial data. 在实际生活中,很显然您不会在每次启动时都执行此操作。 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-standalone.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-standalone.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
).
映射配置器 ( A mapping configurer ( |
以下是对应用编程方式映射的映射配置器的一个示例:
Below is an example of a mapping configurer that applies programmatic mapping:
package org.acme.hibernate.search.elasticsearch.config;
import org.hibernate.search.mapper.pojo.standalone.mapping.StandalonePojoMappingConfigurationContext;
import org.hibernate.search.mapper.pojo.standalone.mapping.StandalonePojoMappingConfigurer;
import org.hibernate.search.mapper.pojo.mapping.definition.programmatic.TypeMappingStep;
import io.quarkus.hibernate.search.standalone.elasticsearch.SearchExtension;
@SearchExtension (1)
public class CustomMappingConfigurer implements StandalonePojoMappingConfigurer {
@Override
public void configure(StandalonePojoMappingConfigurationContext context) {
TypeMappingStep type = context.programmaticMapping() (2)
.type(SomeIndexedEntity.class); (3)
type.searchEntity(); (4)
type.indexed() (5)
.index(SomeIndexedEntity.INDEX_NAME); (6)
type.property("id").documentId(); (7)
type.property("text").fullTextField(); (8)
}
}
1 | Annotate the configurer implementation with the @SearchExtension qualifier
to tell Quarkus it should be used by Hibernate Search Standalone. |
2 | Access the programmatic mapping context. |
3 | Create mapping step for the SomeIndexedEntity type. |
4 | Define SomeIndexedEntity as an entity type for Hibernate Search. |
5 | Define the SomeIndexedEntity entity as indexed. |
6 | Provide an index name to be used for the SomeIndexedEntity entity. |
7 | Define the document id property. |
8 | Define a full-text search field for the text property. |
OpenSearch compatibility
Hibernate Search 与 Elasticsearch 和 OpenSearch 都兼容,但它默认情况下会假定它正在与一个 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-standalone.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.
CDI integration
Injecting entry points
您可以使用 CDI 注入 Hibernate Search 的主要入口点 SearchMapping
:
You can inject Hibernate Search’s main entry point, SearchMapping
, using CDI:
@Inject
SearchMapping searchMapping;
Plugging in custom components
适用于 Hibernate Search Standalone 的 Quarkus 扩展会将用 @SearchExtension
注释的组件自动注入 Hibernate Search。
The Quarkus extension for Hibernate Search Standalone will automatically
inject components annotated with @SearchExtension
into Hibernate Search.
当针对特定后端 (@SearchExtension(backend = "nameOfYourBackend")
)、索引 (@SearchExtension(index = "nameOfYourIndex")
) 或这些元素的组合 (@SearchExtension(backend = "nameOfYourBackend", index = "nameOfYourIndex")
) 时,该注释可以选择性地定位类型,具体取决于正在注入的组件的类型。
The annotation can optionally target a specific
backend (@SearchExtension(backend = "nameOfYourBackend")
), index (@SearchExtension(index = "nameOfYourIndex")
),
or a combination of those
(@SearchExtension(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 application.
请参阅 this section of the reference documentation获取更多信息。
See this section of the reference documentation for more information.
org.hibernate.search.mapper.pojo.standalone.mapping.StandalonePojoMappingConfigurer
-
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 application.
还可以通过 <<`quarkus.hibernate-search-standalone.indexing.plan.synchronization.strategy`,quarkus-hibernate-search-standalone-elasticsearch_quarkus-hibernate-search-standalone-indexing-plan-synchronization-strategy>> 设置为内置实现。
Can also be set to built-in implementations through <<`quarkus.hibernate-search-standalone.indexing.plan.synchronization.strategy`,quarkus-hibernate-search-standalone-elasticsearch_quarkus-hibernate-search-standalone-indexing-plan-synchronization-strategy>>.
请参阅 this section of the reference documentation获取更多信息。
See this section of the reference documentation for more information.
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-standalone.elasticsearch.layout.strategy`,quarkus-hibernate-search-standalone-elasticsearch_quarkus-hibernate-search-standalone-elasticsearch-layout-strategy>> 设置为内置实现。
Can also be set to built-in implementations through <<`quarkus.hibernate-search-standalone.elasticsearch.layout.strategy`,quarkus-hibernate-search-standalone-elasticsearch_quarkus-hibernate-search-standalone-elasticsearch-layout-strategy>>.
See this section of the reference documentation for more information.
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-standalone.elasticsearch.version-check.enabled`,quarkus-hibernate-search-standalone-elasticsearch_quarkus-hibernate-search-standalone-elasticsearch-version-check-enabled>> to
false
. -
Disable schema management on startup by setting the configuration property <<`quarkus.hibernate-search-standalone.schema-management.strategy`,quarkus-hibernate-search-standalone-elasticsearch_quarkus-hibernate-search-standalone-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-standalone.schema-management.strategy
设置为 none
来禁用自动架构创建,则必须在应用程序开始保存/更新实体和执行搜索请求之前,在某个时间手动创建架构。
If you disable automatic schema creation by setting quarkus.hibernate-search-standalone.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.
See this section of the reference documentation for more information.
Loading
作为使用 Elasticsearch 作为主要数据存储的替代方案,此扩展还可以用于索引来自其他数据存储的实体。
As an alternative to using Elasticsearch as a primary datastore, this extension can also be used to index entities coming from another datastore.
在这样的情况下,您需要设置 <<`quarkus.hibernate-search-standalone.mapping.structure`,quarkus-hibernate-search-standalone-elasticsearch_quarkus-hibernate-search-standalone-mapping-structure>> 为与主数据存储的结构匹配的值。 In such a scenario, you will need to set <<`quarkus.hibernate-search-standalone.mapping.structure`,quarkus-hibernate-search-standalone-elasticsearch_quarkus-hibernate-search-standalone-mapping-structure>> to a value matching the structure of the primary datastore. |
为了做到这一点,需要从另一个数据存储加载实体,并且加载必须明确实现。
In order to do this, entities need to be loaded from that other datastore, and such loading must be implemented explicitly.
您可以参考 Hibernate Search 的参考文档,以了解有关配置加载的更多信息:
You can refer to Hibernate Search’s reference documentation for more information about configuring loading:
-
To load entities from an external datasource in order to reindex them, see Mass loading strategy.
-
To load entities from an external datasource when returning search hits, see Selection loading strategy.
在 Quarkus 中,Hibernate Search 的参考文档中提到的实体加载器可以定义为 CDI bean,但仍需要使用 In Quarkus, the entity loader mentioned in Hibernate Search’s reference documentation
can be defined as a plugging-in-custom-components,
but will still need to be attached to particular entities using |
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-standalone.management.enabled=true 2
1 | Enable the management interface. |
2 | Enable Hibernate Search Standalone specific management endpoints. |
一旦启用管理端点,就可以通过 /q/hibernate-search/standalone/reindex
重新对数据建立索引,其中 /q
是默认管理根路径,而 /hibernate-search/standalone/
是默认 Hibernate Search 根管理路径。它 (/hibernate-search/standalone/
) 可以通过配置属性(如下所示)进行更改。
Once the management endpoints are enabled, data can be re-indexed via /q/hibernate-search/standalone/reindex
, where /q
is the default management root path
and /hibernate-search/standalone/
is the default Hibernate Search root management path.
It (/hibernate-search/standalone/
) can be changed via configuration property as shown below.
quarkus.hibernate-search-standalone.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.
要重新为实体类型编制索引,它需要 configured for loading from an external source。 In order to reindex an entity type, it needs to be loading. 如果没有该配置,将通过管理端点(或通过任何其他 API)重新编制索引会失败。 Without that configuration, reindexing through the management endpoint (or through any other API) will fail. |
如果只需要重新索引一部分实体或需要对底层质量索引器进行自定义配置,那么可以通过请求正文(如下所示)传递此信息。
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.
Limitations
-
The Hibernate Search Standalone extension cannot be used in the same application as the Hibernate Search extension with Hibernate ORM[.iokays-translated-fc29e3c917ac553870856a4ad175cfe9] 请参阅 #39517 以跟踪进度。
See #39517 to track progress. * AWS request signing is not available at the moment, unlike in the Hibernate Search extension with Hibernate ORM[.iokays-translated-2978ecebf012e0ef4d26688e27d1fd76] 请参阅 #26991 以跟踪进度。
See #26991 to track progress. * Optimistic concurrency control is not available at the moment.[.iokays-translated-ef0244810d5b91f7afddcf2aacbf0928] 参见 HSEARCH-5105 以跟踪进度。
See HSEARCH-5105 to track progress. * Elasticsearch/OpenSearch do not support transactions, so multi-document updates may fail partially and leave the index in an inconsistent state.[.iokays-translated-c749b475be9a90faead9d01609e875f0] 这无法像 Hibernate Search extension with Hibernate ORM 中采用 coordination through outbox polling 那样避免,因为这种协调需要 Hibernate ORM,并且依赖于数据派生自(事务型)关系数据库的事实。
This cannot be avoided like in the Hibernate Search extension with Hibernate ORM with coordination through outbox polling, because that coordination requires Hibernate ORM and relies on the fact that data is derived from that of a (transactional) relational database.
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#180。
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#180.
Configuration Reference for Hibernate Search Standalone
Unresolved directive in hibernate-search-standalone-elasticsearch.adoc - include::{generated-dir}/config/quarkus-hibernate-search-standalone-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 如果您真的想要在配置属性中使用字符串值来引用 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:
也接受其他格式,但仅适用于高级用例。有关更多信息,请参见 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. |