一、function_score介绍

主要用于让用户自定义查询相关性得分,实现精细化控制评分的目的。

在ES的常规查询中,只有参与了匹配查询的字段才会参与记录的相关性得分score的计算。但很多时候我们希望能根据搜索记录的热度、浏览量、评分高低等来计算相关性得分,提高用户体验。

官网介绍:function_score

哪些信息是用户真正关心的?
搜索引擎本质是一个匹配过程,即从海量数据中找到匹配用户需求的内容。

在这里插入图片描述

除了根据用户输入的查询关键字去检索外,还应根据用户的使用习惯、浏览记录、最近关注、搜索记录的热度等进行更加智能化的匹配。

常见的一些场景:
1、在百度、谷歌中搜索内容;
2、在淘宝、京东上面搜索商品;
3、在抖音上搜索用户和短视频。

二、实战演示

1、创建索引

说明:创建博客blog索引,只有2个字段,博客名title和访问量access_num。
用户根据博客名称搜索的时候,既希望名称能尽可能匹配,也希望访问量越多的排在最前面,因为一般访问量越多的博客质量会越好,这样可以提高用户的检索体验。

DELETE /blog

PUT /blog
{  
  "mappings": {
     "properties": {
      "title": {
        "type": "text",
        "analyzer": "ik_max_word",
        "search_analyzer": "ik_smart"
      },
      "access_num": {
        "type": "integer"
      }
    }
  }
}

2、添加测试数据

PUT blog/_doc/2
{
  "title": "java入门到精通",
  "access_num":30
}

PUT blog/_doc/3
{
  "title": "es入门到精通",
  "access_num":50
}

PUT blog/_doc/4
{
  "title": "mysql入门到精通",
  "access_num":30
}

PUT blog/_doc/5
{
  "title": "精通spark",
  "access_num":40
}

3、常规检索

直接使用match查询,只会根据检索关键字和title字段值的相关性检索排序。

GET /blog/_search
{
    "query": {
       "match": {
                    "title": "java入门"
                }
    }
}

查询结果L

 "hits" : [
      {
        "_index" : "blog",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.3739232,
        "_source" : {
          "title" : "java入门",
          "access_num" : 20
        }
      },
      {
        "_index" : "blog",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 1.0552295,
        "_source" : {
          "title" : "java入门到精通",
          "access_num" : 30
        }
      },
      ……
     ] 

4、采用function_score自定义评分

除了match匹配查询计算相关性得分,还引入了根据浏览量access_num计算得分。

GET /blog/_search
{
    "query": {
        "function_score": {
            "query": {
                "match": {
                    "title": "java入门"
                }
            },
            "functions": [
                {
                    "script_score": {
                        "script": {
                            "params": {
                                "access_num_ratio": 2.5
                            },
                            "lang": "painless",
                            "source": "doc['access_num'].value * params.access_num_ratio "
                        }
                    }
                }
            ]
        }
    }
}

查询结果:
说明:尽管博客名为java入门的名称和搜索词更加匹配,但由于博客名为java入门到精通的博客访问量更高,最终检索品分更高,排名更靠前。

 "hits" : [
      {
        "_index" : "blog",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 79.14222,
        "_source" : {
          "title" : "java入门到精通",
          "access_num" : 30
        }
      },
      {
        "_index" : "blog",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 68.69616,
        "_source" : {
          "title" : "java入门",
          "access_num" : 20
        }
      },

三、自定义评分类型

function_score 查询提供了多种类型的评分函数。

  • script_score script脚本评分
  • weight 字段权重评分
  • random_score 随机评分
  • field_value_factor 字段值因子评分
  • decay functions: gauss, linear, exp 衰减函数

说明:
decay functions衰减函数太过复杂,这里暂时不作介绍。

1、script脚本评分

script_score 函数允许您包装另一个查询并选择性地使用脚本表达式从文档中的其他数字字段值派生的计算自定义它的评分。 这是一个简单的示例:

GET /_search
{
    "query": {
        "function_score": {
            "query": {
                "match": { "message": "elasticsearch" }
            },
            "script_score" : {
                "script" : {
                  "source": "Math.log(2 + doc['likes'].value)"
                }
            }
        }
    }
}

请注意,与 custom_score 查询不同,查询的分数乘以脚本评分的结果。 如果你想禁止这个,设置 “boost_mode”: “replace”

2、weight 权重评分

weight函数是最简单的分支,它将得分乘以一个常数。请注意,普通的boost字段按照标准化来增加分数。
而weight函数却真真切切地将得分乘以确定的数值。

下面的例子意味着,在description字段中匹配了hadoop词条查询的文档,他们的分数将被乘以1.5.

GET /_search
{
    "query": {
        "function_score": {
            "query": {
                "match": { "message": "elasticsearch" }
            },
            "functions":[
	             {
		            "weight":1.5  ,
		            "filter": { "term": { "description": "hadoop" }}   
	             },
	             {
		            "weight":3  ,
		            "filter": { "term": { "description": "flink" }}   
	             }
            ]
        }
    }
}

3、random_score随机评分

random_score 生成从 0 到但不包括 1 的均匀分布的分数。默认情况下,它使用内部 Lucene doc id 作为随机源。

如果您希望分数可重现,可以提供种子和字段。 然后将基于此种子、所考虑文档的字段最小值以及基于索引名称和分片 id 计算的盐计算最终分数,以便具有相同值但存储在不同索引中的文档得到 不同的分数。

请注意,位于同一个分片内且具有相同字段值的文档将获得相同的分数,因此通常希望使用对所有文档具有唯一值的字段。 一个好的默认选择可能是使用 _seq_no 字段,其唯一的缺点是如果文档更新,分数会改变,因为更新操作也会更新 _seq_no 字段的值。

GET /_search
{
    "query": {
        "function_score": {
            "random_score": {
                "seed": 10,
                "field": "_seq_no"
            }
        }
    }
}

4、field_value_factor 字段值因子评分

field_value_factor 函数允许您使用文档中的字段来影响分数。 它类似于使用 script_score 函数,但是,它避免了脚本的开销。 如果用于多值字段,则在计算中仅使用该字段的第一个值。

举个例子,假设你有一个用数字 likes 字段索引的文档,并希望用这个字段影响文档的分数,一个这样做的例子看起来像:

GET /_search
{
    "query": {
        "function_score": {
            "field_value_factor": {
                "field": "likes",
                "factor": 1.2,
                "modifier": "sqrt",
                "missing": 1
            }
        }
    }
}

得分计算公式: sqrt(1.2 * doc['likes'].value)

参数说明:

  • field

要从文档中提取的字段。

  • factor

与字段值相乘的可选因子,默认为 1。

  • modifier

应用于字段值的计算修饰符, none, log, log1p, log2p, ln, ln1p, ln2p, square, sqrt, or reciprocal,默认 none.

  • missing

如果文档没有该字段,则使用的值。 修饰符和因子仍然适用于它,就好像它是从文档中读取的一样。

5、Decay functions 衰减函数

衰减函数使用一个函数对文档进行评分,该函数根据文档的数字字段值与用户给定原点的距离而衰减。 这类似于范围查询,但具有平滑的边缘而不是框。

要对具有数字字段的查询使用距离评分,用户必须为每个字段定义原点和比例。 需要原点来定义计算距离的“中心点”,以及定义衰减率的比例尺。

好吧,一脸懵逼,这里就不继续介绍了。

放一个示例,大家有兴趣可以参考官方文档继续研究下。

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

四、合并得分

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
        }
    }
}

参数说明:

  • max_boost
    可以通过设置 max_boost 参数将新分数限制为不超过某个限制。 max_boost 的默认值是 FLT_MAX。

  • min_score
    默认情况下,修改分数不会更改匹配的文档。 要排除不满足某个分数阈值的文档,可以将 min_score 参数设置为所需的分数阈值。

参数 score_mode 指定如何组合计算的分数:

  • multiply 相乘 (default)

  • sum 求和

  • avg 平均分

  • first 使用具有匹配过滤器的第一个函数的得分

  • max 使用最高分

  • min 使用最低分

boost_mode定义新计算的分数与查询的分数相结合。 具体选项:

  • multiply 查询得分和函数得分相乘,默认

  • replace 仅使用函数得分,查询得分被忽略

  • sum 查询得分和函数得分求和

  • avg 查询得分和函数得分取平均值

  • max 取查询得分和函数得分的最大值

  • min 取查询得分和函数得分的最小值


总结

本文主要介绍了ES中自定义评分函数function_score的使用场景以及各种评分函数的用法。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐