Amazon DynamoDB でセカンダリインデックスから検索したアイテムを削除したい

こんにちは、広野です。

Amazon DynamoDB は大量データからのキーバリューマッチングは超高速で素晴らしいのですが、クエリにかなりの制約があるので扱いが非常に難しいです。

実はセカンダリインデックスを使ったデータ削除もクエリ一発でできないので、スクリプトを組む必要があります。

今回はそんなスクリプトの紹介をしたいと思います。

やりたいこと

以下のサンプル DynamoDB テーブルがあるとします。

通常、category, user, id, checked の 4つの情報を元に検索をしています。※今回の説明で直接的に必要ない他の属性は割愛

categoryuser
パーティションキー
checkedid
ソートキー
id
属性(グローバルセカンダリインデックスのパーティションキー)
AAA#user1 checked#001 001
AAA#user1 unchecked#002 002
AAA#user2 unchecked#001 001
AAA#user2 checked#002 002
AAA#user3 checked#005 005

ここで、以下のように id が 002 のデータだけを削除したいとします。

categoryuser
パーティションキー
checkedid
ソートキー
id
属性(グローバルセカンダリインデックスのパーティションキー)
AAA#user1 unchecked#002 002
AAA#user2 checked#002 002

パーティションキーに格納されている category や user は大量にパターンがあるため、パーティションキー、ソートキーによる検索は難しい状況です。そのため、id をパーティションキーにしたグローバルセカンダリインデックスを作成し、該当のデータを削除したいと思います。

ところが、仕様上、セカンダリインデックスで条件を指定したデータを 1本のクエリで削除することができません。

削除するには、一旦削除対象のデータを取得し、メインのパーティションキー、ソートキーを条件にしてベタに削除処理をループさせることになります。

上の例では、以下 2 つの条件で削除クエリをかけます。

  1. categoryuser が AAA#user1 で、checkedid が unchecked#002 であるもの
  2. categoryuser が AAA#user2 で、checkedid が checked#002 であるもの

さらに、追加要件として削除したい対象の id が複数件(大量)にあったとしましょう。上の例では id は 1件でしたが。

それをコードで一気に処理するには、以下のアルゴリズムが例なります。

  1. 削除対象の id をファイルにリストアップさせ、スクリプトに読み込ませる
  2. リストアップされた id 分、削除処理をループさせる
  3. 1つの id から取得したデータに含まれている categoryuser, checkedid のリストに対して、データを削除する(1件1件なのでループ)

言葉で書いてもわかりにくいので、以降はコードを見て頂いた方が早いと思います。

つくったもの

以下の Python スクリプトです。以下、パラメータは適宜修正してください。

TableName は DynamoDB テーブル名
FileName は 読み込みたいファイル名

DynamoDB に対してアクセスできること、Python、boto3 がインストールされていることが実行環境として必要です。必要に応じて DynamoDB のリージョン名は変更します。

import json
import boto3
import decimal
from boto3.dynamodb.conditions import Key

dynamodb = boto3.resource("dynamodb", region_name="ap-northeast-1")
table = dynamodb.Table("TableName")

# JSONファイルを読み込む
with open("FileName") as f:
    data = json.load(f, parse_float = decimal.Decimal)

# JSONデータの各行から指定した id のデータを取得する
for row in data:
    id_value = row.get("id", None)
    if id_value:
        print(f"Searching for items with id: {id_value}")
        output = table.query(IndexName="id-index", KeyConditionExpression=Key("id").eq(id_value))
        # 該当する項目があれば削除する
        for item in output.get("Items", []):
            print(f"Found item: {item}")
            # categoryuser がパーティションキー、checkedid がソートキー
            res = table.delete_item(
                Key = {
                    "categoryuser": item["categoryuser"],
                    "checkedid": item["checkedid"]
                }
            )
            print(f"Deleted item: {item}")

※エラー処理は省略しています。

削除対象とする id は JSON データで以下のフォーマットでファイルとして用意します。実際に使ったときには他のデータも入っていたため JSON にしましたが、id だけなら単純な配列の方が適切だと思います。

[
  {"id":"002"},
  {"id":"005"},
  {"id":"006"}
]

これで、 削除したい id をファイルにリストアップすれば、DynamoDB に対してセカンダリインデックスベースで削除をかけられるようになりました。

まとめ

いかがでしたでしょうか?

本件、急ぎで必要になったため同僚や ChatGPT の助けも借りて即席でつくったものなんです。一応動いていますので、参考になるかと思います。

本記事が皆様のお役に立てれば幸いです。

著者について
広野 祐司

AWS サーバーレスアーキテクチャを駆使して社内クラウド人材育成アプリとコンテンツづくりに勤しんでいます。React で SPA を書き始めたら快適すぎて、他の言語には戻れなくなりました。サーバーレス & React 仲間を増やしたいです。AWSは好きですが、それよりもAWSすげー!って気持ちの方が強いです。
取得資格:AWS 認定は12資格、ITサービスマネージャ、ITIL v3 Expert 等
2020 - 2023 Japan AWS Top Engineer 受賞
2022 - 2023 Japan AWS Ambassador 受賞
2023 当社初代フルスタックエンジニア認定
好きなAWSサービス:AWS Amplify / AWS AppSync / Amazon Cognito / AWS Step Functions / AWS CloudFormation

広野 祐司をフォローする
クラウドに強いによるエンジニアブログです。
SCSKは専門性と豊富な実績を活かしたクラウドサービス USiZE(ユーサイズ)を提供しています。
USiZEサービスサイトでは、お客様のDX推進をワンストップで支援するサービスの詳細や導入事例を紹介しています。
AWSクラウドソリューションデータベース
シェアする
タイトルとURLをコピーしました