本文章内容根据 Elastic Search Java API Client 7.17 版本官方文档 内容翻译而来,用于方便后续学习翻阅
序言
这是适用于 Elasticsearch 的官方 Java API Client 文档。该客户端针对所有 Elasticsearch API 提供强类型的请求和响应。
功能
- 所有 Elasticsearch API 的强类型请求和响应:为每个 API 的请求与响应赋予明确类型,确保数据的准确性与一致性。
- 所有 API 的阻塞式和异步式版本:开发者既可选择阻塞式操作,以顺序执行任务;也可选择异步式操作,实现非阻塞的高效处理。
- 使用流畅的构建器与函数式模式:在创建复杂嵌套结构时,能让代码简洁易读,提高开发效率。
- 通过对象映射器无缝集成应用类:如使用 Jackson 或任何 JSON-B 实现,方便将应用类与客户端进行整合。
- 将协议处理委托给 HTTP 客户端 :例如 Java Low Level REST Client,它负责处理所有传输层相关事务,如 HTTP 连接池管理、重试机制、节点发现等。
Elasticsearch 服务器兼容性策略
Elasticsearch Java 客户端具有向前兼容性,这意味着客户端支持与更大或同等小版本的Elasticsearch 通信。Elasticsearch 语言客户端仅对默认发行版提供向后兼容性,且不做任何保证。
起步
安装
要求:
- Java 8 或更高版本:这意味着你需要在系统上安装 Java 运行环境,且版本必须是 8 及其后续版本,因为该 Java API Client 是基于此 Java 版本及以上开发和运行的,它可能使用了 Java 8 及其后续版本的新特性和改进,以提供更好的性能和功能。
- 一个 JSON 对象映射库:其目的是使你的应用程序类与 Elasticsearch API 实现无缝集成。Java 客户端支持使用 Jackson 或者像 Eclipse Yasson 这样的 JSON-B 库。JSON 对象映射库在将 Java 对象与 JSON 数据进行相互转换时发挥着关键作用,这对于与 Elasticsearch 这样以 JSON 作为数据传输和存储格式的系统进行交互至关重要。使用 Jackson 或 JSON-B 库,可以方便地将 Java 对象序列化为 JSON 数据发送给 Elasticsearch,也可以将从 Elasticsearch 接收到的 JSON 数据反序列化为 Java 对象,以便在 Java 程序中处理。
发布版本 :
该软件的发布版本托管在 Maven Central 上。如果你需要一个快照(SNAPSHOT)版本,可以在以下地址找到 Elastic Maven 快照存储库:https://snapshots.elastic.co/maven/。Maven Central 是一个广泛使用的 Maven 仓库,包含了大量开源项目的发布版本,开发人员可以通过 Maven 构建工具轻松从这里获取所需的依赖。而快照版本通常是正在开发中的不稳定版本,存储在 Elastic Maven 快照存储库中,供开发者提前测试新功能或获取最新开发进展,但要注意其可能存在的不稳定性。
在 Gradle 项目中使用 Jackson 进行安装。
json
dependencies {
implementation 'co.elastic.clients:elasticsearch-java:7.17.27'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.0'
}
在 Maven 项目中使用 Jackson 进行安装。
xml
<project>
<dependencies>
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>7.17.27</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.0</version>
</dependency>
</dependencies>
</project>
如果遇到 ClassNotFoundException:jakarta. json.spi.JsonProvider
设置依赖项后,您的应用程序可能会失败,
ClassNotFoundException:jakarta. json.spi.JsonProvider
。
如果发生这种情况,您必须显式添加 jakarta. json:jakarta.json-api:2.0.1
依赖项。
Gradle:
json
dependencies {
...
implementation 'jakarta.json:jakarta.json-api:2.0.1'
}
Maven:
xml
<project>
<dependencies>
...
<dependency>
<groupId>jakarta.json</groupId>
<artifactId>jakarta.json-api</artifactId>
<version>2.0.1</version>
</dependency>
</dependencies>
</project>
为什么需要这个?
一些框架,如Spring Boot或Helidon,带有Gradle和Maven插件或Maven BOM文件,以简化开发和依赖管理。这些插件和BOM定义了许多知名库使用的版本。
其中一个库可以是
jakarta. json:json-api
,它定义了标准JavaJSON API。在1.x
版中,这个库使用javax.json
包,而在2.x
版中,它在从JavaEE过渡到JakartaEE
后使用jakarta.json
包。JavaAPI Client依赖于该库的版本
2.0.1
,以便使用更新且面向未来的jakarta. json包。但是一些构建插件和BOM覆盖了 JavaAPI Client 的依赖项,以在旧的javax.json
命名空间中使用版本1.x
,导致ClassNotFoundException:jakarta.json.spi.JsonProvider
。
添加正确的版本作为 顶级项目(top-level project)依赖项可以解决问题。
如果您的应用程序还需要
javax. json
,您可以添加javax.json:javax.json-api:1.1.4
依赖项,相当于jakarta.json:jakarta.json-api:1.1.6
。
连接
Java API 客户端围绕三个主要组件构建:
- API 客户端类 。这些类为 Elasticsearch API 提供强类型数据结构和方法。由于 Elasticsearch API 规模较大,它按功能组(也称为"命名空间")进行结构化,每个功能组都有自己的客户端类。Elasticsearch 的核心功能在
ElasticsearchClient
类中实现。 - JSON对象映射器。这将你的应用程序类映射为 JSON,并与 API 客户端无缝集成。
- 传输层实现。这是所有 HTTP 请求处理发生的地方。
以下代码片段创建并连接这三个组件:
java
// Create the low-level client
// 创建低级别客户端
RestClient restClient = RestClient.builder(
new HttpHost("localhost", 9200)).build();
// Create the transport with a Jackson mapper
// 使用 Jackson 映射器创建传输
ElasticsearchTransport transport = new RestClientTransport(
restClient, new JacksonJsonpMapper());
// And create the API client
// 创建低级别客户端
ElasticsearchClient esClient = new ElasticsearchClient(transport);
// Use the client...
// 使用客户端....
// Close the client, also closing the underlying transport object and network connections.
// 关闭客户端,同时关闭底层传输对象和网络连接。
esClient.close();
身份验证由 Java 低级别 REST 客户端管理。有关配置身份验证的更多详细信息,请参阅其留档。
创建首个请求
下面的代码片段表示从"product"索引中搜索所有name 字段与 "bicycle" 匹配的值,并将它们作为 Product
类的实例返回。
该示例使用流畅的函数构建器将搜索查询编写为简洁的 DSL-like 代码。API约定中会更加详细地解释这种模式。
java
SearchResponse<Product> search = esClient.search(s -> s
.index("products")
.query(q -> q
.term(t -> t
.field("name")
.value(v -> v.stringValue("bicycle"))
)),
Product.class);
for (Hit<Product> hit: search.hits().hits()) {
processProduct(hit.source());
}
上面示例的源代码可以在 JavaAPI 客户端测试中找到。
从高级 REST 客户端迁移。
ElasticsearchJavaAPI客户端是一个全新的客户端库,与旧的高级休息客户端(HLRC)没有关系。这是一个深思熟虑的选择,旨在提供一个独立于Elasticsearch服务器代码的库,并为所有Elasticsearch功能提供一个非常一致且更易于使用的API。
从 HLRC 迁移需要在您的应用程序中重写一些代码。然而,这种转换可以逐步发生,因为两个客户端库可以在一个应用程序中共存,而没有操作开销。
兼容模式:使用带有 Elasticsearch 8. x 的 7.17 客户端
通过启用 HLRC 的兼容模式(参见下面的代码示例),HLRC 版本 7.17
可以与 Elasticsearch 版本 8. x
一起使用。在这种模式下,HLRC 发送额外的标头来指示 Elasticsearch 8.x
像 7.x
服务器一样运行。
Java API 客户端不需要此设置,因为兼容模式始终处于启用状态。
使用具有 HLRC 和 JavaAPI 客户端的 http 客户端
为了避免在过渡阶段(应用程序同时使用HLRC 和新的JavaAPI客户端 )的任何操作开销,两个客户端可以共享相同的LOW Level Rest 客户端,这是管理所有连接、循环策略、节点嗅探等的网络层。
以下代码展示如何使用同一个 HTTP 客户端初始化两个客户端。
java
// Create the low-level client
// 创建 low-level 客户端
RestClient httpClient = RestClient.builder(
new HttpHost("localhost", 9200)
).build();
// Create the HLRC
// 创建HLRC
RestHighLevelClient hlrc = new RestHighLevelClientBuilder(httpClient)
.setApiCompatibilityMode(true)
.build();
// Create the Java API Client with the same low level client
// 使用相同的low-level客户端创建 Java API 客户端。
ElasticsearchTransport transport = new RestClientTransport(
httpClient,
new JacksonJsonpMapper()
);
ElasticsearchClient esClient = new ElasticsearchClient(transport);
// hlrc and esClient share the same httpClient
// hlrc 和 esClient 共享同一个 httpClient
启用允许 HLRC 7.17 与 Elasticsearch 8. x 一起使用的兼容模式。
转型策略
您可以通过不同的方式开始在应用程序代码中从HLRC转换。
例如:
- 保持现有代码不变,并为应用程序中的新功能使用新的Java API Client,然后再迁移现有代码。
- 重写应用程序中新的JavaAPI客户端比HLRC更容易使用的部分,比如与搜索相关的所有内容,
- 通过利用新的JavaAPI Client与JSON对象映射器的紧密集成,重写需要将应用程序对象映射到JSON 或从JSON映射的部分。
API 约定
Java API Client使用非常一致的代码结构,使用现代代码模式,使复杂的请求更容易编写,复杂的响应更容易处理。下面的部分将详细解释这些。
包结构和命名空间客户端
ElasticSearch API很大,并且被组织成功能组,如ElasticSearch API留档中所示。
JavaAPI 客户端遵循这种结构:功能组称为 "命名空间",每个命名空间都位于co. elastic.client.elasticsearch
的子包中。
每个命名空间客户端都可以从顶级 Elasticsearch 客户端访问。唯一的例外是 "搜索" 和 "文档" API,它们位于核心子包中,可以在主要的 Elasticsearch 客户端对象上访问。
下面的片段展示了如何使用indices命名空间客户端创建索引(lambda语法在构建API对象中解释):
java
// Create the "products" index
// 创建 "products" 索引
ElasticsearchClient client = ...
client.indices().create(c -> c.index("products"));
命名空间客户端是非常轻量级的对象,可以动态创建。
方法命名约定
Java API 客户端中的类包含两种方法和属性。
-
作为 API 一部分的方法和属性,例如
ElasticsearchClient.search()
或SearchResponse. maxScore()
。它们是从Elasticsearch JSON API中使用标准的JavacamelCaseNaming
约定的各自名称派生而来的。 -
构建Java API Client的框架的部分方法和属性,例如
Query._kind()
。这些方法和属性以下划线作为前缀,以避免与 API 名称的任何命名冲突,并作为区分 API 和框架的简单方法。
阻塞式和异步式客户端
API 客户端有两种类型:阻塞式和异步式。异步客户端上的所有方法都返回标准的 CompletableFuture
。
两种风格可以根据你的需求同时使用,共享同一个传输对象。
java
ElasticsearchTransport transport = ...
// Synchronous blocking client
// 同步阻塞客户端
ElasticsearchClient client = new ElasticsearchClient(transport);
if (client.exists(b -> b.index("products").id("foo")).value()) {
logger.info("product exists");
}
// Asynchronous non-blocking client
// 异步非阻塞客户端
ElasticsearchAsyncClient asyncClient =
new ElasticsearchAsyncClient(transport);
asyncClient
.exists(b -> b.index("products").id("foo"))
.whenComplete((response, exception) -> {
if (exception != null) {
logger.error("Failed to index", exception);
} else {
logger.info("Product exists");
}
});
虽然我们不会在 Java 中深入介绍异步编程,但请记住处理异步任务的失败。很容易忽略它们并忽略错误。
上面示例的源代码可以在 JavaAPI 客户端测试 中找到。
构建 API 对象
构建对象
Java API Client中的所有数据类型都是不可变的。对象创建使用2008年在Effective Java中普及的构建器模式。
java
ElasticsearchClient client = ...
CreateIndexResponse createResponse = client.indices().create(
new CreateIndexRequest.Builder()
.index("my-index")
.aliases("foo",
new Alias.Builder().isWriteIndex(true).build()
)
.build()
);
请注意,构建器在调用其 build()
方法后不应被重用。
生成器 lambda 表达式
尽管这代码能正常运行,但必须实例化构建器类并调用build()
方法有点冗长。因此,Java API 客户端中的每个属性设置器也接受一个lambda表达式,该表达式将新创建的构建器作为参数并返回一个已填充的构建器。上面的片段也可以写成:
java
ElasticsearchClient client = ...
CreateIndexResponse createResponse = client.indices()
.create(createIndexBuilder -> createIndexBuilder
.index("my-index")
.aliases("foo", aliasBuilder -> aliasBuilder
.isWriteIndex(true)
)
);
这种方法允许更简洁的代码,并且还避免导入类(甚至记住它们的名称),因为类型是从方法参数签名中推断出来的。
请注意,在上面的示例中,构建器变量仅用于启动属性设置器链。因此,这些变量的名称并不重要,可以缩短以提高易读性:
java
ElasticsearchClient client = ...
CreateIndexResponse createResponse = client.indices()
.create(c -> c
.index("my-index")
.aliases("foo", a -> a
.isWriteIndex(true)
)
);
构建 lambda 表达式对于复杂的嵌套查询特别有用,例如下面的查询,取自 interval 查询 API 文档。
此示例还强调了在深度嵌套结构中构建器参数的有用的命名约定。对于具有单个参数的lambda表达式,Kotlin提供了隐式it
参数,Scala允许使用_。这可以通过使用下划线或单个字母前缀后跟表示深度级别的数字(即_0、_1或b0、b1等)来近似Java。这不仅消除了创建一次性变量名的需要,而且还提高了代码的可读性。正确的缩进还允许查询的结构脱颖而出。
java
ElasticsearchClient client = ...
SearchResponse<SomeApplicationData> results = client
.search(b0 -> b0
.query(b1 -> b1
.intervals(b2 -> b2
.field("my_text")
.allOf(b3 -> b3
.ordered(true)
.intervals(b4 -> b4
.match(b5 -> b5
.query("my favorite food")
.maxGaps(0)
.ordered(true)
)
)
.intervals(b4 -> b4
.anyOf(b5 -> b5
.intervals(b6 -> b6
.match(b7 -> b7
.query("hot water")
)
)
.intervals(b6 -> b6
.match(b7 -> b7
.query("cold porridge")
)
)
)
)
)
)
),
// 搜索结果将映射到某些 Application ationData 实例,以便应用程序随时可用。
SomeApplicationData.class
);
上面示例的源代码可以在 Java API 客户端测试中找到。
Lists and maps
添加构造器设置
对象构建器将List
和Map
类型的属性公开为一组重载的仅可添加的方法,这些方法通过向列表添加新条目和向映射添加新条目(或替换现有条目)来更新属性值。
对象构建器创建不可变对象,这也适用于在对象构建时设置为不可变的列表和映射属性。
java
// Prepare a list of index names
// 准备一份索引名称列表
List<String> names = Arrays.asList("idx-a", "idx-b", "idx-c");
// Prepare cardinality aggregations for fields "foo" and "bar"
// 为字段"foo"和"bar"准备基数聚合。
Map<String, Aggregation> cardinalities = new HashMap<>();
cardinalities.put("foo-count", Aggregation.of(a -> a.cardinality(c -> c.field("foo"))));
cardinalities.put("bar-count", Aggregation.of(a -> a.cardinality(c -> c.field("bar"))));
// Prepare an aggregation that computes the average of the "size" field
// 准备一个计算 "size" 字段平均值的聚合。
final Aggregation avgSize = Aggregation.of(a -> a.avg(v -> v.field("size")));
SearchRequest search = SearchRequest.of(r -> r
// Index list:
// - add all elements of a list
.index(names)
// - add a single element
.index("idx-d")
// - add a vararg list of elements
.index("idx-e", "idx-f", "idx-g")
// Sort order list: add elements defined by builder lambdas
// 排序顺序列表:添加构建器lambdas定义的元素
.sort(s -> s.field(f -> f.field("foo").order(SortOrder.Asc)))
.sort(s -> s.field(f -> f.field("bar").order(SortOrder.Desc)))
// Aggregation map:
// - add all entries of an existing map
.aggregations(cardinalities)
// - add a key/value entry
.aggregations("avg-size", avgSize)
// - add a key/value defined by a builder lambda
.aggregations("price-histogram",
a -> a.histogram(h -> h.field("price")))
);
List
和 map
的值不能为null
Elasticsearch API有很多可选属性。对于单值属性,Java API Client将缺失的可选值表示为null
。因此,应用程序必须在使用可选值之前对其进行空值检查。
然而,对于列表和集合,应用程序通常只关心它们是否为空,甚至只是迭代它们的内容。使用null
很麻烦。为了避免这种情况,Java API Client 集合属性永远不会为null
,并且缺失的可选集合将作为空集合返回。
如果您需要区分缺失(未定义)的可选集合和 Elasticsearch 返回的有效为空的集合,ApiTypeHelper
类提供了一个实用方法来区分它们:
java
NodeStatistics stats = NodeStatistics.of(b -> b
.total(1)
.failed(0)
.successful(1)
);
// The `failures` list was not provided.
// - it's not null
assertNotNull(stats.failures());
// - it's empty
assertEquals(0, stats.failures().size());
// - and if needed we can know it was actually not defined
assertFalse(ApiTypeHelper.isDefined(stats.failures()));
变体类型
Elasticsearch API 有很多变体类型:查询、聚合、字段映射、分析器等。在如此大的集合中找到正确的类名可能具有挑战性。
Java API Client 构建器使简化了这一点:变体类型的构建器 ( 如Query
) 为每个可用的实现都提供了方法。我们已经在上面的interval
(一种查询)和 allOf、match 和 anyOf(各种不同种类的interval
)中看到了这一点。
这是因为 Java API Client 中的变体对象是"标记联合"的实现:它们包含它们所持有的变体的标识符(或标签)以及该变体的值。例如,Query
对象可以包含一个带有 intervals
标签的 IntervalsQuery
、带有 term
标签的TermQuery
等。这种方法允许编写流畅的代码,您可以让IDE完成功能指导您构建和导航复杂的嵌套结构:
变体构建器对每个可用的实现都有 setter
方法。它们使用与常规属性相同的约定,并接受构建器 lambda 表达式和实际变体类型的现成对象。以下是构建 term 查询的示例:
java
Query query = new Query.Builder()
.term(t -> t // 1
.field("name") // 2
.value(v -> v.stringValue("foo"))
)
.build(); // 3
- 选择
term
变体以构建 term 查询。 - 使用构建器 lambda 表达式构建 term 查询。
- 构建现在包含
term
类型的 TermQuery 对象的 Query。
变体对象对每个可用的实现都有getter
方法。这些方法检查对象是否实际持有该类型的变体,并返回向下转换为正确类型的值。否则它们会抛出 IllegalStateException
。这种方法允许编写流畅的代码来遍历变体。
java
assertEquals("foo", query.term().value().stringValue());
变体对象还提供了它们当前持有的变体类型的信息:
- 每个变体类型的方法:
isTerm()
、isIntervers()
、isFuzzy()
等。 - 使用定义所有变体类型的嵌套 Kind 枚举。
在检查特定变体的实际类型后,可以使用此信息导航到特定变体:
java
if (query.isTerm()) { // 1
doSomething(query.term());
}
switch(query._kind()) { // 2
case Term:
doSomething(query.term());
break;
case Intervals:
doSomething(query.intervals());
break;
default:
doSomething(query._kind(), query._get()); // 3
}
- 测试变体是否属于特定类型。
- 测试一组更大的变体类型。
- 获取变体对象持有的类型和值。
对象的生命周期和线程安全
Java API Client 中有五种不同生命周期的对象:
Object mapper(对象映射器)
无状态且线程安全,但创建成本很高。它通常是一个单例,在应用程序启动时创建,用于创建传输。
Transport(传输)
线程安全,通过底层 HTTP 客户端保存网络资源。传输对象与Elasticsearch集群相关联,必须显式关闭以释放底层资源(如网络连接)。
Clients(客户端)
不可变、无状态和线程安全。这些都是非常轻量级的对象,它们只是封装传输并提供API端点作为方法。关闭客户端将关闭底层传输。
Builders(构建器)
可变的,非线程安全的。构造器是临时对象,在调用build()
之后不应该被重用。
Requests & other API objects( Requests 对象和其他API对象)
不可变的,线程安全的。如果您的应用程序反复使用相同的请求或请求的相同部分,则可以提前准备好这些对象,并在具有不同传输的多个客户端的多个调用中重用它们。
从 JSON 数据创建 API 对象
此功能是在版本 7.17.2 中添加的。
在使用 Elasticsearch 开发应用程序的过程中,经常使用 Kibana 开发者平台 以交互式地准备和测试查询、聚合、索引映射以及其他复杂的 API 调用。这将产生您可能希望在应用程序中使用的有效的 JSON 片段。
由于将这些JSON片段转换为Java代码非常耗时且容易出错,因此Java API Client中的大多数数据类都可以从JSON文本加载:对象构建器具有 withJson()
方法,该方法从原始 JSON 填充构建器。这也允许你将动态加载的JSON与编程构造的对象结合起来。
在底层, withJson()
方法调用对象的反序列化器。因此,JSON 文本的结构和值类型必须与目标数据结构一致。使用 withJson()
维持了 Java API Client 的强类型保证。
示例
从资源文件中加载索引定义
准备一个包含索引定义的资源文件 sam-index. json:
json
{
"mappings": {
"properties": {
"field1": { "type": "text" }
}
}
}
您可以根据该定义创建索引,如下所示:
java
InputStream input = this.getClass()
.getResourceAsStream("some-index.json"); // 1
CreateIndexRequest req = CreateIndexRequest.of(b -> b
.index("some-index")
.withJson(input) // 2
);
boolean created = client.indices().create(req).acknowledged();
- 打开 JSON 资源文件的输入流。
- 用资源文件内容填充索引创建请求。
从JSON文件中摄取文档
同样,你可以从数据文件中读取要存储在 Elasticsearch 中的文档。
json
FileReader file = new FileReader(new File(dataDir, "document-1.json"));
IndexRequest<JsonData> req; // 1
req = IndexRequest.of(b -> b
.index("some-index")
.withJson(file)
);
client.index(req);
- 当对具有泛型类型参数的数据结构调用
withJson()
时,这些泛型类型将被认为是JsonData
。
创建一个结合JSON和编程构造的搜索请求
您可以将 withJson()
与对 setter
方法的常规调用结合起来。下面的示例从 String
加载搜索请求的查询部分,并以编程方式添加聚合。
java
Reader queryJson = new StringReader(
"{" +
" \"query\": {" +
" \"range\": {" +
" \"@timestamp\": {" +
" \"gt\": \"now-1w\"" +
" }" +
" }" +
" }" +
"}");
SearchRequest aggRequest = SearchRequest.of(b -> b
.withJson(queryJson) // 1
.aggregations("max-cpu", a1 -> a1 // 2
.dateHistogram(h -> h
.field("@timestamp")
.calendarInterval(CalendarInterval.Hour)
)
.aggregations("max", a2 -> a2
.max(m -> m.field("host.cpu.usage"))
)
)
.size(0)
);
Map<String, Aggregate> aggs = client
.search(aggRequest, Void.class) // 3
.aggregations();
- 从 JSON 字符串加载查询。
- 添加聚合。
- 由于这是一个聚合,我们不关心结果文档并将其目标类设置为
Void
,这意味着它们将被忽略。注意,将size
设置为 0 实际上会阻止返回任何文档。
从多个 JSON 片段创建搜索请求
withJson()
方法是部分反序列化器:从 JSON 加载的属性将设置属性值或替换以前的属性,但不会重置 JSON 输入中未找到的其他属性。您可以使用它来组合多个 JSON 片段来构建复杂的搜索请求。在下面的示例中,我们将选择一些文档的查询的单独定义和对该查询的结果运行的聚合结合起来。
java
Reader queryJson = new StringReader(
"{" +
" \"query\": {" +
" \"range\": {" +
" \"@timestamp\": {" +
" \"gt\": \"now-1w\"" +
" }" +
" }" +
" }," +
" \"size\": 100" + // 1
"}");
Reader aggregationJson = new StringReader(
"{" +
" \"size\": 0, " + // 2
" \"aggregations\": {" +
" \"hours\": {" +
" \"date_histogram\": {" +
" \"field\": \"@timestamp\"," +
" \"interval\": \"hour\"" +
" }," +
" \"aggregations\": {" +
" \"max-cpu\": {" +
" \"max\": {" +
" \"field\": \"host.cpu.usage\"" +
" }" +
" }" +
" }" +
" }" +
" }" +
"}");
SearchRequest aggRequest = SearchRequest.of(b -> b
.withJson(queryJson) // 3
.withJson(aggregationJson) // 4
.ignoreUnavailable(true) // 5
);
Map<String, Aggregate> aggs = client
.search(aggRequest, Void.class)
.aggregations();
- 将查询返回的最大文档数设置为100。
- 我们不希望聚合中有任何匹配的文档。
- 加载请求的查询部分。
- 加载请求的聚合部分(覆盖查询中的
size
)。 - 以编程方式设置的附加请求属性。
需要注意,当 JSON 片段具有一些共同属性时,顺序会变得尤为重要:就像以编程方式设置属性值时一样,为属性设置的最后一个值会覆盖前一个值。
异常
客户端方法可以抛出两种异常:
- Elasticsearch服务器收到请求但被拒绝(验证错误、服务器内部超时,等等),将产生
ElasticsearchException
异常。此异常包含有关错误的详细信息,由 Elasticsearch 提供。 - 未能到达服务器的请求(网络错误、服务器不可用等)将产生
TransportException
。这个异常的原因是底层实现抛出的异常。对于RestClientTransfer
,它将是包含低级HTTP响应的ResponseException
。