View READMEcategory(Tag) treefile info
Ludia 0.9.0 ユーザガイドLudiaについて概要LudiaはPostgreSQLに高速な全文検索機能を提供します。 全文検索エンジンSennaを利用し、データベース内のテキスト情報を高速検索します。 Ludiaは以下のような特徴をもっています。
ライセンスLudiaはOSS(オープンソースソフトウェア)です。 あなたは、Free Software Foundationが公表した GNU Lesser General Public Licenseのバージョン2.1が定める条項に従って、 本プログラムを再頒布または変更することができます。 頒布にあたっては、 市場性及び特定目的適合性についての暗黙の保証を含めて、 いかなる保障も行いません。 詳細は GNU LESSER GENERAL PUBLIC LICENSE Version 2.1 をお読みください。 動作環境以下の環境で動作確認をしています。
制限事項
問い合わせ先バグ報告や技術的な質問については、 Ludia-usersメーリングリスト でお問い合わせください。 インストールインストール方法については、 このファイルと同じディレクトリにあるINSTALLを参照してください。 使い方インデックスアクセスメソッドの登録Ludiaを使用するデータベースに対してインデックスアクセスメソッドを登録します。 ソースアーカイブに含まれている pgsenna2.sql をpsqlから実行してください。 (pgsenna2.sqlはPostgreSQLのshareディレクトリにインストールされます。): $ psql -f /usr/local/pgsql/share/pgsenna2.sql testdb 設定ファイルの編集Ludiaを使用するデータベースクラスタのpostgresql.confファイルに、 以下の設定内容を追加してください。 設定を反映するためにはPostgreSQLを再起動する必要があります。 postgresql.confの設定が反映されていないと、 実行時にエラーになってしまうので注意してください。 設定内容についての詳細は、 実行時の設定 の節を参照してください。: custom_variable_classes = 'ludia' ludia.max_n_sort_result = 10000 ludia.enable_seqscan = on ludia.sen_index_flags = 31 もしすでにcustom_variable_classesが設定されている場合は、 そこにludiaというクラス名を追加してください。 インデックスの作成ここでは、例として以下のようなテーブルを利用します。:
CREATE TABLE table1 (col1 text, col2 varchar(128));
INSERT INTO table1 VALUES ('すもももももももものうち', 'あの壺はよいものだ');
INSERT INTO table1 VALUES ('ももから生まれた桃太郎', 'あの壷はよいものだ');
全文検索インデックスはCREATE INDEX 文を利用して作成します。: CREATE INDEX index1 ON table1 USING fulltext(col1); Ludiaがインデックス対象とできるのはtext型のみなので、 char型などの列に対してインデックスを作成したい場合はキャストしてください。: CREATE INDEX index2 ON table1 USING fulltextb((col2::text)); インデックスアクセスメソッド名には
の3種類があり、どれを指定するかによってSennaインデックスのフラグが変わります。 ユーザ定義(fulltextu)の詳細は Sennaインデックス作成時のオプション の節を参照してください。 検索の実行Ludiaのインデックスを用いた検索を行う場合には @@ 演算子を使用します。 @@ 演算子の右辺には Sennaの検索クエリ を指定してください。
SELECT * FROM table1 WHERE col1 @@ 'もも';
col1 | col2
--------------------------+--------------------
すもももももももものうち | あの壺はよいものだ
ももから生まれた桃太郎 | あの壷はよいものだ
(2 rows)
また、この検索における検索スコアを取得するためには、 pgs2getscore関数を利用します。 pgs2getscore関数は2つの引数をとります。 1番目の引数には検索対象となった行のTIDを、 2番目の引数にはインデックス名を指定してください。:
SELECT col1, pgs2getscore(table1.ctid, 'index1') FROM table1 WHERE col1 @@ 'もも';
col1 | pgs2getscore
--------------------------+--------------
すもももももももものうち | 10
ももから生まれた桃太郎 | 5
インデックスの削除PostgreSQLのインデックスリレーションファイルと、 Ludiaのインデックスファイルは以下の5つから構成されます。 (テーブル空間を使用している場合は、テーブル空間定義時に指定した場所に置かれます。)
1 はPostgreSQLのインデックスリレーションファイル、 2〜5はSennaのインデックスファイルです。 2〜5のファイルは手作業で削除する必要があります。 参考として、インデックスのファイルノード番号は以下のようなクエリで取得できます。: SELECT relfilenode FROM pg_class WHERE relname = 'index1'; また、データベースのOIDは以下のようなクエリで取得できます。: SELECT oid FROM pg_database WHERE datname = 'dbname'; 1のファイルについては、DROP INDEXを実行することで削除されます。: DROP INDEX index1; あるいは、pgs2destroy関数を利用すると、 データベース中の不要になったSennaインデックスファイルを一括して削除できます。 pgs2destroy関数は、2~5が存在するが1のファイルが存在しない、という場合に、 2~5のファイルを削除します。:
# DROP TABLE table1;
DROP TABLE
# SELECT pgs2destroy();
pgs2destroy
-------------
1
(1 row)
関数の返り値は、削除したインデックス数です。 (上記の2~5のファイルで1セットです。) 実行時の設定シーケンシャルスキャンの抑制@@演算子を用いた全文検索条件を指定しても、シーケンシャルスキャンが実行された場合には、 インデックススキャンの場合と同様の検索を行うことができません。 そのためLudiaでは、 デフォルトではシーケンシャルスキャンが実行された場合にエラーにする設定になっています。 (この例ではenable_indexscanをoffにして、シーケンシャルスキャンを実行しています。):
# SET enable_indexscan TO off;
SET
# EXPLAIN SELECT col1 FROM table1 WHERE col1 @@ 'もも';
QUERY PLAN
-------------------------------------------------------
Seq Scan on table1 (cost=0.00..1.02 rows=1 width=32)
Filter: (col1 @@ 'もも'::text)
(2 rows)
# SELECT col1 FROM table1 WHERE col1 @@ 'もも';
ERROR: pgsenna2: sequencial scan disabled.
ERROR: pgsenna2: sequencial scan disabled.
この設定はpostgresql.confのludia.enable_seqscan変数で指定されますが、 SETコマンドでも変更することができます。 (SETコマンドによる変更はそのセッション内でのみ有効です。):
# SET ludia.enable_seqscan TO on;
SET
# SELECT col1 FROM table1 WHERE col1 @@ 'もも';
col1
--------------------------
すもももももももものうち
ももから生まれた桃太郎
(2 rows)
しかし、この場合@@演算子は、単純な文字列の比較をしているだけで、 全文検索を行っている場合と同じ結果が得られるわけではないので注意してください。 たとえば、Sennaの+演算子を付けて検索しようとした場合、 単純に'+もも'という文字列が含まれるかが検査されるため、文字列は一致しなくなります。: # SELECT col1 FROM table1 WHERE col1 @@ '+もも'; col1 ------ (0 rows) 検索ヒット数の上限の設定Ludiaのデフォルトの設定では、 検索でヒットした行をスコアが高い順に postgresql.confのludia.max_n_sort_resultで設定された行数まで返却します。:
# SHOW ludia.max_n_sort_result;
ludia.max_n_sort_result
-------------------------
10000
(1 row)
# SELECT col1, pgs2getscore(ctid, 'index1') FROM table1 WHERE col1 @@ 'もも';
col1 | pgs2getscore
--------------------------+--------------
すもももももももものうち | 10
ももから生まれた桃太郎 | 5
(2 rows)
この上限はSETコマンドでも変更することができます。 (SETコマンドによる変更はそのセッション内でのみ有効です。):
# SET ludia.max_n_sort_result TO 1;
SET
# SELECT col1, pgs2getscore(ctid, 'index1') FROM table1 WHERE col1 @@ 'もも';
col1 | pgs2getscore
--------------------------+--------------
すもももももももものうち | 10
(1 row)
Sennaインデックス作成時のオプションアクセスメソッドとしてfulltextuを選択すると、 インデックス作成時にSennaインデックスのフラグを指定することができます。 利用できるフラグは(Senna 0.8.2では)以下のような定義と意味をもっています。 (詳しくは SennaのAPIドキュメント を参照してください。) #define SEN_INDEX_NORMALIZE 0x0001 #define SEN_INDEX_SPLIT_ALPHA 0x0002 #define SEN_INDEX_SPLIT_DIGIT 0x0004 #define SEN_INDEX_SPLIT_SYMBOL 0x0008 #define SEN_INDEX_NGRAM 0x0010 #define SEN_INDEX_DELIMITED 0x0020
postgresql.confの設定には、10進数の値を指定してください。 例えば、 SEN_INDEX_NGRAM|SEN_INDEX_NORMALIZE|SEN_INDEX_SPLIT_ALPHA というフラグを指定する場合には、: ludia.sen_index_flags = 19 となります。 使い方(応用編)ヒット件数を高速に取得するpgs2getnhits() 関数を用いると、 セッション内で最後に行われたSennaの検索ヒット件数を取得することができます。:
# SELECT * FROM table1 WHERE col1 @@ 'もも';
col1 | col2
--------------------------+--------------------
すもももももももものうち | あの壺はよいものだ
ももから生まれた桃太郎 | あの壷はよいものだ
(2 rows)
# SELECT pgs2getnhits();
pgs2getnhits
--------------
2
(1 row)
これを利用すると、ヒット件数が非常に多い場合でも、 LIMIT句と組み合わせて利用することで、高速にヒット件数を取得することができます。:
# SELECT * FROM table1 WHERE col1 @@ 'もも' LIMIT 0;
col1 | col2
------+------
(0 rows)
# SELECT pgs2getnhits();
pgs2getnhits
--------------
2
(1 row)
ただし、ここで得られるヒット件数はSennaの検索結果についての値であるため、 以下に挙げるような制限があります。
テキストフィルタを利用するLudiaのユーティリティ関数を利用することで、PDFファイルに対してインデックスを作成することができます。 ここでは Xpdf というツールに含まれている、pdftotextというコマンドを利用します。 まずはXpdfと日本語サポートパッケージをインストールしてください。 Ludiaではpdftotextを利用するための関数が2種類用意されています、 pgs2pdftotext1という関数は、PDFファイルのpathを引数としてとり、 pdftotextを呼び出してPDFファイルからテキストを取り出します。:
# select pgs2pdftotext1('/tmp/PostgresForest.pdf');
pgs2pdftotext1
-----------------
高性能・高信頼の並列分散データベース環境を低コストで実現 複数ノード上
でそれぞれ稼動している PostgreSQL をシングルシステムイメー ジとしてユー
ザに提供 PostgreSQL と互換性があるため、アプリケーション開発時に新たな
トレーニン グが不要 オープンソースでのシステム構築可能性を向上
...(省略)
また、pdf2pdftotext2という関数はPDFファイルそのものをbytea型のデータとして受け取り、 (それをtmpディレクトリに一時ファイルとして書き出して) pdftotextを呼び出し、PDFファイルからテキストを書き出します。:
# select pgs2pdftotext1('\\120\\104\\106\\055\\061\\056\\064\\012...(省略)');
ここでは例として、以下のようなテーブルを使用します。:
# CREATE TABLE pdffiles (id SERIAL PRIMARY KEY, filepath text, filedata bytea);
# \d pdffiles
Table "public.pdffiles"
Column | Type | Modifiers
----------+---------+-------------------------------------------------------
id | integer | not null default nextval('pdffiles_id_seq'::regclass)
filepath | text |
filedata | bytea |
Indexes:
"pdffiles_pkey" PRIMARY KEY, btree (id)
PDFファイルは、
のいずれかの方法で格納されているとします。: # SELECT id, filepath, substring(encode(filedata, 'hex') from 1 for 30) FROM pdffiles; id | filepath | substring ----+-------------------------+-------------------------------- 1 | /tmp/PostgresForest.pdf | 255044462d312e340a25c7ec8fa20a (1 row) 1の場合にはpgs2pdftotext1を、2の場合にはpgs2pdftotext2を利用して 関数インデックスを作成することができます。:
# CREATE INDEX pidx1 on pdffiles USING fulltextb(pgs2pdftotext1(filepath));
CREATE INDEX
# CREATE INDEX pidx2 on pdffiles USING fulltextb(pgs2pdftotext2(filedata));
CREATE INDEX
# \d pdffiles
Table "public.pdffiles"
Column | Type | Modifiers
----------+---------+-------------------------------------------------------
id | integer | not null default nextval('pdffiles_id_seq'::regclass)
filepath | text |
filedata | bytea |
Indexes:
"pdffiles_pkey" PRIMARY KEY, btree (id)
"pidx1" fulltextb (pgs2pdftotext1(filepath))
"pidx2" fulltextb (pgs2pdftotext2(filedata))
このインデックスを利用することで、PDFファイル中のテキストに対する検索を行うことができます。 検索を実行する際にも列名に対して関数を適用してください。 (検索の際には関数は実行されません。): # SELECT id FROM pdffiles WHERE pgs2pdftotext1(filepath) @@ '高性能'; id ---- 1 (1 row) # SELECT id FROM pdffiles WHERE pgs2pdftotext2(filedata) @@ '高信頼'; id ---- 1 (1 row) ここで、PDFファイルに複製不可やパスワードの設定が行われていると、 この関数はエラーを返すことに注意してください。 |