DynamoDB基礎知識とboto3での簡単な使用方法

# 記事について

DynamoDBを使用するために学習した内容まとめ用

# DynamoDB

NoSQLデータベースサービス

# DB構造、用語

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695430/ce661297-99f4-6dfd-d29a-ce4903947653.png)

- table
    RDSでのtable

- item
    RDSでのrecord

- attribute
    RDSでのcolumn
    item間でattributeは一致しなくてよい
    以下Partition Key、Sort Keyとなるattribute以外はtable生成時に設定不要

- Primary Key
    DynamoDBでItemを一意に決定するためのキー
    主キーとも言うらしい

- Partition Key
    table生成時に設定（必須）
    単体でPrimary Keyとして使用可能

- Sort Key
    table生成時に設定
    Partition Key + Sort KeyでPrimary Keyとして使用可能
    Partition Keyの値が同じでSort Keyの値が異なる場合に、Partition Key + Sort KeyでItemを一意に決定

# Primary Key

table生成時にPrimary Keyを以下2パターンから選択

1. Partition Key
2. Partition Key + Sort Key

1の場合、Partition Keyが同じデータは登録不可
2の場合、Partition Key、Sort Keyが共に同じデータは登録不可

# Index

Key（Partition Key、Sort Key）として設定した以外のattributeで絞込検索（クエリ）を行うために、
Partition Key、Sort Keyを別のattributeで設定したもの
ローカルセカンダリインデックス(LSI)と、グローバルセカンダリインデックス(GSI)の2種類あり


- ローカルセカンダリインデックス(LSI)

    以下のようにSort Keyのみ別のattributeに変更したもの
![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695430/d7ab3a47-f3d9-0223-7bce-56f7592ba16a.png)
※Primary KeyをPartition Key + Sort Keyとしている場合のみ作成可能
※LSIを設定する場合、table作成時に設定する必要あり、設定方法は後述


- グローバルセカンダリインデックス(GSI)

    以下のようにPartition Key、Sort Keyを別のattributeに変更したもの
![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695430/98ba7e33-5429-6d2d-edd7-28f5db2f0642.png)


# LSI設定方法

以下のようにテーブル設定欄のデフォルト設定の使用のチェックを外して、インデックスの追加をクリック
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695430/aeddb395-65f6-3b6b-0f97-13f8e303d71a.png)
LSIとして設定する場合は、パーティションキーは、tableのパーティションキーを指定
ソートキーに絞込検索に使用するattributeを設定、ローカルセカンダリインデックスとしての作成にチェック
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695430/cc92f2b6-01d3-1799-0e0f-10445b5ebecf.png)

# GSI設定方法

以下のようにインデックスタブからインデックスの作成をクリック
![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695430/2ebbc089-05e0-b693-2854-637760eb2616.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695430/44395a14-bca8-7a63-f9a1-0b1724c403a0.png)

# データ取得パターン

- getitem
    Primary Keyを使用した検索
    取得できるItemが一意に決定される場合のみ

- query
    Primary Key = Partition Key + Sort Key設定時にPartition Keyでの絞込検索
    LSI、GSIでの絞込検索

- scan(全レコード取得)

## Key設定と、データ取得可/不可

|  Key設定  |  scan  |  get item  |  query  |
| :---: | :---: | :---: | :---: |
|  Primary Key (Partition Key)  |  〇  |  〇  |  ×  |
|  Primary Key (Partition Key + Sort Key)  |  〇  |  〇  |  〇  |
|  LSI  |  -  |  -  |  〇  |
|  GSI  |  -  |  -  |  〇  |

# boto3を使用したデータ取得例
## テーブル定義

【テーブル】

- Primary Key  
    Partition Key（SerialNumber） + Sort Key（BuildingId）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695430/0a036661-5c97-2a94-1eb1-700386be13de.png)

【GSI】

- Partition Key（Maker）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695430/14681703-c53d-ffd1-2541-5478eace7597.png)

# データ取得コード

```python
import boto3
import json
from boto3.dynamodb.conditions import Key, Attr

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

# Primary Key（SerialNumber + BuildingId）を使用した検索
def get_item(SerialNumber, BuildingId):
  response = table.get_item(
    Key={
      'SerialNumber': SerialNumber,
      'BuildingId': BuildingId
    }
  )
  return response['Item']

# Partition Key（SerialNumber）での絞込検索
def query_SerialNumber(SerialNumber):
  response = table.query(
    KeyConditionExpression=Key('SerialNumber').eq(SerialNumber)
  )
  return response['Items']

# Partition Key + Sort Key（SerialNumber + BuildingId）での絞込検索
def query_SerialNumber_BuildingId(SerialNumber, BuildingId):
  response = table.query(
    KeyConditionExpression=
      Key('SerialNumber').eq(SerialNumber) & Key('BuildingId').eq(BuildingId)
  )
  return response['Items']

# GSI（Maker-index）のPartition Key（Maker）での絞込検索
def query_Maker_index(Maker):
  response = table.query(
    IndexName='Maker-index',
    KeyConditionExpression=Key('Maker').eq(Maker)
  )
  return response['Items']


response = get_item('abdc-000002', 'zyxw-00987')
# response = {'BuildingId': 'zyxw-00987', 'DeviceType': '2', 'Maker': 'CCC', 'ProductionDate': '2019/10/2', 'SerialNumber': 'abdc-000002'}

response = query_SerialNumber('abcd-000001')
# response[0] = {'BuildingId': 'zyxw-12345', 'DeviceType': '1', 'LastMaintenanceDate': '2019/10/5', 'Maker': 'AAA', 'ProductionDate': '2015/10/1', 'SerialNumber': 'abcd-000001'}
# response[1] = {'BuildingId': 'zyxw-12399', 'DeviceType': '5', 'Maker': 'BBB', 'ProductionDate': '2018/1/5', 'SerialNumber': 'abcd-000001'}

response = query_SerialNumber_BuildingId('abcd-000001', 'zyxw-12399')
# response[0] = [{'BuildingId': 'zyxw-12399', 'DeviceType': '5', 'Maker': 'BBB', 'ProductionDate': '2018/1/5', 'SerialNumber': 'abcd-000001'}]

response = query_Maker_index('AAA')
# response[0] = {'BuildingId': 'zyxw-12378', 'DeviceType': '3', 'LastMaintenanceDate': '2017/5/7', 'Maker': 'AAA', 'ProductionDate': '2012/4/5', 'SerialNumber': 'abcd-000003'}
# response[1] = {'BuildingId': 'zyxw-12345', 'DeviceType': '1', 'LastMaintenanceDate': '2019/10/5', 'Maker': 'AAA', 'ProductionDate': '2015/10/1', 'SerialNumber': 'abcd-000001'}
```

# データ追加

```python
import boto3
import json
from boto3.dynamodb.conditions import Key, Attr

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

def put_item():
  table.put_item(
      Item={
          'SerialNumber': 'abcd-000001',
          'BuildingId': 'zyxw-12399',
          'DeviceType': '5'
    }
  )
```

# 参考

https://qiita.com/UpAllNight/items/a15367ca883ad4588c05
https://d1.awsstatic.com/webinars/jp/pdf/services/20181225_AWS-BlackBelt_DynamoDB.pdf