Elasticsearch – 二次打分处理方案

Stella981
• 阅读 615

有时ES默认的评分规则并不能满足需求,时长会有在old_source的基础上根据某字段的值需要重新打分的需求场景,比如在相关度相差不大的情况下时间越进越靠前,或者评论数越多越靠前等等。此时需要重新打分。对此可以通过两种方式法来处理:

1、在创建索引的时候提高文档的权重,根据因素字段值来给文档设置boost.这种方式在lucene之前有效,之后就无效了。不建议采用。

2、通过function_query来设置分数。

通过function_query来设置分数

线性提升

如在相关度相差不大的情况下,投票的数量越多排名越往前。可以采用简单的线性方式来处理:

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

其中:

  • field:表示影响权重的字段例子中是votes

  • modifier:表示影响的规则

  • 修饰语 modifier 的值可以为: none (默认状态) log 、 log1p 、 log2p 、 ln 、 ln1p 、 ln2p 、 square 、 sqrt 以及 reciprocal。 facror:表示影响因子。 通过上面设置后整个打分变为:

    new_score = old_score * log(1 + factor * number_of_votes)
    factor 值大于 1 会提升效果, factor 值小于 1 会降低效果。

初次之外还可以设置boost_mode影响因子和old_source通过什么运算来得出新分值。

GET /blogposts/post/_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" ,  
      "max_boost":  1.5   
    }  
  }  
}  

boost_mode multiply评分 _score 与函数值的积(默认) sum评分 _score 与函数值的和 min评分 _score 与函数值间的较小值 max评分 _score 与函数值间的较大值 replace函数值替代评分 _scoremax_boost设置最大的得分分值 无论 field_value_factor 函数的结果如何,最终结果都不会大于 1.5

引入模型函数来控制二次评分

上线是一种线性的影响关系,有时会有范围性质、分数变化缓慢的需求场景,此时在使用上面的方案未免为使分数变化的不太圆滑,es提供了三种衰减函数(decay functions)来计算分数,让我们有能力在两个滑动标准如:地点和价格,时间等之间权衡。

   linear、 exp 和 gauss (线性、指数和高斯函数),它们可以操作数值、时间以及经纬度地理坐标点这样的字段。所有三个函数都能接受以下参数:

origin:中心点 或字段可能的最佳值,落在原点 origin 上的文档评分 _score 为满分 1.0 。 scale:衰减率,即一个文档从原点origin下落时,评分_score 改变的速度.(例如,每 £10 欧元或每 100 米) decay:从原点 origin 衰减到 scale 所得的评分 _score ,默认值为 0.5 。 offset:以原点 origin 为中心点,为其设置一个非零的偏移量 offset 覆盖一个范围,而不只是单个原点。在范围 -offset <= origin <= +offset 内的所有评分 _score 都是 1.0 。 这三个函数的唯一区别就是它们衰减曲线的形状,用图来说明会更为直观

Elasticsearch – 二次打分处理方案

图 33 “衰减函数曲线” 中所有曲线的原点 origin (即中心点)的值都是 40 , offset 是 5 ,也就是在范围 40 - 5 <= value <= 40 + 5 内的所有值都会被当作原点 origin 处理——所有这些点的评分都是满分 1.0 。

在此范围之外,评分开始衰减,衰减率由 scale 值(此例中的值为 5 )和 衰减值 decay (此例中为默认值 0.5 )共同决定。结果是所有三个曲线在 origin +/- (offset + scale) 处的评分都是 0.5 ,即点 30和 50 处。

linear 、 exp 和 gauss (线性、指数和高斯)函数三者之间的区别在于范围( origin +/- (offset + scale) )之外的曲线形状:

linear 线性函数是条直线,一旦直线与横轴 0 相交,所有其他值的评分都是 0.0 。 exp 指数函数是先剧烈衰减然后变缓。 gauss 高斯函数是钟形的——它的衰减速率是先缓慢,然后变快,最后又放缓。 选择曲线的依据完全由期望评分 _score 的衰减速率来决定,即距原点 origin 的值。

回到我们的例子:用户希望租一个离伦敦市中心近( { "lat": 51.50, "lon": 0.12} )且每晚不超过 £100 英镑的度假屋,而且与距离相比, 我们的用户对价格更为敏感,这样查询可以写成:

{  
    "query": {  
        "function_score": {  
          "functions": [  
            {  
              "gauss": {  
                "price": {  
                  "origin": "0",  
                  "scale": "20"  
                }  
              }  
            },  
            {  
              "gauss": {  
                "location": {  
                  "origin": "11, 12",  
                  "scale": "2km"  
                }  
              }  
            }  
          ],  
          "query": {  
            "match": {  
              "properties": "balcony"  
            }  
          },  
          "score_mode": "multiply"  
        }  
    }  
}  

location 字段以地理坐标点 geo_point 映射。 price 字段是数值。 price 语句是 location 语句权重的两倍。 location 语句可以简单理解为: 以伦敦市中作为原点 origin 。 所有距原点 origin 2km 范围内的位置的评分是 1.0 。 距中心 5km ( offset + scale )的位置的评分是 0.5 。

weight 参数可以被用来调整每个语句的贡献度,权重 weight 的默认值是 1.0 。这个值会先与每个句子的评分相乘,然后再通过 score_mode 的设置方式合并。

参考资料

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Stella981 Stella981
3年前
Python之time模块的时间戳、时间字符串格式化与转换
Python处理时间和时间戳的内置模块就有time,和datetime两个,本文先说time模块。关于时间戳的几个概念时间戳,根据1970年1月1日00:00:00开始按秒计算的偏移量。时间元组(struct_time),包含9个元素。 time.struct_time(tm_y
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
3年前
JS 对象数组Array 根据对象object key的值排序sort,很风骚哦
有个js对象数组varary\{id:1,name:"b"},{id:2,name:"b"}\需求是根据name或者id的值来排序,这里有个风骚的函数函数定义:function keysrt(key,desc) {  return function(a,b){    return desc ? ~~(ak
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Stella981 Stella981
3年前
Gson之实例五
前面四篇博客基本上可以满足我们处理的绝大多数需求,但有时项目中对json有特殊的格式规定.比如下面的json串解析:{"tableName":"students","tableData":{"id":1,"name":"李坤","birthDay":"Jun 22, 2012 9:54:49 PM"},{"id":2,"name":"曹贵生"
Wesley13 Wesley13
3年前
ThinkPHP 根据关联数据查询 hasWhere 的使用实例
很多时候,模型关联后需要根据关联的模型做查询。场景:广告表(ad),广告类型表(ad\_type),现在需要筛选出广告类型表中id字段为1且广告表中status为1的列表先看关联的设置部分 publicfunctionadType(){return$thisbelongsTo('A