Spring Data Elasticsearch中SearchHitsSupport返回命中而非聚合数据的问题
问题描述
我正在使用Spring Data Elasticsearch执行聚合操作,查询构建代码如下:
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder() .withQuery(query) .withPageable(pageRequest) .withAggregations(aggregation);
最初尝试通过以下方式获取结果:
SearchPage<MyData> searchPage = SearchHitSupport.searchPageFor(elasticsearchTemplate.search(query, MyData.class, IndexCoordinates.of(myDataIndexName)), query.getPageable());
但得到的是聚合操作前的所有命中结果。目前只能通过遍历所有桶手动提取文档来获取正确结果,代码如下:
SearchHits<MyData> search = elasticsearchTemplate.search(query, MyData.class, IndexCoordinates.of(myData)); ElasticsearchAggregations aggregations = (ElasticsearchAggregations) search.getAggregations(); Iterator<Aggregation> iterator = aggregations.aggregations().iterator(); List<org.elasticsearch.search.SearchHit> results = new ArrayList<>(); while (iterator.hasNext()) { ParsedTerms aggregation = (ParsedTerms) iterator.next(); List<org.elasticsearch.search.SearchHit> collect = aggregation.getBuckets() .stream() .map(b -> b.getAggregations().asList()) .flatMap(Collection::stream) .map(c -> Arrays.stream(((ParsedTopHits) c).getHits().getHits()).collect(Collectors.toList())) .flatMap(Collection::stream) .collect(Collectors.toList()); results.addAll(collect); } List<MyData> myDataList = getMyData(results);
将SearchHit转换为自定义POJO的方法:
private List<MyData> getMyData(List<org.elasticsearch.search.SearchHit> results) { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); objectMapper.registerModule(new JavaTimeModule()); List<MyData> collect = results.stream().map(e -> { String source = e.getSourceAsString(); try { return objectMapper.readValue(source,MyData.class); } catch (JsonProcessingException ex) { log.error(ex.getMessage(), ex); return null; } }).collect(Collectors.toList()); return collect; }
请问有没有更简便的方式获取聚合后的结果?
简化方案
1. 类型安全的聚合结果提取
Spring Data Elasticsearch支持直接按聚合名称和类型获取结果,无需手动强转迭代,代码更简洁:
SearchHits<MyData> searchHits = elasticsearchTemplate.search(query, MyData.class, IndexCoordinates.of(myDataIndexName)); // 直接获取指定名称的Terms聚合 Terms termsAgg = searchHits.getAggregations().get("你的Terms聚合名称", Terms.class); // 链式提取TopHits并转换为实体 List<MyData> myDataList = termsAgg.getBuckets().stream() // 获取每个桶内的TopHits聚合 .map(bucket -> bucket.getAggregations().get("你的TopHits聚合名称", TopHits.class)) // 复用Elasticsearch自带的实体映射器转换,无需手动ObjectMapper .flatMap(topHits -> Arrays.stream(topHits.getHits().getHits()) .map(hit -> elasticsearchTemplate.getEntityMapper().mapEntity(hit.getSourceAsString(), MyData.class))) .collect(Collectors.toList());
2. 复用内置EntityMapper
ElasticsearchTemplate自带的EntityMapper已经默认配置了Java时间模块、忽略未知属性等规则,完全可以替代你手动创建的ObjectMapper,避免重复配置。如果需要自定义序列化规则,只需在配置类中定义全局EntityMapper Bean即可。
3. 优化查询性能
如果只需要聚合后的TopHits结果,不需要原始查询的命中数据,可以在构建查询时禁用默认命中追踪,提升查询效率:
NativeSearchQuery query = new NativeSearchQueryBuilder() .withQuery(query) .withTrackTotalHits(false) // 不需要统计总命中数,减少资源消耗 .withAggregations(aggregation) .build();
4. 结构化聚合构建
用Spring Data提供的聚合构建器创建嵌套聚合,代码可读性更强:
// 构建TopHits聚合,指定每个桶返回的文档数 TopHitsAggregationBuilder topHits = AggregationBuilders.topHits("top_hits") .size(10) .from(0); // 构建Terms聚合并嵌套TopHits TermsAggregationBuilder termsAgg = AggregationBuilders.terms("terms_agg") .field("聚合字段名") .subAggregation(topHits); // 组装查询 NativeSearchQuery query = new NativeSearchQueryBuilder() .withQuery(QueryBuilders.matchAllQuery()) .withAggregations(termsAgg) .build();
内容的提问来源于stack exchange,提问作者SereneAtk
相关产品推荐
相关产品推荐

