Elasticsearch QueryBuilders 高级使用案例
本文将详细介绍 Elasticsearch QueryBuilders 的高级使用场景,包括复杂组合查询、聚合查询、嵌套查询、分页排序、高亮显示等高级功能,并提供完整的代码示例。:不同版本的 Elasticsearch 中 QueryBuilders 的方法可能有所不同,请根据实际使用的版本查阅官方文档。通过合理利用这些高级特性,您可以构建强大而高效的 Elasticsearch 查询,满足各种复
Elasticsearch QueryBuilders 高级使用案例
本文将详细介绍 Elasticsearch QueryBuilders 的高级使用场景,包括复杂组合查询、聚合查询、嵌套查询、分页排序、高亮显示等高级功能,并提供完整的代码示例。
1. 复杂组合查询(多层布尔查询)
1.1 多层嵌套布尔查询
// 构建复杂的多层嵌套布尔查询 BoolQueryBuilder outerBoolQuery = QueryBuilders.boolQuery(); // 第一层:必须满足的条件 outerBoolQuery.must(QueryBuilders.termQuery("status", "active")); // 第二层:条件组1(OR关系) BoolQueryBuilder innerShouldQuery1 = QueryBuilders.boolQuery(); innerShouldQuery1.should(QueryBuilders.matchQuery("title", "elasticsearch")); innerShouldQuery1.should(QueryBuilders.matchQuery("content", "elasticsearch")); innerShouldQuery1.minimumShouldMatch(1); // 至少满足一个条件 outerBoolQuery.must(innerShouldQuery1); // 第三层:条件组2(复杂范围条件) BoolQueryBuilder innerBoolQuery2 = QueryBuilders.boolQuery(); innerBoolQuery2.filter(QueryBuilders.rangeQuery("publishDate").gte("2023-01-01").lte("2023-12-31")); innerBoolQuery2.filter(QueryBuilders.rangeQuery("viewCount").gt(100)); outerBoolQuery.filter(innerBoolQuery2); // 第四层:排除条件 outerBoolQuery.mustNot(QueryBuilders.termQuery("category", "spam")); // 构建搜索请求 NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(outerBoolQuery) .withPageable(PageRequest.of(0, 20)) .build();
1.2 多字段加权查询
// 使用 function_score 进行多字段加权查询 FunctionScoreQueryBuilder functionScoreQuery = QueryBuilders.functionScoreQuery( // 基础查询 QueryBuilders.matchQuery("content", "elasticsearch tutorial"), // 函数评分列表 new FunctionScoreQueryBuilder.FilterFunctionBuilder[] { // 标题匹配加分 new FunctionScoreQueryBuilder.FilterFunctionBuilder( QueryBuilders.matchQuery("title", "elasticsearch tutorial"), ScoreFunctionBuilders.weightFactorFunction(3.0f) ), // 标签匹配加分 new FunctionScoreQueryBuilder.FilterFunctionBuilder( QueryBuilders.termsQuery("tags", "elasticsearch", "tutorial"), ScoreFunctionBuilders.weightFactorFunction(2.0f) ), // 最近发布的文档加分 new FunctionScoreQueryBuilder.FilterFunctionBuilder( QueryBuilders.matchAllQuery(), ScoreFunctionBuilders.gaussDecayFunction( "publishDate", new DateHistogramInterval("30d"), 0.5 ) ) } ).boostMode(CombineFunction.SUM); // 分数相加模式 NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(functionScoreQuery) .build();
2. 聚合查询(AggregationBuilders)
2.1 基础聚合查询
// 1. 术语聚合(分组统计) TermsAggregationBuilder termsAgg = AggregationBuilders.terms("by_category") .field("category.keyword") .size(10); // 2. 数值统计聚合 StatsAggregationBuilder statsAgg = AggregationBuilders.stats("price_stats") .field("price"); // 3. 平均值聚合 AvgAggregationBuilder avgAgg = AggregationBuilders.avg("avg_rating") .field("rating"); // 构建搜索请求 NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(QueryBuilders.matchAllQuery()) .addAggregation(termsAgg) .addAggregation(statsAgg) .addAggregation(avgAgg) .withPageable(PageRequest.of(0, 0)) // 只获取聚合结果,不返回文档 .build(); // 执行查询并获取聚合结果 SearchHits<Product> searchHits = elasticsearchRestTemplate.search(searchQuery, Product.class); // 解析聚合结果 Terms byCategoryTerms = searchHits.getAggregations().get("by_category"); for (Terms.Bucket bucket : byCategoryTerms.getBuckets()) { String category = bucket.getKeyAsString(); long count = bucket.getDocCount(); System.out.println("Category: " + category + ", Count: " + count); } Stats priceStats = searchHits.getAggregations().get("price_stats"); System.out.println("Min Price: " + priceStats.getMin()); System.out.println("Max Price: " + priceStats.getMax()); System.out.println("Avg Price: " + priceStats.getAvg());
2.2 嵌套聚合查询
// 按品牌分组,然后在每个品牌内按价格区间分组,最后计算每个组的平均评分 TermsAggregationBuilder brandAgg = AggregationBuilders.terms("by_brand") .field("brand.keyword") .size(10); RangeAggregationBuilder priceRangeAgg = AggregationBuilders.range("price_ranges") .field("price") .addUnboundedTo(1000) // 0-1000 .addRange(1000, 5000) // 1000-5000 .addUnboundedFrom(5000); // 5000+ AvgAggregationBuilder ratingAgg = AggregationBuilders.avg("avg_rating") .field("rating"); // 构建嵌套聚合 priceRangeAgg.subAggregation(ratingAgg); brandAgg.subAggregation(priceRangeAgg); NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(QueryBuilders.matchAllQuery()) .addAggregation(brandAgg) .withPageable(PageRequest.of(0, 0)) .build(); // 执行查询并解析嵌套聚合结果 SearchHits<Product> searchHits = elasticsearchRestTemplate.search(searchQuery, Product.class); Terms byBrandTerms = searchHits.getAggregations().get("by_brand"); for (Terms.Bucket brandBucket : byBrandTerms.getBuckets()) { String brand = brandBucket.getKeyAsString(); System.out.println("Brand: " + brand); Range priceRanges = brandBucket.getAggregations().get("price_ranges"); for (Range.Bucket rangeBucket : priceRanges.getBuckets()) { String range = rangeBucket.getKeyAsString(); long count = rangeBucket.getDocCount(); Avg avgRating = rangeBucket.getAggregations().get("avg_rating"); System.out.println(" Price Range: " + range + ", Count: " + count + ", Avg Rating: " + avgRating.getValue()); } }
2.3 日期直方图聚合
// 按月份统计数据量 DateHistogramAggregationBuilder dateAgg = AggregationBuilders.dateHistogram("by_month") .field("publishDate") .dateHistogramInterval(DateHistogramInterval.MONTH) .format("yyyy-MM") .minDocCount(1); // 只返回有文档的区间 // 添加子聚合:每月的平均浏览量 AvgAggregationBuilder viewAvgAgg = AggregationBuilders.avg("avg_views") .field("viewCount"); dateAgg.subAggregation(viewAvgAgg); NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(QueryBuilders.rangeQuery("publishDate").gte("2023-01-01")) .addAggregation(dateAgg) .withPageable(PageRequest.of(0, 0)) .build();
3. 嵌套查询(Nested Query)
3.1 基本嵌套查询
// 假设文档结构包含嵌套字段 comments // {"id": 1, "title": "...", "comments": [{"user": "张三", "content": "...", "rating": 5}]} // 嵌套字段查询 NestedQueryBuilder nestedQuery = QueryBuilders.nestedQuery( "comments", // 嵌套字段路径 QueryBuilders.boolQuery() .must(QueryBuilders.termQuery("comments.user", "张三")) .must(QueryBuilders.rangeQuery("comments.rating").gte(4)), ScoreMode.Avg // 评分模式 ); NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(nestedQuery) .build();
3.2 复杂嵌套查询与聚合结合
// 查找包含高质量评论的文章,并统计每篇文章的平均评论评分 BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); // 基础查询条件 boolQuery.must(QueryBuilders.termQuery("status", "published")); // 嵌套查询:至少有一条评论评分大于4 boolQuery.filter( QueryBuilders.nestedQuery( "comments", QueryBuilders.rangeQuery("comments.rating").gt(4), ScoreMode.None ) ); // 嵌套聚合:计算每篇文章的平均评论评分 NestedAggregationBuilder nestedAgg = AggregationBuilders.nested("comments_agg", "comments"); AvgAggregationBuilder avgRatingAgg = AggregationBuilders.avg("avg_comment_rating").field("comments.rating"); nestedAgg.subAggregation(avgRatingAgg); NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(boolQuery) .addAggregation(nestedAgg) .withPageable(PageRequest.of(0, 10)) .build();
4. 分页、排序与高亮
4.1 高效分页与排序
// 构建查询 BoolQueryBuilder query = QueryBuilders.boolQuery() .must(QueryBuilders.matchQuery("title", "elasticsearch")) .filter(QueryBuilders.termQuery("status", "active")); // 多字段排序 List<FieldSortBuilder> sortBuilders = new ArrayList<>(); sortBuilders.add(SortBuilders.fieldSort("publishDate").order(SortOrder.DESC)); sortBuilders.add(SortBuilders.fieldSort("viewCount").order(SortOrder.DESC)); sortBuilders.add(SortBuilders.fieldSort("_score").order(SortOrder.DESC)); // 构建搜索请求(使用searchAfter进行深度分页) NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(query) .withSorts(sortBuilders) .withPageable(PageRequest.of(0, 100)) // 第一页 .build(); // 执行查询 SearchHits<Article> searchHits = elasticsearchRestTemplate.search(searchQuery, Article.class); // 获取最后一条记录的排序值,用于下一页查询 if (searchHits.hasSearchHits() && searchHits.getSearchHits().size() == 100) { SearchHit<Article> lastHit = searchHits.getSearchHits().get(searchHits.getSearchHits().size() - 1); Object[] sortValues = lastHit.getSortValues(); // 构建下一页查询(使用searchAfter) NativeSearchQuery nextPageQuery = new NativeSearchQueryBuilder() .withQuery(query) .withSorts(sortBuilders) .withSearchAfter(sortValues) // 使用上一页最后一条记录的排序值 .withPageable(PageRequest.of(0, 100)) // 页码始终为0 .build(); }
4.2 高亮显示
// 构建查询 QueryBuilder query = QueryBuilders.multiMatchQuery( "elasticsearch tutorial", "title", "content", "description" ); // 配置高亮 HighlightBuilder highlightBuilder = new HighlightBuilder(); highlightBuilder.field("title") .preTags("<em>") .postTags("</em>") .fragmentSize(100) .numOfFragments(1); highlightBuilder.field("content") .preTags("<em>") .postTags("</em>") .fragmentSize(200) .numOfFragments(3); // 构建搜索请求 NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(query) .withHighlightBuilder(highlightBuilder) .build(); // 执行查询并处理高亮结果 SearchHits<Article> searchHits = elasticsearchRestTemplate.search(searchQuery, Article.class); for (SearchHit<Article> hit : searchHits) { Article article = hit.getContent(); Map<String, List<String>> highlightFields = hit.getHighlightFields(); // 处理高亮片段 if (highlightFields.containsKey("title")) { String highlightedTitle = highlightFields.get("title").get(0); System.out.println("Highlighted Title: " + highlightedTitle); } if (highlightFields.containsKey("content")) { List<String> contentFragments = highlightFields.get("content"); System.out.println("Content Fragments:"); for (String fragment : contentFragments) { System.out.println(" " + fragment); } } }
5. 脚本查询与性能优化
5.1 脚本查询
// 使用脚本查询(计算两个字段的差值) Script script = new Script(ScriptType.INLINE, "painless", "doc['price'].value - doc['discount_price'].value > params.threshold", Collections.singletonMap("threshold", 100)); ScriptQueryBuilder scriptQuery = QueryBuilders.scriptQuery(script); NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(scriptQuery) .build();
5.2 性能优化技巧
// 1. 使用filter上下文而不是must(不计算评分,可缓存) BoolQueryBuilder optimizedQuery = QueryBuilders.boolQuery(); // 过滤条件使用filter optimizedQuery.filter(QueryBuilders.termQuery("status", "active")); optimizedQuery.filter(QueryBuilders.rangeQuery("price").from(100).to(1000)); // 只有搜索条件使用must optimizedQuery.must(QueryBuilders.matchQuery("title", "elasticsearch")); // 2. 使用terms查询替代多个term查询 optimizedQuery.filter(QueryBuilders.termsQuery("category.keyword", "tech", "programming", "database")); // 3. 合理设置查询大小和超时 NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(optimizedQuery) .withPageable(PageRequest.of(0, 50)) // 限制返回数量 .withTimeout(TimeValue.timeValueSeconds(10)) // 设置超时时间 .build(); // 4. 禁用不必要的功能 NativeSearchQuery optimizedSearchQuery = new NativeSearchQueryBuilder() .withQuery(optimizedQuery) .withFetchSource("id", "title", "price") // 只返回需要的字段 .withTrackScores(false) // 不需要评分时禁用 .withTrackTotalHitsUpTo(10000) // 限制总命中数跟踪,提高性能 .build();
6. 实际业务场景综合示例
6.1 电商搜索场景
@Service public class ProductSearchService { @Autowired private ElasticsearchRestTemplate elasticsearchRestTemplate; /** * 电商搜索综合查询 * 支持:关键词搜索、分类筛选、品牌筛选、价格范围、评分筛选、排序、分页、高亮 */ public SearchResult searchProducts(ProductSearchRequest request) { BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); // 1. 关键词搜索 if (StringUtils.hasText(request.getKeyword())) { boolQuery.must(QueryBuilders.multiMatchQuery( request.getKeyword(), "name", "description", "keywords" ).minimumShouldMatch("70%")); } // 2. 分类筛选 if (StringUtils.hasText(request.getCategory())) { boolQuery.filter(QueryBuilders.termQuery("category.keyword", request.getCategory())); } // 3. 品牌筛选 if (CollectionUtils.isNotEmpty(request.getBrands())) { boolQuery.filter(QueryBuilders.termsQuery("brand.keyword", request.getBrands())); } // 4. 价格范围 if (request.getMinPrice() != null || request.getMaxPrice() != null) { RangeQueryBuilder priceRange = QueryBuilders.rangeQuery("price"); if (request.getMinPrice() != null) priceRange.gte(request.getMinPrice()); if (request.getMaxPrice() != null) priceRange.lte(request.getMaxPrice()); boolQuery.filter(priceRange); } // 5. 评分筛选 if (request.getMinRating() != null) { boolQuery.filter(QueryBuilders.rangeQuery("rating").gte(request.getMinRating())); } // 6. 排序 List<FieldSortBuilder> sorts = new ArrayList<>(); if (StringUtils.hasText(request.getSortBy())) { SortOrder order = request.isAscending() ? SortOrder.ASC : SortOrder.DESC; switch (request.getSortBy()) { case "price": sorts.add(SortBuilders.fieldSort("price").order(order)); break; case "rating": sorts.add(SortBuilders.fieldSort("rating").order(order)); break; case "sales": sorts.add(SortBuilders.fieldSort("salesCount").order(order)); break; case "newest": sorts.add(SortBuilders.fieldSort("createTime").order(SortOrder.DESC)); break; default: // 默认按相关性排序 sorts.add(SortBuilders.fieldSort("_score").order(SortOrder.DESC)); } } else { // 默认排序 sorts.add(SortBuilders.fieldSort("_score").order(SortOrder.DESC)); sorts.add(SortBuilders.fieldSort("salesCount").order(SortOrder.DESC)); } // 7. 高亮配置 HighlightBuilder highlightBuilder = new HighlightBuilder(); highlightBuilder.field("name") .preTags("<em class='highlight'>") .postTags("</em>") .fragmentSize(100) .numOfFragments(1); // 8. 聚合查询(用于筛选器) TermsAggregationBuilder brandAgg = AggregationBuilders.terms("by_brand") .field("brand.keyword") .size(20); RangeAggregationBuilder priceAgg = AggregationBuilders.range("price_ranges") .field("price") .addUnboundedTo(100) .addRange(100, 500) .addRange(500, 1000) .addRange(1000, 3000) .addUnboundedFrom(3000); // 9. 构建搜索请求 NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(boolQuery) .withSorts(sorts) .withHighlightBuilder(highlightBuilder) .addAggregation(brandAgg) .addAggregation(priceAgg) .withPageable(PageRequest.of(request.getPage(), request.getSize())) .withTimeout(TimeValue.timeValueSeconds(5)) .build(); // 10. 执行查询 SearchHits<Product> searchHits = elasticsearchRestTemplate.search(searchQuery, Product.class); // 11. 处理结果 List<ProductDTO> products = new ArrayList<>(); for (SearchHit<Product> hit : searchHits) { Product product = hit.getContent(); ProductDTO dto = convertToDTO(product); // 设置高亮 Map<String, List<String>> highlightFields = hit.getHighlightFields(); if (highlightFields.containsKey("name")) { dto.setHighlightName(highlightFields.get("name").get(0)); } products.add(dto); } // 12. 处理聚合结果 Map<String, List<FilterOption>> filters = new HashMap<>(); // 品牌筛选器 Terms byBrandTerms = searchHits.getAggregations().get("by_brand"); List<FilterOption> brandOptions = byBrandTerms.getBuckets().stream() .map(bucket -> new FilterOption(bucket.getKeyAsString(), bucket.getDocCount())) .collect(Collectors.toList()); filters.put("brand", brandOptions); // 价格筛选器 Range priceRanges = searchHits.getAggregations().get("price_ranges"); List<FilterOption> priceOptions = priceRanges.getBuckets().stream() .map(bucket -> new FilterOption(bucket.getKeyAsString(), bucket.getDocCount())) .collect(Collectors.toList()); filters.put("price", priceOptions); // 13. 返回结果 SearchResult result = new SearchResult(); result.setProducts(products); result.setTotalHits(searchHits.getTotalHits()); result.setFilters(filters); result.setCurrentPage(request.getPage()); result.setPageSize(request.getSize()); return result; } private ProductDTO convertToDTO(Product product) { // 转换逻辑 return new ProductDTO(); } }
7. 注意事项与最佳实践
-
版本兼容性:不同版本的 Elasticsearch 中 QueryBuilders 的方法可能有所不同,请根据实际使用的版本查阅官方文档。
-
查询性能:
- 优先使用 filter 上下文而不是 query 上下文进行过滤操作
- 避免使用 leading wildcard 查询
- 对于深度分页,使用 search_after 而不是 from/size
- 合理设置查询超时时间
-
内存使用:
- 聚合查询可能消耗大量内存,特别是高基数字段的聚合
- 限制返回字段,只获取必要的数据
- 对于大型结果集,考虑使用 scroll API
-
索引设计:
- 为需要精确匹配的字段使用 keyword 类型
- 为全文搜索字段选择合适的分词器
- 对于频繁过滤的字段,考虑启用 doc_values
-
复杂查询调试:
- 使用 explain API 分析查询执行计划
- 检查慢查询日志,优化性能瓶颈
- 考虑使用预计算或缓存热点数据
通过合理利用这些高级特性,您可以构建强大而高效的 Elasticsearch 查询,满足各种复杂的业务需求。
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)