この節では、全文検索に関連する便利な追加の関数と演算子を説明します。
12.3.1に、もとのテキスト形式の文書がどのようにしてtsvector
に変換されるのか書いてあります。また、PostgreSQLではtsvector
形式に変換済の文書を操作する関数と演算子が提供されています。
tsvector
|| tsvector
tsvector
の結合演算子で、2つのベクトルの語彙素と位置情報を合成し、tsvector
を返します。
位置と重み付けラベルは、結合では維持されます。
右辺のベクトルの位置は左辺のベクトルの一番大きな位置情報のオフセットになります。その結果、この関数の結果は、元の2つの文書文字列を結合したものにto_tsvector
を適用したものとほぼ同じになります。
(まったく同じと言うわけではありません。左辺の引数の最後から取り除かれたストップワードは結果に影響を与えないのに対し、テキストの結合が行われた場合は、右辺の引数にある語彙素位置に影響を与えるからです。)
to_tsvector
を適用する前のテキストを結合するよりも、ベクトルを結合することの利点の一つは、文書の異なる部分をパースするために、異なる設定を使うことができることです。なお、setweight
関数は与えられたベクトルのすべての語彙素を同じ方法でマーク付けするため、もしも文書に異なる部分に別の重み付けを行いたいなら、結合する前に文書をパースしてsetweight
を適用することが必要です。
setweight(vector
tsvector
, weight
"char"
) returns tsvector
setweight
は、A
, B
, C
, D
のいずれかの与えられたweight
を入力のベクトル中の位置にラベル付けし、そのコピーを返します。
(D
は新しいベクトルのデフォルトで、出力する際には表示されません。)
これらのラベルはベクトルが結合される際に保存されるので、ランキング関数によって文書中の異なる部分の語を別々に重み付けできます。
なお、重み付けラベルは語彙素ではなく位置に与えられることに注意してください。
入力のベクトルから位置が削除されていると、setweight
は何もしません。
length(vector
tsvector
) returns integer
ベクトル中に格納されている語彙素の数を返します。
strip(vector
tsvector
) returns tsvector
入力のベクトルと同じ語彙素のリストを持つが、位置と重みの情報が全くないベクトルを返します。
その結果は、通常は情報を削除されていないベクトルよりもずっと小さくなりますが、有用性も低くなります。
また、tsquery
演算子<->
(FOLLOWED BY)は情報を削除した入力とマッチすることはありません。
なぜなら語彙素が発生する間の距離を決定できないからです。
tsvector
に関連した関数の完全なリストが表 9.41にあります。
12.3.2は、元のテキストがいかにしてtsquery
値に変換されるかを解説しています。またPostgreSQLは、tsquery
形式に変換済の問い合わせを操作するために使用できる関数と演算子を提供しています。
tsquery
&& tsquery
2つの問い合わせをANDで結合したものを返します。
tsquery
|| tsquery
2つの問い合わせをORで結合したものを返します。
!! tsquery
与えられた問い合わせの否定を返します。
tsquery
<-> tsquery
1番目の問い合わせにマッチし、その直後に2番目の問い合わせにマッチするものを検索する問い合わせを、tsquery
演算子<->
(FOLLOWED BY) を使って返します。
例を示します。
SELECT to_tsquery('fat') <-> to_tsquery('cat | rat'); ?column? ----------------------------------- 'fat' <-> 'cat' | 'fat' <-> 'rat'
tsquery_phrase(query1
tsquery
, query2
tsquery
[, distance
integer
]) returns tsquery
1番目の問い合わせにマッチし、その後にdistance
個の語彙素の距離で2番目の問い合わせにマッチするものを検索する問い合わせを、tsquery
演算子<
を使って返します。
例を示します。
N
>
SELECT tsquery_phrase(to_tsquery('fat'), to_tsquery('cat'), 10); tsquery_phrase ------------------ 'fat' <10> 'cat'
numnode(query
tsquery
) returns integer
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
querytree(query
tsquery
) returns text
インデックス検索の際に使用できるtsquery
の部分を返します。この関数は、たとえばストップワードのみ、あるいは否定語だけのように、インデックス検索できない問い合わせを検出するのに役立ちます。例を示します。
SELECT querytree(to_tsquery('!defined')); querytree -----------
ts_rewrite
ファミリー関数は、与えられたtsquery
から目的の副問い合わせ部分を探し、それを代わりの副問い合わせに置き換えます。本質的には、この操作は、部分文字列置き換えのtsquery
版です。置き換え候補と置き換え内容の組は、問い合わせ書き換えルールであると考えることができます。そのような書き換えルールの集合は、強力な検索ツールとなり得ます。たとえば、同義語(たとえばnew york
, big apple
, nyc
, gotham
)を使って問い合わせをより広範囲にしたり、逆によりホットな話題にユーザを導くために問い合わせを狭い範囲に絞ったりすることができます。この機能と、同義語辞書(12.6.4)の間には、機能的な重複があります。しかし、再インデックス付けすることなしに、その場で書き換えルールを変更できるのに対し、同義語辞書の更新が有効になるためには、再インデックス付けを行わなければなりません。
ts_rewrite (query
tsquery
, target
tsquery
, substitute
tsquery
) returns tsquery
この形式の ts_rewrite
は、単純に単一の書き換えルールを適用します。query
中に表れるtarget
は、substitute
ですべて置き換えられます。例を示します。
SELECT ts_rewrite('a & b'::tsquery, 'a'::tsquery, 'c'::tsquery); ts_rewrite ------------ 'b' & 'c'
ts_rewrite (query
tsquery
, select
text
) returns tsquery
この形式の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 FUNCTION 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 FUNCTION messages_trigger();
tsvector
値をトリガ内で作るときには、設定名を明示的に与えることが重要であることを銘記しておいてください。そうすれば、default_text_search_config
が変更されても列の内容は影響を受けません。これを怠ると、ダンプしてリロードすると検索結果が変わってしまうような問題が起きる可能性があります。
ts_stat
関数は、設定をチェックしたり、ストップワードの候補を探すのに役立ちます。
ts_stat(sqlquery
text
, [weights
text
, ] OUTword
text
, OUTndoc
integer
, OUTnentry
integer
) returnssetof 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;