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