Go の勉強がてら Google Cloud Datastore へ読み書きする簡単なプログラムを作成したため共有する。
Go は Java と比べてリフレクション周りが癖があって扱いにくかった。
あと、オーバーロードがないのが辛い。
ds.go
package ds
import (
"context"
"fmt"
"reflect"
"cloud.google.com/go/datastore"
)
type Entity interface {
EntityKey() *datastore.Key
SetEntityKey(key *datastore.Key)
EntityKind() string
}
func NewClient(ctx context.Context) (*datastore.Client, error) {
return NewClientWithProjectID(ctx, "your project id here.") // FIXME
}
func NewClientWithProjectID(ctx context.Context, projectID string) (*datastore.Client, error) {
client, err := datastore.NewClient(ctx, projectID)
return client, err
}
func GetEntity(ctx context.Context, entity Entity) error {
client, err := NewClient(ctx)
if err != nil {
return err
}
defer client.Close()
return GetEntityWithClient(ctx, client, entity)
}
func GetEntityWithClient(ctx context.Context, client *datastore.Client, entity Entity) error {
entity.EntityKey().Kind = entity.EntityKind()
if err := client.Get(ctx, entity.EntityKey(), entity); err != nil {
return err
}
return nil
}
func GetEntities(ctx context.Context, q *datastore.Query, dst interface{}) error {
client, err := NewClient(ctx)
if err != nil {
return err
}
defer client.Close()
return GetEntitiesWithClient(ctx, client, q, dst)
}
func GetEntitiesWithClient(ctx context.Context, client *datastore.Client, q *datastore.Query, dst interface{}) error {
keys, err := client.GetAll(ctx, q, dst)
if err != nil {
return err
}
slice := reflect.ValueOf(dst).Elem()
if len(keys) != slice.Len() {
return fmt.Errorf("failed to get entities: len(keys)=%d, slice.Len()=%d", len(keys), slice.Len())
}
for i, key := range keys {
entity := slice.Index(i).Addr().Interface().(Entity)
entity.SetEntityKey(key)
}
return nil
}
func PutEntity(ctx context.Context, entity Entity) error {
client, err := NewClient(ctx)
if err != nil {
return err
}
defer client.Close()
return PutEntityWithClient(ctx, client, entity)
}
func PutEntityWithClient(ctx context.Context, client *datastore.Client, entity Entity) error {
entity.EntityKey().Kind = entity.EntityKind()
key, err := client.Put(ctx, entity.EntityKey(), entity)
if err != nil {
return err
}
entity.SetEntityKey(key)
return nil
}
func PutEntities(ctx context.Context, entities interface{}) error {
client, err := NewClient(ctx)
if err != nil {
return err
}
defer client.Close()
return PutEntitiesWithClient(ctx, client, entities)
}
func PutEntitiesWithClient(ctx context.Context, client *datastore.Client, entities interface{}) error {
slice := reflect.ValueOf(entities)
keys := make([]*datastore.Key, 0, slice.Len())
for i := 0; i < slice.Len(); i++ {
entity := slice.Index(i).Addr().Interface().(Entity)
entity.EntityKey().Kind = entity.EntityKind()
keys = append(keys, entity.EntityKey())
}
keys, err := client.PutMulti(ctx, keys, entities)
if err != nil {
return err
}
if len(keys) != slice.Len() {
return fmt.Errorf("failed to put entities: len(keys)=%d, slice.Len()=%d", len(keys), slice.Len())
}
for i, key := range keys {
entity := slice.Index(i).Addr().Interface().(Entity)
entity.SetEntityKey(key)
}
return nil
}
func DeleteEntity(ctx context.Context, key *datastore.Key) error {
client, err := NewClient(ctx)
if err != nil {
return err
}
defer client.Close()
return DeleteEntityWithClient(ctx, client, key)
}
func DeleteEntityWithClient(ctx context.Context, client *datastore.Client, key *datastore.Key) error {
if err := client.Delete(ctx, key); err != nil {
return err
}
return nil
}
func DeleteEntities(ctx context.Context, keys []*datastore.Key) error {
client, err := NewClient(ctx)
if err != nil {
return err
}
defer client.Close()
return DeleteEntitiesWithClient(ctx, client, keys)
}
func DeleteEntitiesWithClient(ctx context.Context, client *datastore.Client, keys []*datastore.Key) error {
if err := client.DeleteMulti(ctx, keys); err != nil {
return err
}
return nil
}