【環境構築】Local環境でDynamoDBを動かすまとめ【動作確認】

# 導入
AWS上で利用できる、DynamoDBに触れることになった。クラウドサービスなのにローカルでテストどうやるの？？KVS？CLI？なにそれ？となったので、まとめることにする。

# 目次
1. 辞書整理
2. ローカルでテストする
    - テーブル作成
    - データ登録
    - データ取得
    - データ削除
3. コマンド集

# 本題

## 1. 辞書整理

- DynamoDB
    - NoSQLデータベース
    - データ構造はRDBとは異なり、KVSと呼ばれる構造
    - 高速なデータアクセス、書き込み
    - 結合できない
    - 複合キーは2つまで
    - 各レコードが同一の項目数でなくても良い
    - pythonで言う辞書型の構造でデータの出し入れができる（実際にpythonで実装してみると、辞書型をよく使うことになる）
    - 変化に強く、テーブル設計が楽（不要とは言っていない）
    - 横方向スケールや等、改修時の手間が少ない
- KVS
    - Key-Value Store
    - DyanmoDBなどで採用されているデータの持ち方（対義語：RDB）
    - キーを指定することで、対象のValueの格納や取得を行う
    - Javaで言うMapで、Pythonで言う辞書だと考えれば分かりやすい
    - 外部結合ができない
    - DynamoDBの場合は、キーを2つまで指定可能。言い換えると、主キーが2つまでのテーブル構造にする必要がある。
- aws-cli
    - AWS Command Line Interface
    - コマンドラインからAWSサービスを管理することが可能な統合ツール
- boto3
    - AWS SDK for Python
    - pythonでAWS環境を触るために必要なライブラリ
    - DynamoDBに触れる以外のシーンでも登場する

## 2. ローカルでテストする

### 前提条件

- Linux環境を利用
    - [windows環境でLinuxを導入する参考](https://qiita.com/Shunichirou/items/ce7cc3c69eef690e08a8)
- AWSアカウントを保有の上、アクセスキー、シークレットキーを控えている
    - [IAMユーザーのアクセスキーの管理](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_credentials_access-keys.html)
    - [IAMユーザとは](https://aws.amazon.com/jp/iam/)

### 環境構築

- [Amazon DynamoDB](https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/DynamoDBLocal.html)の中段より、アジアパシフィック (東京) リージョンのファイルをダウンロードし、解答する
- awscliのインストール
    - `$sudo pip install awscli`
- boto3のインストール
    - `$sudo pip intstall boto3`
- aws-cliの設定情報変更
    - $ `$aws configure`
    - 控えておいたAccess Key、Secret Access Keyを用いて、下記の設定情報に書き換え
    - Dafault region nameとDefault output formatは今回は何でも良い
    - [参考：AWS-CLIの初期設定](http://www.task-notes.com/entry/20141026/1414322858)

```
AWS Access Key ID [None]: xxxxxxxxxx
AWS Secret Access Key [None]: xxxxxxxxxx
Default region name [None]: ap-northeast-1
Default output format [None]: json
```
- DynamoDBの起動
    - DynamoDBをダウンロードし、配置した場所に移動する
    - `$java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar -sharedDb http://localhost:8000/shell/`

### テーブル作成

※DynamoDBを起動しているコマンドラインとは別のコマンドラインを立ち上げておく。


- 以下jsonファイルを作成する。

```tableDef.json
{
  "TableName": "NewsTable",
  "AttributeDefinitions": [
    {
      "AttributeName": "id",
      "AttributeType": "N"
    },
    {
      "AttributeName": "category",
      "AttributeType": "S"
    }
  ],
  "KeySchema": [
    {
      "KeyType": "HASH",
      "AttributeName": "id"
    },
    {
      "KeyType": "RANGE",
      "AttributeName": "category"
    }
  ],
  "ProvisionedThroughput": {
    "WriteCapacityUnits": 5,
    "ReadCapacityUnits": 5
  }
}
```

- `$aws dynamodb create-table --endpoint-url http://localhost:8000 --cli-input-json file://xxxxx/tableDef.json`
    - file://まではおまじない。それ以降に、tableDef.jsonを置いた相対パスを記載。
- 下記が返って来れば、正しくTableが作成されていることが確認できる

```
{
    "TableDescription": {
        "AttributeDefinitions": [
            {
                "AttributeName": "id",
                "AttributeType": "N"
            },
            {
                "AttributeName": "category",
                "AttributeType": "S"
            }
        ],
        "TableName": "NewsTable",
        "KeySchema": [
            {
                "AttributeName": "id",
                "KeyType": "HASH"
            },
            {
                "AttributeName": "category",
                "KeyType": "RANGE"
            }
        ],
        "TableStatus": "ACTIVE",
        "CreationDateTime": 1527735035.761,
        "ProvisionedThroughput": {
            "LastIncreaseDateTime": 0.0,
            "LastDecreaseDateTime": 0.0,
            "NumberOfDecreasesToday": 0,
            "ReadCapacityUnits": 5,
            "WriteCapacityUnits": 5
        },
        "TableSizeBytes": 0,
        "ItemCount": 0,
        "TableArn": "arn:aws:dynamodb:ddblocal:000000000000:table/NewsTable"
    }
}
```

### insert及びdelete

- 以下pythonファイルを作成する。

```test.py
import boto3
import json
from boto3.dynamodb.conditions import Key, Attr


def main():
    dynamodb = boto3.resource('dynamodb',endpoint_url='http://localhost:8000')
    table = dynamodb.Table('NewsTable')

    id = 1
    category = "newsPage"
    day = "yyyymmdd"
    time = "mmssxx"
    news = "News Title"
    url = "http://xxxxxxxxxxxxxxxxx"

    news = News(id,category,day,time,news,url)

    table.put_item(Item = news.to_dict())

    ret = table.get_item(Key = {'id': 1,  'category':'finovate'})
    item = ret['Item']
    print("登録成功")
    print("レコード数" + str(table.item_count))
    print(item)

    table.delete_item(Key = {'id': 1,  'category':'finovate'})

    print("削除成功")
    print("レコード数" + str(table.item_count))

class News:

    def __init__(self, id, category, day, time, news, url):
        self.id = id
        self.category = category
        self.day = day
        self.time = time
        self.news = news
        self.url = url

    def to_dict(cls):

        return {
        "id" : cls.id,
        "category" : cls.category,
        "day" : cls.day,
        "time" : cls.time,
        "news" : cls.news,
        "url" : cls.url
        }

if __name__ == '__main__':
    main()
```

- `$python test.py`を実行し、結果が返って来ればOK
- データの登録
    - table.put_item(Item = 辞書型)
- データの取得
    - ret = table.get_item(Key = {key1 : x, key2 : y})
    - ret['Item']
- データの削除
    - table.delete_item(Key = {key1 : x, key2 : y})

※keyが一つのテーブルであれば、取得・削除についてはkey1までの指定で
良い

## 3. コマンド集

aws-cliからのテーブル操作

- テーブル削除
    - `$aws dynamodb delete-table --endpoint-url http://localhost:8000 --table-name NewsTable`
- テーブル一覧
    - `$aws dynamodb list-tables --endpoint-url http://localhost:8000`
- テーブル構造の確認（全件scan）
    - `$aws dynamodb describe-table --endpoint-url http://localhost:8000 --table-name NewsTable`
- テーブルの中身確認例
    - 下記ファイル作成し、`$python scan.py`

```scan.py
import boto3
import json
from boto3.dynamodb.conditions import Key, Attr


def main():
    dynamodb = boto3.resource('dynamodb',endpoint_url='http://localhost:8000')
    table_name = "NewsTable"
    table = dynamodb.Table(table_name)

    for item in scan(table)['Items']:
        print("id : " + item['id'])
        print("category : " + item['category'])
        print("day : " + item['day'])
        print("time : " + item['time'])
        print("url : " + item['url'])
        print("\n")

def scan(table):

    try:
        res = table.scan()
        return res
    except e:
        return e

if __name__ == '__main__':
    main()
```

# おわりに
- 今回AWSのことはほぼ触れず、DynamoDBに特化した内容になりました。AWSに明るくない人はシークレットキー・アクセスキーやIAMユーザの時点で躓くと思うので、分からない場合は質問ください。
- ローカルでDynamoDBを動かしたい人向けの記事なので、デプロイ方法からデプロイ後の動きなどもまとめれたらなと思ってます。