GAE/GoのDatastoreチートシート  with "goon"

#この記事の目的#
Go言語によるデータストアの頻出処理をメモ
随時更新

#[公式ドキュメント](https://cloud.google.com/datastore/docs/)#
[datastore - GoDoc](https://godoc.org/google.golang.org/appengine/datastore)
[goon - GoDoc](https://godoc.org/github.com/mjibson/goon)

#前提知識#
RDBと比較して知識整理

|Concept|Datastore|RDB|
|:-:|:-:|:-:|
|Category of object|Kind|Table|
|One object|Entity|Row|
|Individual data for an object|Property|Field|
|Unique ID for an object|Key|Primary Key|

Memcacheについても、[軽い記事](https://qiita.com/sensuikan1973/items/86c667d2e53ef9dcc6ea)を書きました。

#ダミーデータ
以下のようなデータを対象としてみる。 (参考: 拙著「[エンティティの関連付けパターン](https://qiita.com/sensuikan1973/items/a81873e7651f9d30c1d8)」)
なお、datastoreのオプションについては→[補足知識](#補足知識)

```go:dataobj.go
import "google.golang.org/appengine/datastore"

type Parent struct {
    ID       string `datastore:"-" goon:"id"`
	Name     string `datastore:"Name,noindex"`
	Age      string `datastore:"Age,noindex"`
}

type Child struct {
    Parent   *datastore.Key `datastore:"-" goon:"parent"`
	Name     string         `datastore:"Name,noindex"`
}
```

```go:dummy_data.go
import "dataobj"
var Parent1 = &dataobj.Parent{
    ID      : "123",
	Name    : "玉子",
 	Age     : "40",
}
```


#頻出関数#
##Goonインスタンスの生成##
定義: [NewGoon(r *http.Request) *Goon](https://godoc.org/github.com/mjibson/goon#NewGoon) 

```go
g := goon.NewGoon(r)
```
##Keyの取得 方法1##
定義: [NewKey(c context.Context, kind, stringID string, intID int64, parent *Key) *Key](https://godoc.org/google.golang.org/appengine/datastore#NewKey)

```go
parent_key := datastore.NewKey(c, "Parent", "123", 0, nil)
```

☆親を持つエンティティのkeyを取得したい場合は、**「stringIDあるいはintID」＋「parentKey」の両方**を指定しなければならない。エンティティグループを組むとこの点が面倒。

##Keyの取得 方法2##
定義: [(g *Goon) Key(src interface{}) *datastore.Key](https://godoc.org/github.com/mjibson/goon#Goon.Key)

```go
parent := &dataobj.Parent{ID: "123"} 
parentKey := g.Key(parent)
```

##データの保存##
定義: [(g \*Goon) Put(src interface{}) (\*datastore.Key, error)](https://godoc.org/github.com/mjibson/goon#Goon.Put)

```go
// Parent1を保存
g.Put(Parent1)

// 上述のparent_keyを親Keyに持つChildを2人保存
g.Put( &dataobj.Child{ Parent: parentKey, Name: "のびた"} )
g.Put( &dataobj.Child{ Parent: parentKey, Name: "どらえもん"} )
```

##Kindを指定するクエリを生成##
定義: [NewQuery(kind string) *Query](https://godoc.org/google.golang.org/appengine/datastore#NewQuery)

```go
q := datastore.NewQuery("Parent")
```

##親Keyを条件に付したクエリの生成##
定義: [(q *Query) Ancestor(ancestor *Key) *Query](https://godoc.org/google.golang.org/appengine/datastore#Query.Ancestor)
*トランザクション内でも実行可能*

```go
q := datastore.NewQuery("Child").Ancestor(parentKey)
```

##クエリに合致するデータを全取得##
定義: [(g \*Goon) GetAll(q \*datastore.Query, dst interface{}) ([]*datastore.Key, error)](https://godoc.org/github.com/mjibson/goon#Goon.GetAll)

```go
// 取得データを受け止める配列を用意
var childs []*dataobj.Child

// クエリに基づき全て取得
g.GetAll(q, &childs)
```

```go
// Keyのみを取得したい場合は、こんな感じ
q := datastore.NewQuery("Parent").KeysOnly()
keys, err := g.GetAll(q, nil)
```

##エンティティの取得##
定義: [(g *Goon) Get(dst interface{}) error](https://godoc.org/github.com/mjibson/goon#Goon.Get)

```go
parent := &dataobj.Parent{ID:"123"} 
err := g.Get(parent)
if err!= nil {
   // エラー処理
}

// Propertyを参照してみる
name := parent.Name
```

##エンティティの取得(複数)##
定義: [(g *Goon) GetMulti(dst interface{}) error](https://godoc.org/github.com/mjibson/goon#Goon.GetMulti)
*KeysOnlyクエリと相性が良い*

```go
// 取得データを受け止める配列を用意。 idsは、1～10のstring配列とする
parents := make([]*dataobj.Parent, len(ids))
for i, id := range ids {
	parents[i] = &dataobj.Parent{ID: id}
}

// IDが1～10のParentエンティティを一括取得
g.GetMulti(parents)
```

##KeyからIDを取得する##
定義: [(k *Key) StringID() string](https://godoc.org/google.golang.org/appengine/datastore#Key.StringID)
entity name あるいは key nameとも呼ばれる。

```go
// Child.Parentに親Keyが入っているとする
parentID := Child.Parent.StringID()
```

##クエリの取得件数をカウント##
定義: [(g *Goon) Count(q *datastore.Query) (int, error)](https://godoc.org/github.com/mjibson/goon#Goon.Count)
なお、Countは内部で`KeysOnly`を付加しているため、クエリ生成時に付加する必要はない

```go
count, err := g.Count(q)
```

##フィルター付きクエリの生成##
定義: [(q *Query) Filter(filterStr string, value interface{}) *Query](https://godoc.org/google.golang.org/appengine/datastore#Query.Filter)

```go
// プロパティに対して (<, >, <=, >=, = の5種)
q := datastore.NewQuery("Parent").Filter("Age =", 40)
``` 

```go
// keyに対して
q := datastore.NewQuery("Parent").Filter("__key__ =", key)
```

ちなみに、**sliceに対しても検索は可能** (参考: 拙著「[エンティティのsliceプロパティを条件に検索する](https://qiita.com/sensuikan1973/items/74cac8ea8f9dadb5af6a)」)

##ソート順を指定したクエリの生成##
定義: [(q *Query) Order(fieldName string) *Query](https://godoc.org/google.golang.org/appengine/datastore#Query.Order)

```go
// Ageの降順でソート
q := datastore.NewQuery("Parent").Order("-Age")
```

#補足知識
##datastoreのタグ名##
"" ：単にフィールド名を使用
**"-"：データストアがそのフィールドを無視する**
##datastoreのオプション##
"omitempty"
フィールドの値が空の場合、自動保存される。
(false、0、任意のnilインタフェース値、長さ0の配列、スライス、マップ、文字列)

"noindex"
インデックスを登録しない。
長い文字列とバイトスライスを格納するために使用されるフィールドには必須のオプション。

#必読リスト！
- [公式 Cloud Datastoreのベストプラクティス](https://cloud.google.com/datastore/docs/best-practices?hl=ja)
- [公式 トランザクションについて](https://cloud.google.com/appengine/docs/standard/go/datastore/transactions)
- [公式 強整合性と結果整合性のバランス](https://cloud.google.com/datastore/docs/articles/balancing-strong-and-eventual-consistency-with-google-cloud-datastore/)
- [公式 Datastoreでのデータ整理の考え方](https://cloudplatform-jp.googleblog.com/2015/08/google-cloud-datastore.html)
- [Datastore/Goのデータ設計のコツ](http://pospome.hatenablog.com/entry/2017/02/05/171635)
- [Datastoreのデザインパターン](https://docs.google.com/presentation/d/1ogO88RFNY3guT8p_UsPHzA8lTNGwyjjNDz0-0iFwZlQ/edit)
- [GAE Datastoreについて](http://blog.sojiro.me/blog/2016/08/07/about-gae-datastore/)
- [GAEでハマったこと](https://qiita.com/hogedigo/items/25b4dbefe694dbfc7dbc)
- [Cloud Datastore 活用術](https://sinmetal-slide.appspot.com/2016/gaejanight0607/gaejanight0607.slide#1)
- [GAE/Goのdatastoreの挙動について](https://qiita.com/vvakame/items/f98b5189b302cd729734)
- [DatastoreのプロジェクションクエリをGoon経由で発行してはいけない](https://qiita.com/hnw/items/52addaebe95bb9a16eac)

他にもあればコメントお願いします:thumbsup: