0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

DynamoDB がベクトル検索に対応したので試してみた

0
Posted at

はじめに

DynamoDB に埋め込みベクトルを保存して、そのまま類似度検索までできるようになったようです。

この記事では、Terraform でベーステーブルを作り、Terraform 未対応の部分(ベクトルインデックス)は AWS CLI で追加する形で、実際にデータを入れて検索するところまで試してみます。

DynamoDBベクトル検索とは

ベクトルインデックスは、テーブルの属性に埋め込みベクトル(数値の配列)を持たせておくと、そのベクトルに対して近似最近傍検索(ANN)ができる新しいインデックス種別です。既存の GSI/LSI とは別枠で、テーブルごとに最大 5 個まで作成できます。

検索は専用の SearchVectors API を使います。

距離関数は

  • COSINE(方向のみ比較、テキスト埋め込みの定番)
  • DOT_PRODUCT(大きさも考慮、レコメンドで人気度を重みづけしたい場合など)
  • EUCLIDEAN(空間的な距離、画像の近似重複検出など)

の 3 種類から選び、インデックス作成後は変更できません。

最大 4,096 次元まで対応しています。

RAG での関連ドキュメント検索、AI エージェントの記憶、類似商品のレコメンド、異常検知などが主なユースケースとして挙げられています。操作データとベクトルを同じテーブルに置けるので、別ストアとの整合性を気にしなくてよくなるのが一番のうれしさかと思います。

やってみた

前提

  • AWS CLI 2.36.16 以降
  • Terraform 1.5.0 以降

1. Terraform でベーステーブルを作る

Terraform の AWS providerは 6.60.0(本記事執筆時点の最新)でも aws_dynamodb_tablevector_index 系の引数がありません。レジストリのリソースドキュメントを確認しましたが該当パラメータは存在しないため、ベクトルインデックスは AWS CLI 側で追加する構成にしました。

main.tf
# main.tf
terraform {
  required_version = ">= 1.5.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 6.60"
    }
  }
}

provider "aws" {
  region = "ap-northeast-1"
}

resource "aws_dynamodb_table" "products" {
  name         = "vector-search-demo-products"
  billing_mode = "PAY_PER_REQUEST" # ベクトルインデックスはオンデマンドモード限定
  hash_key     = "ProductId"

  attribute {
    name = "ProductId"
    type = "S"
  }
}

output "table_name" {
  value = aws_dynamodb_table.products.name
}
terraform init
terraform apply -auto-approve

2. AWS CLI でベクトルインデックスを追加する

以降のコマンドは同じシェルセッションで上から順にコピペで実行できます。

環境変数の設定
export REGION=ap-northeast-1
export TABLE_NAME=vector-search-demo-products
export INDEX_NAME=DescriptionIndex
export MODEL_ID=amazon.titan-embed-text-v2:0
export DIMENSIONS=256
ベクトルインデックスの作成
aws dynamodb update-table \
    --region "$REGION" \
    --table-name "$TABLE_NAME" \
    --vector-index-updates \
        "[
            {
                \"Create\": {
                    \"IndexName\": \"$INDEX_NAME\",
                    \"VectorAttribute\": {\"AttributeName\": \"DescriptionVector\"},
                    \"Projection\": {\"ProjectionType\": \"ALL\"},
                    \"Dimensions\": $DIMENSIONS,
                    \"DistanceFunction\": \"COSINE\"
                }
            }
        ]"

IndexStatusACTIVE になるまで待ちます。aws dynamodb wait table-exists はテーブル自体のステータスしか見ないため使えません。

IndexStatusのポーリング
while true; do
  STATUS=$(aws dynamodb describe-table --region "$REGION" --table-name "$TABLE_NAME" \
    --query 'Table.VectorIndexes[0].IndexStatus' --output text)
  echo "IndexStatus=$STATUS"
  [ "$STATUS" = "ACTIVE" ] && break
  sleep 10
done

空のテーブルに新規作成する場合でも、実際に手元で試したところ ACTIVE になるまで約 8 分半かかりました。想定より待たされるので、その間に次の埋め込み生成コードを準備しておきます。

スクリーンショット 2026-08-19 20.51.25.png

3. 埋め込みを作ってデータを投入する

3 カテゴリ(コーヒー用品・オフィス用品・ランニング用品)×3 商品、計 9 件だけの小さいカタログで検証します。テキストは英語のままにしています(Titan モデルの挙動をチュートリアル通りに再現するため)。

商品データ投入と埋め込み生成
# データファイル作成
mkdir -p emb

# 商品データ作成
cat > products.tsv <<'EOF'
p01	Insulated Travel Mug	A vacuum insulated stainless steel mug that keeps hot drinks warm for up to twelve hours.
p02	Stovetop Espresso Maker	A compact aluminum pot that brews strong espresso style coffee directly on a burner.
p03	Manual Burr Coffee Grinder	A hand cranked grinder with adjustable ceramic burrs for consistent coffee grounds.
p11	Ergonomic Mesh Office Chair	An adjustable desk chair with breathable mesh back and lumbar support.
p12	Sit Stand Desk Converter	A height adjustable platform that raises a monitor and keyboard for standing work.
p13	Monitor Arm Mount	A clamp mounted articulating arm that lifts a display off the desk surface.
p06	Lightweight Running Shoe	A breathable mesh road shoe with cushioned foam midsole for daily training runs.
p08	Moisture Wicking Running Socks	Ankle height socks knitted from synthetic yarn that pulls sweat away from the skin.
p10	Hydration Waist Belt	An elastic belt that holds two small water flasks during long runs.
EOF

# 埋め込み生成とDynamoDB投入
while IFS=$'\t' read -r id title description; do
  aws bedrock-runtime invoke-model \
    --region "$REGION" \
    --model-id "$MODEL_ID" \
    --body "$(jq -n --arg t "$description" --argjson d "$DIMENSIONS" '{inputText:$t,dimensions:$d,normalize:true}')" \
    --cli-binary-format raw-in-base64-out \
    --content-type application/json \
    --accept application/json \
    "emb/$id.json" >/dev/null

  item=$(jq -c --arg id "$id" --arg title "$title" \
    '{ProductId:{S:$id},Title:{S:$title},DescriptionVector:{L:[.embedding[]|{"N":(.|tostring)}]}}' \
    "emb/$id.json")
  aws dynamodb put-item --region "$REGION" --table-name "$TABLE_NAME" --item "$item"
  echo "put-item done: $id ($title)"
done < products.tsv

# DynamoDBに投入した件数を確認
aws dynamodb scan --region "$REGION" --table-name "$TABLE_NAME" --select COUNT --query 'Count'
# => 9

ベクトル属性は List 型(L)で、各要素が Number 型(N)というシンプルな構造です。

スクリーンショット 2026-08-19 20.52.27.png

4. SearchVectors で検索する

クエリの埋め込み化と検索実行
# 検索クエリを埋め込みベクトルに変換し
QUERY_TEXT="How can I make my desk more comfortable to work at"
aws bedrock-runtime invoke-model \
  --region "$REGION" \
  --model-id "$MODEL_ID" \
  --body "$(jq -n --arg t "$QUERY_TEXT" --argjson d "$DIMENSIONS" '{inputText:$t,dimensions:$d,normalize:true}')" \
  --cli-binary-format raw-in-base64-out \
  --content-type application/json \
  --accept application/json \
  query.json >/dev/null

jq '[.embedding[]|{"N":(.|tostring)}]' query.json > query-vector.json

# DynamoDB SearchVectors API で検索する
aws dynamodb search-vectors \
  --region "$REGION" \
  --table-name "$TABLE_NAME" \
  --index-name "$INDEX_NAME" \
  --search-vector file://query-vector.json \
  --top-k 3 \
  --projection-expression "ProductId, Title"

「デスク周りを快適にしたい」という英文クエリに対して、実際に返ってきた結果です。

順位 ProductId Title Score(COSINE, 低いほど類似)
1 p11 Ergonomic Mesh Office Chair 0.607
2 p12 Sit Stand Desk Converter 0.713
3 p06 Lightweight Running Shoe 0.792

コンソールからも同じ検索ができます。テーブルのアイテム一覧で検索モードを「検索」に切り替えて実行します。

注意点として、コンソールの検索ベクトル欄は CLI の--search-vector{"N": "..."}が並ぶ DynamoDB AttributeValue形式)ではなく、[-0.234, 0.891, ...]のようなプレーンな数値配列を求めます。{"N": "..."}形式のまま貼り付けると「検索ベクトルは有効な数値の配列でなければなりません」というエラーになるので、以下のようにembeddingをそのまま抜き出してから使ってください。

jq '.embedding' query.json

スクリーンショット 2026-08-19 23.46.53.png
スクリーンショット 2026-08-19 23.47.04.png

5. 後片付け

ベクトルインデックスとテーブルの削除
# ベクトルインデックスを削除してからテーブルを削除する
aws dynamodb update-table \
  --region "$REGION" \
  --table-name "$TABLE_NAME" \
  --vector-index-updates "[{\"Delete\": {\"IndexName\": \"$INDEX_NAME\"}}]"

#  IndexStatus が null になるまで待つ
while true; do
  COUNT=$(aws dynamodb describe-table --region "$REGION" --table-name "$TABLE_NAME" \
    --query 'length(Table.VectorIndexes || `[]`)' --output text)
  echo "残インデックス数: $COUNT"
  [ "$COUNT" = "0" ] && break
  sleep 10
done

# テーブルを削除する
terraform destroy -auto-approve

参考リンク

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?