2008/02/20
カテゴリ : Tech
Senna
Tritonn
Ludia
PostgreSQL
Ludia (PostgreSQL + Senna) で全文検索
先日のデブサミで華々しく散ってきた森川です。最近 8.3 がリリースされたPostgreSQLにLudiaという全文検索モジュールを組み込んで、MySQLのTritonnと比較してみました。
インストールについては、それぞれのサイトに書いてあるので割愛します(Ludia、Tritonn)。
今回使用したテキストは青空文庫から太宰治の作品を拝借しました。以下のようなテーブルに作品名と内容を入れています。
MySQLでは、text型ではおさまりきらない作品もあったので、contentsをlongtext型としています。インデックスは両方ともMecabを使用した単語インデックスを使用しました。インデックスのサイズは変わりません(同じテキスト・ライブラリを使用しているので当然といえば当然ですね)。
Ludiaの検索についてですが、たとえば「人間」「失格」の2つの単語が入っているもの単語の多い順に調べる場合は、以下のようにします。pgs2getscoreを使用することで、検索スコアを取得することができます。
Tritonnでは以下のようします。MATCH AGAINSTをSELECTの中で使用することで検索スコアを取得します。IN BOOLEAN MODEを指定することで、Sennaの検索クエリを使用することが可能になります。
結果については、完全に同じと言いたいところだったのですが、なぜかスコアの値が若干変わってしまいます。おそらく内部で使用しているSennaのバインディング部分が異なるからだと思うのですが、他のクエリでも完全にスコアを同じ値にすることはできませんでした。
最後にSennaの検索クエリの「*S」演算子を紹介したいと思います。これを使用すると指定した文章と関連した(両方に含まれる単語の数で決まる)文書を検索することができます。たとえば、人間失格のあらすじ(Wikipedia)から関連する文章を取得するクエリは以下のようになります。
SELECT name, pgs2getscore(ludia_test.ctid, 'fulltext_contents_index')
FROM ludia_test
WHERE contents @@ '*S「自分」は人とは違う感覚を持っており... 省略 ...'
一方Tritonnで同じことをすると以下のようになります。とりあえず似たような結果になりますが、結構違いますね。
使い方に問題があるのかもしれませんが、使用してみた感じでは、完全にTritonnと同じ結果を出すのは難しいようです。ただ、普通に単語検索として使う分にはほとんど結果は変わらないので、問題は特に感じませんでした。
Sennaを使った全文検索といえば、MySQLというイメージがあったのですが、PostgreSQLでも特に問題ないということがわかりました。実際、公式サイトを見る感じでは、色々と実績はあるようなので、これから機会があれば使ってみたいと思います。
インストールについては、それぞれのサイトに書いてあるので割愛します(Ludia、Tritonn)。
今回使用したテキストは青空文庫から太宰治の作品を拝借しました。以下のようなテーブルに作品名と内容を入れています。
- PostgreSQL:
CREATE TABLE ludia_test ( id serial primary key, name text, contents text ); CREATE INDEX fulltext_contents_index ON ludia_test USING fulltext(contents); - MySQL:
CREATE TABLE ludia_test ( id integer not null auto_increment primary key, name text, contents longtext ); CREATE FULLTEXT INDEX fulltext_contents_index ON ludia_test(contents);
MySQLでは、text型ではおさまりきらない作品もあったので、contentsをlongtext型としています。インデックスは両方ともMecabを使用した単語インデックスを使用しました。インデックスのサイズは変わりません(同じテキスト・ライブラリを使用しているので当然といえば当然ですね)。
Ludiaの検索についてですが、たとえば「人間」「失格」の2つの単語が入っているもの単語の多い順に調べる場合は、以下のようにします。pgs2getscoreを使用することで、検索スコアを取得することができます。
- SELECT
name, pgs2getscore(ludia_test.ctid, 'fulltext_contents_index') - FROM
ludia_test - WHERE
contents @@ '*D+ 人間 失格'
Tritonnでは以下のようします。MATCH AGAINSTをSELECTの中で使用することで検索スコアを取得します。IN BOOLEAN MODEを指定することで、Sennaの検索クエリを使用することが可能になります。
- SELECT
name, match(contents) against('*D+ 人間 失格') as score - FROM
ludia_test - WHERE
match(contents) against('*D+ 人間 失格' IN BOOLEAN MODE) - ORDER
BY score DESC;
結果については、完全に同じと言いたいところだったのですが、なぜかスコアの値が若干変わってしまいます。おそらく内部で使用しているSennaのバインディング部分が異なるからだと思うのですが、他のクエリでも完全にスコアを同じ値にすることはできませんでした。
- Ludia
name | pgs2getscore - --------------+--------------
人間失格 | 455 鉄面皮 | 40 桜桃 | 20 懶惰の歌留多 | 20 俗天使 | 15 二十世紀旗手 | 10 - Tritonn
- +--------------------+-------+
- |
name | score | - +--------------------+-------+
- |
人間失格 | 465 | - |
鉄面皮 | 45 | - |
桜桃 | 20 | - |
懶惰の歌留多 | 20 | - |
俗天使 | 15 | - |
二十世紀旗手 | 10 | - +--------------------+-------+
最後にSennaの検索クエリの「*S」演算子を紹介したいと思います。これを使用すると指定した文章と関連した(両方に含まれる単語の数で決まる)文書を検索することができます。たとえば、人間失格のあらすじ(Wikipedia)から関連する文章を取得するクエリは以下のようになります。
SELECT name, pgs2getscore(ludia_test.ctid, 'fulltext_contents_index')
FROM ludia_test
WHERE contents @@ '*S「自分」は人とは違う感覚を持っており... 省略 ...'
name | pgs2getscore - -----------------------------------------------+--------------
人間失格 | 12475 畜犬談 | 4835 思ひ出 | 3760 お伽草紙 | 2695 もの思う葦 ――当りまえのことを当りまえに語る。 | 2345 男女同権 | 2060 女の決闘 | 1575 新釈諸国噺 | 1570 苦悩の年鑑 | 1410 善蔵を思う | 1365 パンドラの匣 | 1360 津軽 | 1130 正義と微笑 | 1060 女神 | 1060 - ...
省略 ... - (47
rows)
一方Tritonnで同じことをすると以下のようになります。とりあえず似たような結果になりますが、結構違いますね。
- +--------------------+-------+
- |
name | score | - +--------------------+-------+
- |
人間失格 | 5085 | - |
猿面冠者 | 4225 | - |
答案落第 | 4225 | - |
畜犬談 | 3180 | - |
服装に就いて | 2120 | - |
正義と微笑 | 1905 | - |
パンドラの匣 | 1410 | - |
お伽草紙 | 1060 | - |
善蔵を思う | 1060 | - |
盲人独笑 | 1060 | - ...
省略 ... - 20
rows in set (0.00 sec)
使い方に問題があるのかもしれませんが、使用してみた感じでは、完全にTritonnと同じ結果を出すのは難しいようです。ただ、普通に単語検索として使う分にはほとんど結果は変わらないので、問題は特に感じませんでした。
Sennaを使った全文検索といえば、MySQLというイメージがあったのですが、PostgreSQLでも特に問題ないということがわかりました。実際、公式サイトを見る感じでは、色々と実績はあるようなので、これから機会があれば使ってみたいと思います。
コメント
コメントフォーム
トラックバックURI
最近の記事
システム開発エンジニア募集! [2012年02月03日 : 小林有佳]
OpenVPNで細々便利な設定 [2012年01月31日 : 門脇優児]
【iOS】Viewの開発・デバッグに役立つ色々 [2012年01月23日 : 中川善樹]
PHPDocumentorの利用方法まとめ [2012年01月19日 : 笹亀弘]
Google Chart Toolsを使ってサイトマップを描こう! [2011年12月21日 : 志田仁美]
stumpwm設定v2 [2011年12月19日 : 門脇優児]
Mashup Awards 7の授賞式が行われました [2011年12月16日 : 中川善樹]
社員旅行に行きました [2011年12月12日 : 大橋寛子]
iCloud風のアイコンを作成する(Fireworks) [2011年12月07日 : 和田記光]
iScroll4でネイティブに近いスマホ向けHTMLページを作成する [2011年12月02日 : 松田惇]













お元気ですか?
スコアが違う理由は「Ludiaがインデックススキャンを選択していないため」です。
レコード数を増やして、調査してみてください。
インデックススキャンが選択され、(たぶん)Tritonnと同じ結果になると思います。
ちなみに、インデックススキャンが選択されているかどうかは、
EXPLAIN SELECT name FROM ludia_test WHERE contents @@ '*D+ 人間 失格'
でチェックできます。
PostgreSQL8.3+Ludia1.5(来月リリース予定)では、
レコードが少なくても、インデックススキャンが選択されます。