github.com/bhojpur/cache@v0.0.4/pkg/memory/cursor_test.go (about) 1 package memory_test 2 3 // Copyright (c) 2018 Bhojpur Consulting Private Limited, India. All rights reserved. 4 5 // Permission is hereby granted, free of charge, to any person obtaining a copy 6 // of this software and associated documentation files (the "Software"), to deal 7 // in the Software without restriction, including without limitation the rights 8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 // copies of the Software, and to permit persons to whom the Software is 10 // furnished to do so, subject to the following conditions: 11 12 // The above copyright notice and this permission notice shall be included in 13 // all copies or substantial portions of the Software. 14 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 // THE SOFTWARE. 22 23 import ( 24 "bytes" 25 "encoding/binary" 26 "fmt" 27 "log" 28 "os" 29 "reflect" 30 "sort" 31 "testing" 32 "testing/quick" 33 34 memcache "github.com/bhojpur/cache/pkg/memory" 35 ) 36 37 // Ensure that a cursor can return a reference to the bucket that created it. 38 func TestCursor_Bucket(t *testing.T) { 39 db := MustOpenDB() 40 defer db.MustClose() 41 if err := db.Update(func(tx *memcache.Tx) error { 42 b, err := tx.CreateBucket([]byte("widgets")) 43 if err != nil { 44 t.Fatal(err) 45 } 46 if cb := b.Cursor().Bucket(); !reflect.DeepEqual(cb, b) { 47 t.Fatal("cursor bucket mismatch") 48 } 49 return nil 50 }); err != nil { 51 t.Fatal(err) 52 } 53 } 54 55 // Ensure that a Tx cursor can seek to the appropriate keys. 56 func TestCursor_Seek(t *testing.T) { 57 db := MustOpenDB() 58 defer db.MustClose() 59 if err := db.Update(func(tx *memcache.Tx) error { 60 b, err := tx.CreateBucket([]byte("widgets")) 61 if err != nil { 62 t.Fatal(err) 63 } 64 if err := b.Put([]byte("foo"), []byte("0001")); err != nil { 65 t.Fatal(err) 66 } 67 if err := b.Put([]byte("bar"), []byte("0002")); err != nil { 68 t.Fatal(err) 69 } 70 if err := b.Put([]byte("baz"), []byte("0003")); err != nil { 71 t.Fatal(err) 72 } 73 74 if _, err := b.CreateBucket([]byte("bkt")); err != nil { 75 t.Fatal(err) 76 } 77 return nil 78 }); err != nil { 79 t.Fatal(err) 80 } 81 82 if err := db.View(func(tx *memcache.Tx) error { 83 c := tx.Bucket([]byte("widgets")).Cursor() 84 85 // Exact match should go to the key. 86 if k, v := c.Seek([]byte("bar")); !bytes.Equal(k, []byte("bar")) { 87 t.Fatalf("unexpected key: %v", k) 88 } else if !bytes.Equal(v, []byte("0002")) { 89 t.Fatalf("unexpected value: %v", v) 90 } 91 92 // Inexact match should go to the next key. 93 if k, v := c.Seek([]byte("bas")); !bytes.Equal(k, []byte("baz")) { 94 t.Fatalf("unexpected key: %v", k) 95 } else if !bytes.Equal(v, []byte("0003")) { 96 t.Fatalf("unexpected value: %v", v) 97 } 98 99 // Low key should go to the first key. 100 if k, v := c.Seek([]byte("")); !bytes.Equal(k, []byte("bar")) { 101 t.Fatalf("unexpected key: %v", k) 102 } else if !bytes.Equal(v, []byte("0002")) { 103 t.Fatalf("unexpected value: %v", v) 104 } 105 106 // High key should return no key. 107 if k, v := c.Seek([]byte("zzz")); k != nil { 108 t.Fatalf("expected nil key: %v", k) 109 } else if v != nil { 110 t.Fatalf("expected nil value: %v", v) 111 } 112 113 // Buckets should return their key but no value. 114 if k, v := c.Seek([]byte("bkt")); !bytes.Equal(k, []byte("bkt")) { 115 t.Fatalf("unexpected key: %v", k) 116 } else if v != nil { 117 t.Fatalf("expected nil value: %v", v) 118 } 119 120 return nil 121 }); err != nil { 122 t.Fatal(err) 123 } 124 } 125 126 func TestCursor_Delete(t *testing.T) { 127 db := MustOpenDB() 128 defer db.MustClose() 129 130 const count = 1000 131 132 // Insert every other key between 0 and $count. 133 if err := db.Update(func(tx *memcache.Tx) error { 134 b, err := tx.CreateBucket([]byte("widgets")) 135 if err != nil { 136 t.Fatal(err) 137 } 138 for i := 0; i < count; i += 1 { 139 k := make([]byte, 8) 140 binary.BigEndian.PutUint64(k, uint64(i)) 141 if err := b.Put(k, make([]byte, 100)); err != nil { 142 t.Fatal(err) 143 } 144 } 145 if _, err := b.CreateBucket([]byte("sub")); err != nil { 146 t.Fatal(err) 147 } 148 return nil 149 }); err != nil { 150 t.Fatal(err) 151 } 152 153 if err := db.Update(func(tx *memcache.Tx) error { 154 c := tx.Bucket([]byte("widgets")).Cursor() 155 bound := make([]byte, 8) 156 binary.BigEndian.PutUint64(bound, uint64(count/2)) 157 for key, _ := c.First(); bytes.Compare(key, bound) < 0; key, _ = c.Next() { 158 if err := c.Delete(); err != nil { 159 t.Fatal(err) 160 } 161 } 162 163 c.Seek([]byte("sub")) 164 if err := c.Delete(); err != memcache.ErrIncompatibleValue { 165 t.Fatalf("unexpected error: %s", err) 166 } 167 168 return nil 169 }); err != nil { 170 t.Fatal(err) 171 } 172 173 if err := db.View(func(tx *memcache.Tx) error { 174 stats := tx.Bucket([]byte("widgets")).Stats() 175 if stats.KeyN != count/2+1 { 176 t.Fatalf("unexpected KeyN: %d", stats.KeyN) 177 } 178 return nil 179 }); err != nil { 180 t.Fatal(err) 181 } 182 } 183 184 // Ensure that a Tx cursor can seek to the appropriate keys when there are a 185 // large number of keys. This test also checks that seek will always move 186 // forward to the next key. 187 func TestCursor_Seek_Large(t *testing.T) { 188 db := MustOpenDB() 189 defer db.MustClose() 190 191 var count = 10000 192 193 // Insert every other key between 0 and $count. 194 if err := db.Update(func(tx *memcache.Tx) error { 195 b, err := tx.CreateBucket([]byte("widgets")) 196 if err != nil { 197 t.Fatal(err) 198 } 199 200 for i := 0; i < count; i += 100 { 201 for j := i; j < i+100; j += 2 { 202 k := make([]byte, 8) 203 binary.BigEndian.PutUint64(k, uint64(j)) 204 if err := b.Put(k, make([]byte, 100)); err != nil { 205 t.Fatal(err) 206 } 207 } 208 } 209 return nil 210 }); err != nil { 211 t.Fatal(err) 212 } 213 214 if err := db.View(func(tx *memcache.Tx) error { 215 c := tx.Bucket([]byte("widgets")).Cursor() 216 for i := 0; i < count; i++ { 217 seek := make([]byte, 8) 218 binary.BigEndian.PutUint64(seek, uint64(i)) 219 220 k, _ := c.Seek(seek) 221 222 // The last seek is beyond the end of the the range so 223 // it should return nil. 224 if i == count-1 { 225 if k != nil { 226 t.Fatal("expected nil key") 227 } 228 continue 229 } 230 231 // Otherwise we should seek to the exact key or the next key. 232 num := binary.BigEndian.Uint64(k) 233 if i%2 == 0 { 234 if num != uint64(i) { 235 t.Fatalf("unexpected num: %d", num) 236 } 237 } else { 238 if num != uint64(i+1) { 239 t.Fatalf("unexpected num: %d", num) 240 } 241 } 242 } 243 244 return nil 245 }); err != nil { 246 t.Fatal(err) 247 } 248 } 249 250 // Ensure that a cursor can iterate over an empty bucket without error. 251 func TestCursor_EmptyBucket(t *testing.T) { 252 db := MustOpenDB() 253 defer db.MustClose() 254 if err := db.Update(func(tx *memcache.Tx) error { 255 _, err := tx.CreateBucket([]byte("widgets")) 256 return err 257 }); err != nil { 258 t.Fatal(err) 259 } 260 261 if err := db.View(func(tx *memcache.Tx) error { 262 c := tx.Bucket([]byte("widgets")).Cursor() 263 k, v := c.First() 264 if k != nil { 265 t.Fatalf("unexpected key: %v", k) 266 } else if v != nil { 267 t.Fatalf("unexpected value: %v", v) 268 } 269 return nil 270 }); err != nil { 271 t.Fatal(err) 272 } 273 } 274 275 // Ensure that a Tx cursor can reverse iterate over an empty bucket without error. 276 func TestCursor_EmptyBucketReverse(t *testing.T) { 277 db := MustOpenDB() 278 defer db.MustClose() 279 280 if err := db.Update(func(tx *memcache.Tx) error { 281 _, err := tx.CreateBucket([]byte("widgets")) 282 return err 283 }); err != nil { 284 t.Fatal(err) 285 } 286 if err := db.View(func(tx *memcache.Tx) error { 287 c := tx.Bucket([]byte("widgets")).Cursor() 288 k, v := c.Last() 289 if k != nil { 290 t.Fatalf("unexpected key: %v", k) 291 } else if v != nil { 292 t.Fatalf("unexpected value: %v", v) 293 } 294 return nil 295 }); err != nil { 296 t.Fatal(err) 297 } 298 } 299 300 // Ensure that a Tx cursor can iterate over a single root with a couple elements. 301 func TestCursor_Iterate_Leaf(t *testing.T) { 302 db := MustOpenDB() 303 defer db.MustClose() 304 305 if err := db.Update(func(tx *memcache.Tx) error { 306 b, err := tx.CreateBucket([]byte("widgets")) 307 if err != nil { 308 t.Fatal(err) 309 } 310 if err := b.Put([]byte("baz"), []byte{}); err != nil { 311 t.Fatal(err) 312 } 313 if err := b.Put([]byte("foo"), []byte{0}); err != nil { 314 t.Fatal(err) 315 } 316 if err := b.Put([]byte("bar"), []byte{1}); err != nil { 317 t.Fatal(err) 318 } 319 return nil 320 }); err != nil { 321 t.Fatal(err) 322 } 323 tx, err := db.Begin(false) 324 if err != nil { 325 t.Fatal(err) 326 } 327 defer func() { _ = tx.Rollback() }() 328 329 c := tx.Bucket([]byte("widgets")).Cursor() 330 331 k, v := c.First() 332 if !bytes.Equal(k, []byte("bar")) { 333 t.Fatalf("unexpected key: %v", k) 334 } else if !bytes.Equal(v, []byte{1}) { 335 t.Fatalf("unexpected value: %v", v) 336 } 337 338 k, v = c.Next() 339 if !bytes.Equal(k, []byte("baz")) { 340 t.Fatalf("unexpected key: %v", k) 341 } else if !bytes.Equal(v, []byte{}) { 342 t.Fatalf("unexpected value: %v", v) 343 } 344 345 k, v = c.Next() 346 if !bytes.Equal(k, []byte("foo")) { 347 t.Fatalf("unexpected key: %v", k) 348 } else if !bytes.Equal(v, []byte{0}) { 349 t.Fatalf("unexpected value: %v", v) 350 } 351 352 k, v = c.Next() 353 if k != nil { 354 t.Fatalf("expected nil key: %v", k) 355 } else if v != nil { 356 t.Fatalf("expected nil value: %v", v) 357 } 358 359 k, v = c.Next() 360 if k != nil { 361 t.Fatalf("expected nil key: %v", k) 362 } else if v != nil { 363 t.Fatalf("expected nil value: %v", v) 364 } 365 366 if err := tx.Rollback(); err != nil { 367 t.Fatal(err) 368 } 369 } 370 371 // Ensure that a Tx cursor can iterate in reverse over a single root with a couple elements. 372 func TestCursor_LeafRootReverse(t *testing.T) { 373 db := MustOpenDB() 374 defer db.MustClose() 375 376 if err := db.Update(func(tx *memcache.Tx) error { 377 b, err := tx.CreateBucket([]byte("widgets")) 378 if err != nil { 379 t.Fatal(err) 380 } 381 if err := b.Put([]byte("baz"), []byte{}); err != nil { 382 t.Fatal(err) 383 } 384 if err := b.Put([]byte("foo"), []byte{0}); err != nil { 385 t.Fatal(err) 386 } 387 if err := b.Put([]byte("bar"), []byte{1}); err != nil { 388 t.Fatal(err) 389 } 390 return nil 391 }); err != nil { 392 t.Fatal(err) 393 } 394 tx, err := db.Begin(false) 395 if err != nil { 396 t.Fatal(err) 397 } 398 c := tx.Bucket([]byte("widgets")).Cursor() 399 400 if k, v := c.Last(); !bytes.Equal(k, []byte("foo")) { 401 t.Fatalf("unexpected key: %v", k) 402 } else if !bytes.Equal(v, []byte{0}) { 403 t.Fatalf("unexpected value: %v", v) 404 } 405 406 if k, v := c.Prev(); !bytes.Equal(k, []byte("baz")) { 407 t.Fatalf("unexpected key: %v", k) 408 } else if !bytes.Equal(v, []byte{}) { 409 t.Fatalf("unexpected value: %v", v) 410 } 411 412 if k, v := c.Prev(); !bytes.Equal(k, []byte("bar")) { 413 t.Fatalf("unexpected key: %v", k) 414 } else if !bytes.Equal(v, []byte{1}) { 415 t.Fatalf("unexpected value: %v", v) 416 } 417 418 if k, v := c.Prev(); k != nil { 419 t.Fatalf("expected nil key: %v", k) 420 } else if v != nil { 421 t.Fatalf("expected nil value: %v", v) 422 } 423 424 if k, v := c.Prev(); k != nil { 425 t.Fatalf("expected nil key: %v", k) 426 } else if v != nil { 427 t.Fatalf("expected nil value: %v", v) 428 } 429 430 if err := tx.Rollback(); err != nil { 431 t.Fatal(err) 432 } 433 } 434 435 // Ensure that a Tx cursor can restart from the beginning. 436 func TestCursor_Restart(t *testing.T) { 437 db := MustOpenDB() 438 defer db.MustClose() 439 440 if err := db.Update(func(tx *memcache.Tx) error { 441 b, err := tx.CreateBucket([]byte("widgets")) 442 if err != nil { 443 t.Fatal(err) 444 } 445 if err := b.Put([]byte("bar"), []byte{}); err != nil { 446 t.Fatal(err) 447 } 448 if err := b.Put([]byte("foo"), []byte{}); err != nil { 449 t.Fatal(err) 450 } 451 return nil 452 }); err != nil { 453 t.Fatal(err) 454 } 455 456 tx, err := db.Begin(false) 457 if err != nil { 458 t.Fatal(err) 459 } 460 c := tx.Bucket([]byte("widgets")).Cursor() 461 462 if k, _ := c.First(); !bytes.Equal(k, []byte("bar")) { 463 t.Fatalf("unexpected key: %v", k) 464 } 465 if k, _ := c.Next(); !bytes.Equal(k, []byte("foo")) { 466 t.Fatalf("unexpected key: %v", k) 467 } 468 469 if k, _ := c.First(); !bytes.Equal(k, []byte("bar")) { 470 t.Fatalf("unexpected key: %v", k) 471 } 472 if k, _ := c.Next(); !bytes.Equal(k, []byte("foo")) { 473 t.Fatalf("unexpected key: %v", k) 474 } 475 476 if err := tx.Rollback(); err != nil { 477 t.Fatal(err) 478 } 479 } 480 481 // Ensure that a cursor can skip over empty pages that have been deleted. 482 func TestCursor_First_EmptyPages(t *testing.T) { 483 db := MustOpenDB() 484 defer db.MustClose() 485 486 // Create 1000 keys in the "widgets" bucket. 487 if err := db.Update(func(tx *memcache.Tx) error { 488 b, err := tx.CreateBucket([]byte("widgets")) 489 if err != nil { 490 t.Fatal(err) 491 } 492 493 for i := 0; i < 1000; i++ { 494 if err := b.Put(u64tob(uint64(i)), []byte{}); err != nil { 495 t.Fatal(err) 496 } 497 } 498 499 return nil 500 }); err != nil { 501 t.Fatal(err) 502 } 503 504 // Delete half the keys and then try to iterate. 505 if err := db.Update(func(tx *memcache.Tx) error { 506 b := tx.Bucket([]byte("widgets")) 507 for i := 0; i < 600; i++ { 508 if err := b.Delete(u64tob(uint64(i))); err != nil { 509 t.Fatal(err) 510 } 511 } 512 513 c := b.Cursor() 514 var n int 515 for k, _ := c.First(); k != nil; k, _ = c.Next() { 516 n++ 517 } 518 if n != 400 { 519 t.Fatalf("unexpected key count: %d", n) 520 } 521 522 return nil 523 }); err != nil { 524 t.Fatal(err) 525 } 526 } 527 528 // Ensure that a Tx can iterate over all elements in a bucket. 529 func TestCursor_QuickCheck(t *testing.T) { 530 f := func(items testdata) bool { 531 db := MustOpenDB() 532 defer db.MustClose() 533 534 // Bulk insert all values. 535 tx, err := db.Begin(true) 536 if err != nil { 537 t.Fatal(err) 538 } 539 b, err := tx.CreateBucket([]byte("widgets")) 540 if err != nil { 541 t.Fatal(err) 542 } 543 for _, item := range items { 544 if err := b.Put(item.Key, item.Value); err != nil { 545 t.Fatal(err) 546 } 547 } 548 if err := tx.Commit(); err != nil { 549 t.Fatal(err) 550 } 551 552 // Sort test data. 553 sort.Sort(items) 554 555 // Iterate over all items and check consistency. 556 var index = 0 557 tx, err = db.Begin(false) 558 if err != nil { 559 t.Fatal(err) 560 } 561 562 c := tx.Bucket([]byte("widgets")).Cursor() 563 for k, v := c.First(); k != nil && index < len(items); k, v = c.Next() { 564 if !bytes.Equal(k, items[index].Key) { 565 t.Fatalf("unexpected key: %v", k) 566 } else if !bytes.Equal(v, items[index].Value) { 567 t.Fatalf("unexpected value: %v", v) 568 } 569 index++ 570 } 571 if len(items) != index { 572 t.Fatalf("unexpected item count: %v, expected %v", len(items), index) 573 } 574 575 if err := tx.Rollback(); err != nil { 576 t.Fatal(err) 577 } 578 579 return true 580 } 581 if err := quick.Check(f, qconfig()); err != nil { 582 t.Error(err) 583 } 584 } 585 586 // Ensure that a transaction can iterate over all elements in a bucket in reverse. 587 func TestCursor_QuickCheck_Reverse(t *testing.T) { 588 f := func(items testdata) bool { 589 db := MustOpenDB() 590 defer db.MustClose() 591 592 // Bulk insert all values. 593 tx, err := db.Begin(true) 594 if err != nil { 595 t.Fatal(err) 596 } 597 b, err := tx.CreateBucket([]byte("widgets")) 598 if err != nil { 599 t.Fatal(err) 600 } 601 for _, item := range items { 602 if err := b.Put(item.Key, item.Value); err != nil { 603 t.Fatal(err) 604 } 605 } 606 if err := tx.Commit(); err != nil { 607 t.Fatal(err) 608 } 609 610 // Sort test data. 611 sort.Sort(revtestdata(items)) 612 613 // Iterate over all items and check consistency. 614 var index = 0 615 tx, err = db.Begin(false) 616 if err != nil { 617 t.Fatal(err) 618 } 619 c := tx.Bucket([]byte("widgets")).Cursor() 620 for k, v := c.Last(); k != nil && index < len(items); k, v = c.Prev() { 621 if !bytes.Equal(k, items[index].Key) { 622 t.Fatalf("unexpected key: %v", k) 623 } else if !bytes.Equal(v, items[index].Value) { 624 t.Fatalf("unexpected value: %v", v) 625 } 626 index++ 627 } 628 if len(items) != index { 629 t.Fatalf("unexpected item count: %v, expected %v", len(items), index) 630 } 631 632 if err := tx.Rollback(); err != nil { 633 t.Fatal(err) 634 } 635 636 return true 637 } 638 if err := quick.Check(f, qconfig()); err != nil { 639 t.Error(err) 640 } 641 } 642 643 // Ensure that a Tx cursor can iterate over subbuckets. 644 func TestCursor_QuickCheck_BucketsOnly(t *testing.T) { 645 db := MustOpenDB() 646 defer db.MustClose() 647 648 if err := db.Update(func(tx *memcache.Tx) error { 649 b, err := tx.CreateBucket([]byte("widgets")) 650 if err != nil { 651 t.Fatal(err) 652 } 653 if _, err := b.CreateBucket([]byte("foo")); err != nil { 654 t.Fatal(err) 655 } 656 if _, err := b.CreateBucket([]byte("bar")); err != nil { 657 t.Fatal(err) 658 } 659 if _, err := b.CreateBucket([]byte("baz")); err != nil { 660 t.Fatal(err) 661 } 662 return nil 663 }); err != nil { 664 t.Fatal(err) 665 } 666 667 if err := db.View(func(tx *memcache.Tx) error { 668 var names []string 669 c := tx.Bucket([]byte("widgets")).Cursor() 670 for k, v := c.First(); k != nil; k, v = c.Next() { 671 names = append(names, string(k)) 672 if v != nil { 673 t.Fatalf("unexpected value: %v", v) 674 } 675 } 676 if !reflect.DeepEqual(names, []string{"bar", "baz", "foo"}) { 677 t.Fatalf("unexpected names: %+v", names) 678 } 679 return nil 680 }); err != nil { 681 t.Fatal(err) 682 } 683 } 684 685 // Ensure that a Tx cursor can reverse iterate over subbuckets. 686 func TestCursor_QuickCheck_BucketsOnly_Reverse(t *testing.T) { 687 db := MustOpenDB() 688 defer db.MustClose() 689 690 if err := db.Update(func(tx *memcache.Tx) error { 691 b, err := tx.CreateBucket([]byte("widgets")) 692 if err != nil { 693 t.Fatal(err) 694 } 695 if _, err := b.CreateBucket([]byte("foo")); err != nil { 696 t.Fatal(err) 697 } 698 if _, err := b.CreateBucket([]byte("bar")); err != nil { 699 t.Fatal(err) 700 } 701 if _, err := b.CreateBucket([]byte("baz")); err != nil { 702 t.Fatal(err) 703 } 704 return nil 705 }); err != nil { 706 t.Fatal(err) 707 } 708 709 if err := db.View(func(tx *memcache.Tx) error { 710 var names []string 711 c := tx.Bucket([]byte("widgets")).Cursor() 712 for k, v := c.Last(); k != nil; k, v = c.Prev() { 713 names = append(names, string(k)) 714 if v != nil { 715 t.Fatalf("unexpected value: %v", v) 716 } 717 } 718 if !reflect.DeepEqual(names, []string{"foo", "baz", "bar"}) { 719 t.Fatalf("unexpected names: %+v", names) 720 } 721 return nil 722 }); err != nil { 723 t.Fatal(err) 724 } 725 } 726 727 func ExampleCursor() { 728 // Open the database. 729 db, err := memcache.Open(tempfile(), 0666, nil) 730 if err != nil { 731 log.Fatal(err) 732 } 733 defer os.Remove(db.Path()) 734 735 // Start a read-write transaction. 736 if err := db.Update(func(tx *memcache.Tx) error { 737 // Create a new bucket. 738 b, err := tx.CreateBucket([]byte("animals")) 739 if err != nil { 740 return err 741 } 742 743 // Insert data into a bucket. 744 if err := b.Put([]byte("dog"), []byte("fun")); err != nil { 745 log.Fatal(err) 746 } 747 if err := b.Put([]byte("cat"), []byte("lame")); err != nil { 748 log.Fatal(err) 749 } 750 if err := b.Put([]byte("liger"), []byte("awesome")); err != nil { 751 log.Fatal(err) 752 } 753 754 // Create a cursor for iteration. 755 c := b.Cursor() 756 757 // Iterate over items in sorted key order. This starts from the 758 // first key/value pair and updates the k/v variables to the 759 // next key/value on each iteration. 760 // 761 // The loop finishes at the end of the cursor when a nil key is returned. 762 for k, v := c.First(); k != nil; k, v = c.Next() { 763 fmt.Printf("A %s is %s.\n", k, v) 764 } 765 766 return nil 767 }); err != nil { 768 log.Fatal(err) 769 } 770 771 if err := db.Close(); err != nil { 772 log.Fatal(err) 773 } 774 775 // Output: 776 // A cat is lame. 777 // A dog is fun. 778 // A liger is awesome. 779 } 780 781 func ExampleCursor_reverse() { 782 // Open the database. 783 db, err := memcache.Open(tempfile(), 0666, nil) 784 if err != nil { 785 log.Fatal(err) 786 } 787 defer os.Remove(db.Path()) 788 789 // Start a read-write transaction. 790 if err := db.Update(func(tx *memcache.Tx) error { 791 // Create a new bucket. 792 b, err := tx.CreateBucket([]byte("animals")) 793 if err != nil { 794 return err 795 } 796 797 // Insert data into a bucket. 798 if err := b.Put([]byte("dog"), []byte("fun")); err != nil { 799 log.Fatal(err) 800 } 801 if err := b.Put([]byte("cat"), []byte("lame")); err != nil { 802 log.Fatal(err) 803 } 804 if err := b.Put([]byte("liger"), []byte("awesome")); err != nil { 805 log.Fatal(err) 806 } 807 808 // Create a cursor for iteration. 809 c := b.Cursor() 810 811 // Iterate over items in reverse sorted key order. This starts 812 // from the last key/value pair and updates the k/v variables to 813 // the previous key/value on each iteration. 814 // 815 // The loop finishes at the beginning of the cursor when a nil key 816 // is returned. 817 for k, v := c.Last(); k != nil; k, v = c.Prev() { 818 fmt.Printf("A %s is %s.\n", k, v) 819 } 820 821 return nil 822 }); err != nil { 823 log.Fatal(err) 824 } 825 826 // Close the database to release the file lock. 827 if err := db.Close(); err != nil { 828 log.Fatal(err) 829 } 830 831 // Output: 832 // A liger is awesome. 833 // A dog is fun. 834 // A cat is lame. 835 }