他のバージョンの文書 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.1. 導入

全文検索(または単にテキスト検索)は、問い合わせを満たす自然言語の文書を識別し、更には問い合わせとの関連性の順に並べ替えることができます。 もっとも一般的な検索は、与えられた検索語を含む文書を探し、問い合わせとの類似性の順に返す、というものです。 問い合わせ類似性の記法は非常に柔軟で、特定の用途に適合できます。 もっとも単純な検索では、問い合わせは単語の集合として、類似性は文書中の問い合わせ対象の単語の頻度として扱います。

テキスト検索演算子は、データベースシステムに長年存在していました。PostgreSQLは、テキストデータ型用に、~,~*, LIKE,ILIKEの各演算子を持っています。しかし、近代的な情報システムに必要な以下の本質的な特徴を欠いています。

全文検索のインデックス付けでは、文書を前もって処理しておき、後で素早く検索するために、インデックスを保存しておくことができます。前処理には以下があります。

辞書を使ってトークンの正規化を細かく制御できます。 適当な辞書を用意すれば次のようなことができます。

前処理した文書を格納するために、データ型tsvectorが提供されています。また、処理済問い合わせを表現するためにtsquery型も提供されています(8.11)。 これらのデータ型のために、多数の関数と演算子が利用できますが(9.13)、もっとも重要なのは、12.1.2で紹介している@@演算子です。 全文検索はインデックス(12.9)を使って高速化できます。

12.1.1. 文書とは何か?

文書は全文検索システムにおける検索の単位です。 たとえば、雑誌記事やメールのメッセージです。 テキスト検索エンジンは、文書をパースし、語彙素(キーワード)とそれが含まれる親文書の関連を格納できなければなりません。 後で、この関連を使って問い合わせ語を含む文書を検索するのに使います。

PostgreSQLでの検索においては、ドキュメントはデータベースのテーブルの行内のテキストフィールドか、あるいはそのようなフィールドの組み合わせ(結合)でもよいです。そうしたフィールドはおそらく複数のテーブルに格納されていたり、動的に獲得されるものであったりします。 言い換えると、文書はインデックス付けのために複数の異なる部分から構成されても良く、それらが全体としてはひとまとまりに格納されていなくても良いのです。例を示します。

SELECT title || ' ' ||  author || ' ' ||  abstract || ' ' || body AS document
FROM messages
WHERE mid = 12;

SELECT m.title || ' ' || m.author || ' ' || m.abstract || ' ' || d.body AS document
FROM messages m, docs d
WHERE m.mid = d.did AND m.mid = 12;

注記

実際には、これらの例の問い合わせでは、coalesceを使って、一部NULLが含まれているためにドキュメント全体がNULLになってしまうのを防ぐべきです。

別な方法としては、ファイルシステム上に文書を単純なテキストファイルとして格納することです。 この場合、データベースは、フルテキストインデックスを格納し、検索を実行するために使うことができます。ファイルシステムから文書を取り出すためには、何かのユニークな識別子を使います。 しかし、データベースの外にあるファイルを取り出すには、スーパーユーザの許可か、特殊な関数のサポートが必要です。そういうわけでたいていの場合はPostgreSQLの中にすべてのデータを保持するのよりも不便です。 また、すべてのデータをデータベースに保持することにより、文書のインデックス付けと表示の際に文書のメタデータにアクセスすることが容易になります。

テキスト検索という目的のため、各々の文書は前処理されてtsvector形式に変換しておかなければなりません。 検索とランキングはすべて文書のtsvector表現上で実行されます — オリジナル文書は、ユーザに表示のため選択された場合にのみ取り出される必要があります。 というわけで、ここではtsvectorを文書と見なすことがよくあります。といっても、tsvectorは完全な文書の縮小表現でしかありません。

12.1.2. 基本的なテキスト照合

PostgreSQLにおける全文検索は、tsvector(文書)が、tsquery(問い合わせ)に一致したらtrueを返す照合演算子@@に基づいています。どちらのデータ型を先に書いても構いません。

SELECT 'a fat cat sat on a mat and ate a fat rat'::tsvector @@ 'cat & rat'::tsquery;
 ?column?
----------
 t

SELECT 'fat & cow'::tsquery @@ 'a fat cat sat on a mat and ate a fat rat'::tsvector;
 ?column?
----------
 f

上記の例でわかるように、tsqueryは、tsvectorと違って、単なるテキストではありません。 tsqueryは正規化済の語彙素である検索表現を含み、AND, OR, NOT, FOLLOWED BY演算子を使って複数の表現を組み合わせても構いません。 (詳細は8.11.2を見てください。) 主にテキスト中の単語を正規化することにより、ユーザが入力したテキストを適切なtsqueryに変換するのに便利な関数to_tsqueryplainto_tsqueryphraseto_tsqueryがあります。 同様に、文書文字列をパースして正規化するためにto_tsvectorが利用できます。 というわけで、実際にはテキスト検索照合はこんな感じになります。

SELECT to_tsvector('fat cats ate fat rats') @@ to_tsquery('fat & rat');
 ?column?
----------
 t

この照合は、もしつぎのように書くとうまくいかないことに注意してください。

SELECT 'fat cats ate fat rats'::tsvector @@ to_tsquery('fat & rat');
 ?column?
----------
 f

というのも、単語ratsに対して正規化が行われないからです。 tsvectorの要素は、すでに正規化されている語彙素であることになっているので、ratsratに一致しません。

また、@@演算子は、textを入力として受付けるので、簡単に使うときには、明示的にテキスト文字列をtsvectorまたはtsqueryに変換することを省略できます。応用として以下のものがあります。

tsvector @@ tsquery
tsquery  @@ tsvector
text @@ tsquery
text @@ text

最初の2つについてはすでに説明しました。 text @@ tsqueryという形式は、to_tsvector(x) @@ yと同じです。 text @@ textという形式は、to_tsvector(x) @@plainto_tsquery(y)と同じです。

tsquery内において、演算子 & (AND) は、マッチと見なされるには引数の両方がドキュメント内に現れる必要があるということを指定します。 同様に、演算子 | (OR) では、引数の少なくとも一方が現れる必要があり、また演算子 ! (NOT) は、マッチと見なされるには引数が現れてはならないことを指定します。 例えば、fat & ! ratという問い合わせは、fatは含むがratは含まないドキュメントとマッチします。

句の検索は、tsquery演算子 <-> (FOLLOWED BY)を使うことで可能です。 この演算子は、その引数にマッチする語が隣接していて、かつ指定と同じ順序である場合にのみマッチします。 例を示します。

SELECT to_tsvector('fatal error') @@ to_tsquery('fatal <-> error');
 ?column?
----------
 t

SELECT to_tsvector('error is not fatal') @@ to_tsquery('fatal <-> error');
 ?column?
----------
 f

FOLLOWED BY演算子にはもっと汎用的なバージョンがあり、それは<N>という構文で使います。 ここでNは整数で、マッチする語彙素の位置の差を表します。 <1><->と同じですが、<2>ではマッチする語の間にちょうど1つ、他の語彙素が現れることを許容する、という具合です。 phraseto_tsquery関数は、この演算子を利用して、ストップワードを含む複数語の句にマッチ可能なtsqueryを構築するものです。 例を示します。

SELECT phraseto_tsquery('cats ate rats');
       phraseto_tsquery
-------------------------------
 'cat' <-> 'ate' <-> 'rat'

SELECT phraseto_tsquery('the cats ate the rats');
       phraseto_tsquery
-------------------------------
 'cat' <-> 'ate' <2> 'rat'

ときに役立つことがある特別な場合として、<0>を2つのパターンが同じ語にマッチすることを要求するために使うことができます。

tsquery演算子を入れ子にして管理するために括弧を使うことができます。 括弧がない場合、|の結合が最も弱く、次が&、その次が<->で、!が最も強く結合します。

FOLLOWED BYの中ではマッチの正確な位置が重要ですので、AND/OR/NOT演算子は、FOLLOWED BY演算子の引数の中で使われる場合にはそうでない場合と微妙に異なる意味になることに言及しておく価値はあります。 例えば、通常!xxをどこにも含まない文書とのみマッチします。 しかし、!x <-> yは、xの直後にあるのでなければyとマッチします。文書の他のところでのxの出現は、マッチを邪魔しません。 もう一つの例は、x & yは通常xyの両方が文書のどこかに現れることだけを要求しますが、(x & y) <-> zは、xyが同じ場所、zの直前でマッチすることを要求します。 そのため、この問い合わせはx <-> z & y <-> zとは異なった振る舞いをします。後者は2つの別の文字列、x zy zを含む文書にマッチします。 (xyが同じ場所でマッチすることはあり得ませんので、上に書いたこの特別な問い合わせは、役に立ちません。しかし、接頭辞マッチパターンのようにより複雑な状況では、この形の問い合わせは役に立つかもしれません。)

12.1.3. 設定

今までのはすべて単純なテキスト検索の例でした。 すでに述べたように、全文検索機能を使えば、もっと色々なことができます。 インデックス付けの際に特定の単語をスキップ(ストップワード)、同義語(synonym)処理、賢いパース処理、すなわち、単に空白区切りに基づくパース処理以上のものです。 この機能はテキスト検索設定で制御します。 PostgreSQLには、多くの言語用の設定があらかじめ組み込まれていますが、ユーザ設定を容易に作ることもできます。 (psql\dFコマンドで、利用できる設定を表示できます。)

インストールの際には、適当な設定が選ばれ、default_text_search_configpostgresql.conf中にセットされます。 クラスタ全体で同じ設定を使用する場合はpostgresql.confの設定値を利用できます。 クラスタの設定とは異なるが、あるデータベースの中で同じ設定を使う場合には、ALTER DATABASE ... SETを利用します。 さもなければ、セッション単位でdefault_text_search_configを設定できます。

設定に依存するテキスト検索関数は、オプションでregconfig引数を持っており、使用する設定を明示的に指定できます。default_text_search_configは、この引数が省略されたときだけ使用されます。

カスタムテキスト検索設定を作り易くするため、設定はより単純なデータベースオブジェクトから作られます。PostgreSQLのテキスト検索機能は、4つの設定関連のデータベースオブジェクトを提供しています。

  • テキスト検索パーサは、文書をトークンに分解し、トークンを分類します(たとえば、単語とか数のように)。

  • テキスト検索辞書はトークンを正規化された形式に変換し、ストップワードを排除します。

  • テキスト検索テンプレートは、現在の辞書が利用する関数を提供します(辞書は、単にテンプレートと、その引数の集合を指定するだけです)。

  • テキスト検索設定は、パーサと使用する辞書の集合を選択し、パーサが生成したトークンを正規化します。

テキスト検索パーサとテンプレートは、低レベルのC関数で作ります。したがって、新しく開発するためにはCのプログラミング能力と、データベースにインストールするためのスーパーユーザ権限が必要になります。 (PostgreSQLの配布物のcontrib/には、追加パーサとテンプレートの例があります。) 辞書と設定は、単に配下のパーサとテンプレートのパラメータを設定し、両者を結び付けるだけなので、新しい辞書と設定を作るために特別な権限は必要ありません。 この章の後でカスタム辞書と設定を作る例が登場します。