Elasticsearch 中 query 和 filter 有什么区别?
简化版
query context 会判断文档是否匹配,并计算 _score 相关性分数;filter context 只判断是否匹配,不计算相关性,适合状态、时间范围、权限、分类这类精确过滤条件。面试里可以概括为:query 负责“有多相关”,filter 负责“要不要这条”。
详细版
Elasticsearch Query DSL 里,同一个条件放在不同上下文中,语义和性能特点不同。
query context:
- 需要计算
_score; - 适合全文检索和相关性排序;
- 常见查询:
match、multi_match、match_phrase; - 例如搜索“Java 集合”,希望更相关的文章排前面。
filter context:
- 只返回 true/false;
- 不计算相关性分数;
- 适合精确过滤;
- 经常可以被缓存;
- 常见条件:状态、租户、权限、时间范围、枚举值。
典型写法是 bool 查询:
GET /articles/_search
{
"query": {
"bool": {
"must": [
{ "match": { "title": "Java 集合" } }
],
"filter": [
{ "term": { "status": "published" } },
{ "range": { "created_at": { "gte": "now-30d" } } }
]
}
}
}
这里 title 用 query 参与评分,status 和 created_at 用 filter 缩小范围。
完整版教学
一、为什么要区分 query 和 filter
搜索系统里有两类问题:
第一类是“这篇文章和关键词有多相关”:
用户搜:Java 并发
文章 A:Java 并发编程面试题
文章 B:Java 基础知识
这种问题需要评分,文章 A 应该排在前面。
第二类是“这篇文章是否满足硬条件”:
status = published
tenant_id = 1001
created_at >= now-30d
这种条件没有“更相关”的概念,只需要判断能不能进入结果集。把两类问题分开,查询语义会更清晰,性能也更容易优化。
二、query context 关注相关性
query context 会计算 _score。例如:
{
"query": {
"match": {
"content": "Elasticsearch 倒排索引"
}
}
}
这里 match 查询会分析用户输入,匹配倒排索引,并根据相关性算法计算每个文档的 _score。默认情况下,搜索结果会按 _score 从高到低排序。
query context 适合:
- 搜索标题、正文、评论;
- 多字段权重匹配;
- 短语匹配;
- 需要用户感知“排序更准确”的场景。
三、filter context 关注硬过滤
filter context 不计算 _score,只判断是否匹配。典型写法:
{
"query": {
"bool": {
"filter": [
{ "term": { "category": "java" } },
{ "range": { "price": { "lte": 100 } } }
]
}
}
}
这些条件不需要排名:
- 分类等于 Java;
- 价格小于 100;
- 是否已发布;
- 用户是否有权限看;
- 时间是否在最近 7 天。
把它们放到 filter 中,可以避免无意义评分,也让 Elasticsearch 有机会复用过滤结果。
四、bool 查询里各子句怎么理解
bool 查询是面试最高频入口之一:
| 子句 | 语义 | 是否影响评分 |
|---|---|---|
| must | 必须匹配 | 通常影响 |
| should | 可选匹配,可提升相关性 | 通常影响 |
| filter | 必须匹配 | 不影响 |
| must_not | 必须不匹配 | 不影响 |
例如电商搜索:
{
"query": {
"bool": {
"must": [
{ "match": { "name": "无线耳机" } }
],
"filter": [
{ "term": { "brand": "sony" } },
{ "range": { "price": { "gte": 300, "lte": 1500 } } }
],
"must_not": [
{ "term": { "status": "deleted" } }
]
}
}
}
用户搜索词走 must,品牌和价格走 filter,已删除商品走 must_not。
五、常见错误
常见错误一:把精确条件全放进 must,导致每个条件都参与评分,结果评分含义混乱。
常见错误二:对 text 字段使用 term 查询。term 不分析查询词,适合 keyword、数值、布尔等精确值;全文字段通常用 match。
常见错误三:认为 filter 一定比 query 快。更准确的说法是 filter 不评分、适合缓存,但最终性能还取决于字段类型、基数、数据分布、缓存命中和查询组合。
六、常见误区与追问
| 条件 | 建议上下文 | 原因 |
|---|---|---|
match title: "Java 并发" | query | 需要相关性评分和排序 |
term status: "published" | filter | 只是硬条件,不需要评分 |
range created_at >= now-7d | filter | 时间窗口过滤,适合缩小结果集 |
should title^3 summary^2 | query | 用字段权重影响 _score |
query: 这篇文档和“无线耳机”有多相关?
filter: 这篇文档是不是 sony,价格是不是 300~1500?
易错点:filter 不参与
_score,但“不评分”不等于任何 filter 都一定快,字段基数和数据分布仍然重要。
举个面试场景:搜索 Java 集合 时,标题和正文匹配应放 query;status=published、tenant_id=42、created_at>=now-30d 应放 filter。这样 _score 只表达内容相关性,不会被租户、状态、时间这些硬条件污染。
- 误区:所有条件都放
must更统一。 精确过滤条件放must会参与评分或影响评分语义,通常应放filter。 - 误区:filter 一定会被缓存。 ES 会根据查询频率、成本、segment 等因素决定是否值得缓存,不能把缓存当成必然结果。
- 误区:
term和match只是写法不同。term不分析查询词,适合 keyword、数字、布尔;match会分析文本,适合全文字段。 - 追问:
must_not是否影响评分?must_not处于过滤语义,只排除文档,不贡献_score。 - 追问:为什么权限条件适合 filter? 权限是能不能看,不是相关不相关,放 filter 能保持评分只反映内容匹配程度。
- 追问:排序字段显式指定后
_score还有用吗? 如果完全按时间或价格排序,_score可能不再作为主排序,但 query/filter 的语义区分仍影响匹配集合和计算成本。
七、加强记忆
记住“query 管相关性,filter 管硬条件”。全文检索、排序靠 query;状态、时间、权限、枚举过滤放 filter;bool 查询是把两者组合起来的标准姿势。