AWS日記③ (DynamoDB)

# はじめに
今回はDynamoDBを利用して簡易な掲示板を作成します。

# 準備
DynamoDBのテーブルを作成します。
[Lambda , API Gatewayの準備をします。]
(https://qiita.com/tanaka_takurou/items/3f93295de6cff060ec09)

[参考資料]
[テーブルの作成](https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/SQLtoNoSQL.CreateTable.html)
[DynamoDBのテーブル作成](https://qiita.com/blackcat5016/items/e41f7fb8b6b7a0c9b90b)

# WEBページ・API作成
GO言語のAWS Lambda関数ハンドラー aws-lambda-go を使用してHTMLやJSONを返す処理を作成します。
また、DynamoDB を使用するため aws-sdk-go を利用します。

[参考資料]
[AWS SDK for Go API Reference](https://docs.aws.amazon.com/sdk-for-go/api/service/dynamodb/)
[あえて aws-sdk-go で dynamoDB を使うときの基本操作](https://qiita.com/ezaki/items/2a9f10c53d958070ca95)
[Reading an Amazon DynamoDB Table Item](https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/dynamo-example-read-table-item.html)


今回は、ページ用とAPI用のLambda関数を別々に作成し、ページ用のメソッドはGET、API用のメソッドはPOSTに設定しました。
![apigateway.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/586707/1cc6605e-7194-1296-ecfc-7bf6eef6f30b.jpeg)



##### 要素を取得する際にはScanを使う。

```golang:main.go
func scan(tableName string, filt expression.ConditionBuilder)(*dynamodb.ScanOutput, error)  {
        sess := session.Must(session.NewSessionWithOptions(session.Options{
                SharedConfigState: session.SharedConfigEnable,
        }))
        svc := dynamodb.New(sess)
        expr, err := expression.NewBuilder().WithFilter(filt).Build()
        if err != nil {
                return nil, err
        }
        params := &dynamodb.ScanInput{
                ExpressionAttributeNames:  expr.Names(),
                ExpressionAttributeValues: expr.Values(),
                FilterExpression:          expr.Filter(),
                ProjectionExpression:      expr.Projection(),
                TableName:                 aws.String(tableName),
        }
        return svc.Scan(params)
}

func getMessageList(room_id int, threshold int)([]MessageData, error)  {
        var messageList []MessageData
        result, err := scan(messageTableName, expression.Name("room_id").Equal(expression.Value(room_id)))
        if err != nil {
                return nil, err
        }
        for _, i := range result.Items {
                item := MessageData{}
                err = dynamodbattribute.UnmarshalMap(i, &item)
                if err != nil {
                        return nil, err
                }
                if item.Status >= threshold {
                        continue
                }
                messageList = append(messageList, item)
        }
        return messageList, nil
}
```

##### 要素を挿入する際にはPutItemを使う。

```golang:main.go
func put(tableName string, av map[string]*dynamodb.AttributeValue) error {
        sess := session.Must(session.NewSessionWithOptions(session.Options{
                SharedConfigState: session.SharedConfigEnable,
        }))
        svc := dynamodb.New(sess)
        input := &dynamodb.PutItemInput{
                Item:      av,
                TableName: aws.String(tableName),
        }
        _, err := svc.PutItem(input)
        return err
}

func putMessage(room_id int, user string, message string, icon_id int) error {
        t := time.Now()
        count, err := getMessageCount(room_id)
        if err != nil {
                return err
        }
        item := MessageData {
                Message_Id: int(*count) + 1,
                Room_Id: room_id,
                Icon_Id: icon_id,
                Status: 0,
                User: user,
                Message: message,
                Created: t.Format(layout),
        }
        av, err := dynamodbattribute.MarshalMap(item)
        if err != nil {
                return err
        }
        err = put(messageTableName, av)
        if err != nil {
                return err
        }
        return nil
}
```

##### 要素を更新する際にはUpdateItemを使う。

```golang:main.go
func update(tableName string, an map[string]*string, av map[string]*dynamodb.AttributeValue, key map[strin
g]*dynamodb.AttributeValue, updateExpression string) error {
        sess := session.Must(session.NewSessionWithOptions(session.Options{
                SharedConfigState: session.SharedConfigEnable,
        }))
        svc := dynamodb.New(sess)
        input := &dynamodb.UpdateItemInput{
                ExpressionAttributeNames: an,
                ExpressionAttributeValues: av,
                TableName: aws.String(tableName),
                Key: key,
                ReturnValues:     aws.String("UPDATED_NEW"),
                UpdateExpression: aws.String(updateExpression),
        }

        _, err := svc.UpdateItem(input)
        return err
}

func updateMessage(message_id int, value int, name string) error {
        an := map[string]*string{
                "#s": aws.String(name),
        }
        av := map[string]*dynamodb.AttributeValue{
                ":new": {
                        N: aws.String(strconv.Itoa(value)),
                },
        }
        key := map[string]*dynamodb.AttributeValue{
                "message_id": {
                        N: aws.String(strconv.Itoa(message_id)),
                },
        }
        updateExpression := "set #s = #s + :new"
        err := update(messageTableName, an, av, key, updateExpression)
        if err != nil {
                return err
        }
        return nil
}
```

[作成したLambda 関数、テンプレート](https://github.com/tanaka-takurou/serverless-message-board-go)


# 終わりに
DynamoDBは NoSQLのため慣れるまでは扱いにくく感じましたが、メリットが多くあります。
[参考資料]
[DynamoDBを使いこなして精神的安定を手に入れた](https://qiita.com/walkers/items/9b39d752cc5b8df7b4b5)