この記事ではDevOps・自動化に特化して解説します。AI自動化・DevOps全般は AI自動化ツール完全ガイド2026|ノーコードからコードまで徹底比較 をご覧ください。

何が起きたか

Timescaleが開発するPostgreSQL用拡張機能「pg_textsearch」がv1.0.0でリリースされ、プロダクション環境での利用が可能になった。BM25ランキング関数を使用した高速な全文検索機能を提供。シンプルなSQL構文で関連度の高いドキュメントを素早く検出できる。PostgreSQL 17・18に対応し、Linux・macOS(amd64/arm64)向けのプレビルドバイナリが配布されている。

RAGシステム(検索拡張生成)における文書検索の精度向上という観点では、RAGFlowによる検索パイプライン構築と組み合わせることで、PostgreSQLだけで完結した高品質な検索基盤が実現できる。

背景と経緯

PostgreSQLの標準全文検索機能は基本的なキーワードマッチングに限定されており、検索品質(ランキング精度)の面で不十分という課題があった。プロジェクトの元の名称「Tapir(Textual Analysis for Postgres Information Retrieval)」が示すように、情報検索技術をPostgreSQLに統合する試みから発展。BM25はElasticsearchやLuceneなど企業向け検索エンジンで標準的なランキング関数であり、これをPostgresで実装することで、複雑な外部検索システムなしに高品質な全文検索が可能になる。

主な機能と仕様

機能 詳細
BM25ランキング 調整可能なパラメータ(k1, b)でドキュメント関連度を計算
Block-Max WAND最適化 TOP-K検索時に競争力のないブロックをスキップして高速化
言語サポート English, French, German等PostgreSQL標準テキスト検索設定対応
並列インデックス構築 大規模テーブルで複数ワーカーを活用
パーティションテーブル対応 各パーティションが独立してインデックス構築

BM25全文検索の仕組み

BM25(Best Match 25)は、語頻度(TF)と逆文書頻度(IDF)を組み合わせたランキング関数だ。PostgreSQL標準の ts_rank よりも検索品質が高く、ElasticsearchやLuceneが採用する実績ある手法でもある。

pg_textsearchがクエリを受け取ってから結果を返すまでのフローは以下の通りだ。

flowchart TD A["SQLクエリ受信
content <@> 'search terms'"] --> B[BM25インデックスのスキャン開始] B --> C{Block-Max WAND最適化} C -->|スコアが閾値以上| D[ブロックを候補として処理] C -->|スコアが閾値未満| E["ブロックをスキップ
処理コスト削減"] D --> F["語頻度TFの計算
k1パラメータで飽和調整"] F --> G["逆文書頻度IDFの計算
全体文書数から重み付け"] G --> H["文書長正規化
bパラメータで調整"] H --> I["BM25スコア算出
負値で返却"] E --> J{"TOP-K到達?"} I --> J J -->|未到達| C J -->|到達| K["昇順ソートで結果返却
低スコア=高関連度"] style C fill:#fff3cd style E fill:#d4edda style K fill:#cce5ff

Block-Max WAND最適化(Block-Max Weak AND)は、上位K件の候補が確定した後に残りのブロックを積極的にスキップする技術だ。全文書をスコアリングする必要がなくなるため、大規模テーブルでの検索速度が大幅に向上する。

技術的な詳細

インデックス作成と検索構文

BM25インデックスの作成は以下のように行う:

-- テーブル作成
CREATE TABLE documents (
  id bigserial PRIMARY KEY,
  content text
);

-- インデックス作成
CREATE INDEX docs_idx ON documents 
  USING bm25(content) 
  WITH (text_config = 'english');

-- 検索実行(<@> オペレータ使用)
SELECT * FROM documents 
  ORDER BY content <@> 'database system' 
  LIMIT 5;

検索結果は負のBM25スコアで返される(PostgreSQLは昇順インデックススキャンのみサポートのため)。低いスコアほど関連度が高い。

明示的なインデックス指定

PL/pgSQLやストアドプロシージャ内では、自動インデックス検出が機能しないため、明示的に指定:

SELECT * FROM documents 
  WHERE content <@> to_bm25query('database system', 'docs_idx') < -1.0
  ORDER BY content <@> 'database system'
  LIMIT 10;

パラメータ調整

BM25ランキングは調整可能なパラメータ(k1, b)をサポート。パラメータはインデックス作成時に指定できる。

CREATE INDEX docs_idx ON documents 
  USING bm25(content) 
  WITH (text_config = 'english');

text_configパラメータはPostgreSQL標準テキスト検索設定(english, french等)を指定する。

フィルタリング戦略

検索時のフィルタリングは2つのアプローチがある:

1. プリフィルタリング(推奨)

CREATE INDEX ON documents (category_id);
SELECT * FROM documents 
  WHERE category_id = 123
  ORDER BY content <@> 'search terms' 
  LIMIT 10;

行数が少なければBM25スコアリング負荷が軽減される。

2. ポストフィルタリング

SELECT * FROM documents 
  WHERE content <@> to_bm25query('search terms', 'docs_idx') < -5.0
  ORDER BY content <@> 'search terms'
  LIMIT 10;

インデックスが先に上位結果を返してからフィルタリング。結果数が要求値より少なくなる可能性あり。

対応環境とインストール

バージョン互換性

  • PostgreSQL 17:完全対応
  • PostgreSQL 18:完全対応
  • OS:Linux(amd64/arm64)、macOS(amd64/arm64)
  • ステータス:v1.0.0 プロダクション対応

インストール手順

# ソースからコンパイル
cd /tmp
git clone https://github.com/timescale/pg_textsearch
cd pg_textsearch
make
make install  # sudoが必要な場合あり

# PostgreSQLの設定編集
# postgresql.conf に以下を追加
shared_preload_libraries = 'pg_textsearch'

# サーバー再起動後、拡張機能を有効化
CREATE EXTENSION pg_textsearch;

複数PostgreSQL版がある場合、PG_CONFIGを環境変数で指定してコンパイル対象を明示できる:

export PG_CONFIG=/path/to/pg_config
make clean && make && make install

開発ファイルのインストール

# Ubuntu/Debian
sudo apt install postgresql-server-dev-17  # PostgreSQL 17の場合
sudo apt install postgresql-server-dev-18  # PostgreSQL 18の場合

パフォーマンス最適化

インデックス構築時の設定

-- 並列ワーカー数を指定(デフォルト2)
SET max_parallel_maintenance_workers = 4;
SET maintenance_work_mem = '256MB';  -- 並列構築には最低64MB必須

-- インデックス構築
CREATE INDEX docs_idx ON documents 
  USING bm25(content) 
  WITH (text_config = 'english');

並列構築が有効な場合、ログに以下のノーティスが出力:

NOTICE: parallel index build: launched 4 of 4 requested workers

制限事項と回避策

フレーズクエリ非対応

BM25インデックスは語位置情報を保有しないため、”database system” のようなフレーズマッチに直接対応していない。回避策:

-- BM25で候補を取得、サブクエリでフレーズ検索
SELECT * FROM (
  SELECT *, content <@> 'database system' AS score 
  FROM documents 
  ORDER BY score 
  LIMIT 100  -- 過度にフェッチしてフィルタ損失に備える
) sub 
WHERE content ILIKE '%database system%' 
ORDER BY score 
LIMIT 10;

複数列インデックス非対応

インデックスは単一列限定。複数列検索には生成カラムを使用:

ALTER TABLE documents 
  ADD COLUMN search_text text 
  GENERATED ALWAYS AS (
    COALESCE(title, '') || ' ' || COALESCE(content, '')
  ) STORED;

CREATE INDEX ON documents USING bm25(search_text) 
  WITH (text_config = 'english');

パーティション間スコア比較不可

各パーティションは独立した統計情報(total_docs, avg_doc_len)を保有するため、クロスパーティション検索時のスコアは同一スケールでない:

-- 単一パーティション検索(推奨):スコア正確
SELECT * FROM docs 
  WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01'
  ORDER BY content <@> 'search terms' 
  LIMIT 10;

-- クロスパーティション検索:パーティション別に独立計算
SELECT * FROM docs 
  ORDER BY content <@> 'search terms' 
  LIMIT 10;

語長制限

PostgreSQL標準の2047文字制限を継承。Base64やURLなど超長語は切り詰められる(INFOメッセージで通知)。

実装例:記事検索アプリケーション

CREATE TABLE articles (
  id serial PRIMARY KEY,
  title text,
  content text,
  category text,
  published_at timestamp
);

CREATE INDEX articles_content_idx ON articles 
  USING bm25(content) 
  WITH (text_config = 'english');

CREATE INDEX ON articles (category);  -- プリフィルタリング用

-- テストデータ
INSERT INTO articles (title, content, category) VALUES
  ('Database Systems', 
   'PostgreSQL is a powerful relational database system', 
   'technology'),
  ('Search Technology', 
   'Full text search enables finding relevant documents quickly', 
   'technology'),
  ('Information Retrieval', 
   'BM25 is a ranking function used in search engines', 
   'academic');

-- 検索例:テクノロジーカテゴリから"database search"に関連する記事
SELECT 
  title, 
  content <@> 'database search' AS relevance_score
FROM articles 
WHERE category = 'technology'
ORDER BY relevance_score
LIMIT 5;

クエリ計画の確認

EXPLAIN SELECT * FROM documents 
  ORDER BY content <@> 'database system' 
  LIMIT 5;

小規模データセットではPostgreSQLがシーケンシャルスキャンを選択する場合がある。強制的にインデックス使用:

SET enable_seqscan = off;

ただし、BM25スコアリングに必要な統計情報(文書数、平均文書長)は常にインデックスから取得される。

デバッグと統計

インデックス利用状況の確認

SELECT 
  schemaname, 
  tablename, 
  indexname, 
  idx_scan, 
  idx_tup_read, 
  idx_tup_fetch
FROM pg_stat_user_indexes 
WHERE indexrelid::regclass::text ~ 'pg_textsearch';

今後の展望

プロジェクトのROADMAP.mdに記載されている計画中の機能により、全文検索性能の継続的な向上が予定されている。

pg_textsearchのような拡張機能によってPostgreSQL単体での検索品質が高まることで、ElasticsearchやOpenSearchといった専用検索エンジンへの依存を減らす選択肢が現実的になる。特にインフラを簡素化したいチームにとって、Apache Airflowによるデータパイプラインと組み合わせてPostgreSQL中心のアーキテクチャを構築する意義は大きい。

関連記事: AI自動化ツール完全ガイド2026|ノーコードからコードまで徹底比較

参照ソース


この記事はAI業界の最新動向を速報でお届けする「AI Heartland ニュース」です。