0%

elasticsearch(十)相关性得分及分值排序优化

relevance score(相关性得分)

TF/IDF

  • TF(term frequency): 关键词在每个doc中出现的次数
  • IDF(inverse doc frequency): 关键词在整个索引中出现的次数

计算规则

​ TF数值大的得分较高,因为关键词在一个文档出现的次数多,证明该文档的相关性更大

​ IDF数值大的得分较低,关键词在整个倒排索引中出现的次数越多,关联的文档越多,证明其相关越低

​ relevance score(相关性得分):每个query的分数,乘以matched query数量,除以总query数量

  1. 它会执行 should 语句中的两个查询。
  2. 两个查询的评分相加。
  3. 乘以匹配语句的总数。
  4. 除以所有语句总数

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{"match": {"name": "吃鸡手机"}},
doc1: 吃鸡1次,手机1次,计2分
doc2: 吃鸡0次,手机1次,计1分
doc3: 吃鸡0次,手机1次,计1分
{"match": {"desc": "吃鸡手机"}}
doc1: 吃鸡0次,手机0次,计0分
doc2: 吃鸡0次,手机1次,计1分
doc3: 吃鸡0次,手机1次,计1分

总分:(query1分数+query2分数)*matched query / total query

doc1: query1+query2:2 matched:1 total query:2 result:2*1/2=1
doc2: query1+query2:2 matched:2 total query:2 result:2*2/2=2
doc3: query1+query2:2 matched:2 total query:2 result:2*2/2=2

matched query数量 = 2
总query数量 = 2

总结:总分 =(query1分数+query2分数)*matched query / total query

shard local idf和global idf

es的每个shard只保存一部分的doc数据,当我们的查询路由到某个shard上进行查询时,只在该shard上计算idf,即shard local idf;如果不同的shard存在的文档数量不同,出现不同的shard local idf,在差距较大的情况下会影响分值的计算,影响排序结果。

解决:

  1. 一般来说,生产环境下,数据量大时尽可能实现均匀分配,一定程度是哪个避免多个shard之间出现shard local idf差距过大的情况
  2. 搜索附带search_type=dfs_query_then_fetch参数,当计算一个doc的相关度分数的时候,就会将所有shard对应的local IDF获取出来,在本地进行global IDF分数的计算,所有shard的doc作为上下文来进行计算确保准确性,生产环境下,不推荐这个参数,因为性能很差。

排序规则优化

dix_max

Disjunction Max Query,将任何与任一查询匹配的文档作为结果返回,但只将最佳匹配的评分作为查询的评分结果返回 。

tie_breaker:取值范围 [0,1],其中 0 代表使用 dis_max 最佳匹配语句的普通逻辑,1表示所有匹配语句同等重要。最佳的精确值需要根据数据与查询调试得出,但是合理值应该与零接近(处于 0.1 - 0.4 之间),这样就不会颠覆 dis_max 最佳匹配性质的根本。

boost:设置分值比例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#查询匹配的文档,被更多term命中的feild查询语句作为查询评分结果(默认使用best_fields)
GET product/_search
{
"query": {
"dis_max": {
"queries": [
{"match": {"name": "超级快充"}},
{"match": {"desc": "超级快充"}}
]
}
}
}

GET product/_search
{
"query": {
"dis_max": {
"queries": [
{"match": {"name": "超级快充"}},
{"match": {"desc": "超级快充"}}
],
"boost": 2.0,
"tie_breaker": 0.7
}
}
}

multi_match

type属性

best_fields(默认)

​ 多个字段进行查询匹配时,返回评分最高的字段评分作为查询结果。类似:dis_max query。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
POST product/_search
{
  "query": {
    "multi_match": {
      "type""best_fields",
      "query""超级快充",
      "fields": [
        "name",
        "desc"
      ],
      "tie_breaker"0.2
    }
  }

#dis_max query查询与上述best_fields等价
POST product/_search
{
  "query": {
    "dis_max": {
      "queries": [
        {
          "match": {
            "name""超级快充"
          }
        },
        {
          "match": {
            "desc""超级快充"
          }
        }
      ],
      "tie_breaker"0.2
    }
  }
}
most_fields

​ 多个字段进行查询匹配时,多个字段的评分一起参与计算,返回的综合评分(非最高分)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
GET /product/_search
{
  "query": {
    "multi_match": {
      "query""超级快充",
      "type""most_fields",
      "fields": [
        "name^10",
        "desc"
      ]
    }
  }
}

#与上面的most_fields等价
GET product/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "name": {
              "query""超级快充",
              "boost"10
            }
          }
        },
        {
          "match": {
            "desc""超级快充"
          }
        }
      ]
    }
  }
}
cross_fields

​ 跨字段匹配与operator指令结合操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
#查询的关键词的term必须在name and desc中出现
#例如,超级必须在name或desc中出现 and 快充必须在name或desc中出现
GET product/_search
{
"query": {
"multi_match" : {
"query": "超级 快充",
"type": "cross_fields",
"fields": [ "name", "desc" ],
"operator": "and"
}
}
}

function_score

必须定义一个查询和一个或多个函数,自定义函数会为查询返回的每个文档计算一个新分数

​ field:要计算的字段

​ factor:当前分数计算,对整个结果产生的权重比

​ modifier:以何种运算方式计算,接受以下枚举

  • none:不处理
  • log:计算对数
  • log1p:先将字段值 +1,再计算对数
  • log2p:先将字段值 +2,再计算对数
  • ln:计算自然对数
  • ln1p:先将字段值 +1,再计算自然对数
  • ln2p:先将字段值 +2,再计算自然对数
  • square:计算平方
  • sqrt:计算平方根
  • reciprocal:计算倒数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#默认分值与指定field字段数值相乘得出分数、根据modifier指定的运算方式处理、factor权重比、max_boost表示最大的分值、boost_mode指定计算后的分数与原始的_score如何合并
GET /person/_search
{
"query": {
"function_score": {
"query": {
"match_all": {}
},
"field_value_factor": {
"field": "age",
"modifier": "none",
"factor": 1.2
},
"max_boost": 10,
"boost_mode": "multiply"
}
}
}

boost_mode:指定计算后的分数与原始的_score如何合并,有以下选

  • multiply:查询分数和函数分数相乘

  • sum:查询分数和函数分数相加

  • avg:取平均值

  • replace:替换原始分数

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

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

    script_score:通过自定义脚本计算分值

max_boost:分数上限

random_score:随机得到 0 到 1 分数

term vectors-term向量指标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#查询指定内容对应字段的文档term向量指标
GET /test_vector/_termvectors
{
"doc": {
"content":"you can kill"
},
"offsets": true,
"payloads": true,
"positions": true,
"term_statistics": true,
"field_statistics": true
}
#查询指定字段的文档term向量指标
GET /test_vector/_termvectors/1
{
"fields": ["content"],
"offsets": true,
"payloads": true,
"positions": true,
"term_statistics": true,
"field_statistics": true
}