すべてのプロダクト
Search
ドキュメントセンター

AnalyticDB:ベクトル検索と全文検索に基づく双方向検索

最終更新日:May 14, 2025

AnalyticDB for PostgreSQL ベクトルデータベースのハイブリッド検索機能を使用すると、構造化データと非構造化データを同時に検索できます。ハイブリッド検索は、構造化フィールドフィルタリング、半構造化フィールドフィルタリング、およびベクトル検索と全文検索に基づく双方向検索をサポートしています。

このトピックは、AnalyticDB for PostgreSQL V6.0 および V7.0 での双方向検索に適しています。ただし、V7.0 には V7.0 でのベクトル検索と全文検索に基づく双方向検索 の方が適しています。

ハイブリッド検索の概要

近似最近傍探索 (ANNS) ベクトルインデックスは、非構造化データ検索のみを処理できます。実際の運用環境では、構造化データと非構造化データを同時に検索する必要がある場合があります。たとえば、特定の期間内に特定の領域に特定の人物が現れたかどうかをクエリしたいとします。期間と領域の範囲は構造化データである可能性があり、顔は非構造化データとして保存される可能性があります。これらの要件を満たすために、AnalyticDB for PostgreSQL は、構造化データと非構造化データ向けのハイブリッド検索機能を提供しています。

業界のほとんどのハイブリッド検索ソリューションは、構造化データをデータベースシステムに、非構造化データをベクトル検索システムに保存します。2 種類のシステムからの検索結果が交差して、共通の一致が検出されます。次に、交差した結果が集計されて、最終結果が得られます。この方法では、ベクトル検索システムから増幅された結果 (数量 = 増幅係数 × 上位 K) が抽出され、構造化インデックスを使用して結果がフィルタリングされます。ただし、構造化インデックスでは多数の結果が除外される可能性があり、上位 K 個の結果を取得するという要件を満たせない場合があります。次のセクションでは、構造化フィールドと非構造化フィールドを使用したハイブリッド検索の仕組みについて説明します。

ハイブリッド検索の仕組み

AnalyticDB for PostgreSQL は、FastANN ベクトル検索エンジンをインデックス拡張としてデータベースに統合し、構造化インデックスと半構造化インデックスを同時に使用してベクトル検索を実行できるようにします。オプティマイザーのランクバイアスオーバーラップ (RBO) コスト推定ルールを使用して、構造化データと非構造化データを同時に検索するためのさまざまな実行計画を生成できます。

次の例は、ハイブリッド検索の仕組みを示しています。

指定された画像と最も類似していて、価格が 100 米ドルから 200 米ドルの範囲内で、先月販売された上位 100 個のアイテムを検索するとします。

テーブルとインデックスを作成するためのステートメントの例:

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 ツリーインデックスを作成します。
CREATE INDEX ON products(price, intime);

-- ベクトル列のベクトルインデックスを作成します。
CREATE INDEX ON products USING ann(feature) WITH (dim=512);

-- 統計を収集して、ハイブリッド検索の最適な実行計画を生成します。
ANALYZE products;

ハイブリッド検索を実行するためのステートメントの例:

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;

AnalyticDB for PostgreSQL は、構造化条件列 (price と inTime) に対してインデックスが作成されているかどうか、および構造化条件の選択性に基づいて、3 つのカテゴリの実行計画を生成できます。パフォーマンスと取得の要件を満たすために、AnalyticDB for PostgreSQL は最適な実行計画を選択します。

  • カテゴリ 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: price 列のフィルター条件。
                             Filter: time 列のフィルター条件。
    
     Optimizer: Postgres クエリ オプティマイザー
  • カテゴリ 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: price 列と time 列のフィルター条件。
    
     Optimizer: Postgres クエリ オプティマイザー
  • カテゴリ 3: ベクトル検索と構造化フィルタリングを組み合わせたハイブリッド検索

    この実行計画は、前述の 2 つの実行計画の利点を組み合わせています。インデックスを使用してデータをクエリし、データ結果が少なくなる可能性を防ぎます。構造化条件列にインデックスが付けられていて、インデックスタイプがビットマップをサポートしている場合、この実行計画では他のインデックスを使用してビットマップを生成できます。これにより、ハイブリッド検索が高速化されます。実行計画の例:

                                           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: price 列のフィルター条件。
                         ->  Ann Index Scan with filter using products_feature_idx on products
                             ORDER BY: (feature <-> $0)
                             Filter: time 列のフィルター条件。
    
     Optimizer: Postgres クエリ オプティマイザー

    Fusion Ann Scan ノードまたは Ann Index Scan with filter ノードが表示される場合は、この実行計画でハイブリッド検索が使用されていることを示します。

    • Ann Index Scan with filter: このノードは、フィルター条件とインデックスを使用できるように、フィルター条件をベクトルインデックスにプッシュダウンします。

    • Fusion Ann Scan: このノードは、特定の構造化条件列にインデックスが付けられている場合に表示されます。このノードは、左側のサブツリーとしてビットマップを生成し、ビットマップを右側のサブツリーとしてベクトルインデックスにプッシュダウンすることで、構造化条件の計算を高速化できます。Fusion Ann Scan ノードが表示される場合、右側のサブツリーは Ann Index Scan または Ann Index Scan with filter の場合があります。Ann Index Scan ノードが表示される場合は、ビットマップのみがプッシュダウンされます。Ann Index Scan with filter ノードが表示される場合は、ビットマップ以外のフィルター条件がプッシュダウンされます。

使用方法

AnalyticDB for PostgreSQL ベクトルデータベースのハイブリッド検索は、構造化フィールドフィルタリング、半構造化フィールドフィルタリング、およびベクトル検索と全文検索に基づく双方向検索をサポートしています。次のいずれかの方法を使用して、ハイブリッド検索を実装できます。

  • 構造化フィールドフィルタリングと組み合わせたベクトル検索。BIGINT、BOOLEAN、BYTEA、CHAR、VARCHAR、INTEGER、FLOAT、DOUBLE、DATE、SMALLINT、TIMESTAMP、SERIAL など、すべての構造化フィールドのデータ型がサポートされています。構造化フィールドは、デフォルトの B ツリーインデックスを作成することで、ハイブリッド検索を高速化するのに役立ちます。詳細については、「データ型」をご参照ください。

  • 半構造化フィールドフィルタリングと組み合わせたベクトル検索。JSON、JSONB、ARRAY など、すべての半構造化フィールドのデータ型がサポートされています。構造化フィールドは、GIN インデックスを作成することで、ハイブリッド検索を高速化するのに役立ちます。詳細については、「JSON データと JSON-B データの管理」および「ARRAY 型」をご参照ください。

  • ベクトル検索と全文検索に基づく双方向検索。

構造化フィールドによるクエリの高速化については、このトピックの「ハイブリッド検索の仕組み」セクションをご参照ください。次の例では、半構造化フィールドフィルタリングを使用してハイブリッド検索を実行する方法と、ベクトル検索と全文検索に基づいて双方向検索を実行する方法を示します。

ハイブリッド検索で半構造化フィールドフィルタリングを使用する

証券会社に、株式分析記事を含む stock_analysis_chunks という名前のデータベースがあるとします。次の表に、データベースに含まれるフィールドを示します。

フィールド

データ型

説明

id

serial

シリアル番号。

chunk

varchar(1024)

株式分析記事の一部であるテキストチャンク。

release_time

timestamp

株式分析記事の公開時刻。

stock_id_list

char(10)[]

株式分析記事に関連する株式の ID。

url

varchar(1024)

テキストチャンクが属する記事の URL。

feature

real[]

テキストブロックの埋め込みベクトル。

  1. stock_analysis_chunks という名前のテキストデータベーステーブルを作成します。

    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);
  2. ベクトル列のストレージモードを PLAIN に設定します。

    ALTER TABLE stock_analysis_chunks ALTER COLUMN feature SET STORAGE PLAIN;
  3. ベクトル列のベクトルインデックスを作成します。

    CREATE INDEX ON stock_analysis_chunks USING ann(feature) WITH (dim=1536, distancemeasure=cosine, hnsw_m=64, pq_enable=1, hnsw_ef_construction=128);
  4. ハイブリッド検索に関連付けられた構造化列と半構造化列のインデックスを作成します。

    -- 構造化列の B ツリーインデックスを作成します。
    CREATE INDEX ON stock_analysis_chunks(release_time);
    -- 半構造化列の GIN インデックスを作成します。
    CREATE INDEX ON stock_analysis_chunks USING gin(stock_id_list);
  5. 統計を収集します。

    ANALYZE stock_analysis_chunks;
  6. ハイブリッド検索を実行します。

    先月公開された特定のテキストコンテンツを含む株式分析記事をクエリするには、次のステートメントを実行します。

    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 の双方向検索方法では、ベクトル検索と全文検索を使用してデータのサブセットを個別に取得し、マージされた検索結果を再ランク付けおよび後処理して効果を高めます。

手順

  1. テーブルを作成します。

    -- vector: ベクトル列。to_tsvector: 全文トークナイザー列。
    CREATE TABLE IF NOT EXISTS documents(
                    id TEXT,
                    docname TEXT,
                    title TEXT,
                    vector real[],
                    text TEXT,
                    to_tsvector TSVECTOR);
  2. ベクトル列のストレージモードを PLAIN に設定します。

    ALTER TABLE documents ALTER COLUMN vector SET STORAGE PLAIN;
  3. インデックスを作成します。

    • ベクトル列のベクトルインデックスを作成します。

      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);
  4. 依存関数を作成します。

    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$
  5. 双方向検索を実行します。

     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;
                  

    UNION 句では、最初の部分でベクトル検索を使用して 10 行のデータを取得し、2 番目の部分で全文検索を使用して 10 行のデータを取得します。次に、id 列に基づいてデータ結果の重複が削除されます。最後に、前述の双方向検索ステートメントは、20 行以下のデータを返します。