如何将带有聚合的 Elasticsearch JSON 字符串响应转换为 Elasticsearch SearchResponse 对象

Jul*_*an8 6 java json elasticsearch

我想将 json 字符串序列化为 Elasticsearch SearchResponse 对象。如果 json 字符串不包含聚合,它可以正常工作。

如果 json 字符串包含聚合,则 XContentParser 会抛出 ParsingException[无法解析键控为 [target_field] 的聚合 异常。

我用来将 json 字符串序列化为 Elasticsearch SearchResponse 对象的代码:

    Settings settings = Settings.builder().build();
    SearchModule searchModule = new SearchModule(settings, false, new ArrayList<>());

    NamedXContentRegistry xContentRegistry = new NamedXContentRegistry(searchModule.getNamedXContents());

    JsonXContentParser xContentParser = new JsonXContentParser(xContentRegistry,
            new JsonFactory().createParser(json));
    SearchResponse response = SearchResponse.fromXContent(xContentParser);
Run Code Online (Sandbox Code Playgroud)

似乎我必须向 NamedXContentRegistry 注册聚合,但我不知道如何注册。

tec*_*rat 14

背景:
我是根据我为编写Java 单元测试而创建SearchResponse对象的经验编写此答案的。目标是从 Elasticsearch 查询中获取任何 JSON 响应对象,将其编组到SearchResponse 中对象中,并对创建可消费输出的业务逻辑进行单元测试。

我们正在使用 Elasticsearch 6.7,即高级别的客户端,并使用 Elastic 的 POJO解析SearchResponse(而不是仅执行 .toString() 并使用 GSON 或 Jackson 对其进行操作)。

解决方案说明:
Elasticsearch的高层rest客户端一般会解析低层rest客户端的结果。在搜索方法的第 129 行,将SearchRequest的响应 JSON 转换为RestHighLevelClient 中SearchResponse对象。此方法在第 1401 行调用performRequestAndParseEntity,它接受 an作为。最后,我们可以看到,在调用1401 行时,它调用了1714 行上的方法,该方法确定将XContentType传递给解析器。此注册表包含响应字段可能是的所有可能的 XContent 值。这entityParserCheckedFunction<XContentParser, Resp, IOException>entityParserparseEntity实体并最终执行解析。值得注意的是,当解析器在第 1726 行创建时registry第 288 行构造RestHighLevelClient时创建注册表。第1748 行列出了包括聚合类型在内的完整类型列表。

解决方案:
阅读 Elasticsearch 对此的讨论后,如果您想将来自 Elastic 的 JSON 响应注入SearchResponse对象,则需要创建NamedXContentRegistry和 XContents 测试列表,您必须重新创建解析。一个帮助方法来做到这一点,来自 Elastic 的讨论

public static List<NamedXContentRegistry.Entry> getDefaultNamedXContents() {
    Map<String, ContextParser<Object, ? extends Aggregation>> map = new HashMap<>();
    map.put(TopHitsAggregationBuilder.NAME, (p, c) -> ParsedTopHits.fromXContent(p, (String) c));
    map.put(StringTerms.NAME, (p, c) -> ParsedStringTerms.fromXContent(p, (String) c));
    List<NamedXContentRegistry.Entry> entries = map.entrySet().stream()
            .map(entry -> new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(entry.getKey()), entry.getValue()))
            .collect(Collectors.toList());
  return entries;
}
Run Code Online (Sandbox Code Playgroud)

上面代码中的地图需要有ALL测试所需的聚合。有两个以上,为了简洁起见,这里有两个。

使用此助手 getNamedXContents() 方法,您现在可以使用以下方法获取 JSON 字符串并将其注入 SearchResponse。 也来自 Elastic 的讨论

public static SearchResponse getSearchResponseFromJson(String jsonResponse){
    try {
        NamedXContentRegistry registry = new NamedXContentRegistry(getDefaultNamedXContents());
        XContentParser parser = JsonXContent.jsonXContent.createParser(registry, jsonResponse);
        return SearchResponse.fromXContent(parser);
    } catch (IOException e) {
        System.out.println("exception " + e);
    }catch (Exception e){
        System.out.println("exception " + e);
    }
    return new SearchResponse();
}
Run Code Online (Sandbox Code Playgroud)

将解决方案与聚合结果一起应用:
Elasticsearch 需要一个提示,以了解要将其解析为哪种类型的聚合。将?typed_keys添加到查询时,该提示由 elastic 提供。有关聚合类型提示的 Elasticsearch 文档中显示了一个示例

要将 JSON 字符串注入SearchResponse对象,必须 (1) 使用上述方法和 (2) 注入带有类型提示的字符串。

主要资源:

  1. https://discuss.elastic.co/t/elasticsearch-json-response-to-searchresponse-object/124394/6
  2. https://github.com/elastic/elasticsearch/blob/master/client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java
  3. https://github.com/elastic/elasticsearch/blob/master/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java
  4. https://www.elastic.co/guide/en/elasticsearch/reference/current/returning-aggregation-type.html

注意:大约 2015 年有很多文章说这是不可能的。这显然是不正确的。