GET my_index/_search
{
"explain": true,
"query":{
"multi_match": {
"fields": [
"name^3.87",
"name_native^4.12",
"address_native^0.75",
"street^0.50",
],
"query": "jinshan Hotel",
"tie_breaker": 0.12
}
}
}面对这样一个常见的query,ElasticSearch是如何执行Query?我们只知道它是要在Fields数组里的这些字段搜索“jinshan Hotel”,但是ElasticSearch是如何找出相关的doc并且评分返回最相关的docs?
这个Query,ElasticSearch的执行可以分为两步
Boolean用于找到相关的doc,这里分为两步:
ElasticSearch默认的score函数是采用TFIDF,它是两个值TF和IDF,最后得分就是两者的乘积:
最后的公式就是TF=(freq * (k1 + 1)) / (freq + k1 * (1 - b + b * fieldLength / avgFieldLength))。
最后的公式就是IDF=log(1 + (docCount - docFreq + 0.5) / (docFreq + 0.5))
计算得分是基于目前shard级别,最后合并的机器只是做分数的统一排序
具体事例如下:
有一个index一共有5个shard,里面有9个doc,具体分布为

执行Query如下
GET /my_index/_search
{
"explain": true,
"query": {
"match": {
"text": "fox"
}
}
}返回结果
{
"took": 3,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 5,
"max_score": 1.3857586,
"hits": [
{
"_shard": "[my_index][1]",这个doc在第1个shard
"_node": "h9ZfNNHpSDCx8hNTcSlGxQ",
"_index": "my_index",
"_type": "doc",
"_id": "9",
"_score": 1.3857586,
"_source": {
"text": "hello fox"
},
"_explanation": {
"value": 1.3857586,
"description": "weight(text:fox in 0) [PerFieldSimilarity], result of:",
"details": [
{
"value": 1.3857586,
"description": "score(doc=0,freq=1.0 = termFreq=1.0\n), product of:",
"details": [
{
"value": 0.98082924,
"description": "idf, computed as log(1 + (docCount - docFreq + 0.5) / (docFreq + 0.5)) from:",
"details": [
{
"value": 1,
"description": "docFreq",第一个shard里出现fox的doc只有1个
"details": []
},
{
"value": 3,
"description": "docCount",第一个shard里一共有3个doc
"details": []
}
]
},
{
"value": 1.412844,
"description": "tfNorm, computed as (freq * (k1 + 1)) / (freq + k1 * (1 - b + b * fieldLength / avgFieldLength)) from:",
"details": [
{
"value": 1,
"description": "termFreq=1.0", 在这个shard里fox这个关键字这个Field里出现了1次
"details": []
},
{
"value": 1.2,
"description": "parameter k1", 常量
"details": []
},
{
"value": 0.75,
"description": "parameter b", 常量
"details": []
},
{
"value": 7,
"description": "avgFieldLength", 在这个shard里这个field的平均term数,在这个shard里有三个doc,第一个doc有一个term,第二个doc有18个term,第三个doc有2个term,平均下来这个field的长度就是(1+18+2)/3=7
"details": []
},
{
"value": 2,
"description": "fieldLength", 当前doc所包含term数2个
"details": []
}
]
}
]
}
]
}
},
{
"_shard": "[my_index][3]", 这个doc在第3个shard
"_node": "h9ZfNNHpSDCx8hNTcSlGxQ",
"_index": "my_index",
"_type": "doc",
"_id": "1",
"_score": 0.5754429,
"_source": {
"text": "quick brown fox"
},
"_explanation": {...}
},
{
"_shard": "[my_index][4]", 这个doc在第4个shard上
"_node": "h9ZfNNHpSDCx8hNTcSlGxQ",
"_index": "my_index",
"_type": "doc",
"_id": "3",
"_score": 0.45207188,
"_source": {
"text": "fox fox fox"
},
"_explanation": {...}
},
{
"_shard": "[my_index][2]", 这个doc在第2个shard
"_node": "h9ZfNNHpSDCx8hNTcSlGxQ",
"_index": "my_index",
"_type": "doc",
"_id": "4",
"_score": 0.4471386,
"_source": {
"text": "fox is unlucky"
},
"_explanation": {...}
},
{
"_shard": "[my_index][2]",这个doc在第2个shard
"_node": "h9ZfNNHpSDCx8hNTcSlGxQ",
"_index": "my_index",
"_type": "doc",
"_id": "2",
"_score": 0.3901917,
"_source": {
"text": "I am a fox"
},
"_explanation": {...}
}
]
}
}上面是搜索关键字分词之后只有一个term,如果分词之后有多个term是如何打分的呢?
其实很简单,就是针对每个term重复上面的操作,然后将两个分数加起来。
GET /my_index/_search
{
"explain": true,
"query": {
"match": {
"text": "fox hello"
}
}
}现在搜索的term有两个,一个是fox,另外一个是hello
"_explanation": {
"value": 1.4435054,
"description": "sum of:",
"details": [每个搜索term的得分
{
"value": 0.9530774,
"description": "weight(text:fox in 0) [PerFieldSimilarity], result of:", fox的得分
"details": [
{
"value": 0.9530774,
"description": "score(doc=0,freq=1.0 = termFreq=1.0\n), product of:",
"details": [
{
"value": 0.6931472,
"description": "idf, computed as log(1 + (docCount - docFreq + 0.5) / (docFreq + 0.5)) from:",
"details": [
{
"value": 2,
"description": "docFreq",
"details": []
},
{
"value": 4,
"description": "docCount",
"details": []
}
]
},
{
"value": 1.375,
"description": "tfNorm, computed as (freq * (k1 + 1)) / (freq + k1 * (1 - b + b * fieldLength / avgFieldLength)) from:",
"details": [
{
"value": 1,
"description": "termFreq=1.0",
"details": []
},
{
"value": 1.2,
"description": "parameter k1",
"details": []
},
{
"value": 0.75,
"description": "parameter b",
"details": []
},
{
"value": 6,
"description": "avgFieldLength",
"details": []
},
{
"value": 2,
"description": "fieldLength",
"details": []
}
]
}
]
}
]
},
{
"value": 0.49042803,
"description": "weight(text:hello in 0) [PerFieldSimilarity], result of:", hello的得分
"details": [
{
"value": 0.49042803,
"description": "score(doc=0,freq=1.0 = termFreq=1.0\n), product of:",
"details": [
{
"value": 0.35667494,
"description": "idf, computed as log(1 + (docCount - docFreq + 0.5) / (docFreq + 0.5)) from:",
"details": [
{
"value": 3,
"description": "docFreq",
"details": []
},
{
"value": 4,
"description": "docCount",
"details": []
}
]
},
{
"value": 1.375,
"description": "tfNorm, computed as (freq * (k1 + 1)) / (freq + k1 * (1 - b + b * fieldLength / avgFieldLength)) from:",
"details": [
{
"value": 1,
"description": "termFreq=1.0",
"details": []
},
{
"value": 1.2,
"description": "parameter k1",
"details": []
},
{
"value": 0.75,
"description": "parameter b",
"details": []
},
{
"value": 6,
"description": "avgFieldLength",
"details": []
},
{
"value": 2,
"description": "fieldLength",
"details": []
}
]
}
]
}
]
}
]
}如果是搜索term里某个term出现多次
GET /my_index/_search
{
"explain": true,
"query": {
"match": {
"text": "fox hello hello"
}
}
}也是求和 score(fox)+2*score(hello)
"_explanation": {
"value": 2.012745,
"description": "sum of:",
"details": [
{
"value": 0.87138504,
"description": "weight(text:fox in 0) [PerFieldSimilarity], result of:",
"details": [
{
"value": 0.87138504,
"description": "score(doc=0,freq=1.0 = termFreq=1.0\n), product of:",
"details": [
{
"value": 0.6931472,
"description": "idf, computed as log(1 + (docCount - docFreq + 0.5) / (docFreq + 0.5)) from:",
"details": [
{
"value": 2,
"description": "docFreq",
"details": []
},
{
"value": 4,
"description": "docCount",
"details": []
}
]
},
{
"value": 1.2571429,
"description": "tfNorm, computed as (freq * (k1 + 1)) / (freq + k1 * (1 - b + b * fieldLength / avgFieldLength)) from:",
"details": [
{
"value": 1,
"description": "termFreq=1.0",
"details": []
},
{
"value": 1.2,
"description": "parameter k1",
"details": []
},
{
"value": 0.75,
"description": "parameter b",
"details": []
},
{
"value": 6,
"description": "avgFieldLength",
"details": []
},
{
"value": 3,
"description": "fieldLength",
"details": []
}
]
}
]
}
]
}所以假设一个search keywords是A B C C,那这个doc的最终得分就是TFIDF(A)+TFIDF(B)+2*TFIDF(C)
如果针对多个Field进行打分,就看这多个Field是如何组合方式?
比如是multimatch,那就是选择Field得分最高的分数作为这个doc的分数
从上面看到所有的得分都是shard级别,也就是说最后的排序并不是按照全局得分进行排序,而是按照shard得分进行排序的。这就会带来一个问题,shard得分比较高,但是全局来看并不高,这种doc还会在前面。比如上面的布局
GET my_index/_search
{
"explain": true,
"query":{
"match": {
"text": "fox"
}
}
}得分最高的是“hello fox”,而不是“fox fox fox”。从全局来看后者得分应该更高,但是在shard得分里“hello fox”的得分会更高。
ElasticSearch是在shard级别找到符合search term的doc,然后计算search term在各个Field的TFIDF值,然后根据query条件计算出最终得分。再又合并的机器将所有shard结果进行合并并且根据各自的TFIDF值进行排序返回给用户。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/31509949/viewspace-3156701/ ,如需转载,请注明出处,否则将追究法律责任。