2017-05-11 16:39:54 +02:00
|
|
|
// Copyright 2014 Google Inc. All Rights Reserved.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
package datastore_test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"cloud.google.com/go/datastore"
|
|
|
|
"golang.org/x/net/context"
|
|
|
|
"google.golang.org/api/iterator"
|
|
|
|
)
|
|
|
|
|
|
|
|
func ExampleNewClient() {
|
|
|
|
ctx := context.Background()
|
|
|
|
client, err := datastore.NewClient(ctx, "project-id")
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
_ = client // TODO: Use client.
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExampleClient_Get() {
|
|
|
|
ctx := context.Background()
|
|
|
|
client, err := datastore.NewClient(ctx, "project-id")
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
|
|
|
|
type Article struct {
|
|
|
|
Title string
|
|
|
|
Description string
|
|
|
|
Body string `datastore:",noindex"`
|
|
|
|
Author *datastore.Key
|
|
|
|
PublishedAt time.Time
|
|
|
|
}
|
|
|
|
key := datastore.NameKey("Article", "articled1", nil)
|
|
|
|
article := &Article{}
|
|
|
|
if err := client.Get(ctx, key, article); err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExampleClient_Put() {
|
|
|
|
ctx := context.Background()
|
|
|
|
client, err := datastore.NewClient(ctx, "project-id")
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
|
|
|
|
type Article struct {
|
|
|
|
Title string
|
|
|
|
Description string
|
|
|
|
Body string `datastore:",noindex"`
|
|
|
|
Author *datastore.Key
|
|
|
|
PublishedAt time.Time
|
|
|
|
}
|
|
|
|
newKey := datastore.IncompleteKey("Article", nil)
|
|
|
|
_, err = client.Put(ctx, newKey, &Article{
|
|
|
|
Title: "The title of the article",
|
|
|
|
Description: "The description of the article...",
|
|
|
|
Body: "...",
|
|
|
|
Author: datastore.NameKey("Author", "jbd", nil),
|
|
|
|
PublishedAt: time.Now(),
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExampleClient_Put_flatten() {
|
|
|
|
ctx := context.Background()
|
|
|
|
client, err := datastore.NewClient(ctx, "project-id")
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
type Animal struct {
|
|
|
|
Name string
|
|
|
|
Type string
|
|
|
|
Breed string
|
|
|
|
}
|
|
|
|
|
|
|
|
type Human struct {
|
|
|
|
Name string
|
|
|
|
Height int
|
|
|
|
Pet Animal `datastore:",flatten"`
|
|
|
|
}
|
|
|
|
|
|
|
|
newKey := datastore.IncompleteKey("Human", nil)
|
|
|
|
_, err = client.Put(ctx, newKey, &Human{
|
|
|
|
Name: "Susan",
|
|
|
|
Height: 67,
|
|
|
|
Pet: Animal{
|
|
|
|
Name: "Fluffy",
|
|
|
|
Type: "Cat",
|
|
|
|
Breed: "Sphynx",
|
|
|
|
},
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExampleClient_Delete() {
|
|
|
|
ctx := context.Background()
|
|
|
|
client, err := datastore.NewClient(ctx, "project-id")
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
|
|
|
|
key := datastore.NameKey("Article", "articled1", nil)
|
|
|
|
if err := client.Delete(ctx, key); err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExampleClient_DeleteMulti() {
|
|
|
|
ctx := context.Background()
|
|
|
|
client, err := datastore.NewClient(ctx, "project-id")
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
var keys []*datastore.Key
|
|
|
|
for i := 1; i <= 10; i++ {
|
|
|
|
keys = append(keys, datastore.IDKey("Article", int64(i), nil))
|
|
|
|
}
|
|
|
|
if err := client.DeleteMulti(ctx, keys); err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type Post struct {
|
|
|
|
Title string
|
|
|
|
PublishedAt time.Time
|
|
|
|
Comments int
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExampleClient_GetMulti() {
|
|
|
|
ctx := context.Background()
|
|
|
|
client, err := datastore.NewClient(ctx, "project-id")
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
|
|
|
|
keys := []*datastore.Key{
|
|
|
|
datastore.NameKey("Post", "post1", nil),
|
|
|
|
datastore.NameKey("Post", "post2", nil),
|
|
|
|
datastore.NameKey("Post", "post3", nil),
|
|
|
|
}
|
|
|
|
posts := make([]Post, 3)
|
|
|
|
if err := client.GetMulti(ctx, keys, posts); err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExampleClient_PutMulti_slice() {
|
|
|
|
ctx := context.Background()
|
|
|
|
client, err := datastore.NewClient(ctx, "project-id")
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
|
|
|
|
keys := []*datastore.Key{
|
|
|
|
datastore.NameKey("Post", "post1", nil),
|
|
|
|
datastore.NameKey("Post", "post2", nil),
|
|
|
|
}
|
|
|
|
|
|
|
|
// PutMulti with a Post slice.
|
|
|
|
posts := []*Post{
|
|
|
|
{Title: "Post 1", PublishedAt: time.Now()},
|
|
|
|
{Title: "Post 2", PublishedAt: time.Now()},
|
|
|
|
}
|
|
|
|
if _, err := client.PutMulti(ctx, keys, posts); err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExampleClient_PutMulti_interfaceSlice() {
|
|
|
|
ctx := context.Background()
|
|
|
|
client, err := datastore.NewClient(ctx, "project-id")
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
|
|
|
|
keys := []*datastore.Key{
|
|
|
|
datastore.NameKey("Post", "post1", nil),
|
|
|
|
datastore.NameKey("Post", "post2", nil),
|
|
|
|
}
|
|
|
|
|
|
|
|
// PutMulti with an empty interface slice.
|
|
|
|
posts := []interface{}{
|
|
|
|
&Post{Title: "Post 1", PublishedAt: time.Now()},
|
|
|
|
&Post{Title: "Post 2", PublishedAt: time.Now()},
|
|
|
|
}
|
|
|
|
if _, err := client.PutMulti(ctx, keys, posts); err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExampleNewQuery() {
|
|
|
|
// Query for Post entities.
|
|
|
|
q := datastore.NewQuery("Post")
|
|
|
|
_ = q // TODO: Use the query with Client.Run.
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExampleNewQuery_options() {
|
|
|
|
// Query to order the posts by the number of comments they have recieved.
|
|
|
|
q := datastore.NewQuery("Post").Order("-Comments")
|
|
|
|
// Start listing from an offset and limit the results.
|
|
|
|
q = q.Offset(20).Limit(10)
|
|
|
|
_ = q // TODO: Use the query.
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExampleClient_Count() {
|
|
|
|
ctx := context.Background()
|
|
|
|
client, err := datastore.NewClient(ctx, "project-id")
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
// Count the number of the post entities.
|
|
|
|
q := datastore.NewQuery("Post")
|
|
|
|
n, err := client.Count(ctx, q)
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
fmt.Printf("There are %d posts.", n)
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExampleClient_Run() {
|
|
|
|
ctx := context.Background()
|
|
|
|
client, err := datastore.NewClient(ctx, "project-id")
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
// List the posts published since yesterday.
|
|
|
|
yesterday := time.Now().Add(-24 * time.Hour)
|
|
|
|
q := datastore.NewQuery("Post").Filter("PublishedAt >", yesterday)
|
|
|
|
it := client.Run(ctx, q)
|
|
|
|
_ = it // TODO: iterate using Next.
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExampleClient_NewTransaction() {
|
|
|
|
ctx := context.Background()
|
|
|
|
client, err := datastore.NewClient(ctx, "project-id")
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
const retries = 3
|
|
|
|
|
|
|
|
// Increment a counter.
|
|
|
|
// See https://cloud.google.com/appengine/articles/sharding_counters for
|
|
|
|
// a more scalable solution.
|
|
|
|
type Counter struct {
|
|
|
|
Count int
|
|
|
|
}
|
|
|
|
|
|
|
|
key := datastore.NameKey("counter", "CounterA", nil)
|
|
|
|
var tx *datastore.Transaction
|
|
|
|
for i := 0; i < retries; i++ {
|
|
|
|
tx, err = client.NewTransaction(ctx)
|
|
|
|
if err != nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
var c Counter
|
|
|
|
if err = tx.Get(key, &c); err != nil && err != datastore.ErrNoSuchEntity {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
c.Count++
|
|
|
|
if _, err = tx.Put(key, &c); err != nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
// Attempt to commit the transaction. If there's a conflict, try again.
|
|
|
|
if _, err = tx.Commit(); err != datastore.ErrConcurrentTransaction {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExampleClient_RunInTransaction() {
|
|
|
|
ctx := context.Background()
|
|
|
|
client, err := datastore.NewClient(ctx, "project-id")
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
|
|
|
|
// Increment a counter.
|
|
|
|
// See https://cloud.google.com/appengine/articles/sharding_counters for
|
|
|
|
// a more scalable solution.
|
|
|
|
type Counter struct {
|
|
|
|
Count int
|
|
|
|
}
|
|
|
|
|
|
|
|
var count int
|
|
|
|
key := datastore.NameKey("Counter", "singleton", nil)
|
|
|
|
_, err = client.RunInTransaction(ctx, func(tx *datastore.Transaction) error {
|
|
|
|
var x Counter
|
|
|
|
if err := tx.Get(key, &x); err != nil && err != datastore.ErrNoSuchEntity {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
x.Count++
|
|
|
|
if _, err := tx.Put(key, &x); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
count = x.Count
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
// The value of count is only valid once the transaction is successful
|
|
|
|
// (RunInTransaction has returned nil).
|
|
|
|
fmt.Printf("Count=%d\n", count)
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExampleClient_AllocateIDs() {
|
|
|
|
ctx := context.Background()
|
|
|
|
client, err := datastore.NewClient(ctx, "project-id")
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
var keys []*datastore.Key
|
|
|
|
for i := 0; i < 10; i++ {
|
|
|
|
keys = append(keys, datastore.IncompleteKey("Article", nil))
|
|
|
|
}
|
|
|
|
keys, err = client.AllocateIDs(ctx, keys)
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
_ = keys // TODO: Use keys.
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExampleKey_Encode() {
|
|
|
|
key := datastore.IDKey("Article", 1, nil)
|
|
|
|
encoded := key.Encode()
|
|
|
|
fmt.Println(encoded)
|
|
|
|
// Output: EgsKB0FydGljbGUQAQ
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExampleDecodeKey() {
|
|
|
|
const encoded = "EgsKB0FydGljbGUQAQ"
|
|
|
|
key, err := datastore.DecodeKey(encoded)
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
fmt.Println(key)
|
|
|
|
// Output: /Article,1
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExampleIDKey() {
|
|
|
|
// Key with numeric ID.
|
|
|
|
k := datastore.IDKey("Article", 1, nil)
|
|
|
|
_ = k // TODO: Use key.
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExampleNameKey() {
|
|
|
|
// Key with string ID.
|
|
|
|
k := datastore.NameKey("Article", "article8", nil)
|
|
|
|
_ = k // TODO: Use key.
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExampleIncompleteKey() {
|
|
|
|
k := datastore.IncompleteKey("Article", nil)
|
|
|
|
_ = k // TODO: Use incomplete key.
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExampleClient_GetAll() {
|
|
|
|
ctx := context.Background()
|
|
|
|
client, err := datastore.NewClient(ctx, "project-id")
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
var posts []*Post
|
|
|
|
keys, err := client.GetAll(ctx, datastore.NewQuery("Post"), &posts)
|
|
|
|
for i, key := range keys {
|
|
|
|
fmt.Println(key)
|
|
|
|
fmt.Println(posts[i])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-19 16:51:38 +01:00
|
|
|
func ExampleClient_Mutate() {
|
|
|
|
ctx := context.Background()
|
|
|
|
client, err := datastore.NewClient(ctx, "project-id")
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
|
|
|
|
key1 := datastore.NameKey("Post", "post1", nil)
|
|
|
|
key2 := datastore.NameKey("Post", "post2", nil)
|
|
|
|
key3 := datastore.NameKey("Post", "post3", nil)
|
|
|
|
key4 := datastore.NameKey("Post", "post4", nil)
|
|
|
|
|
|
|
|
_, err = client.Mutate(ctx,
|
|
|
|
datastore.NewInsert(key1, Post{Title: "Post 1"}),
|
|
|
|
datastore.NewUpsert(key2, Post{Title: "Post 2"}),
|
|
|
|
datastore.NewUpdate(key3, Post{Title: "Post 3"}),
|
|
|
|
datastore.NewDelete(key4))
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-11 16:39:54 +02:00
|
|
|
func ExampleCommit_Key() {
|
|
|
|
ctx := context.Background()
|
|
|
|
client, err := datastore.NewClient(ctx, "")
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
var pk1, pk2 *datastore.PendingKey
|
|
|
|
// Create two posts in a single transaction.
|
|
|
|
commit, err := client.RunInTransaction(ctx, func(tx *datastore.Transaction) error {
|
|
|
|
var err error
|
|
|
|
pk1, err = tx.Put(datastore.IncompleteKey("Post", nil), &Post{Title: "Post 1", PublishedAt: time.Now()})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
pk2, err = tx.Put(datastore.IncompleteKey("Post", nil), &Post{Title: "Post 2", PublishedAt: time.Now()})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
// Now pk1, pk2 are valid PendingKeys. Let's convert them into real keys
|
|
|
|
// using the Commit object.
|
|
|
|
k1 := commit.Key(pk1)
|
|
|
|
k2 := commit.Key(pk2)
|
|
|
|
fmt.Println(k1, k2)
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExampleIterator_Next() {
|
|
|
|
ctx := context.Background()
|
|
|
|
client, err := datastore.NewClient(ctx, "project-id")
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
it := client.Run(ctx, datastore.NewQuery("Post"))
|
|
|
|
for {
|
|
|
|
var p Post
|
|
|
|
key, err := it.Next(&p)
|
|
|
|
if err == iterator.Done {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
fmt.Println(key, p)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExampleIterator_Cursor() {
|
|
|
|
ctx := context.Background()
|
|
|
|
client, err := datastore.NewClient(ctx, "project-id")
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
it := client.Run(ctx, datastore.NewQuery("Post"))
|
|
|
|
for {
|
|
|
|
var p Post
|
|
|
|
_, err := it.Next(&p)
|
|
|
|
if err == iterator.Done {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
fmt.Println(p)
|
|
|
|
cursor, err := it.Cursor()
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
// When printed, a cursor will display as a string that can be passed
|
|
|
|
// to datastore.NewCursor.
|
|
|
|
fmt.Printf("to resume with this post, use cursor %s\n", cursor)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExampleDecodeCursor() {
|
|
|
|
// See Query.Start for a fuller example of DecodeCursor.
|
|
|
|
// getCursor represents a function that returns a cursor from a previous
|
|
|
|
// iteration in string form.
|
|
|
|
cursorString := getCursor()
|
|
|
|
cursor, err := datastore.DecodeCursor(cursorString)
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
_ = cursor // TODO: Use the cursor with Query.Start or Query.End.
|
|
|
|
}
|
|
|
|
|
|
|
|
func getCursor() string { return "" }
|
|
|
|
|
|
|
|
func ExampleQuery_Start() {
|
|
|
|
// This example demonstrates how to use cursors and Query.Start
|
|
|
|
// to resume an iteration.
|
|
|
|
ctx := context.Background()
|
|
|
|
client, err := datastore.NewClient(ctx, "project-id")
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
// getCursor represents a function that returns a cursor from a previous
|
|
|
|
// iteration in string form.
|
|
|
|
cursorString := getCursor()
|
|
|
|
cursor, err := datastore.DecodeCursor(cursorString)
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
it := client.Run(ctx, datastore.NewQuery("Post").Start(cursor))
|
|
|
|
_ = it // TODO: Use iterator.
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExampleLoadStruct() {
|
|
|
|
type Player struct {
|
|
|
|
User string
|
|
|
|
Score int
|
|
|
|
}
|
|
|
|
// Normally LoadStruct would only be used inside a custom implementation of
|
|
|
|
// PropertyLoadSaver; this is for illustrative purposes only.
|
|
|
|
props := []datastore.Property{
|
|
|
|
{Name: "User", Value: "Alice"},
|
|
|
|
{Name: "Score", Value: int64(97)},
|
|
|
|
}
|
|
|
|
|
|
|
|
var p Player
|
|
|
|
if err := datastore.LoadStruct(&p, props); err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
fmt.Println(p)
|
|
|
|
// Output: {Alice 97}
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExampleSaveStruct() {
|
|
|
|
type Player struct {
|
|
|
|
User string
|
|
|
|
Score int
|
|
|
|
}
|
|
|
|
|
|
|
|
p := &Player{
|
|
|
|
User: "Alice",
|
|
|
|
Score: 97,
|
|
|
|
}
|
|
|
|
props, err := datastore.SaveStruct(p)
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle error.
|
|
|
|
}
|
|
|
|
fmt.Println(props)
|
|
|
|
// TODO(jba): make this output stable: Output: [{User Alice false} {Score 97 false}]
|
|
|
|
}
|