0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

DynamoDB&Golang データ取得/更新/削除/操作まとめ

Last updated at Posted at 2024-06-17

PutItem/UpdateItem/DeleteItem/BatchWriteItem/TransactWriteItems

アプリケーションを作成する上で非常によく使用する操作の違いや特徴をまとめてみました。

特徴の比較

特徴 GetItem BatchGetItem TransactGetItem
用途 単一アイテムの取得 複数アイテムの一括取得 複数の一貫したアイテム取得
最大アイテム数 1つ 100個まで 25個まで
対応テーブル数 1つのテーブル 複数のテーブル 複数のテーブル
強い整合性 オプション オプション デフォルトで強い整合性
トランザクション サポートしない サポートしない サポートする
未処理アイテムの再試行 不要 必要な場合がある 不要
特徴 PutItem UpdateItem DeleteItem BatchWriteItem TransactWriteItems
用途 アイテムの追加または更新 既存アイテムの更新 アイテムの削除 複数アイテムの一括追加または削除 複数のアイテム操作のトランザクション
最大アイテム数 1つ 1つ 1つ 25個まで 25個まで
対応操作 Put (追加/更新) Update (更新) Delete (削除) Put, Delete (追加/削除) Put, Update, Delete, ConditionCheck
トランザクション サポートしない サポートしない サポートしない サポートしない サポートする
未処理アイテムの再試行 不要 不要 不要 必要な場合がある 不要

各メソッドの説明と使用例

GetItem

用途: 単一のアイテムを取得する。
特徴: シンプルで高速。プライマリキーを使用してアイテムを取得する。
使用例: ユーザー情報をユーザーIDで取得する場合。

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/aws/aws-sdk-go-v2/aws"
    "github.com/aws/aws-sdk-go-v2/config"
    "github.com/aws/aws-sdk-go-v2/service/dynamodb"
    "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
)

func main() {
    cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion("us-west-2"))
    if err != nil {
        log.Fatalf("unable to load SDK config, %v", err)
    }

    // DynamoDBクライアントを作成
    svc := dynamodb.NewFromConfig(cfg)

    // テーブル名とキーを設定
    tableName := "YourTableName"
    key := map[string]types.AttributeValue{
        "PrimaryKey": &types.AttributeValueMemberS{Value: aws.String("Key1")},
    }

    // GetItemInputの作成
    input := &dynamodb.GetItemInput{
        TableName: aws.String(tableName),
        Key:       key,
    }

    // GetItem APIの呼び出し
    result, err := svc.GetItem(context.TODO(), input)
    if err != nil {
        log.Fatalf("GetItem failed: %v", err)
    }

    if result.Item == nil {
        log.Println("No item found")
        return
    }

    fmt.Printf("Item: %v\n", result.Item)
}

BatchGetItem

用途: 複数のアイテムを一度に取得する。
特徴: 最大100個のアイテムを複数のテーブルから一括取得可能。未処理アイテムがある場合は再試行が必要。
使用例: 一度に複数のユーザー情報を取得する場合。

package main

import (
    "context"
    "log"

    "github.com/aws/aws-sdk-go-v2/aws"
    "github.com/aws/aws-sdk-go-v2/config"
    "github.com/aws/aws-sdk-go-v2/service/dynamodb"
    "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
)

func main() {
    cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion("us-west-2"))
    if err != nil {
        log.Fatalf("unable to load SDK config, %v", err)
    }

    svc := dynamodb.NewFromConfig(cfg)

    tableName := "YourTableName"
    items := []types.TransactGetItem{
        {
            Get: &types.Get{
                TableName: aws.String(tableName),
                Key: map[string]types.AttributeValue{
                    "PrimaryKey": &types.AttributeValueMemberS{Value: aws.String("Key1")},
                },
            },
        },
        {
            Get: &types.Get{
                TableName: aws.String(tableName),
                Key: map[string]types.AttributeValue{
                    "PrimaryKey": &types.AttributeValueMemberS{Value: aws.String("Key2")},
                },
            },
        },
    }

    input := &dynamodb.TransactGetItemsInput{
        TransactItems: items,
    }

    result, err := svc.TransactGetItems(context.TODO(), input)
    if err != nil {
        log.Fatalf("TransactGetItems failed: %v", err)
    }

    for _, item := range result.Responses {
        log.Printf("Item: %v", item.Item)
    }
}

TransactGetItem

用途: トランザクションとして複数のアイテムを一貫して取得する。
特徴: 最大25個のアイテムを複数のテーブルから一括取得可能。全ての操作が成功するか、全て失敗する。
使用例: 複数の関連データを一度に取得する場合。

package main

import (
    "context"
    "log"

    "github.com/aws/aws-sdk-go-v2/aws"
    "github.com/aws/aws-sdk-go-v2/config"
    "github.com/aws/aws-sdk-go-v2/service/dynamodb"
    "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
)

func main() {
    cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion("us-west-2"))
    if err != nil {
        log.Fatalf("unable to load SDK config, %v", err)
    }

    svc := dynamodb.NewFromConfig(cfg)

    tableName := "YourTableName"
    items := []types.TransactGetItem{
        {
            Get: &types.Get{
                TableName: aws.String(tableName),
                Key: map[string]types.AttributeValue{
                    "PrimaryKey": &types.AttributeValueMemberS{Value: aws.String("Key1")},
                },
            },
        },
        {
            Get: &types.Get{
                TableName: aws.String(tableName),
                Key: map[string]types.AttributeValue{
                    "PrimaryKey": &types.AttributeValueMemberS{Value: aws.String("Key2")},
                },
            },
        },
    }

    input := &dynamodb.TransactGetItemsInput{
        TransactItems: items,
    }

    result, err := svc.TransactGetItems(context.TODO(), input)
    if err != nil {
        log.Fatalf("TransactGetItems failed: %v", err)
    }

    for _, item := range result.Responses {
        log.Printf("Item: %v", item.Item)
    }
}

PutItem

用途: アイテムを追加または更新します。
特徴: 単一のアイテムを対象とし、存在しない場合は新規追加、存在する場合は上書きします。

package main

import (
    "context"
    "log"

    "github.com/aws/aws-sdk-go-v2/aws"
    "github.com/aws/aws-sdk-go-v2/config"
    "github.com/aws/aws-sdk-go-v2/service/dynamodb"
    "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
)

func main() {
    cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion("us-west-2"))
    if err != nil {
        log.Fatalf("unable to load SDK config, %v", err)
    }

    svc := dynamodb.NewFromConfig(cfg)

    tableName := "YourTableName"
    item := map[string]types.AttributeValue{
        "PrimaryKey": &types.AttributeValueMemberS{Value: aws.String("Key1")},
        "Attribute":  &types.AttributeValueMemberS{Value: aws.String("Value1")},
    }

    input := &dynamodb.PutItemInput{
        TableName: aws.String(tableName),
        Item:      item,
    }

    _, err = svc.PutItem(context.TODO(), input)
    if err != nil {
        log.Fatalf("PutItem failed: %v", err)
    }

    log.Println("PutItem succeeded")
}

UpdateItem

用途: 既存のアイテムを更新します。
特徴: 単一のアイテムの特定の属性を更新します。

package main

import (
    "context"
    "log"

    "github.com/aws/aws-sdk-go-v2/aws"
    "github.com/aws/aws-sdk-go-v2/config"
    "github.com/aws/aws-sdk-go-v2/service/dynamodb"
    "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
)

func main() {
    cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion("us-west-2"))
    if err != nil {
        log.Fatalf("unable to load SDK config, %v", err)
    }

    svc := dynamodb.NewFromConfig(cfg)

    tableName := "YourTableName"
    key := map[string]types.AttributeValue{
        "PrimaryKey": &types.AttributeValueMemberS{Value: aws.String("Key1")},
    }

    input := &dynamodb.UpdateItemInput{
        TableName: aws.String(tableName),
        Key:       key,
        UpdateExpression: aws.String("SET Attribute = :value"),
        ExpressionAttributeValues: map[string]types.AttributeValue{
            ":value": &types.AttributeValueMemberS{Value: aws.String("UpdatedValue")},
        },
    }

    _, err = svc.UpdateItem(context.TODO(), input)
    if err != nil {
        log.Fatalf("UpdateItem failed: %v", err)
    }

    log.Println("UpdateItem succeeded")
}

UpdateItem追加説明

アクション 説明
SET 1つ以上の属性とその値をアイテムに追加。存在しない属性の場合、新しくその属性が作成される。既に存在する場合は値を置き換える。数値属性に対して値を加算または減算できる。
関数:
- if_not_exists(path, operand):指定されたパスに属性が存在しない場合に operand を評価する。
- list_append(operand, operand):リストに新しい要素を追加する。
REMOVE 1つ以上の属性をアイテムから削除。
ADD 指定された値をアイテムに追加。属性が存在する場合は数値の加算またはセットへの追加を行う。数値とセットデータ型のみサポート。トップレベルの属性にのみ使用可能。
DELETE セットから要素を削除。指定した値を既存のセットから引く。セットデータ型のみサポート。トップレベルの属性にのみ使用可能。

UpdateExpressionは、どの属性にどのような操作を行うかを定義する式です。例えば、属性を追加、削除、または変更する操作を指定します。

そして、ExpressionAttributeValuesと続けて使用していますが、このマップは、UpdateExpression内で使用されるプレースホルダー(今回だと:cの部分)に対応する実際の値を提供します。

つまり、以下の部分では、指定されたitem.ID を持つDynamoDBのアイテムを見つけ、そのアイテムの Content 属性を :c として指定された値(この場合は item.Content)に更新します。

input := &dynamodb.UpdateItemInput{
	TableName: aws.String(tableName),
	Key: map[string]*dynamodb.AttributeValue{
		"id": {
			S: aws.String(item.ID),
		},
	},
	UpdateExpression: aws.String("set Content = :c"),
	ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
		":c": {
			S: aws.String(item.Content),
		},
	},
}	

SET

ちなみに、以下のようにまとめてSETして更新することもできます。

UpdateExpression: aws.String("set Content = :c, OtherAttribute = :o"),
ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
    ":c": {
        S: aws.String(item.Content),
    },
    ":o": {
        S: aws.String(item.OtherAttribute),
    },
}

他にも数値の加算減算もできるようです。
例えば以下の例だとすでにあるAge属性にプレースホルダー:vを加算するようsetしていて、具体的な値をExpressionAttributeValuesでvにN(Number)で1を渡しています。

つまり、既存のage属性に+1して更新するような書き方となります。
数値なのにaws.Stringっていうのが若干気持ち悪いなと思いますが、DynamoDBでは内部的に数値を文字列として扱うためらしいです。。。

UpdateExpression: aws.String("set Age = Age + :v"),
ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
    ":v": {
        N: aws.String("1"),
    },
}

あとは、list_appendのような関数が用意されていてそれを使えば、例えばリストに新しい要素を加えることができます。

以下の場合は、Tagsというリストにプレースホルダー:tを追加しています。
具体的な値はExpressionAttributeValuesに書かれていて、:tに対してL(リスト型)で"NewTag"という文字列を追加しています。

UpdateExpression: aws.String("set Tags = list_append(Tags, :t)"),
ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
    ":t": {
        L: []dynamodb.AttributeValue{
            {S: aws.String("NewTag")},
        },
    },
}

ついでに、残りのアクションも見ておきます。

REMOVE 属性を取り除く

まずremoveですが、これは単純でアイテムから1つ以上の属性を取り除きます。deleteと混同しそうですが、後述するようにdeleteの場合はセットから要素を削除するので、属性を取り除くわけでありません。

UpdateExpression: aws.String("remove desginatedAttribute")

setと同様に複数をまとめてremoveすることもできます

UpdateExpression: aws.String("remove Attribute1, Attribute2")

ADD 数値かセットデータのみ

続いて、addですが、数値セットデータ型の属性のみ使用できます。

  • 数値の場合
    属性が存在する場合、指定した値が既存の値に加算されますが、属性が存在しない場合は、0とみなし加算されます。

例えば、以下の場合、Score属性が存在しない場合は10となり、既にScore属性に仮に5が入っている場合、結果は15となります。

UpdateExpression: aws.String("ADD Score :inc"),
ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
    ":inc": {
        N: aws.String("10"),
    },
}
  • セットデータの場合
    ["42.2", "-19", "7.5", "3.14"]のようなセットに対して追加することができます。

以下の場合、 次のような形のセットデータが既に存在している場合は、"SS": ["something", "something"]このセットデータにSS(String Set)でnewTagのように指定して追加することができます。

また、属性が存在しない場合は、セットは空のセットから始まります。

UpdateExpression: aws.String("ADD Tags :newTag"),
ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
    ":newTag": {
        SS: []*string{aws.String("urgent"), aws.String("important")},
    },
}

余談ですが、リストとセットはどちらも配列(のように)値を保持することができますが、リストは同じ値を保持できる一方でセットは同じ値を保持できないです。

もちろん、以下のようにNS(Numbers Set)で追加することも可能です。

UpdateExpression: aws.String("add Numbers :n"),
ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
    ":n": {
        NS: []*string{"3"},
    },
}

DELETE セットデータから要素を取り除く

最後にdeleteですが、前述しましたが、こちらは属性の削除ではなく、セットから要素を取り除きます。
つまり、addの逆だと考えると分かりやすいですね。ただ数値や文字列、バイナリなど他のデータ型には使用できないので注意です。

例えば、以下の場合は"TagToRemove"という文字列をTagsというセットデータ型の属性から削除しています。

UpdateExpression: aws.String("delete Tags :t"),
ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
    ":t": {
        SS: []string{"TagToRemove"},
    },
}

つまり、例えばTags属性が["Tag1","Tag2", "TagsToRemove"]だとした場合、このdeleteを実行すると["Tag1","Tag2"]になるということですね。

おまけ

またdeleteに限ったことではないですが、他のアクションと組み合わせて使用することもできます。
例えば、以下のようにsetremoveを組み合わせて使ったりすることも可能です。

UpdateExpression: aws.String("set Content = :c remove ObsoleteAttribute"),
ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
    ":c": {
        S: aws.String("Updated Content"),
    },
}

DeleteItem

用途: アイテムを削除します。
特徴: 単一のアイテムを削除します。

package main

import (
    "context"
    "log"

    "github.com/aws/aws-sdk-go-v2/aws"
    "github.com/aws/aws-sdk-go-v2/config"
    "github.com/aws/aws-sdk-go-v2/service/dynamodb"
    "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
)

func main() {
    cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion("us-west-2"))
    if err != nil {
        log.Fatalf("unable to load SDK config, %v", err)
    }

    svc := dynamodb.NewFromConfig(cfg)

    tableName := "YourTableName"
    key := map[string]types.AttributeValue{
        "PrimaryKey": &types.AttributeValueMemberS{Value: aws.String("Key1")},
    }

    input := &dynamodb.DeleteItemInput{
        TableName: aws.String(tableName),
        Key:       key,
    }

    _, err = svc.DeleteItem(context.TODO(), input)
    if err != nil {
        log.Fatalf("DeleteItem failed: %v", err)
    }

    log.Println("DeleteItem succeeded")
}

BatchWriteItem

用途: 複数のアイテムを一括で追加または削除します。
特徴: 最大25個のPutItemまたはDeleteItemリクエストを一度に実行します。

package main

import (
    "context"
    "log"

    "github.com/aws/aws-sdk-go-v2/aws"
    "github.com/aws/aws-sdk-go-v2/config"
    "github.com/aws/aws-sdk-go-v2/service/dynamodb"
    "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
)

func main() {
    cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion("us-west-2"))
    if err != nil {
        log.Fatalf("unable to load SDK config, %v", err)
    }

    svc := dynamodb.NewFromConfig(cfg)

    tableName := "YourTableName"
    writeRequests := []types.WriteRequest{
        {
            PutRequest: &types.PutRequest{
                Item: map[string]types.AttributeValue{
                    "PrimaryKey": &types.AttributeValueMemberS{Value: aws.String("Key1")},
                    "Attribute":  &types.AttributeValueMemberS{Value: aws.String("Value1")},
                },
            },
        },
        {
            DeleteRequest: &types.DeleteRequest{
                Key: map[string]types.AttributeValue{
                    "PrimaryKey": &types.AttributeValueMemberS{Value: aws.String("Key2")},
                },
            },
        },
    }

    input := &dynamodb.BatchWriteItemInput{
        RequestItems: map[string][]types.WriteRequest{
            tableName: writeRequests,
        },
    }

    _, err = svc.BatchWriteItem(context.TODO(), input)
    if err != nil {
        log.Fatalf("BatchWriteItem failed: %v", err)
    }

    log.Println("BatchWriteItem succeeded")
}

TransactWriteItems

用途: 複数のアイテム操作をトランザクションとして一貫して実行します。
特徴: 最大25個のPutItem、UpdateItem、DeleteItem、ConditionCheckリクエストを一度に実行します。

package main

import (
    "context"
    "log"

    "github.com/aws/aws-sdk-go-v2/aws"
    "github.com/aws/aws-sdk-go-v2/config"
    "github.com/aws/aws-sdk-go-v2/service/dynamodb"
    "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
)

func main() {
    cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion("us-west-2"))
    if err != nil {
        log.Fatalf("unable to load SDK config, %v", err)
    }

    svc := dynamodb.NewFromConfig(cfg)

    tableName := "YourTableName"

    transactItems := []types.TransactWriteItem{
        {
            Put: &types.Put{
                TableName: &tableName,
                Item: map[string]types.AttributeValue{
                    "PrimaryKey": &types.AttributeValueMemberS{Value: aws.String("Key1")},
                    "Attribute":  &types.AttributeValueMemberS{Value: aws.String("Value1")},
                },
            },
        },
        {
            Update: &types.Update{
                TableName: &tableName,
                Key: map[string]types.AttributeValue{
                    "PrimaryKey": &types.AttributeValueMemberS{Value: aws.String("Key2")},
                },
                UpdateExpression: aws.String("SET Attribute = :value"),
                ExpressionAttributeValues: map[string]types.AttributeValue{
                    ":value": &types.AttributeValueMemberS{Value: aws.String("UpdatedValue")},
                },
            },
        },
        {
            Delete: &types.Delete{
                TableName: &tableName,
                Key: map[string]types.AttributeValue{
                    "PrimaryKey": &types.AttributeValueMemberS{Value: aws.String("Key3")},
                },
            },
        },
    }

    input := &dynamodb.TransactWriteItemsInput{
        TransactItems: transactItems,
    }

    _, err = svc.TransactWriteItems(context.TODO(), input)
    if err != nil {
        log.Fatalf("TransactWriteItems failed: %v", err)
    }

    log.Println("TransactWriteItems succeeded")
}
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?