読者です 読者をやめる 読者になる 読者になる

elasticsearchとkuromojiプラグインで日本語の全文検索

elasticsearch

に続き。elasticsearch0.90.2でやってみました。

大きな流れはこんな感じ。

  1. kuromojiプラグインのインストール
  2. デフォルトでkuromojiを使うようにelasticsearch.ymlを設定
  3. kuromojiを使って形態素解析分かち書きしてインデクシング
  4. 検索

kuromojiプラグインインストール前に、検索できないことをテスト

学術研究用にlivedoorグルメのデータが公開されているので、利用規約に同意してダウンロード、解凍展開した後、
下記のPHPスクリプトを使ってとりあえず47都道府県データのprefのみをelasticsearchへ登録してみました。

<?php

setlocale(LC_ALL, 'ja_JP.UTF-8');
$delimiter = ',';

$index = 'ldgourmet';
// 下記コメントアウトを外すと、pref以外のCSVファイルもelasticsearchへデータ登録します。
$csv_files = array(
  // 'area'       => './areas.csv',
  // 'category'   => './categories.csv',
  'pref'       => './prefs.csv',
  // 'rating'     => './ratings.csv',
  // 'restaurant' => './restaurants.csv',
  // 'station'    => './stations.csv',
);

foreach($csv_files as $type => $file) {

  echo sprintf("[%s] START %s\n", date('Y-m-d H:i:s'), $type);

  $tp = fopen('php://temp', 'r+');

  $fp = fopen($file, 'r');
  while(!feof($fp)) {

    $l = fgets($fp);
    $l = str_replace('\\', '', $l);
    fwrite($tp, $l);
  }
  fclose($fp);

  rewind($tp);
  $columns = array();

  $n = 0; while(($row = fgetcsv($tp, 0, $delimiter)) !== false) {

    if ($n++ == 0) {
      $columns = $row;
    } else {

      $data = array_combine($columns, $row);
      if (!is_array($data)) {
        print_r($columns);
        print_r($row);
        continue;
      }

      $id = isset($data['id'])? $data['id'] : $n;
      $uri = sprintf('http://localhost:9200/%s/%s/%d', $index, $type, $id);

      $ch = curl_init();
      curl_setopt_array($ch, array(
        CURLOPT_URL => $uri,
        CURLOPT_HEADER => false,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_CUSTOMREQUEST => 'PUT',
        CURLOPT_POSTFIELDS => json_encode($data),
      ));

      $result = curl_exec($ch);
      curl_close($ch);

      $result = json_decode($result, 'UTF-8');
      if (empty($result['ok']) || $result['ok'] != true) {
        echo sprintf("[%s] ERROR /%s/%s/%s\n", date('Y-m-d H:i:s'), $index, $type, $id);
      }
    }

  }
  fclose($tp);
  echo sprintf("[%s] FINISH %s\n", date('Y-m-d H:i:s'), $type);
}
exit();
php -f import_ldgourmet.php

この状態で次のように検索してみます。
画面はelasticsearchのGUI「elasticsearch-head」がとても便利 - yuhei.kagayaでインストールしたelasticsearch-headのAnyRequestタブでリクエストを組み立てている箇所です。
f:id:yuhei_kagaya:20130806003635p:plain

すると、島根県の他に福島県、徳島県、広島県、鹿児島県がヒットしてしまいました。
島と根が1文字ずつで認識されて、「島」の方でヒットしているようです。
N-gramのNの値が1であるユニグラム(uni-gram)。

 curl -XGET 'http://localhost:9200/ldgourmet/_analyze?pretty' -d '島根'
{
  "tokens" : [ {
    "token" : "島",
    "start_offset" : 0,
    "end_offset" : 1,
    "type" : "<IDEOGRAPHIC>",
    "position" : 1
  }, {
    "token" : "根",
    "start_offset" : 1,
    "end_offset" : 2,
    "type" : "<IDEOGRAPHIC>",
    "position" : 2
  } ]
}

いったん登録データを消した後、次以降で「島根」で検索できるようにしてみます。

curl -XDELETE 'http://localhost:9200/ldgourmet'

kuromojiプラグインのインストール

をインストールします。pluginコマンド一発で完了です。

/usr/local/elasticsearch-0.90.2/bin/plugin -install elasticsearch/elasticsearch-analysis-kuromoji/1.4.0

インストールされたかテストします。

curl -XPUT 'http://localhost:9200/test/' -d '
{
    "index":{
        "analysis":{
            "filter":{
                "kuromoji_rf":{
                    "type":"kuromoji_readingform",
                    "use_romaji" : "true"
                },
                "kuromoji_pos" : {
                    "type": "kuromoji_part_of_speech",
                    "enable_position_increment" : "false",
                    "stoptags" : ["# verb-main:", "動詞-自立"]
                },
                "kuromoji_ks" : {
                    "type": "kuromoji_stemmer",
                    "minimum_length" : 6
                }
               
               
            },
           
            "tokenizer" : {
                "kuromoji" : {
                   "type":"kuromoji_tokenizer"
                }
           
            },
            "analyzer" : {
                "kuromoji_analyzer" : {
                    "type" : "custom",
                    "tokenizer" : "kuromoji_tokenizer"
                }
            }
           
        }
    }
}'

{"ok":true,"acknowledged":true} と返ってくればOKです。

テスト用のindexはいらないので削除します。

curl -XDELETE 'http://localhost:9200/test'

デフォルトでkuromojiを使うようにelasticsearch.ymlを設定

vi /usr/local/elasticsearch-0.90.2/config/elasticsearch.yml
 
index.analysis.analyzer.default.type: custom
index.analysis.analyzer.default.tokenizer: kuromoji_tokenizer

elasticsearchを再起動します。

/etc/init.d/elasticsearch restart

日本語を分かち書きしてインデクシング

上のPHPスクリプトで、もう一度都道府県データのみ登録してみます。

php -f import_ldgourmet.php

検索

「島根」で島根県のみヒットするようになりました。
f:id:yuhei_kagaya:20130806010527p:plain