PostgreSQL 9.3.2文書 | ||||
---|---|---|---|---|
前のページ | 上に戻る | 第 12章全文検索 | 次のページ |
この節では、全文検索に関連する便利な追加の関数と演算子を説明します。
項12.3.1に、もとのテキスト形式の文書がどのようにしてtsvectorに変換されるのか書いてあります。また、PostgreSQLではtsvector形式に変換済の文書を操作する関数と演算子が提供されています。
tsvectorの結合演算子で、2つのベクターの語彙素と位置情報を合成し、tsvectorを返します。右辺のベクターの位置は左辺のベクターの一番大きな位置情報のオフセットになります。その結果、この関数の結果は、元の文書を結合したものにto_tsvector
を適用したものとほぼ同じになります(まったく同じと言うわけではありません。左辺の引数の最後の位置にあるストップワードは取り除かれるのに対し、テキストの結合が行われた場合は、その影響が右辺の引数にある語彙素位置に影響を与えるからです)。
to_tsvector
を適用する前のテキストを結合するよりも、ベクターを結合することの利点の一つは、文書の異なる部分をパースするために、異なる設定を使うことができることです。なお、setweight
関数は与えられたベクターのすべての語彙素を同じ方法でマーク付けするため、もしも文書に異なる部分に別の重み付けを行いたいなら、結合する前に文書をパースしてsetweight
を適用することが必要です。
setweight
は、A, B, C, Dのいずれかの与えられたweightを入力のベクター中の位置にラベル付けし、そのコピーを返します(Dは新しいベクターのデフォルトで、出力する際には表示されません)。これらのラベルはベクターが結合される際にに保存されるので、ランキング関数によって文書中の異なる部分の語が別々に重み付けすることができます。
なお、重み付けラベルは語彙素ではなく位置に与えられることに注意してください。入力のベクターから位置が削除されていると、setweight
は何もしません。
ベクター中に格納されている語彙素の数を返します。
位置、重みの情報がないことを除けば入力のベクターと同じ語彙素のリストを持つベクターを返します。返却されたベクターは、情報を削除されていないベクターに比べてランキングに関しては、ずっと有用性が低くなりますが、通常非常に小さくなります。
項12.3.2は、元のテキストがいかにしてtsquery値に変換されるかを解説しています。またPostgreSQLは、tsquery形式に変換済の問合わせを操作するために使用できる関数と演算子を提供しています。
2つの問合わせをANDで結合したものを返します。
2つの問合わせをORで結合したものを返します。
与えられた問合わせの否定を返します。
tsquery中のノード(語彙素と演算子)の数を返します。この関数は、問合わせが意味のあるものか(返却値 > 0)、ストップワードだけを含んでいるか(返却値 0)を判断するのに役に立ちます。例を示します。
SELECT numnode(plainto_tsquery('the any')); NOTICE: query contains only stopword(s) or doesn't contain lexeme(s), ignored numnode --------- 0 SELECT numnode('foo & bar'::tsquery); numnode --------- 3
インデックス検索の際に使用できるtsqueryの部分を返します。この関数は、たとえばストップワードのみ、あるいは否定語だけのように、インデックス検索できない問合わせを検出するのに役立ちます。例を示します。
SELECT querytree(to_tsquery('!defined')); querytree -----------
ts_rewrite
ファミリー関数は、与えられたtsqueryから目的の副問合わせ部分を探し、それを代わりの副問い合わせに置き換えます。本質的には、この操作は、部分文字列置き換えのtsquery版です。置き換え候補と置き換え内容の組は、問合わせ書き換えルールであると考えることができます。そのような書き換えルールの集合は、強力な検索ツールとなり得ます。たとえば、同義語(たとえばnew york, big apple, nyc, gotham)を使って問合わせをより広範囲にしたり、逆によりホットな話題にユーザを導くために問合わせを狭い範囲に絞ったりすることができます。この機能と、同義語辞書(項12.6.4)の間には、機能的な重複があります。しかし、再インデックス付けすることなしに、その場で書き換えルールを変更できるのに対し、同義語辞書の更新が有効になるためには、再インデックス付けを行わなければなりません。
この形式の ts_rewrite
は、単純に単一の書き換えルールを適用します。query中に表れるtargetは、substituteですべて置き換えられます。例を示します。
SELECT ts_rewrite('a & b'::tsquery, 'a'::tsquery, 'c'::tsquery); ts_rewrite ------------ 'b' & 'c'
この形式のts_rewrite
は、開始問合わせと、テキスト文字列で与えられるSQLのSELECTコマンドを受け取ります。SELECTは、tsquery型の2つの列を出力しなければなりません。現在の問合わせは、SELECTのそれぞれの結果行中の最初の列の結果(ターゲット)が、2番目の列の結果(置き換え値)に、置き換えられます。例を示します。
CREATE TABLE aliases (t tsquery PRIMARY KEY, s tsquery); INSERT INTO aliases VALUES('a', 'c'); SELECT ts_rewrite('a & b'::tsquery, 'SELECT t,s FROM aliases'); ts_rewrite ------------ 'b' & 'c'
なお、複数の書き換えルールを適用する際は、適用する順番が重要です。ですから、実際には並び替えのキーを適用するORDER BYを問合わせに入れておくのがよいでしょう。
天文学上の実際的な例を考えてみます。テーブル駆動の書き換えルールを使って、supernovaeを展開します。
CREATE TABLE aliases (t tsquery primary key, s tsquery); INSERT INTO aliases VALUES(to_tsquery('supernovae'), to_tsquery('supernovae|sn')); SELECT ts_rewrite(to_tsquery('supernovae & crab'), 'SELECT * FROM aliases'); ts_rewrite --------------------------------- 'crab' & ( 'supernova' | 'sn' )
テーブルを更新するだけで、書き換えルールを変更することができます。
UPDATE aliases SET s = to_tsquery('supernovae|sn & !nebulae') WHERE t = to_tsquery('supernovae'); SELECT ts_rewrite(to_tsquery('supernovae & crab'), 'SELECT * FROM aliases'); ts_rewrite --------------------------------------------- 'crab' & ( 'supernova' | 'sn' & !'nebula' )
書き換えルールが多くなると、書き換えが遅くなる可能性があります。なぜなら、書き換えの対象になるものを求めて、すべてのルールをチェックするからです。明らかに使われないルールを取り除くために、tsqueryの含有演算子を使うことができます。以下の例では、元の問合わせにマッチするルールだけを選ぶことができます。
SELECT ts_rewrite('a & b'::tsquery, 'SELECT t,s FROM aliases WHERE ''a & b''::tsquery @> t'); ts_rewrite ------------ 'b' & 'c'
tsvector形式の文書を格納するために別の列を使う場合、文書の内容を格納した列が変更されたときにtsvectorを格納した列を更新するトリガを作っておく必要があります。この目的のために、2つの組み込み関数を利用できます。自分で関数を書くこともできます。
tsvector_update_trigger(tsvector_column_name, config_name, text_column_name [, ... ]) tsvector_update_trigger_column(tsvector_column_name, config_column_name, text_column_name [, ... ])
これらのトリガ関数は、1つ以上のテキスト列から、CREATE TRIGGERコマンドで指定されたパラメータの制御により、tsvector列を自動的に計算します。使い方の例を示します。
CREATE TABLE messages ( title text, body text, tsv tsvector ); CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE ON messages FOR EACH ROW EXECUTE PROCEDURE tsvector_update_trigger(tsv, 'pg_catalog.english', title, body); INSERT INTO messages VALUES('title here', 'the body text is here'); SELECT * FROM messages; title | body | tsv ------------+-----------------------+---------------------------- title here | the body text is here | 'bodi':4 'text':5 'titl':1 SELECT title, body FROM messages WHERE tsv @@ to_tsquery('title & body'); title | body ------------+----------------------- title here | the body text is here
このトリガを作っておくことにより、 title またはbodyへの変更は、アプリケーションで考慮しなくても自動的にtsvに反映されます。
トリガの最初の引数は更新対象のtsvectorの列名でなければなりません。2番目の引数は、変換を実行する際に使用されるテキスト検索の設定です。tsvector_update_trigger
では、設定の名前は単に2番目のトリガ引数で与えられます。上で示すように、スキーマ修飾されていなければなりません。search_pathの変更がトリガの振る舞いに影響を与えないためです。tsvector_update_trigger_column
では、2番目のトリガ引数は別のテーブル列の列名です。この列の型はregconfigでなければなりません。この方法により、設定を行単位で変えることができます。残りの引数はテキスト型(text, varchar, charのいずれか)の列の名前です。与えられた順に、文書中に取り込まれます。NULL値はスキップされます(ただし、それ以外の列はインデックス付けされます)。
これらの組み込みトリガの制限事項として、すべての列を同じようにしか扱えないというものがあります。それぞれの列を違うように扱うには — たとえば本文とタイトルの重みを変えるとか —、カスタムトリガを書く必要があります。トリガ言語としてPL/pgSQLを使った例を示します。
CREATE FUNCTION messages_trigger() RETURNS trigger AS $$ begin new.tsv := setweight(to_tsvector('pg_catalog.english', coalesce(new.title,'')), 'A') || setweight(to_tsvector('pg_catalog.english', coalesce(new.body,'')), 'D'); return new; end $$ LANGUAGE plpgsql; CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE ON messages FOR EACH ROW EXECUTE PROCEDURE messages_trigger();
tsvector値をトリガ内で作るときには、設定名を明示的に与えることが重要であることを銘記しておいてください。そうすれば、default_text_search_configが変更されても列の内容は影響を受けません。これを怠ると、ダンプしてリロードすると検索結果が変わってしまうような問題が起きる可能性があります。
ts_stat
関数は、設定をチェックしたり、ストップワードの候補を探すのに役立ちます。
ts_stat(sqlquery text, [ weights text, ]
OUT word text, OUT ndoc integer,
OUT nentry integer) returns setof record
sqlqueryは単一のtsvector列を返すSQL問合わせのテキスト値です。ts_stat
は問合わせを実行し、tsvectorデータに含まれる語彙素(単語)各々の統計情報を返します。返却される列は以下のものです。
word text — 語彙素の値
ndoc integer — 単語が含まれる文書(tsvector)の数
nentry integer — 含まれる単語の数
weightsが与えられていたら、その重みを持つものだけがカウントされます。
たとえば、文書中もっとも頻繁に現れる単語の上位10位を探すには以下のようにします。
SELECT * FROM ts_stat('SELECT vector FROM apod') ORDER BY nentry DESC, ndoc DESC, word LIMIT 10;
同じ例で、重みがAかBの単語だけをカウントするには、以下のようにします。
SELECT * FROM ts_stat('SELECT vector FROM apod', 'ab') ORDER BY nentry DESC, ndoc DESC, word LIMIT 10;