Skip to content
This repository has been archived by the owner on Mar 9, 2019. It is now read-only.

ForEach() #36

Merged
merged 1 commit into from
Feb 16, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions db.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
67 changes: 67 additions & 0 deletions db_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package bolt

import (
"bytes"
"io"
"io/ioutil"
"os"
Expand Down Expand Up @@ -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) {
Expand Down
24 changes: 24 additions & 0 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 19 additions & 0 deletions transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down