DynamoDBからデータを取得

こんな感じの、毎日名前が変えられる謎のUserNameテーブルがあるとします。
![DynamoDB · AWS Console.png](https://qiita-image-store.s3.amazonaws.com/0/98749/95fbfb14-b033-25ea-a9f2-a986cb0ab519.png)

## パーティションキーとソートキー
IDとDate（データ登録日）によってユニークが決まるようなテーブルの場合、
IDをパーティションキー、Dateをソートキーと設定するといいようです。
http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/GuidelinesForTables.html

## GetItem
GetItemを使用すると、ユニークキーを指定してデータが１つ取得できます。
http://docs.aws.amazon.com/sdk-for-go/api/service/dynamodb/#DynamoDB.GetItem

以下の例では、dynamodbから取得後、User構造体に入れたりJsonにしたりして遊んでます。

```go:dynamodb-get-item.go
package main

import (
	"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"
)

type User struct {
	UserID int    `json:"user_id" dynamodbav:"UserID"`
	Date   string `json:"date" dynamodbav:"Date"`
	Time   string `json:"time" dynamodbav:"Time"`
	Name   string `json:"name" dynamodbav:"Name"`
}

func main() {
	svc := dynamodb.New(session.New(), aws.NewConfig().WithRegion("ap-northeast-1"))

	input := &dynamodb.GetItemInput{
		TableName: aws.String("UserName"),
		Key: map[string]*dynamodb.AttributeValue{
			"UserID": {
				N: aws.String("1"),
			},
			"Date": {
				S: aws.String("20171215"),
			},
		},
	}

	result, err := svc.GetItem(input)
	if err != nil {
		fmt.Println("[GetItem Error]", err)
		return
	}

	user := &User{}
	if err := dynamodbattribute.UnmarshalMap(result.Item, user); err != nil {
		fmt.Println("[Unmarshal Error]", err)
		return
	}

	fmt.Println(result)

	j, _ := json.Marshal(user)
	fmt.Println(string(j))
}
```

```console
$ go run dynamodb-get-item.go
{
  Item: {
    UserID: {
      N: "1"
    },
    Date: {
      S: "20171215"
    },
    Time: {
      S: "20171215120000"
    },
    Name: {
      S: "hoge"
    }
  }
}
{"user_id":1,"date":"20171215","time":"20171215120000","name":"hoge"}
```

## Query
ユーザの名前変更履歴が見たい！とかあると思います。
そんな時はQueryで検索します。(GetItemsとかは無いようです)
http://docs.aws.amazon.com/sdk-for-go/api/service/dynamodb/#DynamoDB.Query


```go:dynamodb-query.go
package main

import (
	"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"
)

type User struct {
	UserID int    `json:"user_id" dynamodbav:"UserID"`
	Date   string `json:"date" dynamodbav:"Date"`
	Time   string `json:"time" dynamodbav:"Time"`
	Name   string `json:"name" dynamodbav:"Name"`
}

func main() {
	svc := dynamodb.New(session.New(), aws.NewConfig().WithRegion("ap-northeast-1"))

	input := &dynamodb.QueryInput{
		TableName: aws.String("UserName"),
		ExpressionAttributeNames: map[string]*string{
			"#ID":   aws.String("UserID"), // alias付けれたりする
			"#Date": aws.String("Date"),   // 予約語はそのままだと怒られるので置換する
			"#Name": aws.String("Name"),   // 予約語は（
		},
		ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
			":id": { // :を付けるのがセオリーのようです
				N: aws.String("1"),
			},
		},
		KeyConditionExpression: aws.String("#ID = :id"),         // 検索条件
		ProjectionExpression:   aws.String("#ID, #Date, #Name"), // 取得カラム
		ScanIndexForward:       aws.Bool(false),                 // ソートキーのソート順（指定しないと昇順）
		Limit:                  aws.Int64(100),                  // 取得件数の指定もできる
	}

	result, err := svc.Query(input)
	if err != nil {
		fmt.Println("[Query Error]", err)
		return
	}

	users := make([]*User, 0)
	if err := dynamodbattribute.UnmarshalListOfMaps(result.Items, &users); err != nil {
		fmt.Println("[Unmarshal Error]", err)
		return
	}

	fmt.Println(result)

	j, _ := json.Marshal(users)
	fmt.Println(string(j))
}

```

```console

$ go run dynamodb-query.go
{
  Count: 2,
  Items: [{
      Date: {
        S: "20171216"
      },
      UserID: {
        N: "1"
      },
      Name: {
        S: "fuga"
      }
    },{
      Date: {
        S: "20171215"
      },
      UserID: {
        N: "1"
      },
      Name: {
        S: "hoge"
      }
    }],
  ScannedCount: 2
}
[{"user_id":1,"date":"20171216","time":"","name":"fuga"},{"user_id":1,"date":"20171215","time":"","name":"hoge"}]
```

## 日付だけ指定して取得したい時は？
じゃあ日付(ソートキー)だけ指定して取得したいな〜ってなるじゃないですか。

```go
input := &dynamodb.QueryInput{
	TableName: aws.String("UserName"),
	ExpressionAttributeNames: map[string]*string{
		"#Date": aws.String("Date"),
	},
	ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
		":date": {
			S: aws.String("20171215"),
		},
	},
	KeyConditionExpression: aws.String("#Date = :date"),
}
```

```console
ValidationException: Query condition missed key schema element: UserID
```
となって、できません。

### →そんな時はグローバルセカンダリインデックス 
http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/GSI.html

GSIを使うと、パーティションキー・ソートキー以外での検索ができるようになるみたいです。

DateにGSIを張ってみます。
![DynamoDB · AWS Console (1).png](https://qiita-image-store.s3.amazonaws.com/0/98749/830a9f9b-aaff-3965-34d9-41d83ef88b03.png)

これでGSIを使って検索できるようになりました。

```go
input := &dynamodb.QueryInput{
	TableName: aws.String("UserName"),
	ExpressionAttributeNames: map[string]*string{
		"#Date": aws.String("Date"),
	},
	ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
		":date": {
			S: aws.String("20171215"),
		},
	},
	KeyConditionExpression: aws.String("#Date = :date"),
	IndexName:              aws.String("Date-index"),
}
```

```console
$ go run dynamodb-query.go
{
  Count: 1,
  Items: [{
      Date: {
        S: "20171215"
      },
      UserID: {
        N: "1"
      },
      Time: {
        S: "20171215120000"
      },
      Name: {
        S: "hoge"
      }
    }],
  ScannedCount: 1
}
[{"user_id":1,"date":"20171215","time":"20171215120000","name":"hoge"}]
```

ハピなる[:cherry_blossom:](http://www.tv-tokyo.co.jp/anime/prettyrhythm2013/index2.html)