DynamoDB Streamsを使用した

## はじめに
DynamoDB Streamsを初めて使用したので、設定方法をまとめておきます。

## 使用した理由
DynamoDBのテーブルに保存されたデータを使用した構築済みの仕組みがあり、ここに同じデータから別の仕組みを一時的に追加する必要がありました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/d4f1944a-38f6-ba17-2a67-e473e6de7c09.png)

## 追加した構成
DynamoDBのテーブルにDynamoDB Streamsを設定し、テーブルに追加されるデータを別処理にストリーミングさせる構成としました。既存の構成では、テーブルの設定を変更するだけで、何も影響がありません。パフォーマンスへの影響もありません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/99cdf024-e55d-2764-90e5-a0752de3f5e7.png)

## 実際の設定
実際に設定をしてみます。
### DynamoDB側
コンソール > DynamoDB > テーブル > 設定対象のテーブル とすすみます。
「エクスポートおよびストリーム」タブの「DynamoDB ストリームの詳細」で機能をオンにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/1f845922-5910-3cff-1d45-3c223dbe5b59.png)

ストリームしたいのは追加データだけなので、新しいイメージとしました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/18d8f505-96e1-e8b1-2e53-76eea2c76848.png)


続いてLambdaトリガーを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/691a7c6b-6d8e-1700-fb01-44c53a66b2ab.png)

### Lambdaの設定
Lambdaを設定していきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/757e771c-2dbe-c222-95a4-e5fc7ad211e7.png)

Lambdaトリガーが起動されると、ストリームされたDynamoDBデータレコードをログに出力するようにコードを作成します。
``` python:lambda_function.py
import json
import logging
import boto3

# ロガーの設定
logger = logging.getLogger()
logger.setLevel(logging.INFO)

# CloudWatch Logs クライアントの初期化
cloudwatch_logs = boto3.client('logs')

def lambda_handler(event, context):
    # TODO implement
    
    logger.info(json.dumps(event))
    
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }
```

Lambda関数がDynamoDBのデータレコードを読み込むための権限設定を行います。
※画面はFUllAccessにしていますが、実際に行う場合は、必要最小限の権限にしてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/bd267fa1-218e-37ba-469b-4cf8e8b8d9fb.png)

### フィルターの設定
ここまでで、DynamoDBのテーブルに追加、変更、削除されたデータはすべて連携されるようになります。
今回必要なのは追加されたデータで、かつ、特定のフィールドの値を条件としたいので、フィルターを設定します。

Lambdaのトリガーを編集していきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/b983b14b-3141-b57b-f48d-593f99a1acad.png)

追加の設定を展開します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/5cd1e29a-66d8-80cd-5f98-71e0a1ff30be.png)

フィルタリング条件を設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/d5a5c98e-8772-c82e-1501-395de6410ab3.png)

```JSON
{
  "eventName": ["INSERT"],
  "dynamodb": {
    "NewImage": {
      "field_moji": { "S": ["test1"] },
      "field_moji2": { "S": [{"prefix": "{\"c1\": \"test2\","}] }
    }
  }
}
```
条件の説明：
以下の3つの条件が一致したレコードだけが、Lambda関数にストリームされます。
* レコードの種類が「追加」
* フィールド「field_moji」の値が、「test1」
* フィールド「field_moji2」の値が、「{"c1\": "test2",」で始まる。(上記の条件内にはエスケープ文字が含まれます)

## 結果
次の２つのレコードを追加しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/cba15000-8715-afe6-afa5-74de92ff8f8e.png)

ログに出力されたのは3つの条件を満たす Key1の値が「123457」のレコードだけとなりましたので、狙ったフィルターができていることがわかりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/3f276681-4009-9cb3-43db-3be9183e87de.png)

## おまけ
AWSの公式では、フィルタリング条件に「で終わる」と「ワイルドカード」の記載がありません。

https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Streams.Lambda.Tutorial2.html

EventBridgeでは、「で終わる」に相当するサフィックスマッチングや「ワイルドカード」が使用できるようなので、DynamoDB Streamsで使用できるか試してみました。

https://docs.aws.amazon.com/ja_jp/eventbridge/latest/userguide/eb-event-patterns-content-based-filtering.html

### 「で終わる」
設定可能でした。フィルタリングも動作しました。

```JSON
{
  "eventName": ["INSERT"],
  "dynamodb": {
    "NewImage": {
      "field_moji": { "S": ["test1"] },
      "field_moji2": { "S": [{"suffix": "end111"}] }
    }
  }
}
```

### 「ワイルドカード」
設定不可でした。何パターンか試しましたがダメでした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/77c533e5-16cf-c155-2a76-9e6580ea68f3.png)


```JSON
{
  "eventName": ["INSERT"],
  "dynamodb": {
    "NewImage": {
      "field_moji": { "S": ["test1"] },
      "field_moji2": { "S": [{"wildcard": "test*"}] }
    }
  }
}
```

特定の文字が含まれる　を実現したい場合は、Lambda関数のコード内で処理する必要があります。