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

Spring Boot中Elasticsearch用聚合按数值范围统计数据

一次Elasticsearch查询实现score范围分组统计及效率对比

一次查询的实现方案

方式1:通过Spring Data Elasticsearch Repository自定义聚合查询

直接在Repository接口中定义带有聚合的查询,利用@Query注解指定查询和聚合语句:

public interface ItemRepository extends ElasticsearchRepository<Item, String> {

    // 原三次查询方法可保留或替换
    @Query(value = "{\"bool\":{\"must\":[{\"match\":{\"userId\":?0}}]}}",
           countQuery = "{\"bool\":{\"must\":[{\"match\":{\"userId\":?0}}]}, \"aggs\":{\"score_ranges\":{\"range\":{\"field\":\"score\",\"ranges\":[{\"key\":\"low\",\"gte\":0,\"lte\":4},{\"key\":\"medium\",\"gt\":4,\"lte\":7},{\"key\":\"high\",\"gt\":7,\"lte\":10}]}}}}")
    AggregatedPage<Item> findByUserIdWithScoreRanges(String userId);
}

在Service中解析聚合结果:

@Service
public class ItemService {

    @Autowired
    private ItemRepository itemRepository;

    public Map<String, Long> getScoreCountRanges() {
        AggregatedPage<Item> result = itemRepository.findByUserIdWithScoreRanges("100");
        RangeAggregation scoreAgg = result.getAggregation("score_ranges", RangeAggregation.class);
        
        Map<String, Long> countMap = new HashMap<>();
        for (Range.Bucket bucket : scoreAgg.getBuckets()) {
            countMap.put(bucket.getKeyAsString(), bucket.getDocCount());
        }
        // 输出格式符合预期:{"high":20, "medium":10, "low":3}
        System.out.println(countMap);
        return countMap;
    }
}

方式2:使用ElasticsearchRestTemplate构建聚合查询(更灵活)

如果需要动态调整查询条件,推荐用RestTemplate手动构建聚合:

@Service
public class ItemService {

    @Autowired
    private ElasticsearchRestTemplate restTemplate;

    public Map<String, Long> getScoreCountRanges() {
        // 构建用户筛选条件
        BoolQueryBuilder query = QueryBuilders.boolQuery()
                .must(QueryBuilders.matchQuery("userId", 100));

        // 构建score范围聚合,精确匹配需求的区间
        RangeAggregationBuilder scoreAgg = AggregationBuilders.range("score_ranges")
                .field("score")
                .addRange("low").gte(0).lte(4)
                .addRange("medium").gt(4).lte(7)
                .addRange("high").gt(7).lte(10);

        // 组装搜索请求
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(query)
                .addAggregation(scoreAgg)
                .build();

        // 执行查询并解析结果
        SearchHits<Item> hits = restTemplate.search(searchQuery, Item.class);
        RangeAggregation aggResult = hits.getAggregations().get("score_ranges");

        Map<String, Long> countMap = new HashMap<>();
        for (Range.Bucket bucket : aggResult.getBuckets()) {
            countMap.put(bucket.getKeyAsString(), bucket.getDocCount());
        }
        return countMap;
    }
}

效率对比

一次聚合查询的优势

  1. 减少网络开销:仅需一次请求往返,避免三次查询带来的网络延迟,跨服务器部署时差异尤为明显。
  2. 降低集群负载:Elasticsearch只需遍历一次符合条件的文档集合,三次查询会重复执行相同的文档扫描,消耗更多CPU、内存和IO资源。
  3. 结果一致性:一次查询的统计基于同一数据快照,避免三次查询期间文档增删改导致的结果不一致。

三次查询的适用场景

仅当每个范围的查询逻辑完全独立、或需要单独缓存各范围结果时才有意义,当前场景下完全不适用。


内容的提问来源于stack exchange,提问作者Majd Mahajenah

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 18:07:10