0
0

Merge pull request #256 from pmezard/fix-rangeiterator-seek

Fix rangeiterator seek
This commit is contained in:
Marty Schoch 2015-10-20 14:08:57 -04:00
commit 74780b028e
8 changed files with 151 additions and 50 deletions

View File

@ -29,14 +29,19 @@ type Iterator struct {
func (i *Iterator) updateValid() { func (i *Iterator) updateValid() {
i.valid = (i.key != nil) i.valid = (i.key != nil)
if i.valid && i.prefix != nil { if i.valid {
i.valid = bytes.HasPrefix(i.key, i.prefix) if i.prefix != nil {
} else if i.end != nil { i.valid = bytes.HasPrefix(i.key, i.prefix)
i.valid = bytes.Compare(i.key, i.end) < 0 } else if i.end != nil {
i.valid = bytes.Compare(i.key, i.end) < 0
}
} }
} }
func (i *Iterator) Seek(k []byte) { func (i *Iterator) Seek(k []byte) {
if bytes.Compare(k, i.start) < 0 {
k = i.start
}
i.key, i.val = i.cursor.Seek(k) i.key, i.val = i.cursor.Seek(k)
i.updateValid() i.updateValid()
} }

View File

@ -72,6 +72,12 @@ func TestBoltDBRangeIterator(t *testing.T) {
test.CommonTestRangeIterator(t, s) test.CommonTestRangeIterator(t, s)
} }
func TestBoltDBRangeIteratorSeek(t *testing.T) {
s := open(t, nil)
defer cleanup(t, s)
test.CommonTestRangeIteratorSeek(t, s)
}
func TestBoltDBMerge(t *testing.T) { func TestBoltDBMerge(t *testing.T) {
s := open(t, &test.TestMergeCounter{}) s := open(t, &test.TestMergeCounter{})
defer cleanup(t, s) defer cleanup(t, s)

View File

@ -75,6 +75,12 @@ func TestGoLevelDBRangeIterator(t *testing.T) {
test.CommonTestRangeIterator(t, s) test.CommonTestRangeIterator(t, s)
} }
func TestGoLevelDBRangeIteratorSeek(t *testing.T) {
s := open(t, nil)
defer cleanup(t, s)
test.CommonTestRangeIteratorSeek(t, s)
}
func TestGoLevelDBMerge(t *testing.T) { func TestGoLevelDBMerge(t *testing.T) {
s := open(t, &test.TestMergeCounter{}) s := open(t, &test.TestMergeCounter{})
defer cleanup(t, s) defer cleanup(t, s)

View File

@ -31,10 +31,14 @@ type Iterator struct {
currOk bool currOk bool
prefix []byte prefix []byte
start []byte
end []byte end []byte
} }
func (w *Iterator) Seek(k []byte) { func (w *Iterator) Seek(k []byte) {
if bytes.Compare(k, w.start) < 0 {
k = w.start
}
w.restart(&Item{k: k}) w.restart(&Item{k: k})
} }

View File

@ -46,8 +46,9 @@ func (w *Reader) PrefixIterator(k []byte) store.KVIterator {
func (w *Reader) RangeIterator(start, end []byte) store.KVIterator { func (w *Reader) RangeIterator(start, end []byte) store.KVIterator {
rv := Iterator{ rv := Iterator{
t: w.t, t: w.t,
end: end, start: start,
end: end,
} }
rv.restart(&Item{k: start}) rv.restart(&Item{k: start})
return &rv return &rv

View File

@ -70,6 +70,12 @@ func TestGTreapRangeIterator(t *testing.T) {
test.CommonTestRangeIterator(t, s) test.CommonTestRangeIterator(t, s)
} }
func TestGTreapRangeIteratorSeek(t *testing.T) {
s := open(t, nil)
defer cleanup(t, s)
test.CommonTestRangeIteratorSeek(t, s)
}
func TestGTreapMerge(t *testing.T) { func TestGTreapMerge(t *testing.T) {
s := open(t, &test.TestMergeCounter{}) s := open(t, &test.TestMergeCounter{})
defer cleanup(t, s) defer cleanup(t, s)

View File

@ -59,6 +59,12 @@ func TestMetricsRangeIterator(t *testing.T) {
test.CommonTestRangeIterator(t, s) test.CommonTestRangeIterator(t, s)
} }
func TestMetricsRangeIteratorSeek(t *testing.T) {
s := open(t, nil)
defer cleanup(t, s)
test.CommonTestRangeIteratorSeek(t, s)
}
func TestMetricsMerge(t *testing.T) { func TestMetricsMerge(t *testing.T) {
s := open(t, &test.TestMergeCounter{}) s := open(t, &test.TestMergeCounter{})
defer cleanup(t, s) defer cleanup(t, s)

View File

@ -3,6 +3,7 @@ package test
import ( import (
"bytes" "bytes"
"reflect" "reflect"
"strings"
"testing" "testing"
"github.com/blevesearch/bleve/index/store" "github.com/blevesearch/bleve/index/store"
@ -10,12 +11,39 @@ import (
// tests around the correct behavior of iterators // tests around the correct behavior of iterators
type testRow struct {
key []byte
val []byte
}
func batchWriteRows(s store.KVStore, rows []testRow) error {
// open a writer
writer, err := s.Writer()
if err != nil {
return err
}
// write the data
batch := writer.NewBatch()
for _, row := range rows {
batch.Set(row.key, row.val)
}
err = writer.ExecuteBatch(batch)
if err != nil {
return err
}
// close the writer
err = writer.Close()
if err != nil {
return err
}
return nil
}
func CommonTestPrefixIterator(t *testing.T, s store.KVStore) { func CommonTestPrefixIterator(t *testing.T, s store.KVStore) {
data := []struct { data := []testRow{
key []byte
val []byte
}{
{[]byte("apple"), []byte("val")}, {[]byte("apple"), []byte("val")},
{[]byte("cat1"), []byte("val")}, {[]byte("cat1"), []byte("val")},
{[]byte("cat2"), []byte("val")}, {[]byte("cat2"), []byte("val")},
@ -39,24 +67,7 @@ func CommonTestPrefixIterator(t *testing.T, s store.KVStore) {
[]byte("dog4"), []byte("dog4"),
} }
// open a writer err := batchWriteRows(s, data)
writer, err := s.Writer()
if err != nil {
t.Fatal(err)
}
// write the data
batch := writer.NewBatch()
for _, row := range data {
batch.Set(row.key, row.val)
}
err = writer.ExecuteBatch(batch)
if err != nil {
t.Fatal(err)
}
// close the writer
err = writer.Close()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -122,10 +133,7 @@ func CommonTestPrefixIterator(t *testing.T, s store.KVStore) {
func CommonTestRangeIterator(t *testing.T, s store.KVStore) { func CommonTestRangeIterator(t *testing.T, s store.KVStore) {
data := []struct { data := []testRow{
key []byte
val []byte
}{
{[]byte("a1"), []byte("val")}, {[]byte("a1"), []byte("val")},
{[]byte("b1"), []byte("val")}, {[]byte("b1"), []byte("val")},
{[]byte("b2"), []byte("val")}, {[]byte("b2"), []byte("val")},
@ -153,24 +161,7 @@ func CommonTestRangeIterator(t *testing.T, s store.KVStore) {
} }
} }
// open a writer err := batchWriteRows(s, data)
writer, err := s.Writer()
if err != nil {
t.Fatal(err)
}
// write the data
batch := writer.NewBatch()
for _, row := range data {
batch.Set(row.key, row.val)
}
err = writer.ExecuteBatch(batch)
if err != nil {
t.Fatal(err)
}
// close the writer
err = writer.Close()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -275,3 +266,79 @@ func CommonTestRangeIterator(t *testing.T, s store.KVStore) {
t.Fatal(err) t.Fatal(err)
} }
} }
func CommonTestRangeIteratorSeek(t *testing.T, s store.KVStore) {
data := []testRow{
{[]byte("a1"), []byte("val")},
{[]byte("b1"), []byte("val")},
{[]byte("c1"), []byte("val")},
{[]byte("d1"), []byte("val")},
{[]byte("e1"), []byte("val")},
}
err := batchWriteRows(s, data)
if err != nil {
t.Fatal(err)
}
// open a reader
reader, err := s.Reader()
if err != nil {
t.Fatal(err)
}
// get an iterator on a central subset of the data
start := []byte("b1")
end := []byte("d1")
iter := reader.RangeIterator(start, end)
// seek before, at and after every possible key
targets := [][]byte{}
for _, row := range data {
prefix := string(row.key[:1])
targets = append(targets, []byte(prefix+"0"))
targets = append(targets, []byte(prefix+"1"))
targets = append(targets, []byte(prefix+"2"))
}
for _, target := range targets {
found := []string{}
for iter.Seek(target); iter.Valid(); iter.Next() {
found = append(found, string(iter.Key()))
if len(found) > len(data) {
t.Fatalf("enumerated more than data keys after seeking to %s",
string(target))
}
}
wanted := []string{}
for _, row := range data {
if bytes.Compare(row.key, start) < 0 ||
bytes.Compare(row.key, target) < 0 ||
bytes.Compare(row.key, end) >= 0 {
continue
}
wanted = append(wanted, string(row.key))
}
fs := strings.Join(found, ", ")
ws := strings.Join(wanted, ", ")
if fs != ws {
t.Fatalf("iterating from %s returned [%s] instead of [%s]",
string(target), fs, ws)
}
}
err = iter.Close()
if err != nil {
t.Fatal(err)
}
if err != nil {
t.Fatal(err)
}
// close the reader
err = reader.Close()
if err != nil {
t.Fatal(err)
}
}