我想将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);
似乎我必须向NamedXContentRegistry注册聚合,但我不知道如何。
答案 0 :(得分:0)
背景:
我正在根据创建 SearchResponse 对象以编写 Java单元测试的经验来编写此答案。目标是从Elasticsearch查询中获取任何JSON响应对象,将其编组为 SearchResponse 对象,并对创建消耗性输出的业务逻辑进行单元测试。
我们使用的是高级REST客户端Elasticsearch 6.7,并使用Elastic的POJO(仅执行.toString()并使用GSON或Jackson对其进行解析)来解析 SearchResponse 。 / p>
解决方案的说明:
Elasticsearch的高级REST客户端通常解析低级REST客户端的结果。 SearchRequest 的响应JSON在 search 方法的第129行的RestHighLevelClient中转换为 SearchResponse 对象。此方法在行1401上调用performRequestAndParseEntity,它接受entityParser
作为CheckedFunction<XContentParser, Resp, IOException>
。最后,我们可以看到,在第1401行调用entityParser
时,它在第1714行调用了parseEntity
方法,该方法为实体确定 XContentType 并最终执行了解析。值得注意的是,当在第1726行创建解析器时,会将registry
传递到解析器中。该 registry 包含响应字段可能是的所有可能的XContent值。 注册表是在line 288上构建 RestHighLevelClient 时创建的。类型的完整列表(包括聚合类型)列在line 1748上。
解决方案:
阅读关于Elasticsearch的讨论之后,如果您想将来自Elastic的JSON响应注入到 SearchResponse 对象中,则有必要创建一个 NamedXContentRegistry 并列出XContents测试中,您必须重新创建解析。一种帮助方法,sourced from Elastic's discussion:
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;
}
上面代码中的地图需要具有测试所需的汇总的 ALL 。有两个以上,为简洁起见,这里有两个。
使用此助手getNamedXContents()方法,您现在可以使用以下方法获取JSON字符串并将其注入SearchResponse中。 Also sourced from Elastic's Discussion:
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需要一个提示来知道将其解析为哪种聚合类型。在向查询中添加?typed_keys 时,elastic会提供提示。在Aggregation Type Hints上的Elasticsearch文档中显示了一个示例。
要将JSON字符串注入到 SearchResponse 对象中,必须(1)使用上述方法,以及(2)注入其中带有类型提示的字符串。
主要来源:
注意:2015年左右有很多文章说这是不可能的。这显然是不正确的。
答案 1 :(得分:0)
基于上面的答案,我设法做到了:
我写了这样的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;
}
希望它能起作用:)
答案 2 :(得分:0)
您需要在您的请求 URL 末尾添加 ?typed_keys
,例如 /cranking/_search?typed_keys
,看看这个 reference。
并且您最好在 parse
中添加更多 NamedXContentRegistry
注册表,就像框架源代码一样。以下是所有注册表项:
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;
}