同じdocのはずがscoreが違う謎の事象と遭遇した
具体的には
tweetインデックスに以下のdocが入っている
| id | user | message | likes |
|---|---|---|---|
| 1 | Aさん | elasticsearch | 14 |
| 2 | Bさん | elasticsearch | 10 |
| 3 | Cさん | elasticsearch | 12 |
| 4 | Aさん | elasticsearch | 10 |
| 5 | Bさん | elasticsearch | 14 |
これに対して、messageが「elasticsearch」のものを検索する
GET tweet/item/_search
{
"query": {
"match_phrase": {
"message": "elasticsearch"
}
}
}
全てメッセージは「elasticsearch」なので、検索結果は全て同じスコアを期待した
しかし、結果はスコアにバラ付きがあった
{
"_index" : "tweet",
"_type" : "item",
"_id" : "3",
"_score" : 0.2876821,
"_source" : {
"id" : "3",
"message" : "elasticsearch",
"user" : "Cさん",
"likes" : 12
}
},
{
"_index" : "tweet",
"_type" : "item",
"_id" : "5",
"_score" : 0.18232156,
"_source" : {
"id" : "5",
"message" : "elasticsearch",
"user" : "Bさん",
"likes" : 14
}
},
...
Cさんがスコア高い理由が全くわからなかった
原因はプライマリシャード数
どういうロジックでスコアを出しているかを_explainを使ってみてみる
GET tweet/item/3/_explain
{
"query": {
"match_phrase": {
"message": "elasticsearch"
}
}
}
スコアが高いid=3の場合
...
"details" : [
{
"value" : 0.2876821,
"description" : "idf, computed as log(1 + (docCount - docFreq + 0.5) / (docFreq + 0.5)) from:",
"details" : [
{
"value" : 1.0,
"description" : "docFreq",
"details" : [ ]
},
{
"value" : 1.0,
"description" : "docCount",
"details" : [ ]
}
]
},
...
id=5の場合
...
"details" : [
{
"value" : 0.18232156,
"description" : "idf, computed as log(1 + (docCount - docFreq + 0.5) / (docFreq + 0.5)) from:",
"details" : [
{
"value" : 2.0,
"description" : "docFreq",
"details" : [ ]
},
{
"value" : 2.0,
"description" : "docCount",
"details" : [ ]
}
]
},
...
違いはdocFreqとdocFreqの数で、これによりスコアが違うみたい
つまり、計算に使うドキュメントの数が違う
計算に使うドキュメントはプライマリシャードにあるdocが関係あるため、シャードとdoc数をGET _cat/shardで確認してみる
tweet 4 r STARTED 1 5.2kb 10.174.0.37 staylist-elasticsearch-wp0f tweet 4 p STARTED 1 5.2kb 10.174.0.33 staylist-elasticsearch-hf8c tweet 2 r STARTED 2 15.2kb 10.174.0.32 staylist-elasticsearch-45k1 tweet 2 p STARTED 2 15.2kb 10.174.0.37 staylist-elasticsearch-wp0f tweet 1 p STARTED 2 10.2kb 10.174.0.32 staylist-elasticsearch-45k1 tweet 1 r STARTED 2 10.2kb 10.174.0.37 staylist-elasticsearch-wp0f tweet 3 p STARTED 2 10.2kb 10.174.0.32 staylist-elasticsearch-45k1 tweet 3 r STARTED 2 10.2kb 10.174.0.33 staylist-elasticsearch-hf8c tweet 0 p STARTED 0 261b 10.174.0.32 staylist-elasticsearch-45k1 tweet 0 r STARTED 0 261b 10.174.0.33 staylist-elasticsearch-hf8c
左からindex名、シャード番号、レプリカorプライマリ、状態、doc数、、、
これをみると、確かにシャードごとにdoc数が違う
つまり原因はプライマリシャードが5つあり、docがそのいずれかのシャードに振り分けられ、それぞれで計算を行うことが原因みたい
解決法
シャード数は初期設定からなにもしないと、プライマリシャード5、レプリカシャード5になるので、templateを定義してあげる必要がある
PUT _template/tweet-template
{
"index_patterns": "tweet-*",
"order": 1,
"settings": {
"number_of_shards": 1,
"number_of_replicas": 2
},
"mappings": {
....
これによりプライマリシャード1、レプリカシャード2になる
doc数も均等になり、スコアも同じになった
tweet-1 0 r STARTED 5 20.8kb 10.174.0.32 staylist-elasticsearch-45k1 tweet-1 0 p STARTED 5 20.8kb 10.174.0.37 staylist-elasticsearch-wp0f tweet-1 0 r STARTED 5 20.8kb 10.174.0.33 staylist-elasticsearch-hf8c