美文网首页
elasticsearch——影响文档评分

elasticsearch——影响文档评分

作者: 奕_然 | 来源:发表于2018-05-24 10:57 被阅读0次

    一般情况下,类似关系型数据库中影响返回结果的方式,在elasticsearch中也存在,包括sort、size等。但是这都是简单粗暴的对返回结果进行直接处理,很大的可能会影响返回文档的相关度。比如,通过sort操作对搜索结果按时间排序,这时排在前面的文档很可能相关度非常小,而相关度大的文档则因为时间排序被放在了下面。这显然不是我们想要的结果。elasticsearch提供了方法,允许我们用除了搜索之外的其他因素影响返回文档的顺序,同时兼顾文档的相关度。

    简单粗暴地评分

    首先先说一下elasticsearch的搜索评分逻辑。
    查询的权重基于三个因素:词频、逆向文档频率和字段长度归一值。

    • 词频:查询词在该文档中出现的频率。频率越高,权重越高。
    • 逆向文档频率:查询词在所有文档中出现的频率。频率越高,权重越低。可以降低日常使用的高频率词的权重。
    • 字段长度归一值:查询字段的长度。字段长度越长,查询词权重越高,反之越低。

    不同词对搜索结果的影响基本取决于以上三个因素,这里不列出详细的计算公式。

    如何影响文档评分

    首先,影响文档评分的操作推荐是查询的时候进行,这样灵活性更好。这里不介绍简单的评分提升或者降低,直接介绍elasticsearch中控制文档评分的终极武器:function_scor。

    function_score

    function_score是query结构的一个子集,它对每一个符合查询的文档应用一个或一组函数,达到影响甚至替换原始查询评分的目的。这个操作可以很方便的实现复杂的查询逻辑。
    Elasticsearch 预定义了一些函数:

    • weight:为每个文档应用一个简单而不被规范化的权重提升值:当 weight 为 2 时,最终结果为 2 * _score 。
    • field_value_factor:使用这个值来修改 _score ,如将 popularity 或 votes (受欢迎或赞)作为考虑因素。
    • random_score:为每个用户都使用一个不同的随机评分对结果排序,但对某一具体用户来说,看到的顺序始终是一致的。
    • 衰减函数 —— linear 、 exp 、 gauss:将浮动值结合到评分 _score 中,例如结合 publish_date 获得最近发布的文档,结合 geo_location 获得更接近某个具体经纬度(lat/lon)地点的文档,结合 price 获得更接近某个特定价格的文档。
    • script_score:如果需求超出以上范围时,用自定义脚本可以完全控制评分计算,实现所需逻辑。

    以上函数开箱即用,一般情况下,elasticsearch提供的函数就可以满足需求,如果有特殊要求,也可以使用最后一个script_score自己写控制评分脚本。但是script_score对性能有较大影响,能不用就不用。
    下面简单说明几个需求,来看看elasticsearch是如何通过function_score实现的。

    点击数影响评分

    如果想要在原来搜索的基础上,加入点击数的影响,即将点击数高的文档放到搜索结果靠上的位置,但是搜索的评分仍然是主要的依据。

    PUT /blogposts/post/1
    {
      "title":   "About popularity",
      "content": "In this post we will talk about...",
      "votes":   6
    }
    

    这是一篇文章的文档,其中保存了点击数。这里可以通过field_value_factor实现相关需求。

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

    function_score嵌入了一个query查询中,然后内部又设定了一个query,内部的query查询是主查询。在function_score内部还有一个field_value_factor函数,这个函数会对符合每一个主查询的文档使用。每个文档的最终评分都会进行如下的计算:new_score = old_score * number_of_votes
    默认的field_value_factor计算是线性的,votes的原始值直接用来计算,这通常不会产生好的结果。一般情况下,通过对数计算取代现行计算,可以取得更平滑的结果。

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

    将field_value_factor设为对数计算,计算公式:new_score = old_score * log(1 + number_of_votes)
    field_value_factor提供了众多参数,设置相关计算的各种参数,这里不再一一列举。如果想要细致的调整搜索结果的话,参考官方文档进行。

    对查询结果进行随机评分

    作为网站的所有者,总会希望让广告有更高的展现率。在当前查询下,有相同评分 _score 的文档会每次都以相同次序出现,为了提高展现率,在此引入一些随机性可能会是个好主意,这能保证有相同评分的文档都能有均等相似的展现机率。
    我们想让每个用户看到不同的随机次序,但也同时希望如果是同一用户翻页浏览时,结果的相对次序能始终保持一致。这种行为被称为一致随机。
    random_score 函数会输出一个 0 到 1 之间的数,当种子 seed 值相同时,生成的随机结果是一致的。例如:

    GET /blogposts/post/_search
    {
      "query": {
        "function_score": {
          "query": {
            "multi_match": {
              "query":    "popularity",
              "fields": [ "title", "content" ]
            }
          },
          "random_score": {
            "seed":  "userID"
          }
        }
      }
    }
    

    使用每个用户的id作为seed传入,可以使每个用户的随机保持一致,同时在不同用户之间保持不同的随机性。

    时间、空间上的“越近越好”

    一般来说,搜索要求具有时间相关性。也就是说,用户只想看到时间较近的文档,而时间较远的文档,就算相关度较高,用户也不想看到。空间上也有相关的性质,比如以某个点为中心,周围一定距离以内的文档排在返回结果的前面,而超过一定距离的文档就算相关度较高也排在后面。elasticsearch提供了衰减函数,可以对文档的相关性按某个维度进行衰减。

    这里不能直接使用sort。因为如果直接使用sort,会让相关度较低的文档排在前面,维度近了但是相关度很差,达不到相关度较高、同时某个维度较 近 的需求。

    elasticsearch提供了三个衰减函数,分别是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 。
      衰减曲线

    比如,想要搜索某条博客,同时发表时间近的排在前面,引入基于时间的衰减函数。

    GET /blogposts/post/_search
    {
      "query": {
        "function_score": {
          "query": {
            "multi_match": {
              "query":    "popularity",
              "fields": [ "title", "content" ]
            }
          },
          "gauss" :{
              "timestamp": {
                "origin": "now timestamp",
                "offset": "5d",
                "scale": "10d"
              }
           }
        }
      }
    }
    

    按时间和地理空间做衰减,offset 和 scale 必须加上单位。
    这里我们确定以当前的时间为衰减函数的中心, 5 天以内的文档,相关度不做处理;5 天到 15 天,衰减系数逐渐降低到0.5;15 天之外,系数继降低。

    比如,想将地理空间和价格的影响引入搜索,可以这样实现:

    GET /_search
    {
      "query": {
        "function_score": {
          "functions": [
            {
              "gauss": {
                "location": {
                  "origin": { "lat": 51.5, "lon": 0.12 },
                  "offset": "2km",
                  "scale":  "3km"
                }
              }
            },
            {
              "gauss": {
                "price": { 
                  "origin": "50", 
                  "offset": "50",
                  "scale":  "20"
                }
              },
              "weight": 2
            }
          ]
        }
      }
    }
    

    这里地理空间类型,在衰减函数的参数里要加上单位,而普通的数值类型不用加单位。

    通过衰减函数,可以在相关度为主的前提下,引入时间、空间、数值等其他因素,从而影响搜索的返回结果。比起简单粗暴的sort,衰减函数可以保证相关度高的依然排在靠前的位置。

    终极武器script_score

    elasticsearch支持用户自己写groovy脚本,来自定义复杂的评分影响逻辑。因为日常中不太实用,同时脚本对性能影响较大,所以这里不做介绍。

    相关文章

      网友评论

          本文标题:elasticsearch——影响文档评分

          本文链接:https://www.haomeiwen.com/subject/vtxdjftx.html