elasticsearch自定义打分操作

2023-11-06

ES 自定义打分

Elasticsearch 会为 query 的每个文档计算一个相关度得分 score ,并默认按照 score 从高到低的顺序返回搜索结果。 在很多场景下,我们不仅需要搜索到匹配的结果,还需要能够按照某种方式对搜索结果重新打分排序。例如:

  • 搜索具有某个关键词的文档,同时考虑到文档的时效性进行综合排序。
  • 搜索某个旅游景点附近的酒店,同时根据距离远近和价格等因素综合排序。
  • 搜索标题包含 elasticsearch 的文章,同时根据浏览次数和点赞数进行综合排序。

Function score query 就可以让我们实现对最终 score 的自定义打分。

score 自定义打分过程

为了方便,下面把 ES 对 query 匹配的文档进行打分得到的 score 记为 query_score ,而最终搜索结果的 score 记为 result_score ,显然,一般情况下(也就是不使用自定义打分时),result_score 就是 query_score

那么当我们使用了自定义打分之后呢?最终结果的 score 即 result_score 的计算过程如下:

1.跟原来一样执行 query 并且得到原来的 query_score

2.执行设置的自定义打分函数,并为每个文档得到一个新的分数,本文记为 func_score

3.最终结果的分数 result_score 等于 query_scorefunc_score 按某种方式计算的结果(默认是相乘)。

例如,搜索标题包含 elasticsearch 的文档。

不使用自定义打分,则搜索形如:

GET /_search
{
  "query": {
    "match": {
      "title": "elasticsearch"
    }
  }
}

假设我们最终得到了三个搜索结果,score 分别是 0.3、0.2、0.1

使用自定义打分,即 function_score ,则语法形如:

GET /_search
{
  "query": {
    "function_score": {
      "query": {
        "match": {
          "title": "elasticsearch"
        }
      }
      <!-- 设置自定义打分函数,这里先省略,后面再展开讲解 -->
      "boost_mode": "multiply"
    }
  }
}

最终搜索结果 score 的计算过程就是:

1.执行 query 得到原始的分数,与上文假设对应,即 query_score 分别是 0.3、0.2、0.1

2.执行自定义的打分函数,这一步会为每个文档得到一个新的分数,假设新的分数即 func_score 分别是 1、3、5

3.最终结果的 score 分数即 result_score = query_score * func_score ,对应假设的三个搜索结果最终的 score 分别就是 0.3 * 1 = 0.30.2 * 3 = 0.60.1 * 5 = 0.5 ,至此我们完成了新的打分过程,而搜索结果也会按照最终的 score 降序排列。

最终的分数 result_score 是由 query_scorefunc_score 进行计算而来,计算方式由参数 boost_mode 定义:

multiply : 相乘(默认),result_score = query_score * function_score

replace : 替换,result_score = function_score

sum : 相加,result_score = query_score + function_score

avg : 取两者的平均值,result_score = Avg(query_score, function_score)

max : 取两者之中的最大值,result_score = Max(query_score, function_score)

min : 取两者之中的最小值,result_score = Min(query_score, function_score)

function_score 打分函数

function_score 提供了以下几种打分的函数:

weight : 加权。

random_score : 随机打分。

field_value_factor : 使用字段的数值参与计算分数。

decay_function : 衰减函数 gauss, linear, exp 等。

script_score : 自定义脚本。

weight

weight 加权,也就是给每个文档一个权重值。

示例:

{
  "query": {
    "function_score": {
      "query": { "match": { "message": "elasticsearch" } },
      "weight": 5
    }
  }
}

例子中的 weight 是 5 ,即自定义函数得分 func_score = 5 ,最终结果的 score 等于 query_score * 5 。

当然这个示例将匹配项全部加权并不会改变搜索结果顺序,我们再看一个例子:

{
  "query": {
    "function_score": {
      "query": { "match": { "message": "elasticsearch" } },
      "functions": [
        {
          "filter": { "match": { "title": "elasticsearch" } },
          "weight": 5
        }
      ]
    }
  }
}

我们可以通过 filter 去限制 weight 的作用范围,另外我们可以在 functions 中同时使用多个打分函数。

random_score

random_score 随机打分,生成 [0, 1) 之间均匀分布的随机分数值。

示例:

GET /_search
{
  "query": {
    "function_score": {
      "random_score": {}
    }
  }
}

虽然是随机值,但是有时候我们需要随机值保持一致,比如所有用户都随机产生搜索结果,但是同一个用户的随机结果前后保持一致,这时只需要为同一个用户指定相同的 seed 即可。

示例:

{
  "query": {
    "function_score": {
      "random_score": {
        "seed": 10,
        "field": "_seq_no"
      }
    }
  }
}

默认情况下,即不设置 field 时会使用 Lucene doc ids 作为随机源去生成随机值,但是这会消耗大量内存,官方建议可以设置 field_seq_no ,主要注意的是,即使指定了相同的 seed ,随机值某些情况下也会改变,这是因为一旦字段进行了更新,_seq_no 也会更新,进而导致随机源发生变化。

多个函数组合示例:

GET /_search
{
  "query": {
    "function_score": {
      "query": { "match_all": {} },
      "boost": "5",
      "functions": [
        {
          "filter": { "match": { "test": "bar" } },
          "random_score": {},
          "weight": 23
        },
        {
          "filter": { "match": { "test": "cat" } },
          "weight": 42
        }
      ],
      "max_boost": 42,
      "score_mode": "max",
      "boost_mode": "multiply",
      "min_score": 42
    }
  }
}

上例 functions 中设置了两个打分函数:

  • 一个是 random_score 随机打分,并且 weight 是 23
  • 另一个只有 weight 是 42

假设:

•第一个函数随机打分得到了 0.1 ,再与 weight 相乘就是 2.3

•第二个函数只有 weight ,那么这个函数得到的分数就是 weight 的值 42

score_mode 设置为了 max,意思是取两个打分函数的最大值作为 func_score,对应上述假设也就是 2.3 和 42 两者中的最大值,即 func_score = 42

boost_mode 设置为了 multiply,就是把原来的 query_scorefunc_score 相乘就得到了最终的 score 分数。

参数 score_mode 指定多个打分函数如何组合计算出新的分数:

multiply : 分数相乘(默认)

sum : 相加

avg : 加权平均值

first : 使用第一个 filter 函数的分数

max : 取最大值

min : 取最小值

为了避免新的分数的数值过高,可以通过 max_boost 参数去设置上限。

需要注意的是:不论我们怎么自定义打分,都不会改变原始 query 的匹配行为,我们自定义打分,都是在原始 query 查询结束后,对每一个匹配的文档进行重新算分。

为了排除掉一些分数太低的结果,我们可以通过 min_score 参数设置最小分数阈值。

field_value_factor

field_value_factor 使用字段的数值参与计算分数。

例如使用 likes 点赞数字段进行综合搜索:

{
  "query": {
    "function_score": {
      "query": { "match": { "message": "elasticsearch" } },
      "field_value_factor": {
        "field": "likes",
        "factor": 1.2,
        "missing": 1,
        "modifier": "log1p"
      }
    }
  }
}

说明:

field : 参与计算的字段。

factor : 乘积因子,默认为 1 ,将会与 field 的字段值相乘。

missing : 如果 field 字段不存在则使用 missing 指定的缺省值。

modifier : 计算函数,为了避免分数相差过大,用于平滑分数,可以是以下之一:

​ •none : 不处理,默认

​ •log : log(factor * field_value)

​ •log1p : log(1 + factor * field_value)

​ •log2p : log(2 + factor * field_value)

​ •ln : ln(factor * field_value)ln1p : ln(1 + factor * field_value)

​ •ln2p : ln(2 + factor * field_value)

​ •square : 平方,(factor * field_value)^2

​ •sqrt : 开方,sqrt(factor * field_value)

​ •reciprocal : 求倒数,1/(factor * field_value)

假设某个匹配的文档的点赞数是 1000 ,那么例子中其打分函数生成的分数就是 log(1 + 1.2 * 1000),最终的分数是原来的 query 分数与此打分函数分数相差的结果。

decay_function

decay_function 衰减函数,例如:

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

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

示例:

"DECAY_FUNCTION": {
    "FIELD_NAME": {
          "origin": "30, 120",
          "scale": "2km",
          "offset": "0km",
          "decay": 0.33
    }
}

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

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

​ •linear : 线性函数

​ •exp : 指数函数

​ •gauss : 高斯函数

origin : 中心点,只能是数值、日期、geo-point

scale : 定义到中心点的距离

offset : 偏移量,默认 0

decay : 衰减指数,默认是 0.5

示例:

GET /_search
{
  "query": {
    "function_score": {
      "gauss": {
        "@timestamp": {
          "origin": "2013-09-17",
          "scale": "10d",
          "offset": "5d",
          "decay": 0.5
        }
      }
    }
  }
}

中心点是 2013-09-17 日期,scale 是 10d 意味着日期范围是 2013-09-12 到 2013-09-22 的文档分数权重是 1 ,日期在 scale + offset = 15d 之外的文档权重是 0.5 。

如果参与计算的字段有多个值,默认选择最靠近中心点的值,也就是离中心点的最近距离,可以通过 multi_value_mode 设置:

min : 最近距离

max : 最远距离

avg : 平均距离

sum : 所有距离累加

示例:

GET /_search
{
  "query": {
    "function_score": {
      "query": {
        "match": {
          "properties": "大阳台"
        }
      },
      "functions": [
        {
          "gauss": {
            "price": {
              "origin": "0",
              "scale": "2000"
            }
          }
        },
        {
          "gauss": {
            "location": {
              "origin": "30, 120",
              "scale": "2km"
            }
          }
        }
      ],
      "score_mode": "multiply"
    }
  }
}

假设这是搜索大阳台的房源,上例设置了 price 价格字段的中心点是 0 ,范围 2000 以内,以及 location 地理位置字段的中心点是 “30, 120” ,方圆 2km 之内,在这个范围之外的匹配结果的 score 分数会进行高斯衰减,即打分降低。

script_score

script_score 自定义脚本打分,可以直接编写脚本打分。

示例:

GET /_search
{
  "query": {
    "function_score": {
      "query": {
        "match": { "message": "elasticsearch" }
      },
      "script_score": {
        "script": {
          "source": "Math.log(2 + doc['my-int'].value)"
        }
      }
    }
  }
}

在脚本中通过 doc['field'] 的形式去引用字段,doc['field'].value 就是使用字段值。

可以把额外的参数与脚本内容分开:

GET /_search
{
  "query": {
    "function_score": {
      "query": {
        "match": { "message": "elasticsearch" }
      },
      "script_score": {
        "script": {
          "params": {
            "a": 5,
            "b": 1.2
          },
          "source": "params.a / Math.pow(params.b, doc['my-int'].value)"
        }
      }
    }
  }
}

本文原创作者:奇想派、一名努力分享的程序员。

文章首发平台:微信公众号【编程达人】

编程达人公众号

原创不易!各位小伙伴觉得文章不错的话,不妨关注公众号,进行点赞(在看)、转发三连走起!谢谢大家!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

elasticsearch自定义打分操作 的相关文章

随机推荐

  • 股票数据API接口进行实际对接过程当中要注意哪些方面?

    投资者使用股票量化接口API接口方面使用能够节约不少的成本 不过在进行实际对接的过程当中 一定要从零开始做好研发建设 只要进入系统之后就能够完全体验到接口带来的更多优势 如果选择一些不靠谱的API接口 可能会浪费金钱 甚至会给大多数用户造成
  • 汇编基础(1)--ARM32

    简介 ARM32 也称为ARM Architecture v7 是一种32位的指令集架构 ISA 由ARM公司开发并广泛应用于嵌入式系统和移动设备 ARM32是ARM体系结构中较早的版本 被许多处理器核使用 包括Cortex A Corte
  • 【PTA】数组排序

    对n个整数进行降序排列 然后输出 import java util public class Main public static void main String args Scanner scanner new Scanner Syst
  • python 第三方库的安装与出错解决方案

    今天介绍五种第三方库的安装方法与错误解决方式 1 wordcloud win 加r输入cmd回车在命令行输入pip install wordcloud 如果下载成功则会出现successful 如果出现错误的话则会出现红色字体和erro提示
  • Android 字符串的替换,截取,拆分,拼接

    1 去除字符串中的 逗号替换成 符号 public static String ReplaceString List
  • uniapp中版本更新下载.apk文件并安装

    首先调用版本更新的接口传入当前版本好 判断是否需要版本更新 版本需要更新使用plus downloader createDownload进行下载 下载完成后使用plus runtime install进行安装 updateVersion d
  • 02_uboot的工作方式_常用命令_常用环境变量

    一 uboot的工作方式 1 uboot的本质 uboot的本质是一个裸机程序 由若干的 c文件和 h文件组成 配置编译后生成uboot bin 把这个镜像文件烧录至启动介质中给soc启动 一般的uboot大小在180k 400k之间 我你
  • RegNeRF,FreeNeRF: 神经辐射场的自由频率正则化,几何正则化,外观正则化,遮挡正则化

    目录 概要 一 论文 RegNeRF Regularizing Neural Radiance Fields for View Synthesis from Sparse Inputs 1 几何正则化 2 外观正则化 二 论文 FreeNe
  • Python编程题每日一练day2(附答案)

    Python编程题每日一练day2 Python编程题每日一练day1 附答案 一 被8整除的数字 二 九九乘法表 三 判断素数 四 重复出现的字符串 五 密码游戏 提高编程能力的最有效办法就是 敲代码 Python编程题每日一练day1
  • 彻底搞懂String:字符串常量池

    作为最基础的引用数据类型 Java 设计者为 String 提供了字符串常量池以提高其性能 那么字符串常量池的具体原理是什么 我们带着以下三个问题 去理解字符串常量池 1 字符串常量池的设计意图是什么 2 字符串常量池在哪里 3 如何操作字
  • React styled-components

    React styled components 参考 styled components 让 React 如虎添翼 React styled components 让前端开发如虎添翼 哔哩哔哩 bilibili styled compone
  • matlab数字仿真实验,matlab数值仿真

    matlab数值仿真10 1知识要点与背景 单自由度阻尼系统 2 观察程序 zxy10 1 m 图10 1 a clear clf global c w x0 1 1 x0 2 0 w 10 n 3 tspan linspace 0 4 1
  • 概率图论PGM的D-Separation(D分离)

    出处 http my oschina net dillan blog 134011 本文大部分来自 http www zhujun me d separation separation d html 其中找了一些资料发现原文中阻塞 bloc
  • 北京大学肖臻老师《区块链技术与应用》公开课笔记【03-BTC-数据结构】

    北大肖臻老师 区块链技术与应用 课程链接 点击这里 全系列文章链接 点击这里 主要补充内容及图片来源 区块链 技术驱动金融 该系列文章如中有任何侵权内容 或者有链接无法打开 图片加载上传失败等情况 请及时与我个人联系删除或修改 一 哈希指针
  • 【NoReady】Kubernetes集群新添加node节点出错排查记录

    文章目录 1 查看node节点概况 2 查看Pod容器概况 3 问题发现及解决 4 总结 1 查看node节点概况 发现新添加的Node节点处在NoReady状态 root master01 kubectl get nodes NAME S
  • fiddler抓包工具实战二(实战抓包小游戏)

    这一章我们要做的是实战抓包某小游戏 具体是什么小游戏这里就不在做解释了 主要使用到的工具是 1 fiddler 2 安卓模拟器 不喜欢用模拟器的小伙伴就可以直接用自己的手机抓包也是可以的哦 具体可以去百度搜索教程 好的闲话不多说 在抓取小游
  • Django启动服务器无效的解决办法

    在跟着菜鸟教程学Django 本地启动服务器命令无效 菜鸟教程的命令为 python3 manage py runserver 0 0 0 0 8000 将python3修改为python后启动成功
  • 服务器+返回500错误信息,HttpWebResponse远程服务器返回错误: (500) 内部服务器错误 的解决办法...

    在工作中用C 开发了一个小程序 不断访问去请求一个网站的页面 在循环过程中有时会报 远程服务器返回错误 500 内部服务器错误 有时不会 出现的时机也不太一样 开始以为是网站的问题 后来网站是可以正常访问的 那就是自己程序的问题了 for
  • 软件测试岗位,真正达到月收入1万以上的有多少?

    软件测试工程师工资有多高 我想说拿4k 30k的都有 因为软件测试工程师的薪资水平是受各种因素影响的 比如 所在城市 个人工作能力 技能掌握情况 工作年限等等都有很大关系 这是要根据每个人的具体情况而定的 记得在某一线城市有位阿姨给自己是程
  • elasticsearch自定义打分操作

    ES 自定义打分 Elasticsearch 会为 query 的每个文档计算一个相关度得分 score 并默认按照 score 从高到低的顺序返回搜索结果 在很多场景下 我们不仅需要搜索到匹配的结果 还需要能够按照某种方式对搜索结果重新打