同じ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