[JAWS-UG CLI] DynamoDB:#5 テーブル(ハッシュキー + レンジキー)作成、アイテム登録、スキャン、クエリ

https://jawsug-cli.doorkeeper.jp/events/20523 でのハンズオン資料です。

AWS CLIを利用して、DynamoDBのテーブル(ハッシュキー + レンジキー)を作成し、アイテムを登録してみます。

ここでは、DynamoDB開発者ガイドのサンプルテーブル「Thread」をCLIで作成してみます。
http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/GettingStartedCreateTables.html


前提条件
========

## DynamoDBへの権限

* DynamoDBに対してフル権限があること。

## AWS CLIのバージョン

以下のバージョンで動作確認済

* AWS CLI 1.7.18


```bash:コマンド:
aws --version
```

```text:結果(例):
aws-cli/1.7.18 Python/2.7.5 Darwin/13.4.0
```
0. 準備
=======


0.1. リージョンの決定
---------------------

作成するユーザのデフォルトリージョンを決めます。
(カレントユーザが利用するカレントリージョンも切り変わります。)

```bash:コマンド(東京リージョンの場合):
export AWS_DEFAULT_REGION='ap-northeast-1'
```


0.2. 変数の確認
---------------

プロファイルとリージョンが想定のものになっていることを確認します。

```bash:変数の確認:
aws configure list
```

```text:結果(例):
            Name                    Value             Type    Location
            ----                    -----             ----    --------
         profile      iam_full-prjZ-mbp13              env    AWS_DEFAULT_PROFILE
      access_key     ****************LOAQ shared-credentials-file
      secret_key     ****************I1O1 shared-credentials-file
          region           ap-northeast-1              env    AWS_DEFAULT_REGION
```


0. テーブルの設計
==============


テーブル名:   Thread (ForumName, Subject, ...)

プライマリキーのタイプ:   ハッシュ + レンジ

ハッシュ属性の名前と型
  * 属性名: ForumName
  *  (型: String)

範囲属性の名前と型
  * 属性名: Subject
  *  (型: String)

プロビジョニングされたスループット:
   *   読み込みキャパシティーユニット: 10
   *   書き込みキャパシティーユニット: 5


1. 事前準備
===========


1.1. DynamoDBテーブル名の決定
-----------------------------

```bash:コマンド(例):
DYNAMO_TABLE_NAME='Thread'
```

同じ名前のテーブルが無いことを確認します。

```bash:コマンド:
aws dynamodb describe-table --table-name ${DYNAMO_TABLE_NAME}
```

```text:結果:
      A client error (ResourceNotFoundException) occurred when calling the DescribeTable operation: Requested resource not found: Table: ProductCatalog not found
```


1.2. スキーマの定義
-------------------

### ハッシュキーの指定

```bash:コマンド(例):
DYNAMO_HASH_NAME='ForumName'
DYNAMO_HASH_TYPE='S'
```

### レンジキーの指定

```bash:コマンド(例):
DYNAMO_RANGE_NAME='Subject'
DYNAMO_RANGE_TYPE='S'
```

### CLIパラメータの作成

```bash:コマンド:
DYNAMO_ATTRIBUTE="AttributeName=${DYNAMO_HASH_NAME},AttributeType=${DYNAMO_HASH_TYPE} AttributeName=${DYNAMO_RANGE_NAME},AttributeType=${DYNAMO_RANGE_TYPE}"
DYNAMO_KEY_SCHEMA="AttributeName=${DYNAMO_HASH_NAME},KeyType=HASH AttributeName=${DYNAMO_RANGE_NAME},KeyType=RANGE"
```


1.3. プロビジョンドスループットの指定
-------------------------------------

```bash:コマンド(例):
DYNAMO_READ_UNIT='10'
DYNAMO_WRITE_UNIT='5'

DYNAMO_PROV_THPUT="ReadCapacityUnits=${DYNAMO_READ_UNIT},WriteCapacityUnits=${DYNAMO_WRITE_UNIT}"
```


2. テーブルの作成
=================

## 2.1. テーブルの作成

```bash:変数の確認:
cat << ETX

        DYNAMO_TABLE_NAME: ${DYNAMO_TABLE_NAME}
        DYNAMO_ATTRIBUTE:  ${DYNAMO_ATTRIBUTE}
        DYNAMO_KEY_SCHEMA: ${DYNAMO_KEY_SCHEMA}
        DYNAMO_PROV_THPUT: ${DYNAMO_PROV_THPUT}

ETX
```

```bash:コマンド:
aws dynamodb create-table \
        --table-name ${DYNAMO_TABLE_NAME} \
        --attribute-definitions ${DYNAMO_ATTRIBUTE} \
        --key-schema ${DYNAMO_KEY_SCHEMA} \
        --provisioned-throughput ${DYNAMO_PROV_THPUT}
```

```json:結果:
      {
        "TableDescription": {
          "AttributeDefinitions": [
              {
                  "AttributeName": "ForumName",
                  "AttributeType": "S"
              },
              {
                  "AttributeName": "Subject",
                  "AttributeType": "S"
              }
          ],
          "ProvisionedThroughput": {
              "NumberOfDecreasesToday": 0,
              "WriteCapacityUnits": 5,
              "ReadCapacityUnits": 10
          },
          "TableSizeBytes": 0,
          "TableName": "Thread",
          "TableStatus": "CREATING",
          "KeySchema": [
              {
                  "KeyType": "HASH",
                  "AttributeName": "ForumName"
              },
              {
                  "KeyType": "RANGE",
                  "AttributeName": "Subject"
              }
          ],
          "ItemCount": 0,
          "CreationDateTime": 1427678617.154
        }
      }
```


## 2.2. 確認(一覧)

```bash:コマンド:
aws dynamodb list-tables 
```

```json:結果(例):
      {
        "TableNames": [
          "Thread"
        ]
      }
```


## 2.3. 確認(詳細)

```bash:コマンド:
aws dynamodb describe-table --table-name ${DYNAMO_TABLE_NAME}
```

```json:結果:
      {
        "Table": {
          "AttributeDefinitions": [
              {
                  "AttributeName": "ForumName",
                  "AttributeType": "S"
              },
              {
                  "AttributeName": "Subject",
                  "AttributeType": "S"
              }
          ],
          "ProvisionedThroughput": {
              "NumberOfDecreasesToday": 0,
              "WriteCapacityUnits": 5,
              "ReadCapacityUnits": 10
          },
          "TableSizeBytes": 0,
          "TableName": "Thread",
          "TableStatus": "ACTIVE",
          "KeySchema": [
              {
                  "KeyType": "HASH",
                  "AttributeName": "ForumName"
              },
              {
                  "KeyType": "RANGE",
                  "AttributeName": "Subject"
              }
          ],
          "ItemCount": 0,
          "CreationDateTime": 1427678617.154
        }
      }
```











3. アイテムの作成 (put-item)
============================


3.1. Thread 1
--------

sample forum1:

ForumName:
   "Amazon DynamoDB"

Subject:
   "DynamoDB Thread 1"

Message:
   "DynamoDB thread 1 message text"

LastPostedBy:
   "User A"

LastPostedDateTime:
   DateTime.UtcNow.Subtract(new TimeSpan(14, 0, 0, 0))

Views:
   0

Replies:
   0

Answered:
   false

Tags:
   { "index", "primarykey", "table" }

```bash:コマンド:
DYNAMO_ITEM_STRING='{"ForumName":{"S":"Amazon DynamoDB"},"Subject":{"S":"DynamoDB Thread 1"},"Message":{"S":"DynamoDB thread 1 message text"},"LastPostedBy":{"S":"User A"},"LastPostedDateTime":{"S":"2015-03-20 00:00:01"},"Views":{"N":"0"},"Replies":{"N":"0"},"Answered":{"BOOL":false},"Tags":{"SS":["index","primarykey","table"]}}'
```

```bash:コマンド:
echo ${DYNAMO_ITEM_STRING} | json_verify
```

```text:結果:
      JSON is valid
```

```bash:変数の確認:
cat << ETX

        DYNAMO_TABLE_NAME:  ${DYNAMO_TABLE_NAME}
        DYNAMO_ITEM_STRING: ${DYNAMO_ITEM_STRING}

ETX
```

```bash:コマンド:
 aws dynamodb put-item \
        --table-name ${DYNAMO_TABLE_NAME} \
        --item "${DYNAMO_ITEM_STRING}"
```

```text:結果:
      (戻り値なし)
```


3.2. Thread 2
--------

sample forum2:

ForumName:
   "Amazon DynamoDB"

Subject:
   "DynamoDB Thread 2"

Message:
   "DynamoDB thread 2 message text"

LastPostedBy:
   "User A"

LastPostedDateTime:
   DateTime.UtcNow.Subtract(new TimeSpan(21, 0, 0, 0))

Views:
   0

Replies:
   0

Answered:
   false

Tags:
   { "index", "primarykey", "rangekey" }

```bash:コマンド:
DYNAMO_ITEM_STRING='{"ForumName":{"S":"Amazon DynamoDB"},"Subject":{"S":"DynamoDB Thread 2"},"Message":{"S":"DynamoDB thread 2 message text"},"LastPostedBy":{"S":"User A"},"LastPostedDateTime":{"S":"2015-03-20 00:01:01"},"Views":{"N":"0"},"Replies":{"N":"0"},"Answered":{"BOOL":false},"Tags":{"SS":["index","primarykey","rangekey"]}}'
```

```bash:コマンド:
echo ${DYNAMO_ITEM_STRING} | json_verify
```

```text:結果:
      JSON is valid
```

```bash:変数の確認:
cat << ETX

        DYNAMO_TABLE_NAME:  ${DYNAMO_TABLE_NAME}
        DYNAMO_ITEM_STRING: ${DYNAMO_ITEM_STRING}

ETX
```

```bash:コマンド:
aws dynamodb put-item \
        --table-name ${DYNAMO_TABLE_NAME} \
        --item "${DYNAMO_ITEM_STRING}"
```

```text:結果:
      (戻り値なし)
```


3.3. Thread 3
--------

sample forum3:

ForumName:
   "Amazon S3"

Subject:
   "S3 Thread 1"

Message:
   "S3 thread 3 message text"

LastPostedBy:
   "User A"

LastPostedDateTime:
   DateTime.UtcNow.Subtract(new TimeSpan(7, 0, 0, 0))

Views:
   0

Replies:
   0

Answered:
   false

Tags:
   { "largeobjects", "multipart upload" }

```bash:コマンド:
DYNAMO_ITEM_STRING='{"ForumName":{"S":"Amazon S3"},"Subject":{"S":"S3 Thread 1"},"Message":{"S":"S3 thread 3 message text"},"LastPostedBy":{"S":"User A"},"LastPostedDateTime":{"S":"2015-03-20 00:02:02"},"Views":{"N":"0"},"Replies":{"N":"0"},"Answered":{"BOOL":false},"Tags":{"SS":["largeobjects","multipart upload"]}}'
```

```bash:コマンド:
echo ${DYNAMO_ITEM_STRING} | json_verify
```

```text:結果:
      JSON is valid
```

```bash:変数の確認:
cat << ETX

        DYNAMO_TABLE_NAME:  ${DYNAMO_TABLE_NAME}
        DYNAMO_ITEM_STRING: ${DYNAMO_ITEM_STRING}

ETX
```

```bash:コマンド:
aws dynamodb put-item \
        --table-name ${DYNAMO_TABLE_NAME} \
        --item "${DYNAMO_ITEM_STRING}"
```

```text:結果:
      (戻り値なし)
```


4. スキャン
=======


4.1. アイテム数確認
-------------------

```bash:コマンド:
aws dynamodb scan --table-name ${DYNAMO_TABLE_NAME} --query 'Count'
```

```text:結果:
      3
```


4.2. 全件表示
-------------

```bash:コマンド:
aws dynamodb scan --table-name ${DYNAMO_TABLE_NAME}
```

```json:結果:
      {
        "Count": 3,
        "Items": [
          {
              "Tags": {
                  "SS": [
                      "largeobjects",
                      "multipart upload"
                  ]
              },
              "LastPostedDateTime": {
                  "S": "2015-03-20 00:02:02"
              },
              "Views": {
                  "N": "0"
              },
              "LastPostedBy": {
                  "S": "User A"
              },
              "Answered": {
                  "BOOL": false
              },
              "Replies": {
                  "N": "0"
              },
              "Message": {
                  "S": "S3 thread 3 message text"
              },
              "Subject": {
                  "S": "S3 Thread 1"
              },
              "ForumName": {
                  "S": "Amazon S3"
              }
          },
          {
              "Tags": {
                  "SS": [
                      "index",
                      "primarykey",
                      "table"
                  ]
              },
              "LastPostedDateTime": {
                  "S": "2015-03-20 00:00:01"
              },
              "Views": {
                  "N": "0"
              },
              "LastPostedBy": {
                  "S": "User A"
              },
              "Answered": {
                  "BOOL": false
              },
              "Replies": {
                  "N": "0"
              },
              "Message": {
                  "S": "DynamoDB thread 1 message text"
              },
              "Subject": {
                  "S": "DynamoDB Thread 1"
              },
              "ForumName": {
                  "S": "Amazon DynamoDB"
              }
          },
          {
              "Tags": {
                  "SS": [
                      "index",
                      "primarykey",
                      "rangekey"
                  ]
              },
              "LastPostedDateTime": {
                  "S": "2015-03-20 00:01:01"
              },
              "Views": {
                  "N": "0"
              },
              "LastPostedBy": {
                  "S": "User A"
              },
              "Answered": {
                  "BOOL": false
              },
              "Replies": {
                  "N": "0"
              },
              "Message": {
                  "S": "DynamoDB thread 2 message text"
              },
              "Subject": {
                  "S": "DynamoDB Thread 2"
              },
              "ForumName": {
                  "S": "Amazon DynamoDB"
              }
          }
        ],
        "ScannedCount": 3,
        "ConsumedCapacity": null
      }
```




5. クエリ
===============

アイテムを以下の条件でクエリしてみます。

* ハッシュキー"ForumName"の値: "Amazon DynamoDB"と同じ
* レンジキー"Subject"の値: "DynamoDB Thread"で始まる



5.1. クエリデータの作成
-----------------------


```bash:コマンド:
pk=ForumName
pt=S
pv="Amazon DynamoDB"
po="EQ"

rk=Subject
rt=S
rv="DynamoDB Thread"
ro="BEGINS_WITH"
```

```bash:コマンド:
STR_QUERY="{\"${pk}\":{\"AttributeValueList\":[{\"${pt}\":\"${pv}\"}],\"ComparisonOperator\":\"${po}\"},\"${rk}\":{\"AttributeValueList\":[{\"${rt}\":\"${rv}\"}],\"ComparisonOperator\":\"${ro}\"}}"

echo ${STR_QUERY} | json_verify
```


5.2. クエリの実行
-----------------

クエリを実行します。

```bash:コマンド:
aws dynamodb query \
        --table-name ${DYNAMO_TABLE_NAME} \
        --key-conditions "${STR_QUERY}"
```

```json:結果(例):
      {
        "Count": 2,
        "Items": [
          {
              "Tags": {
                  "SS": [
                      "index",
                      "primarykey",
                      "table"
                  ]
              },
              "LastPostedDateTime": {
                  "S": "2015-03-20 00:00:01"
              },
              "Views": {
                  "N": "0"
              },
              "LastPostedBy": {
                  "S": "User A"
              },
              "Answered": {
                  "BOOL": false
              },
              "Replies": {
                  "N": "0"
              },
              "Message": {
                  "S": "DynamoDB thread 1 message text"
              },
              "Subject": {
                  "S": "DynamoDB Thread 1"
              },
              "ForumName": {
                  "S": "Amazon DynamoDB"
              }
          },
          {
              "Tags": {
                  "SS": [
                      "index",
                      "primarykey",
                      "rangekey"
                  ]
              },
              "LastPostedDateTime": {
                  "S": "2015-03-20 00:01:01"
              },
              "Views": {
                  "N": "0"
              },
              "LastPostedBy": {
                  "S": "User A"
              },
              "Answered": {
                  "BOOL": false
              },
              "Replies": {
                  "N": "0"
              },
              "Message": {
                  "S": "DynamoDB thread 2 message text"
              },
              "Subject": {
                  "S": "DynamoDB Thread 2"
              },
              "ForumName": {
                  "S": "Amazon DynamoDB"
              }
          }
        ],
        "ScannedCount": 2,
        "ConsumedCapacity": null
      }
```

2件表示されればOKです。