スキップしてメイン コンテンツに移動

ElasticSearchとKuromojiで日本語全文検索してみる

北海道開発オフなどでご一緒させて頂いた方はご存知だと思いますが、元々インフラエンジニアとしてネットワーク機器の設定やサーバの構築ばかりしていた僕がプログラムを書くようになったのは、「形態素解析してマルコフ連鎖するTwitter botを作りたい!」というのがきっかけであり、日本語の分かち書きや全文検索というのは僕にとって興味深くそして好きな技術の一つです。

そこで今回はElasticSearchとそのプラグインであるelasticsearch-analysis-kuromojiについて調べてみました。

ElasticSearchとは
Apache v2ライセンスで公開されているオープンソースソフトウェアの検索エンジンです。特徴として
  • RESTfulなAPIが使える
  • InputもOutputもJSON
  • スキーマフリーなので面倒臭い定義無しにデータを突っ込める
等があります。


Kuromojiとは
atilika社製の、Javaで書かれた日本語形態素解析ソフトウェアで、Apache v2ライセンスで公開されているオープンソースソフトウェアです。形態素解析としてはMeCabがメジャーで古くから使われていますが、Kuromojiは比較的新しいソフトウェアです。

ElasticSearchのセットアップ
環境はAmazon Linuxです。ElasticSearchの動作にはJavaが必要ですが、Amazon Linuxの場合初期導入されています。
$ java -version
java version "1.6.0_24"
OpenJDK Runtime Environment (IcedTea6 1.11.13) (amazon-65.1.11.13.56.amzn1-x86_64)
OpenJDK 64-Bit Server VM (build 20.0-b12, mixed mode)
ElasticSearchの公式サイトから最新バージョンをダウンロードします。なお僕は「サーバにインストールするソフトウェアは保守性の観点から可能な限りパッケージ管理するべき」というポリシーなのでRPMから入れます。
$ wget https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-0.90.5.noarch.rpm
$ sudo rpm -ivh ./elasticsearch-0.90.5.noarch.rpm
Preparing...                          ################################# [100%]
Updating / installing...
   1:elasticsearch-0.90.5-1           ################################# [100%]
Starting elasticsearch: [  OK  ]

ElasticSearchの動作確認
それでは簡単な動作確認をしてみます。まずはXPUTを使ってテストデータを登録します。
ここではIndexを「mytest」、Typeを「test」としました。Oracle用語で言うと、IndexがSchema、TypeがTableに当たるものになります。
$ curl -XPUT http://localhost:9200/mytest/test/1 -d '
> {
>   "title" : "memo",
>   "text"  : "hogehoge"
> }'
{"ok":true,"_index":"mytest","_type":"test","_id":"1","_version":1}
そしてXGETを使って検索してみます。
$ curl -XGET http://localhost:9200/mytest/test/_search -d '
> {
>   "query":
>   { "match":{"title":"memo"}}
> }'
{"took":85,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":1,"max_score":0.30685282,"hits":[{"_index":"mytest","_type":"test","_id":"1","_score":0.30685282, "_source" :
{
  "title" : "memo",
  "text"  : "hogehoge"
}}]}}
titleが「memo」であるデータが戻り値として返ってきました!

Kuromojiプラグインのセットアップ
ElasticSearchのpluginコマンドを使って、Kuromojiプラグインをインストールします。
$ sudo /usr/share/elasticsearch/bin/plugin  --install elasticsearch/elasticsearch-analysis-kuromoji/1.5.0
-> Installing elasticsearch/elasticsearch-analysis-kuromoji/1.5.0...
Trying http://download.elasticsearch.org/elasticsearch/elasticsearch-analysis-kuromoji/elasticsearch-analysis-kuromoji-1.5.0.zip...
Downloading ................................................................................................................................................................................................................................................................................DONE
Installed elasticsearch/elasticsearch-analysis-kuromoji/1.5.0 into /usr/share/elasticsearch/plugins/analysis-kuromoji
プラグインをインストールした後はElasticSearchの再起動が必要です。
$ sudo service elasticsearch restart
Stopping elasticsearch:                                    [  OK  ]
Starting elasticsearch:                                    [  OK  ]

Kuromojiプラグインの動作確認
それでは簡単な動作確認をしてみましょう。
kurotestというIndexで、kuromojiをアナライザと使うように設定します。
$ curl -XPUT 'http://localhost:9200/kurotest/' -d'
>  {
>      "index":{
>          "analysis":{
>              "tokenizer" : {
>                  "kuromoji" : {
>                     "type" : "kuromoji_tokenizer"
>                  }
>              },
>              "analyzer" : {
>                  "analyzer" : {
>                      "type" : "custom",
>                      "tokenizer" : "kuromoji"
>                  }
>              }
>          }
>      }
>  }'
{"ok":true,"acknowledged":true}
このkurotestにXPOSTで日本語文字列を投げると、分かち書きされて返ってきます!
$ curl -XPOST 'http://localhost:9200/kurotest/_analyze?analyzer=analyzer&petty' -d '梅酒は水'
{
  "tokens" : [ {
    "token" : "梅酒",
    "start_offset" : 0,
    "end_offset" : 2,
    "type" : "word",
    "position" : 1
  }, {
    "token" : "は",
    "start_offset" : 2,
    "end_offset" : 3,
    "type" : "word",
    "position" : 2
  }, {
    "token" : "水",
    "start_offset" : 3,
    "end_offset" : 4,
    "type" : "word",
    "position" : 3
  } ]
}
(余談ですが「梅酒は水」はWebbingStudioさんが北海道に広めた偉大な言葉です)

ElasticSearchとKuromojiプラグインで日本語全文検索
お待たせしました!ここからが本番です。
まずはElasticSearchの設定ファイルを修正し、kuromojiをデフォルトアナライザとして設定します。設定後はElasticSearchの再起動が必要です。
$ sudo vi /etc/elasticsearch/elasticsearch.yml
index.analysis.analyzer.default.type: custom
index.analysis.analyzer.default.tokenizer: kuromoji_tokenizer
$ sudo service elasticsearch restart
それでは二つの日本語文字列をテストデータとして登録してみます。
$ curl -XPUT http://localhost:9200/jptest/test/1 -d '
>   {
>     "title" : "メモ",
>     "text"  : "梅酒は水"
>   }'
{"ok":true,"_index":"jptest","_type":"test","_id":"1","_version":1}
$ curl -XPUT http://localhost:9200/jptest/test/2 -d '
>   {
>     "title" : "メモ2",
>     "text"  : "麦酒は命"
>   }'
{"ok":true,"_index":"jptest","_type":"test","_id":"2","_version":1}
梅酒」という文字列で検索してみます。
$ curl -XGET http://localhost:9200/jptest/test/_search -d '
>    {
>      "query":
>      { "match":{"text":"梅酒"}}
>    }'
{"took":3,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":1,"max_score":0.15342641,"hits":[{"_index":"jptest","_type":"test","_id":"1","_score":0.15342641, "_source" :
  {
    "title" : "メモ",
    "text"  : "梅酒は水"
  }}]}}
ちゃんと「梅酒は水」がヒットしました!

次に「梅酒」という文字列で検索してみます。
$ curl -XGET http://localhost:9200/jptest/test/_search -d '
>     {
       "query":>        { "match":{"text":"梅酒は"}}
>      }'
{"took":5,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":2,"max_score":0.2169777,"hits":[{"_index":"jptest","_type":"test","_id":"1","_score":0.2169777, "_source" :
  {
    "title" : "メモ",
    "text"  : "梅酒は水"
  }},{"_index":"jptest","_type":"test","_id":"2","_score":0.02250402, "_source" :
   {
     "title" : "メモ2",
     "text"  : "麦酒は命"
   }}]}}
「梅酒は水」と「麦酒は命」の2つがヒットしました。ここで注目したいのは赤字にした_scoreです。
「梅酒は水」は「梅酒は」のうち3文字がヒットするためスコアが高く、「麦酒は命」は「酒は」の2文字のみがヒットするためスコアが低くなっています。これで分かち書きによって日本語全文検索が行われていることがわかります。

感想
In/OutがJSONであることから、使い道は色々あるなぁと思います。例えばBlogの記事検索にも使えるし、fluentdと組み合わせてログ検索にも使るし。Kuromojiの分かち書きも試した分には精度良さそうなので、コレを使って何か作ってみたいです。



パーフェクトRuby (PERFECT SERIES 6)パーフェクトRuby (PERFECT SERIES 6)
Rubyサポーターズ,すがわら まさのり,寺田 玄太郎,三村 益隆,近藤 宇智朗,橋立 友宏,関口 亮一

技術評論社
売り上げランキング : 13917

Amazonで詳しく見る by AZlink

コメント

このブログの人気の投稿

リモートワークは仕組みじゃなくて文化です

ここ最近、コロナウイルス関連の報道が数多くあるが、その中でも多くの企業がリモートワークを推奨するという記事やプレスリリースが注目を浴びている。それ自体はもちろん大変望ましい。不要な対面での接点を減らすことで感染リスクを抑えることが出来るし、通勤ラッシュや首都圏への経済集中も抑制出来るからだ。

だがちょっと待ってほしい。リモートワークというのは社員が在宅で働くことだけを指すのではない。社員が在宅で働いても出社時と同じパフォーマンスが出ることをリモートワークというのだ。だからこの記事のタイトルで「リモートワークは仕組みじゃなくて文化です」と書いた。

弊社がリモートワークを導入したのは2011年の東日本大震災がきっかけだけれど、9年経った今、どのようにリモートワークを運用して、そしてパフォーマンスを維持しているかを共有したいと思う。以下のことが文化として根付けば、その会社のメンバーはリモートワークでもオフィスでも同じようなパフォーマンスが発揮出来るはずだ。

1.勤怠を厳密に管理しない え、だってダルくないすか。管理するの。何時に働き始めて何時に働き終わったかなんて関係ないっしょ。大事なのは働いた結果のアウトプットであり、働いた時間なんか問題じゃない。

2.休憩も厳密に管理しない え、だってダルくないすか。管理するの。何時に休憩し始め(ry

3.工数を厳密に管理しない え、だ(ry

4.目に見えるアウトプットを意識する 当然のことながら、仕事は結果が全てであり、結果が出なければどこで何時間働いたって意味がない。そして結果というのは目に見えなければ意味がない。 だからこそ、アウトプットを出すこと、アウトプットを評価することに徹底的にこだわる。それはドキュメントかもしれないし、お客様やパートナーとコミュニケーションするためのメールかもしれないし、社内の改善活動かもしれないし、メンバーへのフォローかもしれないし、ブログかもしれないし、Slackでの発言かもしれない。 とにかく目に見えないものは周りも認められない。目に見えるアウトプットしか評価されないし、そのために徹底的にアウトプットするんだ、という意識を社内でしっかりと作ることが重要。

5.コミュニケーションコストを意識する どんなに頑張っても、オンラインのコミュニケーションはオフラインのコミュニケーションの密度を越えられない…

41歳になりました

30代の頃は40歳になるまでの1年1年を意識しながら生きていたんだけど、40歳を越えてから自分の年齢にまるで興味が無くなり「あれ、今40歳だっけ、41歳だっけ」くらいの感じだったのですが、昨晩妻に確認したところ今日で41歳になりました。本厄ですが今の所は大きなトラブルもなく、もしかしたらコロナウイルスって僕が厄年のせいかな?くらいの気持ちでおります。

折角なので近況報告です。

コロナウイルス ... 現在弊社ではBCP体制として全オフィス閉鎖、全員在宅勤務、出張禁止となっています。僕も在宅勤務をしており、3月の出張を全てキャンセルしました。丸々一ヶ月自宅にいるのはなんと2015年8月以来4年7ヶ月ぶりです。ほぼ5年じゃん...この5年出張しかしてねえじゃん俺...おかげで毎朝6時起床で飲酒ゼロという健康的な生活を送っております。仕事 ... 2019年7月に弊社執行役員から取締役に変わったのですが、「使用人兼務役員だし大した変わらないだろ」と高をくくっていたところ、なんだかんだと自分の部署以外にも目を向ける必要が出てきたり、海外拠点のビジネスについても責任が伴ったりと、なんか結構変わっちゃったな、という感じです。41歳もまた新しいチャレンジをする一年になる予定なのでお楽しみに。家庭 ... 長女が大学進学して一人暮らしを始め、一安心していたところに長男が酒と煙草で停学2連チャンし自主退学、何とか私立高校に編入させて寮に引っ越しをさせ、結果的に妻と次男と3人暮らし、というのが現状です。この長男についてはまぁちょっと色々酷い時期があったものの、正直なところ僕も他人様に胸を晴れるほど立派な高校生だったわけでもないし、長男自体は編入後にすごく大人になったことから、コレ自体はまぁ長男にとって必要な経験だったんだろう、とポジティブに捉えています。彼も来年は高校卒業を迎えるので、ちゃんと手に職つけてくれると良いな。あと長女には2歳年上の彼氏が出来ました。うん、お父さん大丈夫。お父さん冷静。お父さんちょっとジム行ってくる。英語 ... 1日2時間の英語の勉強時間をキープし続けて、まぁとりあえず海外に独りで行っても困らないし、英語のミーティングは70%くらいは言ってることわかる、たまに返事も出来る、くらいが現状。今年も勉強頑張ります。大学 ... 僕は工業高校卒ですぐ就職しており大学…

贅沢に対する恐怖に怯えている

僕が小さい頃、我が家は貧乏だった。

とは言え、極端なほどではない。家もあったし、サッカーという習い事も出来ていたし、三食ちゃんと食べることが出来た。でも例えば外食はしないとか、ブランドものの服は着ないとか(今思い出せばジャージ以外着たことなかった)、旅行には行かないとか、ファミコンを買ってもらえないとか、そういうレベルでは裕福ではなかった。母子家庭だったので父親はおらず、慰謝料は一銭も入ってくることがなく、母はいつも頑張って働いてくれていた。

そんな母の姿を見ていたから、僕もとにかく金を稼がなくては生きていないという想いが強く、中学時代は知り合いの伝手でちょっとした日銭が稼げるアルバイトをしていて、中学卒業後にはすぐに手に職を得ることが出来そうな工業高校に進学した。高校時代は平日も土日もずっとアルバイトで、年末年始も休まず働いていた。その後進学したいと思うようになり、高度専門士の取得が可能な4年生の専門学校への入学が決まっていたものの、金銭的な事情から進学を諦めざるを得なかった。結果として、高校卒業にすぐ就職した。

その後21歳という若年で結婚し、長女が生まれたけど、もちろん高卒で21歳の若者の給与なんかたかだか知れており、必死に働いた。本業だけでは食って行けず、知人の紹介で副業を持ち、朝8時から夜中3時まで働いた。結婚後の幸せ太りで10kg増えた僕の体重は、一番過酷だった2ヶ月間であっという間に元に戻った。

そんながむしゃらに生きて、今の僕がある。

率直に言えば、今の僕は僕の人生の中で最も金銭的な余裕がある。40歳になり、ある程度の給与を貰えるようになり、幸いなことにボーナスまでもらえる(僕がボーナスをもらったのは今の会社が初めてだ。ボーナスを初めてもらった時、「あ、ボーナスって本当に存在するんだな」と思った)ような状況だ。すごく幸せなことだと思う。

しかし、だからこそ、僕は贅沢が怖い。贅沢をすること、それに慣れること、そして贅沢にスポイルされることが怖い。必死に働いて、節約して、誰もやらないような泥臭い仕事をして、それで何とか生きていた経験の積み重ねが、今の僕を作っている。僕が今仕事をさせてもらえているのは、その経験を買ってもらえているからだけれど、それも「たまたま」であり、仕事がなくなった時にまた同じように必死の努力をしなくてはならない。必死の努力をするのは…