Py学习  »  Elasticsearch

通过Function Score Query优化Elasticsearch搜索结果

oddds • 3 年前 • 294 次点击  
阅读 70

通过Function Score Query优化Elasticsearch搜索结果

在使用 Elasticsearch 进行全文搜索时,搜索结果默认会以文档的相关度进行排序,如果想要改变默认的排序规则,也可以通过sort指定一个或多个排序字段。

但是使用sort排序过于绝对,它会直接忽略掉文档本身的相关度(根本不会去计算)\color{red}{但是使用sort排序过于绝对,它会直接忽略掉文档本身的相关度(根本不会去计算)}。在很多时候这样做的效果并不好,这时候就需要对多个字段进行综合评估,得出一个最终的排序。

在 Elasticsearch 中function_score是用于处理文档分值的 DSL,它会在查询结束后对每一个匹配的文档进行一系列的重打分操作,最后以生成的最终分数进行排序。它提供了几种默认的计算分值的函数:

  • weight:设置权重

  • field_value_factor:将某个字段的值进行计算得出分数。

  • random_score:随机得到 0 到 1 分数

  • 衰减函数:同样以某个字段的值为标准,距离某个值越近得分越高

  • 它还有一个属性boost_mode可以指定计算后的分数与原始的_score如何合并,有以下选项:

    • multiply:将结果乘以_score
    • sum:将结果加上_score
    • min:取结果与_score的较小值
    • max:取结果与_score的较大值
    • replace:使结果替换掉_score

weight

weight 的用法最为简单,只需要设置一个数字作为权重,文档的分数就会乘以该权重。

他最大的用途应该就是和过滤器一起使用了,因为过滤器只会筛选出符合标准的文档,而不会去详细的计算每个文档的具体得分,所以只要满足条件的文档的分数都是 1,而 weight 可以将其更换为你想要的数值。

field_value_factor

  • field:指定字段名
  • factor:对字段值进行预处理,乘以指定的数值(默认为 1)
  • modifier:将字段值进行加工
    • log:计算对数
    • log1p:先将字段值 +1,再计算对数
    • log2p:先将字段值 +2,再计算对数
    • square:计算平方
    • sqrt:计算平方根
    • reciprocal:计算倒数

举一个简单的例子,假设有一个商品索引,搜索时希望在相关度排序的基础上,销量(sales)更高的商品能排在靠前的位置,那么这条查询 DSL 可以是这样的。结果是:_score = _score + log (1 + 0.1 * sales)

{
  "query": {
    


    
"function_score": {
      "query": {
        "match": {
          "title": "雨伞"
        }
      },
      "field_value_factor": {
        "field": "sales",
        "modifier": "log1p",
        "factor": 0.1
      },
      "boost_mode": "sum"
    }
  }
}
复制代码

衰减函数

衰减函数(Decay Function)提供了一个更为复杂的公式,它描述了这样一种情况:对于一个字段,它有一个理想的值,而字段实际的值越偏离这个理想值(无论是增大还是减小),就越不符合期望。这个函数可以很好的应用于数值、日期和地理位置类型\color{red}{应用于数值、日期和地理位置类型},由以下属性组成:

  • 原点(origin):该字段最理想的值,这个值可以得到满分(1.0)
  • 偏移量(offset):与原点相差在偏移量之内的值也可以得到满分
  • 衰减规模(scale):当值超出了原点到偏移量这段范围,它所得的分数就开始进行衰减了,衰减规模决定了这个分数衰减速度的快慢
  • 衰减值(decay):该字段可以被接受的值(默认为 0.5),相当于一个分界点,具体的效果与衰减的模式有关

衰减函数还可以指定三种不同的模式:线性函数(linear)、以 e 为底的指数函数(Exp)和高斯函数(gauss),它们拥有不同的衰减曲线:

举一个简单的例子。我们希望租房的位置在(40, 116)坐标附近,5km以内是满意的距离,15km以内是可以接受的距离。

{
  "query": {
    "function_score": {
      "query": {
        "match": {
          "title": "公寓"
        }
      },
      "gauss": {
        "location": {
          "origin": { "lat": 40, "lon": 116 },
          "offset": "5km",
          "scale": "10km"
           }
         },
         "boost_mode": "sum"
    }
  }
}
复制代码

同时使用多个函数

使用functions属性指定多个函数。它是一个数组,所以原有函数不需要发生改动。同时还可以通过score_mode指定各个函数分值之间的合并处理,值跟最开始提到的boost_mode相同。

例如在大众点评中,应用希望向用户推荐一些不错的餐馆,特征是:范围要在当前位置的 5km 以内,有停车位是最重要的,有 Wi-Fi 更好,餐厅的评分(1 分到 5 分)越高越好,并且对不同用户最好展示不同的结果以增加随机性。

这样一个饭馆的最高得分应该是 2 分(有停车位)+ 1 分(有 wifi)+ 6 分(评分 5 分 * 1.2)+ 1 分(随机评分)。

{
  "query": {
    "function_score": {
      "filter": {
        "geo_distance": {
          "distance": "5km",
          "location": {
            "lat": $lat,
            "lon": $lng
          }
        }
      },
      "functions": [
        {
          "filter": {
            "term": {
              "features": "wifi"
            }
          },
          "weight": 1
        },
        {
          "filter": {
            "term": {
              "features": "停车位"
            }
          },
          "weight": 2
        },
        {
            "field_value_factor": {
               "field": "score",
               "factor": 1.2
             }
        },
        {
          "random_score": {
            "seed": "$id"
          }
        }
      ],
      "score_mode": "sum",
      "boost_mode": "multiply"
    }
  }
}
复制代码
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/108014
 
294 次点击