Apache Solr + CassandraなソリューションのSolandraを使ってみました。日本語の全文検索が高速に出来て単一障害点のないスケーラブルなシステムに使えるかと思います。

とりあえず適当なパスにダウンロード&インストール
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 ant
Java環境はyumでインストールしておく。
yum -y install java-1.6.0-openjdk java-1.6.0-openjdk-devel
PATHを通す。

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
ビルドする
ant
Solrを日本語で検索するために必要なファイル等をそれぞれ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 -f 
SolandraのSolrに当たる部分にスキーマを登録する。スキーマは後からでも書きかえられるのでとりあえず適当に日本語で検索できるフィールドを定義しておく。スキーマの書き方がよくわからない場合は本家Solrをダウンロードしてデフォルトのschema.xmlを参考にするといいです。Solandraといってもインフラ部分をCassandraに変えただけで表面上はがっつりSolrです。
vi schema.xml




  
  
    
      
      
    
  


    
    
    

nihongo
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=日本語'



043abcdefd1日本語のテストです


XML形式で検索結果が返ってくることが確認出来る。
他にも色々検索方法があるっぽい。
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なら
日本語のテストです
abcdefd
といったデータがまとめてbigramフィールドに登録される。よってbigramフィールドに対して「日本語」とか「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'


0491

あとfacet検索。amazonとか楽天とかのナビゲーションメニューにあるカテゴリ名(件数)というような検索を簡単に実現出来るやつ。
WS000012












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(らいど)の技術メモ