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() {
i.valid = (i.key != nil)
if i.valid && i.prefix != nil {
i.valid = bytes.HasPrefix(i.key, i.prefix)
} else if i.end != nil {
i.valid = bytes.Compare(i.key, i.end) < 0
if i.valid {
if i.prefix != nil {
i.valid = bytes.HasPrefix(i.key, i.prefix)
} else if i.end != nil {
i.valid = bytes.Compare(i.key, i.end) < 0
}
}
}
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.updateValid()
}

View File

@ -72,6 +72,12 @@ func TestBoltDBRangeIterator(t *testing.T) {
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) {
s := open(t, &test.TestMergeCounter{})
defer cleanup(t, s)

View File

@ -75,6 +75,12 @@ func TestGoLevelDBRangeIterator(t *testing.T) {
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) {
s := open(t, &test.TestMergeCounter{})
defer cleanup(t, s)

View File

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

View File

@ -70,6 +70,12 @@ func TestGTreapRangeIterator(t *testing.T) {
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) {
s := open(t, &test.TestMergeCounter{})
defer cleanup(t, s)

View File

@ -59,6 +59,12 @@ func TestMetricsRangeIterator(t *testing.T) {
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) {
s := open(t, &test.TestMergeCounter{})
defer cleanup(t, s)

View File

@ -3,6 +3,7 @@ package test
import (
"bytes"
"reflect"
"strings"
"testing"
"github.com/blevesearch/bleve/index/store"
@ -10,12 +11,39 @@ import (
// 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) {
data := []struct {
key []byte
val []byte
}{
data := []testRow{
{[]byte("apple"), []byte("val")},
{[]byte("cat1"), []byte("val")},
{[]byte("cat2"), []byte("val")},
@ -39,24 +67,7 @@ func CommonTestPrefixIterator(t *testing.T, s store.KVStore) {
[]byte("dog4"),
}
// open a writer
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()
err := batchWriteRows(s, data)
if err != nil {
t.Fatal(err)
}
@ -122,10 +133,7 @@ func CommonTestPrefixIterator(t *testing.T, s store.KVStore) {
func CommonTestRangeIterator(t *testing.T, s store.KVStore) {
data := []struct {
key []byte
val []byte
}{
data := []testRow{
{[]byte("a1"), []byte("val")},
{[]byte("b1"), []byte("val")},
{[]byte("b2"), []byte("val")},
@ -153,24 +161,7 @@ func CommonTestRangeIterator(t *testing.T, s store.KVStore) {
}
}
// open a writer
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()
err := batchWriteRows(s, data)
if err != nil {
t.Fatal(err)
}
@ -275,3 +266,79 @@ func CommonTestRangeIterator(t *testing.T, s store.KVStore) {
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)
}
}