ElasticSearch-5-搜索相关性与自定义评分

 

ElasticSearch-5-搜索相关性与自定义评分

 

 

1、简介

Elasticsearch 的核心功能是全文检索,而评分就是衡量查询条件与文档匹配相关度的机制得分越高,代表文档与搜索词越相关,排名就越靠前。

核心要点:

  1. 基于 TF-IDF / BM25 算法:

    • TF(词频):一个查询词在某个文档中出现的次数越多,该文档的相关性得分可能越高。

    • IDF(逆文档频率):一个查询词在所有文档中出现的频率。如果某个词在所有文档中都很常见(如“的”、“是”),它的区分度就低,权重就小;反之,一个稀有词权重就高。

    • BM25:是 TF-IDF 的现代优化版本,ES 从 5.x 版本开始默认使用。它改进了长文档的权重处理,避免了长文档天然占优的问题,效果通常更好。

  2. 评分过程:

    • 当你执行一个查询时,ES 会为每个匹配的文档计算一个 _score

    • 这个分数是一个正浮点数,是相对值,仅在同一查询的上下文中有意义。

    • 你可以通过 explain=true API 查看详细的评分计算过程,了解每个因子是如何贡献的。

Elasticsearch自定义评分的主要作用如下:
  • 1) 排序偏好:通过在搜索结果中给每个文档自定义评分,可以更好地满足搜索用户的排序偏好。
  • 2) 特殊字段权重:通过给特定字段赋予更高的权重,可以让这些字段对搜索结果的影响更大。
  • 3) 业务逻辑需求:根据业务需求,可以定义复杂的评分逻辑,使搜索结果更符合业务需求。

自定义评分广泛应用于需要复杂业务排序逻辑的场景,而不仅仅是文本相关性。例如:

  1. 电商搜索:

    • 综合排序:相关性 + 销量权重 + 好评率 + 店铺等级 + 是否促销。

    • 个性化:结合用户历史行为(如常购品牌、价格区间)提升相关商品排名。

    • 运营需求:对赞助商品、新品、自营商品进行加权。

  2. 本地服务/OTA(在线旅游):

    • 地理位置:酒店、餐厅的搜索,距离是极其重要的因素(使用衰减函数)。

    • 多维排序:餐厅搜索 = 关键词匹配 + 距离衰减 + 评分 + 人均价格偏好 + 是否营业中。

 

2、以下是几种主要的自定义评分策略:

  • Index Boost: 在索引层面修改相关性。
  • boosting: 修改文档相关性。
  • negative_boost: 降低相关性。
  • function_score: 自定义评分。
  • rescore_query:查询后二次打分。

 

1、Index Boost: 在索引层面修改相关性
Index Boost这种方式能在跨多个索引搜索时为每个索引配置不同的级别。所以它适用于索引级别调整评分。
实战举例:一批数据里有不同的标签,数据结构一致,要将不同的标签存储到不同的索引(A、B、C),并严格按照标签来分类展示(先展示A类,然后展示B类,最后展示C类)​,应该用什么方式查询呢?
具体实现如下。借助 indices_boost 提升索引的权重,让A排在最前,其次是B,最后是C。
POST my_index_*/_search
{
  "query": {
    "term": {
      "field.keyword": {
        "value": "queryValue"
      }
    }
  },
  "indices_boost": [
    {
      "my_index_100a": 1.5
    },
    {
      "my_index_100b": 1.2
    },
    {
      "my_index_100c": 1
    }
  ]
}

 

2、boosting: 修改文档相关性

给某些文档或查询条件一个权重因子,以提升或降低其重要性。

  • 若boosting值为0~1,如0.2,代表降低评分。
  • 若boosting值>1,如1.5,则代表提升评分。

实现方式:

  • bool查询中使用boost参数。
  • function_score中使用weight函数。
GET /blogs/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "title": {
              "query": "apple,ipad",
              "boost": 4
            }
          }
        },
        {
          "match": {
            "content": {
              "query": "apple,ipad",
              "boost": 1
            }
          }
        }
      ]
    }
  }
}

 

3、negative_boost: 降低相关性

若对某些返回结果不满意,但又不想将其排除(must_not),则可以考虑采用negative_boost的方式。
原理说明如下:
  • negative_boost仅对查询中定义为negative的部分生效。
  • 计算评分时,不修改boosting positive部分评分,而negative部分的评分则乘以negative_boost的值。
  • negative_boost取值为0~1.0,如0.3。
{
  "query": {
    "boosting": {
      "positive": {
        "match": { "title": "apple" }
      },
      "negative": {
        "match": { "title": "pie" }
      },
      "negative_boost": 0.5
    }
  }
}
  • 匹配positive查询的文档会正常计算得分。

  • 同时匹配了negative查询的文档,其得分会在原始得分的基础上乘以negative_boost(即0.5),从而降低得分。

  • 只匹配negative而不匹配positive的文档不会出现在结果中(因为boosting查询要求必须匹配positive部分)。

 

4、function_score: 自定义评分

该方式精细化控制评分,以对搜索结果进行高度个性化的排序设置。适用于需进行复杂查询的自定义评分业务场景。function_score 的核心结构:
{
  "query": {
    "function_score": {
      "query": { "match_all": {} }, // 原始查询,得到基础文档集和原始 _score
      "functions": [                // 一个或多个“算分函数”
        {
          "filter": { ... },        // (可选)指定一个过滤器,仅对匹配的文档应用此函数
          "weight": 2,              // (常用函数)将原始分数乘以一个常数
          "field_value_factor": {   // (常用函数)用文档的某个数值字段值来影响分数
            "field": "popularity",
            "factor": 1.2,
            "modifier": "log1p"
          },
          "random_score": { ... },  // 随机函数,用于随机排序
          "script_score": {         // (最强大)自定义脚本完全控制分数计算
            "source": "_score * doc['boost_field'].value"
          },
          "exp": { ... },           // 衰减函数,如高斯衰减,用于距离、时间等
          "gauss": { ... },
          "linear": { ... }
        }
      ],
      "score_mode": "sum",          // 多个函数分数如何合并?sum, multiply, avg, max, min...
      "boost_mode": "multiply"      // 函数合并后的分数如何与原始 _score 结合?multiply, replace, sum...
    }
  }
}

常用自定义评分方法示例:

 1、加权(weight):简单粗暴,给某些文档直接乘个系数。

{
  "function_score": {
    "query": { "match": { "title": "手机" } },
    "functions": [
      {
        "filter": { "term": { "brand": "苹果" } },
        "weight": 1.5
      }
    ],
    "score_mode": "sum",
    "boost_mode": "multiply"
  }
}

2、字段值因子(field_value_factor):用文档的数值字段(如销量、热度)来提升分数。

{
  "function_score": {
    "query": { "match": { "content": "编程" } },
    "functions": [
      {
        "field_value_factor": {
          "field": "view_count", // 使用 view_count 字段
          "factor": 0.1,         // 原始字段值先乘这个因子
          "modifier": "log1p",   // 然后进行 log(1 + value) 运算,防止高分值字段影响过大
          "missing": 1           // 如果字段缺失,默认值
        }
      }
    ],
    "boost_mode": "sum"          // 原始分 + 处理后的 view_count 值
  }
}

3、脚本评分(script_score):终极武器,用 Painless 脚本编写任意复杂度的逻辑

{
  "function_score": {
    "query": { "match": { "title": "酒店" } },
    "functions": [
      {
        "script_score": {
          "source": """
            double score = _score;
            // 根据地理距离减分
            if (doc['location'].size() > 0) {
              double distance = geoDistance(params.lat, params.lon, doc['location'].lat, doc['location'].lon);
              score *= 1 / (distance + 1); // 距离越远,分数越低
            }
            // 根据价格加分(越便宜越好)
            score += (100 - doc['price'].value) * 0.01;
            // 根据用户偏好标签加分
            for (String tag : params.user_preferred_tags) {
              if (doc['tags'].contains(tag)) {
                score += 10;
              }
            }
            return score;
          """,
          "params": {
            "lat": 39.909,
            "lon": 116.397,
            "user_preferred_tags": ["免费WiFi", "游泳池"]
          }
        }
      }
    ],
    "boost_mode": "replace"      // 完全使用脚本计算的分数
  }
}

 

 

 

 

 

 

 

posted @ 2026-01-07 11:47  邓维-java  阅读(2)  评论(0)    收藏  举报