メインコンテンツまでスキップ

全文検索

Postgresには、全文検索クエリを処理する関数が組み込まれています。これは、Postgres内の「検索エンジン」のようなものです。

準備

このガイドでは、以下の例をデータとして使用します。

idtitleauthordescription
1The Poky Little PuppyJanette Sebring LowreyPuppy is slower than other, bigger animals.
2The Tale of Peter RabbitBeatrix PotterRabbit eats some vegetables.
3TootleGertrude CramptonLittle toy train has big dreams.
4Green Eggs and HamDr. SeussSam has changing food preferences and eats unusually colored food.
5Harry Potter and the Goblet of FireJ.K. RowlingFourth year of school starts, big drama ensues.

使用方法

このガイドで次の関数を取り上げます。

to_tsvector()

データを検索可能な「トークン(処理上の一単位)」に変換します。to_tsvector()は「to text search vector(テキスト検索文書に変換)」の略です。例えば、以下のようなものです。

select to_tsvector('green eggs and ham')

-- 'egg':2 'green':1 'ham':4 を返します

これらのトークンをまとめて「ドキュメント」と呼び、Postgresはこれを比較に使用できます。

to_tsquery()

to_tsquery()は「to text search query(テキスト検索問い合わせに変換)」の略で、クエリ文字列をマッチする「トークン」に変換します。

この変換ステップは、キーワードを「曖昧にマッチ」させるために重要です。 例えば、ユーザーが「eggs」を検索して、カラムに「egg」という値があった場合、マッチしたものを返したくなります。

マッチ: @@

@@記号は、全文検索の「マッチ」記号です。この記号は、to_tsvectorの結果とto_tsqueryの結果の間で一致するものを返します。

次の例を見てみましょう。

select * 
from books
where title = 'Harry';

上の等号記号(=)は、マッチするものに対して非常に「厳密」です。フルテキスト検索では、「Harry Potter」の本をすべて見つけたい場合がありますので、上記の例を書き換えることができます。

select * 
from books
where to_tsvector(title) @@ to_tsquery('Harry');

基本的な全文検索クエリ

1つの列を検索

descriptionに「big」という単語が含まれるbooksをすべて検索するには次の通りです。

select 
*
from
books
where
to_tsvector(description)
@@ to_tsquery('big');

複数の列を検索

descriptionまたはtitlelittleという単語が含まれるすべてのbooksを検索します。

select 
*
from
books
where
to_tsvector(description || ' ' || title) -- 列を連結する際には、必ずスペースを入れて区切ってください。
@@ to_tsquery('little');

すべての検索ワードに一致

descriptionlittlebigの両方の単語が含まれているbooksをすべて検索するには、&記号を使用します。

select 
*
from
books
where
to_tsvector(description)
@@ to_tsquery('little & big'); -- 検索条件のANDに&を使います

任意の検索語にマッチ

descriptionlittleまたはbigのいずれかの単語が含まれているbooksをすべて検索するには、記号を使用します。

select 
*
from
books
where
to_tsvector(description)
@@ to_tsquery('little | big'); -- 検索条件のORに|を使用します

bigを検索すると、bigger(またはbigestなど)という単語を含む結果が表示されることに注目してください。

インデックスを作成

さて、全文検索が動作するようになったので、インデックスを作成してみましょう。これにより、Postgresが事前に文書を「構築」でき、クエリ実行時に文書を作成する必要がなくなります。これにより、クエリの実行がより速くなります。

検索可能な列

titledescriptionの列の検索可能なインデックスを格納するために、booksテーブルの中に新しい列ftsを作成しましょう。

Postgresの特別な機能である、生成列を使用して、titledescriptionsの列の値が変更されるたびにインデックスが更新されるようにします。

alter table 
books
add column
fts tsvector generated always as (to_tsvector('english', description || ' ' || title)) stored;

create index books_fts on books using gin (fts); -- インデックスを生成

select id, fts
from books;

新しいカラムを使った検索

インデックスを作成して追加したので、これまでと同じ手法で検索してみましょう。

select 
*
from
books
where
fts @@ to_tsquery('little & big');

クエリ演算子

次により高度な全文検索クエリに使用できる演算子を紹介します。さらに学ぶには、PostgreSQLのテキスト検索関数と演算子を参照してください。

近接:<->

近接は、一定の「距離」を隔てた用語を検索するのに便利です。 例えば、「big」にマッチした後、「dreams」にマッチする「big dreams」というフレーズを検索する場合に使用します。

select 
*
from
books
where
to_tsvector(description) @@ to_tsquery('big <-> dreams');

<->を使って、一定の距離内にある単語を探すこともできます。例えば、yearschoolを2語以内で検索するには、次のようにします。

select 
*
from
books
where
to_tsvector(description) @@ to_tsquery('year <2> school');

否定:!

否定は、検索語を含まないフレーズを見つけるために使用できます。例えば、bigという単語を含み、littleという単語を含まないレコードを見つけるには次の通りになります。

select 
*
from
books
where
to_tsvector(description) @@ to_tsquery('big & !little');

リソース