他のバージョンの文書 15 | 14 | 13 | 12 | 11 | 10 | 9.6 | 9.5 | 9.4 | 9.3 | 9.2 | 9.1 | 9.0 | 8.4 | 8.3 | 8.2 | 8.1 | 8.0 | 7.4 | 7.3 | 7.2

12.4. 追加機能

この節では、全文検索に関連する便利な追加の関数と演算子を説明します。

12.4.1. 文書の操作

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

位置、重みの情報がないことを除けば入力のベクトルと同じ語彙素のリストを持つベクトルを返します。返却されたベクトルは、情報を削除されていないベクトルに比べてランキングに関しては、ずっと有用性が低くなりますが、通常非常に小さくなります。

12.4.2. 問合わせを操作する

12.3.2. 問合わせのパースは、元のテキストがいかにしてtsquery値に変換されるかを解説しています。またPostgreSQLは、tsquery形式に変換済の問合わせを操作するために使用できる関数と演算子を提供しています。

tsquery && tsquery

2つの問合わせをANDで結合したものを返します。

tsquery || tsquery

2つの問合わせをORで結合したものを返します。

!! tsquery

与えられた問合わせの否定を返します。

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
-----------

12.4.2.1. 問合わせの書き換え

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'

12.4.3. 自動更新のためのトリガ

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が変更されても列の内容は影響を受けません。これを怠ると、ダンプしてリロードすると検索結果が変わってしまうような問題が起きる可能性があります。

12.4.4. 文書の統計情報の収集

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;

同じ例で、重みがABの単語だけをカウントするには、以下のようにします。

SELECT * FROM ts_stat('SELECT vector FROM apod', 'ab')
ORDER BY nentry DESC, ndoc DESC, word
LIMIT 10;