CouchDBで全文検索を使えるようにする(couchdb-lucene)
紙で散在するドキュメントを電子化した場合、様々な利点がありますがそのうちの一つに「検索」機能があると思います。ドキュメント指向のDBであるCouchDBも例外ではありません。coucdb-luceneを使えば、CouchDB上にあるデータベース内を縦横無尽に検索して、キーワードに当てはまるドキュメントを探し出すことができます。以下、事前準備と簡単な使い方について書いておこうと思います。
主な手順はこんな感じです。(CouchDBは事前にインストールしてあることを前提に書きます)
1.Javaのインストール
Java 5以上が推奨のようです。私はSunのサイトから最新版を入手しました。インストールの方法はSunのサイトに詳しく載っているのでそちらをご覧ください。ちなみに私はJavaのことをよくわかっていません。でも、使うだけなら問題ありませんでした。
2.coucdb-luceneの取得、設定
Githubにソースコードがありますが、作者によるとソースコード版はCouchDBのv0.10.0や0.11.0向けに現在開発中とのこと。ここで熟成前のお酒を味わうのも一興なのですが、今の私の目的は「全文検索を使うこと」なので、作者のおススメにしたがって安定版のjarファイルで入手しました。その後、unpack200コマンドを使って、gz形式からjarファイルにします。unpack200コマンドはjavaをインストールした場所(/usr/binなど)に入っています。その後、jarファイルの所有者をCouchDBを起動するときに使用するユーザーに変更し、パーミッションも変えておきます。
$ unpack200 couchdb-lucene-0.4-jar-with-dependencies.jar.gz $ sudo chown couchdb:couchdb couchdb-lucene-0.4-jar-with-dependencies.jar $ sudo chmod 0770 couchdb-lucene-0.4-jar-with-dependencies.jar
3.CouchDBの設定変更
default.iniという設定ファイルに以下のように記述します。全文検索機能はCouchDBからすれば、External Process(外部プロセス)という位置づけになるようです。パスは各環境に応じて適当に変更してください。下の例は私の環境の場合です。 couchdb-luceneはデフォルトだと、localhostや127.0.0.1に紐付けられたCouchDBを検索しにいきます。検索先を変更したい場合(大半は変更したいと思いますが)は-Dcouchdb.url というパラメーターにIPアドレスやホスト名を指定しておきます。そのくらいbind_addressから読み込んでよ、とも思うのですが couchdb-luceneが入ったサーバーとCouchDBが入ったサーバーを分けた構成にすることができるようです。サーバーへの負荷を分散させることができますね。
;/usr/local/etc/couchdb/default.ini の一部 [couchdb] ;5000から60000変更 os_process_timeout = 60000 ; 5 seconds. for view and external servers. ;以下を追加 [external] fti=/usr/java/jre1.6.0_17/bin/java -Dcouchdb.url="http://aa.bb.cc.dd:5984" -jar /your-path/couchdb-lucene-0.4-jar-with-dependencies.jar -search ;以下を追加 [update_notification] indexer=/usr/java/jre1.6.0_17/bin/java -Dcouchdb.url="http://aa.bb.cc.dd:5984" -jar /your-path/couchdb-lucene-0.4-jar-with-dependencies.jar -index [httpd_db_handlers] ;以下を追加 _fti = {couch_httpd_external, handle_external_req, <<"fti">>}
書き終わったら、CouchDBを起動 or 再起動します。
$ sudo -i -u couchdb couchdb -b $ sudo /usr/local/etc/init.d/couchdb restart
4.インデックスの作成
検索の対象となるデータはあらかじめインデックスを用意しておく必要があります。インデックスは CouchDBのデザインドキュメント内に定義します。CouchDBのドキュメントに更新が入った時点でインデックスにも追加してくれるようです。先ほどの設定項目である"upddate_notification"に指定していた処理が走り出すのでしょう。このフックは全文検索の目的以外にも使えそうですね。
インデックスとして登録しておきたいドキュメントの項目を下の例のように追加していきます。この場合はドキュメントの中に"name"という項目があれば、項目の値がインデックスとして登録されます。
function(doc) { var ret=new Document(); ret.add(doc.name); ret.add(doc.type); //複数指定できる return ret; }
関数ができたら、以下のようにデザインドキュメントとして登録しておきます。全文検索の関数を書く項目は"fulltext"です。
{ "_id": "_design/f01", "_rev": "26-58900ea8d4daf981e93c827fb428272a", //ここは自動的に付与される。定義時はなくてOK "fulltext": { "by_name": { "index": "function(doc) { var ret=new Document(); ret.add(doc.name); return ret; }" } }, "views": { //通常のビュー定義など。同居可能。 } } }
次のコードはドキュメントの全ての内容をインデックスとして登録してくれます。Futonの画面からだと、ソースコードの編集が面倒です。couchappを使ってソースコードをアップロードするとよいでしょう。
function(doc) { var ret = new Document(); function idx(obj) { for (var key in obj) { switch (typeof obj[key]) { case 'object': idx(obj[key]); break; case 'function': break; default: ret.add(obj[key]); break; } } }; idx(doc); if (doc._attachments) { for (var i in doc._attachments) { ret.attachment("attachment", i); } } return ret; }
5.検索のテスト
これで検索の準備が整いました。通常の問い合わせと同じように、GETメソッドで検索結果を取得できます。"q"パラメータに検索したいキーワードを指定します。そうするとJSON形式でドキュメントの_idとキーワードに対するスコアがJSON形式で返ってきます。
$ curl http://aa.bb.cc.dd:5984/database-name/_fti/f01/by_name?q="ウィスキー"
検索結果にドキュメントの内容を全て含めたい場合はinclude_docs=trueを追加で指定します。他にもsortなど色々と便利そうなパラメータがあるようです。
_fti/f01/by_name?q="マティーニ"&include_docs=true
ただ、実際のアプリケーションに全文検索機能を組み込む場合、ドキュメント全体のデータが一度に必要になることはあまりないと思います。CouchDBであれば、_idやいくつかのデータ項目をリストとしてリンク一覧を出力してshow関数につなげるパターンがいいのかな、と思いました。全文検索の view(fulltext)をlist関数に渡したら、リスト表示がスムーズにできるのかなと淡い期待を抱いていたのですが、それははまだ無理のようです。
couchdb-luceneの導入方法がよくわからず、だいぶ長い間ハマっておりました。これでスッキリしました。まだ、全文検索をお試しでない方は是非。