Elasticsearch 查询之Function Score Query

news2025/1/19 20:41:22

前言

ES 的主查询评分模式分为两种,是信息检索领域的重要算法: TF-IDF 算法 和 BM25 算法。

Elasticsearch 从版本 5.0 开始引入了 BM25 算法作为默认的文档评分(relevance scoring)算法。在此之前,Elasticsearch 使用的是 TF-IDF 算法作为默认的文档评分算法。从版本 5.0 起,BM25 算法取代了 TF-IDF,成为了默认的算法,用于计算文档与查询之间的相关性得分。

这个变化主要是为了更好地适应现代信息检索需求,BM25 算法在一些情况下能够提供更准确的文档排序和检索结果。

而 Function Score Query 不夸张的说是 ES 里面终极自定义打分的大招,非常的灵活并且功能强大,常规情况下,我们排序都是基于 _score 的,如果 _score相等的情况下,我们还可以额外增加排序字段,比如按日期,数量,价格等,但在搜索引擎中,排序往往并不像 SQL 那样,从左到右规整的按照多字段排序,在 SQL 里面,排序的主顺序一定是由左边的第一个字段决定的,但在搜索引擎种,却不仅仅是这样的,还可以通过 function score 做到那个字段贡献的分值大,排序顺序就以谁为主,因为这些是真实存在的需求场景,如下:

  • 新闻场景:搜索具有某个关键词的文档,同时结合文档的时效性进行综合排序
  • 导航场景:搜索某个地点附近的饭店,同时根据距离远近和价格等因素综合排序
  • 论坛场景:搜索包含某个关键词的文章,同时根据浏览次数和点赞数进行综合排序

SQL 的排序模型

select * from table order by A, B, C

搜索引擎的排序模型

query * from index oder by score max(A, B, C)

写入数据

为了用实际例子讲解 function score,我们先写入几条数据

POST test01/doc/_bulk
{ "index" : { "_id" : "1" } }
{"title": "kubernetes", "content": "Development History","vote": 3,"year": 2015}
{ "index" : { "_id" : "2" } }
{"title": "kubernetes", "content": "Competitive Analysis","vote": 5,"year": 2018}
{ "index" : { "_id" : "3" } }
{"title": "kubernetes docker","content": "The connection between virtual and docker technology","vote": 100,"year": 2011}
{ "index" : { "_id" : "4" } }
{"title": "kubernetes network","content": "router vlan tcp","vote": 20,"year": 2009}

查询数据

查询关键词:kubernetes

GET test01/_search?search_type=dfs_query_then_fetch
{
  "query": {
    "bool": {
      "should": [
        {"term": {"title": "kubernetes"}}
        ]
    }

  },
  "explain": false
}

返回结果:

    "hits" : [
      {
        "_index" : "test01",
        "_type" : "doc",
        "_id" : "2",
        "_score" : 0.12776,
        "_source" : {
          "title" : "kubernetes",
          "content" : "Competitive Analysis",
          "vote" : 5,
          "year" : 2018
        }
      },
      {
        "_index" : "test01",
        "_type" : "doc",
        "_id" : "1",
        "_score" : 0.12776,
        "_source" : {
          "title" : "kubernetes",
          "content" : "Development History",
          "vote" : 3,
          "year" : 2015
        }
      },
      {
        "_index" : "test01",
        "_type" : "doc",
        "_id" : "4",
        "_score" : 0.09954306,
        "_source" : {
          "title" : "kubernetes network",
          "content" : "router vlan tcp",
          "vote" : 20,
          "year" : 2009
        }
      },
      {
        "_index" : "test01",
        "_type" : "doc",
        "_id" : "3",
        "_score" : 0.081535265,
        "_source" : {
          "title" : "kubernetes mesos swarm",
          "content" : "The connection between virtual and docker technology",
          "vote" : 100,
          "year" : 2011
        }
      }
    ]

结果看起来是正常的,ok,现在我们要改变需求了,加入了基于点赞量的加权,也就是说匹配关键词并且点赞量高的优先展示,因为点赞量高意味着这些文章质量更高,所以需要优先曝光,这个时候我们就需要用到 function score

Function Score Query介绍

计算原理

使用主查询 的 TF-IDF 或者 BM25 算法得出来的默认评分简称为: query_score

使用 Function Score 查询结合自定义策略得出来的评分简称为:function_score

最终用于排序的评分称为 sort_score

在使用了 自定义的 Fuction Score 之后,我们最终得出来的 sort_score 就是使用 query_score 和 function_score以某种运算形式 (score_mode) 计算出来的,这个策略默认是相乘,也即:

sort_score = query_score * function_score

function_score内的score_mode

score_mode有六种:

mode

描述

multiply

多个函数 score 相乘(默认)

sum

多个函数 score 求和

avg

多个函数 score 取平均值

first

使用第一个 filter 函数的 score

max

取多个函数 score 中最大的那个

min

取多个函数 score 中最大的那个

sort_score运算策略

sort_score 是 query_score 和 function_score以某种形式运算而来,支持的运算操作也有六种:

mode

描述

multiply

sort_score = query_score * function_score(默认)

sum

sort_score = query_score + function_score

avg

sort_score = avg ( query_score + function_score ) / 2

replace

sort_score = function_score

max

sort_score = max ( query_score + function_score )

min

sort_score = min ( query_score + function_score )

默认情况下,修改分数不会更改匹配的文档。要排除不满足特定分数阈值的文档,可以将 min_score 参数设置为所需的分数阈值

fuction score的评分函数
script_score

script_score 支持自定义脚本打分,也就是说可以用类编程语言的脚本来嵌入的打分逻辑,ES 之前用的是 groovy脚本因安全性有问题,现在换成了 Painless 脚本,详细可参考:Painless scripting language | Elasticsearch Guide [8.9] | Elastic

现在我们用 script_score 来完成上面查询场景中的,给点赞量的加权:

GET test01/_search?search_type=dfs_query_then_fetch
{
  "query": {
    "function_score": {
      "query": {
        "match": { "title": "kubernetes" }
      },
      "script_score": {
        "script": {
          "params": {
            "baseScore": 1
          },
          "source": "params.baseScore + doc['vote'].value"
        }
      },
      "boost_mode": "replace",
      "score_mode": "multiply"
    }
  },
  "explain": false
}

结果如下:

    "hits" : [
      {
        "_index" : "test01",
        "_type" : "doc",
        "_id" : "3",
        "_score" : 101.0,
        "_source" : {
          "title" : "kubernetes mesos swarm",
          "content" : "The connection between virtual and docker technology",
          "vote" : 100,
          "year" : 2011
        }
      },
      {
        "_index" : "test01",
        "_type" : "doc",
        "_id" : "4",
        "_score" : 21.0,
        "_source" : {
          "title" : "kubernetes network",
          "content" : "router vlan tcp",
          "vote" : 20,
          "year" : 2009
        }
      },
      {
        "_index" : "test01",
        "_type" : "doc",
        "_id" : "2",
        "_score" : 6.0,
        "_source" : {
          "title" : "kubernetes",
          "content" : "Competitive Analysis",
          "vote" : 5,
          "year" : 2018
        }
      },
      {
        "_index" : "test01",
        "_type" : "doc",
        "_id" : "1",
        "_score" : 4.0,
        "_source" : {
          "title" : "kubernetes",
          "content" : "Development History",
          "vote" : 3,
          "year" : 2015
        }
      }
    ]

在这个函数查询中,我们使用了 replace 策略,来直接使用 fuction_score的分数,注意 从 docValue 里面取出来的字段必须是number 类型才可以

weight

直接对查询加权:

例子一:

GET test01/_search?search_type=dfs_query_then_fetch
{
  "query": {
    "function_score": {
      "query": {
        "match": { "title": "kubernetes" }
      },
      "weight": 10
    }
  },
  "explain": false
}

结果:

    "hits" : [
      {
        "_index" : "test01",
        "_type" : "doc",
        "_id" : "2",
        "_score" : 1.2775999,
        "_source" : {
          "title" : "kubernetes",
          "content" : "Competitive Analysis",
          "vote" : 5,
          "year" : 2018
        }
      },
      {
        "_index" : "test01",
        "_type" : "doc",
        "_id" : "1",
        "_score" : 1.2775999,
        "_source" : {
          "title" : "kubernetes",
          "content" : "Development History",
          "vote" : 3,
          "year" : 2015
        }
      },
      {
        "_index" : "test01",
        "_type" : "doc",
        "_id" : "4",
        "_score" : 0.9954306,
        "_source" : {
          "title" : "kubernetes network",
          "content" : "router vlan tcp",
          "vote" : 20,
          "year" : 2009
        }
      },
      {
        "_index" : "test01",
        "_type" : "doc",
        "_id" : "3",
        "_score" : 0.8153527,
        "_source" : {
          "title" : "kubernetes mesos swarm",
          "content" : "The connection between virtual and docker technology",
          "vote" : 100,
          "year" : 2011
        }
      }
    ]

例子二:

GET test01/_search?search_type=dfs_query_then_fetch
{
  "query": {
    "function_score": {
      "query": {
        "match_all": {}
      },
      "functions": [
        {
          "filter": { "match": { "content": "kubernetes" } },
          "weight": 1
        },
        {
          "filter": { "match": { "title": "mesos" } },
          "weight": 10
        },
        {
          "filter": { "match": { "content": "tcp" } },
          "weight": 20
        }
      ]
    }
  },
  "explain": false
}

结果如下:

    "hits" : [
      {
        "_index" : "test01",
        "_type" : "doc",
        "_id" : "4",
        "_score" : 20.0,
        "_source" : {
          "title" : "kubernetes network",
          "content" : "router vlan tcp",
          "vote" : 20,
          "year" : 2009
        }
      },
      {
        "_index" : "test01",
        "_type" : "doc",
        "_id" : "3",
        "_score" : 10.0,
        "_source" : {
          "title" : "kubernetes mesos swarm",
          "content" : "The connection between virtual and docker technology",
          "vote" : 100,
          "year" : 2011
        }
      },
      {
        "_index" : "test01",
        "_type" : "doc",
        "_id" : "2",
        "_score" : 1.0,
        "_source" : {
          "title" : "kubernetes",
          "content" : "Competitive Analysis",
          "vote" : 5,
          "year" : 2018
        }
      },
      {
        "_index" : "test01",
        "_type" : "doc",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "title" : "kubernetes",
          "content" : "Development History",
          "vote" : 3,
          "year" : 2015
        }
      }
    ]

这个 filter 很适合竞价排名

random_score

random score 相当于把返回文档的顺序给打乱,比较适合随机召回文档

GET test01/_search?search_type=dfs_query_then_fetch
{
  "query": {
    "function_score": {
      "query": {
        "match_all": {}
      },
      "random_score": {}
    }
  },
  "explain": false
}

默认情况下,是每次查询的值都是随机的,但有时候我们想用同一个 id 的保持不变,不同 id 的结果随机,这个时候可以使用 seed 和 field 来控制:


GET test01/_search?search_type=dfs_query_then_fetch
{
  "query": {
    "function_score": {
      "query": {
        "match_all": {}
      },
      "random_score": {
                "seed": 10,
        "field": "_seq_no"
      }
    }
  },
  "explain": false
}

这个时候 seed 的值,就可以等同于 id,id 值一样的结果不变

field_value_factor
GET test01/_search?search_type=dfs_query_then_fetch
{
  "query": {
    "function_score": {
      "query": {
        "match": {"title":"kubernetes"}
      },
      "field_value_factor": {
        "field": "vote",
        "factor": 1.2,
        "modifier": "sqrt",
        "missing": 1
      },
      "boost_mode": "max"
    }
  },
  "explain": false
}

等价于script score 脚本 sqrt(1.2 * doc['vote'].value)

其中field 是文档种的字段,missing 是缺失值,factor 是放大的比值默认是 1,modifier 是对结果的再次处理,支持多种函数如:none, log, log1p, log2p, ln, ln1p, ln2p, square, sqrt, or reciprocal

decay functions

衰减函数

  • 以某个数值作为中心点,距离多少的范围之外逐渐衰减(缩小分数)
  • 以某个日期作为中心点,距离多久的范围之外逐渐衰减(缩小分数)
  • 以某个地理位置点作为中心点,方圆多少距离之外逐渐衰减(缩小分数)

一个例子:

"DECAY_FUNCTION": { 
    "FIELD_NAME": { 
          "origin": "11, 12",
          "scale": "2km",
          "offset": "0km",
          "decay": 0.33
    }
}

上例的意思就是在距中心点方圆 2 公里之外,分数减少到三分之一(乘以 decay 的值 0.33)

DECAY_FUNCTION 可以是以下任意一种函数:

linear : 线性衰减函数
exp : 指数衰减函数
gauss : 高斯正常衰减函数

origin :

用于计算距离的原点。对于数字字段,必须以数字形式给出;对于日期字段,必须以日期形式给出;对于地理字段,必须以地理点形式给出。地理和数字字段是必需的。对于日期字段,默认值为现在。 origin 支持日期数学(例如 now-1h)

scale :

定义计算得分等于衰减参数时距原点 + 偏移量的距离。对于地理字段:可以定义为数字+单位(1km、12m、...)。默认单位是米。对于日期字段:可以定义为数字+单位(“1h”、“10d”、...)。默认单位是毫秒。对于数字字段:任何数字

offset :

如果定义了偏移量,则衰减函数将仅计算距离大于定义的偏移量的文档的衰减函数。默认值为 0

decay :

衰减参数定义如何在给定比例的距离上对文档进行评分。如果未定义衰减,则距离尺度上的文档将评分为 0.5

例如,现在新数据,标题匹配 kubernetes 后,按照优先检索位于 2011-2015 年份进行加权,不再按照点赞量:

GET test01/_search?search_type=dfs_query_then_fetch
{
  "query": {
    "function_score": {
      "query": {
        "match": {"title":"kubernetes"}
      },
      "gauss": {
        "year": {
          "origin": "2013", 
          "offset": "2",
          "scale": "2",
          "decay": 0.1            
        }
      },
      "boost_mode": "max"
    }
  },
  "explain": false
}

解释一下:

上面使用高斯函数作为衰减,使用的是年份字段:

orgin:代表中心点是 2013 年

offset:2 代表 [2011, 2015] 作为中心圆,也就是 [2011, 2015]位于这之间的文档评分直接为 1

scala: 2 代表 [2009, 2017]之外的评分为 0.1

其他的,如果位于 2009-2011 范围的以及 2015-2017 范围的,就按正常评分就好了

结果如下:

    "hits" : [
      {
        "_index" : "test01",
        "_type" : "doc",
        "_id" : "3",
        "_score" : 1.0,
        "_source" : {
          "title" : "kubernetes mesos swarm",
          "content" : "The connection between virtual and docker technology",
          "vote" : 100,
          "year" : 2011
        }
      },
      {
        "_index" : "test01",
        "_type" : "doc",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "title" : "kubernetes",
          "content" : "Development History",
          "vote" : 3,
          "year" : 2015
        }
      },
      {
        "_index" : "test01",
        "_type" : "doc",
        "_id" : "2",
        "_score" : 0.12776,
        "_source" : {
          "title" : "kubernetes",
          "content" : "Competitive Analysis",
          "vote" : 5,
          "year" : 2018
        }
      },
      {
        "_index" : "test01",
        "_type" : "doc",
        "_id" : "4",
        "_score" : 0.1,
        "_source" : {
          "title" : "kubernetes network",
          "content" : "router vlan tcp",
          "vote" : 20,
          "year" : 2009
        }
      }
    ]

三种衰减的函数的曲线如下:

此外,如果用于计算衰减的字段包含多个值,则默认情况下会选择最接近中心点的值来确定距离。这可以通过设置 multi_value_mode 来更改:

min:距离是最小距离

max:距离是最大距离

avg:距离是平均距离

sum:距离是所有距离的总和

    "DECAY_FUNCTION": {
        "FIELD_NAME": {
              "origin": ...,
              "scale": ...
        },
        "multi_value_mode": "avg"
    }

function score 的其他参数

max_boost: 最大权重值的范围

boost_mode: 最终 query_score 和 function_score的计算策略

min_score: 最终的结果过滤掉评分低于这个值的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/895897.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第 4 章 链表(1)

4.1链表(Linked List)介绍 链表是有序的列表,但是它在内存中是存储如下 小结: 链表是以节点的方式来存储,是链式存储每个节点包含 data 域, next 域:指向下一个节点.如图:发现链表的各个节点不一定是连续存储.链表分带头节点的链…

forEach时候,Exception in thread “AWT-EventQueue-0“ java.util.ConcurrentModificat

问题分析: 在很多容器中,都有一个变量记录你从结构上修改此容器的次数,叫做modCount,查看ArrayList的add()和remove()方法就可以发现,每次你调用add方法()向容器里面增加了一个元素,或者你调用Remove()方法…

每日一博 - MPP(Massively Parallel Processing,大规模并行处理)架构

文章目录 概述优点缺点小结 概述 MPP(Massively Parallel Processing,大规模并行处理)架构是一种常见的数据库系统架构,主要用于提高数据处理性能。它通过将多个单机数据库节点组成一个集群,实现数据的并行处理。 在 …

PAT 1018 Public Bike Management

个人学习记录,代码难免不尽人意。 There is a public bike service in Hangzhou City which provides great convenience to the tourists from all over the world. One may rent a bike at any station and return it to any other stations in the city. The Pu…

微服务基础概念【内含图解】

目录 拓展补充: 单体架构 分布式架构 面向服务的体系结构 云原生 微服务架构 什么是微服务? 微服务定义 拓展补充: 单体架构 单体架构:将业务的所有功能集中在一个项目中开发,最终打成一个包部署 优点&#x…

Hlang--用Python写个解释器

文章目录 前言流程数学解释器结果封装数的操作运行时异常运行解释实现总结前言 没错今天提前来做这个东西,昨天晚上干这个玩意差不多干了两个多小时才搞定,导致凌晨2点才睡觉,最要命的是,写着写着突然想到有一道线代理解错了,一个晚上,做梦全是这两个东西。尤其是晚上效…

一篇学会软硬链接|快捷方式|操作系统|centos7

前言 那么这里博主先安利一些干货满满的专栏了! 首先是博主的高质量博客的汇总,这个专栏里面的博客,都是博主最最用心写的一部分,干货满满,希望对大家有帮助。 高质量博客汇总https://blog.csdn.net/yu_cblog/categ…

安装和配置 Ansible

安装和配置 Ansible 按照下方所述,在控制节点 control.area12.example.com 上安装和配置 Ansible: 安装所需的软件包 创建名为 /home/curtis/ansible/inventory 的静态清单文件,以满足以下要求: node1 是 dev 主机组的成员 node2 …

【C语言】字符串和内存函数的介绍 -- 详解

重点介绍处理字符和字符串的库函数的使用和注意事项。 C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。字符串常量适用于那些对它不做修改的字符串函数。 一、求字符串长度⚪strlen …

LC-对称二叉树

LC-对称二叉树 链接:https://leetcode.cn/problems/symmetric-tree/description/ 描述:给你一个二叉树的根节点 root , 检查它是否轴对称。 例1: 输入:root [1,2,2,3,4,4,3] 输出:true 例2: 输入&…

【C++】—— 详解AVL树

目录 序言 (一)AVL树的概念 1、AVL树的由来 2、AVL树的特点 3、平衡因子 (二)AVL树的插入 1、插入操作的思想理解 2、AVL树的旋转 1️⃣ LL平衡旋转(右单旋转) 2️⃣ RR平衡旋转(左单…

基于Canal实现MySQL 8.0 数据库数据同步

前言 服务器说明 主机名称操作系统说明192.168.11.82Ubuntu 22.04主库所在服务器192.168.11.28Oracle Linux Server 8.7从库所在服务器 版本说明 <span style"color:#000000"><span style"background-color:#ffffff"><code class"la…

carla中lka实现(二)

前言&#xff1a; 首先计算之前检测出来的车道线的中线与输入图像的中线进行计算距离&#xff0c;&#xff0c;并设置不同的阈值对于不同的方向进行相关的调整。 一、车辆中心线 一般而言将摄像头架设在车辆的正中心轴上&#xff0c;所获得的图像的中间线极为车辆的中心。 …

激光雷达 01 线数

一、线数 对于 360 旋转式和一维转镜式架构的激光雷达来说&#xff0c;有几组激光收发模块&#xff0c;垂直方向上就有几条线&#xff0c;被称为线数。这种情况下&#xff0c;线数就等同于激光雷达内部激光器的数量[参考]。 通俗来讲&#xff0c;线数越高&#xff0c;激光器的…

Adobe Acrobat 无法使用 PS 编辑图片 的解决方法

问题描述 使用较新版本的Adobe Acrobat时&#xff0c;有时会遇到问题。 比如对pdf中的图片使用 PS 进行编辑&#xff0c;会弹出以下窗口&#xff0c;导致打不开 PS &#xff0c;无法对图片进行编辑。 Adobe 无法启动您指定的图像编辑应用程序。请在"首选项"的"…

【STM32CubeMX】低功耗模式

前言 本文讲解STM32F10X的低功耗模式&#xff0c;部分资料参考自STM32手册。STM32F10X提供了三种低功耗模式&#xff1a;睡眠模式&#xff08;Sleep mode&#xff09;、停机模式&#xff08;Stop mode&#xff09;和待机模式&#xff08;Standby mode&#xff09;。这些低功耗模…

双环传动CIO吴学信:数字化转型为企业高效运转和业绩腾飞提供重要支撑

引言 浙江双环传动机械股份有限公司&#xff08;股票代码&#xff1a;002472&#xff09;创建40年来专注于机械传动核心部件——齿轮及其组件的研发、制造与销售&#xff0c;已成为全球最大的专业齿轮产品制造商和服务商之一。 2019年以来&#xff0c;双环传动与纷享销客CRM建…

【0Ω电阻在PCB板中的5大常见作用】

文章目录 前言1、充当跳线2、进行调试时的前后级隔离3、让调试更灵活4、方便测试电流5、用于单点接地 前言 在PCB板中&#xff0c;时常见到一些阻值为0Ω的电阻。我们都知道&#xff0c;在电路中&#xff0c;电阻的作用是阻碍电流&#xff0c;而0Ω电阻显然失去了这个作用。那…

LC-将有序数组转换为二叉搜索树

LC-将有序数组转换为二叉搜索树 链接&#xff1a;https://leetcode.cn/problems/convert-sorted-array-to-binary-search-tree/description/ 描述&#xff1a; 给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 高度平衡 二叉搜索…

new String()到底创建了几个对象

题目&#xff1a; new String&#xff08;"abc"&#xff09;会创建几个对象&#xff1f; 看字节码&#xff0c;就知道是两个。