読者です 読者をやめる 読者になる 読者になる

The Round

合同会社ナイツオの開発ブログ

静岡でGo言語やりたい人!!→こちら

GAE/Go - Datastoreでの保存、取得、更新、削除

Google App Engine golang

GAE/Go Datastore

どうもマツウラです。

App Engineを使用するにあたって欠かせないDatastore。
Go言語での使用方法はどのようになっているのでしょうか?
公式チュートリアルを参考に簡単に見てゆきます。

参考:Go — Google Developers Entities, Properties, and Keys

PUT, GET, UPDATE, DELETE

まずは基本的なデータストアへのエンティティの保存、取得、更新、削除です。

簡単な例を見てみます。
次の例ではエラー処理を省略しています。

// プロパティ名は大文字で始めるよう注意してください!!
// 小文字で付けたい場合はタグを使用します。
type Employee struct {
  Name string
  Role string
  HireDate time.Time
  Account string
}

func handler(w http.ResponseWriter, r *http.Request) {
  c := appengine.NewContext(r)
  // App Engineの各種サービスとのコミュニケーションに使用します。今回はDatastoreです。

  e1 := Employee{
    Name:     "Joe Citizen",
    Role:     "Manager",
    HireDate: time.Now(),
    Account:  user.Current(c).String(),
  }
  // put
  key := datastore.NewIncompleteKey(c, "employee", nil)
  key, err := datastore.Put(c, key, &e1)

  // get
  var e2 Employee
  err = datastore.Get(c, key, &e2)

  // update
  key, err = datastore.Put(c, key, &e2)

  // delete
  err = datastore.Delete(c, key)
}

エンティティの作成

func Put(c appengine.Context, key *Key, src interface{}) (*Key, error)

Goではデータストアエンティティは構造体の値から作成されます。
その際、構造体のフィールドがエンティティのプロパティとなります。

格納する値を設定し、keyを作成、datastore.Put()に構造体オブジェクトとkeyの両方を渡すことでエンティティを作成します。
Keyの作成方法には二通りあります。

datastore.NewIncompleteKey(c appengine.Context, kind string, parent *Key) *Key
datastore.NewKey(c appengine.Context, kind, stringID string, intID int64, parent *Key) *Key

一意のIDを持った完全なKeyを作成するNewKeyメソッドとは異なり、NewIncompleteKeyメソッドはIDを持たない不完全なKeyを作成して返します。
不完全なKeyはデータストアにエンティティと格納される際に、データストアにより自動的に数値IDが付けられます。

Key名を割り当てて、datastore.NewKeyに文字列IDを設定するには次のように行います。

key := datastore.NewKey(
    c,  // appengine.Context
    "Employee", // 種類
    "asalieri", // 文字列ID; 空であれば非文字列IDとなる
    0,  // 整数値ID; 0の場合、自動的に生成されます。文字列IDが指定されている場合は無視されます
    nil //親キー; nilならば親が無いことになります。
)

データストアが自動的に整数値IDを割り当てるには、空の文字列ID引数を使います。

key := datastore.NewKey(c, "Employee", "", 0, nil)
// こちらと同等です。
// key := datastore.NewIncompleteKey(c, "Employee", nil)

エンティティの取得

func Get(c appengine.Context, key *Key, dst interface{}) error

データストアからエンティティを取得するには、Keyと構造体ポインタをGet()に渡します。
Get()を実行すると、渡した構造体ポインタにエンティティが読み込まれます。

エンティティを取得する際に使用するGetですが、次の注意点があります。

  • dstの一致しなかった構造体フィールドの値は変更されません
  • 一致したスライス型フィールドは値が追加される前にリセットされません

そのため、Getの呼び出しではゼロ値の構造体ポインタを渡すことが推奨されています。

エンティティの更新

更新においても作成同様にPut()を使用します。

エンティティの作成、および更新で使用するdatastore.Put()ですが、データストアAPIは作成と更新を区別しません。
そのため、Keyが既存のエンティティを示す場合は上書きします。
ゼロ値の構造体に更新値だけ設定して上書きすることで、既存のフィールド値を誤って消してしまわないよう注意して下さい。

エンティティの削除

func Delete(c appengine.Context, key *Key) error

削除は、削除するエンティティのkeyをDelete()に渡すことで行います。

バッチ処理

次に複数のエンティティを一度に処理する方法です。

Put(), Get(), Delete()それぞれに対応するdatastore.PutMulti, datastore.GetMulti, datastore.DeleteMultiメソッドが用意されています。
次はこれらメソッドの簡単な使用例です。

// batch put
err := datastore.RunInTransaction(c, func(c appengine.Context) error {
    _, err := datastore.PutMulti(c, []*datastore.Key{k1, k2, k3}, []interface{e1, e2, e3})
    return err
}, nil)

// batch get
entities := make([]*Person, 3)
err := datastore.GetMulti(c, []*datastore.Key{k1, k2, k3}, entities)

// batch delete
err := datastore.DeleteMulti(c, []*datastore.Key{k1, k2, k3})

上記の例でPUTの際にトランザクション内でデータストア操作を実行しています。
これはPutMultiおよびDeleteMultiについては一部のエンティティ操作については成功し、その他が失敗する恐れがあるためです。
エンティティを作成、削除するためそれら全てのエンティティについて操作が完全に成功、または失敗することを保証したい場合はトランザクションを使用して下さい。

func GetMulti(c appengine.Context, key []*Key, dst interface{}) error

func PutMulti(c appengine.Context, key []*Key, src interface{}) ([]*Key, error)

func DeleteMulti(c appengine.Context, key []*Key) error

まず第一回としてGo言語でApp Engine Datastoreへのデータの保存、取得、更新、削除の基本的な動作を確認しました。
次回はクエリについて触れていこうと思います。