~/tools / 埋め込みと検索(RAG) / pgvector SQL Builder

pgvector SQL Builder — pgvector のテーブル・索引・検索の SQL を書く

PostgreSQLのpgvectorでembeddingを保存して検索するためのSQLを書きます。vector(n) または halfvec(n) の列を持つテーブル、距離に合った演算子クラスを使ったHNSWまたはIVFFlatのインデックス、読みやすいスコアを返す最近傍検索のクエリが得られます。pgvectorのドキュメントにある制限を確認し、ベクトルが使う容量も見積もります。データがブラウザの外に出ることはありません。

ベクトル1つが 1,544 バイトなので、10,000 行ではベクトルだけで約 15.4 MB になります(インデックスとほかの列は含みません)。

1. 拡張機能とテーブル

CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE documents (
  id bigserial PRIMARY KEY,
  content text NOT NULL,
  embedding vector(384) NOT NULL
);

2. インデックス

CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops) WITH (m = 16, ef_construction = 64);

3. 検索

PREPARE は、/api/embed で得た質問のベクトルを $1 として受け取ります。'[0.01,-0.02,…]' のようにテキストとして渡してください。

PREPARE search_documents(vector(384)) AS
  SELECT id, content, 1 - (embedding <=> $1) AS cosine_similarity
  FROM documents
  ORDER BY embedding <=> $1
  LIMIT 5;

-- EXECUTE search_documents('[0.0123, -0.0456, ...]');

制限とデフォルト値は、pgvector 0.8.7 のREADMEとソースコードによるもので、2026-11-04に確認しました。 pgvector README

すべてブラウザ上で処理します。このツールはSQLを書くだけで、どこにも接続しません。

pgvectorは、PostgreSQLにベクトルの類似度検索を加えるオープンソースの拡張機能です。Ollamaで作ったembeddingを、ほかのデータと同じデータベースに保存できます。間違えやすい点がいくつかあります。列の次元数は、embeddingモデルの次元数と同じでなければなりません。距離ごとに専用の演算子があり、コサイン距離は <=>、L2距離は <->、内積の符号を反転した値は <#>、L1距離は <+> です。近似インデックスが使われるのは、同じ距離の演算子クラス(<=> なら vector_cosine_ops など)で作られていて、さらにクエリがその演算子そのものを昇順で使って並べ替え、LIMIT を付けているときだけです。このツールは、1組の選択から3つの部分を書きます。CREATE EXTENSION と CREATE TABLE、CREATE INDEX、そして読みやすいスコア付きで最も近い行を返す PREPARE 文です(コサイン類似度は1からコサイン距離を引いた値、内積は <#> に -1 を掛けた値です)。pgvector 0.8.7のREADMEとソースコードにある制限を適用します。1つの列は最大16,000次元、インデックスは vector で2,000次元、halfvec で4,000次元まで、IVFFlatにはL1の演算子クラスがなく、HNSWの m は2から100、ef_construction は4から1,000で、かつ 2 × m 以上です。HNSWでは検索が返すのは最大でも hnsw.ef_search 行(デフォルトは40)なので、LIMIT がそれより大きいと警告します。IVFFlatでは、READMEの出発点である、100万行までは rows / 1000、それより多いときは sqrt(rows)、probes には sqrt(lists) を使って、lists と probes を入れます。生成したSQLは、Ollamaで作った実際の384次元のベクトルを使い、PostgreSQL 16上のpgvector 0.8.7で実行し、EXPLAIN でインデックスが使われていることを確認しました。

このツールの計算部分は、オープンソース(MIT ライセンス)で公開しています。 github.com/seigo5555/toolkitbox-lib/src/pgvector-sql.ts

こんな時に使う

  • OllamaのembeddingをPostgreSQLに読み込む前に、文書のチャンクを入れるテーブルを作る
  • コサイン、L2、内積、L1の距離で、どの演算子がどの演算子クラスに対応するかを確認し、インデックスが実際に使われるようにする
  • 小さなVPSでベクトルが使う容量を見積もり、vector と halfvec を比べる

使い方

  1. テーブル名と、embeddingモデルの次元数を入力します。次元数は /api/embed が返すベクトル1つの長さで、このサイトのEmbedding Similarity Calculatorで確認できます。
  2. 型、距離、インデックスを選びます。Ollamaのドキュメントは、ほとんどのセマンティック検索ではコサイン類似度を勧めており、/api/embed は長さ1のベクトルを返します。
  3. 3つのブロックをコピーして、psqlなどのクライアントで実行します。IVFFlatのインデックスはデータを読み込んだあとに作ります。HNSWのインデックスは空のテーブルにも作れます。

FAQ

どの距離を選べばよいですか?

Ollamaのembeddingsのドキュメントは、ほとんどのセマンティック検索ではコサイン類似度を勧めています。pgvectorでは <=> がコサイン距離を返し、このツールは1から距離を引いて cosine_similarity にします。/api/embed が返すような長さ1のベクトルでは、L2距離の2乗が「2 − 2 × コサイン類似度」になるため、コサイン距離、L2距離、内積のどれでも行の並び順は同じになります。

HNSWとIVFFlatのどちらがよいですか?

pgvectorのREADMEによると、HNSWは(速度と再現率のトレードオフの点で)IVFFlatよりクエリ性能が高い一方、構築が遅くメモリを多く使います。また、学習のステップがないので空のテーブルにも作れます。IVFFlatは構築が速くメモリも少なく、テーブルにある程度データが入ってから作るものです。インデックスがなければ、pgvectorは厳密な検索を行い、完全な再現率が得られます。

クエリでインデックスが使われないのはなぜですか?

READMEによると、クエリには ORDER BY と LIMIT が必要で、ORDER BY は 1 - (embedding <=> …) DESC のような式ではなく、距離の演算子そのものを昇順で使う必要があります。演算子はインデックスの演算子クラスとも合っていなければなりません。<=> には vector_cosine_ops、<-> には vector_l2_ops、<#> には vector_ip_ops、<+> には vector_l1_ops です。このツールは、1つの選択から両方を書きます。READMEは、テーブルが小さいときはテーブルスキャンのほうが速いこともあるとも述べています。

LIMIT より少ない行しか返らないのはなぜですか?

HNSWでは、結果の数が hnsw.ef_search で制限されます。デフォルトは40です。200行のテーブルで LIMIT 100 を試したところ、hnsw.ef_search を100に上げるまでは40行でした。READMEによると、WHERE による絞り込みはインデックスをスキャンしたあとに適用されるので、条件によってはさらに行が減ります。バージョン0.8.0以降は、反復インデックススキャン(SET hnsw.iterative_scan = strict_order または relaxed_order)で、必要に応じてインデックスをさらに走査します。

halfvec とは何ですか?

半精度のベクトル型です。ベクトル1つあたりのサイズは 4 × 次元数 + 8 バイトではなく 2 × 次元数 + 8 バイトで、halfvec のHNSWとIVFFlatのインデックスは、2,000次元ではなく4,000次元まで対応します。各要素は半精度の浮動小数点数なので、値の精度は低くなります。

データはどこかに送信されますか?

いいえ。このツールはブラウザ上でSQLを書くだけで、データベースには接続しません。

関連する記事

関連ツール