5月27日 23:48

Elasticsearch 的 bool 查询如何组合多个查询条件?

Elasticsearch 的 bool 查询是日常开发中使用频率最高的复合查询,它通过四个子句——must、should、must_not、filter——实现 AND/OR/NOT 逻辑组合。面试中,能否讲清这四个子句的区别、各自对相关性评分的影响,以及 filter 上下文的性能优势,是考察重点。

四个子句各自做什么

bool 查询的四个子句分别对应不同的逻辑角色:

  • must:文档必须匹配,等价于逻辑 AND,参与相关性评分
  • should:文档匹配任意一个即可,等价于逻辑 OR,参与相关性评分
  • must_not:文档必须不匹配,等价于逻辑 NOT,不参与评分(属于 filter 上下文)
  • filter:文档必须匹配,但不参与评分,仅做过滤(属于 filter 上下文)

这里有一个容易混淆的点:must 和 filter 在逻辑上都是 AND 语义,区别在于 must 参与评分,filter 不参与。这意味着当你只关心"是否匹配"而不关心"匹配得好不好"时,应该用 filter。

Query Context 与 Filter Context

理解 bool 查询的关键在于区分两种上下文:

  • Query Context(查询上下文):must 和 should 处于此上下文,会计算相关性评分(_score),回答"匹配得有多好"
  • Filter Context(过滤上下文):filter 和 must_not 处于此上下文,不计算评分,回答"是否匹配",且结果会被缓存以提升后续查询性能

这也是为什么面试中常问"filter 和 must 有什么区别"——本质是评分 vs 不评分、缓存 vs 不缓存的区别。

minimum_should_match 的行为

should 子句有一个关键参数 minimum_should_match,它决定了至少需要匹配几个 should 条件:

  • 当 bool 中没有 must 或 filter 时,默认值为 1,即至少匹配一个 should 条件
  • 当 bool 中存在 must 或 filter 时,默认值为 0,即 should 条件完全可选,仅用于提升评分

这个默认值的变化是面试高频考点。如果不知道这个规则,查询结果可能和预期不一致。

json
{ "query": { "bool": { "must": [ { "match": { "title": "手机" } } ], "should": [ { "term": { "brand": "华为" } }, { "term": { "brand": "小米" } } ] } } }

上面这个查询中,由于存在 must,should 默认不强制匹配。意味着"手机"关键词匹配即可,华为和小米只是加分项。如果要求必须匹配华为或小米之一,需要显式设置 "minimum_should_match": 1

组合示例

AND 逻辑:must

查询标题包含"手机"且价格低于 1000 的商品:

json
{ "query": { "bool": { "must": [ { "match": { "title": "手机" } }, { "range": { "price": { "lt": 1000 } } } ] } } }

OR 逻辑:should

查询标题包含"手机"或类别为"电子产品":

json
{ "query": { "bool": { "should": [ { "match": { "title": "手机" } }, { "term": { "category": "电子产品" } } ], "minimum_should_match": 1 } } }

NOT 逻辑:must_not

查询标题包含"手机"但排除品牌为"Apple":

json
{ "query": { "bool": { "must": [ { "match": { "title": "手机" } } ], "must_not": [ { "term": { "brand": "Apple" } } ] } } }

filter 优先的复合查询

实际开发中最常用的模式:全文检索用 must,精确过滤用 filter:

json
{ "query": { "bool": { "must": [ { "match": { "title": "手机" } } ], "filter": [ { "range": { "price": { "gte": 500, "lt": 1000 } } }, { "term": { "status": "在售" } } ], "must_not": [ { "term": { "brand": "Apple" } } ], "should": [ { "term": { "brand": "华为" } } ], "minimum_should_match": 0 } } }

这个查询的语义是:标题匹配"手机",价格在 500-1000 之间,状态为"在售",排除 Apple,华为品牌加分但不强制。

评分机制

bool 查询的评分遵循"匹配越多分数越高"的原则:

  • must 子句的评分会相加
  • should 子句的评分也会相加
  • filter 和 must_not 不影响评分
  • 最终 _score = must 评分之和 + should 评分之和

可以用 boost 参数调整单个查询的权重,比如让标题匹配的权重是内容匹配的 3 倍:

json
{ "match": { "title": { "query": "手机", "boost": 3 } } }

bool 嵌套

bool 查询可以嵌套使用,实现更复杂的逻辑。比如"查询标题包含手机或电脑,且价格低于 1000":

json
{ "query": { "bool": { "must": [ { "bool": { "should": [ { "match": { "title": "手机" } }, { "match": { "title": "电脑" } } ], "minimum_should_match": 1 } ], "filter": [ { "range": { "price": { "lt": 1000 } } } ] } } }

嵌套时内层 bool 的 minimum_should_match 规则同样适用:内层 bool 只有 should 没有 must/filter,所以默认 minimum_should_match 为 1。

面试常见追问

Q: filter 和 must 都是 AND 语义,什么时候用 filter? A: 当条件不需要影响排序(即不关心相关性评分)时用 filter。filter 上下文不计算评分,且结果会被 Elasticsearch 自动缓存,查询性能显著优于 must。典型场景:状态过滤、价格范围、日期区间等精确值过滤。

Q: should 在什么情况下是可选的? A: 当 bool 中同时存在 must 或 filter 子句时,should 默认不强制匹配(minimum_should_match 默认为 0),仅用于提升匹配文档的评分。如果需要强制匹配,显式设置 minimum_should_match。

Q: bool 查询性能优化有哪些手段? A: 三点:一是精确匹配条件放 filter 而非 must,利用缓存;二是避免 should 中放过多子句,每个子句都会计算评分;三是对 filter 中使用的字段确保映射类型正确(如 price 用数值类型而非 keyword),避免类型转换开销。

标签:ElasticSearch