MANA-DOT

PIXEL ART, PROGRAMING, ETC.

node.js で elasticsearchを使ってみる

elasticsearch

node.jsはいろんなパッケージがあるので色々出来る!いろんなことをちょっとやってみるのに捗る!全文検索もできたらもっといろんなこと出来そう!

そうnpmを探してみたのですが、node.jsネイティブな全文検索エンジンは見つけたもののまだアルファ版だったりしたので、最近よく聞くelasticsearchをnode.jsから使ってみました。

elasticsearch とは

Solrとかgroongaとなどが競合の全文検索エンジンです。 予めテキストデータをインデキシングしておいて、文字列を投げるとそれを含むデータを引いてきてくれるやつですね。

詳しくは調べてないので、他の競合と比べての優位点がわかっていませんが、 RESTfulなAPIJSONを投げることで、インデキシングや検索を行え、結果もJSONで返ってくる点がユニークな点であると思います。

また、httpでJSONを投げるとJSONが返ってくるという点から、JavaScript(node.js)との相性は良好であると思います。

elasticsearch の準備

本体のインストール

今回OSはUbuntuでした。 公式でdeb形式のパッケージを配布しているので、それを利用できます。

wget https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-0.90.5.deb
sudo dpkg -i elasticsearch-0.90.5.deb

以上のようにしたら、/usr/share/elasticsearch にプログラムが展開され、サービスの登録・起動も行ってくれました。

デフォルトだと9200番ポートで待ち受けるので、curlしてインストールできていることをチェックします。

curl localhost:9200
{
  "ok" : true,
  "status" : 200,
  "name" : "Indech",
  "version" : {
    "number" : "0.90.5",
    "build_hash" : "c8714e8e0620b62638f660f6144831792b9dedee",
    "build_timestamp" : "2013-09-17T12:50:20Z",
    "build_snapshot" : false,
    "lucene_version" : "4.4"
  },
  "tagline" : "You Know, for Search"
}

kuromoji のインストール

elasticsearchはデフォルトで日本語を使うことは出来ません。 kuromoji という日本語懐石のためのプラグインがあるので、それをインストールします。

elasticsearchでプラグインのインストールを行う場合、 elasticsearch/bin下のpluginを実行すればいいようです。

sudo /usr/share/elasticsearch/bin/plugin -i elasticsearch/elasticsearch-analysis-kuromoji/1.5.0
sudo service elasticsearch restart

サービスを再起動することで利用できるようになります。

インデックスを作る

以上で日本語でelasticsearchを利用できます。 あとはインデックス(これも詳しくは調べてないですが、SQLにおけるDBスキーマのようなもの?) を作ってあげれば、データを突っ込んでそのデータを検索できるようになります。

elasticsearch はREST API経由で操作を行えるので、curlJSONを投げてあげます。

curl -X PUT localhost:9200/test -d '
{
  "mappings": {
    "entry": {
      "properties": {
        "title": {
          "type": "string",
          "analyzer": "kuromoji"
        },
        "body": {
          "type": "string",
          "analyzer": "kuromoji"
        },
        "url": {
          "type": "string"
        }
      }
    }
  }
}'

これは、testというindexにentryというtype(SQLで言うところのテーブルみたいなものらしい。同一のスキーマで定義されるデータの集合ってことかな) で、stringでkuromojiで解析するtitle、bodyというカラムと、urlというカラム(analyzerを省略すればデフォルトになると思って省略した)を定義することになるようです。

RSSで取得したエントリを打ち込むイメージです。

とりあえずこれでインデックスが出来たので、nodeからデータの登録と検索を試せます。

node.js から使ってみる(elasticsearchclient編)

とりあえず、nodeから利用するためにモジュールをnpmで探しました。 今回は、node-elasticsearch-client を利用してみました。

ちなみに、以下のコードはすべてCoffeeScriptで書いています。特にJSONデータを扱うときに 行数を短く記述することができるので便利です。

elasticsearch への接続

コンストラクタにホスト名とポート番号を渡します。

ElasticSearchClient = require 'elasticsearchclient'

elasticSearchClient = new ElasticSearchClient
  host: 'localhost'
  port: 9200

データの登録

indexメソッドにindex名、type名と登録するデータを渡してexecすれば良いようです。

elasticSearchClient.index("test", "entry",
  title : "【パズドラ】『日本ゲーム大賞2013』受賞記念イベント 明日から開催!"
  body  : "おかげ様で「パズル&ドラゴンズ」は 2013年9月19日(木)に開催された『日本ゲーム大賞2013』で 「経済産業大臣賞」を受賞しました!"
  url   : "http://pazusoku.blog.fc2.com/blog-entry-1257.html"
).on('data', console.log.bind(console)).exec()

elasticSearchClient.index("test", "entry",
  title : "【パズドラ】これがウズメ愛だ!HAHAHA!!!!【その間僅か4分】"
  body  : "こうじゃ!これがウズメ愛だ!HAHAHA!!!!クシナダに全ステ負けてるとかいったらドリルくちばしするぞ "
  url   : "http://blog.livedoor.jp/gachagacha123/archives/33090447.html"
).on('data', console.log.bind(console)).exec()

データは適当に自分のFeedlyからとってきたものです。

データの検索

searchメソッドにindex名、type名とクエリを渡してあげます。

elasticSearchClient.search 'test', 'entry',
  query:
    text:
      body: 'クシナダ'
, (err, data)->
  JSON.parse(data).hits.hits.forEach (d)->
    console.log d

次のような結果が帰ってきます。

{ _index: 'test',
  _type: 'entry',
  _id: 'yCiFrfdNRHWgjowA3RQYjQ',
  _score: 0.25,
  _source:
   { title: '【パズドラ】これがウズメ愛だ!HAHAHA!!!!【その間僅か4分】',
     body: 'こうじゃ!これがウズメ愛だ!HAHAHA!!!!クシナダに全ステ負けてるとかいったらドリルくちばしするぞ ',
     url: 'http://blog.livedoor.jp/gachagacha123/archives/33090447.html' } }

ちゃんと「クシナダ」というワードで検索できているようです。

node.js から使ってみる(request編)

elasticsearchclient を試していて、JSONを投げてJSONを取得するだけだったら、別に専用モジュールがなくてもrequest使ったほうが簡単なんじゃね?と思ったので、そちらも試してみました。

データの登録

request.post
  uri: 'http://localhost:9200/test/entry'
  json:
    title : "【パズドラ】さっき麒麟にこれにしてきたばっかなのにどんな顔すりゃいいんだよ"
    body  : "いまさっき麒麟にこれにしてきたばっかなのにどんな顔すりゃいいんだよ"
    url   : "http://mbmato.doorblog.jp/archives/33349715.html"
, (err, res, val)->
  console.log val

データの検索

request
  uri: 'http://localhost:9200/test/entry/_search'
  json:
    query:
      text:
        body: '麒麟'
, (err, res, val)->
  val.hits.hits.forEach (d)->
    console.log d

以上のようになります。

行数は殆ど変わらないので、こっちでもよくね?って思うのですが、 URLの共通部分などをクラスにラップしていくと、結局 elasticsearchclient のようなものを自分で作ることになりそうな気もします。

ただ、インデックスの作成などは elasticsearchclient では出来ないようなので、そういうことをするときはrequestを使ったほうがいいのかなと思います。

参考リンク