Carpe Diem

備忘録

検索時に複数のクエリでスコアを付ける その2

概要

前回と同じく、検索した時に「部分一致させたいけど、上位に出るのは前方一致してるものがいいなぁ」といった時の対応。
今回はcopy_toを使います。

元々の経緯としてはフィールド構造が異なる複数の対象に対してTopHitsクエリを用いていたのですが、何かクエリを揃える方法はないかなぁと思って調べてみたところあったので使ってみました。

検索文字 デフォルト 希望
fa fate
refactor
facebook
fate
facebook
refactor

環境

  • Ubuntu 14.04
  • Elasticsearch 2.2.0

マッピング

前回は1つのフィールドに対し、複数のanalyzerを指定しました。
今回はフィールドをコピーすることで、そちらも検索対象にします。
元々存在するtermというフィールドを、rawという新フィールドにコピーします。
termraw、それぞれ別のanalyzerを使うことで標題の件を解決します。

{
    "settings": {
        "analysis": {
            "analyzer": {
                "ngram_analyzer": {
                    "tokenizer": "ngram_tokenizer"
                }
            },
            "tokenizer": {
                "ngram_tokenizer": {
                    "type": "nGram",
                    "min_gram": 2,
                    "max_gram": 3,
                    "token_chars": [
                        "letter",
                        "digit"
                    ]
                }
            }
        }
    },
    "mappings": {
        "internet": {
            "_all": {
                "enabled": false
            },
            "dynamic": false,
            "properties": {
                "raw": {
                    "type": "string",
                    "index": "not_analyzed"
                },
                "term": {
                    "type": "string",
                    "index": "analyzed",
                    "analyzer": "ngram_analyzer",
                    "copy_to": "raw"
                }
            }
        }
    }
}

データ用意

curl -s -XPOST localhost:9200/test/_bulk -d '
{"index": {"_type": "internet", "_id": "1"}}
{"term": "fate"}
{"index": {"_type": "internet", "_id": "2"}}
{"term": "refactor"}
{"index": {"_type": "internet", "_id": "3"}}
{"term": "facebook"}
'

クエリ

今まで

curl localhost:9200/test/internet/_search?pretty -d '
{
    "query": {
        "match": {
            "term": {
                "query": "fa"
            }
        }
    },
    "size": 5,
    "from": 0
}
'

結果

fate
refactor
facebook

今回

  • 部分一致:match query(ngram)
  • 前方一致:prefix query

の2要素でスコアを付けます。

termの方は部分一致、rawの方は前方一致のスコアをつけます。

curl localhost:9200/test/internet/_search?pretty -d '
{
    "query": {
        "bool": {
            "should": [
                {
                    "match": {
                        "term": {
                            "query": "fa"
                        }
                    }
                },
                {
                    "prefix": {
                        "raw": {
                            "value": "fa"
                        }
                    }
                }
            ]
        }
    },
    "size": 5,
    "from": 0
}
'

結果

fate
facebook
refactor

prefix queryによってスコアが加算されたため、順番が変わり要望通りになりました。

ソース