1// Copyright 2014 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package datastore_test
16
17import (
18	"context"
19	"fmt"
20	"log"
21	"time"
22
23	"cloud.google.com/go/datastore"
24	"google.golang.org/api/iterator"
25)
26
27func ExampleNewClient() {
28	ctx := context.Background()
29	client, err := datastore.NewClient(ctx, "project-id")
30	if err != nil {
31		// TODO: Handle error.
32	}
33	_ = client // TODO: Use client.
34}
35
36func ExampleClient_Get() {
37	ctx := context.Background()
38	client, err := datastore.NewClient(ctx, "project-id")
39	if err != nil {
40		// TODO: Handle error.
41	}
42
43	type Article struct {
44		Title       string
45		Description string
46		Body        string `datastore:",noindex"`
47		Author      *datastore.Key
48		PublishedAt time.Time
49	}
50	key := datastore.NameKey("Article", "articled1", nil)
51	article := &Article{}
52	if err := client.Get(ctx, key, article); err != nil {
53		// TODO: Handle error.
54	}
55}
56
57func ExampleClient_Put() {
58	ctx := context.Background()
59	client, err := datastore.NewClient(ctx, "project-id")
60	if err != nil {
61		// TODO: Handle error.
62	}
63
64	type Article struct {
65		Title       string
66		Description string
67		Body        string `datastore:",noindex"`
68		Author      *datastore.Key
69		PublishedAt time.Time
70	}
71	newKey := datastore.IncompleteKey("Article", nil)
72	_, err = client.Put(ctx, newKey, &Article{
73		Title:       "The title of the article",
74		Description: "The description of the article...",
75		Body:        "...",
76		Author:      datastore.NameKey("Author", "jbd", nil),
77		PublishedAt: time.Now(),
78	})
79	if err != nil {
80		// TODO: Handle error.
81	}
82}
83
84func ExampleClient_Put_flatten() {
85	ctx := context.Background()
86	client, err := datastore.NewClient(ctx, "project-id")
87	if err != nil {
88		log.Fatal(err)
89	}
90
91	type Animal struct {
92		Name  string
93		Type  string
94		Breed string
95	}
96
97	type Human struct {
98		Name   string
99		Height int
100		Pet    Animal `datastore:",flatten"`
101	}
102
103	newKey := datastore.IncompleteKey("Human", nil)
104	_, err = client.Put(ctx, newKey, &Human{
105		Name:   "Susan",
106		Height: 67,
107		Pet: Animal{
108			Name:  "Fluffy",
109			Type:  "Cat",
110			Breed: "Sphynx",
111		},
112	})
113	if err != nil {
114		log.Fatal(err)
115	}
116}
117
118func ExampleClient_Delete() {
119	ctx := context.Background()
120	client, err := datastore.NewClient(ctx, "project-id")
121	if err != nil {
122		// TODO: Handle error.
123	}
124
125	key := datastore.NameKey("Article", "articled1", nil)
126	if err := client.Delete(ctx, key); err != nil {
127		// TODO: Handle error.
128	}
129}
130
131func ExampleClient_DeleteMulti() {
132	ctx := context.Background()
133	client, err := datastore.NewClient(ctx, "project-id")
134	if err != nil {
135		// TODO: Handle error.
136	}
137	var keys []*datastore.Key
138	for i := 1; i <= 10; i++ {
139		keys = append(keys, datastore.IDKey("Article", int64(i), nil))
140	}
141	if err := client.DeleteMulti(ctx, keys); err != nil {
142		// TODO: Handle error.
143	}
144}
145
146type Post struct {
147	Title       string
148	PublishedAt time.Time
149	Comments    int
150}
151
152func ExampleClient_GetMulti() {
153	ctx := context.Background()
154	client, err := datastore.NewClient(ctx, "project-id")
155	if err != nil {
156		// TODO: Handle error.
157	}
158
159	keys := []*datastore.Key{
160		datastore.NameKey("Post", "post1", nil),
161		datastore.NameKey("Post", "post2", nil),
162		datastore.NameKey("Post", "post3", nil),
163	}
164	posts := make([]Post, 3)
165	if err := client.GetMulti(ctx, keys, posts); err != nil {
166		// TODO: Handle error.
167	}
168}
169
170func ExampleMultiError() {
171	ctx := context.Background()
172	client, err := datastore.NewClient(ctx, "project-id")
173	if err != nil {
174		// TODO: Handle error.
175	}
176
177	keys := []*datastore.Key{
178		datastore.NameKey("bad-key", "bad-key", nil),
179	}
180	posts := make([]Post, 1)
181	if err := client.GetMulti(ctx, keys, posts); err != nil {
182		if merr, ok := err.(datastore.MultiError); ok {
183			for _, err := range merr {
184				// TODO: Handle error.
185				_ = err
186			}
187		} else {
188			// TODO: Handle error.
189		}
190	}
191}
192
193func ExampleClient_PutMulti_slice() {
194	ctx := context.Background()
195	client, err := datastore.NewClient(ctx, "project-id")
196	if err != nil {
197		// TODO: Handle error.
198	}
199
200	keys := []*datastore.Key{
201		datastore.NameKey("Post", "post1", nil),
202		datastore.NameKey("Post", "post2", nil),
203	}
204
205	// PutMulti with a Post slice.
206	posts := []*Post{
207		{Title: "Post 1", PublishedAt: time.Now()},
208		{Title: "Post 2", PublishedAt: time.Now()},
209	}
210	if _, err := client.PutMulti(ctx, keys, posts); err != nil {
211		// TODO: Handle error.
212	}
213}
214
215func ExampleClient_PutMulti_interfaceSlice() {
216	ctx := context.Background()
217	client, err := datastore.NewClient(ctx, "project-id")
218	if err != nil {
219		// TODO: Handle error.
220	}
221
222	keys := []*datastore.Key{
223		datastore.NameKey("Post", "post1", nil),
224		datastore.NameKey("Post", "post2", nil),
225	}
226
227	// PutMulti with an empty interface slice.
228	posts := []interface{}{
229		&Post{Title: "Post 1", PublishedAt: time.Now()},
230		&Post{Title: "Post 2", PublishedAt: time.Now()},
231	}
232	if _, err := client.PutMulti(ctx, keys, posts); err != nil {
233		// TODO: Handle error.
234	}
235}
236
237func ExampleNewQuery() {
238	// Query for Post entities.
239	q := datastore.NewQuery("Post")
240	_ = q // TODO: Use the query with Client.Run.
241}
242
243func ExampleNewQuery_options() {
244	// Query to order the posts by the number of comments they have received.
245	q := datastore.NewQuery("Post").Order("-Comments")
246	// Start listing from an offset and limit the results.
247	q = q.Offset(20).Limit(10)
248	_ = q // TODO: Use the query.
249}
250
251func ExampleClient_Count() {
252	ctx := context.Background()
253	client, err := datastore.NewClient(ctx, "project-id")
254	if err != nil {
255		// TODO: Handle error.
256	}
257	// Count the number of the post entities.
258	q := datastore.NewQuery("Post")
259	n, err := client.Count(ctx, q)
260	if err != nil {
261		// TODO: Handle error.
262	}
263	fmt.Printf("There are %d posts.", n)
264}
265
266func ExampleClient_Run() {
267	ctx := context.Background()
268	client, err := datastore.NewClient(ctx, "project-id")
269	if err != nil {
270		// TODO: Handle error.
271	}
272	// List the posts published since yesterday.
273	yesterday := time.Now().Add(-24 * time.Hour)
274	q := datastore.NewQuery("Post").Filter("PublishedAt >", yesterday)
275	it := client.Run(ctx, q)
276	_ = it // TODO: iterate using Next.
277}
278
279func ExampleClient_NewTransaction() {
280	ctx := context.Background()
281	client, err := datastore.NewClient(ctx, "project-id")
282	if err != nil {
283		// TODO: Handle error.
284	}
285	const retries = 3
286
287	// Increment a counter.
288	// See https://cloud.google.com/appengine/articles/sharding_counters for
289	// a more scalable solution.
290	type Counter struct {
291		Count int
292	}
293
294	key := datastore.NameKey("counter", "CounterA", nil)
295	var tx *datastore.Transaction
296	for i := 0; i < retries; i++ {
297		tx, err = client.NewTransaction(ctx)
298		if err != nil {
299			break
300		}
301
302		var c Counter
303		if err = tx.Get(key, &c); err != nil && err != datastore.ErrNoSuchEntity {
304			break
305		}
306		c.Count++
307		if _, err = tx.Put(key, &c); err != nil {
308			break
309		}
310
311		// Attempt to commit the transaction. If there's a conflict, try again.
312		if _, err = tx.Commit(); err != datastore.ErrConcurrentTransaction {
313			break
314		}
315	}
316	if err != nil {
317		// TODO: Handle error.
318	}
319}
320
321func ExampleClient_RunInTransaction() {
322	ctx := context.Background()
323	client, err := datastore.NewClient(ctx, "project-id")
324	if err != nil {
325		// TODO: Handle error.
326	}
327
328	// Increment a counter.
329	// See https://cloud.google.com/appengine/articles/sharding_counters for
330	// a more scalable solution.
331	type Counter struct {
332		Count int
333	}
334
335	var count int
336	key := datastore.NameKey("Counter", "singleton", nil)
337	_, err = client.RunInTransaction(ctx, func(tx *datastore.Transaction) error {
338		var x Counter
339		if err := tx.Get(key, &x); err != nil && err != datastore.ErrNoSuchEntity {
340			return err
341		}
342		x.Count++
343		if _, err := tx.Put(key, &x); err != nil {
344			return err
345		}
346		count = x.Count
347		return nil
348	})
349	if err != nil {
350		// TODO: Handle error.
351	}
352	// The value of count is only valid once the transaction is successful
353	// (RunInTransaction has returned nil).
354	fmt.Printf("Count=%d\n", count)
355}
356
357func ExampleClient_AllocateIDs() {
358	ctx := context.Background()
359	client, err := datastore.NewClient(ctx, "project-id")
360	if err != nil {
361		// TODO: Handle error.
362	}
363	var keys []*datastore.Key
364	for i := 0; i < 10; i++ {
365		keys = append(keys, datastore.IncompleteKey("Article", nil))
366	}
367	keys, err = client.AllocateIDs(ctx, keys)
368	if err != nil {
369		// TODO: Handle error.
370	}
371	_ = keys // TODO: Use keys.
372}
373
374func ExampleKey_Encode() {
375	key := datastore.IDKey("Article", 1, nil)
376	encoded := key.Encode()
377	fmt.Println(encoded)
378	// Output: EgsKB0FydGljbGUQAQ
379}
380
381func ExampleDecodeKey() {
382	const encoded = "EgsKB0FydGljbGUQAQ"
383	key, err := datastore.DecodeKey(encoded)
384	if err != nil {
385		// TODO: Handle error.
386	}
387	fmt.Println(key)
388	// Output: /Article,1
389}
390
391func ExampleIDKey() {
392	// Key with numeric ID.
393	k := datastore.IDKey("Article", 1, nil)
394	_ = k // TODO: Use key.
395}
396
397func ExampleNameKey() {
398	// Key with string ID.
399	k := datastore.NameKey("Article", "article8", nil)
400	_ = k // TODO: Use key.
401}
402
403func ExampleIncompleteKey() {
404	k := datastore.IncompleteKey("Article", nil)
405	_ = k // TODO: Use incomplete key.
406}
407
408func ExampleClient_GetAll() {
409	ctx := context.Background()
410	client, err := datastore.NewClient(ctx, "project-id")
411	if err != nil {
412		// TODO: Handle error.
413	}
414	var posts []*Post
415	keys, err := client.GetAll(ctx, datastore.NewQuery("Post"), &posts)
416	if err != nil {
417		// TODO: Handle error.
418	}
419	for i, key := range keys {
420		fmt.Println(key)
421		fmt.Println(posts[i])
422	}
423}
424
425func ExampleClient_Mutate() {
426	ctx := context.Background()
427	client, err := datastore.NewClient(ctx, "project-id")
428	if err != nil {
429		// TODO: Handle error.
430	}
431
432	key1 := datastore.NameKey("Post", "post1", nil)
433	key2 := datastore.NameKey("Post", "post2", nil)
434	key3 := datastore.NameKey("Post", "post3", nil)
435	key4 := datastore.NameKey("Post", "post4", nil)
436
437	_, err = client.Mutate(ctx,
438		datastore.NewInsert(key1, Post{Title: "Post 1"}),
439		datastore.NewUpsert(key2, Post{Title: "Post 2"}),
440		datastore.NewUpdate(key3, Post{Title: "Post 3"}),
441		datastore.NewDelete(key4))
442	if err != nil {
443		// TODO: Handle error.
444	}
445}
446
447func ExampleCommit_Key() {
448	ctx := context.Background()
449	client, err := datastore.NewClient(ctx, "")
450	if err != nil {
451		// TODO: Handle error.
452	}
453	var pk1, pk2 *datastore.PendingKey
454	// Create two posts in a single transaction.
455	commit, err := client.RunInTransaction(ctx, func(tx *datastore.Transaction) error {
456		var err error
457		pk1, err = tx.Put(datastore.IncompleteKey("Post", nil), &Post{Title: "Post 1", PublishedAt: time.Now()})
458		if err != nil {
459			return err
460		}
461		pk2, err = tx.Put(datastore.IncompleteKey("Post", nil), &Post{Title: "Post 2", PublishedAt: time.Now()})
462		if err != nil {
463			return err
464		}
465		return nil
466	})
467	if err != nil {
468		// TODO: Handle error.
469	}
470	// Now pk1, pk2 are valid PendingKeys. Let's convert them into real keys
471	// using the Commit object.
472	k1 := commit.Key(pk1)
473	k2 := commit.Key(pk2)
474	fmt.Println(k1, k2)
475}
476
477func ExampleIterator_Next() {
478	ctx := context.Background()
479	client, err := datastore.NewClient(ctx, "project-id")
480	if err != nil {
481		// TODO: Handle error.
482	}
483	it := client.Run(ctx, datastore.NewQuery("Post"))
484	for {
485		var p Post
486		key, err := it.Next(&p)
487		if err == iterator.Done {
488			break
489		}
490		if err != nil {
491			// TODO: Handle error.
492		}
493		fmt.Println(key, p)
494	}
495}
496
497func ExampleIterator_Cursor() {
498	ctx := context.Background()
499	client, err := datastore.NewClient(ctx, "project-id")
500	if err != nil {
501		// TODO: Handle error.
502	}
503	it := client.Run(ctx, datastore.NewQuery("Post"))
504	for {
505		var p Post
506		_, err := it.Next(&p)
507		if err == iterator.Done {
508			break
509		}
510		if err != nil {
511			// TODO: Handle error.
512		}
513		fmt.Println(p)
514		cursor, err := it.Cursor()
515		if err != nil {
516			// TODO: Handle error.
517		}
518		// When printed, a cursor will display as a string that can be passed
519		// to datastore.DecodeCursor.
520		fmt.Printf("to resume with this post, use cursor %s\n", cursor)
521	}
522}
523
524func ExampleDecodeCursor() {
525	// See Query.Start for a fuller example of DecodeCursor.
526	// getCursor represents a function that returns a cursor from a previous
527	// iteration in string form.
528	cursorString := getCursor()
529	cursor, err := datastore.DecodeCursor(cursorString)
530	if err != nil {
531		// TODO: Handle error.
532	}
533	_ = cursor // TODO: Use the cursor with Query.Start or Query.End.
534}
535
536func getCursor() string { return "" }
537
538func ExampleQuery_Start() {
539	// This example demonstrates how to use cursors and Query.Start
540	// to resume an iteration.
541	ctx := context.Background()
542	client, err := datastore.NewClient(ctx, "project-id")
543	if err != nil {
544		// TODO: Handle error.
545	}
546	// getCursor represents a function that returns a cursor from a previous
547	// iteration in string form.
548	cursorString := getCursor()
549	cursor, err := datastore.DecodeCursor(cursorString)
550	if err != nil {
551		// TODO: Handle error.
552	}
553	it := client.Run(ctx, datastore.NewQuery("Post").Start(cursor))
554	_ = it // TODO: Use iterator.
555}
556
557func ExampleLoadStruct() {
558	type Player struct {
559		User  string
560		Score int
561	}
562	// Normally LoadStruct would only be used inside a custom implementation of
563	// PropertyLoadSaver; this is for illustrative purposes only.
564	props := []datastore.Property{
565		{Name: "User", Value: "Alice"},
566		{Name: "Score", Value: int64(97)},
567	}
568
569	var p Player
570	if err := datastore.LoadStruct(&p, props); err != nil {
571		// TODO: Handle error.
572	}
573	fmt.Println(p)
574	// Output: {Alice 97}
575}
576
577func ExampleSaveStruct() {
578	type Player struct {
579		User  string
580		Score int
581	}
582
583	p := &Player{
584		User:  "Alice",
585		Score: 97,
586	}
587	props, err := datastore.SaveStruct(p)
588	if err != nil {
589		// TODO: Handle error.
590	}
591	fmt.Println(props)
592	// TODO(jba): make this output stable: Output: [{User Alice false} {Score 97 false}]
593}
594