aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--db.go11
-rw-r--r--db_test.go67
-rw-r--r--example_test.go24
-rw-r--r--transaction.go19
4 files changed, 121 insertions, 0 deletions
diff --git a/db.go b/db.go
index a519f73..0276d7a 100644
--- a/db.go
+++ b/db.go
@@ -359,6 +359,17 @@ func (db *DB) Do(fn func(*RWTransaction) error) error {
return t.Commit()
}
+// ForEach executes a function for each key/value pair in a bucket.
+// An error is returned if the bucket cannot be found.
+func (db *DB) ForEach(name string, fn func(k, v []byte) error) error {
+ t, err := db.Transaction()
+ if err != nil {
+ return err
+ }
+ defer t.Close()
+ return t.ForEach(name, fn)
+}
+
// Bucket retrieves a reference to a bucket.
// This is typically useful for checking the existence of a bucket.
func (db *DB) Bucket(name string) (*Bucket, error) {
diff --git a/db_test.go b/db_test.go
index 43c119d..ecf2290 100644
--- a/db_test.go
+++ b/db_test.go
@@ -1,6 +1,7 @@
package bolt
import (
+ "bytes"
"io"
"io/ioutil"
"os"
@@ -215,6 +216,72 @@ func TestDBTransactionBlockWhileClosed(t *testing.T) {
})
}
+// Ensure a database can loop over all key/value pairs in a bucket.
+func TestDBForEach(t *testing.T) {
+ withOpenDB(func(db *DB, path string) {
+ db.CreateBucket("widgets")
+ db.Put("widgets", []byte("foo"), []byte("0000"))
+ db.Put("widgets", []byte("baz"), []byte("0001"))
+ db.Put("widgets", []byte("bar"), []byte("0002"))
+
+ var index int
+ err := db.ForEach("widgets", func(k, v []byte) error {
+ switch index {
+ case 0:
+ assert.Equal(t, k, []byte("bar"))
+ assert.Equal(t, v, []byte("0002"))
+ case 1:
+ assert.Equal(t, k, []byte("baz"))
+ assert.Equal(t, v, []byte("0001"))
+ case 2:
+ assert.Equal(t, k, []byte("foo"))
+ assert.Equal(t, v, []byte("0000"))
+ }
+ index++
+ return nil
+ })
+ assert.NoError(t, err)
+ assert.Equal(t, index, 3)
+ })
+}
+
+// Ensure a database can stop iteration early.
+func TestDBForEachShortCircuit(t *testing.T) {
+ withOpenDB(func(db *DB, path string) {
+ db.CreateBucket("widgets")
+ db.Put("widgets", []byte("bar"), []byte("0000"))
+ db.Put("widgets", []byte("baz"), []byte("0000"))
+ db.Put("widgets", []byte("foo"), []byte("0000"))
+
+ var index int
+ err := db.ForEach("widgets", func(k, v []byte) error {
+ index++
+ if bytes.Equal(k, []byte("baz")) {
+ return &Error{"marker", nil}
+ }
+ return nil
+ })
+ assert.Equal(t, err, &Error{"marker", nil})
+ assert.Equal(t, index, 2)
+ })
+}
+
+// Ensure a database returns an error when trying to attempt a for each on a missing bucket.
+func TestDBForEachBucketNotFound(t *testing.T) {
+ withOpenDB(func(db *DB, path string) {
+ err := db.ForEach("widgets", func(k, v []byte) error { return nil })
+ assert.Equal(t, err, ErrBucketNotFound)
+ })
+}
+
+// Ensure a closed database returns an error when executing a for each.
+func TestDBForEachWhileClosed(t *testing.T) {
+ withDB(func(db *DB, path string) {
+ err := db.ForEach("widgets", func(k, v []byte) error { return nil })
+ assert.Equal(t, err, ErrDatabaseNotOpen)
+ })
+}
+
// Ensure a closed database returns an error when finding a bucket.
func TestDBBucketWhileClosed(t *testing.T) {
withDB(func(db *DB, path string) {
diff --git a/example_test.go b/example_test.go
index 655e283..542109c 100644
--- a/example_test.go
+++ b/example_test.go
@@ -87,6 +87,30 @@ func ExampleDB_Do() {
// The value of 'foo' is: bar
}
+func ExampleDB_ForEach() {
+ // Open the database.
+ var db DB
+ db.Open("/tmp/bolt/db_foreach.db", 0666)
+ defer db.Close()
+
+ // Insert data into a bucket.
+ db.CreateBucket("animals")
+ db.Put("animals", []byte("dog"), []byte("fun"))
+ db.Put("animals", []byte("cat"), []byte("lame"))
+ db.Put("animals", []byte("liger"), []byte("awesome"))
+
+ // Iterate over items in sorted key order.
+ db.ForEach("animals", func(k, v []byte) error {
+ fmt.Printf("A %s is %s.\n", string(k), string(v))
+ return nil
+ })
+
+ // Output:
+ // A cat is lame.
+ // A dog is fun.
+ // A liger is awesome.
+}
+
func ExampleRWTransaction() {
// Open the database.
var db DB
diff --git a/transaction.go b/transaction.go
index 0c9da7b..54aae8c 100644
--- a/transaction.go
+++ b/transaction.go
@@ -93,6 +93,25 @@ func (t *Transaction) Get(name string, key []byte) (value []byte, err error) {
return c.Get(key), nil
}
+// ForEach executes a function for each key/value pair in a bucket.
+// An error is returned if the bucket cannot be found.
+func (t *Transaction) ForEach(name string, fn func(k, v []byte) error) error {
+ // Open a cursor on the bucket.
+ c, err := t.Cursor(name)
+ if err != nil {
+ return err
+ }
+
+ // Iterate over each key/value pair in the bucket.
+ for k, v := c.First(); k != nil; k, v = c.Next() {
+ if err := fn(k, v); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
// page returns a reference to the page with a given id.
// If page has been written to then a temporary bufferred page is returned.
func (t *Transaction) page(id pgid) *page {