You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 05:37:28