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 mid = did AND mid = 12;

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

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

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

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

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

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演算子を使って複数の表現を組み合わせても構いません。(詳細はこれを見てください 項8.11。) たとえば、テキスト中の単語を正規化することにより、ユーザが入力したテキストを適切なtsqueryに変換するto_tsqueryplainto_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

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/には、追加パーサとテンプレートの例があります)。辞書と設定は、単に配下のパーサとテンプレートのパラメータを設定し、両者を結び付けるだけなので、新しい辞書と設定を作るために特別な権限は必要ありません。この章の後でカスタム辞書と設定を作る例が登場します。