1. 什么是Function Score Query

在Elasticsearch中,搜索结果的默认排序由相关性分数(_score)决定,该分数基于BM25算法计算得出。然而在实际业务场景中,我们常常需要根据业务需求调整相关度排序,这时候Function Score Query就显得尤为重要。

Function Score Query是Elasticsearch提供的一种强大的自定义评分工具,它允许我们在原始查询得分的基础上,通过应用一个或多个函数来修改文档的最终得分。这意味着我们可以基于文档的特定字段、属性或其他因素来定制排序逻辑,实现更加智能的搜索结果排序。

1.1 核心价值与应用场景

Function Score Query的核心价值在于它能够将业务逻辑融入搜索排序中。常见的应用场景包括:

  • 竞价排名:付费推广的文档获得更高排名
  • 个性化推荐:根据用户偏好提升特定内容的相关性
  • 时效性排序:新闻、促销等有时间敏感性的内容
  • 业务权重:考虑销量、评分、流行度等业务因素
  • 地理位置排序:附近优先的位置搜索

2. Function Score查询结构详解

2.1 基本查询格式

一个典型的Function Score查询包含以下核心组成部分:

{
  "query": {
    "function_score": {
      "query": { ... },              // 原始查询
      "functions": [ ... ],          // 函数列表
      "score_mode": "multiply",      // 函数间分数组合方式
      "boost_mode": "multiply",      // 原始分数与函数分数组合方式
      "max_boost": 42,               // 最大分数限制
      "min_score": 0.5               // 最小分数阈值
    }
  }
}

2.2 核心组件说明

  • query:定义原始查询条件,用于初步筛选文档并计算基础相关性分数。
  • functions:包含一个或多个评分函数,每个函数可以有自己的过滤器,确定函数应用的文档范围。
  • score_mode:指定多个函数分数之间的组合方式
  • boost_mode:定义原始查询分数与函数分数的组合方式
  • max_boost:限制最终得分的上限,防止某个文档因函数评分过高而永远排在前面。
  • min_score:设置分数阈值,只返回高于此值的文档。

3. 评分函数类型详解

3.1 Weight函数:静态权重

Weight函数是最简单的评分函数,它为匹配的文档应用一个静态权重乘数:

{
  "functions": [
    {
      "filter": { "term": { "brand": "小米" } },
      "weight": 10
    }
  ]
}

在这个例子中,所有品牌为"小米"的文档都会获得10倍的权重提升。Weight函数的好处是简单高效,适用于基于静态条件的权重调整。

3.2 Field Value Factor函数:字段值影响评分

Field Value Factor函数允许我们使用文档中字段的值来影响得分:

{
  "functions": [
    {
      "field_value_factor": {
        "field": "popularity",
        "factor": 1.2,
        "modifier": "sqrt",
        "missing": 1
      }
    }
  ]
}

这将转换为计算公式:sqrt(1.2 * doc['popularity'].value)

Field Value Factor参数说明:

参数 说明 默认值
field 要从文档中提取的字段名 必填
factor 与字段值相乘的系数 1
modifier 应用于字段值的修改器 none
missing 字段缺失时的默认值

Modifier修饰符选项:

修饰符 含义 适用场景
none 不使用修改器 原始字段值
log 取常用对数 压缩大数值范围
log1p 字段值+1后取对数 处理含0的数据
ln 取自然对数 数学计算
ln1p 字段值+1后取自然对数 处理含0的数据
square 平方 放大差异
sqrt 平方根 缩小差异
reciprocal 倒数 数值越小得分越高

3.3 Random Score函数:随机排序

Random Score函数为文档提供随机分数,可用于实现随机推荐A/B测试

{
  "functions": [
    {
      "random_score": {
        "seed": 42
      }
    }
  ]
}

通过设置不同的seed值,可以控制随机序列,确保相同的种子产生相同的随机顺序。

3.4 Script Score函数:完全自定义评分

当内置函数无法满足复杂业务需求时,Script Score函数提供了完全自定义评分的能力:

{
  "functions": [
    {
      "script_score": {
        "script": {
          "lang": "painless",
          "source": "_score * doc['read_count'].value / Math.pow(params.param1, params.param2)",
          "params": {
            "param1": 2,
            "param2": 0.5
          }
        }
      }
    }
  ]
}

Script Score函数支持Painless脚本语言,可以访问文档字段、当前分数_score,并接受外部参数。

3.5 衰减函数:基于距离的评分

衰减函数(Decay Functions)允许我们根据数值字段或地理位置与目标值的距离来衰减文档分数。Elasticsearch支持三种衰减函数:

高斯衰减(gauss):产生平滑的钟形曲线,适合大多数场景
指数衰减(exp):产生陡峭的衰减曲线,衰减更快
线性衰减(linear):直线衰减,简单直接

{
  "functions": [
    {
      "gauss": {
        "location": {
          "origin": { "lat": 40.0, "lon": 116.0 },
          "scale": "10km",
          "offset": "1km",
          "decay": 0.5
        }
      }
    }
  ]
}

衰减函数参数说明:

  • origin:理想值点,距离为0时得分最高
  • scale:衰减速率,值越大衰减越慢
  • offset:从原点开始的不衰减区域
  • decay:在scale + offset距离处的得分比例

4. 分数组合模式详解

4.1 Score Mode:多函数分数组合

当有多个评分函数时,score_mode参数决定如何组合各个函数的分数:

模式 说明 适用场景
multiply 函数结果求积 默认行为,函数间相互增强/减弱
sum 函数结果求和 累加各函数的贡献
avg 函数结果平均值 平衡多个函数的影响
max 取函数结果最大值 仅关注最重要的函数
min 取函数结果最小值 保守策略,取最低影响
first 使用首个匹配函数的结果 优先级明确的场景

4.2 Boost Mode:与原始查询分数组合

boost_mode参数定义函数评分如何与原始查询评分组合:

模式 说明 公式
multiply 查询得分与函数得分相乘 final_score = query_score * function_score
replace 仅使用函数得分,忽略查询得分 final_score = function_score
sum 查询得分与函数得分相加 final_score = query_score + function_score
avg 查询得分与函数得分取平均值 final_score = (query_score + function_score) / 2
max 取查询得分和函数得分的最大值 final_score = max(query_score, function_score)
min 取查询得分和函数得分的最小值 final_score = min(query_score, function_score)

5. 实战应用案例

5.1 案例一:新闻搜索综合排序

假设我们有一个新闻系统,需要根据相关性、时效性和热度进行综合排序:

{
  "query": {
    "function_score": {
      "query": {
        "match": {
          "title": "台风"
        }
      },
      "functions": [
        {
          "filter": { "range": { "pub_time": { "gte": "now-7d/d" } } },
          "weight": 2
        },
        {
          "field_value_factor": {
            "field": "read_count",
            "modifier": "log1p",
            "factor": 0.1
          }
        },
        {
          "gauss": {
            "pub_time": {
              "origin": "now",
              "scale": "7d",
              "offset": "1d",
              "decay": 0.5
            }
          }
        }
      ],
      "score_mode": "sum",
      "boost_mode": "multiply",
      "max_boost": 3.0
    }
  }
}

这个查询实现了:

  • 基础相关性:匹配"台风"关键词
  • 时效性提升:近7天发布的新闻权重×2
  • 热度考虑:阅读数影响评分(使用log1p压缩数值范围)
  • 时间衰减:使用高斯函数平滑衰减过去新闻的得分

5.2 案例二:电商商品竞价排名

在电商场景中,需要平衡相关性和广告投放:

{
  "query": {
    "function_score": {
      "query": {
        "match": {
          "name": "手机"
        }
      },
      "functions": [
        {
          "filter": { "term": { "brand": "小米" } },
          "weight": 10
        },
        {
          "filter": { "term": { "is_sponsored": true } },
          "weight": 5
        },
        {
          "field_value_factor": {
            "field": "sales_volume",
            "modifier": "sqrt",
            "factor": 0.5
          }
        }
      ],
      "score_mode": "sum",
      "boost_mode": "multiply"
    }
  }
}

这个案例中,我们为小米品牌和广告商品设置了更高的权重,同时考虑销量因素,实现了业务价值与相关性的平衡

5.3 案例三:按类型优先级排序

在某些情况下,我们需要按文档类型设置优先级:

{
  "query": {
    "function_score": {
      "query": {
        "wildcard": {
          "term": {
            "value": "*flu*"
          }
        }
      },
      "functions": [
        {
          "filter": {
            "term": {
              "type": "procedure"
            }
          },
          "weight": 2
        },
        {
          "filter": {
            "term": {
              "type": "drug"
            }
          },
          "weight": 1
        }
      ],
      "score_mode": "sum",
      "boost_mode": "replace"
    }
  }
}

这个例子中,我们将类型为"procedure"的文档权重设为2,"drug"类型权重设为1,使用replace模式完全依赖函数评分进行排序。

6. 性能优化与最佳实践

6.1 性能考虑

  1. 过滤器顺序:将选择性高的过滤器放在前面,减少后续函数需要处理的文档数量
  2. 函数复杂度:Script Score函数最昂贵,Weight函数最轻量
  3. 缓存利用:使用过滤器可以利用Elasticsearch的查询缓存
  4. 分页限制:避免深度分页,考虑使用search_after参数

6.2 最佳实践

  1. 明确业务需求:在设计评分策略前,明确排序的业务目标
  2. 适度使用:不要过度复杂化评分逻辑,保持可维护性
  3. 测试验证:通过实际查询验证评分效果,确保符合预期
  4. 参数调优:根据数据分布调整函数参数,特别是衰减函数的scale和decay
  5. 监控分析:定期分析查询性能,确保评分函数不会成为瓶颈

7. 总结

Function Score Query是Elasticsearch中极其强大且灵活的功能,它打破了传统相关性排序的限制,让开发者能够将业务逻辑融入搜索排序中。通过合理使用各种评分函数和组合模式,我们可以实现更加智能、符合业务需求的搜索体验。

无论是简单的权重提升、复杂的字段值计算,还是基于地理位置或时间的衰减排序,Function Score Query都能提供有效的解决方案。掌握这一功能,将极大提升你构建高质量搜索应用的能力。

关键要点回顾:

  • 从业务目标出发设计评分策略
  • 理解不同评分函数的特点和适用场景
  • 合理配置score_mode和boost_mode
  • 注意性能影响,特别是脚本函数的使用
  • 充分测试确保评分效果符合预期
Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐