サービスインして、検索ワードと検索結果数、それから実行にかかったtookなどをログにとって記録していると
検索結果が0件のものがいくつかある。
うちのサービスの場合はマンガなので、マンガの作品名だったり、作家名だったりするのですが、検索ワードを見てみると
「あー、おしいっ!」
というようなものが多々あるわけです。
今回はそれをなるべく拾ってあげようという思考。
elasticsearchには
more_like_this
というクエリが有り、これを使うと、何となく似ているものを拾ってくれる。
何となくというところがミソで、何となく具合を色々パラメータで指定してあげるわけです。
では、クエリを見てみます。
{
"query" : {
"more_like_this" : {
"fields" : ["text1"],
"like_text" : "うーやーたー",
"percent_terms_to_match" : 0.7,
"min_term_freq" : 1,
"min_doc_freq" : 1
}
},
"from":0,
"size":100
}
text1というフィールドに対してmore like this(もしかしてこれではないですか?)検索を行います。
パラメータ | 説明 |
---|---|
like_text | 検索文字 |
percent_terms_to_match | 似ているとされているドキュメントにマッチする語句のパーセンテージ。0.7は70%に相当。 |
min_term_freq | 検索ドキュメントにlike_textで指定したワードが含まれる件数が何件以上を指定する。この数より小さいものは無視。 |
min_doc_freq | 採用される語句が含まれるべきドキュメントの最小件数を指定 |
ぶっちゃけよく分からんけど、色々試してみた結果
percent_terms_to_matchは検索ワードの長さに対してどのくらい適合しているかで、小さければ結構適当だし、大きければけんさくもじにちかいものしかひっぱらなくなる。
min_term_freqについてはうちの場合はまずは作品のタイトル名に引っ掛けたので、普通に考えて1より大きいのはあまり考えられない。
min_doc_freqも同様。
この辺は検索する対象の性質によって大きく変わってくると思うので、利用する人の検索対象によって変えてください。
と、意気揚々とでけた!とばかりに実際に投入してみると、
・・・あれ?
結構というか、こちらの想像通りに動かないことのほうが多い。
具体的にどうおかしいのかは少し動きを見ながら調べないといけなさそう。
今はまだ適用はしないけど、たぶんあまりにも適当すぎる結果を指摘されたらScoreでフィルターかけない時が来そうな気もしなくも無いので書いときます。
{
"query" : {
"more_like_this" : {
"fields" : ["text1"],
"like_text" : "うーやーたー",
"percent_terms_to_match" : 0.7,
"min_term_freq" : 1,
"min_doc_freq" : 1
}
},
"from":0,
"size":100,
"min_score":1.0 ←Scoreのフィルター設定
}
2016-06-15追記
さて、いろいろ実際に投入してみて触ってみると以下の場合に変な動きをすることが分かってきました。
・検索ワードがカタカナの場合
・検索ワードがアルファベットの場合
原因が何かを調べる前に、今回検索するtext1というフィールドは特にtemplateで指定していないので、PUT時にElasticSearchが勝手に定義しているものです。
なので、その定義を見てみます。
"text1" : {
"type" : "string"
}
まあ予想通りですが、作品のタイトルを入れているのでStringが入ります。特にAnalyzer等は紐付けていません。
この状態で次の3パターンJsonをAnalyzeモードで投げてみます。
アルファベット
{
"query" : {
"more_like_this" : {
"fields" : ["text1"],
"like_text" : "ascdef",
"percent_terms_to_match" : 0.7,
"min_term_freq" : 1,
"min_doc_freq" : 1
}
},
"from":0,
"size":100
}
{
{
"token" : "abcdef",
"start_offset" : 112,
"end_offset" : 118,
"type" : "<ALPHANUM>",
"position" : 6
}
カタカナ
{
"query" : {
"more_like_this" : {
"fields" : ["text1"],
"like_text" : "アカサタナ",
"percent_terms_to_match" : 0.7,
"min_term_freq" : 1,
"min_doc_freq" : 1
}
},
"from":0,
"size":100
}
{
"token" : "アカサタナ",
"start_offset" : 112,
"end_offset" : 117,
"type" : "<KATAKANA>",
"position" : 6
}
ひらがな
{
"query" : {
"more_like_this" : {
"fields" : ["text1"],
"like_text" : "あかさたな",
"percent_terms_to_match" : 0.7,
"min_term_freq" : 1,
"min_doc_freq" : 1
}
},
"from":0,
"size":100
}
{
"token" : "あ",
"start_offset" : 112,
"end_offset" : 113,
"type" : "<HIRAGANA>",
"position" : 6
}, {
"token" : "か",
"start_offset" : 113,
"end_offset" : 114,
"type" : "<HIRAGANA>",
"position" : 7
}, {
"token" : "さ",
"start_offset" : 114,
"end_offset" : 115,
"type" : "<HIRAGANA>",
"position" : 8
}, {
"token" : "た",
"start_offset" : 115,
"end_offset" : 116,
"type" : "<HIRAGANA>",
"position" : 9
}, {
"token" : "な",
"start_offset" : 116,
"end_offset" : 117,
"type" : "<HIRAGANA>",
"position" : 10
}
ひらがなは1文字づつToken化されているのに対してアルファベット、カタカナは全てを一つとしてtoken化されています。
これがい私の感じていた違和感でした。
たとえば、
『あかさたな』というタイトルを持つ作品を『あかたな』で検索しても引っかかる。
でも
『アカサタナ』というタイトルを持つ作品を『アカタナ』で検索しても引っかからない。
おなじで
『abcde』というタイトルを持つ作品を『abde』で検索しても引っかからないわけです。
この違和感はこのAnalyzeの結果を見れば一目瞭然ですね。
ということで、text1に対してAnalyzerを指定してみます。
単純に打ち間違いの作品を拾うならばngramでもいい気がするので、設定してみます。
その辺の設定は省略しますが、無事設定してみて、アルファベットやカタカナでも無事に検索で拾うことが出来ました。
以前話していたtemplateでのオートマッピングは便利なのですが、こういったものを使う場合には注意いが必要です。
目的に応じて適切にtemplate設定が出来るように気をつけたいですな。