Apache Solr + CassandraなソリューションのSolandraを使ってみました。日本語の全文検索が高速に出来て単一障害点のないスケーラブルなシステムに使えるかと思います。
とりあえず適当なパスにダウンロード&インストール
CentOSのyumだと1.6.*台しか見つからなかったので公式サイトからダウンロードしてくる。
パスを読み込む。
以上で設定は終わったのでバージョンを確認する。
・igo-0.4.3.jar
http://sourceforge.jp/projects/igo/downloads/52344/igo-0.4.3.jar/
・lucene-gosen-1.1.1-ipadic.jar
http://lucene-gosen.googlecode.com/files/lucene-gosen-1.1.1-ipadic.jar
・igo-analyzer-0.0.1.jar
http://sourceforge.jp/projects/igo/downloads/46413/igo-analyzer-0.0.1.jar/
Solandraを起動する
データを登録する
他にも色々検索方法があるっぽい。
N-gramのメリットは検索の精度がいいけどデメリットとしてその分インデックスサイズが大きくなりレスポンスに影響する。
形態素解析は検索の精度はN-gramには劣るけどインデックスサイズがコンパクトでN-gramより若干軽い。
N-gramを使用する場合はschema.xmlに以下のような設定を追加する。fieldType名は任意で。検索文字が一字の場合はbigram_typeタイプフィールドでは検索してもヒットしないので、unigram_typeタイプのフィールドを使用する。
multivaluedがtrueになっているとそのフィールドに他のフィールドからcopyFieldなどで複数の値をコピーすることが出来る。
例えば
のようにすればデータを登録する際にbigramフィールドにnihongoフィールドとalphaフィールドの値がコピーされる。 上記のtest.csvなら
あとはお好きなクライアントライブラリから実際にSolrにデータを登録していったりします。
とりあえずPHPクライアントがあったのでそれを使用。
http://code.google.com/p/solr-php-client/
サンプルを見つつ適当に登録してSolrにデータが登録されて検索が出来ることを確認。
データを削除したいときは、field名:値の形でdelete用のクエリを作る。
ファセットされる単位はanalyzerのTokenizer次第のためbi-gramだと二文字ごとにまとめられ、
StandardTokenizerFactoryの場合は単語ごとにまとめられる。
例えば「日本語のテストです。」という文章だとbi-gramだと「日本」「本語」「語の」とかでまとめられちゃうのでファセットとしては使えない。
StandardTokenizerとかだとたぶん「日本語」「テスト」という風に単語単位でトークン化されてファセットされる。
そのためカテゴリ名などその単語自体でまとめたい場合などは分割されないようにフィールド全体を一つのトークンにする必要がある。その場合はKeywordTokenizerFactoryを使用する。
こんな感じでfieldTypeを定義する。あとはデータを追加していってファセット出来るかを確認する。
しかし、ここで問題発生。なぜかファセット出来ない…さっきデータ消したときのやりかたがおかしかったのかな?Solrの問題かCassandraの問題か分からなかったから取りあえず、solandra-app/conf/cassandra.yamlで指定したデータ保存ディレクトリ(commitlog, data, saved_cache)以下を削除してもう一回Solandraを起動してデータを追加してみる→ファセット出来る。なんだったんだ…
あとクエリに全角や半角の空白が含まれているとクライアントからリクエストするときエラーになるので空白をエスケープする。 solrphpclientの場合searchする前に
みたいな感じでバックスラッシュしておく。
空白自体はsearchする前に自動的にURLエンコードされる。
おしまい
※2012-10-7 追記
Solandraのバグか分からないけどデータ件数が1000万件規模になるとand検索、ファセット検索が本家Solrに比べて極端に遅くなった。特にand検索「A+AND+B」のような検索はレスポンスがまともに返ってこなくてタイムアウトするレベルだった。Cassandraがボトルネックになっているような気もするので特にCassandraである必要がないならSolr本家のみでいいかもしれない。4.0台からSolrCloudで冗長化機能もパワーアップしたようなのでCassandra程の耐障害性はないもののそこそこの信頼性はあるはず。
参考にさせて頂いたサイト一覧
参考:solrでmultiValueなフィールドのunigram検索 - ひ孫
参考:solr schema.xml (1文字の日本語検索が CJKAnalyzer では駄目だった) - Ride(らいど)の技術メモ
とりあえず適当なパスにダウンロード&インストール
cd /usr/local/ git clone https://github.com/tjake/Solandra.git cd Solandraビルドする。antは1.7.0 ~以降じゃないとエラーが出るのでant -versionでantのバージョンを確認して古い場合やantがない場合は新しいバージョンのantをインストールした後Solandraをビルド。
CentOSのyumだと1.6.*台しか見つからなかったので公式サイトからダウンロードしてくる。
cd /usr/local wget http://ftp.yz.yamagata-u.ac.jp/pub/network/apache//ant/binaries/apache-ant-1.8.4-bin.tar.gz tar xvf apache-ant-1.8.4-bin.tar.gz rm -f apache-ant-1.8.4-bin.tar.gz ln -s apache-ant-1.8.4 antJava環境はyumでインストールしておく。
yum -y install java-1.6.0-openjdk java-1.6.0-openjdk-develPATHを通す。
vi /etc/profile 以下を追記 export JAVA_HOME=/usr/lib/jvm/java-1.6.0-openjdk.x86_64 export ANT_HOME=/usr/local/ant export PATH=$PATH:$JAVA_HOME/bin:$ANT_HOME/bin
パスを読み込む。
source /etc/profile
以上で設定は終わったのでバージョンを確認する。
ant -version Apache Ant(TM) version 1.8.4 compiled on May 22 2012 cd /usr/local/Solandra ビルドする antSolrを日本語で検索するために必要なファイル等をそれぞれsolandra-app/libフォルダ以下に配置する。
・igo-0.4.3.jar
http://sourceforge.jp/projects/igo/downloads/52344/igo-0.4.3.jar/
・lucene-gosen-1.1.1-ipadic.jar
http://lucene-gosen.googlecode.com/files/lucene-gosen-1.1.1-ipadic.jar
・igo-analyzer-0.0.1.jar
http://sourceforge.jp/projects/igo/downloads/46413/igo-analyzer-0.0.1.jar/
Solandraを起動する
cd solandra-app bin/solandra -fSolandraのSolrに当たる部分にスキーマを登録する。スキーマは後からでも書きかえられるのでとりあえず適当に日本語で検索できるフィールドを定義しておく。スキーマの書き方がよくわからない場合は本家Solrをダウンロードしてデフォルトのschema.xmlを参考にするといいです。Solandraといってもインフラ部分をCassandraに変えただけで表面上はがっつりSolrです。
vi schema.xmlnihongo id
curl http://localhost:8983/solandra/schema/igo --data-binary @schema.xml -H 'Content-type:text/xml; charset=utf-8'上記の「igo」というのがschema.xmlを登録するコア名です。ここは任意の名前でOK。
データを登録する
vi test.csv id,nihongo,alpha 1,日本語のテストです,abcdefd
curl http://localhost:8983/solandra/igo/update/csv?commit=true --data-binary @test.csv -H 'Content-type:text/plain; charset=utf-8'検索する
curl 'http://localhost:8983/solandra/igo/select?q=日本語'XML形式で検索結果が返ってくることが確認出来る。0 43 abcdefd 1 日本語のテストです
他にも色々検索方法があるっぽい。
curl 'http://localhost:8983/solandra/igo/select?q=alpha:abcdefd,nihongo:日本語' //複数フィールドで検索 curl 'http://localhost:8983/solandra/igo/select?q=alpha:abcdefd,nihongo:日本語&wt=json' // レスポンスの形式をJSONにする curl 'http://localhost:8983/solandra/igo/select?q=alpha:abcdefd,nihongo:日本語&wt=json&indent=on' レスポンスをインデントさせて見やすくする curl 'http://localhost:8983/solandra/igo/select?q=alpha:abcdefd,nihongo:日本語&wt=json&indent=on&rows=1' //取得行数を1行までにする他にもN-gramで日本語検索も出来る。上記のスキーマの場合インデックスの作成に形態素解析を使用しているので「日本語」と検索したらヒットするが「語の」とか「テス」ではヒットしない。
N-gramのメリットは検索の精度がいいけどデメリットとしてその分インデックスサイズが大きくなりレスポンスに影響する。
形態素解析は検索の精度はN-gramには劣るけどインデックスサイズがコンパクトでN-gramより若干軽い。
N-gramを使用する場合はschema.xmlに以下のような設定を追加する。fieldType名は任意で。検索文字が一字の場合はbigram_typeタイプフィールドでは検索してもヒットしないので、unigram_typeタイプのフィールドを使用する。
あとは以下の様にunigram_type型とbigram_type型を使うフィールドを定義する。
multivaluedがtrueになっているとそのフィールドに他のフィールドからcopyFieldなどで複数の値をコピーすることが出来る。
例えば
のようにすればデータを登録する際にbigramフィールドにnihongoフィールドとalphaフィールドの値がコピーされる。 上記のtest.csvなら
といったデータがまとめてbigramフィールドに登録される。よってbigramフィールドに対して「日本語」とか「abcdefd」とか検索しても同じレコードがヒットする。日本語のテストです abcdefd
schema.xmlの定義でcopyFieldを↑のように設定しとけば全てのフィールドをとりあえずbigramでインデックスさせとくことが出来る。
あとはお好きなクライアントライブラリから実際にSolrにデータを登録していったりします。
とりあえずPHPクライアントがあったのでそれを使用。
http://code.google.com/p/solr-php-client/
サンプルを見つつ適当に登録してSolrにデータが登録されて検索が出来ることを確認。
データを削除したいときは、field名:値の形でdelete用のクエリを作る。
vi deleteAll.xml
このようなxmlファイルを作成しupdateで削除する。*:*
curl http://localhost:8983/solandra/igo/update --data-binary @deleteAll.xml -H 'Content-type:text/xml; charset=utf-8'あとfacet検索。amazonとか楽天とかのナビゲーションメニューにあるカテゴリ名(件数)というような検索を簡単に実現出来るやつ。0 491
curl 'http://localhost:8983/solandra/igo/select?q=*:*&indent=on&facet=true&facet.field=id'上のfacet.fieldで指定したフィールドで検索結果がグループ化される。
ファセットされる単位はanalyzerのTokenizer次第のためbi-gramだと二文字ごとにまとめられ、
StandardTokenizerFactoryの場合は単語ごとにまとめられる。
例えば「日本語のテストです。」という文章だとbi-gramだと「日本」「本語」「語の」とかでまとめられちゃうのでファセットとしては使えない。
StandardTokenizerとかだとたぶん「日本語」「テスト」という風に単語単位でトークン化されてファセットされる。
そのためカテゴリ名などその単語自体でまとめたい場合などは分割されないようにフィールド全体を一つのトークンにする必要がある。その場合はKeywordTokenizerFactoryを使用する。
KeywordTokenizerFactoryはフィールド全体を一つのトークンとしてトークン化する。ただそうすると通常の検索時にはトークンが一つしかないため完全一致や後方一致、前方一致でしかひっかからなくなってしまうため検索用のフィールドにcopyFieldしてファセット用とは別にインデックスを持っておいた方がいいかもしれない。先ほどの例でいえばbigramフィールドにcopyFieldしといてbigramフィールド自体はindexd=true stored=falseでインデックスのみ保持でデータ自体は保持しないようにしておくとか。
こんな感じでfieldTypeを定義する。あとはデータを追加していってファセット出来るかを確認する。
しかし、ここで問題発生。なぜかファセット出来ない…さっきデータ消したときのやりかたがおかしかったのかな?Solrの問題かCassandraの問題か分からなかったから取りあえず、solandra-app/conf/cassandra.yamlで指定したデータ保存ディレクトリ(commitlog, data, saved_cache)以下を削除してもう一回Solandraを起動してデータを追加してみる→ファセット出来る。なんだったんだ…
あとクエリに全角や半角の空白が含まれているとクライアントからリクエストするときエラーになるので空白をエスケープする。 solrphpclientの場合searchする前に
$query = str_replace(' ', '\ ', $query);
みたいな感じでバックスラッシュしておく。
空白自体はsearchする前に自動的にURLエンコードされる。
おしまい
※2012-10-7 追記
Solandraのバグか分からないけどデータ件数が1000万件規模になるとand検索、ファセット検索が本家Solrに比べて極端に遅くなった。特にand検索「A+AND+B」のような検索はレスポンスがまともに返ってこなくてタイムアウトするレベルだった。Cassandraがボトルネックになっているような気もするので特にCassandraである必要がないならSolr本家のみでいいかもしれない。4.0台からSolrCloudで冗長化機能もパワーアップしたようなのでCassandra程の耐障害性はないもののそこそこの信頼性はあるはず。
参考にさせて頂いたサイト一覧
参考:solrでmultiValueなフィールドのunigram検索 - ひ孫
参考:solr schema.xml (1文字の日本語検索が CJKAnalyzer では駄目だった) - Ride(らいど)の技術メモ