LoginSignup
2
0

More than 3 years have passed since last update.

DynamoDBはどんなCRUD操作が可能か確認する

Posted at

はじめに

以前の記事で書いたように、DynamoDBはちゃんとキャパシティ管理さえしてあげれば、サーバレスで高性能でかなり良い感じなデータベースだ。

ただし、データベースと言ってもRDBとは違うので、そちらに慣れている人にはハマりどころが多々ある。

DynamoDBではどんなCRUD操作が可能かを確認しておこう。

前提条件

前提条件はあまりないが、以下の記事くらいを読んでおくと入りが良いとおもう。

特に、LSI、GSIはちゃんと理解しておかないと混乱することになるのでしっかり覚えておこう。

事前準備

今回は、以下のような仕様のデータベースを管理する前提とする。
- 社員ID(項目名:id)をキーとする社員管理テーブルを想定
- 社員は複数の部署(項目名:department)に所属可能である
- 社員の年齢をデータベースに持っておく

上記から、ハッシュキーはidとする。
と同時に、2つ目の要件を満たすために、departmentをレンジキーに指定する。
さらに、どんな検索要件に対応できるか確認するために、GSIとLSIにdepartmentを設定したテーブルをそれぞれ用意する。
※これ、書いてから気付いたが、別にテーブル分けなくてもそれぞれのインデックスを設定すれば1つのテーブルでも実験できたな……。

上記のテーブルを用意するTerraformは以下のような感じで準備。

################################################################################
# DynamoDB Table                                                               #
################################################################################
resource "aws_dynamodb_table" "test" {
  name           = "test-table"
  billing_mode   = "PROVISIONED"
  read_capacity  = 1
  write_capacity = 1
  hash_key       = "id"
  range_key      = "department"

  attribute {
    name = "id"
    type = "S"
  }

  attribute {
    name = "department"
    type = "S"
  }

  local_secondary_index {
    name               = "department_lsi"
    range_key          = "department"
    projection_type    = "ALL"
  }

  global_secondary_index {
    name               = "department_gsi"
    hash_key           = "department"
    write_capacity     = 1
    read_capacity      = 1
    projection_type    = "ALL"
  }
}

さらに、スクリプトで以下のような感じでベースとなるアイテムをロードしておく。

def prepare_001():
    try:
        with table.batch_writer() as batch:
            batch.put_item(Item={ 'id': '00001', 'department': '11111', 'age': 35 })
            batch.put_item(Item={ 'id': '00001', 'department': '22222', 'age': 35 })
            batch.put_item(Item={ 'id': '00002', 'department': '11111', 'age': 28 })
            batch.put_item(Item={ 'id': '00003', 'department': '11111', 'age': 34 })
            batch.put_item(Item={ 'id': '00004', 'department': '22222', 'age': 39 })
        return "OK"
    except Exception as error:
        return error

実験開始

重複したキーの項目をPUTする

def test_001_01():
    try:
        table.put_item(Item={ 'id': '00001', 'department': '11111', 'age': 36 })
        return "OK"
    except Exception as error:
        return error

結果:重複したキーの項目が更新された。

単一項目の非キー項目のアップデート

def test_001_02():
    try:
        option = {
            'Key': {'id': '00001', 'department': '11111'},
            'UpdateExpression': 'set #age = :age',
            'ExpressionAttributeNames': {
                '#age': 'age'
            },
            'ExpressionAttributeValues': {
                ':age': 37
            }
        }
        table.update_item(**option)
        return "OK"
    except Exception as error:
        return error

結果:問題なく実行可能。

単一項目のキー項目のアップデート

def test_001_03():
    try:
        option = {
            'Key': {'id': '00001', 'department': '11111'},
            'UpdateExpression': 'set #department = :department, #age = :age',
            'ExpressionAttributeNames': {
                '#department': 'department',
                '#age': 'age'
            },
            'ExpressionAttributeValues': {
                ':department': "33333",
                ':age': 37
            }
        }
        table.update_item(**option)
        return "OK"
    except Exception as error:
        return error

結果:エラーになる。キー項目の更新は deleteput するしかないようだ(詳細は後述)。

'An error occurred (ValidationException) when calling the UpdateItem operation: One or more parameter values were invalid: Cannot update attribute department. This attribute is part of the key'

ハッシュキーのみでqueryする

def test_001_04():
    try:
        response = table.query(
            KeyConditionExpression=Key('id').eq('00001')
        )
        return response['Items']
    except Exception as error:
        return error

結果同一ハッシュキーのアイテムが複数返却される

## ハッシュキー+レンジキーでqueryする

def test_001_05():
    try:
        response = table.query(
            KeyConditionExpression=Key('id').eq('00001') & Key('department').eq('22222')
        )
        return response['Items']
    except Exception as error:
        return error

結果:単一アイテムに絞り込まれて返却される

ハッシュキー+レンジキー以外の項目でqueryする

def test_001_06():
    try:
        response = table.query(
            KeyConditionExpression=Key('id').eq('00001'),
            FilterExpression=Attr('age').eq(35)
        )
        return response['Items']
    except Exception as error:
        return error

結果: queryで指定した条件で絞ることができる

LSIでレンジキーのみ指定してqueryする

def test_001_07():
    try:
        response = table.query(
            IndexName="department_lsi",
            KeyConditionExpression=Key('department').eq('11111')
        )
        return response['Items']
    except Exception as error:
        return error

結果: 不可能。LSIの場合、ハッシュキーが必要になるためエラーになる。

'An error occurred (ValidationException) when calling the Query operation: Query condition missed key schema element: id'

GSIでレンジキーのみ指定してqueryする

def test_001_08():
    try:
        response = table.query(
            IndexName="department_gsi",
            KeyConditionExpression=Key('department').eq('11111')
        )
        return response['Items']
    except Exception as error:
        return error

結果: GSIの場合はハッシュキー不要なので、セカンダリインデックスで指定したアイテムが複数返却される

GSIでレンジキーのみ指定してqueryしてさらに件数を2件までに絞る

def test_001_09():
    try:
        response = table.query(
            IndexName="department_gsi",
            KeyConditionExpression=Key('department').eq('11111'),
            Limit=2
        )
        return response['Items']
    except Exception as error:
        return error

結果: 想定通りの件数で絞られる

キー項目を更新できない代わりに、delete → put する

def test_001_10():
    try:
        table.delete_item(Key={ 'id': '00001', 'department': '22222' })
        table.put_item(Item={ 'id': '00001', 'department': '33333', 'age': 36 })
        return "OK"
    except Exception as error:
        return error

結果: 問題なく動作。ただし、DynamoDBは結果整合でしかないため、delete前にput結果が取得されることを考慮しておく。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0