[AWS][boto3] DynamoDBテーブルのデータを全削除する。


# やりたいこと
DynamoDBのテーブルのデータを全削除したい。
けど DynamoDB にはデータを全削除するようなコマンドがない。

テーブルごと再作成する方法もあるけどテーブル設定を完全復元するような仕組みが必要になるし、DynamoDB Stream は ARN が変わってしまうため完全復元が不可能。
今後あるかもしれない機能拡張に追従できるか？という懸念もある。

というわけでデータを全削除するコードをPythonで作りました。
Rangeキー有り/無し両方に対応しています。


# サンプルコード
```python
# coding: utf-8
import sys
import os
import boto3

REGION_NAME = "ap-northeast-1"
TABLE_NAME  = "sample-table"

def main():
    dynamodb = boto3.resource('dynamodb', region_name=REGION_NAME)
    table    = dynamodb.Table(TABLE_NAME)
    truncate_dynamo_items(table)

    return 0

def truncate_dynamo_items(dynamodb_table):

    # データ全件取得
    delete_items = []
    parameters   = {}
    while True:
        response = dynamodb_table.scan(**parameters)
        delete_items.extend(response["Items"])
        if ( "LastEvaluatedKey" in response ):
            parameters["ExclusiveStartKey"] = response["LastEvaluatedKey"]
        else:
            break

    # キー抽出
    key_names = [ x["AttributeName"] for x in dynamodb_table.key_schema ]
    delete_keys = [ { k:v for k,v in x.items() if k in key_names } for x in delete_items ]

    # データ削除
    with dynamodb_table.batch_writer() as batch:
        for key in delete_keys:
            batch.delete_item(Key = key)

    return 0


if __name__ == '__main__':
    ret = main()
    sys.exit(ret)


```

# 使い方
上記サンプルコードから `def truncate_dynamo_items(dynamodb_table):` のファンクションをコピペして、対象テーブルの `Tableリソース` を渡せばOK。

# 解説

## キー抽出
データをまとめて削除するコマンドがないので、`delete_item`で１件ずつ削除します。
そのためには「各データのキー情報のみ」が必要です。キー情報以外のデータが入っているとエラーになります。

例として
↓↓ これを

```json:dynamodb-item
{
    "HashKeyName" : "hash-value-1",
    "RangeKeyName" : "range-value-2",
    "OtherAttr-1" : "ABC",
    "OtherAttr-2" : "12345",
}
```

↓↓ こうする必要があります。

```json:dynamodb-item-key-only
{
    "HashKeyName" : "hash-value-1",
    "RangeKeyName" : "range-value-2"
}
```

※ Rangeキーは無い場合もあります。

そのために、まずはテーブルから `key_schema` を取ってきて、キー情報の一覧を作成します。

↓↓ `Table.key_schema` の内容

```json:key-schema
[
  {
    "KeyType": "HASH",
    "AttributeName": "HashKeyName"
  },
  {
    "KeyType": "RANGE",
    "AttributeName": "RangeKeyName"
  }
]
```
Rangeキーの設定がないテーブルは、`"KeyType": "HASH"` の dictが１つだけ格納されているリストになります。
HASH か RANGE かはどうでもよくて AttributeName だけあればよいので、下記コードで余計な情報は捨てます。

↓↓ これを通すと

```python
key_names = [ x["AttributeName"] for x in dynamodb_table.key_schema ]
```
↓↓ こうなる。

```json:key-schema_only_attribute-name
[
  "HashKeyName",
  "RangeKeyName"
]
```

Scanで取得したItem全件と、これで得られたキー名称リストを突き合わせ、必要なカラムだけ抽出します。

↓↓ これが

```json:dynamodb-items
[
    {
        "HashKeyName" : "hash-value-1",
        "RangeKeyName" : "range-value-2",
        "OtherAttr-1" : "ABC",
        "OtherAttr-2" : "12345",
    },
    ....
]
```

↓↓ これを通すと

```python
delete_keys = [ { k:v for k,v in x.items() if k in key_names } for x in delete_items ]
```

↓↓ こうなる。

```json:dynamodb-items-key-only
[
    {
        "HashKeyName" : "hash-value-1",
        "RangeKeyName" : "range-value-2"
    },
    ....
]
```

これを `delete_item` の `Key` に渡せば削除できます。

ここで使用した
`[ x["AttributeName"] for x in dynamodb_table.key_schema ]`
`[ { k:v for k,v in x.items() if k in key_names } for x in delete_items ]`
のような書き方は `リスト内包表記` `辞書内包表記` というものです。
わからない＆理解したい方はぐぐってください。


## データ削除
キー抽出の項で「１件ずつ削除」と言いましたが、実際は何件かずつまとめて実行したほうが高パフォーマンスです。
ですが一度のリクエストで「何件まで|何KBまで」と制限があるため、全件まとめて実行はできません。
件数やサイズをチェックしながら自力で分割実行するのはちょっと面倒です。
`batch_writer()` を使用すれば、特に意識しなくてもそこは上手いこと処理してくれます。