雑文発散

«前の日記(2015-06-07) 最新 次の日記(2015-06-09)» 編集
過去の日記

2015-06-08 [長年日記]

[Elasticsearch] ハートは見つかっても♡が見つからなかったのよ Elasticsearch では。

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:*♡* とかやれば対象に引っかかってくれた。

しかし、こういう場合(「♡」で検索したい場合)は、どう対処するのが正解なんだろう?