N-gram を使用した、組み込み型の日本語全文検索です。
日本語テキストの全文検索を行います。 形態素解析ベースである textsearch-ja とは異なり、textsearch_senna では N-gram ベースの全文検索を行います。 検索には、全文検索エンジン Senna を使用しています。
利点として、文字すべてをインデックス化するため LIKE 中間一致検索に近い結果を得ることができます。 検索用インデックスには、専用の検索演算子の他、LIKE 演算子をそのまま使うこともできます。 また、既に tsearch2 互換のインタフェースを備えるため、少々の設定変更で textsearch-ja と textsearch_senna を切り替えて使用することができます。
ただし、クラッシュ・リカバリやアーカイブ・リカバリに対応していないことに注意してください。リカバリ後にインデックスの再作成を行う必要があります。
以下の外部プロジェクトに依存しています。
最初に senna をビルドします。 詳しくは Senna のインストール方法を参照してください。 環境によっては make に長時間 (数分~数10分) かかる場合があるようです。
textsearch_senna は pgxs を使ってビルドできます。
$ cd textsearch_senna $ make USE_PGXS=1 $ su $ make USE_PGXS=1 install
その後、データベースに関数を登録します。
$ pg_ctl start $ psql -f $PGSHARE/contrib/textsearch_senna.sql your_database
Windows へバイナリをインストールするには、PostgreSQL をインストールしたフォルダ (通常は C:\Program Files\PostgreSQL\*.*, 以下 %PGHOME%) へファイルを展開します。 libsenna.dll を %PGHOME%\bin またはパスの通ったフォルダ (%WINDOWS% 等) に、textsearch_senna.dll を %PGHOME%\lib に配置します。 その後、textsearch_senna.sql によるデータベースへの登録を行います。
OPERATOR %% (document text, query text) が追加されます。 以下の形式で使用します。
=# SELECT * FROM tbl WHERE document %% '検索キーワード';
組み込みの全文検索との互換性向上のため、OPERATOR @@ (document text, query senquery) も登録されています。
=# SELECT * FROM tbl WHERE senna.to_tsvector(document) @@ senna.to_tsquery('検索キーワード');
senna.to_tsvector(), senna.to_tsquery() を組み込みの同名の関数よりも優先して使用する場合は、設定パラメータ search_path を 'senna', 'pg_catalog' の順に検索するよう設定します。
=# SET search_path = 'senna','pg_catalog','$user','public'; =# SELECT * FROM tbl WHERE to_tsvector(document) @@ to_tsquery('検索キーワード');
'senna' というインデックス・アクセス・メソッドが登録されます。 CREATE INDEX の際に USING senna を指定することで使用できます。
=# CREATE TABLE test (id serial, t text); =# COPY test(t) FROM '...'; =# CREATE INDEX idx ON test USING senna (t); =# ANALYZE; =# EXPLAIN SELECT * FROM test WHERE t %% 'リレーショナルデータベース'; QUERY PLAN -------------------------------------------------------------------- Index Scan using idx on test (cost=0.00..55.01 rows=615 width=36) Index Cond: (t %% 'リレーショナルデータベース'::text) (2 rows)
LIKE 演算子でインデックスを使うためには、like_ops
演算子クラスを指定します。
LIKE の中間一致検索 (LIKE '%foo%') であってもインデックスが利用できます。
=# CREATE TABLE test (id serial, t text); =# COPY test(t) FROM '...'; =# CREATE INDEX idx_like ON test USING senna (t like_ops); =# ANALYZE; =# EXPLAIN SELECT * FROM test WHERE t LIKE '%リレーショナルデータベース%'; QUERY PLAN ------------------------------------------------------------------------- Index Scan using idx_like on test (cost=0.00..55.01 rows=615 width=36) Index Cond: (t ~~ '%リレーショナルデータベース%'::text) (2 rows)
デフォルトの演算子クラス (senna_ops) では LIKE 検索はできませんが、英数字は単語単位でインデックス化するため、インデックスのサイズが比較的小さくなり、検索も効率的です。 一方、LIKE 検索用の演算子クラス (like_ops) は英数字も各文字ごとにインデックス化するため、LIKE 演算子と同じ検索結果になりますが、性能や効率は若干劣ります。 用途に応じて使い分けて下さい。
CREATE INDEX の際に WITH 句でインデックス・オプションを指定できます。
WARNING: senna: [CRITICAL] index full. set bigger value to initial_n_segments.ALTER INDEX を使って作成済みのインデックスに再設定しても反映されず、senna.reindex_index() 等でインデックスを再作成する必要があります。 文書サイズが大きい等、単語数が多くなることが予想される場合には、予め 2048 程度まで増やすことを検討してください。 詳細は Senna のドキュメントを参照してください。
Senna インデックスは標準 DROP INDEX や REINDEX REINDEX ではなく、専用の関数を使ってメンテナンスを行ってください。インデックスの削除が発生する可能性のあるSQLは以下になります。これらを実行すると、不要ファイルが残ってしまいます。
特に、データベース全体への VACUUM FULL, REINDEX, CLUSTER で不要ファイルを残してしまいがちなので注意してください。
DROP INDEX の代わりに senna.drop_index() 関数を使ってください。
=# SELECT senna.drop_index('index_name');
REINDEX の代わりに senna.reindex_index() 関数を使ってください。
=# SELECT senna.reindex_index('index_name');
textsearch_senna が利用する Senna ライブラリは、PostgreSQL 標準のインデックス・ファイルのほかに、以下のファイルを利用します。インデックス・ファイルが削除される際には、これらのファイルも削除する必要があります。不要ファイルは残っていてもデータアクセスには問題ありませんが、ディスクの容量が無駄になります。
残ってしまった不要ファイルを調査するために、以下のビューを用意しています。
CREATE VIEW senna.all_files; -- 全てのデータベース・ファイル CREATE VIEW senna.index_files; -- 全ての senna インデックス・ファイル CREATE VIEW senna.orphan_files; -- 不要な senna インデックス・ファイル
不要ファイルを削除するには、contrib/adminpack をインストールした上で、下記のSQLを発行します。
=# CHECKPOINT; -- PostgreSQL のファイルを削除するため。 =# SELECT pg_file_unlink(file) FROM senna.orphan_files;
senna インデックスは ANALYZE で収集される統計情報を利用しません。 そのため、統計情報ヒストグラムを作成しないように設定することで、ディスク容量やCPUコストを節約できます。 senna インデックスを張ったカラムのみ統計情報を収集しないように設定するには、ALTER TABLE SET STATISTICS を使います。
=# ALTER TABLE tbl ALTER COLUMN document SET STATISTICS 0;