Add Transaction.ForEach().

pull/34/head
Ben Johnson 2014-02-16 13:51:35 -07:00
parent 63e8e474d7
commit b22480fd32
4 changed files with 121 additions and 0 deletions

11
db.go
View File

@ -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) {

View File

@ -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) {

View File

@ -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

View File

@ -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 {