CreateField Blog

システムに関係のない仕事をしているサラリーマンがオープンソースを使って個人でWebサービスを開発・運営するブログ

連想検索エンジンGETAssocとGroongaを連携させるための設計案

はじめに

以前、Groongaが得意でない類似文書検索にGETAssocという連想検索エンジンを使った話というタイトルで、類似文書検索に連想検索エンジンGETAssocを使ったという記事を書きました。

連想検索エンジンGETAssocでは、単語と文書の頻度行列を作って演算することにより、高速、且つ、精度の高い類似文書を取得することができます。ある文書に似た文書(関連記事)、検索文章から連想されるワード(関連語)を高速に取得することができます。検索結果のナビゲーションに関連記事、関連語を追加することができます。

しかしながら、GETAssocは、データストアの仕組みをもたなかったり、インデックスを作るために定型のitbファイル形式に整形して、多数のオプションを使い分けてコマンドを打たないといけなかったりしてなかなか面倒です。

そこで、高速な全文検索エンジンGroongaとGETAssocを連携するための設計案を検討しました。検討しただけでまだ作ってません。

没案 GETAssocライブラリlibnwamと連携

Groongaはライブラリとして利用してもサーバとして利用する場合とほぼ同じように全文検索の機能を使うことができ、簡単にアプリケーションに全文検索の機能を組み込むことができます。

そこで、GETAssocのライブラリlibnwamを使って、Groongaと密に連携してみようと思いソースを読んでみました。しかしながら、GETAssocのインデックス更新のAPIwam_update自体がitbファイルへのパスを要求しており、Groongaのデータを直接連携させるのは難しそうでした。ライブラリとして利用しても、結局のところファイルに吐き出す必要があり、Groongaのカラム更新と直列に連携させると更新速度にも悪影響を及ぼしそうです。また、せっかくの分散機能も使えなくなってしまいます。

採用案 Dockerを使ってGETAssocサーバと連携

GETAssocは、行と列の演算を分散して行えるような仕組みもあるものの、パッケージが提供されていなかったり、依存関係が厳しかったり、サーバとデーモンを起動させたり、かなり面倒です。

そこで、Dockerを使ってGETAssocサーバ環境を簡単に作れるようにして、Docker上のGETAssocサーバとGroongaを連携させようと思います。

以下のようなイメージになりそうです。

f:id:naoa_y:20140803141445p:plain

stpは、xgetassocのラッパーコマンドであり、itbファイルを読み込んでインデックスであるNWAMまたはPWAMを構築します。
NWAMは単一CPU版のWAM(Word Article Matrix)であり、PWAMは分散版のWAMです。 getassocは、Apache httpdから起動されるgss3/XMLサーバです。
xgetassocは、WAMの作成等、GETAssocの基本機能を実行します。行列計算ノードのサーバとしても動作します。
dnwam.confは、行列計算ノードのIPアドレス等の分散設定ファイルです。
stmdは、ICUのNFKC正規化およびMeCabで形態素解析して、単語と出現頻度のベクトルをつくります。かなり厳しいフィルターがかけられています。複数起動させることができ、高速に単語ベクトルを作ることができます。
GETAssocには、オプションで組み込みの全文検索フィルターがあり、これを使う場合は、独自の正規化が行われます。GETAssoc組み込みの全文検索フィルターは、24GBという制限があるようなので、本格的な全文検索には、Groongaを使った方がいいと思います。

作るもの

GETAssocのDocker

GETAssoc環境の構築済みイメージをDockerで起動したら、/var/lib/getassoc/data/配下にGETAssocのNWAM名に相当するディレクトリを作り、そのディレクトリにitbファイルを追加していくだけで使える状態になるようにします。分散させる場合は、あらかじめ/var/lib/getassoc/etc/dnwam.confで分散数とIPアドレス等を設定するようにします。

  • itb_crawler
    itbファイルを読み取る簡易クローラー(単なるbsh)
    inotifywaitを使って、/var/lib/getassoc/data/フォルダのファイル書き込み終了イベントを監視します。ファイルが追加されたら、拡張子に応じて、記事追加、記事削除、WAM新規作成、WAM削除のstpコマンドを逐次実行します。成功したらファイルを削除、失敗したらファイルをerrフォルダに移動します。サブディレクトリ名によってWAM名を振り分けます。起動時には、/var/lib/getassoc/etc/dnwam.confを読み込んで分散構成かシングル構成を確認して、stpコマンドのオプションを切り替えます。

    拡張子 動作 フォーマット
    .itb 記事追加 itb形式
    .idl 記事削除 削除するIDが1行ごとに記載されたファイル
    .dmp WAM削除後、新規作成 itb形式
    .drp WAM削除 中身なしでOK
  • docker-getassoc-master
    GETAssocの親ノードのDockerファイル

  • docker-getassoc-compute
    GETAssocの行列計算ノードのDockerファイル

Groongaプラグイン

  • getassoc-select
    GETAssocのgss3プロトコルで連想検索するGroongaのコマンドプラグイン
    連想ワード取得、自然文での類似文書検索、文書IDでの類似文書検索、文書クラスタリングができるようにします。Groongaのカラムの値を出力、および、Groongaの全文検索結果でフィルターをかけられるようにします。ただし、最後の出力の段階でフィルターさせるだけなので、適正な件数を出力できなくなると思います。
  • getassoc-words
    GETAssocのgss3プロトコルで連想ワードのみを取得するGroongaのコマンドプラグインもしくは関数プラグイン
  • getassoc-catalogue
    GETAssocに登録されているカタログを表示するGroongaのコマンドプラグイン
  • getassoc-getvector
    GETAssocに登録されている文書IDの単語ベクトルを取得するGroongaのコマンドプラグイン
  • grn2itb
    Groongaのテーブルからitbファイル形式を作るコマンドプラグイン
    文書IDまたはキーによる1記事出力、削除する文書ID出力、全レコードダンプ出力、検索結果のみをダンプ出力、GETAssocのNWAM削除に相当するファイル出力ができるようにします。テーブルのどのカラムをitbファイルに吐き出すかマッピング定義ができるようにします。
  • grn2itb-hook
    Groongaのテーブルにレコード追加、削除があったタイミングでitbファイルが自動的に作成されるフックを追加するGroongaのコマンドプラグイン
    MroongaでMySQLからSQLでのINSERTUPDATEでも動作するようにします。

gss3プロトコルしゃべる系のコマンドは、別にGroongaで使わなくても、アプリからXMLをPOSTするだけでいいような気もします。が、Groongaとフォーマットをそろえたり、カラムの値をとってきたり、俺得なので作ると思います。

おわりに

基本的にファイル入出力させたり、gss3プロトコルをGroongaにマッピングするぐらいなのでたいした作業にはならなそうです。

かなり上位のレイヤーでの連携なので、Groongaの分散システムDroongaで連携させた方がRubyで書けて楽かなぁとか思ったりもしていますが、Mroongaを使ってMySQLから使えるようにしておきたいので、たぶん上記の感じでCかC++で作ると思います。