从 JSON 数据创建 API 对象

编辑

在使用 Elasticsearch 进行应用程序开发时,一个常见的工作流程是使用 Kibana 开发控制台交互式地准备和测试查询、聚合、索引映射和其他复杂的 API 调用。这会生成您可能希望在应用程序中使用的 JSON 代码片段。

由于将这些 JSON 代码片段转换为 Java 代码可能既耗时又容易出错,因此 Java API 客户端中的大多数数据类都可以从 JSON 文本加载:对象构建器具有 withJson() 方法,这些方法可以使用原始 JSON 填充构建器。这还允许您将动态加载的 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 = client.indices().create(req).acknowledged();

为 JSON 资源文件打开一个输入流。

使用资源文件内容填充索引创建请求。

从 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)
);

client.index(req);

在对具有泛型类型参数的数据结构调用 withJson() 时,这些泛型类型将被视为 JsonData

创建结合 JSON 和编程构造的搜索请求
编辑

您可以将 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 = client
    .search(aggRequest, Void.class) 
    .aggregations();

从 JSON 字符串加载查询。

添加聚合。

由于这是一个聚合,我们不关心结果文档,并将它们的目标类设置为 Void,这意味着它们将被忽略。请注意,将 size 设置为零实际上会阻止返回任何文档。

从多个 JSON 代码片段创建搜索请求
编辑

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 = client
    .search(aggRequest, Void.class)
    .aggregations();

为查询设置返回文档的最大数量为 100。

我们不希望在聚合中有任何匹配的文档。

加载请求的查询部分。

加载请求的聚合部分(覆盖查询中的 size)。

以编程方式设置其他请求属性。

请注意,当 JSON 代码片段具有一些公共属性时,顺序很重要:就像以编程方式设置属性值一样,为属性设置的最后一个值将覆盖前一个值。

上面示例的源代码可以在 Java API 客户端测试中找到。