Carpe Diem

備忘録

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

概要

検索した時に「部分一致させたいけど、上位に出るのは前方一致してるものがいいなぁ」といった時の対応。

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

環境

  • Ubuntu 14.04
  • Elasticsearch 1.5.0

マッピング

fieldsを複数設定します。以前はmulti_fieldとして使われていましたが、1.5系からdeprecatedになるそうです。
これによって「部分一致用」と「前方一致用」の2つのデータを登録できます。

{
    "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": {
                "term": {
                    "type": "string",
                    "fields": {
                        "raw": {
                            "type": "string",
                            "index": "not_analyzed"
                        },
                        "tokenized": {
                            "type": "string",
                            "index": "analyzed",
                            "analyzer": "ngram_analyzer"
                        }
                    }
                }
            }
        }
    }
}

このように各フィールド(term.tokenzedterm.tokenized)毎にアナライザを設定します。

データ用意

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.tokenized": {
                "query": "fa"
            }
        }
    },
    "size": 5,
    "from": 0
}
'

結果

fate
refactor
facebook

今回

Bool Queryを使います。Multi Match QueryDis Max Queryでも似たようなことができますが、単純にスコアに加点していく方式ならこちらを使います。

今回は

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

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

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

結果

fate
facebook
refactor

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

ソース