相关性常用策略

相关性常用策略 #

本页以"配方"的形式给出几个常见场景下的相关性策略,帮助你在默认评分的基础上做业务级调优。

通用配方:标题 + 正文 + 标签 #

适用:大多数文档检索(文章、博客、知识库等)。

思路:

  • 使用 multi_match 将多个字段一起搜索
  • 提高标题权重,其次是标签,再是正文

示意:

{
  "query": {
    "multi_match": {
      "query": "搜索 引擎",
      "fields": [
        "title^3",
        "tags^2",
        "content"
      ]
    }
  }
}

要点:

  • 确保字段类型合理(title/content 为 text,tags 可为 keyword 或 text+keyword)
  • 对排序特别重要的字段,优先在 Mapping 里设计好 multi-fields

电商配方:匹配 + 业务信号 #

适用:商品搜索。

核心元素:

  • 文本相关性:标题、品牌、类目、卖点等字段
  • 业务信号:销量、点击率、转化率、上下架状态、库存

常见做法:

  • 先通过 bool + multi_match 保证文本相关性
  • 再通过字段 boost 或 function_score 将业务信号以"加分项"的形式叠加进来

注意:

  • 下架/无库存商品要在 filter 层过滤掉,而不是靠"权重降低"
  • 对业务信号做归一化(例如映射到 0–1 或有限区间)以避免极端值影响整体排序

示例:按受欢迎度提升权重

设想有个网站供用户发布博客并且可以让他们为自己喜欢的博客点赞,我们希望将更受欢迎的博客放在搜索结果列表中相对较上的位置,同时全文搜索的评分仍然作为相关度的主要排序依据:

GET /blogposts/_search
{
  "query": {
    "function_score": {
      "query": {
        "multi_match": {
          "query":    "popularity",
          "fields": [ "title", "content" ]
        }
      },
      "field_value_factor": {
        "field":    "votes",
        "modifier": "log1p",
        "factor":   0.1
      },
      "boost_mode": "sum"
    }
  }
}

日志配方:时间权重 + 精确过滤 #

适用:日志/监控类场景。

思路:

  • 结构化过滤先行:时间范围、服务名、环境、主机等
  • 全文仅对确实有助于定位问题的字段(如 message)做匹配
  • 对时间施加一定权重(越近的日志权重更高)

实践建议:

  • 避免在大范围时间上直接做全文搜素,优先限定最近一段时间
  • 按相关性和时间做综合排序:先看"相关且最近"的日志

地理位置配方:距离衰减 #

适用:需要根据地理位置排序的场景(如度假屋、餐厅、商店等)。

思路:

  • 使用 function_score 查询结合衰减函数(gaussexplinear
  • 根据距离原点的远近调整评分

示例:

GET /_search
{
  "query": {
    "function_score": {
      "functions": [
        {
          "gauss": {
            "location": {
              "origin": { "lat": 51.5, "lon": 0.12 },
              "offset": "2km",
              "scale":  "3km"
            }
          }
        },
        {
          "gauss": {
            "price": {
              "origin": "50",
              "offset": "50",
              "scale":  "20"
            }
          },
          "weight": 2
        }
      ]
    }
  }
}

location 语句可以简单理解为:

  • 以伦敦市中作为原点 origin
  • 所有距原点 origin 2km 范围内的位置的评分是 1.0
  • 距中心 5kmoffset + scale)的位置的评分是 0.5

过滤集提升权重 #

回到忽略 TF/IDF 里处理过的问题,我们希望根据每个度假屋的特性数量来评分,当时我们希望能用缓存的过滤器来影响评分,现在 function_score 查询正好可以完成这件事情。

到目前为止,我们展现的都是为所有文档应用单个函数的使用方式,现在会用过滤器将结果划分为多个子集(每个特性一个过滤器),并为每个子集使用不同的函数。

在下面例子中,我们会使用 weight 函数,它与 boost 参数类似可以用于任何查询。有一点区别是 weight 没有被 Lucene 归一化成难以理解的浮点数,而是直接被应用。

查询的结构需要做相应变更以整合多个函数:

GET /_search
{
  "query": {
    "function_score": {
      "filter": {
        "term": { "city": "Barcelona" }
      },
      "functions": [
        {
          "filter": { "term": { "features": "wifi" }},
          "weight": 1
        },
        {
          "filter": { "term": { "features": "garden" }},
          "weight": 1
        },
        {
          "filter": { "term": { "features": "pool" }},
          "weight": 2
        }
      ],
      "score_mode": "sum"
    }
  }
}

function_score 查询有个 filter 过滤器而不是 query 查询。functions 关键字存储着一个将被应用的函数列表。函数会被应用于和 filter 过滤器(可选的)匹配的文档。pool 比其他特性更重要,所以它有更高 weightscore_mode 指定各个函数的值进行组合运算的方式。

评分模式 score_mode #

每个函数返回一个结果,所以需要一种将多个结果缩减到单个值的方式,然后才能将其与原始评分 _score 合并。评分模式 score_mode 参数正好扮演这样的角色,它接受以下值:

  • multiply:函数结果求积(默认)
  • sum:函数结果求和
  • avg:函数结果的平均值
  • max:函数结果的最大值
  • min:函数结果的最小值
  • first:使用首个函数(可以有过滤器,也可能没有)的结果作为最终结果

在本例中,我们将每个过滤器匹配结果的权重 weight 求和,并将其作为最终评分结果,所以会使用 sum 评分模式。

不与任何过滤器匹配的文档会保有其原始评分,_score 值的为 1

站内搜索配方:引入用户反馈 #

适用:内容站点、文档中心、社区问答。

思路:

  • 基础检索同"通用配方"
  • 持续采集用户行为信号:
    • 某次查询下被点击的结果
    • 停留时长、跳出情况
  • 将这些信号累积成"内容质量分数",在排序中作为一项独立的加权因子

要点:

  • 行为信号需要做时间衰减(近期行为权重更高)
  • 适合与 AB 实验结合评估效果

调整与验证 #

无论哪种配方,都建议:

  • 先在小范围或灰度环境中验证改动
  • 通过 AB 实验、标注样本或业务方评审,观察结果质量是否有明显提升或退化
  • 将相关性策略版本化管理(记录参数与规则集),便于回滚与持续迭代

这些配方并非"银弹",但可以作为你为不同业务场景设计相关性策略的起点。

小结 #

  • 不同场景需要不同的相关性策略
  • 通用配方适合大多数文档检索场景
  • 电商场景需要结合业务信号
  • 日志场景需要时间权重和精确过滤
  • 地理位置场景可以使用距离衰减函数
  • 过滤集提升权重适合多条件评分场景
  • 监控用户行为是评价搜索结果质量的关键

下一步可以继续阅读: