Lucandra を使ってみる 〜第 2 回〜
☞ Solandra を動かすまで
2010/8/19
株式会社ぐるなび 佐藤 史彦
Agenda
➲ 前回までのおさらい
➲ Solandra を動かす
➲ 今回のまとめ
- 前回までのおさらい -
➲ Lucandra とは?
➲ Index 構成
➲ 特徴
➲ 注意点
Lucandra とは ?
Cassandra ベースの Lucene バックエンド Lucene の Index を Cassandra にストア
(IndexWriter/Reader の Cassandra 対応 ) 対応する Lucene のバージョンは 2.9.1
Cassandra は 0.6 系
Disk Java
Application
HitsHits DocumentDocument Document Document
Lucene
Document Document
Field Field
Field Field
Field Field
インデックス作 成
QueryParser QueryParser Document
Document Document Document
Document Document
検索
Analyzer Analyzer
Query Query
Lucene Index Lucene Index IndexReader
IndexReader
IndexWriter IndexWriter Analyzer
Analyzer IndexSearcher
IndexSearcher
Cassandra Java
Application
HitsHits DocumentDocument Document Document
Lucandra
Document Document
Field Field
Field Field
Field Field
インデックス作 成
QueryParser QueryParser Document
Document Document Document
Document Document
検索
Analyzer Analyzer
Query Query
Lucene Index Lucene Index IndexReader
IndexReader
IndexWriter IndexWriter Analyzer
Analyzer IndexSearcher
IndexSearcher
Keyspace : Lucandra
Index 構成
ColumnFamily : Document
Key : インデックス名のハッシュ + ドキュメント ID Column Name : フィールド名
Value :フールド値
SuperColumnFamily : TermInfo
Key :( インデックス名 + フィールド名 ) のハッシュ + フィールド名 + 単語 SuperColumn : ドキュメント ID
Column Name : Frequencies
Value :当該文書中の当該単語の出現頻度 Column Name : Norms
Value :当該単語における文書のノルム Column Name : Offsets
Value :当該文書中の当該単語のバイト位置オフセッ トColumn Name : Position
Value :当該文書中の当該単語の出現位置 インデックス生
成時のオプショ ンによって、作 られるカラムが 決まる(Lucene と同じ )
特徴
➲ Lucene API がほぼそのまま利用可能
➲ Index のスケーラビリティは Cassandra 依 存
➲ Partitioner に OrderPreservingPartitioner を 使う(と、フル機能が使える)
➲ できること ☞ 次スライド
README より Lucandra でできること
1 Real-Time indexing
(documents become available almost immediately)
2 No optimizing 3 Search
4 Sort
5 Range Queries 6 Delete
7 Wildcards and other Lucene magic 8 Faceting/Highlighting
4,5,7 -> RandomPartitioner では不可
注意点
➲ RandomPartitioner ではできないことがあ る(レンジスキャンを必要とするもの)
➲ OrderPreservingPartitioner だと、単一イン デックスで大量のドキュメントを扱う場合 にデータが偏りがちになると予想される
→ InitialToken の調整で対応?
注意点
➲ Analyzer で複数単語の Query が生成される もの( N-gram など)を使用する場合
は、 TermInfo の Position カラムを作成する 必要がある(ここは Lucene とは違うとこ ろ)
指定例:
doc.add(new Field("name", name, Store.YES,
Index.ANALYZED,
Field.TermVector.WITH_POSITIONS));
- Solandra 動かす -
➲ Solandra とは ?
➲ ビルド
➲ 設定
➲ 起動
➲ データストア
➲ 検索
Solandra とは ?
Cassandra ベースの Solr バックエンド Solr の Index を Cassandra にストア
(IndexWriter/Reader の Cassandra 対応 ) 対応する Solr のバージョンは 1.4.0
Solandra とは ?
Solr が Lucene ベースの検索サーバであるよ うに、 Solandra は Lucandra がベースとい うだけ
ちなみに、 Solr の特徴は
- XML/HTTP や JSON, Ruby, Python の API - 検索ヒット箇所のハイライト機能
- ファセット検索 - キャッシュ機能
- レプリケーション機能 - Web 管理インタフェース
ビルド
lucandra.jar に solandra も含まれているの
で、 Lucandra をビルドしていれば必要なし
solr-example/ に Solr(+Jetty) 一式があります
$ tar xztf tjake-Lucandra-c632677.tar.gz
$ cd tjake-Lucandra-c632677
$ ant lucandra.jar
$ cd solr-example
設定 (solrconfig)
solrconfig.xml にて、 Cassandra(Thrift) の設定 をします
(1) SolandraIndexWriter の部分
$ vi solr/conf/solrconfig.xml
<!-- the Solandra high-performance update handler --> <updateHandler class="solandra.SolandraIndexWriter"> <str name="cassandraHost">192.168.56.101</str>
<int name="cassandraPort">9160</int>
<bool name="cassandraFramed">false</bool> </updateHandler>
設定 (solrconfig)
(2) SolandraIndexReaderFactory の部分
この通り、 Writer/Reader に指定するクラス Solandraが 固有のものになっていることが
わかります
<indexReaderFactory name="IndexReaderFactory" class="solandra.SolandraIndexReaderFactory"> <str name="indexName">solrshopsearch</str>
<str name="cassandraHost">192.168.56.101</str> <int name="cassandraPort">9160</int>
<bool name="cassandraFramed">false</bool> </indexReaderFactory >
設定 (solrconfig)
ここでは Cassandra 接続の設定が効いているかどうか確認す るために、単一ノードだがあえて外部サーバの Cassandra を使用した
実際、エラーが出て結構はまってしまったのだが Lucandra では設定ホストに初回接続後、 describe_ring で取得した end-point に対してランダムに接続する実装が含まれている
( lucandra.CassandraProxyClient )
上記はメソッドの引数で「ランダム」か「単体直」かを指 定できるのだが、そのメソッドを呼んでいる部分が
「ランダム」でハードコーディングされている
設定 (solrconfig)
SolandraIndexWriter
コレ
SolandraIndexReaderFactory
設定 (solrconfig)
コレ
設定 (solrconfig)
ちなみに、エラーが出た理由は、 Cassandra が単一ノードで ノード間接続の ListenAddress が localhost だったため、
describe_ring で取得される end-point が 127.0.0.1 だった
storage-conf.xml: <!--
~ Address to bind to and tell other nodes to connect to. You _must_ ~ change this if you want multiple nodes to be able to communicate! ~
~ Leaving it blank leaves it up to InetAddress.getLocalHost(). This ~ will always do the Right Thing *if* the node is properly configured ~ (hostname, name resolution, etc), and the Right Thing is to use the ~ address associated with the hostname (it might not be).
-->
<ListenAddress>localhost</ListenAddress> <!-- internal communications port --> <StoragePort>7000</StoragePort>
設定 (schema)
次に、 schema.xml にて Index の設定をしま す
ここでの設定は Solr でスキーマ定義をする 場合と一緒です(あまり詳しくはないけど)
$ vi solr/conf/schema.xml
設定 (schema)
今回は第 1 回の Lucandra で使った日本語サ ンプルと同様に、某飲食店検索 API から取得 したデータを使うことを想定します
- id 店舗 ID (ユニークキー) - name 店舗名称
- url 店舗ページ URL - address 住所
- tel 電話番号 - budget 平均予算
設定 (schema)
(1) スキーマ名(=インデックス名)を変更
※現行では複数定義できないのがいまいち
(2) schema/types/fieldType を追加
( CJKAnalyzer を使う fieldType を定義)
<schema name="solrshopsearch" version="1.2">
<fieldType name="text_cjk" class="solr.TextField"> <analyzer
class="org.apache.lucene.analysis.cjk.CJKAnalyzer"/>
</fieldType>
設定 (schema)
(3) schema/fields/field の内容を今回のデータ に合わせて変更します
<field name="id" type="string" indexed="true" stored="true" required="true" />
<field name="name" type="text_cjk" indexed="true" stored="true" termPositions="true" />
<field name="url" type="text" indexed="false" stored="true" />
<field name="address" type="text_cjk" indexed="true" stored="true" termPositions="true" />
<field name="tel" type="text" indexed="true" stored="true" termPositions="true" />
<field name="budget" type="int" indexed="true" stored="true" />
設定 (schema)
(4) 全文検索用のフィールド (name="text") を CJKAnalyzer に対応させます
(5) schema/uniqueKey (ユニークキー フィールド)を設定します
(今回はデフォルトのまま)
<field name="text" type="text_cjk" indexed="true" stored="false" multiValued="true"
termPositions="true" />
<uniqueKey>id</uniqueKey>
設定 (schema)
(6) schema/defaultSearchField (デフォルト の全文検索用フィールド)を設定します
(こちらも今回はデフォルトのまま)
(7) schema/copyField で全文検索対象にする フィールドをコピーする指定をします
<defaultSearchField>text</defaultSearchField>
<copyField source="id" dest="text"/>
<copyField source="name" dest="text"/>
<copyField source="address" dest="text"/>
<copyField source="tel" dest="text"/>
起動
Jetty を起動します
ブラウザから管理ツールを確認します http://localhost:8983/solr/admin/
$ java -jar start.jar
起動
データストア
exampledocs/ にデータポスト用のスクリプ ト( post.sh )があります
あらかじめ作っておいた某飲食店データの XML ( data.xml )を投入します
$ cd exampledocs
$ ./post.sh data.xml
データストア
ちなみに、データはこんな感じです
$ head data.xml
<add> <doc>
<field name="id">a683900</field>
<field name="name">CAFE & BAR DONQ MARU NOUCHI </field>
<field
name="url">http://r.gnavi.co.jp/a683900/</f ield>
<field name="address"> 〒 100-0005 東京都千代田区丸の 内 2-1-1 明治安田生命ビル丸の内マイプラザ B2</field>
<field name="tel">03-5219-5481</field> <field name="budget">2500</field>
</doc> <doc>
検索
管理ツールからクエリを投げてみます
中段にある QueryString にクエリを記述して Search ボタンをクリックします
検索
こんな結果が返ります
検索
JSON もあります( wt=json を指定)
- 今回のまとめ -
➲ Solandra の動かし方を確認した
➲ 途中からうっすら気づいていたように、後 半はほとんど Solr の話しかしていない
➲ つまり、 lib に lucandra.jar を入れて solrconfig の IndexWriter/Reader を
Solrandra に変更すれば、既存の Solr 環境 にも適用できる