Percolator QueryのIDFのパラメータが必ず1になってしまう


(Po3rin) #1

基本的な質問なのですが、Percolator Queryのidf計算において必ずdocFreqとdocCountが両方とも1になってしまいます。これをindexのドキュメント全体に対しての値に変更することは可能ですが?というのも、Percolator QueryのscoreとSearch API のスコアのソート結果を一致させたいという背景がある為です。ElasticsearchのバージョンはElasticsearch5.6と6.6で試しました。シャード数5、クラスター数は3です。

index作成

PUT /my-index
{
    "mappings": {
        "article": {
            "properties": {
                "message": {
                    "type": "text"
                },
                "query": {
                    "type": "percolator"
                }
            }
        }
    }
}

percolator登録

PUT /my-index/article/1?refresh
{
    "query" : {
        "match" : {
            "message" :  "fox"
        }
    }
}

データ投入

PUT /my-index/article/2
{
  "message" : "quick brown fox"
}
PUT /my-index/article/3
{
  "message" : "quick fox"
}
PUT /my-index/article/4
{
  "message" : "brown"
}

Percolator Query

GET /my-index/_search
{
    "explain": true,
    "query" : {
        "percolate" : {
            "field" : "query",
            "index" : "my-index",
            "type" : "article",
            "id" : "2"
        }
    }
}

Percolator Query response

{
  "took" : 30,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 0.2876821,
    "hits" : [
      {
        "_shard" : "[my-index][1]",
        "_node" : "poVG6FmxSXGB792tjGkoLA",
        "_index" : "my-index",
        "_type" : "article",
        "_id" : "5",
        "_score" : 0.2876821,
        "_source" : {
          "query" : {
            "match" : {
              "message" : "fox"
            }
          }
        },
        "fields" : {
          "_percolator_document_slot" : [
            0
          ]
        },
        "_explanation" : {
          "value" : 0.2876821,
          "description" : "PercolateQuery",
          "details" : [
            {
              "value" : 0.2876821,
              "description" : "weight(message:fox in 0) [BM25Similarity], result of:",
              "details" : [
                {
                  "value" : 0.2876821,
                  "description" : "score(doc=0,freq=1.0 = termFreq=1.0\n), product of:",
                  "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" : [ ]
                        }
                      ]
                    },
                    {
                      "value" : 1.0,
                      "description" : "tfNorm, computed as (freq * (k1 + 1)) / (freq + k1 * (1 - b + b * fieldLength / avgFieldLength)) from:",
                      "details" : [
                        {
                          "value" : 1.0,
                          "description" : "termFreq=1.0",
                          "details" : [ ]
                        },
                        {
                          "value" : 1.2,
                          "description" : "parameter k1",
                          "details" : [ ]
                        },
                        {
                          "value" : 0.75,
                          "description" : "parameter b",
                          "details" : [ ]
                        },
                        {
                          "value" : 3.0,
                          "description" : "avgFieldLength",
                          "details" : [ ]
                        },
                        {
                          "value" : 3.0,
                          "description" : "fieldLength",
                          "details" : [ ]
                        }
                      ]
                    }
                  ]
                }
              ]
            }
          ]
        }
      }
    ]
  }
}

普通にSearch API でpercolatorと同じクエリを使った際にはdocFreqとdocCountは正しい値が入ります。シャード分割の問題かと考えsearch_type=dfs_query_then_fetchを適用しましたが結果は変わりませんでした。

Percolator Queryのidf計算において必ずdocFreqとdocCountが両方とも1になってしまいますが、これをindex全体のドキュメントに対しての値に変更することは可能ですが?そもそもPercolator Queryについて重大な勘違いをしていますか?よろしくお願いいたします。


(Makoto Nozawa) #2

今回の例はid=2のドキュメントを検索するだけのクエリであり、1つしかヒットしないのは自然な挙動に思います

percolatorは通常の検索とは逆に「クエリ」を先にindexingしておいて「ドキュメント」で検索するというものですが、投入しているデータを見る限り何か勘違いをされているようです
percolatorを使う場合、投入するデータはちょうどQuery DSLと同じ構造になるはずですが、今回のデータは一般的なドキュメントに見えます

この部分で投入しているデータはドキュメントとしてPUTするのではなく、searchの中で、例えば下記のように使うことになります

GET /my-index/_search
{
    "query" : {
        "percolate" : {
            "field" : "query",
            "document" : {
                "message" : "quick brown fox"
            }
        }
    }
}

この場合、当然ながらdocFreq/docCountは1になります(ドキュメントが1つしかないので)
おそらく「正しい値」というのは2を想定しているかと思うのですが、

  • 「3つのdocumentに対して1つのqueryで検索」 -> 2つのdocumentがヒットする

というのは

  • 「1つのqueryに対して3つのdocumentでそれぞれpercolator queryを使う」 -> 2回ヒットする

と等価です

結論として

index全体のドキュメントに対しての値に変更すること

これは、知る限りESだけではできないです
3回percolate queryで検索してESの外で計算することは可能です
もしかするとaggregationを使ってできるかもしれないですが、percolatorではない通常の検索をすればいいだけに思えます

Percolator QueryのscoreとSearch API のスコアのソート結果を一致させたい

こちらはスコア計算のアルゴリズム設定でなんとかなるかもしれません
percolatorの性質上、tf-idfのように(ヒットしないものも含めた)indexingされているドキュメントの全体数を参照するような計算式だと合致しない気がします

あまり分かりやすい説明ができずすみません


(Po3rin) #3

非常に詳しく説明いただきありがとうございます!!

いただいた回答で、指定したドキュメント1つしか見てくれないことは理解しました。ご提案いただいたように信頼できるidfの値を得るために全てのドキュメントに対して検索してESの外で計算するのは良さそうですが。しかし、ドキュメント数の多い実践ではパフォーマンス的にどうなるか分からないので1度計測してみます。

アルゴリズム設定においても、今回の要件にマッチするアルゴリズムがあるか探してみます!

ありがとうございました!!

P.S.

投入しているデータを見る限り何か勘違いをされているようです

こちらに関しては、すでにindexされているドキュメントに対してid指定でpercolator queryを走らせる例が公式ドキュメントにあったので今回はそれを例にあげました。
https://www.elastic.co/guide/en/elasticsearch/reference/6.6/query-dsl-percolate-query.html#_example_3


(Makoto Nozawa) #4

これは完全に知りませんでした
大変失礼しました :bowing_man: