Merge pull request #256 from pmezard/fix-rangeiterator-seek
Fix rangeiterator seek
This commit is contained in:
commit
74780b028e
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user