当前位置: 首页 > 知识库问答 >
问题:

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

游鸣
2023-03-14

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

如果json字符串包含聚合,XContentParser会抛出一个ParsingException[无法解析键控为[target\u字段]异常的聚合。

我用于将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);

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

共有3个答案

艾俊晖
2023-03-14

您需要添加?键入请求URL的\u键,例如启动搜索?键入\u键,请查看此参考。

并且您最好像框架源代码一样在NamedXContent注册表中添加更多parse注册表。以下是所有注册表条目:

private List<NamedXContentRegistry.Entry> getProvidedNamedXContents() {
    List<NamedXContentRegistry.Entry> entries = new ArrayList<>();

    for (NamedXContentProvider service : ServiceLoader.load(NamedXContentProvider.class)) {
        entries.addAll(service.getNamedXContentParsers());
    }

    return entries;
}

private NamedXContentRegistry getDefaultNamedXContentRegistry() {
    List<NamedXContentRegistry.Entry> entries = new ArrayList<>();
    entries.addAll(getDefaultNamedXContents());
    entries.addAll(getProvidedNamedXContents());
    return new NamedXContentRegistry(entries);
}


private List<NamedXContentRegistry.Entry> getDefaultNamedXContents() {
    Map<String, ContextParser<Object, ? extends Aggregation>> map = new HashMap<>();
    map.put("cardinality", (p, c) -> ParsedCardinality.fromXContent(p, (String) c));
    map.put("hdr_percentiles", (p, c) -> ParsedHDRPercentiles.fromXContent(p, (String) c));
    map.put("hdr_percentile_ranks", (p, c) -> ParsedHDRPercentileRanks.fromXContent(p, (String) c));
    map.put("tdigest_percentiles", (p, c) -> ParsedTDigestPercentiles.fromXContent(p, (String) c));
    map.put("tdigest_percentile_ranks", (p, c) -> ParsedTDigestPercentileRanks.fromXContent(p, (String) c));
    map.put("percentiles_bucket", (p, c) -> ParsedPercentilesBucket.fromXContent(p, (String) c));
    map.put("min", (p, c) -> ParsedMin.fromXContent(p, (String) c));
    map.put("max", (p, c) -> ParsedMax.fromXContent(p, (String) c));
    map.put("sum", (p, c) -> ParsedSum.fromXContent(p, (String) c));
    map.put("avg", (p, c) -> ParsedAvg.fromXContent(p, (String) c));
    map.put("value_count", (p, c) -> ParsedValueCount.fromXContent(p, (String) c));
    map.put("simple_value", (p, c) -> ParsedSimpleValue.fromXContent(p, (String) c));
    map.put("derivative", (p, c) -> ParsedDerivative.fromXContent(p, (String) c));
    map.put("bucket_metric_value", (p, c) -> ParsedBucketMetricValue.fromXContent(p, (String) c));
    map.put("stats", (p, c) -> ParsedStats.fromXContent(p, (String) c));
    map.put("stats_bucket", (p, c) -> ParsedStatsBucket.fromXContent(p, (String) c));
    map.put("extended_stats", (p, c) -> ParsedExtendedStats.fromXContent(p, (String) c));
    map.put("extended_stats_bucket", (p, c) -> ParsedExtendedStatsBucket.fromXContent(p, (String) c));
    map.put("geo_bounds", (p, c) -> ParsedGeoBounds.fromXContent(p, (String) c));
    map.put("geo_centroid", (p, c) -> ParsedGeoCentroid.fromXContent(p, (String) c));
    map.put("histogram", (p, c) -> ParsedHistogram.fromXContent(p, (String) c));
    map.put("date_histogram", (p, c) -> ParsedDateHistogram.fromXContent(p, (String) c));
    map.put("sterms", (p, c) -> ParsedStringTerms.fromXContent(p, (String) c));
    map.put("lterms", (p, c) -> ParsedLongTerms.fromXContent(p, (String) c));
    map.put("dterms", (p, c) -> ParsedDoubleTerms.fromXContent(p, (String) c));
    map.put("missing", (p, c) -> ParsedMissing.fromXContent(p, (String) c));
    map.put("nested", (p, c) -> ParsedNested.fromXContent(p, (String) c));
    map.put("reverse_nested", (p, c) -> ParsedReverseNested.fromXContent(p, (String) c));
    map.put("global", (p, c) -> ParsedGlobal.fromXContent(p, (String) c));
    map.put("filter", (p, c) -> ParsedFilter.fromXContent(p, (String) c));
    map.put("sampler", (p, c) -> ParsedSampler.fromXContent(p, (String) c));
    map.put("geohash_grid", (p, c) -> ParsedGeoHashGrid.fromXContent(p, (String) c));
    map.put("range", (p, c) -> ParsedRange.fromXContent(p, (String) c));
    map.put("date_range", (p, c) -> ParsedDateRange.fromXContent(p, (String) c));
    map.put("geo_distance", (p, c) -> ParsedGeoDistance.fromXContent(p, (String) c));
    map.put("filters", (p, c) -> ParsedFilters.fromXContent(p, (String) c));
    map.put("adjacency_matrix", (p, c) -> ParsedAdjacencyMatrix.fromXContent(p, (String) c));
    map.put("siglterms", (p, c) -> ParsedSignificantLongTerms.fromXContent(p, (String) c));
    map.put("sigsterms", (p, c) -> ParsedSignificantStringTerms.fromXContent(p, (String) c));
    map.put("scripted_metric", (p, c) -> ParsedScriptedMetric.fromXContent(p, (String) c));
    map.put("ip_range", (p, c) -> ParsedBinaryRange.fromXContent(p, (String) c));
    map.put("top_hits", (p, c) -> ParsedTopHits.fromXContent(p, (String) c));
    map.put("composite", (p, c) -> ParsedComposite.fromXContent(p, (String) c));
    List<NamedXContentRegistry.Entry> entries = map.entrySet().stream()
            .map((entry) -> new NamedXContentRegistry.Entry(Aggregation.class, new ParseField((String) entry.getKey()), entry.getValue()))
            .collect(Collectors.toList());
    entries.add(new NamedXContentRegistry.Entry(Suggest.Suggestion.class, new ParseField("term"), (parser, context) -> TermSuggestion.fromXContent(parser, (String) context)));
    entries.add(new NamedXContentRegistry.Entry(Suggest.Suggestion.class, new ParseField("phrase"), (parser, context) -> PhraseSuggestion.fromXContent(parser, (String) context)));
    entries.add(new NamedXContentRegistry.Entry(Suggest.Suggestion.class, new ParseField("completion"), (parser, context) -> CompletionSuggestion.fromXContent(parser, (String) context)));
    return entries;
}

利永年
2023-03-14

根据上面的答案,我设法这样做了:

我写了一个这样的JSON:

XContentBuilder builder = XContentFactory.jsonBuilder();
response.toXContent(builder, ToXContent.EMPTY_PARAMS);
String result = Strings.toString(builder);

然后我设法这样读:

 try {
     NamedXContentRegistry registry = new NamedXContentRegistry(getDefaultNamedXContents());
     XContentParser parser = JsonXContent.jsonXContent.createParser(registry, DeprecationHandler.THROW_UNSUPPORTED_OPERATION, result);
     SearchResponse searchResponse = SearchResponse.fromXContent(parser);
 } catch (IOException e) {
     System.out.println("exception " + e);
 } catch (Exception e) {
     System.out.println("exception " + e);
 }

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;
}

希望它能起作用:)

钱建本
2023-03-14

背景:
我写这个答案是因为我有创建SearchSecurity对象的经验,目的是编写一个Java单元测试。目标是从Elasticsearch查询中获取任何JSON响应对象,将其编入SearchSecurity对象,并单元测试创建可消耗输出的业务逻辑。

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

解决方案说明:
Elasticsearch的高级rest客户端一般解析来自低级rest客户端的结果。SearchRequest的响应JSON在search方法第129行的RestHighLevelClient中转换为SearchACK Object。此方法在第1401行调用performRequest estAndParseEntity,它接受entityParser作为CheckedFunction

关于解决方案:
在阅读了Elasticsearch对此的讨论之后,如果您想将Elastic的JSON响应注入SearchResponse对象,那么有必要创建NamedXContentRegistry和Xcontent测试列表,您必须重新创建解析。这是一种辅助方法,来源于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;
}

上面代码中的map需要包含测试所需的所有聚合。有两个以上,为简洁起见,这里有两个。

使用这个助手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();
}

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

要将JSON字符串注入SearchResponse对象,必须(1)使用上面的方法,(2)注入一个包含类型提示的字符串。

主要来源:

  1. https://discuss.elastic.co/t/elasticsearch-json-response-to-searchresponse-object/124394/6

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

 类似资料:
  • 我正在构建一个计算器,为了让计算器工作,我需要将字符串中的所有整数转换为双数。例如,如果我有一个字符串:3*8 5/2-4,我想将其转换为:3.0*8.0 5.0/2.0-4.0。我该怎么做? 编辑:如果我有这个字符串:3.0*8.0 5.0/2.0-4,我想将他转换为:3.0*8.0 5.0/2.0-4.0

  • 问题内容: 我想获取一串字符串并将其转换为单词对流。例如: 我有: 我想要:。 这几乎与Zipping相同,如使用带有Lambda的JDK8的Zipping流中概述的那样(java.util.stream.Streams.zip) 但是,这会产生: 以下代码有效,但显然是错误的方法(不是线程安全的等): 问题答案: 如果你: 不喜欢使用流中所有字符串创建列表的想法 不想使用外部库 喜欢弄脏你的手

  • 我有一个带有转义Unicode字符的字符串,我想将它转换为常规Unicode字母。例如: 应成为 我知道,当我打印第一个字符串时,它已经显示。我的问题是我从一个文件中读取文件名,然后我搜索它们。文件中的文件名是用Unicode编码转义的,当我搜索这些文件时,我找不到它们,因为它搜索的文件名中有。

  • 问题内容: 指定直方图聚合时,是否可以将字符串转换为浮点数?因为我的文档中的字段是浮点型的,但没有通过Elasticsearch解析,因此当我尝试使用字符串字段求和时,它将引发下一个错误。 我知道我可以更改映射,但是对于我有的用例,如果在编写字段的聚合时可以指定类似“ script:_value.tofloat()”的内容,则将更加方便。 这是我的代码: } 问题答案: 你需要这个 对于称为的字段

  • 问题内容: 我正在阅读有关python的新f字符串的 博客 ,它们看起来很整洁。但是,我希望能够从字符串或文件中加载f字符串。 我似乎找不到任何执行此操作的字符串方法或其他函数。 从上面我的链接中的示例: 但是,如果我有琴弦怎么办?我希望能够像这样: 事实证明,我已经可以执行类似的操作并获得性能提高。即: 问题答案: f字符串是代码。不仅以安全的方式(当然,字符串文字就是代码),而且以危险的任意代

  • 问题内容: 我有一串 现在我必须获取Date对象。我的DateObject应该与startDate的值相同。 我就是这样 但是输出是格式 问题答案: 基本上,你可以有效地将字符串格式的日期转换为日期对象。如果此时将其打印出来,则将获得标准日期格式输出。为了在此之后对其进行格式化,然后需要将其转换回具有指定格式(先前已经指定)的日期对象。