Go
GoogleAppEngine
GoogleCloudPlatform

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

この記事の目的

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

公式ドキュメント

datastore - GoDoc
goon - GoDoc

前提知識

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

ダミーデータ

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

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"`
}
dummy_data.go
import "dataobj"
var Parent1 = &dataobj.Parent{
    ID      : "123",
    Name    : "玉子",
    Age     : "40",
}

頻出関数

Goonインスタンスの生成

定義: NewGoon(r *http.Request) *Goon

g := goon.NewGoon(r)

Keyの取得 方法1

定義: NewKey(c context.Context, kind, stringID string, intID int64, parent *Key) *Key

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

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

Keyの取得 方法2

定義: (g *Goon) Key(src interface{}) *datastore.Key

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

データの保存

定義: (g *Goon) Put(src interface{}) (*datastore.Key, error)

// 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

q := datastore.NewQuery("Parent")

親Keyを条件に付したクエリの生成

定義: (q *Query) Ancestor(ancestor *Key) *Query
トランザクション内でも実行可能

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

クエリに合致するデータを全取得

定義: (g *Goon) GetAll(q *datastore.Query, dst interface{}) ([]*datastore.Key, error)

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

// クエリに基づき全て取得
g.GetAll(q, &childs)
// Keyのみを取得したい場合は、こんな感じ
q := datastore.NewQuery("Parent").KeysOnly()
keys, err := g.GetAll(q, nil)

エンティティの取得

定義: (g *Goon) Get(dst interface{}) error

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

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

エンティティの取得(複数)

定義: (g *Goon) GetMulti(dst interface{}) error
KeysOnlyクエリと相性が良い

// 取得データを受け止める配列を用意。 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
entity name あるいは key nameとも呼ばれる。

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

クエリの取得件数をカウント

定義: (g *Goon) Count(q *datastore.Query) (int, error)
なお、Countは内部でKeysOnlyを付加しているため、クエリ生成時に付加する必要はない

count, err := g.Count(q)

フィルター付きクエリの生成

定義: (q *Query) Filter(filterStr string, value interface{}) *Query

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

ちなみに、sliceに対しても検索は可能 (参考: 拙著「エンティティのsliceプロパティを条件に検索する」)

ソート順を指定したクエリの生成

定義: (q *Query) Order(fieldName string) *Query

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

補足知識

datastoreのタグ名

"" :単にフィールド名を使用
"-":データストアがそのフィールドを無視する

datastoreのオプション

"omitempty"
フィールドの値が空の場合、自動保存される。
(false、0、任意のnilインタフェース値、長さ0の配列、スライス、マップ、文字列)

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

必読リスト!

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