Elasticsearch + Kibana で調べ物をしていたら「♡」が検索できないことに気が付いた。分かってみれば「そうだよね」って感じの原因だったんだけど、ちょっとメモっておくよ。
再現用に適当なインデックス dummy
を作る。
% curl -XPUT 'http://localhost:9200/dummy'
{"acknowledged":true}
このインデックスのタイプ test
へドキュメントを登録していく。id = 1 に「title:♡
」、id = 2 に「title:ハート
」を登録。
% curl -XPUT 'http://localhost:9200/dummy/test/1' -d '{"title": "♡"}'
{"_index":"dummy","_type":"test","_id":"1","_version":1,"created":true}
% curl -XPUT 'http://localhost:9200/dummy/test/2' -d '{"title": "ハート"}'
{"_index":"dummy","_type":"test","_id":"2","_version":1,"created":true}
それぞれの内容を確認する。「♡」も「ハート」も登録されているのが分かる。
% curl -XGET 'http://localhost:9200/dummy/test/1?pretty'
{
"_index" : "dummy",
"_type" : "test",
"_id" : "1",
"_version" : 1,
"found" : true,
"_source":{"title": "♡"}
}
% curl -XGET 'http://localhost:9200/dummy/test/2?pretty'
{
"_index" : "dummy",
"_type" : "test",
"_id" : "2",
"_version" : 1,
"found" : true,
"_source":{"title": "ハート"}
}
では検索してみる。まずは「♡」をそのままクエリに入れて投げてみる。
% curl -XGET 'http://localhost:9200/dummy/test/_search?q=♡&pretty'
{
"took" : 4,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"failed" : 0
},
"hits" : {
"total" : 0,
"max_score" : null,
"hits" : [ ]
}
}
まぁ、これは無理なのは分かる。少なくとも URL エンコードすべきだろう。では、UTF-8 で URL エンコードして、もう一度試してみる。
% curl -XGET 'http://localhost:9200/dummy/test/_search?q=%E2%99%A1&pretty'
{
"took" : 3,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"failed" : 0
},
"hits" : {
"total" : 0,
"max_score" : null,
"hits" : [ ]
}
}
しかし、これでも結果が返ってこない。
もうひとつの「ハート」で試してみる。こちらも最初は「ハート」をそのままクエリに入れてみる。
% curl -XGET 'http://localhost:9200/dummy/test/_search?q=ハート&pretty'
{
"took" : 3,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"failed" : 0
},
"hits" : {
"total" : 0,
"max_score" : null,
"hits" : [ ]
}
}
これはやっぱりダメ。URL エンコードをしてもう一度試す。
% curl -XGET 'http://localhost:9200/dummy/test/_search?q=%E3%83%8F%E3%83%BC%E3%83%88&pretty'
{
"took" : 4,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"failed" : 0
},
"hits" : {
"total" : 1,
"max_score" : 0.30685282,
"hits" : [ {
"_index" : "dummy",
"_type" : "test",
"_id" : "2",
"_score" : 0.30685282,
"_source":{"title": "ハート"}
} ]
}
}
これだと結果が返ってきた。
クエリは URL エンコードして投げるのが正解なのは分かったけど、なんで「ハート」だと OK で「♡」だとダメなんだろう?と思って、しばらく悩んでいたのだけど、結局のところ、「♡」が kuromoji のストップワードに該当してしまい、検索対象にならなかったようだ。まぁ、「♡」は一文字だもんな。
どのようにアナライズされるのかをチェックしてみる。まずは答えが返ってきた「ハート」の場合。
% curl -XGET 'http://localhost:9200/dummy/_analyze?analyzer=kuromoji&pretty' -d 'ハート'
{
"tokens" : [ {
"token" : "ハート",
"start_offset" : 0,
"end_offset" : 3,
"type" : "word",
"position" : 1
} ]
}
「ハート」がトークンとして返ってきた。では、同じように「♡」で試してみる。
% curl -XGET 'http://localhost:9200/dummy/_analyze?analyzer=kuromoji&pretty' -d '♡'
{
"tokens" : [ ]
}
こちらは何も返ってこない。
今回は、アナライズしていない( not_analyzed
)なフィールドも用意していたので、それを利用して検索した。このときには title:*♡*
とかやれば対象に引っかかってくれた。
しかし、こういう場合(「♡」で検索したい場合)は、どう対処するのが正解なんだろう?