社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Elasticsearch

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

oddds • 4 年前 • 441 次点击  
阅读 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
 
441 次点击