最近,在公司学习ES的使用,导师给了个题目,如何对一个文档先计算分数,用分数进行排序,在分数相同的情况下再按照别的字段(如时间)进行排序,为此,从来没接触过ES的我开启了艰难的学习之路
本文参考自 ES权威指南(中文版)
以下是目录:
- 1、相关性算分
- 2、function_score
- 3、多级排序
- 4、示例
相关性算分描述了一个文档和查询语句的匹配程度,ES会对每个查询到的文档进行自动打分,打分的本质就是排序,ES会默认按照_score进行降序排序,分数更高(匹配程度高)的文档将排在前面。
ES5之前默认使用TF-IDF算法进行计算,ES6之后开始使用它的改进版BM25算法,这两个算法大致都是通过计算词频和文档频率、逆文档频率等对文档进行相关性算分。
function_score用于计算文档相关分值,它会在查询结束后对没一个匹配的文档进行一系列的重打分操作,最后以生成的分数进行排序。
如果不想使用相关性算分,可以使用constant_score,此时所有分数均会被置为1.0,constant_score一般适用于结构化数据查询。
function_score提供了几种默认的计算分值的函数:
weight:设置权重,跟boost类似,区别在于weight的权重不会被规范化,当某个文档的weight为2时,最终结果就是_score * 2 ,一般来说都和filter一起使用。因为过滤器只会筛选出符合标准的文档,不会去计算文档的具体得分,所有符合条件的文档的分都是1,使用weight可以将分数替换为你想要的分数。
field_value_factor:允许使用文档中某些字段参与相关性算分
random_score: 随机得到0~1的分数
script_score: 通过自定义脚本计算分值
示例如下:
GET test/_search
{
"query":{
"function_score":{
"query":{
"terms":{
"system":[
"1",
"2"
]
}
},
"functions":[
{
"filter":{
"term":{
"system":"1"
}
},"weight":"1"
},
{
"filter":{
"term":{
"system":"2"
}
},"weight":"10"
}
]
}
}
}
上述示例中,将system值为1的文档分数设置为1,将system为2的文档分数设置为10,同时查询结果会按照分数的从高到低进行排序。
如果在这个时候使用sort对结果进行排序,sort默认不使用_score进行排序,如果想要强制使用相关性得分参与排序,需要将track_score设置为true
在使用sort进行多级排序的时候,会严格按照排序字段的先后顺序进行排序,结果集会先用第一字段进行排序,在第一字段相同的情况下再使用第二字段进行排序。
"sort":[
{
"age":{
"order":"desc"
}
},
{
"update_time":{
"order":"desc"
}
}
]
在上述示例中,会先按照年龄进行降序排序,在年龄相同的时候按照更新时间降序排序
4、示例由上面两个示例我们可以得知,使用weight和filter相结合可以跟据指定字段进行打分,使用sort(track_score = true)可以对文档进行多级排序
示例如下:
GET test/_search?track_score=true
{
"query":{
"function_score":{
"query":{
"terms":{
"system":[
"1",
"2"
]
}
},
"functions":[
{
"filter":{
"term":{
"system":"1"
}
},"weight":"1"
},
{
"filter":{
"term":{
"system":"2"
}
},"weight":"10"
}
]
}
},
"sort":[
{
"_score":{
"order":"desc"
}
},
{
"update_time":{
"order":"desc"
}
}
]
}
改示例会先对system属性为1、2的文档分别打分,对打完分的文档先按照分数降序排序,这里即把system为2的文档排在前面,如果分数相同的话,再按照更新时间进行排序,即可完成先打分后排序的操作
本文只讨论了一个方向,想要实现这样的功能还有很多方法,有关于文中具体的定义,大家可以去ES官方文档进行查看



