从 JSON 数据创建 API 对象
在使用 Elasticsearch 进行应用程序开发时,一个常见的工作流程是使用 Kibana 开发人员控制台(Developer Console)交互式地准备和测试查询、聚合、索引映射以及其他复杂的 API 调用。这会生成可在应用程序中使用的可用 JSON 片段。
由于将这些 JSON 片段转换为 Java 代码可能既耗时又容易出错,因此 Java API 客户端中的大多数数据类都可以从 JSON 文本加载:对象 builders 具有 withJson() 方法,可以通过原始 JSON 填充 builder。这也允许你将动态加载的 JSON 与对象的程序化构建结合使用。
在底层,withJson() 方法会调用对象的反序列化器。因此,JSON 文本的结构和值类型必须与目标数据结构相匹配。使用 withJson() 可以保持 Java API 客户端的强类型保证。
考虑包含索引定义的资源文件 some-index.json
{
"mappings": {
"properties": {
"field1": { "type": "text" }
}
}
}
你可以根据该定义按如下方式创建索引
InputStream input = this.getClass()
.getResourceAsStream("some-index.json");
CreateIndexRequest req = CreateIndexRequest.of(b -> b
.index("some-index")
.withJson(input)
);
boolean created = esClient.indices().create(req).acknowledged();
- 为 JSON 资源文件打开输入流。
- 使用资源文件内容填充索引创建请求。
类似地,你可以从数据文件中读取要存储在 Elasticsearch 中的文档
FileReader file = new FileReader(new File(dataDir, "document-1.json"));
IndexRequest<JsonData> req;
req = IndexRequest.of(b -> b
.index("some-index")
.withJson(file)
);
esClient.index(req);
- 当对具有泛型类型参数的数据结构调用
withJson()时,这些泛型类型将被视为JsonData。
你可以将 withJson() 与对 setter 方法的常规调用结合使用。以下示例从 String 加载搜索请求的查询部分,并通过程序化方式添加聚合。
Reader queryJson = new StringReader(
"{" +
" \"query\": {" +
" \"range\": {" +
" \"@timestamp\": {" +
" \"gt\": \"now-1w\"" +
" }" +
" }" +
" }" +
"}");
SearchRequest aggRequest = SearchRequest.of(b -> b
.withJson(queryJson)
.aggregations("max-cpu", a1 -> a1
.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 = esClient
.search(aggRequest, Void.class)
.aggregations();
- 从 JSON 字符串加载查询。
- 添加聚合。
- 由于这是一个聚合,我们不关心结果文档,因此将其目标类设置为
Void,这意味着它们将被忽略。请注意,将size设置为零实际上会阻止返回任何文档。
withJson() 方法是部分反序列化器:从 JSON 加载的属性将设置属性值或替换先前的属性值,但不会重置 JSON 输入中未找到的其他属性。你可以利用这一点组合多个 JSON 片段来构建复杂的搜索请求。在下面的示例中,我们将选择某些文档的查询的单独定义与在该查询结果上运行的聚合结合起来。
Reader queryJson = new StringReader(
"{" +
" \"query\": {" +
" \"range\": {" +
" \"@timestamp\": {" +
" \"gt\": \"now-1w\"" +
" }" +
" }" +
" }," +
" \"size\": 100" +
"}");
Reader aggregationJson = new StringReader(
"{" +
" \"size\": 0, " +
" \"aggregations\": {" +
" \"hours\": {" +
" \"date_histogram\": {" +
" \"field\": \"@timestamp\"," +
" \"interval\": \"hour\"" +
" }," +
" \"aggregations\": {" +
" \"max-cpu\": {" +
" \"max\": {" +
" \"field\": \"host.cpu.usage\"" +
" }" +
" }" +
" }" +
" }" +
" }" +
"}");
SearchRequest aggRequest = SearchRequest.of(b -> b
.withJson(queryJson)
.withJson(aggregationJson)
.ignoreUnavailable(true)
);
Map<String, Aggregate> aggs = esClient
.search(aggRequest, Void.class)
.aggregations();
- 将查询返回的最大文档数设置为 100。
- 我们不希望在聚合中返回任何匹配的文档。
- 加载请求的查询部分。
- 加载请求的聚合部分(会覆盖查询中的
size)。 - 以程序化方式设置的其他请求属性。
请注意,当 JSON 片段具有某些公共属性时,顺序非常重要:就像以程序化方式设置属性值一样,为属性设置的最后一个值会覆盖前一个值。
上述示例的源代码可以在 Java API Client 测试中找到。