2011年7月27日

DotCloud で Solr を使ってみる

DotCloud で Solr 3.1 を使えるようなので、簡単な検索 Web アプリを作ってみます。

Solr の基本

Solr は検索用の Web アプリケーションで、Tomcat などのコンテナに WAR を置けば使えます。 もっとも簡単な試用方法は Jetty を使うことで、これは Solr の配布用アーカイブに含まれているサンプルで確認できます。

Solr の最新バージョンは 3.3 ですが、DotCloud で利用可能なバージョンは 3.1 なので、 まずはこれをダウンロードして展開してみます。

$ wget http://ftp.riken.jp/net/apache//lucene/solr/3.1.0/apache-solr-3.1.0.tgz
$ tar xzvf apache-solr-3.1.0.tgz
$ cd apache-solr-3.1.0/
$ ls . dist example
.:
CHANGES.txt  LICENSE.txt  NOTICE.txt   README.txt   client/      contrib/     dist/        docs/        example/

dist:
apache-solr-3.1.0.war                           apache-solr-core-3.1.0.jar                      apache-solr-uima-3.1.0.jar
apache-solr-analysis-extras-3.1.0.jar           apache-solr-dataimporthandler-3.1.0.jar         solrj-lib/
apache-solr-cell-3.1.0.jar                      apache-solr-dataimporthandler-extras-3.1.0.jar
apache-solr-clustering-3.1.0.jar                apache-solr-solrj-3.1.0.jar

example:
README.txt   etc/         example-DIH/ exampledocs/ lib/         logs/        multicore/   solr/        start.jar    webapps/     work/

dist ディレクトリには JAR ファイルと WAR ファイルがあり、 これをそのままコピーすれば必要なコンポーネントを使えます。 Web コンテナには apache-solr-3.1.0.war を置くことになります。

example ディレクトリはサンプルを実行できる環境になっています。 solr ディレクトリに設定ファイルを置き、 start.jar を実行させると Jetty を使ってサーバが起動します。 サーブレットに関するものは lib ディレクトリに置かれています。

$ cd example/
$ java -jar start.jar

Web ブラウザで http://localhost:8983/solr/admin/ にアクセスすると、Solr の管理画面になります。

どのフィールドでどのような値を管理するかは SCHEMA のリンクから確認できます。 また、検索クエリに対してフィルタをかけたり、コンポーネントを実行させる設定は CONFIG のリンクから確認できます。 これらはそれぞれ schema.xmlsolrconfig.xml に対応しており、どちらも solr/conf ディレクトリにあります。

$ tree -L 2 solr
solr
|-- README.txt
|-- bin
|-- conf
|   |-- admin-extra.html
|   |-- elevate.xml
|   |-- mapping-FoldToASCII.txt
|   |-- mapping-ISOLatin1Accent.txt
|   |-- protwords.txt
|   |-- schema.xml
|   |-- scripts.conf
|   |-- solrconfig.xml
|   |-- spellings.txt
|   |-- stopwords.txt
|   |-- synonyms.txt
|   |-- velocity
|   `-- xslt
|-- data
|   |-- index
|   `-- spellchecker
`-- solr.xml

bin ディレクトリはユーティリティスクリプト置き場、 data ディレクトリは Lucene (実際の検索エンジン) のデータ置き場ですから、 「設定ファイル」として必要なものは solr.xmlconf ディレクトリ以下のものになります。

つまり、ちょっと乱暴な表現をするならば、Solr の WAR ファイルと solr.xmlconf ディレクトリの中のファイルがあれば Solr を動かせる、と言えます。

Solr のバージョン

Solr は Lucene と密接に関係しています。 初めは別々のレポジトリで開発されていましたが、ある時からレポジトリが統合されました。 それからほどなくして、リリースも一緒のタイミングで実施されるようになりました。 そして、バージョン番号も合わせることになりました。

統合後の最初のリリースが 3.1 です。 以前の Solr のバージョンは 1.4 でしたが、一気に飛んだことになります。

バージョンは検索インデクスにも影響があり、Solr は Lucene のどのバージョンを使っているかを把握する必要があります。 これは solrconfig.xmlluceneMatchVersion に記述されています。

$ grep luceneMatchVersion solr/conf/solrconfig.xml
  <luceneMatchVersion>LUCENE_31</luceneMatchVersion>

設定ファイルと実行環境でこの設定が異なっていると良ろしくありませんので、 最初に Solr 3.1 をダウンロードしたのはこのためです。 特に、Solr 1.4 と Solr 3.1 では Java オプジェクトをシリアライズした形式が異なるため、 クライアント側で Java を使う場合には注意しなければなりません。

DotCloud への配置

DotCloud は WAR ファイルとコンテナ、そして、そのコンテナを監視する仕組みを提供してくれます。 開発者は設定ファイルだけを管理し、 DotCloud 用の設定ファイル (dotcloud.yml) を用意してプッシュすれば Solr を使えるようになります。

一番最初は Solr のアーカイブから example/solr を丸ごとコピーするのが分かりやすいと思います。 スキーマ定義を変更したい場合には schema.xml 、ハンドラなどを変更したい場合には solrconfig.xml を編集します。

手元のマシンで用意するもの:

$ tree .
.
|-- conf
|   |-- admin-extra.html
|   |-- elevate.xml
|   |-- mapping-FoldToASCII.txt
|   |-- mapping-ISOLatin1Accent.txt
|   |-- protwords.txt
|   |-- schema.xml
|   |-- scripts.conf
|   |-- solrconfig.xml
|   |-- spellings.txt
|   |-- stopwords.txt
|   `-- synonyms.txt
|-- dotcloud.yml
`-- solr.xml

DotCloud へのプッシュ:

$ cat dotcloud.yml
search:
  type: solr
$ dotcloud push solr .

これで http://{dotcloud-id}.dotcloud.com/solr/admin/ にアクセスすれば Solr の管理画面になります。

ドキュメントのインデクシング

適当なドキュメント群からインデクスデータを作成します。 インデクスデータを作成するには、Solr の update ハンドラに XML を POST します。 Solr が XML を解釈して、Lucene が文字列を解析してくれます。

たとえば Sphinx で管理しているドキュメントの場合には、 プレインテキストに変換したものを XML でラップして POST すれば良いだけです。

今回は、以前に書いた翻訳ドキュメントをインデクシングしてみました。

$ cd {SPHINX_DOCUMENT_DIR}
$ make text
$ python solr-document-indexer.py http://{dotcloud-id}.dotcloud.com/solr _build/text

スクリプトには色々とハードコードしていますが、とりあえず目的は達成できたので良しとします。

最後に、Solr 管理画面の "Query String" に "*:*" と入力してから "Search" ボタンをクリックして確認します。 1件以上のドキュメントが検索結果として返ってくれば成功です。

プロキシの配置

Solr は XML で出力するだけでなく、いくつかのライタータイプを切り替えて使うこともできます。 その一つに JSON ライターがあります。 JSON ならば、Web ブラウザまで渡せば色々と操作できます。

しかし、Web ブラウザがドメインを超えて通信するにはいくつかの制約がありますので、 適当なプロキシを噛ませることにします。 DotCloud では PHP も使えますので、curl モジュールを使って通信を橋渡しさせます。

先ほどの Solr とは別のプロジェクトスペースを用意し、 次のようなスクリプトを search.php としておきます。

<?php
header('Content-type: text/json');

const SOLR_SERVER = 'http://{dotcloud-id}.dotcloud.com';
$url = SOLR_SERVER . 'solr/select?';
$keyword = isset($_GET['q']) ? $_GET['q'] : '*';
$offset = isset($_GET['offset']) ? $_GET['offset'] : 0;
$query = "text:'" . $keyword . "'";
$param = array('q' => $query,
               'fl' => 'id,title,url',
               'hl' => 'on',
               'hl.fl' => 'content',
               'wt' => 'json',
               'start' => $offset,
               'rows' => 20);
$ch = curl_init($url . http_build_query($param));
// passthru output.
curl_exec($ch);
curl_close($ch);

次に、検索画面用の HTML ファイルを index.html とし、適当な JavaScript を書きます。 ユーザーが検索ボックスに入力したものを search.php に "q" パラメータとして渡します。 結果は JSON で返されますので、あとはそれを解釈すれば良いだけです。

なお、サーバ側でセットしている "hl"、"hl.fl" パラメータはハイライト用の設定です。 検索スピードは低下しますが、必要に応じてこれらを使うのも良いと思います。 ハイライトされたフィールドを JSON で扱う場合には ID の参照が必要になりますが、 JSON をその都度確認すれば解釈可能でしょう。

最後に DotCloud の設定ファイル (dotcloud.yml) を用意します。

www:
  type: php

あとはコマンドラインツールからプッシュすれば検索画面の出来上がりです。

DotCloud には複数のインスタンスを配置できますので、 Solr サーバと PHP アプリで同じ設定ファイルを使うこともできます。 この場合、サーバ側の設定ファイル (/home/dotcloud/environment.json) に Solr サーバの URL が記載されていますので、次のようにして参照できます。

$envjson = json_decode(file_get_contents("/home/dotcloud/environment.json"), true);
const SOLR_SERVER = $envjson['DOTCLOUD_SEARCH_HTTP_URL']

この辺りは管理上の好みもあるでしょうから、どちらでも良いんじゃなかなと思います。

終わりに

DotCloud で Solr を使ってみました。 課金について何も気にしていませんので、どれくらいのインデクス量ならどの程度の費用がかかるかは分かりません。 とはいえ、プッシュすれば rsync で同期され、サーバ類が適切に再起動されるのは嬉しいですね。

検索に関しては、ちょっと Solr を使ってみる、あるいは、手頃な検索手段が無いドキュメント (*) をなんとかする、 という点ではお手軽な方法ではないかな、と思いました。 また、Solr にはファセット検索などもありますので、 関連する検索キーワードを提示したり、惜しいキーワードを拾い上げることも出来るかもしれませんね。

(*) Sphinx 1.0 系に組み込まれた検索手法だと、日本語をきちんと検索できません。 1.1 がリリースされれば改善されるかもしれません。
コメントを投稿