Hibernate Search 中文操作指南

18. Elasticsearch backend

18.1. Compatibility

18.1.1. Overview

Hibernate Search 的 Elasticsearch 后端与 Elasticsearch 的多个发行版兼容:

有关 Hibernate Search 的哪些版本与 Elasticsearch/OpenSearch 的特定版本兼容,请参考 compatibility matrix

有关 Hibernate Search 的哪些未来版本应该会保留与 Elasticsearch/OpenSearch 当前兼容版本的兼容性,请参考 compatibility policy

如果可能,集群上运行的分发和版本会在启动时自动检测,而 Hibernate Search 会根据该信息进行调整。

使用 Amazon OpenSearch Serverless,或者当您的群集在启动时不可用时,您将必须明确配置 Hibernate Search 应该期望的版本:有关详细信息,请参见 Version compatibility

目标版本对于 Hibernate Search 用户而言大多是透明的,但是 Hibernate Search 根据可能影响你的 Elasticsearch 发行版和版本的行为存在一些差异。以下章节详细介绍了这些差异。

18.1.2. Elasticsearch

Hibernate Search 的 Elasticsearch 后端与运行版本 7.10+ 或 8.x 的 Elasticsearch 集群兼容,并针对版本 7.10、7.17 或 8.14 定期进行测试。

目前,使用 Elasticsearch 不需要特定的配置且不会暗示特定的限制。

18.1.3. OpenSearch

Hibernate Search 的 Elasticsearch 后端与运行版本 1.3 或 2.x 的 OpenSearch 集群兼容,并针对版本 1.3 或 2.14 定期进行测试。

目前使用 OpenSearch 无需特定配置 Hibernate Search 在使用 knn predicate与 OpenSearch 时会应用某些限制。这些限制源自 OpenSearch 的功能可用性,更多详情请参阅 this section of the documentation

18.1.4. Amazon OpenSearch Service

Hibernate Search 的 Elasticsearch 后端与 Amazon OpenSearch Service 兼容,并针对关键版本定期进行测试。

使用亚马逊 OpenSearch 服务需要 proprietary authentication,其中涉及额外的配置。

使用亚马逊 OpenSearch 服务意味着一项限制:在运行 Elasticsearch(而非 OpenSearch)并且仅在 7.1 或更低版本中时, closing indexes is not possible,进而 automatic schema updates ( not recommended in production) will fail when trying to update analyzer definitions

18.1.5. Amazon OpenSearch Serverless (incubating)

以下列出的特性尚处于 incubating 阶段:它们仍在积极开发中。

通常 compatibility policy 不适用:孵化元素(例如类型、方法、配置属性等)的契约在后续版本中可能会以向后不兼容的方式更改,甚至可能被移除。

我们建议您使用孵化特性,以便开发团队可以收集反馈并对其进行改进,但在需要时您应做好更新依赖于这些特性的代码的准备。

Amazon OpenSearch Serverless 兼容性已实现并处于孵化中;随时对 HSEARCH-4867 提供反馈。

但是,请注意:

  1. Hibernate Search 目前未针对 Amazon OpenSearch Serverless 进行测试;请参阅 HSEARCH-4919

  2. 连接到 Amazon OpenSearch Serverless 集群需要涉及额外配置的 proprietary authentication

  3. 必须通过 setting hibernate.search.backend.version to amazon-opensearch-serverless 显式启用与 Amazon OpenSearch Serverless 的兼容性。

此外, Amazon OpenSearch Serverless也有其自身的特定限制:

  1. Closing indexes is not possible,因此 automatic schema updates( not recommended in production) will fail when trying to update analyzer definitions

  2. Distribution/version detection on startup不可行,因此它默认禁用,并且不能明确启用。

  3. Minimal index status requirement用于模式管理不可行,因此它默认禁用,并且不能明确启用。

  4. Purgingflushingrefreshing,或 merging segments is not possible,因此始终会 perform these operations explicitly失败。

  5. 如果在启动时尝试执行清除操作(默认值) mass indexer 将会失败,因为 Amazon OpenSearch Serverless doesn’t support it 。改用 .dropAndCreateSchemaOnStart(…​) 在启动时删除索引。参见 HSEARCH-4930

  6. mass indexer将默认跳过刷新、刷新和合并段操作,并且尝试明确启用它们将导致失败,因为 Amazon OpenSearch Serverless doesn’t support them

  7. 目前不支持 Jakarta Batch integration。请参阅 HSEARCH-4929HSEARCH-4930

18.1.6. Upgrading Elasticsearch

升级 Elasticsearch 集群后,您的集群上仍需要某些操作:Hibernate Search 不会处理这些操作。

除此之外,Elasticsearch 的一些版本之间可能存在一些基本差异。请参阅 Elasticsearch 文档和迁移指南,以识别任何不兼容的模式更改。

在上述情况下,最简单的升级方式是手动删除索引,让 Hibernate Search 重新创建索引及其架构并 reindex your data

18.2. Basic configuration

Elasticsearch 后端的全部配置属性都是可选的,但默认值可能并不适合所有人。尤其是您的生产 Elasticsearch 集群可能无法在 _ http://localhost:9200_ 访问,因此您需要通过 configuring the client 设置集群的地址。

相关配置属性在本文档的相关部分中有所提及。您可以在 the Elasticsearch backend configuration properties appendix中找到可用属性的完整参考。

18.3. Configuration of the Elasticsearch cluster

多数情况下,Hibernate Search 不需要手动向 Elasticsearch 集群应用任何特定配置,而超出 can be automatically generated的索引映射(架构)。

唯一的例外是 Sharding,需要显式启用。

18.4. Client configuration

Elasticsearch 后端通过 REST 客户端与 Elasticsearch 集群通信。以下选项会影响此客户端。

18.4.1. Target hosts

以下属性配置了 Elasticsearch 主机(或主机)以发送索引请求和搜索查询:

hibernate.search.backend.hosts = localhost:9200

此属性的默认值是 localhost:9200

此属性可以设置为表示主机和端口的字符串,例如 localhostes.mycompany.com:4400,或包含多个以逗号分隔的此类主机和端口字符串的字符串,或包含此类主机和端口字符串的 Collection<String>

您可以使用此配置属性更改用于与主机通信的协议:

hibernate.search.backend.protocol = http

此属性的默认值是 http

此属性可以设置为 httphttps

或者,这两种协议和主机都可以使用单个属性定义为一个或多个 URI:

hibernate.search.backend.uris = http://localhost:9200

此属性可以设置为表示 URI 的字符串,例如 _ http://localhost_ 或 _ https://es.mycompany.com:4400_ ,或包含逗号分隔的多重此类 URI 字符串的字符串,或包含此类 URI 字符串的 Collection<String>

对于使用此属性有一些限制:

所有 uri 必须具有相同的协议。

如果设置了 hostsprotocol,则无法使用。

提供的 URI 列表不得为空。

18.4.2. Path prefix

默认情况下,REST API 预计可用于根路径 (/)。例如,一个面向所有索引的搜索查询会被发送到路径 /_search。这是标准 Elasticsearch 设置所需的内容。

如果你的设置非标准,例如因为应用程序和 Elasticsearch 集群之间存在非透明的代理,你可以使用与此类似的配置:

hibernate.search.backend.path_prefix = my/path

通过以上方式,面向所有索引的搜索查询将被发送到 /my/path/_search 路径,而不是 /_search 路径。对于发送到 Elasticsearch 的所有请求,路径的前缀都类似。

18.4.3. Node discovery

在使用自动发现时,Elasticsearch 客户端会定期探测群集中的新节点,并将这些节点添加到主机列表中(请参阅 Client configuration中的 hosts)。

自动发现通过下列属性控制:

hibernate.search.backend.discovery.enabled = false
hibernate.search.backend.discovery.refresh_interval = 10
  1. discovery.enabled 定义该功能是否启用。预期一个布尔值。此属性的默认值为 false

  2. discovery.refresh_interval 定义自动发现两次执行之间的间隔。预期一个以秒为单位的正整数。此属性的默认值为 10

18.4.4. HTTP authentication

HTTP 身份验证默认情况下处于禁用状态,但是可以通过设置以下配置属性启用:

hibernate.search.backend.username = ironman
hibernate.search.backend.password = j@rv1s

这些属性的默认值为一个空字符串。

连接到 Elasticsearch 服务器时要发送的用户名和密码。

如果您使用 HTTP 而不是 HTTPS(见上文),您的密码将以明文形式通过网络传输。

18.4.5. Authentication on Amazon Web Services

完成配置后,Hibernate Search Elasticsearch 后端在大多数设置中会正常运作。但是,如果您需要使用 Amazon OpenSearch ServiceAmazon OpenSearch Serverless,您会发现它们需要一种专用身份验证方法: request signing

虽然请求签名默认情况下不受支持,但是你可以使用附加的依赖项和几分配置启用它。

你需要添加此依赖项:

<dependency>
   <groupId>org.hibernate.search</groupId>
   <artifactId>hibernate-search-backend-elasticsearch-aws</artifactId>
   <version>7.2.0.Alpha2</version>
</dependency>

在类路径中添加此依赖项后,你仍需要对其进行配置。

下列配置是强制要求的:

hibernate.search.backend.aws.signing.enabled = true
hibernate.search.backend.aws.region = us-east-1
  1. aws.signing.enabled 定义是否启用请求签名。预期一个布尔值。默认为 false

  2. aws.region 定义 AWS region。预期一个字符串值。此属性没有默认值,并且必须提供才能进行 AWS 身份验证。

默认情况下,Hibernate Search 将依赖 AWS SDK 的默认凭据提供者。此提供者会在各种位置(Java 系统属性、环境变量、特定于 AWS 的配置等)中查找凭据。有关默认凭据提供者工作方式的更多信息,请参阅以下内容。

或者,你可以用以下选项设置静态凭据:

hibernate.search.backend.aws.credentials.type = static
hibernate.search.backend.aws.credentials.access_key_id = AKIDEXAMPLE
hibernate.search.backend.aws.credentials.secret_access_key = wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY
  1. aws.credentials.type 定义凭证的类型。设置成 default 来获得默认行为(如上所述),或者设置成 static 来使用下面的属性提供凭证。

  2. aws.credentials.access_key_id 定义 access key ID。预期一个字符串值。此属性没有默认值,并且必须在将凭证类型设为 static 时提供。

  3. aws.credentials.secret_access_key 定义 secret access key。预期一个字符串值。此属性没有默认值,并且必须在将凭证类型设为 static 时提供。

18.4.6. Connection tuning

Timeouts

# hibernate.search.backend.request_timeout = 30000 hibernate.search.backend.connection_timeout = 1000 hibernate.search.backend.read_timeout = 30000__request_timeout 定义执行请求时的超时时间。这包括建立连接、发送请求和读取响应所需的时间。此属性默认未定义。

connection_timeout 定义建立连接时的超时。此属性的默认值为 1000

read_timeout 定义读取响应时的超时时间。此属性的默认值为 30000

这些属性需要以毫秒为单位的正数 Integer value,例如 3000

Connection pool

hibernate.search.backend.max_connections = 20 hibernate.search.backend.max_connections_per_route = 10__max_connections 定义到 Elasticsearch 集群的最大同时连接数(包括所有主机)。此属性的默认值为 20

max_connections_per_route 定义与 Elasticsearch 集群的每个主机的最大并发连接数。此属性的默认值为 10

这些属性需要一个正数 Integer value,例如 20

Keep Alive

hibernate.search.backend.max_keep_alive = 10000__max_keep_alive 定义与 Elasticsearch 集群的连接的最长闲置时间。

需要以毫秒为单位的正数 Long value,例如 60000

如果 Elasticsearch 集群的响应包含 Keep-Alive 标头,则有效最大空闲时间为 Keep-Alive 标头或此属性的值(如果已设置)中较低的一个。

如果未设置此属性,则仅考虑 Keep-Alive 标头,如果它不存在,则空闲连接将永久保留。

18.4.7. Custom HTTP client configurations

可以使用 org.apache.http.impl.nio.client.HttpAsyncClientBuilder 的实例直接配置 HTTP 客户端。

使用此 API,您可以添加拦截器、更改连接保持活动、最大连接数、SSL 密钥/信任存储设置以及许多其他客户端配置。

直接配置 HTTP 客户端需要两步:

  • 定义一个实现 org.hibernate.search.backend.elasticsearch.client.ElasticsearchHttpClientConfigurer 接口的类。

  • 通过将配置属性 hibernate.search.backend.client.configurer 设置为指向实现的 bean reference 来配置 Hibernate Search 使用该实现,例如 class:org.hibernate.search.documentation.backend.elasticsearch.client.HttpClientConfigurer

示例 429. 实现和使用 ElasticsearchHttpClientConfigurer
public class HttpClientConfigurer implements ElasticsearchHttpClientConfigurer { (1)

    @Override
    public void configure(ElasticsearchHttpClientConfigurationContext context) { (2)
        HttpAsyncClientBuilder clientBuilder = context.clientBuilder(); (3)
        clientBuilder.setMaxConnPerRoute( 7 ); (4)
        clientBuilder.addInterceptorFirst( (HttpResponseInterceptor) (request, httpContext) -> {
            long contentLength = request.getEntity().getContentLength();
            // doing some stuff with contentLength
        } );
    }
}
示例 430. 在属性中定义自定义 HTTP 客户端配置程序
(1)
hibernate.search.backend.client.configurer = class:org.hibernate.search.documentation.backend.elasticsearch.client.HttpClientConfigurer

自定义 http client配置器定义的任何设置都将覆盖 Hibernate Search 定义的任何其他设置。

18.5. Version compatibility

18.5.1. Version assumed by Hibernate Search

不同发行版和版本的 Elasticsearch/OpenSearch 公开了稍有不同的 API。因此,Hibernate Search 需要了解它要通信的发行版和版本,以生成正确的 HTTP 请求。

默认情况下,Hibernate Search 将在启动时查询 Elasticsearch/OpenSearch 集群以检索此信息,并将推断出要采用的正确行为。

您可以通过将属性 hibernate.search.backend.version 设置为遵循 x.y.z-qualifier<distribution>:x.y.z-qualifier 或仅 <distribution> 格式的版本字符串来强制 Hibernate Search 期望某个特定版本的 Elasticsearch/OpenSearch,其中:

  1. &lt;distribution&gt;elasticopensearchamazon-opensearch-serverless 之一。可选,默认为 elastic

  2. xyz 为整数。x 为必填,yz 为可选。

  3. qualifier 为单词字符(字母数字或 _)字符串。可选。

例如,88.08.9opensearch:2.9amazon-opensearch-service 均为有效的版本字符串。

Amazon OpenSearch Serverless 是一个特例,因为它不使用版本号。

使用该平台时,您必须设置版本,并且必须简单地将它设置为 amazon-opensearch-serverless ,没有拖尾的 : 或版本号。

Hibernate Search 仍会查询 Elasticsearch/OpenSearch 集群以检测该集群的实际分布和版本(不支持的除外,即 Amazon OpenSearch Serverless),以便检查已配置的分布和版本是否与实际版本相匹配。

18.5.2. Disabling the version check on startup

如有必要,您可以在启动时禁用对 Elasticsearch/OpenSearch 集群的调用,并手动提供信息。

为此,将属性 hibernate.search.backend.version_check.enabled 设置为 false

您还必须将属性 _hibernate.search.backend.version_设置为一个版本字符串,如 previous section中所述。

在这种情况下,主版本号和次版本号(上面的格式中的 xy )是必需的,但如果它是默认值 ( elasticsearch ),则 distribution 可以省略,所有其他组件(低级、识别符)仍然是可选的。例如, 8.08.9opensearch:2.9 在这种情况下都是有效的版本字符串,但 8 不够精确。

18.6. Request logging

hibernate.search.backend.log.json_pretty_printing boolean property定义是否将 request logs中包含的 JSON 漂亮打印(缩进,带换行符)。其默认值为 false

18.7. Sharding

有关分片的初步介绍,包括它在 Hibernate Search 中的工作方式以及它的局限性是什么,请参阅 Sharding and routing

Elasticsearch 默认禁用分片。要启用它,请链接:https://www.elastic.co/guide/en/elasticsearch/reference/8.14/index-modules.html# static_index_settings[set the property _index.number_of_shards 在您的集群中]。

18.8. Schema management

Elasticsearch 索引需要在用于编制索引和搜索之前创建;有关如何在 Hibernate Search 中创建索引及其架构的更多信息,请参阅 Managing the index schema

专门针对 Elasticsearch,可以通过以下选项进行一些微调:

# To configure the defaults for all indexes:
hibernate.search.backend.schema_management.minimal_required_status = green
hibernate.search.backend.schema_management.minimal_required_status_wait_timeout = 10000
# To configure a specific index:
hibernate.search.backend.indexes.<index-name>.schema_management.minimal_required_status = green
hibernate.search.backend.indexes.<index-name>.schema_management.minimal_required_status_wait_timeout = 10000
  1. minimal_required_status 定义创建被认为完成之前的索引的最小所需状态。此属性的默认值为 yellow,但 Amazon OpenSearch Serverless 除外,因为该平台不支持索引状态检查,会跳过索引状态检查。

  2. minimal_required_status_wait_timeout 定义以毫秒为单位的 integer value 等待此状态的最大时间。此属性的默认值为 10000

这些属性仅在作为模式管理一部分创建或验证索引时才有效。

18.9. Index layout

Hibernate Search 可使用索引。这意味着 Hibernate Search 中具有给定名称的索引不会直接映射到 Elasticsearch 中具有相同名称的索引。

索引布局是 Hibernate Search 索引名称映射到 Elasticsearch 索引的方式,并且控制该布局的策略在后端级别设置:

hibernate.search.backend.layout.strategy = simple

此属性的默认值为 simple

有关可用策略的详细信息,请参阅以下小节。

18.9.1. simple: the default, future-proof strategy

对于 Hibernate Search 中名称为 myIndex 的索引:

  1. 如果 Hibernate Search creates the index automatically,它将命名索引 myindex-000001,并将自动创建写和读别名。

  2. 写操作(索引、清除、……)将以别名 myindex-write 为目标。

  3. 读操作(搜索、解释、……)将以别名 myindex-read 为目标。

simple 布局比它可能存在的要复杂一些,但它遵循了最佳实践。

使用别名比直接针对索引有显著的优势:它可以在不宕机的情况下对实时应用程序进行完全重新索引,当 listener-triggered indexing 禁用 ( completelypartially ) 且您需要定期(例如每天)完全重新索引时尤其有用。

使用别名,您只需将读取别名(由搜索查询使用)定向到索引的旧副本,而写入别名(由文档写入使用)将被重定向到索引的新副本。在没有别名(特别是使用 no-alias 布局)的情况下,这是不可能的。

这种“零宕机”重新索引与 "blue/green" deployment 有一些共同特点,目前还没有由 Hibernate Search 本身提供。然而,您可以通过直接向 Elasticsearch 的 REST API 发出命令在您的应用程序中实现它。基本操作顺序如下:

创建一个新索引 myindex-000002

将写别名 myindex-writemyindex-000001 切换到 myindex-000002

使用 mass indexer等重新索引。

将读别名 myindex-read_从 _myindex-000001_切换到 _myindex-000002

删除 myindex-000001

请注意,这仅在 Hibernate Search 映射没有更改时才有效;具有不断变化的模式的零宕机升级将更加复杂。您将在 HSEARCH-2861HSEARCH-3499 中找到关于此主题的讨论。

18.9.2. no-alias: a strategy without index aliases

此策略主要对旧版集群有用。

对于 Hibernate Search 中名称为 myIndex 的索引:

  1. 如果 Hibernate Search creates the index automatically,它将索引命名为 myindex,并且不会创建任何别名。

  2. 写入操作(索引、清除、…​)按名称直接针对索引,myindex

  3. 读取操作(搜索、解释、…​)按名称直接针对索引 myindex

18.9.3. Custom strategy

如果内置布局策略不符合您的要求,您可以通过两个简单步骤定义自定义布局:

  • 定义实现了界面 _org.hibernate.search.backend.elasticsearch.index.layout.IndexLayoutStrategy_的类。

  • 通过将配置属性 hibernate.search.backend.layout.strategy_设置为引用实现的 bean reference,比如 _class:com.mycompany.MyLayoutStrategy,配置后端以使用该实现。

例如,下面的实现将为名为 myIndex 的索引生成以下布局:

  1. 写操作(索引、清除、……)将以别名 myindex-write 为目标。

  2. 读取操作(搜索、解释、…​)将针对别名 myindex(无后缀)。

  3. 如果 Hibernate Search creates the index automatically2017 年 11 月 6 日 19:19:00,它将索引命名为 myindex-20171106-191900-000000000

示例 431. 使用 Elasticsearch 后端实现自定义索引布局策略
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

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

public class CustomLayoutStrategy implements IndexLayoutStrategy {

    private static final DateTimeFormatter INDEX_SUFFIX_FORMATTER =
            DateTimeFormatter.ofPattern( "uuuuMMdd-HHmmss-SSSSSSSSS", Locale.ROOT )
                    .withZone( ZoneOffset.UTC );
    private static final Pattern UNIQUE_KEY_PATTERN =
            Pattern.compile( "(.*)-\\d+-\\d+-\\d+" );

    @Override
    public String createInitialElasticsearchIndexName(String hibernateSearchIndexName) {
        // Clock is Clock.systemUTC() in production, may be overridden in tests
        Clock clock = MyApplicationClock.get();
        return hibernateSearchIndexName + "-"
                + INDEX_SUFFIX_FORMATTER.format( Instant.now( clock ) );
    }

    @Override
    public String createWriteAlias(String hibernateSearchIndexName) {
        return hibernateSearchIndexName + "-write";
    }

    @Override
    public String createReadAlias(String hibernateSearchIndexName) {
        return hibernateSearchIndexName;
    }

    @Override
    public String extractUniqueKeyFromHibernateSearchIndexName(
            String hibernateSearchIndexName) {
        return hibernateSearchIndexName;
    }

    @Override
    public String extractUniqueKeyFromElasticsearchIndexName(
            String elasticsearchIndexName) {
        Matcher matcher = UNIQUE_KEY_PATTERN.matcher( elasticsearchIndexName );
        if ( !matcher.matches() ) {
            throw new IllegalArgumentException(
                    "Unrecognized index name: " + elasticsearchIndexName
            );
        }
        return matcher.group( 1 );
    }
}

18.9.4. Retrieving index or alias names

用于读写的索引或别名名称可从 metamodel中检索。

示例 432. 从 Elasticsearch 索引管理器中检索索引名称
SearchMapping mapping = /* ... */ (1)
IndexManager indexManager = mapping.indexManager( "Book" ); (2)
ElasticsearchIndexManager esIndexManager = indexManager.unwrap( ElasticsearchIndexManager.class ); (3)
ElasticsearchIndexDescriptor descriptor = esIndexManager.descriptor();(4)
String readName = descriptor.readName();(5)
String writeName = descriptor.writeName();(5)

18.10. Schema ("mapping")

Elasticsearch 中所谓的架构是分配给每个索引的架构,它指定每个“属性”的数据类型和功能(在 Hibernate Search 中称为“索引字段”)。

在大部分情况下,Elasticsearch 映射是从 the mapping configured through Hibernate Search’s mapping APIs推断出来的,这些映射是通用的,并且与 Elasticsearch 无关。

本节对针对 Elasticsearch 后端的一些特定方面进行了说明。

Hibernate Search 可以配置为在通过 schema management 创建索引时将映射推送到 Elasticsearch。

18.10.1. Field types

Available field types

Elasticsearch 后端并不直接支持某些类型,但它们仍然可以工作,因为它们由映射器“桥接”。例如,实体模型中的 java.util.Date “桥接”到 Elasticsearch 后端支持的 java.time.Instant。有关更多信息,请参阅 Supported property types

不在此列表中的字段类型仍然可以使用更少的工作:

如果实体模型中的属性具有不受支持的类型,但可以转换为受支持的类型,则需要桥接。请参见 Binding and bridges

如果您需要一个 Hibernate Search 不支持的特定类型的索引字段,您将需要定义本机字段类型的桥接器。请参阅 Index field type DSL extension

表 14. Elasticsearch 后端支持的字段类型

Field type

Data type in Elasticsearch

Limitations

java.lang.String

text 如果定义了分析器,否则 keyword

-

java.lang.Byte

byte

-

java.lang.Short

short

-

java.lang.Integer

integer

-

java.lang.Long

long

-

java.lang.Double

double

-

java.lang.Float

float

-

java.lang.Boolean

boolean

-

java.math.BigDecimal

scaled_float scaling_factor 等于 10^decimalScale

-

java.math.BigInteger

scaled_float scaling_factor 等于 10^decimalScale

-

java.time.Instant

date with format uuuu-MM-dd’T’HH:mm:ss.SSSSSSSSSZZZZZ

Lower range/resolution

java.time.LocalDate

date with format uuuu-MM-dd

Lower range/resolution

java.time.LocalTime

date with format HH:mm:ss.SSSSSSSSS

Lower range/resolution

java.time.LocalDateTime

date with format uuuu-MM-dd’T’HH:mm:ss.SSSSSSSSS

Lower range/resolution

java.time.ZonedDateTime

date with format uuuu-MM-dd’T’HH:mm:ss.SSSSSSSSSZZZZZ'['VV']'

Lower range/resolution

java.time.OffsetDateTime

date with format uuuu-MM-dd’T’HH:mm:ss.SSSSSSSSSZZZZZ

Lower range/resolution

java.time.OffsetTime

date with format HH:mm:ss.SSSSSSSSSZZZZZ

Lower range/resolution

java.time.Year

date with format uuuu

Lower range/resolution

java.time.YearMonth

date with format uuuu-MM

Lower range/resolution

java.time.MonthDay

date 格式为 uuuu-MM-ddThe year is always set to 0

-

GeoPoint

geo_point

-

日期/时间字段的范围和精度Elasticsearch date 类型不支持 java.time 类型可以表示的全部年份范围:

_java.time_可以表示从 _-999.999.999_到 _999.999.999_的年份。

Elasticsearch 的 _date_类型支持从年份 _-292.275.054_到年份 _292.278.993_的时间范围。

超出范围的值会触发索引失败。

精度也较低:

java.time 支持纳秒精度。

Elasticsearch 的 _date_类型支持毫秒分辨率。

索引时,毫秒精度以上的精度会丢失。

Index field type DSL extension

并非所有 Elasticsearch 字段类型都在 Hibernate Search 中得到内置支持。但是,通过利用“原生”字段类型,仍然可以使用不受支持的字段类型。使用此字段类型,Elasticsearch“映射”可直接定义为 JSON,就能访问 Elasticsearch 提供的一切功能。

以下是如何使用 Elasticearch“原生”类型的示例。

示例 433. 使用 Elasticearch “本机”类型
public class IpAddressValueBinder implements ValueBinder { (1)
    @Override
    public void bind(ValueBindingContext<?> context) {
        context.bridge(
                String.class,
                new IpAddressValueBridge(),
                context.typeFactory() (2)
                        .extension( ElasticsearchExtension.get() ) (3)
                        .asNative() (4)
                                .mapping( "{\"type\": \"ip\"}" ) (5)
        );
    }

    private static class IpAddressValueBridge implements ValueBridge<String, JsonElement> {
        @Override
        public JsonElement toIndexedValue(String value,
                ValueBridgeToIndexedValueContext context) {
            return value == null ? null : new JsonPrimitive( value ); (6)
        }

        @Override
        public String fromIndexedValue(JsonElement value,
                ValueBridgeFromIndexedValueContext context) {
            return value == null ? null : value.getAsString(); (7)
        }
    }
}
@Entity
@Indexed
public class CompanyServer {

    @Id
    @GeneratedValue
    private Integer id;

    @NonStandardField( (1)
            valueBinder = @ValueBinderRef(type = IpAddressValueBinder.class) (2)
    )
    private String ipAddress;

    // Getters and setters
    // ...

}

18.10.2. Entity type name mapping

当 Hibernate Search 执行针对多个实体类型(即多个索引)的搜索查询时,它需要确定每个搜索结果的 entity type,以便将其映射回一个实体。

有多种策略用于处理此“实体类型名称解析”,每种策略各有优缺点。

此策略在后端级别设置:

hibernate.search.backend.mapping.type_name.strategy = discriminator

此属性的默认值为 discriminator

有关可用策略的详细信息,请参阅以下小节。

discriminator: type name mapping using a discriminator field

使用 discriminator 策略,判别符字段用于直接从每个文档检索实体类型名称。

索引时,__entity_type_字段会自动填充每个文档的 entity type名称。

搜索时,_entity_type 字段的 docvalues 会透明地从 Elasticsearch 请求并从响应中提取。

优点:

  1. 针对 index aliases工作时正确运行。

缺点:

  1. 存储开销小:每个文档的存储几个字节。

  2. 如果 entity名称更改,则需要 full reindexing,即使索引名称未更改。

index-name: type name mapping using the index name

使用 _index-name_策略,为每个搜索结果返回的 __index_元字段用于解析索引名称,然后解析 entity type名称。

优点:

  1. No storage overhead.

缺点:

  1. 依赖实际索引名称,而不是别名,因为 Elasticsearch 返回的 _index_元字段包含实际索引名称(例如 _myindex-000001),而不是别名(例如 myindex-read)。因此,如果索引不遵循默认命名方案 &lt;hibernateSearchIndexName&gt;-&lt;6 digits&gt;,则必须配置自定义 index layout

18.10.3. Dynamic mapping

默认情况下,Hibernate Search 将 Elasticsearch 索引映射中的 dynamic 属性设置为 strict 。这意味着尝试索引映射中不存在字段的文档将导致索引失败。

如果 Hibernate Search 是唯一的客户端,这不会成问题,因为 Hibernate Search 通常仅对已声明的模式字段进行操作。对于我们需要更改此设置的其他情况,可以使用以下索引级别属性更改值。

# To configure the defaults for all indexes:
hibernate.search.backend.dynamic_mapping = strict
# To configure a specific index:
hibernate.search.backend.indexes.<index-name>.dynamic_mapping = strict

此属性的默认值为 strict

我们说过 Hibernate Search 通常在已声明的模式字段上工作。更确切地说,如果未定义 Dynamic fields with field templates ,它始终会这样。当定义字段模板时, dynamic 将被强制为 true ,以允许动态字段。在这种情况下, dynamic_mapping 属性的值将被忽略。

18.10.4. Multi-tenancy

根据在当前会话中定义的租户 ID,多租户功能得到支持并且会以透明的方式处理:

  1. 文档将会通过相应的值进行索引,允许稍后进行过滤;

  2. 查询将适当过滤结果。

如果在映射器中启用了多租户,则在后端中会自动启用多租户,例如,如果 a multi-tenancy strategy is selected in Hibernate ORM,或者如果 multi-tenancy is explicitly configured in the Standalone POJO mapper

但是,可以手动启用多租户功能。

多租户策略是在后端级别设置的:

hibernate.search.backend.multi_tenancy.strategy = none

有关可用策略的详细信息,请参阅以下小节。

none: single-tenancy

none 策略(默认策略)完全禁用多租户功能。

尝试设置租户 ID 会导致索引编制失败。

discriminator: type name mapping using the index name

使用 discriminator 策略,所有租户的所有文档都存储在同一个索引中。每个文档的 Elasticsearch ID 设置为租户 ID 和原始 ID 的连接。

在索引时,为每个文档透明填充两个字段:

  1. _tenant_id:“discriminator”字段,保存租户 ID。

  2. _tenant_doc_id:保存原始(租户范围)文档 ID 的字段。

在搜索时,将针对租户 ID 字段的过滤器透明添加到搜索查询,以便仅返回当前租户的搜索结果。ID 字段用于检索原始文档 ID。

18.10.5. Custom index mapping

Basics

Hibernate Search 可以 create and validate indexes,但默认情况下,创建的索引仅包括编制索引和搜索所需的最低限度:映射和分析设置。如果您需要自定义一些 mapping parameters,可以向 Hibernate Search 提供自定义映射:在创建索引时,它将包括自定义映射。

Hibernate Search 映射与自定义 Elasticsearch 映射的一致性将不会以任何方式得到检查。您有责任确保映射中的任何覆盖都可以工作,例如您不会将索引字段的类型从 text 更改为 integer ,或对用于排序的字段禁用 doc_values

无效的自定义映射在引导时可能不会触发任何异常,但在以后索引或查询时会触发。在最坏的情况下,它可能不会触发任何异常,而只是导致错误的搜索结果。极其谨慎。

# To configure the defaults for all indexes:
hibernate.search.backend.schema_management.mapping_file = custom/index-mapping.json
# To configure a specific index:
hibernate.search.backend.indexes.<index-name>.schema_management.mapping_file = custom/index-mapping.json
示例 434. custom/index-mapping.json 文件可能的內容
{
  "properties":{
    "userField":{
      "type":"keyword",
      "index":true,
      "norms":true,
      "doc_values":true
    },
    "userObject":{
      "dynamic":"true",
      "type":"object"
    }
  },
  "_source": {
    "enabled": false
  }
}

仅在自定义映射文件中定义但尚未由 Hibernate Search 映射的属性将对 Hibernate Search 不可见。

这意味着如果您尝试在 Search DSL 中引用这些属性,或者尝试在 writing to a document from a bridge 中引用这些属性,Hibernate Search 将抛出异常。

该文件不需要包含完整的映射:Hibernate Search 会自动将缺少的属性(索引字段)注入给定的映射。

将如下处理给定映射与 Hibernate Search 生成的映射之间的冲突:

  • dynamic_templates/_routing/_dynamic_映射参数将是从给定映射中的参数,回退到 Hibernate Search 生成的值(如果有)。

  • 除了映射根目录的 _properties_之外的任何其他映射参数都将是从给定映射中的参数;Hibernate Search 生成的那些参数将被忽略。

  • _properties_将合并,使用在给定映射和由 Hibernate Search 生成的映射中定义的属性。

  • 如果一侧和另一侧都定义了属性,它将递归合并,按照步骤 1-4 执行。

在上面的示例中,合成的结果映射可能如下所示:

示例 435. 将 custom/index-mapping.json 的内容与 Hibernate Search 映射合并后的可能的映射结果
{
  "_source":{
    "enabled":false
  },
  "dynamic":"strict",
  "properties":{
    "_entity_type":{ (1)
      "type":"keyword",
      "index":false
    },
    "title":{ (2)
      "type":"text",
      "analyzer":"english"
    },
    "userField":{
      "type":"keyword",
      "norms":true
    },
    "userObject":{
      "type":"object",
      "dynamic":"true"
    }
  }
}
Disabling _source

使用该功能,可以 disable the _source field 。例如,您可以传递以下 custom/index-mapping.json 文件:

示例 436. 可能的 custom/index-mapping.json 文件内容以禁用 _source 字段
{
  "_source": {
    "enabled": false
  }
}

禁用 _source 便于减小文件系统中 Elasticsearch 索引的大小,但这是有代价的。

一些 projections 依赖于启用 source 。如果您尝试在禁用 source 的情况下使用投影,则行为未定义:搜索查询可能返回 null 次命中,也可能完全失败并出现异常。

18.11. Analysis

18.11.1. Basics

Analysis 是由分析器执行的文本处理,包括在索引编制(文档处理)时和在搜索(查询处理)时。

全部 built-in Elasticsearch analyzers均可立即使用,无需在 Hibernate Search 中进行任何配置:只需在 Hibernate Search 期望为分析器名称的任何位置使用其名称即可。但是,分析也可以显式配置。

Elasticsearch 分析配置不会在启动时立即应用:它需要推送到 Elasticsearch 集群。

只有在通过 schema management 告知 Hibernate Search 之后,它才会将配置推送到集群。

要配置 Elasticsearch 后端中的分析,您需要:

  • 定义实现了 _org.hibernate.search.backend.elasticsearch.analysis.ElasticsearchAnalysisConfigurer_界面的类。

  • 通过将配置属性 hibernate.search.backend.analysis.configurer_设置为指向实现的 bean reference(例如 _class:com.mycompany.MyAnalysisConfigurer),将后端配置为使用该实现。

当启动时,Hibernate Search 将调用此实现的 configure 方法,而配置器能够充分利用 DSL 定义 analyzers and normalizers

可将不同的分析配置器分配给每个索引:

# To set the default configurer for all indexes: hibernate.search.backend.analysis.configurer = class:com.mycompany.MyAnalysisConfigurer # To assign a specific configurer to a specific index: hibernate.search.backend.indexes.<index-name>.analysis.configurer = class:com.mycompany.MySpecificAnalysisConfigurer # To set the default configurer for all indexes: hibernate.search.backend.analysis.configurer = class:com.mycompany.MyAnalysisConfigurer # To assign a specific configurer to a specific index: hibernate.search.backend.indexes.<index-name>.analysis.configurer = class:com.mycompany.MySpecificAnalysisConfigurer 如果将特定配置器分配给索引,则该索引将忽略默认配置器:只考虑来自特定配置器的定义。

18.11.2. Built-in analyzers

开箱即用的内置分析器不需要显式配置。如有必要,通过用相同名称定义自己的分析器可以覆盖它们。

Elasticsearch 后端带有几个内置分析器。确切的列表取决于 Elasticsearch 的版本,且可在 here 中找到。

无论 Elasticsearch 版本如何,名称在 org.hibernate.search.engine.backend.analysis.AnalyzerNames 中列为常量的分析器始终可用:

default

@FullTextField 默认使用的分析器。

这只是 standard 的别名。

standard

默认行为:首先,使用标准标记化器进行标记化,该标记化器遵循 Unicode 文本分段算法的单词分隔规则,如 Unicode Standard Annex #29中指定的那样。然后,将每个标记小写。

simple

默认行为:首先在非字母字符处拆分文本。然后将每个单词转换为小写。

whitespace

默认行为:在空格字符处拆分文本。不更改单词。

stop

默认行为:首先在非字母字符处拆分文本。然后将每个单词转换为小写。最后,删除英语停用词。

keyword

默认行为:不以任何方式更改文本。

通过这个分析器,全文字段的行为将类似于关键字字段,但功能更少:例如,没有词组聚合。

请考虑改用 @KeywordField

18.11.3. Built-in normalizers

Elasticsearch 后端不提供任何内置规范化器。

18.11.4. Custom analyzers and normalizers

传递给配置器的上下文采用了 DSL 来定义分析器和规范化器:

示例 437. 使用 Elasticsearch 后端实施和使用分析配置器来定义分析器和规范化器
package org.hibernate.search.documentation.analysis;

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

public class MyElasticsearchAnalysisConfigurer implements ElasticsearchAnalysisConfigurer {
    @Override
    public void configure(ElasticsearchAnalysisConfigurationContext context) {
        context.analyzer( "english" ).custom() (1)
                .tokenizer( "standard" ) (2)
                .charFilters( "html_strip" ) (3)
                .tokenFilters( "lowercase", "snowball_english", "asciifolding" ); (4)

        context.tokenFilter( "snowball_english" ) (5)
                .type( "snowball" )
                .param( "language", "English" ); (6)

        context.normalizer( "lowercase" ).custom() (7)
                .tokenFilters( "lowercase", "asciifolding" );

        context.analyzer( "french" ).custom() (8)
                .tokenizer( "standard" )
                .tokenFilters( "lowercase", "snowball_french", "asciifolding" );

        context.tokenFilter( "snowball_french" )
                .type( "snowball" )
                .param( "language", "French" );
    }
}
(1)
hibernate.search.backend.analysis.configurer = class:org.hibernate.search.documentation.analysis.MyElasticsearchAnalysisConfigurer

也可以向带参数的内置分析器分配一个名称:

示例 438. 在 Elasticsearch 后端中命名参数化内置分析器
context.analyzer( "english_stopwords" ).type( "standard" ) (1)
        .param( "stopwords", "_english_" ); (2)

要了解有哪些分析器、字符过滤器、分词器和分词器过滤器可用,请参阅文档:

  1. 如果您想使用内置分析器,而不是创建自己的分析器: analyzers

  2. 如果您想定义自己的分析器: character filterstokenizerstoken filters

18.11.5. Overriding the default analyzer

使用 @FullTextField 但未明确指定分析器时的默认分析器名为 default

如同其他 built-in analyzer 一样,通过定义同名 custom analyzer,可以替代默认分析器:

示例 439. 在 Elasticsearch 后端中覆盖默认分析器
package org.hibernate.search.documentation.analysis;

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

public class MyElasticsearchAnalysisConfigurer implements ElasticsearchAnalysisConfigurer {
    @Override
    public void configure(ElasticsearchAnalysisConfigurationContext context) {
        context.analyzer( "english" ).custom() (1)
                .tokenizer( "standard" ) (2)
                .charFilters( "html_strip" ) (3)
                .tokenFilters( "lowercase", "snowball_english", "asciifolding" ); (4)

        context.tokenFilter( "snowball_english" ) (5)
                .type( "snowball" )
                .param( "language", "English" ); (6)

        context.normalizer( "lowercase" ).custom() (7)
                .tokenFilters( "lowercase", "asciifolding" );

        context.analyzer( "french" ).custom() (8)
                .tokenizer( "standard" )
                .tokenFilters( "lowercase", "snowball_french", "asciifolding" );

        context.tokenFilter( "snowball_french" )
                .type( "snowball" )
                .param( "language", "French" );
    }
}
(1)
hibernate.search.backend.analysis.configurer = class:org.hibernate.search.documentation.analysis.DefaultOverridingElasticsearchAnalysisConfigurer

18.12. Custom index settings

Hibernate Search 可以 create and validate indexes,但是根据默认设置创建的索引只包含索引和搜索所需的最低限度:映射和分析设置。如果需要设置某些 custom index settings,可以将其提供给 Hibernate Search:在创建索引和验证索引时会包含这些设置。

# To configure the defaults for all indexes:
hibernate.search.backend.schema_management.settings_file = custom/index-settings.json
# To configure a specific index:
hibernate.search.backend.indexes.<index-name>.schema_management.settings_file = custom/index-settings.json
示例 440. Possible content of custom/index-settings.json file
{
  "number_of_shards": "3",
  "number_of_replicas": "3",
  "analysis": {
    "analyzer": {
      "my_standard-english": {
        "type": "standard",
        "stopwords": "_english_"
      },
      "my_analyzer_ngram": {
        "type": "custom",
        "tokenizer": "my_analyzer_ngram_tokenizer"
      }
    },
    "tokenizer": {
      "my_analyzer_ngram_tokenizer": {
        "type": "ngram",
        "min_gram": "5",
        "max_gram": "6"
      }
    }
  }
}

所提供的设置将与由 Hibernate Search 生成的设置合并,包括分析器定义。当通过 analysis configurer 和自定义设置配置分析时,将未定义该行为;不应依赖它。

自定义索引设置必须以简化形式提供,即不带属性 the index 属性。

18.12.1. Max result window size

如果使用设置 index.max_result_window,则 Hibernate Search 将使用此值限制返回的命中数大小,如果用户未在查询中定义限制。在这种情况下,如果 Hibernate Search 注意到还有更多结果,则将记录警告。

18.13. Threads

Elasticsearch 后端依赖于内部线程池来协调索引请求(添加/更新/删除)和计划请求超时。

默认情况下,此池包含的线程数恰好等于引导时 JVM 可用的处理器数。可以使用配置属性更改此设置:

hibernate.search.backend.thread_pool.size = 4

每索引这个数字都是 per backend,而不是每个索引。添加更多索引不会添加更多线程。

由于此线程池中发生的所有操作都是无阻塞的,因此将其大小提升到超出 JVM 可用的处理器内核数不会带来明显的性能优势。

更改此设置的唯一原因是减少线程数;例如,在具有单个索引和单个索引队列的应用程序中,在具有 64 个处理器内核的机器上运行时,您可能想减少线程数。

18.14. Indexing queues

在 Hibernate Search 发送给 Elasticsearch 的所有请求中,预期会有许多“索引”请求来创建/更新/删除特定文档。逐个发送这些请求会很低效(主要是因为网络延迟)。此外,我们通常希望保留这些请求的相对顺序,当它们与同一文档相关时。

由于这些原因,Hibernate Search 将这些请求推送到有序队列并依靠 Bulk API 分批发送它们。每个索引维护 10 个队列,每个队列最多包含 1000 个元素,每个队列将发送最多包含 100 个索引请求的批量请求。队列独立(并行)操作,但每个队列都会逐个发送一个批量请求,因此在任何给定时间,每个索引最多可以发送 10 个批量请求。

相对于同一文档 ID 的索引操作始终会被推送到同一队列。

为了降低 Elasticsearch 服务器上的负载,或相反,为了提高吞吐量,可以自定义队列。这可通过以下配置属性完成:

# To configure the defaults for all indexes:
hibernate.search.backend.indexing.queue_count = 10
hibernate.search.backend.indexing.queue_size = 1000
hibernate.search.backend.indexing.max_bulk_size = 100
# To configure a specific index:
hibernate.search.backend.indexes.<index-name>.indexing.queue_count = 10
hibernate.search.backend.indexes.<index-name>.indexing.queue_size = 1000
hibernate.search.backend.indexes.<index-name>.indexing.max_bulk_size = 100
  1. indexing.queue_count 定义队列数。需要严格为正整数。此属性的默认值为 10

较高值会导致并行使用更多连接,这可能会提高索引吞吐量,但会产生 overloading Elasticsearch 的风险,导致 Elasticsearch 放弃一些请求并导致索引失败。

  1. indexing.queue_size 定义每个队列能容纳的最大元素数。需要严格为正整数。此属性的默认值为 1000

较低值可能导致较低的内存使用量,尤其是在存在大量队列时,但值过低会降低达到最大批量大小的可能性,并增加 application threads blocking 的可能性,原因是队列已满,这可能会导致降低索引吞吐量。

  1. indexing.max_bulk_size 定义每个批量请求中的最大索引请求数。需要严格的正整数值。此属性的默认值为 100

较高值会导致在发送给 Elasticsearch 的每个 HTTP 请求中发送更多文档,这可能会提高索引吞吐量,但会产生 overloading Elasticsearch 的风险,导致 Elasticsearch 放弃一些请求并导致索引失败。

请注意,将此数字提高到高于队列大小时不起作用,因为批量不能包含比队列中包含的更多请求。

当队列已满时,任何请求索引的尝试都会阻塞,直到该请求可以放入队列。

为了达到合理的性能水平,务必将队列的大小设置为足够高的数字,以便仅在应用程序负载非常高时才会发生此类阻塞。

Elasticsearch 节点只能处理如此多的并行请求,具体而言,它们在任何给定时间都 limit the amount of memory 可用于存储所有待处理请求。

为了避免索引故障,请避免为队列数和最大批量大小使用过大的数字,尤其是当您希望您的索引包含大量文档时。

18.15. Writing and reading

有关在 Hibernate

18.15.1. Commit

当写入索引时,Elasticsearch 依靠 transaction log 来确保更改(即使是未提交的更改)在 REST API 调用返回后始终是安全的。

因此,“提交”的概念对 Elasticsearch 后端并不重要,提交要求在很大程度上无关紧要。

18.15.2. Refresh

当从索引中读取时,Elasticsearch 依靠周期性刷新的索引读取器,这意味着搜索查询将返回稍过时的结果,除非强制刷新:这称为 near-real-time 行为。

默认情况下,索引读取器每秒都会刷新一次,但可以通过索引设置在 Elasticsearch 端进行自定义:请参阅 refresh_intervalthis page 处的设置。

使用 Elasticsearch 后端进行搜索依赖于 same APIs as any other backend

本节详细介绍与搜索相关的 Elasticsearch 特定配置。

18.16.1. Scroll timeout

使用 Elasticsearch 后端, scrolls 会受到超时限制。如果 next() 没有长时间被调用(默认值为 60 秒),则滚动将自动关闭,且下次调用 next() 时会失败。

在后端级别使用以下配置属性配置超时(以秒为单位):

hibernate.search.backend.scroll_timeout = 60

此属性的默认值为 60

18.16.2. Partial shard failure

使用 Elasticsearch 后端, fetching results 可能会导致部分分片失败,即一些分片将无法产生结果,而其他分片会成功。在这种情况中,Elasticsearch 集群将生成一个响应,该响应的状态代码为成功状态代码,但将包含关于失败分片的其他信息,以及它们失败的原因。

默认情况下,Hibernate Search 将在获取结果时检查是否有任何分片失败,如果失败将抛出一个异常。

使用后端级别的以下配置属性来更改默认行为:

hibernate.search.backend.query.shard_failure.ignore = true

该属性的默认值为 false