AWS SDK for Go で DynamoDB のテーブルから特定の属性だけ取得する
2020-05-18最近は Go を書くことが多いので、備忘録として AWS SDK for Go を使って DynamoDB のテーブルから特定の属性 (Attributes) のみを取得する方法について書いておきます。
目次
Go で DynamoDB を操作する
Go で DynamoDB を操作する方法についてぐぐってみると、 guregu/dynamo というライブラリを使った例が多く出てきます。DynamoDB に限らず、 Go の SDK で AWS のリソースを操作する際にはポインタ型を扱う必要があるため、実装が複雑だと感じてしまいがちです。 guregu/dynamo を使用すると、非常にかんたんな記述で DynamoDB を操作することができます。
ただし、今回のような場合や少し複雑な使い方をする場合は AWS SDK for Go を使ったほうが柔軟な実装ができます。
やること
DynamoDB のサンプルテーブルの Thread テーブルに対して、次の 2 つの方法でデータを取得してみます。
- すべての属性を含めて Scan する
- 特定の属性のみを取得するように Scan する
サンプルデータについては公式のデータを使用します。
ここからは実際のコードを抜粋して書きますが、全体については Gist に置いていますのでそちらを参照してください。
すべての属性を含めて Scan する
package main
import (
"bytes"
"encoding/json"
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
)
// Thread is a struct of a Thread item
type Thread struct {
ForumName string `dynamodbav:"ForumName" json:"forum_name"`
Subject string `dynamodbav:"Subject" json:"subject"`
Answered int `dynamodbav:"Answered" json:"answered"`
LastPostedBy string `dynamodbav:"LastPostedBy" json:"last_posted_by"`
LastPostedDateTime string `dynamodbav:"LastPostedDateTime" json:"last_posted_date_time"`
Message string `dynamodbav:"Message" json:"message"`
Replies int `dynamodbav:"Replies" json:"replies"`
Tags []string `dynamodbav:"Tags" json:"tags"`
Views int `dynamodbav:"Views" json:"views"`
}
var tableName string = "Thread"
var region string = "ap-northeast-1"
var db = dynamodb.New(session.New(), &aws.Config{
Region: aws.String(region),
})
// Scan is a function to scan table.
func Scan() []Thread {
var threads []Thread = []Thread{}
scanOut, err := db.Scan(&dynamodb.ScanInput{
TableName: aws.String(tableName),
})
if err != nil {
fmt.Println(err.Error())
return threads
}
for _, scanedThread := range scanOut.Items {
var threadTmp Thread
_ = dynamodbattribute.UnmarshalMap(scanedThread, &threadTmp)
threads = append(threads, threadTmp)
}
return threads
}
// ThreadsToJSONString is a function to convert Thread object to JSON string.
func ThreadsToJSONString(threads interface{}) string {
j, err := json.Marshal(threads)
if err != nil {
fmt.Println(err.Error())
return ""
}
var buf bytes.Buffer
jerr := json.Indent(&buf, j, "", " ")
if jerr != nil {
fmt.Println(jerr.Error())
return ""
}
return buf.String()
}
func main() {
fmt.Println("Scan with all attributes.")
allAttrRes := Scan()
fmt.Println(ThreadsToJSONString(allAttrRes))
}
まず Thread
Struct をタグ付きで定義します。 dynamodbav
タグは DynamoDB テーブルの属性名を指定します。 json
タグは JSON で出力する際の属性名を指定します。
Scan()
では引数に *dynamodb.ScanInput
型の変数を渡します。その変数ではテーブル名 TableName
のみ指定しています。
ThreadsToJSONString()
関数では Thread
オブジェクトを整形済みの JSON 文字列に変換しています。
このスクリプトを実行すると、次のような出力が得られます。
$ go run ./src/dynamo/main.go
Scan with all attributes.
[
{
"forum_name": "Amazon CloudFront",
"subject": "CloudFront Thread 1",
"answered": 0,
"last_posted_by": "User A",
"last_posted_date_time": "2015-09-22T19:58:22.514Z",
"message": "CloudFront thread 1 message",
"replies": 0,
"tags": [
"index",
"primarykey",
"table"
],
"views": 10
},
{
"forum_name": "Amazon S3",
"subject": "S3 Thread 1",
"answered": 0,
"last_posted_by": "User A",
"last_posted_date_time": "2015-09-29T19:58:22.514Z",
"message": "S3 thread 1 message",
"replies": 0,
"tags": [
"largeobjects",
"multipart upload"
],
"views": 0
},
{
"forum_name": "Amazon DynamoDB",
"subject": "DynamoDB Thread 1",
"answered": 0,
"last_posted_by": "User A",
"last_posted_date_time": "2015-09-22T19:58:22.514Z",
"message": "DynamoDB thread 1 message",
"replies": 0,
"tags": [
"index",
"primarykey",
"table"
],
"views": 0
},
{
"forum_name": "Amazon DynamoDB",
"subject": "DynamoDB Thread 2",
"answered": 0,
"last_posted_by": "User A",
"last_posted_date_time": "2015-09-15T19:58:22.514Z",
"message": "DynamoDB thread 2 message",
"replies": 0,
"tags": [
"items",
"attributes",
"throughput"
],
"views": 0
}
]
特定の属性のみを取得するように Scan する
基本的には上の例と同じなので、異なる部分のみ抜き出しています。今回は Thread テーブルの属性のうち、 ForumName
と Subject
のみ取得してみます。
// ThreadWithSomeAttr is a struct of a Thread item with some attributes.
type ThreadWithSomeAttr struct {
ForumName string `dynamodbav:"ForumName" json:"forum_name"`
Subject string `dynamodbav:"Subject" json:"subject"`
}
// ScanSomeAttr is a function to scan table with some attributes.
func ScanSomeAttr() []ThreadWithSomeAttr {
var threads []ThreadWithSomeAttr = []ThreadWithSomeAttr{}
scanOut, err := db.Scan(&dynamodb.ScanInput{
TableName: aws.String(tableName),
ExpressionAttributeNames: map[string]*string{
"#FNAME": aws.String("ForumName"),
"#SUBJ": aws.String("Subject"),
},
ProjectionExpression: aws.String("#FNAME, #SUBJ"),
})
if err != nil {
fmt.Println(err.Error())
return threads
}
for _, scanedThread := range scanOut.Items {
var threadTmp ThreadWithSomeAttr
_ = dynamodbattribute.UnmarshalMap(scanedThread, &threadTmp)
threads = append(threads, threadTmp)
}
return threads
}
Scan した結果を受け取る Struct として ThreadWithSomeAttr
を定義します。Scan()
の引数としては、テーブル名に加えて、取得したい属性名を指定した *dynamodb.ScanInput
を指定しています。
この状態で実行すると、次のような出力が得られます。
$ go run ./src/dynamo/main.go
Scan with some attributes.
[
{
"forum_name": "Amazon CloudFront",
"subject": "CloudFront Thread 1"
},
{
"forum_name": "Amazon S3",
"subject": "S3 Thread 1"
},
{
"forum_name": "Amazon DynamoDB",
"subject": "DynamoDB Thread 1"
},
{
"forum_name": "Amazon DynamoDB",
"subject": "DynamoDB Thread 2"
}
]
ちなみに Scan した結果を ThreadWithSomeAttr
Struct の代わりに、すべての属性を定義した Thread
で受け取ると、次のような結果になります。
$ go run ./src/dynamo/main.go
Scan with some attributes.
[
{
"forum_name": "Amazon CloudFront",
"subject": "CloudFront Thread 1",
"answered": 0,
"last_posted_by": "",
"last_posted_date_time": "",
"message": "",
"replies": 0,
"tags": null,
"views": 0
},
{
"forum_name": "Amazon S3",
"subject": "S3 Thread 1",
"answered": 0,
"last_posted_by": "",
"last_posted_date_time": "",
"message": "",
"replies": 0,
"tags": null,
"views": 0
},
{
"forum_name": "Amazon DynamoDB",
"subject": "DynamoDB Thread 1",
"answered": 0,
"last_posted_by": "",
"last_posted_date_time": "",
"message": "",
"replies": 0,
"tags": null,
"views": 0
},
{
"forum_name": "Amazon DynamoDB",
"subject": "DynamoDB Thread 2",
"answered": 0,
"last_posted_by": "",
"last_posted_date_time": "",
"message": "",
"replies": 0,
"tags": null,
"views": 0
}
]
ForumName
と Subject
の値は取得できますが、それ以外の属性についてはそれぞれの属性に定義された型の初期値になります。
まとめ
AWS SDK for Go を使って DynamoDB のテーブルから特定の属性 (Attributes) のみを取得してみた話でした。
ポインタ型については 1 年目のころに C 言語で扱って以来だったのでちょっと拒否反応がありましたが、慣れれば特に問題ないですね。
AWS SDK に関しては公式ドキュメントもしっかり用意されているので、その点でも安心です。
今回のサンプルスクリプトは下記 Gist に置いています。
comments powered by Disqus