关于后端:ES-自定义打分-Function-score-query

12次阅读

共计 5778 个字符,预计需要花费 15 分钟才能阅读完成。

ES 自定义打分 Function score query

ES 会为 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)

本文读到这,你应该曾经对自定义打分的过程有了一个根本印象(query 原始分数、自定义函数得分、最终后果 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 查问完结后,对每一个匹配的文档进行从新算分。<\b>

为了排除掉一些分数太低的后果,咱们能够通过 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)"
        }
      }
    }
  }
}

结语

通过理解 Elasticsearch 的自定义打分置信你能更好的实现合乎业务的综合性搜寻。

正文完
 0