HNSW索引での含まれる列

ベクトル索引での含まれる列により、インメモリー近傍グラフ・ベクトル索引内に非ベクトル列を組み込むことで、属性フィルタによる検索が高速化されます。

住宅価格データを分析する典型的な例について考えてみます。価格が200万ドル未満か特定の郵便番号(あるいはその両方)の条件指定を満たす上位5軒の住宅のみを選び出します。または、より細かい条件指定およびフィルタ属性に一致する上位の住宅のみを取得する必要があります。含まれる列は、価格や郵便番号などのフィルタ可能な属性をベクトル索引に直接組み込むことで、これを可能にします。この統合により、索引はベクトル類似度と属性フィルタの両方に基づいて結果を評価および返すことができ、実表を相互参照するための追加のステップを必要としません。

従来のデータベース・フィルタとAIを利用したベクトル検索のギャップを埋めることで、含まれる列ではより迅速で効率的な結果を得ることができます。この相乗効果により、特に高度な類似度計算とともにきめ細かいフィルタリングが必要なユースケースで、検索のパフォーマンスが向上します。

構文と指定

価格、説明およびベクトルを含む住宅販売データがある実表を作成します:
CREATE TABLE houses (id NUMBER, zip_code NUMBER, 
    price NUMBER, description CLOB, data_vector VECTOR);
その後、次のように、含まれる列にpriceを使用してHNSWベクトル索引を作成できます:
CREATE VECTOR INDEX vidx_hnsw
ON houses (data_vector)
INCLUDE (price)
ORGANIZATION INMEMORY NEIGHBOR GRAPH
WITH TARGET ACCURACY 95;

前述の構文では、含まれる列がPRICEであるDATA_VECTOR列でINMEMORY NEIGHBOR GRAPHとしてORGANIZATIONを使用して、ベクトル索引VIDX_HNSWを作成しています。INCLUDEキーワードにより、ユーザーが、HNSW索引に含める属性を指定できます。

V$VECTOR_GRAPH_INDEXビューを問い合せることで、含まれる列を確認できます:

SELECT OWNER, INDEX_NAME, COVERING_COLS
FROM V$VECTOR_GRAPH_INDEX;

OWNER   INDEX_NAME   COVERING_COLS
------- ------------ --------------------------------
SYS     VIDX_HNSW    PRICE

ノート:

INCLUDEの制限事項:

INCLUDEリストには次のものを含めることができます:

  • 最大31列に制限されます。Oracle索引では、32個のキー列がサポートされます。
  • 次のデータ型のみがサポートされます: NUMBERCHARVARCHAR2DATETIMESTAMPRAWJSON、およびBLOBCLOBなどの参照ベースのLOB型。
  • NLS型およびLONG型はサポートされていません。

含まれる列を使用する利点

  1. 含まれる列がある場合とない場合のHNSWでのフィルタリングなし:

    指定されたベクトルに最も近い5つの住宅をフェッチするための、フィルタリングを使用しない問合せを考えた場合、結果として得られる問合せは、複数の結合を含む実行計画になります。

    SELECT /*+ VECTOR_INDEX_TRANSFORM(t) */ price 
    FROM houses t 
    ORDER BY VECTOR_DISTANCE(data_vector, :query_vector) 
    FETCH APPROX FIRST 5 ROWS ONLY;
    

    ノート:

    query_vectorには、実際の入力ベクトルが含まれています。「FLOAT32ベクトル・ジェネレータを使用したSQLクイック・スタート」で説明されている手順を使用して、query_vectorを生成できます。

    次の実行計画では複数の結合が表示されています:

    PLAN_TABLE_OUTPUT
    --------------------------------------------------------------------------------------------
    Plan hash value: 2484688059
    
    --------------------------------------------------------------------------------------------
    | Id | Operation                      | Name        | Rows | Bytes | Cost (%CPU)| Time     |
    --------------------------------------------------------------------------------------------
    |  0 | SELECT STATEMENT               |             |    5 |    20 |      3 (67)| 00:00:01 |
    |* 1 |  COUNT STOPKEY                 |             |      |       |            |          |
    |  2 |   VIEW                         |             |    5 |    20 |      3 (67)| 00:00:01 |
    |* 3 |    SORT ORDER BY STOPKEY       |             |    5 |  7965 |      3 (67)| 00:00:01 |
    |  4 |     TABLE ACCESS BY INDEX ROWID| HOUSES      |    5 |  7965 |      2 (50)| 00:00:01 |
    |  5 |      VECTOR INDEX HNSW SCAN    | VIDX_HNSW   |    5 |  7965 |      2 (50)| 00:00:01 |
    --------------------------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
       1 - filter(ROWNUM<=5)
       3 - filter(ROWNUM<=5)

    この実行計画では、最も近いベクトルについてVIDX_HNSW索引をスキャンしてから、返されたINDEX ROWIDで実表HOUSESにアクセスしていることがわかります。これは、HNSW索引においてベクトルを見つけた後に、HOUSES表に戻り、索引付けされていない列をその実表から取り出すためです。

    次の実行計画では、含まれる列を指定して作成されたインメモリー近傍グラフ・ベクトル索引(HNSW)を使用する場合と同じ操作が表示されています:

    PLAN_TABLE_OUTPUT
    ---------------------------------------------------------------------------------------
    Plan hash value: 656317923
    
    ---------------------------------------------------------------------------------------
    | Id | Operation                 | Name       | Rows  | Bytes | Cost (%CPU)| Time     |
    ---------------------------------------------------------------------------------------
    |  0 | SELECT STATEMENT          |            |     5 |    20 |      3 (67)| 00:00:01 |
    |* 1 |  COUNT STOPKEY            |            |       |       |            |          |
    |  2 |   VIEW                    |            |     5 |    20 |      3 (67)| 00:00:01 |
    |* 3 |    SORT ORDER BY STOPKEY  |            |     5 |  7965 |      3 (67)| 00:00:01 |
    |  4 |     VECTOR INDEX HNSW SCAN| VIDX_HNSW  |     5 |  7965 |      2 (50)| 00:00:01 |
    ---------------------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
       1 - filter(ROWNUM<=5)
       3 - filter(ROWNUM<=5)

    前述の実行計画からわかるように、ベクトル索引に含まれる列を追加すると、実表の事前フィルタ評価が不要になります。この問合せは、HNSWベクトル索引のみをスキャンすることで解決できます。これは、問合せ対象の列をその索引から直接調達できるためです。

  2. 含まれる列を使用したHNSWでのフィルタリング:

    近傍グラフ・ベクトル索引でより一般的に使用される計画の1つは、事前フィルタ計画です。この場合、実表の内容をフィルタリングして、無関係な行を排除します。ただし、フィルタ属性を使用しない問合せの評価は、操作に複数の結合が含まれるため、非常にコストがかかる場合があります。次の問合せでは、含まれる列を使用しない近傍パーティション・ベクトル索引を使用しています。

    SELECT /*+ vector_index_transform(houses vidx_hnsw in_filter_with_join_back) */ 
    price
    FROM houses
    WHERE price < 2000000
    ORDER BY vector_distance(data_vector, :query_vector)
    FETCH APPROX FIRST 5 ROWS ONLY;

    ノート:

    query_vectorには、実際の入力ベクトルが含まれています。「FLOAT32ベクトル・ジェネレータを使用したSQLクイック・スタート」で説明されている手順を使用して、query_vectorを生成できます。
    一方、included列としてpriceを指定して作成されたHNSW索引に対して、同じインフィルタ問合せを使用した場合、その実行計画では、実表との結合が回避されるため、より少ない結合が表示されます。これを次の計画に示します:
    PLAN_TABLE_OUTPUT
    ------------------------------------------------------------------------------------------------------
    Plan hash value: 942384497
    
    ------------------------------------------------------------------------------------------------------
    | Id | Operation                           | Name            | Rows  | Bytes | Cost (%CPU)| Time     |
    ------------------------------------------------------------------------------------------------------
    |  0 | SELECT STATEMENT                    |                 |     5 |    20 |  1272   (1)| 00:00:01 |
    |* 1 |  COUNT STOPKEY                      |                 |       |       |            |          |
    |  2 |   VIEW                              |                 |     5 |    20 |  1272   (1)| 00:00:01 |
    |* 3 |    SORT ORDER BY STOPKEY            |                 |     5 |  8025 |  1272   (1)| 00:00:01 |
    |  4 |     VECTOR INDEX HNSW SCAN IN-FILTER| VIDX_HNSW       |     5 |  8025 |  1271   (1)| 00:00:01 |
    |  5 |      VIEW                           | VW_HIJ_3C2A15A9 |     1 |       |     2   (0)| 00:00:01 |
    |* 6 |       FILTER                        |                 |       |       |            |          |
    |  7 |        FAST DUAL                    |                 |     1 |       |     2   (0)| 00:00:01 |
    ------------------------------------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
       1 - filter(ROWNUM<=5)
       3 - filter(ROWNUM<=5)
       6 - filter("HOUSES"."PRICE"<2000000)