PostgreSQL全文検索パフォーマンスの最適化
Emily Parker
Product Engineer · Leapcell

はじめに
今日のデータ量の多いアプリケーションでは、高速で正確な検索が最優先事項です。 eコマースプラットフォームが商品を検索する場合でも、ドキュメント管理システムがファイルを検索する場合でも、ナレッジベースが記事を提供する場合でも、ユーザーは即座に関連性の高い結果を期待します。 PostgreSQLの組み込み全文検索(FTS)機能は、これらのニーズに対する強力なソリューションを提供し、開発者は構造化されていないテキストを効率的にクエリできます。 ただし、適切な最適化なしでは、データ量が増加するとFTSのパフォーマンスは著しく低下する可能性があります。 この記事では、インデックス作成戦略、辞書の役割、および高度なランキングアルゴリズムに焦点を当て、PostgreSQL全文検索を最適化するための不可欠な技術を掘り下げ、アプリケーションがシームレスな検索エクスペリエンスを提供できるようにします。
コアコンセプトの理解
最適化に入る前に、操作するPostgreSQLの全文検索の基本的なコンポーネントを理解することが重要です。
- **LexemesとTokens:**テキストをPostgreSQLのFTSに入力すると、最初に「トークン」(個々の単語または記号)に分割されます。 これらのトークンは、トークンの正規化された形態である「Lexemes」を生成するために処理され、バリエーション(例:「running」、「ran」、「runs」はすべてLexeme「run」に還元される可能性があります)が削除されます。 この正規化は、テキスト検索構成を通じて実現されます。
- **Text Search Configuration:**これは、全文検索のためにドキュメントがどのように処理されるかを定義するパーサー、辞書、およびテンプレートのコレクションです。 トークンがどのように抽出され、どれが無視され(ストップワード)、どのようにLexemesに正規化されるかを決定します。
- **
tsvector
:**これはPostgreSQLの特別なデータ型で、ドキュメントからの一意のLexemesのソートされたリストとその位置を格納します。 FTSの主要なインデックス可能な単位です。 - **
tsquery
:**このデータ型は検索クエリを表し、Lexemesに処理されますが、検索ロジックを定義するための演算子(AND、OR、NOT)があります。 - **GIN Index (Generalized Inverted Index):**これは
tsvector
列に使用される主要なインデックスタイプです。 GINインデックスは、各LexemeのドキュメントIDのリストを格納し、特定のLexemesを含むドキュメントの非常に高速なルックアップを可能にします。
全文検索の最適化
FTSパフォーマンスの最適化は、主にドキュメント処理を効率化し、検索クエリを迅速に実行することにあります。
GINによる効率的なインデックス作成
全文検索の最も重要なパフォーマンス向上は、適切なインデックスを使用することから得られます。 tsvector
列の場合、GINインデックスが標準的な選択肢です。
tsvector
列とGINインデックスの作成:
まず、tsvector
データを格納する列が必要になります。 ソーステキストが変更されるたびにこの tsvector
が自動的に更新される生成列またはトリガーを保持することは、しばしば有益です。
ALTER TABLE products ADD COLUMN search_vector tsvector GENERATED ALWAYS AS ( to_tsvector('english', coalesce(name, '') || ' ' || coalesce(description, '')) ) STORED; CREATE INDEX product_search_idx ON products USING GIN (search_vector);
この例では、search_vector
はname
とdescription
フィールドを結合してenglish
tsvector
に変換する生成列です。 STORED
キーワードは、この列が計算され、格納されていることを意味します。これはインデックス作成に不可欠です。 次に、このsearch_vector
列にGINインデックスが作成されます。
適切なテキスト検索構成の選択:
english
構成は、汎用的な選択肢として優れていますが、特定の言語またはドメインではカスタム構成が必要になる場合があります。 構成は、パーサー、テンプレート、および辞書を定義します。
より良いLexeme処理のための辞書の活用
辞書は、生のトークンを意味のあるLexemesに変換するために重要です。 ストップワード、同義語、およびステミングを処理します。
ストップワード:「a」、「the」、「is」、「are」のような単語は、通常、検索には関係ありません。「ストップワード辞書」がこれらを削除します。 PostgreSQLの組み込み構成には通常、1つのストップワード辞書が含まれています。
**ステミング:**単語をそのルートフォームに削減します(例:「running」から「run」)。 これにより、「run」の検索で「running」、「ran」、「runs」などを含むドキュメントが見つかることが保証されます。 PostgreSQLはこれにispell
またはsnowball
辞書を使用します。
**同義語:**カスタム同義語辞書を定義して、類似の用語をマッピングできます。 たとえば、「car」を「automobile」にマッピングすると、「car」の検索でも「automobile」が見つかるようになります。
カスタム辞書の作成:
-
辞書テンプレートの定義:
CREATE TEXT SEARCH DICTIONARY my_synonym_dict ( TEMPLATE = synonym, SYNONYMS = 'my_synonyms.txt' );
これは、PostgreSQLデータディレクトリ(または構成済みのパス)にある
my_synonyms.txt
という名前のファイルに同義語マッピングが含まれていることを想定しています。 形式は通常、word : synonym
のようになります。 -
テキスト検索構成の更新:
ALTER TEXT SEARCH CONFIGURATION english ALTER MAPPING FOR asciiword, hword_asciipart, hword_numpart, numword WITH my_synonym_dict, english_stem;
これは、特定のトークンタイプに対して
my_synonym_dict
をenglish_stem
辞書の前に追加します。これは、同義語がステミングの前に適用されることを意味します。構成内の辞書の順序は重要です。
ランキングアルゴリズムによる検索結果の絞り込み
高速な検索であっても、ユーザーは関連性の高い結果を必要とします。 ランキングアルゴリズムは、クエリに対する認識された重要度に基づいて結果を並べ替えるのに役立ちます。
ts_rank
とts_rank_cd
:
PostgreSQLは、ランクを計算するためにts_rank
およびts_rank_cd
関数を提供します。 ts_rank
は、ドキュメント内のクエリ用語の頻度を考慮します。一方、ts_rank_cd
(Cover Density)は、用語の近接性も考慮します。 一般的に、ts_rank_cd
は複数単語クエリでより良い結果をもたらします。
SELECT title, ts_rank_cd(search_vector, websearch_to_tsquery('english', 'PostgreSQL performance optimization')) AS rank FROM articles WHERE search_vector @@ websearch_to_tsquery('english', 'PostgreSQL performance optimization') ORDER BY rank DESC LIMIT 10;
ここでは、websearch_to_tsquery
がよりユーザーフレンドリーなクエリ解析に使用されます。 ts_rank_cd
関数は、search_vector
がクエリにどれだけ適合するかを基にしたランクを計算します。
ベクトルの重み付け:
tsvector
のさまざまな部分に重みを割り当てて、特定のフィールドをより重視させることができます。 たとえば、title
での一致は、body
での一致よりも重要になる場合があります。
ALTER TABLE products ADD COLUMN weighted_search_vector tsvector GENERATED ALWAYS AS ( setweight(to_tsvector('english', coalesce(name, '')), 'A') || ' ' setweight(to_tsvector('english', coalesce(description, '')), 'B') ) STORED; -- 次にこれをランキングに使用します: SELECT name, ts_rank_cd(weighted_search_vector, websearch_to_tsquery('english', 'portable speaker')) AS rank FROM products WHERE weighted_search_vector @@ websearch_to_tsquery('english', 'portable speaker') ORDER BY rank DESC LIMIT 10;
重みA、B、C、Dは、デフォルトでそれぞれ1.0、0.4、0.2、0.1の乗数値に対応しますが、これらはts_rank
関数でweights
配列を提供することでカスタマイズできます。
実践例:商品カタログ検索
ユーザーが名前と説明で商品を検索する商品カタログを考えてみましょう。 商品名での検索をより高い関連性で重視したいと考えています。
スキーマ:
CREATE TABLE products ( id SERIAL PRIMARY KEY, name TEXT NOT NULL, description TEXT, price NUMERIC );
最適化されたFTSセットアップ:
-- 1. 重み付きtsvector列を追加します ALTER TABLE products ADD COLUMN search_document tsvector GENERATED ALWAYS AS ( setweight(to_tsvector('english', name), 'A') || ' ' setweight(to_tsvector('english', coalesce(description, '')), 'B') ) STORED; -- 2. 重み付きtsvectorにGINインデックスを作成します CREATE INDEX products_search_idx ON products USING GIN (search_document); -- 3. ランキング付きの検索クエリ例 SELECT id, name, description, ts_rank_cd(search_document, websearch_to_tsquery('english', 'wireless earbuds')) AS rank FROM products WHERE search_document @@ websearch_to_tsquery('english', 'wireless earbuds') ORDER BY rank DESC LIMIT 20;
このセットアップにより、GINインデックスによる高速な検索と、説明(重みB)よりも商品名(重みA)の一致に基づいた結果の優先順位付けが保証されます。
結論
PostgreSQLの全文検索の最適化は、インデックス作成戦略の慎重な計画、テキスト処理のためのインテリジェントな辞書の利用、およびランキングアルゴリズムの賢明な適用を含む、多面的なプロセスです。 GINインデックスの効果的な実装、適切な辞書を使用したテキスト検索構成のカスタマイズ、および重み付きtsvector
を使用したts_rank_cd
の活用により、検索結果の速度と関連性の両方を大幅に向上させることができます。 これらの技術により、アプリケーションは強力で応答性の高い検索エクスペリエンスを提供し、生のデータをユーザーが活用できる洞察に変えることができます。