AnalyticDB for PostgreSQL のベクトルデータベースは、構造化フィールドフィルタリング、半構造化フィールドフィルタリング、およびベクトル検索と全文検索に基づく双方向取得を組み合わせたハイブリッド検索をサポートしています。このトピックは V6.0 および V7.0 に適用されます。V7.0 固有のガイダンスについては、「V7.0 でのベクトル検索と全文検索に基づく双方向取得」をご参照ください。
ハイブリッド検索の利用シーン
クエリの特性に基づいて取得メソッドを選択します:
| メソッド | 最適なケース | シナリオ例 |
|---|---|---|
| ベクトル検索のみ | セマンティック類似性 — 正確なキーワードよりも意図が重要 | 概念的に関連するドキュメントの検索 |
| 全文検索のみ | 完全なキーワード一致 — 特定の用語が必須 | 特定のエラーメッセージやプロダクトコードの検索 |
| ベクトル検索 + 構造化フィルタリング | フィルタリングされたデータセット内でのセマンティック類似性 | 価格帯内の類似プロダクトの検索 |
| 双方向取得 (ベクトル + 全文検索) | 意味と完全なキーワードの両方が重要なクエリ、または埋め込みの品質に一貫性がない場合 | RAG パイプライン、e コマース検索、混合クエリワークロード |
ベクトル検索だけでは必要な再現率を達成できない場合に、双方向取得を使用します。たとえば、埋め込みモデルが特定のクエリタイプに対して不十分な表現を生成する場合や、コーパスに逐語的に一致する必要があるキーワード重視の用語が含まれている場合などです。
ハイブリッド検索の仕組み
近似最近傍探索 (ANNS) ベクトルインデックスは、非構造化データのみを扱います。本番環境では、構造化データと非構造化データを一緒に検索する必要がしばしばあります。たとえば、特定の場所で特定の時間枠内に人物が現れるかどうかを検索する場合、時間範囲と場所は構造化データであり、顔のイメージは非構造化データです。
ほとんどのハイブリッド検索の実装では、構造化データをデータベースに、非構造化データをベクトル検索システムに格納し、両方からの結果を交差させます。構造化フィルタリング後にベクトル検索が返す行数が少なすぎないように、フィルターを適用する前に増幅係数によって過剰にフェッチします。しかし、高選択性フィルターは依然として、必要なトップ K の数を下回るほど多くの結果を除外する可能性があります。
AnalyticDB for PostgreSQL は異なるアプローチを採用しています。FastANN ベクトル検索エンジンをインデックス拡張として直接統合し、オプティマイザーのランクバイアスオーバーラップ (RBO) コスト見積もりルールを使用して、構造化データと非構造化データを一緒に検索する実行計画を生成します。
実行計画のカテゴリ
インデックス付きの構造化条件列が存在するかどうか、およびそれらの条件の選択性に基づいて、AnalyticDB for PostgreSQL は 3 つの実行計画のいずれかを選択します。
以下の例では、プロダクトテーブルを使用して 3 つすべての計画を実演します。
テーブルとインデックスを作成します:
CREATE TABLE products (
id serial primary key,
name varchar(256),
price real,
inTime timestamp,
url varchar(256),
feature real[]
);
-- ベクトル列のストレージモードを PLAIN に設定します。
ALTER TABLE products ALTER COLUMN feature SET STORAGE PLAIN;
-- 構造化列に B-tree インデックスを作成します。
CREATE INDEX ON products(price, intime);
-- ベクトル列にベクトルインデックスを作成します。
CREATE INDEX ON products USING ann(feature) WITH (dim=512);
-- 統計情報を収集して、ハイブリッド検索に最適な実行計画を生成します。
ANALYZE products;クエリ:指定されたイメージに最も類似し、価格が 100 USD から 200 USD の間で、2019 年 3 月に追加された上位 100 アイテムを検索します:
SELECT id, price FROM products WHERE
price > 100 AND price <=200
AND inTime > '2019-03-01 00:00:00' AND inTime <= '2019-03-31 00:00:00'
ORDER BY
feature <-> array[10,2.0,..., 512.0]
LIMIT 100;カテゴリ 1:ブルートフォースクエリ
オプティマイザーは、構造化条件に一致するすべての行をフェッチし、ベクトル距離でソートして、最も近い 100 行を返します。これにより 100% の再現率が保証されますが、構造化条件が多数の行に一致する場合は低速になります。
QUERY PLAN
-----------------------------------------------------------------------------------
LIMIT
-> Gather Motion 3:1 (slice1; segments: 3)
Merge Key: (l2_squared_distance($0, feature))
-> LIMIT
-> Sort
Sort Key: (l2_squared_distance($0, feature))
-> Index Scan using products_price_idx on products
Index Cond: the filter condition for the price column.
Filter: the filter condition for the time column.
Optimizer: Postgres query optimizerカテゴリ 2:純粋なベクトル検索と構造化フィルタリング
オプティマイザーは、ベクトルインデックスに N 個の最も類似した行をクエリし、その後、構造化条件でフィルタリングします。これは高速ですが、構造化条件の選択性が高い場合、最終的な結果がリクエストされた行数よりも少なくなる可能性があります。
QUERY PLAN
-----------------------------------------------------------------------------------
LIMIT
-> Gather Motion 3:1 (slice1; segments: 3)
Merge Key: ((feature <-> $0))
-> LIMIT
-> Ann Index Scan using products_feature_idx on products
ORDER BY: (feature <-> $0)
Filter: the filter condition for the price and time columns.
Optimizer: Postgres query optimizerカテゴリ 3:ハイブリッド検索 (ベクトル + 構造化インデックス)
この計画は両方のアプローチを組み合わせたものです。構造化条件列にインデックスがあり、そのインデックスタイプがビットマップをサポートしている場合、オプティマイザーは構造化インデックスからビットマップを生成し、それをベクトルインデックススキャンにプッシュします。これにより、再現率を維持しながらフィルタリングを高速化します。
QUERY PLAN
-----------------------------------------------------------------------------------
LIMIT
-> Gather Motion 3:1 (slice1; segments: 3)
Merge Key: ((feature <-> $0))
-> LIMIT
-> Fusion Ann Scan
-> Bitmap Index Scan on products_price_idx
Index Cond: the filter condition for the price column.
-> Ann Index Scan with filter using products_feature_idx on products
ORDER BY: (feature <-> $0)
Filter: the filter condition for the time column.
Optimizer: Postgres query optimizer2 つの実行計画ノードは、ハイブリッド検索が利用中であることを示します:
Ann Index Scan with filter:フィルター条件をベクトルインデックスにプッシュダウンし、フィルタリングとインデックススキャンを組み合わせます。
Fusion Ann Scan:構造化条件列がサポートされているインデックスを持つ場合に使用されます。構造化インデックス (左サブツリー) からビットマップを生成し、そのビットマップをベクトルインデックススキャン (右サブツリー) にプッシュします。右サブツリーは
Ann Index Scan(ビットマップのみ) またはAnn Index Scan with filter(ビットマップと追加のフィルター条件) のいずれかです。
ハイブリッド検索のメソッド
AnalyticDB for PostgreSQL は 3 つのハイブリッド検索メソッドをサポートしています:
| メソッド | 使用するインデックスタイプ | サポートされるデータ型 |
|---|---|---|
| ベクトル検索 + 構造化フィールドフィルタリング | B-tree インデックス | BIGINT、BOOLEAN、BYTEA、CHAR、VARCHAR、INTEGER、FLOAT、DOUBLE、DATE、SMALLINT、TIMESTAMP、SERIAL |
| ベクトル検索 + 半構造化フィールドフィルタリング | GIN インデックス | JSON、JSONB、ARRAY |
| 双方向取得:ベクトル + 全文検索 | ベクトルインデックス + GIN インデックス | — |
サポートされている構造化データ型の詳細については、「データ型」をご参照ください。
以下のセクションでは、半構造化フィールドフィルタリングと双方向取得の設定方法を示します。B-tree インデックスを使用した構造化フィールドフィルタリングは、上記の「実行計画のカテゴリ」セクションで実演されています。
半構造化フィールドフィルタリングの使用
この例では、各レコードに記事のチャンク、そのリリース時間、言及されている株式 ID、および埋め込みベクトルが含まれる株式分析データベースを使用します。
テーブルスキーマ
| フィールド | データ型 | 説明 |
|---|---|---|
| id | serial | シリアル番号 |
| chunk | varchar(1024) | 株式分析記事からのテキストチャンク |
| release_time | timestamp | 記事のリリース時間 |
| stock_id_list | char(10)[] | 記事で言及されている株式の ID |
| url | varchar(1024) | ソース記事の URL |
| feature | real[] | テキストチャンクの埋め込みベクトル |
前提条件
開始する前に、以下を確認してください:
AnalyticDB for PostgreSQL V6.0 または V7.0 インスタンス
テーブルとインデックスを作成する権限
半構造化フィールドフィルタリングの設定
テーブルを作成します。
CREATE TABLE stock_analysis_chunks ( id serial primary key, chunk varchar(1024), release_time timestamp, stock_id_list integer[], url varchar(1024), feature real[] ) distributed by (id);ベクトル列のストレージモードを PLAIN に設定します。
ALTER TABLE stock_analysis_chunks ALTER COLUMN feature SET STORAGE PLAIN;ベクトルインデックスを作成します。
CREATE INDEX ON stock_analysis_chunks USING ann(feature) WITH (dim=1536, distancemeasure=cosine, hnsw_m=64, pq_enable=1, hnsw_ef_construction=128);構造化列と半構造化列のインデックスを作成します。
-- 構造化列に B-tree インデックスを作成します。 CREATE INDEX ON stock_analysis_chunks(release_time); -- 半構造化列に GIN インデックスを作成します。 CREATE INDEX ON stock_analysis_chunks USING gin(stock_id_list);統計情報を収集します。
ANALYZE stock_analysis_chunks;ハイブリッド検索クエリを実行します。以下のクエリは、2023 年 7 月 18 日から 8 月 18 日の間に公開され、指定された 4 つの株式すべてに言及している株式分析記事を、クエリベクトルとのセマンティック類似性でランク付けして返します:
SELECT id, url, cosine_similarity(feature, array[1.0, 2.0, ..., 1536.0]::real[]) as score FROM stock_analysis_chunks WHERE release_time >= '2023-07-18 00:00:00' AND release_time <= '2023-08-18 00:00:00' AND stock_id_list @> ARRAY['BABA', 'AAPL', 'MSFT', 'AMZN'] ORDER BY feature <=> array[1.0, 2.0, ..., 1536.0]::real[] LIMIT 10;
双方向取得 (ベクトル + 全文検索) の使用
双方向取得は、ベクトル検索と全文検索を並行して実行し、その結果をマージします。このメソッドは、ベクトル検索だけでは十分な再現率を達成できない場合に使用します。たとえば、埋め込みモデルが特定のクエリタイプで性能が低い場合や、コーパスに完全なキーワード一致が必要な用語が含まれている場合などです。
前提条件
開始する前に、以下を確認してください:
AnalyticDB for PostgreSQL V6.0 または V7.0 インスタンス
テーブル、インデックス、および関数を作成する権限
双方向取得の設定
テーブルを作成します。
to_tsvector列には、事前に計算された全文検索トークンが格納されます。行を挿入する際に、PostgreSQL の
to_tsvector()関数を使用してto_tsvector列にデータを入力します。例:to_tsvector('english', text)。行を更新する際は、この列をtext列と同期させてください。-- vector:ベクトル列。to_tsvector:全文検索トークナイザー列。 CREATE TABLE IF NOT EXISTS documents( id TEXT, docname TEXT, title TEXT, vector real[], text TEXT, to_tsvector TSVECTOR);ベクトル列のストレージモードを PLAIN に設定します。
ALTER TABLE documents ALTER COLUMN vector SET STORAGE PLAIN;インデックスを作成します。
ベクトル列にベクトルインデックスを作成します。
CREATE INDEX ON documents USING ann(vector) WITH (dim=1024, distancemeasure=cosine, hnsw_m=64, pq_enable=1, hnsw_ef_construction=128);全文検索トークナイザー列に GIN インデックスを作成します。
CREATE INDEX ON documents USING gin(to_tsvector);
クエリテキストを
tsqueryに変換するためのヘルパー関数を作成します。CREATE OR REPLACE FUNCTION public.to_tsquery_from_text(txt text, lang regconfig DEFAULT 'english'::regconfig) RETURNS tsquery LANGUAGE sql IMMUTABLE STRICT AS $function$ SELECT to_tsquery(lang, COALESCE(string_agg(split_part(word, ':', 1), ' | '), '')) FROM (SELECT UNNEST(STRING_TO_ARRAY(TO_TSVECTOR(lang, txt)::text, ' ')) AS TEXT) AS words_only;$function$双方向取得クエリを実行します。このクエリでは、共通テーブル式 (CTE) を使用して両方の検索を実行し、結果をマージします。最初のブランチはベクトル類似性によって上位 10 行を取得し、2 番目のブランチは全文検索の関連性によって上位 10 行を取得します。
UNION ALLは両方のセットを結合し、外側のSELECTはidで重複排除し、両方のメソッドで見つかった行に対しては高い方の類似度スコアを採用します。結果の列
列 説明 similarityこの行に対するベクトル検索または全文検索のいずれか高い方のスコア sourceどのメソッドが行を見つけたかを示すビットマスク: 1= ベクトルのみ、2= 全文検索のみ、3= 両方WITH combined AS ( ( SELECT id, docname, text, cosine_similarity(densevec,ARRAY{embedding}::real[]) AS similarity, 1 AS source FROM documents ORDER BY vector <=> ARRAY[10,2.0,…, 1024.0] LIMIT 10 ) UNION ALL ( SELECT id, docname, text, ts_rank(to_tsvector, to_tsquery_from_text('{query.query}', 'zh_cn'), 32) AS similarity, 2 AS source FROM documents WHERE to_tsvector@@to_tsquery_from_text('{query.query}', 'zh_cn') ORDER BY similarity DESC LIMIT 10 ) ) SELECT id, docname, title, text, MAX(similarity) AS similarity, BIT_OR(source) AS source FROM combined GROUP BY id ORDER BY similarity DESC;クエリは最大で 20 行を返します (ベクトル検索から 10 行 + 全文検索から 10 行、
idで重複排除)。ベクトル検索と全文検索のバランス調整
デフォルトでは、クエリはベクトル類似度スコアと全文検索関連性スコアに同じ重みを与えます。一方のメソッドを優先するには、
UNION ALLの前にあるいずれかのブランチで類似度の値を乗算します。たとえば、全文検索の結果の重みを 2 倍にするには、2 番目のブランチでts_rank(...) AS similarityをts_rank(...) * 2 AS similarityに置き換えます。