ES bool查询中must与filter的区别

小豆丁 2月前 ⋅ 89 阅读
聊天记录
头像

ES的must 和 filter

头像

在 Elasticsearch 的 bool 查询中,mustfilter 是两种常用的子句,核心区别在于是否影响文档的相关性评分(_score)

核心答案:

  • must:子句中的条件必须匹配,并且匹配的文档会参与相关性评分计算。用于全文搜索等需要排序的场景。
  • filter:子句中的条件必须匹配,但匹配的文档不会影响评分(_score 默认为 0 或保持不变)。用于精确过滤,性能通常更好。

详细说明与对比:

特性 must filter
核心目的 匹配并计算相关性,用于搜索 精确过滤,不关心相关性,用于筛选
是否影响评分 。匹配程度会影响文档的最终 _score 。不参与评分计算,所有匹配文档在评分上等效。
典型用例 全文检索:“title:苹果手机” 结构化数据过滤:“status:published”, “price:[1000 TO 2000]”, “date:>=2024-01-01”
性能 相对较慢,因为需要计算评分。 性能更优。ES会尝试缓存filter子句的结果,对重复过滤提速明显。
结果排序 返回的文档按 _score 降序排列(最相关在前)。 不影响默认排序(按 _score 时,所有匹配的filter文档得分相同)。

使用建议:

  1. 需要按相关性排序(如搜索框关键词查询):使用 must
  2. 只需按条件筛选(如分类、价格区间、状态过滤):总是优先使用 filter,以获得最佳性能。
  3. 混合使用:一个 bool 查询中可以同时包含 mustfilter,这是非常常见且高效的实践。must 负责核心搜索逻辑,filter 负责高效过滤。

简单示例:

JSON
{
  "query": {
    "bool": {
      "must": [
        { "match": { "title": "手机" } } // 搜索标题包含“手机”的文档,并计算评分
      ],
      "filter": [
        { "term": { "brand": "苹果" } }, // 过滤品牌为“苹果”的文档,不参与评分
        { "range": { "price": { "gte": 5000 } } } // 过滤价格>=5000的文档,不参与评分
      ]
    }
  }
}

这个查询的意思是:找出标题与“手机”相关,并且品牌是“苹果”,价格在5000以上的商品。其中相关性由 must 决定,而品牌和价格过滤由高效的 filter 完成。

头像

有了 must 那么sort还会生效吗?

头像

是的,sort 仍然会生效,而且 mustsort 是独立工作的两个部分。

核心关系

  1. must 决定文档是否匹配和相关性评分(_score
  2. sort 决定最终结果的排序方式

四种常见情况

1. 默认情况(不指定 sort

  • _score 降序排序
  • must 计算的 _score 直接影响排序
JSON
{
  "query": {
    "bool": {
      "must": [
        { "match": { "title": "手机" } }
      ]
    }
  }
  // 不指定sort,默认按 _score 降序排序
}

2. 指定按 _score 排序

  • 显式按相关性评分排序
  • 与默认情况效果相同
JSON
{
  "query": {
    "bool": {
      "must": [
        { "match": { "title": "手机" } }
      ]
    }
  },
  "sort": [
    { "_score": "desc" }  // 按must计算的_score排序
  ]
}

3. 按其他字段排序(忽略 _score

  • 按指定字段排序,_score 不再影响排序
  • must 仍然决定文档是否被匹配
JSON
{
  "query": {
    "bool": {
      "must": [
        { "match": { "title": "手机" } }
      ]
    }
  },
  "sort": [
    { "price": "asc" },   // 按价格升序,_score被忽略
    { "sales": "desc" }   // 第二排序字段
  ]
}

4. 混合排序(_score 和其他字段)

  • 可以同时考虑相关性和其他因素
JSON
{
  "query": {
    "bool": {
      "must": [
        { "match": { "title": "手机" } }
      ]
    }
  },
  "sort": [
    { "_score": "desc" },  // 首先按相关性
    { "rating": "desc" },  // 相关性相同时按评分
    { "sales": "desc" }    // 都相同时按销量
  ]
}

性能注意

  • sort 会消耗额外性能,尤其是在对未索引的字段排序时
  • ES 默认缓存 filter 但不缓存 must 的评分计算
  • 对大数据集排序时考虑使用 docvalue_fields 或优化映射

实际示例

JSON
{
  "query": {
    "bool": {
      "must": [
        { "match": { "name": "华为" } }  // 必须包含"华为",并计算_score
      ],
      "filter": [
        { "term": { "category": "手机" } }  // 必须是手机,不计算分数
      ]
    }
  },
  "sort": [
    { "in_stock": "desc" },  // 有库存的排前面
    { "_score": "desc" },    // 然后按相关性
    { "update_time": "desc" } // 最后按更新时间
  ],
  "from": 0,
  "size": 20
}

这个查询的含义是:

  1. 找出名称包含"华为"的手机
  2. 先按库存状态排序(有货的在前)
  3. 再按名称与"华为"的相关性排序
  4. 最后按更新时间排序

总结

场景 must 的作用 sort 的作用 最终排序依据
不指定 sort 计算 _score 默认按 _score 降序 _score
指定按 _score 排序 计算 _score _score 排序 _score
指定按字段排序 计算 _score 按指定字段排序 指定字段
混合排序 计算 _score 多字段组合排序 多个字段优先级

简单记住: must 决定"找什么",sort 决定"怎么排"。它们相互独立,但可以协同工作。

头像

相关性评分怎么算的

头像

这是一个很好的核心问题。Elasticsearch的相关性评分(Relevance Scoring)是一个复杂的计算过程,其目标是判断一个文档与查询语句的匹配程度

核心答案: Elasticsearch 5.0 之后,默认的评分算法是 BM25(Best Matching 25),它取代了旧的 TF-IDF 算法。评分是一个综合结果,受词频、逆文档频率、字段长度、查询结构等多重因素影响。


评分核心要素(基于BM25算法)

你可以把评分想象成由以下几个核心部分共同决定:

1. 词频 - Term Frequency (TF)

  • 是什么:查询词在单个文档的某个字段中出现的次数。
  • 基本逻辑:一个词在文档中出现的次数越多,该文档与该词的相关性可能就越高。
  • BM25的优化:BM25 对词频进行了“饱和”处理,即词频增长对评分的提升会有一个上限,防止某个词重复出现无数次导致分数不合理地高。

2. 逆文档频率 - Inverse Document Frequency (IDF)

  • 是什么:查询词在整个索引的所有文档中出现的普遍程度。
  • 基本逻辑:一个词越常见(如“的”、“是”),其区分度越低,重要性越低;一个词越罕见,其区分度越高,重要性越高。
  • 计算公式(概念)IDF = log(1 + (文档总数 - 包含该词的文档数 + 0.5) / (包含该词的文档数 + 0.5))
    • 词很常见(出现在很多文档中)→ IDF值低 → 对总分贡献小。
    • 词很罕见(出现在很少文档中)→ IDF值高 → 对总分贡献大。

3. 字段长度归一化 - Field-length Normalization

  • 是什么:考虑文档中对应字段的长度。
  • 基本逻辑:同一个词,出现在较短的字段(如title)中,通常比出现在很长的字段(如content)中更重要。BM25 会惩罚长字段,奖励短字段。
  • 例如:搜索“苹果”,在标题“苹果手机”中出现,比在一篇几千字文章中只提到一次“苹果”,相关性要高得多。

4. 查询归一化与协调因子

  • 查询归一化:将上述分数归一化到一个合理的范围内。
  • 协调因子 - Coordination Factor:在布尔查询(如bool中有多个should子句)中,匹配的子句越多,最终分数通常会越高。

5. 权重提升 - Boost

  • 你可以在查询中手动干预评分:
    • 字段提升{ “match”: { “title^3”: “苹果” } }title字段的权重是普通的3倍)
    • 查询子句提升:在bool查询的should子句中设置boost参数。

如何查看和理解评分?

这是最关键的一步。你可以使用 explain: true API 来查看某个文档为什么得到这个分数

示例请求:

BASH
GET /your_index/_search
{
  "explain": true,
  "query": {
    "match": { "title": "苹果手机" }
  }
}

返回结果(简化示例): 你会看到一个非常详细的树状结构,包含:

"explanation": {
  "value": 1.6931472, // 最终得分
  "description": "weight(title:苹果 in 101) [PerFieldSimilarity], result of:", // 描述
  "details": [
    {
      "value": 1.6931472,
      "description": "score(freq=1.0), computed as boost * idf * tf from:", // 告知由 boost, idf, tf 计算而来
      "details": [
        {
          "value": 2.2,
          "description": "idf, computed as log(1 + (N - n + 0.5) / (n + 0.5)) from:", // IDF计算详情
          "details": [...]
        },
        {
          "value": 0.625,
          "description": "tf, computed as freq / (freq + k1 * (1 - b + b * dl / avgdl)) from:", // TF计算详情 (BM25公式!)
          "details": [...]
        }
      ]
    }
  ]
}

通过 explain API,你可以清晰地看到 idftf 的具体数值和计算过程,这是调试搜索相关性问题最强大的工具。


总结与类比

因素 类比 对评分的影响
词频 (TF) 在简历中反复强调“精通Java” 在单份文档中出现次数越多,分数越高(但有上限)。
逆文档频率 (IDF) “精通Java” vs “具有团队精神” 越独特、越罕见的词,权重越高。
字段长度 (Norm) 在“技能专长”栏 vs 在“个人评价”段落里提到 在更短的、更重要的字段中出现,分数更高。
查询结构 同时满足“Java”和“Spring” vs 只满足一个 匹配的查询条件越多,分数通常越高。
权重提升 (Boost) 给“工作经历”栏目加粗标红 手动提高某个字段或条件的重要性。

最终,相关性评分 = BM25(TF, IDF, 字段长度) + 查询结构因子 + 权重提升 + 其他归一化。

给你的建议:

  1. 理解概念:掌握 TF、IDF、字段长度这三个核心思想。
  2. 善用工具:遇到排序不符合预期时,第一时间使用 explain: true 分析具体文档的得分细节。
  3. 不要过度纠结绝对分值:评分主要用于相对排序(哪个文档更相关),其绝对值大小没有统一标准,不同查询之间无法直接比较总分。

全部评论: 0

    我有话说: