Amazon S3 Vectors にベクトルを投入して Bedrock で類似検索してみた

Amazon S3 Vectors にベクトルを投入して Bedrock で類似検索してみた

Amazon S3 Vectorsはベクトル検索をS3ネイティブに提供するサービスです。専用DBなしにコスト効率よくベクトル検索が実現できる仕組みと、実際の使用方法を紹介します。
2026.07.18

こんにちは!製造ビジネステクノロジー部の小林です。

今回は、Amazon S3 Vectors とは何か、何が嬉しいのか、そして実際に触ってみるところまで紹介します。

Amazon S3 Vectors とは?

Amazon S3 Vectors は、S3 にベクトル保存・類似検索機能を提供するバケットタイプ です。
ベクトルデータベースを構築・運用することなく、S3 の耐久性・スケーラビリティ・低コストをそのまま活かしてベクトル検索が実現できます。

https://aws.amazon.com/jp/s3/features/vectors/

ベクトルとは?

ベクトルとは、意味を数値の並びで表現したものです。たとえば「犬」「猫」「自動車」という単語を、Embedding モデルという AI にかけると、次のような数値の配列に変換されます。

犬     → [0.21, 0.88, -0.12, ..., 0.07]
猫     → [0.23, 0.85, -0.10, ..., 0.05]   ← 犬と数値が近い(意味が近い)
自動車 → [-0.71, 0.02, 0.45, ..., 0.33]   ← 犬とは遠い(意味が遠い)

このように「意味の近さ」を数値の距離で計算できるようにしたものがベクトルです。
生成 AI や RAG では、この仕組みを使って「質問に近い意味の文書」を高速に探し出しています。

https://aws.amazon.com/jp/what-is/embeddings-in-machine-learning/
https://aws.amazon.com/jp/what-is/vector-databases/

主な特徴

  • 専用バケット
    ベクトルデータを格納する専用バケット
  • インデックス
    1 つのベクトルバケット内に、最大 10,000 個のインデックスを作成可能
  • 大規模データ対応
    1 つのインデックスあたり、数千万ベクトルまで格納可能
  • 類似検索
    コサイン類似度・ユークリッド距離の 2 種類の距離計算に対応
  • メタデータフィルタリング
    属性値で絞り込みながら、近傍(類似)検索が可能
  • サーバーレス
    クラスタを常時起動する必要がなく、ストレージ・クエリの利用量に応じた従量課金

データの持ち方

S3 Vectors を触る前に、はじめに戸惑いやすいポイントを整理しておきます。

インデックスの作成は必須

S3 Vectors は、通常の S3 バケットのように「作ってすぐ PUT」ではありません。
まずベクトルバケットを作り、その中にベクトルインデックスを作成する必要があります。

保存できるのは「ベクトル + メタデータ」

1 件のベクトルレコードは、次の要素で構成されます。

  • key : 一意な識別子(文字列)
  • data : ベクトル本体(float 配列)
  • metadata : 任意の key-value(JSON)。フィルタリング条件にも使える

メタデータには、元テキストや元ファイルへの参照(S3 URI など)を入れておくのが定番です。

Vector Bucket には「通常のオブジェクト」は置けない

Vector Bucket は通常の S3 バケットとは別種のバケットで、PutObject のような通常のオブジェクト API は使えません。

PDF・画像・音声などの元ファイルは 別の通常の S3 バケットに置き、その S3 URI を Vector のメタデータに紐付けておく、という構成になります。

全体像

上記をまとめると、次のような構成になります。

  • 通常の S3 バケット:元ファイル(PDF・画像など)の置き場
  • Vector Bucket → Vector Index:ベクトル本体とメタデータの置き場
  • 両者はメタデータの s3_uri で紐付ける

何が嬉しいのか?

1. コストを抑えやすい

OpenSearch Service はクラスタを常時起動する必要があるため、クエリ頻度が低いワークロードではコストが課題になる場合があります。

一方、S3 Vectors はサーバーレスで従量課金のため、クエリ頻度がそれほど高くないワークロードではコスト効率が良いです。

2. 運用がラク

ベクトルバケットを作ってインデックスを切れば、あとは PUT/QUERY するだけです。
シャーディング、レプリケーション、スケーリング、パッチ適用といった運用作業がありません。

3. Bedrock とのシームレスな統合

Knowledge Bases のベクトルストアとして数クリックで指定できるため、RAG アプリケーションを簡単に構築できます。

4. 大規模データ + ホット/コールド階層化

アクセス頻度の低いベクトルは S3 Vectors に、頻繁に検索されるものは OpenSearch へ、と自然に階層化できるため、大規模なベクトル資産の運用コストを最適化できます。

どんなユースケースにハマるか

S3 Vectors は「大量に貯めて、時々検索する」ワークロードにハマります。代表的なユースケースはこちら。

1. 大規模 RAG のベクトルストア

社内文書・マニュアル・議事録・過去チケットなどを大量に貯めておき、生成 AI から意味検索するパターン。Bedrock Knowledge Bases のバックエンドとして数クリックで接続できます。

https://aws.amazon.com/jp/bedrock/knowledge-bases/

2. ホット/コールドの階層化

よく検索されるベクトルは OpenSearch に置いて高速レスポンス、古いアーカイブは S3 Vectors に落として保管コスト削減。S3 標準 → S3 IA と同じ発想をベクトル DB でやれるイメージです。

https://aws.amazon.com/jp/opensearch-service/

3. マルチメディア検索

画像・動画・音声を Embedding して「似た画像を探す」「近い曲を探す」といった用途。件数が膨大になりがちなので、S3 Vectors のコスト特性がハマります。EC の類似商品レコメンド、動画コンテンツの類似シーン検索などが代表例です。

4. ログ・イベントの意味検索

エラーログや障害チケットをベクトル化して「これに似た過去障害は?」を検索。発生の都度データを入れていくが、検索は必要な時だけ、という使い方が中心です。常時起動が不要な S3 Vectors のコスト構造にハマります。

逆に不向きなユースケース

S3 Vectors はサーバーレス構成のため、秒間数百〜数千クエリのような継続的な高スループット検索には向きません。そのようなユースケースでは OpenSearch Service や Aurora pgvector など、常時起動可能なマネージドサービスの検討をお勧めします。

やってみた

今回は以下のシンプルなケースを試します。

  1. Vector bucket を作成する
  2. Vector index を作成する
  3. Amazon Bedrock で文章を埋め込みベクトルに変換する
  4. ベクトルを S3 Vectors に保存する
  5. クエリ文で類似検索を実行する

1. ベクトルバケットを作成する

マネジメントコンソールの S3 画面を開き、左メニューの「ベクトルバケット」 を選択します。

スクリーンショット 2026-07-18 11.26.49

「Create vector bucket」をクリックし、以下を設定します。

  • Vector bucket name: devio-s3-vectors-demo

※ 今回は検証用バケットで検証後はすぐ削除するため暗号化は設定しません。

スクリーンショット 2026-07-18 11.28.50
スクリーンショット 2026-07-18 11.30.06

ベクトルバケットが作成できました。
スクリーンショット 2026-07-18 11.31.54

2. ベクトルインデックスを作成する

ベクトルインデックスとは?

ベクトルインデックスは、ベクトルバケットの中に作る検索用の入れ物です。

「同じルール(次元数・距離メトリック)で検索したいベクトルの集まり = 1 つのベクトルインデックス」と考えると分かりやすいです。用途ごとに分けて作れて、1 バケットあたり最大 10,000 個まで作成できます。

DB の「インデックス」とは別モノ

名前は同じ「インデックス」ですが、役割はかなり違います。

DB のインデックス ベクトルインデックス
位置づけ テーブルの「補助」 データ本体の「入れ物」
検索方法 完全一致・範囲(=, >, LIKE) 意味の近さ(距離)で近傍検索
結果 条件に合う 0〜N 件 距離が近い順に上位 K 件
なくても動く ❌(ベクトル自体が入らない)

RDB の CREATE INDEX → 既存データに検索補助を追加する
S3 Vectors の Vector index 作成 → これから入れるベクトルの保存場所そのものを作る

では、ベクトルインデックスを作ります。

ベクトルバケットの詳細画面から「Create vector index」をクリック。
スクリーンショット 2026-07-18 11.33.10

下記の値で設定を行います。

  • インデックス名: devio-index
  • ディメンション: 1024 (Amazon Titan Text Embeddings V2 のデフォルトに合わせる)
  • 距離メトリック: Cosine
  • 暗号化設定: 指定なし

スクリーンショット 2026-07-18 11.35.47

パラメータ説明

  • インデックス名
    • ベクトルバケット内で一意な名前
  • ディメンション
    • 1 ベクトルあたりの次元数。使う Embedding モデルの出力次元と揃える必要があります(例: Titan Text Embeddings V2 = 1024)
    • 1〜4096 の範囲で指定可能。大きいほどストレージ量が増える
  • 距離メトリック
    • Cosine: ベクトルの「向き」で類似度を測る。テキスト検索・RAG では一般的にコサイン類似度が選ばれることが多い
    • Euclidean: ベクトル間の直線距離で測る。ベクトルの「大きさ」にも意味がある場合に使う
  • 暗号化
    • デフォルトは SSE-S3(バケット設定継承)。KMS キーで管理したい場合は「バケット設定を上書きする」で SSE-KMS を選択
    • ⚠️ 暗号化設定はインデックス作成後に変更不可なので最初に決めておく

インデックスが作成できました。
スクリーンショット 2026-07-18 11.40.26

3. 埋め込みベクトルを作って PUT する

ベクトルインデックスにデータを入れていきます。ここでやることは次の 3 ステップです。

  1. 元となる文章(ドキュメント)を用意する
  2. Amazon Bedrock の Titan Text Embeddings V2 を使って、各文章を 1024 次元のベクトルに変換する
  3. 変換したベクトルを put_vectors API で S3 Vectors のインデックスに投入する

今回は AWS サービスの説明文を 4 件用意し、それぞれをベクトル化して devio-index に投入します。あわせて category などのメタデータも付けておき、後段のフィルタ検索で使えるようにします。

まずは Python の実行環境を準備します。

python3 -m venv .venv
source .venv/bin/activate
pip install -U boto3

実行する Python スクリプトはこちら。以下を put_vectors.py として保存します。

put_vectors.py
import json
import boto3
from botocore.exceptions import ClientError

# 設定値
REGION = "ap-northeast-1"                    # S3 Vectors / Bedrock を利用するリージョン
BUCKET = "devio-s3-vectors-demo"             # 事前に作成しておくベクトルバケット名
INDEX = "devio-index"                        # 事前に作成しておくベクトルインデックス名
MODEL_ID = "amazon.titan-embed-text-v2:0"    # 使用する埋め込みモデル(1024 次元)

# AWS クライアント
bedrock = boto3.client("bedrock-runtime", region_name=REGION)   # 埋め込み生成用
s3v = boto3.client("s3vectors", region_name=REGION)             # ベクトル PUT 用

def preflight():
    # このリージョンにあるベクトルバケット一覧を取得
    try:
        buckets = s3v.list_vector_buckets().get("vectorBuckets", [])
    except ClientError as e:
        print(f"[preflight] list_vector_buckets failed: {e}")
        return False

    names = [b.get("vectorBucketName") for b in buckets]
    print(f"[preflight] buckets in {REGION}: {names}")
    if BUCKET not in names:
        # 指定バケットが見つからない(REGION 違い or 名前ミス)
        print(f"[preflight] bucket '{BUCKET}' が見つかりません。REGION か BUCKET を見直してください。")
        return False

    # バケット配下のインデックス一覧を取得
    try:
        indexes = s3v.list_indexes(vectorBucketName=BUCKET).get("indexes", [])
    except ClientError as e:
        print(f"[preflight] list_indexes failed: {e}")
        return False
    # インデックス名の一覧を抽出して表示
    index_names = [i.get("indexName") for i in indexes]
    print(f"[preflight] indexes in '{BUCKET}': {index_names}")
    if INDEX not in index_names:
        # 指定インデックスが未作成
        print(f"[preflight] index '{INDEX}' が見つかりません。INDEX 名を見直すか、まず作成してください。")
        return False

    print("[preflight] bucket / index の存在を確認")
    return True

# 投入対象のドキュメント。key はベクトルの一意識別子。
docs = [
    {"id": "doc-1", "text": "Amazon S3 Vectorsはベクトル検索をS3ネイティブに提供する新機能です。"},
    {"id": "doc-2", "text": "Amazon Bedrockは生成AIモデルをAPIで簡単に呼び出せるサービスです。"},
    {"id": "doc-3", "text": "Amazon OpenSearch Serviceは全文検索やベクトル検索が可能なマネージドサービスです。"},
    {"id": "doc-4", "text": "AWS Lambdaはサーバーレスでコードを実行できるコンピューティングサービスです。"},
]

# 埋め込み生成
def embed(text: str) -> list[float]:
    """
    Bedrock Titan Embeddings V2 でテキストを 1024 次元の float ベクトルに変換する。
    normalize=True にしておくとコサイン類似度の計算に有利。
    """
    body = json.dumps({"inputText": text, "dimensions": 1024, "normalize": True})
    res = bedrock.invoke_model(modelId=MODEL_ID, body=body)
    return json.loads(res["body"].read())["embedding"]

def main():
    # 事前チェック(失敗したら即終了)
    if not preflight():
        raise SystemExit(1)

    # 各ドキュメントを埋め込みベクトル化して PUT 用ペイロードに詰め替え
    vectors = []
    for d in docs:
        vectors.append({
            "key": d["id"],                                # ベクトルの一意 ID(あとで更新・削除で指定)
            "data": {"float32": embed(d["text"])},         # ベクトル本体(1024 次元の float 配列)
            "metadata": {                                  # 検索時のフィルタや、結果への付帯情報
                "source_text": d["text"],
                "category": "aws-service",
            },
        })

    # S3 Vectors にまとめて投入(同じ key で PUT すれば upsert 動作)
    s3v.put_vectors(
        vectorBucketName=BUCKET,
        indexName=INDEX,
        vectors=vectors,
    )
    print(f"PUT {len(vectors)} vectors")

if __name__ == "__main__":
    main()

実行します。

python put_vectors.py

実行結果です。

(.venv) developers-io %python put_vectors.py
[preflight] buckets in ap-northeast-1: ['devio-s3-vectors-demo']
[preflight] indexes in 'devio-s3-vectors-demo': ['devio-index']
[preflight] bucket / index の存在を確認
PUT 4 vectors

スクリーンショット 2026-07-18 18.37.19

ソース説明

  • embed(text)
    • Bedrock の Titan Text Embeddings V2 を呼び出して、テキストを 1024 次元の float 配列に変換
    • normalize: True にしておくとコサイン類似度計算に有利
  • put_vectors
    • key: ベクトルの一意識別子
    • data.float32: ベクトル本体
    • metadata: 任意のキー・バリュー。検索時のフィルタや、結果の付帯情報として利用

ベクトルインデックスが投入されたか確認します。しかしコンソールから個別のベクトル一覧を見ることができません。バケット詳細画面には次のような案内が出ます。

ベクトルを挿入、一覧表示、クエリするには、AWS CLI、AWS SDK、または Amazon S3 REST API を使用してください。

スクリーンショット 2026-07-18 13.47.49

なので、確認は API 経由で行います。

list.py
import boto3
s3v = boto3.client("s3vectors", region_name="ap-northeast-1")
res = s3v.list_vectors(
    vectorBucketName="devio-s3-vectors-demo",
    indexName="devio-index",
    returnMetadata=True,
)
for v in res["vectors"]:
    print(f"- key={v['key']} text={v['metadata']['source_text']}")

実行します。

python list.py

実行結果です。

- key=doc-4  text=AWS Lambdaはサーバーレスでコードを実行できるコンピューティングサービスです。
- key=doc-3  text=Amazon OpenSearch Serviceは全文検索やベクトル検索が可能なマネージドサービスです。
- key=doc-2  text=Amazon Bedrockは生成AIモデルをAPIで簡単に呼び出せるサービスです。
- key=doc-1  text=Amazon S3 Vectorsはベクトル検索をS3ネイティブに提供する新機能です。

ドキュメント 4 件がしっかり保存されました!

スクリーンショット 2026-07-18 13.51.18

4. 類似検索(Query)を実行する

保存したベクトルの中から、質問文と 意味が近いもの を上から順に取り出してみます。ここが RAG や検索アプリの「検索」部分にあたる、S3 Vectors の操作です。

コードの流れ

  1. 質問文 "S3にベクトルを直接保存できるサービスは?" を用意
  2. その質問文を Bedrock(Titan Embeddings V2) でベクトルに変換
  3. そのベクトルを query_vectors API に渡して S3 Vectors に「これに近いもの top 3 ちょうだい」と依頼
  4. S3 Vectors が距離が近い順にドキュメントを返してくれる
  5. key / distance / 元テキスト をターミナルに表示

ポイント: 質問文のベクトル化は PUT のときと 同じモデル・同じ次元数 で行う必要があります(揃っていないと比較が成り立たない)。

以下をquery_vectors.py として保存します。

query_vectors.py
import json
import boto3

REGION = "ap-northeast-1"
BUCKET = "devio-s3-vectors-demo"
INDEX = "devio-index"
MODEL_ID = "amazon.titan-embed-text-v2:0"

bedrock = boto3.client("bedrock-runtime", region_name=REGION)
s3v = boto3.client("s3vectors", region_name=REGION)

def embed(text: str) -> list[float]:
    body = json.dumps({"inputText": text, "dimensions": 1024, "normalize": True})
    res = bedrock.invoke_model(modelId=MODEL_ID, body=body)
    return json.loads(res["body"].read())["embedding"]

# ① 質問文を用意 → ② ベクトル化
query_text = "S3にベクトルを直接保存できるサービスは?"
query_vec = embed(query_text)

# ③ S3 Vectors に「近いものを top 3 ちょうだい」
res = s3v.query_vectors(
    vectorBucketName=BUCKET,
    indexName=INDEX,
    queryVector={"float32": query_vec},
    topK=3,
    returnMetadata=True,
    returnDistance=True,
)

# ④ 結果を表示
for r in res["vectors"]:
    print(f"- key={r['key']} distance={r['distance']:.4f} text={r['metadata']['source_text']}")

query_vectors に渡している主なパラメータ

パラメータ 何をしているか
queryVector 「これに近いものを探して」の元になるベクトル
topK 上から何件返してほしいか(今回は 3)
returnMetadata PUT 時に添えた metadata(元テキストなど)も返してほしい
returnDistance どれくらい近かったか(スコア)も返してほしい

実行結果です。

(.venv) developers-io %python query_vectors.py
- key=doc-1 distance=0.5082 text=Amazon S3 Vectorsはベクトル検索をS3ネイティブに提供する新機能です。
- key=doc-2 distance=0.7358 text=Amazon Bedrockは生成AIモデルをAPIで簡単に呼び出せるサービスです。
- key=doc-3 distance=0.7530 text=Amazon OpenSearch Serviceは全文検索やベクトル検索が可能なマネージドサービスです。

結果の読み方

  • distance はベクトル同士の 遠さ を表す数値。小さいほど 意味が近い
  • 質問「S3 にベクトルを直接保存できるサービスは?」に対して、S3 Vectors の説明文(doc-1)が 0.5082 で一番近く出ています
  • 他のサービス(doc-2 Bedrock, doc-3 OpenSearch)は 0.7 台で、明確に差がついています
  • 全く関係ない doc-4(Lambda)は top 3 に入っていないので出てこない

つまり、キーワード完全一致ではなく意味で「近い順」に取り出せているのが確認できました!

スクリーンショット 2026-07-18 14.09.25

5. メタデータフィルタで絞り込む

RAG や検索アプリを実際に作ると、「意味が近い」だけでは足りない場面がけっこう出てきます。

  • テナント A のユーザーには、テナント A のドキュメントだけを返したい
  • 公開日が新しい記事だけを対象にしたい
  • カテゴリ「database」に限定して検索したい

こういうときに使えるのがメタデータフィルタです。PUT 時にベクトルへ付けておいた metadata を、検索時に filter パラメータで指定するだけで、「意味の近さ」と「属性での絞り込み」を 1 回のクエリで同時にかけられます。

実際に category で絞り込んでみます。

準備:カテゴリ違いのデータを追加

現状のインデックスに入っているのは category = "aws-service" の 4 件だけなので、これだと「フィルタが効いた!」感が出ません。そこで比較用に、category = "database" のドキュメントを 2 件追加投入します。

新しく put_extra_vectors.py を作ります(構造は put_vectors.py と同じで、投入するドキュメントとカテゴリだけ差し替えます)。

put_extra_vectors.py
import json
import boto3

# 設定値(put_vectors.py と揃える)
REGION = "ap-northeast-1"
BUCKET = "devio-s3-vectors-demo"
INDEX = "devio-index"
MODEL_ID = "amazon.titan-embed-text-v2:0"

# AWS クライアント
bedrock = boto3.client("bedrock-runtime", region_name=REGION)   # 埋め込み生成用
s3v = boto3.client("s3vectors", region_name=REGION)             # ベクトル PUT 用

# 埋め込み生成関数
def embed(text: str) -> list[float]:
    """
    Bedrock Titan Embeddings V2 でテキストを 1024 次元の float ベクトルに変換する。
    put_vectors.py の embed() と同じ設定(次元数・normalize)にすること。
    """
    body = json.dumps({"inputText": text, "dimensions": 1024, "normalize": True})
    res = bedrock.invoke_model(modelId=MODEL_ID, body=body)
    return json.loads(res["body"].read())["embedding"]

# 追加投入するドキュメント(category = "database")
# メタデータで category を切ることで、後の Query 時に $eq: "database" などで絞り込める。
extra = [
    {"id": "doc-5", "text": "Amazon RDS for PostgreSQLはリレーショナルデータベースのマネージドサービスです。", "category": "database"},
    {"id": "doc-6", "text": "Amazon DynamoDBはNoSQLのマネージドキーバリューデータベースです。",           "category": "database"},
]

# 各ドキュメントを埋め込みベクトル化して PUT 用ペイロードに詰め替え
vectors = [
    {
        "key": d["id"],                                    # ベクトルの一意 ID(同じ key で PUT すると upsert)
        "data": {"float32": embed(d["text"])},             # ベクトル本体(1024 次元)
        "metadata": {                                      # 検索時のフィルタ・付帯情報
            "source_text": d["text"],
            "category": d["category"],
        },
    }
    for d in extra
]

# S3 Vectors にまとめて投入
s3v.put_vectors(vectorBucketName=BUCKET, indexName=INDEX, vectors=vectors)
print(f"PUT {len(vectors)} extra docs")

実行します。

(.venv) developers-io %python put_extra_vectors.py
PUT 2 extra docs

これで index には aws-service カテゴリ 4 件 + database カテゴリ 2 件の計 6 件が入りました。

スクリーンショット 2026-07-18 18.53.07

フィルタ有無で比較する

これでインデックスには aws-service カテゴリ 4 件 と database カテゴリ 2 件、合わせて 6 件が入りました。実際にフィルタを効かせてみます。

同じクエリを 2 回投げます。

  • 1 回目: フィルタなし → 全カテゴリの中から意味の近い順に上位 5 件
  • 2 回目: category = "database" で絞り込み → その中から意味の近い順に上位 5 件

これで「フィルタありとなしでどう結果が変わるか」を並べて確認できます。

クエリ文はどちらも「データを保存するサービスは?」で固定。以下を query_with_filter.py として保存します。

query_with_filter.py
import json
import boto3

# 設定値
REGION = "ap-northeast-1"
BUCKET = "devio-s3-vectors-demo"
INDEX = "devio-index"
MODEL_ID = "amazon.titan-embed-text-v2:0"

# AWS クライアント
bedrock = boto3.client("bedrock-runtime", region_name=REGION)   # クエリ文の埋め込み生成用
s3v = boto3.client("s3vectors", region_name=REGION)             # ベクトル検索用

# 埋め込み生成関数
def embed(text: str) -> list[float]:
    """
    Bedrock Titan Embeddings V2 でテキストを 1024 次元の float ベクトルに変換。
    PUT 時と同じモデル・同じ次元数・同じ normalize 設定でベクトル化しないと比較が成り立たない。
    """
    body = json.dumps({"inputText": text, "dimensions": 1024, "normalize": True})
    res = bedrock.invoke_model(modelId=MODEL_ID, body=body)
    return json.loads(res["body"].read())["embedding"]

# クエリ文をベクトル化(2 回のクエリで使い回す)
qv = embed("データを保存するサービスは?")

# ===== ① フィルタなし =====
# 意味の近い順にそのまま上位 5 件を取得する。
# category に関係なく「意味の近さ」だけで並ぶことを確認する。
print("=== フィルタなし ===")
res = s3v.query_vectors(
    vectorBucketName=BUCKET,
    indexName=INDEX,
    queryVector={"float32": qv},   # 「これに近いものを探して」の元ベクトル
    topK=5,                        # 上位何件返すか
    returnMetadata=True,           # PUT 時に付けた metadata も返してもらう
    returnDistance=True,           # スコア(距離)も返してもらう
)
for r in res["vectors"]:
    # distance は小さいほど意味が近い(コサインの場合)
    print(f"  {r['key']:6s} d={r['distance']:.4f} cat={r['metadata']['category']:12s} {r['metadata']['source_text']}")

# ===== ② filter で category = 'database' に絞り込み =====
# メタデータで属性検索を効かせる。ベクトル検索(意味の近さ)と
# 属性フィルタ(完全一致)を 1 回のクエリで同時に効かせられる。
print("\n=== filter: category = 'database' ===")
res = s3v.query_vectors(
    vectorBucketName=BUCKET,
    indexName=INDEX,
    queryVector={"float32": qv},
    topK=5,
    filter={"category": {"$eq": "database"}},   # ← ここでカテゴリ絞り込み($eq は MongoDB 風の演算子)
    returnMetadata=True,
    returnDistance=True,
)
for r in res["vectors"]:
    print(f"  {r['key']:6s} d={r['distance']:.4f} cat={r['metadata']['category']:12s} {r['metadata']['source_text']}")

実行します。

python query_with_filter.py

実行結果です。

(.venv) developers-io %python query_with_filter.py
=== フィルタなし ===
  doc-5  d=0.7092 cat=database     Amazon RDS for PostgreSQLはリレーショナルデータベースのマネージドサービスです。
  doc-6  d=0.7481 cat=database     Amazon DynamoDBはNoSQLのマネージドキーバリューデータベースです。
  doc-3  d=0.7551 cat=aws-service  Amazon OpenSearch Serviceは全文検索やベクトル検索が可能なマネージドサービスです。
  doc-4  d=0.7899 cat=aws-service  AWS Lambdaはサーバーレスでコードを実行できるコンピューティングサービスです。
  doc-1  d=0.8058 cat=aws-service  Amazon S3 Vectorsはベクトル検索をS3ネイティブに提供する新機能です。

=== filter: category = 'database' ===
  doc-5  d=0.7092 cat=database     Amazon RDS for PostgreSQLはリレーショナルデータベースのマネージドサービスです。
  doc-6  d=0.7481 cat=database     Amazon DynamoDBはNoSQLのマネージドキーバリューデータベースです。

スクリーンショット 2026-07-18 18.59.28

結果の読み方

filter なし

  • 上位 5 件が「意味の近さ」順にそのまま並びます
  • DB 系(doc-5, doc-6)がまず来て、次に検索・実行系(doc-3, doc-4, doc-1)が続きました
  • category に関係なく、意味が近いものから順に返っています

filter を付けたとき

  • aws-service カテゴリ 4 件はすべて除外され、database カテゴリの 2 件だけが返っています
  • 検索対象そのものが category = "database" の集合に絞られた上で、その中で意味の近い順に並びます

このように「意味の近さ」と「属性による絞り込み」を 1 回のクエリで両立できるのが、S3 Vectors のメタデータフィルタの強みです。

おわりに

今回は Amazon S3 Vectors を使って、ベクトルの投入から類似検索、メタデータフィルタでの絞り込みまでを一通り触ってみました。

S3 Vectors はバケットとインデックスを作るだけ・使った分だけ課金という手軽さで、「ちょっと RAG を試したい」「大量のベクトルを安く保存したい」といったユースケースにはピッタリだと思います。

この記事がどなたかの参考になれば幸いです。

この記事をシェアする

関連記事