目录

一、ES打分机制

1. TF-IDF

2. 其它打分方法

二、boosting

三、explain

四、再打分

五、function_score

六、使用脚本排序


《Elasticsearch In Action》学习笔记。

使得ES查询与select * from users where name like 'bob%'查询不同的是其为文档赋予相关性得分的能力。从这个得分,可以得知文档和原始的查询有多么相关。

一、ES打分机制

确定文档和查询有多么相关的过程被称为打分(scoring)。

1. TF-IDF

Lucene及其扩展ES默认使用TF-IDF算法计算文档得分。TF是词频,即term frequency,IDF是逆文档频率,即inverse document frequency。关于TF-IDF一个简短的解释是,一个词条出现在某个文档的次数越多,它就越相关。但是该词条出现在不同的文档的次数越多,它就越不相关。DF表示一个词条出现在多少篇文档中,而IDF则为1/DF。

Lucene默认的评分公式如下:

用自然语言描述该公式为:“给定查询 q 和文档 d,其得分是查询中每个词条 t 的得分总和。而每个词条的得分是该词条在文档 d 中的词频的平方根,乘以该词逆文档频率的平方和,乘以该文档字段的归一化因子,乘以该词条的提升权重。”

显然词条的词频越高,得分越高;相似地,索引中词条越罕见,逆文档频率越高。调和因子考虑了搜索过多少文档以及发现了多少词条。查询标准化是视图让不同查询的结果具有可比性。

2. 其它打分方法

ES支持的其它打分方法包括:

  • Okapi BM25
  • 随机性分歧(Divergence from randomness),即DFR相似度
  • 基于信息的(Information based),即IB相似度
  • LM Dirichlet相似度
  • LM Jelinek Mercer相似度

二、boosting

boosting是一个可以用来修改文档相关性的程序。用户可以在查询时使用boosting。需要注意的是,boost的数值并不是一个精确的乘数。这是指,在计算分数的时候boost数值是被标准化的。例如,如果为每个单独字段指定了10的boost,那么最终标准化后每个字段会获得1的值,也就意味着没有实施任何boost。应该考虑boost的相对数值,将name字段boost3倍意味着name字段的重要性大概是其它字段的3倍。

(1)查询期间的boosting
        可以使用基本的match、multi_match、simple_query_string或query_string查询,基于每个词条或者某个字段来控制boost。对查询进行boost意味着在所有的配置查询字段中,每个被发现的词条都会获得boost。

curl -XPOST "172.16.1.127:9200/get-together/_search?pretty" -H 'Content-Type: application/json' -d'
{"query": {"bool": {"should": [{"match": {"description": {"query": "elasticsearch big data","boost": 2.5}}},{"match": {"name": {"query": "elasticsearch big data"}}}]}}
}'

注意boost只是添加到第一个match查询。现在对于最终的得分而言,第一个match查询比第二个match查询拥有更大的影响力。当使用bool或and/or/not组合多个查询时,boost查询才有意义。

(2)跨越多个字段的查询
        对于跨越多个字段的查询,如multi_match,也可以使用多个替换的方法。用户可以指定整个multi_match的boost。

curl -XPOST "172.16.1.127:9200/get-together/_search?pretty" -H 'Content-Type: application/json' -d'
{"query": {"multi_match": {"query": "elasticsearch big data","fields": ["name","description"],"boost": 2.5}}
}'

也可以只对特定字段指定一个boost。

curl -XPOST "172.16.1.127:9200/get-together/_search?pretty" -H 'Content-Type: application/json' -d'
{"query": {"multi_match": {"query": "elasticsearch big data","fields": ["name^3",                               # 使用^3后缀,name字段被boost了3倍"description"]}}
}'

在query_string查询中,可以使用特殊的语法来boost单个词条。下面代码会搜索“elasticsearch”和“big data”,而“elasticsearch”被boost了3倍。

curl -XPOST "172.16.1.127:9200/get-together/_search?pretty" -H 'Content-Type: application/json' -d'
{"query": {"query_string": {"query": "elasticsearch^3 AND \"big data\""}}
}'

三、explain

explain包含了对得分的解释,从而了解为什么一篇文档获得了特定的得分,为什么一篇文档无法和某个查询匹配。

curl -XPOST "172.16.1.127:9200/get-together/_search?pretty" -H 'Content-Type: application/json' -d'
{"query": {"match": {"description": "elasticsearch"}},"explain": true
}'

下面来看看该请求返回的第一个结果:

  "hits" : {"total" : 9,"max_score" : 1.1171769,"hits" : [{"_shard" : "[get-together][1]","_node" : "yO9AEg-BTS20V9BhuEWeuA","_index" : "get-together","_type" : "_doc","_id" : "112","_score" : 1.1171769,"_routing" : "5","_source" : {"relationship_type" : {"name" : "event","parent" : "5"},"host" : "Dave Nolan","title" : "real-time Elasticsearch","description" : "We will discuss using Elasticsearch to index data in real time","attendees" : ["Dave","Shay","John","Harry"],"date" : "2013-02-18T18:30","location_event" : {"name" : "SkillsMatter Exchange","geolocation" : "51.524806,-0.099095"},"reviews" : 3},"_explanation" : {"value" : 1.1171769,"description" : "weight(description:elasticsearch in 13) [PerFieldSimilarity], result of:","details" : [{"value" : 1.1171769,"description" : "score(doc=13,freq=1.0 = termFreq=1.0\n), product of:","details" : [{"value" : 0.9614112,"description" : "idf, computed as log(1 + (docCount - docFreq + 0.5) / (docFreq + 0.5)) from:","details" : [{"value" : 6.0,"description" : "docFreq","details" : [ ]},{"value" : 16.0,"description" : "docCount","details" : [ ]}]},{"value" : 1.1620178,"description" : "tfNorm, computed as (freq * (k1 + 1)) / (freq + k1 * (1 - b + b * fieldLength / avgFieldLength)) from:","details" : [{"value" : 1.0,"description" : "termFreq=1.0","details" : [ ]},{"value" : 1.2,"description" : "parameter k1","details" : [ ]},{"value" : 0.75,"description" : "parameter b","details" : [ ]},{"value" : 16.6875,"description" : "avgFieldLength","details" : [ ]},{"value" : 11.0,"description" : "fieldLength","details" : [ ]}]}]}]}}

_explanation部分包含了对于文档得分的解释。这篇文档的最后得分是1.1171769。IDF值为0.9614112,标准化后的TF值为1.1620178,1.1620178 * 0.9614112 = 1.1171769。

下面看一个不匹配的例子:

curl -XPOST "172.16.1.127:9200/get-together/_doc/4/_explain?pretty" -H 'Content-Type: application/json' -d'
{"query": {"match": {"description": "elasticsearch"}}
}'

结果返回:

{"_index" : "get-together","_type" : "_doc","_id" : "4","matched" : false,"explanation" : {"value" : 0.0,"description" : "no matching term",         "details" : [ ]}
}

词条“elasticsearch”没有出现在ID为4的文档的description字段中,得分为0,解释了为什么这篇文档和查询没有匹配成功。

四、再打分

在下列情况下,打分可能会变成资源密集型的操作:

  • 使用脚本的评分,运行了一个脚本来计算索引中每篇文档的得分。这类似于SQL查询中使用UDF,每行数据都要执行函数。
  • 进行phrase词组查询,搜索在一定距离内出现的单词,使用很大的slop值。

在这些情况下,可能希望减轻打分算法所产生的性能影响。为解决这个问题,ES有一个特性称为再打分。再打分(rescoring)是指初始的查询运行后,针对返回的结果集进行第二轮的得分计算。例如,对于可能非常消耗性能的脚本查询,可以先使用更为经济的match匹配查询进行搜索,然后只对前1000项检索到的命中执行该脚本查询。下面是一个再打分的例子。

curl -XPOST "172.16.1.127:9200/get-together/_search?pretty" -H 'Content-Type: application/json' -d'
{"query": {"match": {                                  # 在所有文档上执行的初始查询"title": "elasticsearch"}},"rescore": {"window_size": 20,                          # 运行在再评分的数量"query": {"rescore_query": {"match_phrase": {                       # 在初始查询的前20项结果上运行的新查询"title": {"query": "elasticsearch hadoop","slop": 5}}},"query_weight": 0.8,                      # 初始查询得分的权重"rescore_query_weight": 1.3               # 再评分查询得分的权重}}
}'

这个例子搜索了所有标题中含有“elasticsearch”关键词的文档,然后对获取的前20项结果重新计算得分,它使用了高slop值的phrase查询。尽管高slop值的phrase查询是很耗性能的,但这个查询只会在前20篇文档上执行。用户可以使用query_weight和rescore_query_weight参数来权衡不同查询的重要性,这取决于希望最终的得分多少是由初始查询决定,多少是由再评分查询决定。用户可以按序使用多个rescore再评分查询,每个查询使用前面的结果作为输入。

五、function_score

function_score查询允许用户指定任何数量的任意函数,让它们作用于匹配了初始查询的文档,修改其得分,从而达到精细化控制结果相关性的目的。下面是一个尚未进行任何额外评分的例子:

curl -XPOST "172.16.1.127:9200/get-together/_search?pretty" -H 'Content-Type: application/json' -d'
{"query": {"function_score": {"query": {"match": {"description": "elasticsearch"}},"functions": []}}
}'

(1)weight函数
        weight函数将得分乘以一个常数。注意,普通的boost字段按照标准化来增加分数,而weight是真正将得分乘以确定的数值。下面的代码在初始查询得到的结果中,将description字段中包含“hadoop”的文档得分提升1.5倍。

curl -XPOST "172.16.1.127:9200/get-together/_search?pretty" -H 'Content-Type: application/json' -d'
{"query": {"function_score": {"query": {"match": {"description": "elasticsearch"}},"functions": [{"weight": 1.5,"filter": {"term": {"description": "hadoop"}}}]}}
}'

可以增加多个函数:

curl -XPOST "172.16.1.127:9200/get-together/_search?pretty" -H 'Content-Type: application/json' -d'
{"query": {"function_score": {"query": {"match": {"description": "elasticsearch"}},"functions": [{"weight": 2,"filter": {"term": {"description": "hadoop"}}},{"weight": 3,"filter": {"term": {"description": "logstash"}}}]}}
}'

(2)得分合并
        得分合并有以下两种情况:

  • 从每个单独的函数而来的得分是如何合并的,这被称为score_mode。
  • 从函数而来的得分是如何同原始查询得分合并的,这被称为boost_mode。

第一种情况处理不同函数得分如何合并。前面例子中有两个函数,一个权重为2,另一个权重是3。用户可以设置score_mode参数为multiply、sum、avg、first、max和min。如果没有特别指明,每个函数的得分是相乘的。

如果指定了first,只会考虑第一个拥有匹配过滤器的函数的分数。例如,如果将score_mode设置为first,并且有一篇文档的描述中有“hadoop”和“logstash”关键词,那么只会实施值为2的boost因子,因为这是第一个匹配文档的函数。

第二种得分合并的设置控制了原始查询的得分和函数得分是如何合并的。如果没有指定,新的得分是原始得分和函数得分相乘。用户可以将其设置为sum、avg、max、min或replace。设置为replace,意味着原有的查询得分将会被函数得分所替换。

(3)field_value_factor函数
        field_value_factor函数将包含数值的字段名称作为输入,选择性地将其值乘以常数,然后最终对其运用数学函数,如取数值的对数。

curl -XPOST "172.16.1.127:9200/get-together/_search?pretty" -H 'Content-Type: application/json' -d'
{"query": {"function_score": {"query": {"match": {"description": "elasticsearch"}},"functions": [{"field_value_factor": {"field": "reviews",                 # 用作数值的字段"factor": 2.5,                      # 评论字段将要乘以的因子"modifier": "ln",                   # 可选择的修饰符,用于计算得分"missing": 0.000001                 # 缺少评论字段时的缺省值}}]}}
}'

除ln外,其它修改函数包括:none(默认)、log、log1p、log2p、square、sqrt和reciprocal。field_value_factor将所有用户指定的字段值加载到内存中,因此可以很快计算出得分。这是字段数据的一部分。(4)脚本
        脚本评分可以让用户完全控制如何修改评分,用户可以在脚本中进行任何的排序。

curl -XPOST "172.16.1.127:9200/get-together/_mapping/_doc?pretty" -H 'Content-Type: application/json' -d'
{"properties": {"attendees": {"type": "text","fielddata": "true"}}
}'curl -XPOST "172.16.1.127:9200/get-together/_search?pretty" -H 'Content-Type: application/json' -d'
{"query": {"function_score": {"query": {"match": {"description": "elasticsearch"}},"functions": [{"script_score": {"script": {                         # 将在初始查询返回的每篇文档上运行的脚本"source": "if (doc[\u0027attendees\u0027].value != null) { Math.log(doc[\u0027attendees\u0027].values.size() * params.myweight)} else _score","params": {"myweight": 3}}}}],"boost_mode": "replace"                   # 初始的文档得分将会被脚本产生的得分所代替}}
}'

这个例子将使用参与者(attendees)列表的人数来影响得分,将其和权重相乘,并取对数。脚本比普通的评分操作要慢得多,原因是对于每篇匹配查询的文档而言,它们必须是动态执行的。

(5)随机
        random_score函数给予用户为文档指定随机分数的能力。用户可以选择性地指定种子(seed),这是一个传递给查询的数值,用于产生随机数。这一点可以让用户一随机的方式来排列文档,但是使用相同的随机种子,再次执行相同的请求时,结果排序将总是一样的。

curl -XPOST "172.16.1.127:9200/get-together/_search?pretty" -H 'Content-Type: application/json' -d'
{"query": {"function_score": {"query": {"match": {"description": "elasticsearch"}},"functions": [{"random_score": {"seed": 1234}}]}}
}'

(6)衰减函数
        衰减函数允许用户根据某个字段,应用一个逐步衰减的文档得分。有3种类型的衰减函数,即linear、gauss和exp。对于衰减函数,有以下4种配置选项。

  • origin:中心点,在这里用户希望分数是最高的。
  • offset:分数开始衰减的位置,和原点之间的距离。
  • scale和decay:这两个选项是密切合作的。通过设置它们,可以让字段值为指定的scale时,其得分减少到指定的decay。

下面是一个使用高斯衰减函数的例子。

curl -XPOST "172.16.1.127:9200/get-together/_search?pretty" -H 'Content-Type: application/json' -d'
{"query": {"function_score": {"query": {"match_all": {}},"functions": [{"gauss": {"location_event.geolocation": {"origin": "40.018528,-105.275806",     # 距离原点100米之内的位置,分数保持不变"offset": "100m",                      # 衰减开始距离原点的位置"scale": "2km",                        # 距离原点两公里的地方,分数被降低一半"decay": 0.5}}}]}}
}'

(7)综合示例
        下面展示一个查询,其中:

  • 特定的词条权重更高。
  • 考虑了评论数据。
  • 活动参与人数越多,排名越高。
  • 靠近某个地理位置的文档得以boost加权。
curl -XPOST "172.16.1.127:9200/get-together/_search?pretty" -H 'Content-Type: application/json' -d'
{"query": {"function_score": {"query": {"match_all": {}},"functions": [{"weight": 1.5,"filter": {"term": {"description": "hadoop"}}},{"field_value_factor": {"field": "reviews","factor": 10.5,"modifier": "log1p","missing": 0}},{"script_score": {"script": {"source": "if (doc[\u0027attendees\u0027].value != null) { Math.log(doc[\u0027attendees\u0027].values.size() * params.myweight)} else _score","params": {"myweight": 3}}}},{"gauss": {"location_event.geolocation": {"origin": "40.018528,-105.275806","offset": "100m","scale": "2km","decay": "0.5"}}}],"score_mode":"sum","boost_mode":"replace"}}
}'

在这个例子中包含了以下几点:

  • 由于使用了match_all查询,用户匹配了索引之中所有的文档。
  • 使用了weight函数,提升了描述中包含“hadoop”关键词的文档。
  • 通过field_value_factor函数,使用某个文档中的评论数量来修改得分。
  • 使用了script_score,将参与者的数量纳入考虑范围。
  • 使用了gausss衰减,对于离原点越来越远的点进行了分数的逐步衰减。

六、使用脚本排序

除了使用脚本来修改文档的得分,ES还允许使用脚本在文档返回前对其进行排序。当用户需要在某个不存在的文档字段上排序时,这一点非常有用。例如,在下面的例子中,搜索关于“elasticsearch”的文档,但想根据参与人数排序。

curl -XPOST "172.16.1.127:9200/get-together/_search?pretty" -H 'Content-Type: application/json' -d'
{"query": {"match": {"description": "elasticsearch"}},"sort": [{"_script": {"script": "doc[\u0027attendees\u0027].values.size()",    # 使用参与者的数量作为排序的值"type": "number",                                        # 该排序是一个数值型类型"order": "desc"                                          # 降序}},"_score"                                                     # 如果参与者数量相同,使用_score作为第二排序标准]
}'

触类旁通Elasticsearch:打分相关推荐

  1. 触类旁通Elasticsearch:扩展

    目录 一.添加节点 二.节点发现 1. 广播 2. 单播 3. 选举主节点 4. 错误识别 三.删除节点 1. 丢失节点 2. 停用节点 四.升级节点 五.使用_cat API 六.扩展策略 1. 过 ...

  2. 触类旁通Elasticsearch:关联

    目录 一.文档间关系概览 1. 对象类型 2. 嵌套类型 3. 父子关系 4. 反规范化 二.将对象最为字段值 1. 映射和索引对象 2. 搜索对象 三.嵌套类型 1. 映射并索引嵌套文档 2. 搜索 ...

  3. 触类旁通Elasticsearch:管理

    目录 一.模板 二.动态映射 三.分配感知 四.监控 1. 检查集群健康状况 2. 慢日志.热线程和线程池 3. 内存 4. 操作系统缓存 5. 存储限流 五.备份与恢复 1. 快照API 2. 将数 ...

  4. 触类旁通Elasticsearch:聚合

    目录 一.聚合的结构 二.度量集合 三.桶型聚合 四.嵌套聚合 1. 多桶聚合 2. 单桶聚合 <Elasticsearch In Action>学习笔记. 熟悉SQL的用户一定对聚合不会 ...

  5. 触类旁通Elasticsearch:原理

    目录 一.逻辑设计 1. 文档 2. 类型 3. 索引 二.物理设计 1. 节点 2. 主分片与副本分片 3. 分布式索引和搜索 三.索引数据 四.搜索数据 1. 在哪里搜索 2. 回复的内容 3. ...

  6. 触类旁通Elasticsearch:优化

    目录 一.合并请求 1. 批量操作(bulk) 2. 多条搜索和多条获取 二.优化Lucene分段的处理 1. refresh和flush 2. 合并以及合并策略 三.缓存 1. 过滤器和过滤器缓存 ...

  7. elasticsearch 打分 源码_Elasticsearch搜索之explain评分分析

    Lucene的IndexSearcher提供一个explain方法,能够解释Document的Score是怎么得来的,具体每一部分的得分都可以详细地打印出来.这里用一个中文实例来纯手工验算一遍Luce ...

  8. ElasticSearch之score打分机制原理

    文章目录 1. TF-IDF原理 1.1 计算公式 1.2 示例说明 1.2.1 计算TF 1.2.2 计算IDF 1.2.3 TF-IDF计算 2. Elasticsearch打分机制 2.1 示例 ...

  9. CSDN博文周刊第5期:狼性文化遭质疑,那我们当个佛系程序员可好?

    CSDN每周都会产生大量的博客文章,有一些优质的干货文章值得被更多人阅读,分享.CSDN博文周刊会从过去一周博文中精心挑选一些优质文章来以飨读者,陪伴大家度过一个愉快周末. 程序人生 1.狼性文化遭质 ...

最新文章

  1. HYSBZ 1010 玩具装箱toy (决策单调DP)
  2. 您操作系统的msxml组件版本过低_Fuchsia OS –未来的Google操作系统?
  3. 【STM32 + HAL库】倒立摆
  4. Zotero英文翻译插件安装教程
  5. python微信自动发送信息脚本
  6. linux枪战游戏,AstroMenace:好玩的太空射击游戏
  7. html5 游戏ui设计,7款手机游戏UI界面设计欣赏
  8. computed动态颜色
  9. 分享一个微信扫码连wifi项目
  10. 《无聊教程●第一课●教你编一个牛逼的VBScript邮件发送器》
  11. 什么是pv和uv? pv、uv
  12. 虚拟机下NAT 和 桥接模式 联网操作
  13. 【PHP】安装phpoffice/phpspreadsheet环境OneinStack安装ext-fileinfo这个扩展
  14. Matlab:在多行上延续长语句
  15. vue中使用svg画路径图
  16. python完成文件夹批量word转pdf文件及pdf文件合并+word文件合并
  17. 【优化求解】基于帝国主义竞争算法ICA求解单目标问题Matlab源码
  18. .msu格式文件跳过windowupdate检测直接安装方案(vs2015安装提示0x80240037安装失败,KB2999226无法安装)
  19. Jenkins教程(一)安装Jenkins
  20. 计算机数据管理的三个阶段包括,计算机数据管理技术的发展包括三个阶段

热门文章

  1. 动态时间规整算法(Dynamic Time Warping, DTW)之初探单词语音识别
  2. 【原创】QT 绘制饼图,曲线图,柱状图,详解。
  3. 推荐10个小众实用的办公工具,解决你的各种办公需求
  4. 工具使用 - 开发、办公工具分享
  5. spring到底是什么!新手必看
  6. 如何监控cpu温度(代替鲁大师) core Temp
  7. 提升引理:唯一提升与同伦提升
  8. 微信健康码快捷键设置
  9. java web 服务 构建与运行_Java Web 服务:构建与运行(影印版)
  10. Abaqus-python-二次开发学习笔记-单向长纤维RVE