あえて aws-sdk-go で dynamoDB を使うときの基本操作

# 始めに注意

最初に言っておきますが、goからawsのdynamoDBをいじるなら
色々良いフレームワークが他に作られています。

[気楽にDynamoDBを使おう](http://qiita.com/guregu/items/2e9ac305791935b86f5d)の[guregu](http://qiita.com/guregu)さんは

> aws-sdk-goでは、DynamoDBへの問い合わせはできるが、かなり面倒臭いことをやらなきゃいけない。公式のライブラリーはポインターだらけで、データのMarshalとUnmarshalで苦労することがあります。

と言っていますが本当にその通りです。

それでも何かしらの理由があって aws-sdk-go で書かなきゃいけない場合もあるかもしれないので
そういう場合にだけ使って下さい。

あと、文章が真面目なのはここまでです。

# DynamoDBの使い方

## 公式の情報
github は [https://github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) やね。
公式ドキュメントは [ココ](http://docs.aws.amazon.com/sdk-for-go/api/service/dynamodb/DynamoDB.html)見たらいいね。

## インストールとか
おなじみ
`go get github.com/aws/aws-sdk-go`
だけやね。

import は

```go

import (
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/dynamodb"
)

```

みたいな感じかな。
まぁ、IDE使ってたら補完してくれるでしょ。

あと、認証情報が必要なんで、IAMで良い感じのロールをあてたユーザーを作って、
`~/.aws/credentials`に

```

[default]
aws_access_key_id = AKID1234567890
aws_secret_access_key = MY-SECRET-KEY

```

みたいなファイルを作ったらいいよ。
この辺はdynamoDB以外でも同じなので情報出てくるからいいね。
windowsユーザー？ごめん。知らない。

コードに直接書くことも出来るぽいけど、危ないからやめてね。

##　兎にも角にも dynamodb インスタンスを作る
とは言っても一行

```go

ddb := dynamodb.New(session.New(), aws.NewConfig().WithRegion("us-east-1"))

```

こんだけやね。

`WithRegion()`の中身は使ってるリージョンを指定してね。
東京なら`ap-northeast-1`ね。他は公式の[このページ](https://docs.aws.amazon.com/ja_jp/ElasticMapReduce/latest/DeveloperGuide/emr-plan-region.html)で確認したらいいよ。

## テーブル生成
一番シンプルに作るならこんな感じ

```go:main.go

package main

import (
	"github.com/aws/aws-sdk-go/service/dynamodb"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/aws"
	"fmt"
)

func main()  {
	ddb := dynamodb.New(session.New(), aws.NewConfig().WithRegion("us-east-1"))

	params := &dynamodb.CreateTableInput{
		AttributeDefinitions: []*dynamodb.AttributeDefinition{
			{
				AttributeName: aws.String("id"), 	// プライマリキー名
				AttributeType: aws.String("S"), 	// データ型(String:S, Number:N, Binary:B の三種)
			},
		},
		KeySchema: []*dynamodb.KeySchemaElement{
			{
				AttributeName: aws.String("id"),	// インデックス名
				KeyType:       aws.String("HASH"),	// インデックスの型(HASH または RANGE)
			},
		},
		ProvisionedThroughput: &dynamodb.ProvisionedThroughput{ // Required
			ReadCapacityUnits:  aws.Int64(1),	// 読み込みキャパシティーユニット（デフォルト：１）
			WriteCapacityUnits: aws.Int64(1),	// 書き込みキャパシティーユニット（デフォルト：１）
		},
		TableName: aws.String("TableName"),	// テーブル名
	}

	resp, err := ddb.CreateTable(params)

	if err != nil {
		fmt.Println(err.Error())	// エラー処理
	}

	fmt.Println(resp)
}

```

awsにデータとして渡す時は基本 `aws.String()` とか、型に合わせてしてやればOKかな。

`AttributeType` とか `KeyType` とか、なんで string を渡す仕様になってるのかは不明。
だけどまぁそういうものってことで。

セカンダリインデックスとか、ストリームとか設定できるけど、
そんなことしたい人は多分公式ドキュメント読むだろうからカット。

## テーブル削除

作ったテーブルを消すのは一瞬で、

```go:main.go

package main

import (
	"github.com/aws/aws-sdk-go/service/dynamodb"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/aws"
	"fmt"
)

func main()  {
	ddb := dynamodb.New(session.New(), aws.NewConfig().WithRegion("us-east-1"))
	
	params := &dynamodb.DeleteTableInput{
		TableName: aws.String("TableName"),	// 削除するテーブルの名前
	}

	resp, err := ddb.DeleteTable(params)

	if err != nil {
		fmt.Println(err.Error())	// エラー処理
	}

	fmt.Println(resp)
}

```

まぁ消すだけやし簡単なのは当然やね。

## データを入れる

(ここからは[テーブル生成](#テーブル生成)で作ったテーブルがある前提)

データベースなんだからデータ入れないと始まらんね。

```go:main.go

package main

import (
	"github.com/aws/aws-sdk-go/service/dynamodb"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/aws"
	"fmt"
)

func main()  {
	ddb := dynamodb.New(session.New(), aws.NewConfig().WithRegion("us-east-1"))

	param := &dynamodb.UpdateItemInput{
		TableName: aws.String("TableName"),		// テーブル名を指定

		Key: map[string]*dynamodb.AttributeValue{
			"id": {
				S: aws.String("foo"),	// キー名を指定
			},
		},

		ExpressionAttributeNames: map[string]*string{
			"#username": aws.String("username"),		// 項目名をプレースホルダに入れる
		},
		ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
			":username_value": {
				S: aws.String("hoge"),		// 値をプレースホルダに入れる
			},
		},
		UpdateExpression: aws.String("set #username = :username_value"),	//プレースホルダを利用して更新の式を書く

		//あとは返してくる情報の種類を指定する
		ReturnConsumedCapacity: aws.String("NONE"),
		ReturnItemCollectionMetrics: aws.String("NONE"),
		ReturnValues: aws.String("NONE"),
	}

	resp, err := ddb.UpdateItem(param)		//実行

	if err != nil {
		fmt.Println(err.Error())
	}

	fmt.Println(resp)
}

```

ってやると

![2016_03_15_17_11.jpg](https://qiita-image-store.s3.amazonaws.com/0/92484/abe80bf3-f831-f990-4669-0bbd07c873bf.jpeg)

データ入ってるね。

返してくる情報の種類とその指定する文字列については、[DynamoDBメモ (+PHP SDK2)](http://qiita.com/inouet/items/b9b9d7c2163130cc7d39#returnconsumedcapacity) っていう記事を書いてくれている人がいる。
PHP だけど値は一緒だからこっち参照したら良いって思う。

値の入れ方は `UpdateExpression` を使うと良いって前に読んだ。
(参考(外部サイト): [DynamoDBのupdateItemを、AttributeUpdatesではなくUpdateExpressionで行う](http://dev.classmethod.jp/cloud/aws/dynamodb_updateexpression/))

`UpdateExpression()` で使える式は[公式のココ](https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Expressions.Modifying.html)に載ってるからこっちで確認。

これでデータも入って、データベース始まったね。

## データの更新

データを入れる時と同じ方法で、

```go

Key: map[string]*dynamodb.AttributeValue{
			"id": {
				S: aws.String("foo"),	// キー名を指定
			},
		},

```

の部分のキーを既存のキーにしたら上書きできるからそれだけやね。

## データの検索

検索っていってもまずは一致するプライマリキーを取り出してくるくらいでいいよね。

```go:main.go

package main

import (
	"github.com/aws/aws-sdk-go/service/dynamodb"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/aws"
	"fmt"
)

func main()  {
	ddb := dynamodb.New(session.New(), aws.NewConfig().WithRegion("us-east-1"))

	params := &dynamodb.GetItemInput{
		TableName: aws.String("TableName"),	// テーブル名

		Key: map[string]*dynamodb.AttributeValue{
			"id": {				// キー名
				S: aws.String("foo"),	// 持ってくるキーの値
			},
		},
		AttributesToGet: []*string{
			aws.String("username"),		// 欲しいデータの名前
		},
		ConsistentRead: aws.Bool(true),		// 常に最新を取得するかどうか

		//返ってくるデータの種類
		ReturnConsumedCapacity: aws.String("NONE"),
	}

	resp, err := ddb.GetItem(params)

	if err != nil {
		fmt.Println(err.Error())
	}

	//resp.Item[項目名].型 でデータへのポインタを取得
	fmt.Println(*resp.Item["username"].S)
}

``` 

複雑なことしなければこの程度だから言うほどそんなに面倒じゃないんじゃないかな。
最後のデータの取り出しが、型をくっつける + ポインタで返ってくる でちょっと面倒？
まぁ慣れればそんなでもないね。

## 削除

単純に消すだけならやっぱりキー名だけで OK やね。

```go:main.go

package main

import (
	"github.com/aws/aws-sdk-go/service/dynamodb"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/aws"
	"fmt"
)

func main()  {
	ddb := dynamodb.New(session.New(), aws.NewConfig().WithRegion("us-east-1"))

	params := &dynamodb.DeleteItemInput{
		TableName: aws.String("TableName"),	// テーブル名

		Key: map[string]*dynamodb.AttributeValue{
			"id": {				// キー名
				S: aws.String("foo"),	// 削除するキーの値
			},
		},

		//返ってくるデータの種類
		ReturnConsumedCapacity: aws.String("NONE"),
		ReturnItemCollectionMetrics: aws.String("NONE"),
		ReturnValues: aws.String("NONE"),
	}

	_, err := ddb.DeleteItem(params)

	if err != nil {
		fmt.Println(err.Error())
	}
}

```

とっても単純。
セカンダリキーとか色々設定してるとちょっと面倒っぽいけど、
とりあえずこれくらい出来れば十分やね。


# まとめ
最初に脅し入れた割にはそんなに難しい感じにはならんかったね。
ただ、マップとかリストとか扱い始めると面倒（難しくは無いけど）だから、
やっぱり使えるなら他のフレームワークとか使った方がいいね。