Bool Query:ES 最强大的复合查询

22 May 2026 – wusfe · 3 min read

Bool Query:ES 最强大的复合查询

如果你只能从 ES 带走一种查询,带走 Bool Query。

它不只是一个"多层条件组合"的工具——它是 ES 几乎所有的复合查询的基石。ES 官方文档里大量查询底层都会转化成 Bool Query 执行。理解 Bool Query,就理解了 ES 搜索运转的核心逻辑。

四子句速览

Bool Query 有四个子句,每个子句接受一个查询数组:

{
  "query": {
    "bool": {
      "must":     [],   // 必须满足,参与评分(AND 逻辑)
      "must_not": [],   // 必须不满足(NOT 逻辑)
      "should":   [],   // 最好满足(OR 逻辑)——不强制,满足越多分越高
      "filter":   []    // 必须满足,不参与评分,走缓存
    }
  }
}

Must vs Filter 再强调一次

GET /movies/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "title": "复仇者联盟" } }
      ],
      "filter": [
        { "term":  { "genre": "科幻" } },
        { "range": { "year": { "gte": 2018 } } }
      ]
    }
  }
}

返回结果:标题匹配"复仇者联盟"的所有科幻片(2018 年后),按标题匹配度排序。

  • must 里的 match 产生 _score,决定排序
  • filter 里的 termrange 只是筛子,不算分,走缓存

Should 的两种模式

Should 的行为取决于是否有 must/filter 存在:

模式 1:没有 must/filter → 至少满足一个

{
  "bool": {
    "should": [
      { "term": { "genre": "科幻" } },
      { "term": { "genre": "悬疑" } }
    ]
  }
}

minimum_should_match 默认为 1——至少满足一个 should 条件。结果:科幻类或悬疑类的影片。

模式 2:有 must/filter → 不一定需要满足,但满足会加分

{
  "bool": {
    "must": [
      { "match": { "title": "星际" } }
    ],
    "should": [
      { "term": { "genre": "科幻" } },
      { "term": { "year": 2023 } }
    ]
  }
}

结果:标题必须匹配"星际",但如果是科幻片(genre=科幻)或者是 2023 年的,分数会更高,排在前面。这就是 should 的加分语义

手动控制 minimum_should_match

{
  "bool": {
    "should": [
      { "term": { "tag": "java" } },
      { "term": { "tag": "elasticsearch" } },
      { "term": { "tag": "性能优化" } }
    ],
    "minimum_should_match": 2
  }
}

至少匹配 3 个标签中的 2 个。百分比写法也支持:"minimum_should_match": "75%"

Must_not:排除是硬性的

{
  "bool": {
    "must": [
      { "match": { "title": "复仇者联盟" } }
    ],
    "must_not": [
      { "term": { "status": "deleted" } },
      { "range": { "score": { "lt": 3.0 } } }
    ]
  }
}

过滤掉已删除和评分低于 3 的。must_not 不参与评分,但同样走缓存(底层是 filter 的变体)。

多层嵌套:把 Bool Query 当乐高

{
  "bool": {
    "must": [
      { "match": { "title": "星际" } }
    ],
    "filter": [
      { "term": { "status": "published" } }
    ],
    "should": [
      { "term": { "genre": "科幻" } },
      {
        "bool": {  // 嵌套 bool!满足多个标签的高分
          "must": [
            { "term": { "tag": "高分" } },
            { "range": { "year": { "gte": 2022 } } }
          ]
        }
      }
    ],
    "minimum_should_match": 1
  }
}

这个查询的含义:

  1. 标题必须匹配"星际"(硬性,参与评分)
  2. 必须已发布(硬性,不评分)
  3. 最好满足以下之一(加分项):
    • 属于科幻类
    • 或者:既有"高分"标签,又是 2022 年以后的

每一层嵌套都是独立的小 Bool Query,有自己的规则。

实战:电商搜索的典型 Bool 写法

GET /products/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "multi_match": {
            "query": "iPhone 15",
            "fields": ["title^3", "brand^2", "description"],
            "type": "best_fields"
          }
        }
      ],
      "filter": [
        { "term":  { "status": "on_sale" } },
        { "range": { "price": { "gte": 5000, "lte": 15000 } } },
        { "term":  { "in_stock": true } }
      ],
      "must_not": [
        { "term": { "blocked": true } }
      ],
      "should": [
        { "term": { "is_official": true } },
        { "term": { "has_discount": true } }
      ]
    }
  }
}

这个查询做了:

  • 全文搜索商品标题(标题权重 3x)、品牌(2x)、描述
  • 过滤:在售 + 价格区间 + 有库存
  • 排除:被封禁的
  • 加分:官方店和有折扣的排在前面

这是真实生产环境里电商搜索的骨架模板。

查询被拒绝?可能是 Bool 嵌套太深

# 遇到这个错误:
"too_many_clauses: maxClauseCount is set to 1024"

Bool Query 的嵌套层数和子句数量有上限(indices.query.bool.max_clause_count,默认 1024)。如果你的 Bool Query 嵌套过多或者 terms 里的值过多,会触发这个限制。

解决:把大量 term 值换成 terms lookup(从另一个索引动态加载),或者将嵌套 Bool 重构成更扁平的结构。

总结

Bool Query 的四个子句各司其职:

must     → 必须 + 评分(全文搜索放这里)
must_not → 必须不(排除条件放这里)
should   → 最好有(加分项,结合 minimum_should_match 控制松紧)
filter   → 必须 + 不评分 + 走缓存(精确筛选放这里)

记住这个黄金公式:must 搜内容 + filter 筛条件 + should 调排序 + must_not 排除。 任何复杂的搜索需求,都可以用这四个子句拼出来。