Elasticsearch查询DSL API完全指南:从入门到精通
1. 从搜索框到数据海洋:Elasticsearch查询DSL的核心价值
想象你走进一家超级市场,货架上摆满了数百万种商品。如果你想找到"2023年产的有机橄榄油",最有效的方式是什么?直接向店员描述需求------这就是Elasticsearch查询DSL的作用。作为Elasticsearch的"查询语言",DSL(Domain Specific Language)让你能够精确描述数据需求,从海量信息中快速定位目标。无论是电商平台的商品搜索、日志系统的异常检测,还是智能客服的意图识别,掌握DSL API都是解锁Elasticsearch强大能力的关键。
2. 基础入门:查询DSL的"语法规则"
核心搜索入口:ElasticsearchClient.search()
所有查询操作都始于ElasticsearchClient.search()方法,它就像你递给店员的"购物清单"模板。这个方法需要两个关键参数:描述查询逻辑的DSL配置函数,以及指定返回结果类型的Class对象。
基础语法示例:
java
SearchResponse<Product> response = esClient.search(s -> s
.index("products") // 指定操作的索引(相当于数据库表)
.query(q -> q.match(m -> m // 使用匹配查询
.field("name") // 搜索字段
.query("有机橄榄油") // 搜索关键词
)), Product.class); // 结果映射到Product类
这个简单示例实现了"在products索引中搜索名称包含'有机橄榄油'的商品"的功能。返回的SearchResponse对象包含命中结果、总条数等元数据,通过response.hits().hits()可获取实际数据列表。
查询DSL的基本构成:查询子句
每个查询都是由一个或多个"查询子句"组成的,就像购物清单上的具体条目。Elasticsearch提供了数十种查询子句,常用的可分为"全文查询"和"术语查询"两大类:
全文查询:match
match查询会对输入文本进行分词处理,适合自然语言搜索。例如搜索"有机橄榄油"时,会分别匹配包含"有机"或"橄榄油"的文档,并根据相关性评分排序。
java
// 基础match查询
.query(q -> q.match(m -> m
.field("description")
.query("健康 烹饪 油")
))
// 进阶:指定匹配逻辑(默认是OR)
.query(q -> q.match(m -> m
.field("description")
.query("健康 烹饪 油")
.operator(Operator.And) // 必须同时包含所有词项
))
术语查询:term
term查询用于精确匹配,不对输入文本分词,适合关键词、ID等精确值搜索。例如匹配"category"字段为"food"的文档:
java
.query(q -> q.term(t -> t
.field("category")
.value("food")
))
新手注意:如果对text类型字段使用term查询可能返回空结果,因为text字段会被分词处理。此时应使用match查询,或对keyword子字段(如"category.keyword")使用term查询。
3. 核心查询类型:打造你的"搜索工具箱"
布尔查询:组合条件的逻辑控制
现实中的搜索需求往往包含多个条件。布尔查询(bool)允许你通过must(必须满足)、should(应该满足)、must_not(必须不满足)和filter(过滤条件)四种方式组合多个查询子句,实现复杂逻辑判断。
生活类比:"我要找2023年产(must)且价格低于100元(filter)的橄榄油,最好是有机认证的(should)"
java
.query(q -> q.bool(b -> b
.must(m1 -> m1.term(t -> t.field("production_year").value(2023))) // 必须2023年产
.should(s1 -> s1.term(t -> t.field("certification").value("organic"))) // 最好有机认证
.filter(f1 -> f1.range(r -> r.field("price").lt(100.0))) // 价格必须低于100元
.minimumShouldMatch(1) // should条件至少满足1个
))
关键区别:must和should会影响相关性评分,而filter仅过滤结果不影响评分,且会被Elasticsearch缓存,性能更好。因此,固定条件(如权限过滤、状态筛选)应优先使用filter。
范围查询:数值与时间的边界控制
range查询用于匹配指定范围内的数值或日期,如"价格在50-100元之间"、"创建时间在最近7天内"。
java
// 数值范围查询
.filter(f -> f.range(r -> r
.field("price")
.gte(50.0) // 大于等于
.lt(100.0) // 小于
))
// 日期范围查询
.filter(f -> f.range(r -> r
.field("create_time")
.gte("now-7d/d") // 7天前(四舍五入到天)
.lte("now") // 现在
))
日期表达式支持多种动态格式:now-1h(1小时前)、2023-10-01(具体日期)、2023-10-01T08:00:00Z(带时区的具体时间)等。
KNN向量搜索:AI时代的相似性匹配
在推荐系统、图像搜索等场景中,我们需要找到与"查询向量"最相似的文档。knn查询正是为此设计,它通过计算向量间的距离(如余弦相似度)来找到最相似的Top K结果。
java
// 向量搜索示例(假设存在vector字段存储商品特征向量)
.knn(kn -> kn
.field("vector") // 向量字段名
.queryVector(new float[]{0.12f, 0.34f, 0.56f}) // 查询向量(如用户兴趣向量)
.k(10) // 返回10个结果
.numCandidates(100) // 候选集大小(影响精度和性能)
)
关键参数:numCandidates应大于k,建议设为k的10-20倍。较大的候选集会提高召回率,但增加计算开销。实际应用中需根据数据集特点调整。
4. 进阶技巧:构建高效精准的查询
组合查询:复杂场景的逻辑编排
实际业务场景往往需要组合多种查询类型。例如电商搜索中常见的"关键词搜索+分类过滤+价格范围+用户个性化排序":
java
.search(s -> s
.index("products")
.query(q -> q.bool(b -> b
.must(m -> m.match(m -> m // 全文搜索关键词
.field("name")
.query("橄榄油")
))
.filter(f -> f.bool(fb -> fb // 多个过滤条件组合
.must(fm -> fm.term(t -> t.field("category").value("food"))) // 分类必须是食品
.must(fm -> fm.range(r -> r.field("price").lt(100.0))) // 价格低于100
))
))
.rescore(r -> r // 重排序:结合用户行为数据调整结果顺序
.windowSize(50) // 对前50个结果重排序
.query(rq -> rq
.queryWeight(0.7f) // 原始查询权重
.rescoreQueryWeight(0.3f) // 重排序查询权重
.query(rqq -> rqq.term(t -> t.field("is_hot").value(true))) // 热门商品加权
)
)
.size(20) // 返回20条结果
)
这个示例展示了生产环境常见的复杂查询结构:基础搜索+多条件过滤+重排序,既保证了相关性,又融入了业务规则。
性能优化:让查询飞起来
1. 合理使用过滤器缓存
将高频固定条件(如状态、权限)放入filter子句,Elasticsearch会自动缓存这些条件的结果,显著提升查询速度。
2. 控制返回字段
使用_source参数只返回需要的字段,减少网络传输和内存占用:
java
.source(s -> s
.includes("id", "name", "price") // 只返回这些字段
.excludes("description") // 排除大文本字段
)
3. 分页优化
深度分页(如from:10000, size:10)会导致性能问题,因为Elasticsearch需要从每个分片加载前10010条数据。此时应使用search_after分页:
java
// 首次查询
.search(s -> s
.size(10)
.sort(sort -> sort.field(f -> f.field("id").order(SortOrder.Asc)))
)
// 后续分页(使用上一页最后一条的id作为游标)
.search(s -> s
.size(10)
.sort(sort -> sort.field(f -> f.field("id").order(SortOrder.Asc)))
.searchAfter(lastId) // lastId是上一页最后一条数据的id
)
4. KNN搜索调优
numCandidates参数对向量搜索性能影响显著,建议设为k的10-20倍(如k=10时numCandidates=200),在召回率和性能间平衡。
边界处理:异常情况的优雅应对
当用户无权限或查询条件非法时,可使用matchNone返回空结果,避免抛出异常:
java
// 权限检查失败时返回空结果
if (!hasPermission(user, index)) {
return esClient.search(s -> s.matchNone(mn -> mn), Product.class);
}
5. 高级查询功能:挖掘数据深层价值
聚合查询:数据统计与分析利器
聚合查询(Aggregation)是Elasticsearch的高级分析功能,允许你对数据进行统计、分组和计算,类似于SQL中的GROUP BY和聚合函数。聚合查询分为"指标聚合"(对数值进行计算)和"桶聚合"(对文档进行分组)两大类,广泛用于报表生成、数据仪表盘等场景。
指标聚合:数值计算的五种基础操作
指标聚合用于对文档中的数值字段进行统计计算,支持max(最大值)、min(最小值)、sum(总和)、avg(平均值)和stats(多指标统计)等操作。
基础语法示例:
java
// 统计商品价格的基本指标(max/min/avg/sum)
SearchResponse<Void> response = esClient.search(s -> s
.index("products")
.size(0) // 不返回原始文档,只返回聚合结果
.aggregations(a -> a
.max("max_price", m -> m.field("price")) // 最高价格
.min("min_price", m -> m.field("price")) // 最低价格
.avg("avg_price", m -> m.field("price")) // 平均价格
.sum("total_sales", m -> m.field("sales")) // 销售总额
.stats("price_stats", m -> m.field("price")) // 综合统计
), Void.class);
// 获取聚合结果
double maxPrice = response.aggregations().get("max_price").max().value();
double avgPrice = response.aggregations().get("avg_price").avg().value();
Stats stats = response.aggregations().get("price_stats").stats();
double statsMin = stats.min(); // 等价于min_price
double statsMax = stats.max(); // 等价于max_price
6. 适用场景
电商搜索
电商平台的核心功能之一,需要快速响应用户的商品查询需求,并返回相关性高的结果。用户通常会输入关键词进行搜索,同时可能会有价格范围、品牌、评分等筛选条件。
场景一:电商商品搜索
需求:实现"搜索手机,价格在2000-5000元之间,优先显示评分高于4.5分且有货的商品"
java
SearchResponse<Product> response = esClient.search(s -> s
.index("products")
.query(q -> q.bool(b -> b
.must(m -> m.match(m -> m // 全文搜索"手机"
.field("name")
.query("手机")
))
.filter(fb -> fb.bool(fbb -> fbb // 多条件过滤
.must(fm -> fm.range(r -> r // 价格范围
.field("price")
.gte(2000.0)
.lte(5000.0)
))
.must(fm -> fm.term(t -> t // 有货
.field("stock")
.value(true)
))
))
.should(sm -> sm.range(r -> r // 高分商品优先(影响评分)
.field("rating")
.gt(4.5)
))
.minimumShouldMatch(0) // should条件可选
))
.sort(sort -> sort // 排序:评分降序,价格升序
.field(f -> f.field("rating").order(SortOrder.Desc))
.field(f -> f.field("price").order(SortOrder.Asc))
)
.size(20)
.source(s -> s.includes("id", "name", "price", "rating", "image_url"))
, Product.class);
日志分析
日志分析是系统监控和故障排查的关键环节,通过Elasticsearch的实时查询能力,可以快速定位异常日志,及时发现系统问题。
场景二:系统异常检测
需求:实时监控系统日志,检测过去24小时内error级别且包含'NullPointerException'的记录
java
SearchResponse<Log> response = esClient.search(s -> s
.index("system_logs")
.query(q -> q.bool(b -> b
.must(m -> m.term(t -> t // 日志级别必须是error
.field("level")
.value("error")
))
.must(m -> m.matchPhrase(mp -> mp // 消息包含完整异常短语
.field("message")
.query("NullPointerException")
))
.filter(f -> f.range(r -> r // 时间范围:过去24小时
.field("timestamp")
.gte("now-24h")
.lte("now")
))
))
.sort(sort -> sort.field(f -> f.field("timestamp").order(SortOrder.Desc))) // 最新日志优先
.size(50)
, Log.class);
智能客服
智能客服系统需要准确理解用户问题意图,快速匹配知识库中的标准答案,提供高效的自动化服务。通过Elasticsearch的文本匹配能力,可以将用户输入的自然语言问题与预定义的意图类别关联起来。
场景三:用户意图识别
需求:识别用户问题意图并匹配知识库答案,如将"如何修改密码"归类为"账户设置"意图
java
SearchResponse<KnowledgeItem> response = esClient.search(s -> s
.index("knowledge_base")
.query(q -> q.bool(b -> b
.must(m -> m.matchPhrase(mp -> mp // 精确匹配问题短语
.field("question")
.query("修改密码")
))
.filter(f -> f.term(t -> t // 过滤特定意图类别
.field("intent")
.value("account_setting")
))
))
.sort(sort -> sort // 按相关度和更新时间排序
.field(f -> f.field("_score").order(SortOrder.Desc))
.field(f -> f.field("update_time").order(SortOrder.Desc))
)
.size(5) // 返回Top5匹配结果
.source(s -> s.includes("id", "question", "answer", "intent"))
, KnowledgeItem.class);
- 基础阶段:熟悉match、term、bool等核心查询类型,能构建简单的组合查询
- 应用阶段:学习range、wildcard、prefix等特殊查询,掌握过滤、排序、分页等功能
- 优化阶段:理解查询执行计划,学会使用Profile API分析性能瓶颈,掌握缓存、分片优化等高级技巧
- 专业阶段:深入向量搜索、聚合分析、跨集群搜索等高级特性,结合业务场景设计复杂查询逻辑
Elasticsearch的查询DSL就像一门编程语言,需要不断实践才能灵活运用。官方文档(Elasticsearch Query DSL)是最好的学习资源,建议常备查阅。
7. 结语:解锁数据价值的钥匙
混合检索查询
在实际业务中,单一检索方式往往难以满足复杂需求。混合检索结合关键词精确匹配与向量语义理解的优势,既能保证查询的准确性,又能提升结果的语义相关性。典型实现方式是使用bool查询组合match和knn子句,适用于电商搜索、内容推荐和智能问答等场景。
实现方式:bool组合关键词与向量搜索
通过bool查询的should子句组合match(关键词)和knn(向量)子句,可同时利用精确匹配和语义相似性,通过权重调整平衡两种检索方式的影响。
java
SearchResponse<Document> response = esClient.search(s -> s
.index("documents")
.query(q -> q.bool(b -> b
.should(s1 -> s1.match(m -> m // 关键词检索
.field("title")
.query("Elasticsearch 查询优化")
.boost(2.0f) // 关键词权重更高
))
.should(s2 -> s2.knn(k -> k // 向量语义检索
.field("content_vector")
.queryVector(generateEmbedding("如何优化Elasticsearch查询性能"))
.k(10)
.numCandidates(100)
.boost(1.5f) // 向量权重次之
))
.minimumShouldMatch(1) // 至少满足一个条件
))
.sort(sort -> sort.field(f -> f.field("_score").order(SortOrder.Desc)))
.size(20)
, Document.class);
在信息爆炸的时代,高效检索和分析数据的能力变得越来越重要。Elasticsearch查询DSL不仅是操作Elasticsearch的工具,更是一种"数据思维"------它教会我们如何精确描述需求,如何从海量数据中提取有价值的信息。无论是产品搜索、日志分析还是AI应用,掌握这些API都将让你在数据处理的道路上走得更远。
记住,最好的学习方式是实践。选择一个实际问题,尝试用不同的查询组合去解决它,观察结果的变化,分析性能的差异。随着经验的积累,你会逐渐发现:原来复杂的数据需求,都可以通过这些简洁的API来优雅实现。
Elasticsearch 的查询 DSL(Domain Specific Language)是一套基于 JSON 的查询语法,用于精确描述搜索条件。在 Java API 中,查询 DSL 通过流式构建器(Builder) 实现,无需手动编写 JSON,而是通过链式调用方法生成查询逻辑。本文从基础到进阶,详解如何使用查询 DSL API,涵盖核心查询类型、组合方式及实战技巧。
DSL API
一、查询 DSL API 基础:核心概念与结构
什么是查询 DSL API?
查询 DSL API 是 Elasticsearch Java 客户端中用于构建查询条件的工具集,通过Query.Builder及其子接口(如MatchQuery.Builder、BoolQuery.Builder),以 "方法链" 的形式定义查询逻辑,最终转化为 ES 能识别的 JSON 查询语句。
核心入口:.query()方法
所有查询逻辑都通过SearchRequest.Builder的.query()方法传入,例如:
java
运行
java
// 基础结构:通过.query()传入查询逻辑
esClient.search(s -> s
.index("products") // 指定索引
.query(q -> q // 核心:查询DSL构建
.match(m -> m // 具体查询类型(这里是match查询)
.field("name")
.query("手机")
)
),
Product.class
);
查询的本质:"匹配规则 + 评分"
每个查询都会做两件事:
-
匹配文档:判断文档是否满足条件(如 "name 包含'手机'");
-
计算评分(_score)
:评估文档与查询的相关性(评分越高,越相关)。
(例外:
filter中的查询只匹配不评分,用于筛选)
二、基础查询类型:从简单匹配到精确筛选
1. 全文匹配查询:match(最常用的全文搜索)
作用:对 text 类型字段进行分词后模糊匹配(类似 "百度搜索"),支持自然语言查询。
用法示例:搜索商品名称包含 "智能手机" 的商品
java
运行
java
.query(q -> q
.match(m -> m
.field("name") // 要搜索的字段(需为text类型,支持分词)
.query("智能手机") // 搜索文本
.operator(Operator.And) // 匹配规则:必须包含所有分词("智能"和"手机")
.fuzziness(Fuzziness.One) // 模糊匹配:允许1个字符错误(如"智能手记"也能匹配)
)
)
关键参数:
.field(String):指定 text 类型字段(如商品名称、文章内容);.query(String):搜索文本(会被分词器拆分,如 "智能手机" 拆分为 "智能""手机");.operator(Operator):匹配逻辑(And:必须包含所有分词;Or:包含任一分词即可,默认);.fuzziness(Fuzziness):模糊匹配(Auto/Zero/One/Two),处理拼写错误。
适用场景:
- 用户输入的自然语言搜索(如 "便宜的智能手机");
- 全文检索(如博客内容、商品描述搜索)。
2. 精确匹配查询:term(keyword 字段专用)
作用 :对 keyword 类型字段进行精确匹配(不分词,完全一致才匹配),类似 SQL 中的=。
用法示例:筛选 "品牌为苹果" 的商品
java
运行
java
.query(q -> q
.term(t -> t
.field("brand") // 字段需为keyword类型(不可分词,如品牌、ID、状态)
.value("苹果") // 精确匹配的值(必须完全一致)
)
)
与match的核心区别:
| 维度 | term查询 |
match查询 |
|---|---|---|
| 字段类型 | 仅适用于 keyword 类型 | 适用于 text 类型 |
| 分词处理 | 不分词,精确匹配 | 会分词,模糊匹配 |
| 典型场景 | 筛选标签、状态、ID | 全文搜索、自然语言查询 |
3. 多值精确匹配:terms(类似 SQL 的IN)
作用 :匹配字段值在指定列表中的文档,类似 "where brand in ('苹果', '华为')"。
用法示例:筛选品牌为苹果或华为的商品
java
运行
java
.query(q -> q
.terms(t -> t
.field("brand") // keyword类型字段
.values("苹果", "华为") // 允许的值列表
)
)
4. 范围查询:range(数值 / 日期范围筛选)
作用:匹配字段值在指定范围内的文档(支持数值、日期、时间戳)。
用法示例:筛选价格在 2000-5000 元的商品
java
运行
java
.query(q -> q
.range(r -> r
.field("price") // 数值类型字段(如价格、数量)
.gte(JsonData.of(2000)) // 大于等于(>=)
.lte(JsonData.of(5000)) // 小于等于(<=)
)
)
日期范围示例:筛选 30 天内发布的文章
java
运行
java
.query(q -> q
.range(r -> r
.field("publishTime") // 日期类型字段
.gte(JsonData.of("now-30d/d")) // 30天前(ES支持日期表达式)
.lte(JsonData.of("now")) // 现在
)
)
关键参数:
gte:大于等于;gt:大于;lte:小于等于;lt:小于。
三、复合查询:组合多条件的bool查询(核心)
bool查询是最常用的复合查询,通过must(并且)、should(或者)、filter(过滤)、must_not(非)四种逻辑组合多个查询条件,实现复杂的多条件搜索。
bool查询结构与用法
java
运行
java
.query(q -> q
.bool(b -> b
// 1. must:所有条件必须满足(影响评分)
.must(m1 -> m1.match(m -> m.field("name").query("手机"))) // 名称包含"手机"
.must(m2 -> m2.range(r -> r.field("price").lte(JsonData.of(5000)))), // 价格<=5000
// 2. should:至少满足一个条件(影响评分,满足越多评分越高)
.should(s1 -> s1.term(t -> t.field("brand").value("苹果"))) // 品牌是苹果
.should(s2 -> s2.term(t -> t.field("brand").value("华为"))) // 或华为
// 3. filter:过滤条件(不影响评分,可缓存,性能更好)
.filter(f1 -> f1.term(t -> t.field("status").value("在售"))) // 状态为在售
.filter(f2 -> f2.range(r -> r.field("stock").gt(JsonData.of(0)))), // 库存>0
// 4. must_not:必须不满足(不影响评分)
.must_not(mn -> mn.term(t -> t.field("tags").value("二手"))) // 排除"二手"标签
)
)
各子句作用与区别
| 子句 | 逻辑含义 | 是否影响评分 | 性能特点 | 适用场景 |
|---|---|---|---|---|
must |
必须满足(AND) | 是 | 无缓存,性能一般 | 核心搜索条件(如关键词匹配) |
should |
至少满足一个(OR) | 是 | 无缓存,性能一般 | 可选条件(如多品牌、多标签) |
filter |
过滤条件 | 否 | 有缓存,性能好 | 权限控制、状态 / 库存筛选等固定条件 |
must_not |
必须不满足(NOT) | 否 | 无缓存,性能一般 | 排除不想要的结果(如排除二手商品) |
实战场景:电商商品搜索
需求:搜索 "手机",价格在 2000-5000 元,品牌是苹果或华为,状态为在售且有库存,排除二手商品。用bool查询实现如下:
java
运行
java
.query(q -> q
.bool(b -> b
.must(m1 -> m1.match(m -> m.field("name").query("手机").operator(Operator.And)))
.must(m2 -> m2.range(r -> r.field("price").gte(2000).lte(5000)))
.should(s1 -> s1.term(t -> t.field("brand").value("苹果")))
.should(s2 -> s2.term(t -> t.field("brand").value("华为")))
.filter(f1 -> f1.term(t -> t.field("status").value("在售")))
.filter(f2 -> f2.range(r -> r.field("stock").gt(0)))
.must_not(mn -> mn.term(t -> t.field("tags").value("二手")))
)
)
四、高级查询类型:特殊场景的精准匹配
1. 前缀查询:prefix(匹配前缀)
作用:匹配字段值以指定前缀开头的文档(适合 keyword 类型字段)。
示例:筛选品牌以 "小" 开头的商品(如 "小米")
java
运行
java
.query(q -> q
.prefix(p -> p
.field("brand")
.value("小")
)
)
2. 通配符查询:wildcard(模糊匹配,支持 * 和?)
作用 :通过通配符匹配字段值(*匹配任意字符,?匹配单个字符),适合灵活但精度要求不高的场景。
示例:筛选品牌包含 "米" 的商品(如 "小米""大米")
java
运行
java
.query(q -> q
.wildcard(w -> w
.field("brand")
.value("*米*") // *匹配任意字符
)
)
注意:
通配符查询性能较差(尤其是前缀带*),尽量避免在大数据量场景使用。
3. 模糊查询:fuzzy(容错匹配,比match的 fuzziness 更灵活)
作用 :允许指定字符差异数的模糊匹配(类似 "拼写纠错"),可单独使用(不限于match)。
示例:允许 2 个字符错误匹配 "苹果"(如 "萍果""苹果 1")
java
运行
java
.query(q -> q
.fuzzy(f -> f
.field("brand")
.value("苹果")
.fuzziness(Fuzziness.Two) // 允许2个字符差异
)
)
五、查询与过滤的核心区别(进阶必知)
在bool查询中,must/should与filter/must_not的核心区别在于是否影响评分 和是否被缓存:
| 特性 | must/should(查询) |
filter/must_not(过滤) |
|---|---|---|
| 评分影响 | 影响(匹配度越高,评分越高) | 不影响(只筛选,评分固定为 1) |
| 缓存机制 | 不缓存查询结果 | 缓存过滤结果(重复查询时更快) |
| 适用场景 | 动态变化的搜索条件(如关键词) | 固定不变的筛选条件(如权限、状态) |
优化原则:
- 高频且固定的条件(如 "状态 = 在售""用户权限")用
filter,利用缓存提升性能; - 影响相关性的动态条件(如用户输入的关键词)用
must/should,保证结果排序合理。
六、实战:构建一个复杂的多条件搜索
需求:在 "博客" 索引中搜索满足以下条件的文章:
- 标题或内容包含 "Elasticsearch";
- 发布时间在 2023 年之后;
- 标签是 "技术" 或 "教程";
- 阅读量大于 1000;
- 排除作者为 "测试账号" 的文章。
实现代码:
java
运行
java
SearchResponse<Blog> response = esClient.search(s -> s
.index("blogs")
.query(q -> q
.bool(b -> b
// 条件1:标题或内容包含"Elasticsearch"(should表示"或")
.should(s1 -> s1.match(m -> m.field("title").query("Elasticsearch")))
.should(s2 -> s2.match(m -> m.field("content").query("Elasticsearch")))
// 至少满足一个should条件(默认情况下,无must时需至少满足1个should)
.minimumShouldMatch("1")
// 条件2:发布时间在2023年之后(filter,不影响评分)
.filter(f1 -> f1.range(r -> r.field("publishTime").gte("2023-01-01")))
// 条件3:标签是"技术"或"教程"(filter)
.filter(f2 -> f2.terms(t -> t.field("tags").values("技术", "教程")))
// 条件4:阅读量>1000(filter)
.filter(f3 -> f3.range(r -> r.field("readCount").gt(1000)))
// 条件5:排除作者为"测试账号"(must_not)
.must_not(mn -> mn.term(t -> t.field("author").value("测试账号")))
)
)
.size(10), // 返回10条结果
Blog.class
);
七、查询 DSL API 最佳实践
- 匹配字段类型与查询类型 :
- text 字段用
match/match_phrase(全文搜索); - keyword 字段用
term/terms/prefix(精确匹配); - 数值 / 日期字段用
range。
- text 字段用
- 优先使用
filter提升性能 :固定条件(如权限、状态)放入filter,利用 ES 的过滤缓存(filter cache)减少重复计算。 - 控制
should的匹配数量 :当bool中只有should时,默认返回所有文档(should不生效),需通过.minimumShouldMatch("1")指定至少满足 1 个条件。 - 避免过度复杂的查询 :嵌套过多
bool查询会降低性能,尽量拆分或简化条件。 - 测试查询效果 :用 Kibana 的 Dev Tools 验证查询 JSON(Java API 可通过
.toString()打印生成的 JSON),再移植到代码中。
总结
Elasticsearch 查询 DSL API 的核心是通过组合不同查询类型,精准描述搜索需求 。零基础用户可从match(全文)、term(精确)、bool(组合)入手,逐步掌握范围查询、模糊查询等高级类型;进阶用户需理解评分机制、过滤缓存等原理,优化查询性能。
无论是简单的关键词搜索,还是复杂的多条件筛选,灵活运用查询 DSL API 都能实现高效、精准的搜索功能。