UGA Boxxx

つぶやきの延長のつもりで、知ったこと思ったこと書いてます

【Elasticsearch】同じdocのはずがscoreが違う謎

同じ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" : [ ]
              }
            ]
          },
         ...

違いはdocFreqdocFreqの数で、これによりスコアが違うみたい

つまり、計算に使うドキュメントの数が違う

計算に使うドキュメントはプライマリシャードにある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