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; } }
效率对比
一次聚合查询的优势
- 减少网络开销:仅需一次请求往返,避免三次查询带来的网络延迟,跨服务器部署时差异尤为明显。
- 降低集群负载:Elasticsearch只需遍历一次符合条件的文档集合,三次查询会重复执行相同的文档扫描,消耗更多CPU、内存和IO资源。
- 结果一致性:一次查询的统计基于同一数据快照,避免三次查询期间文档增删改导致的结果不一致。
三次查询的适用场景
仅当每个范围的查询逻辑完全独立、或需要单独缓存各范围结果时才有意义,当前场景下完全不适用。
内容的提问来源于stack exchange,提问作者Majd Mahajenah
相关产品推荐
相关产品推荐

