2015-09-23 20:25:47 +02:00
|
|
|
package test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"reflect"
|
2015-10-20 19:05:53 +02:00
|
|
|
"strings"
|
2015-09-23 20:25:47 +02:00
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/blevesearch/bleve/index/store"
|
|
|
|
)
|
|
|
|
|
|
|
|
// tests around the correct behavior of iterators
|
|
|
|
|
2015-10-20 19:21:01 +02:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2015-09-23 20:25:47 +02:00
|
|
|
func CommonTestPrefixIterator(t *testing.T, s store.KVStore) {
|
|
|
|
|
2015-10-20 19:21:01 +02:00
|
|
|
data := []testRow{
|
2015-09-23 20:25:47 +02:00
|
|
|
{[]byte("apple"), []byte("val")},
|
|
|
|
{[]byte("cat1"), []byte("val")},
|
|
|
|
{[]byte("cat2"), []byte("val")},
|
|
|
|
{[]byte("cat3"), []byte("val")},
|
|
|
|
{[]byte("dog1"), []byte("val")},
|
|
|
|
{[]byte("dog2"), []byte("val")},
|
|
|
|
{[]byte("dog4"), []byte("val")},
|
|
|
|
{[]byte("elephant"), []byte("val")},
|
|
|
|
}
|
|
|
|
|
|
|
|
expectedCats := [][]byte{
|
|
|
|
[]byte("cat1"),
|
|
|
|
[]byte("cat2"),
|
|
|
|
[]byte("cat3"),
|
|
|
|
}
|
|
|
|
|
|
|
|
expectedDogs := [][]byte{
|
|
|
|
[]byte("dog1"),
|
|
|
|
// we seek to "dog3" and ensure it skips over "dog2"
|
|
|
|
// but still finds "dog4" even though there was no "dog3"
|
|
|
|
[]byte("dog4"),
|
|
|
|
}
|
|
|
|
|
2015-10-20 19:21:01 +02:00
|
|
|
err := batchWriteRows(s, data)
|
2015-09-23 20:25:47 +02:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// open a reader
|
|
|
|
reader, err := s.Reader()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// get a prefix reader
|
|
|
|
cats := make([][]byte, 0)
|
|
|
|
iter := reader.PrefixIterator([]byte("cat"))
|
|
|
|
for iter.Valid() {
|
|
|
|
// if we want to keep bytes from iteration we must copy
|
|
|
|
k := iter.Key()
|
|
|
|
copyk := make([]byte, len(k))
|
|
|
|
copy(copyk, k)
|
|
|
|
cats = append(cats, copyk)
|
|
|
|
iter.Next()
|
|
|
|
}
|
|
|
|
err = iter.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// check that we found all the cats
|
|
|
|
if !reflect.DeepEqual(cats, expectedCats) {
|
|
|
|
t.Fatalf("expected cats %v, got %v", expectedCats, cats)
|
|
|
|
}
|
|
|
|
|
|
|
|
// get a prefix reader
|
|
|
|
dogs := make([][]byte, 0)
|
|
|
|
iter = reader.PrefixIterator([]byte("dog"))
|
|
|
|
for iter.Valid() {
|
|
|
|
// if we want to keep bytes from iteration we must copy
|
|
|
|
k := iter.Key()
|
|
|
|
copyk := make([]byte, len(k))
|
|
|
|
copy(copyk, k)
|
|
|
|
dogs = append(dogs, copyk)
|
|
|
|
if len(dogs) < 2 {
|
|
|
|
iter.Seek([]byte("dog3"))
|
|
|
|
} else {
|
|
|
|
iter.Next()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
err = iter.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// check that we found the expected dogs
|
|
|
|
if !reflect.DeepEqual(dogs, expectedDogs) {
|
|
|
|
t.Fatalf("expected dogs %v, got %v", expectedDogs, dogs)
|
|
|
|
}
|
|
|
|
|
|
|
|
// close the reader
|
|
|
|
err = reader.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-26 21:14:29 +01:00
|
|
|
func CommonTestPrefixIteratorSeek(t *testing.T, s store.KVStore) {
|
|
|
|
|
|
|
|
data := []testRow{
|
|
|
|
{[]byte("a"), []byte("val")},
|
|
|
|
{[]byte("b1"), []byte("val")},
|
|
|
|
{[]byte("b2"), []byte("val")},
|
|
|
|
{[]byte("b3"), []byte("val")},
|
|
|
|
{[]byte("c"), []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)
|
|
|
|
}
|
2015-10-26 21:41:58 +01:00
|
|
|
defer func() {
|
|
|
|
err := reader.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
}()
|
2015-10-26 21:14:29 +01:00
|
|
|
|
|
|
|
// get an iterator on a central subset of the data
|
|
|
|
iter := reader.PrefixIterator([]byte("b"))
|
2015-10-26 21:41:58 +01:00
|
|
|
defer func() {
|
|
|
|
err := iter.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
}()
|
2015-10-26 21:14:29 +01:00
|
|
|
|
|
|
|
// check that all keys have prefix
|
|
|
|
found := []string{}
|
|
|
|
for ; iter.Valid(); iter.Next() {
|
|
|
|
found = append(found, string(iter.Key()))
|
|
|
|
}
|
|
|
|
for _, f := range found {
|
|
|
|
if !strings.HasPrefix(f, "b") {
|
2015-10-26 21:41:58 +01:00
|
|
|
t.Errorf("got key '%s' that doesn't have correct prefix", f)
|
2015-10-26 21:14:29 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(found) != 3 {
|
|
|
|
t.Errorf("expected 3 keys with prefix, got %d", len(found))
|
|
|
|
}
|
|
|
|
|
|
|
|
// now try to seek before the prefix and repeat
|
|
|
|
found = []string{}
|
|
|
|
for iter.Seek([]byte("a")); iter.Valid(); iter.Next() {
|
|
|
|
found = append(found, string(iter.Key()))
|
|
|
|
}
|
|
|
|
for _, f := range found {
|
|
|
|
if !strings.HasPrefix(f, "b") {
|
2015-10-26 21:41:58 +01:00
|
|
|
t.Errorf("got key '%s' that doesn't have correct prefix", f)
|
2015-10-26 21:14:29 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(found) != 3 {
|
|
|
|
t.Errorf("expected 3 keys with prefix, got %d", len(found))
|
|
|
|
}
|
|
|
|
|
|
|
|
// now try to seek after the prefix and repeat
|
|
|
|
found = []string{}
|
|
|
|
for iter.Seek([]byte("c")); iter.Valid(); iter.Next() {
|
|
|
|
found = append(found, string(iter.Key()))
|
|
|
|
}
|
|
|
|
for _, f := range found {
|
|
|
|
if !strings.HasPrefix(f, "b") {
|
2015-10-26 21:41:58 +01:00
|
|
|
t.Errorf("got key '%s' that doesn't have correct prefix", f)
|
2015-10-26 21:14:29 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(found) != 0 {
|
|
|
|
t.Errorf("expected 0 keys with prefix, got %d", len(found))
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2015-09-23 20:25:47 +02:00
|
|
|
func CommonTestRangeIterator(t *testing.T, s store.KVStore) {
|
|
|
|
|
2015-10-20 19:21:01 +02:00
|
|
|
data := []testRow{
|
2015-09-23 20:25:47 +02:00
|
|
|
{[]byte("a1"), []byte("val")},
|
|
|
|
{[]byte("b1"), []byte("val")},
|
|
|
|
{[]byte("b2"), []byte("val")},
|
|
|
|
{[]byte("b3"), []byte("val")},
|
|
|
|
{[]byte("c1"), []byte("val")},
|
|
|
|
{[]byte("c2"), []byte("val")},
|
|
|
|
{[]byte("c4"), []byte("val")},
|
|
|
|
{[]byte("d1"), []byte("val")},
|
|
|
|
}
|
|
|
|
|
|
|
|
expectedAll := make([][]byte, 0)
|
|
|
|
expectedBToC := make([][]byte, 0)
|
|
|
|
expectedCToDSeek3 := make([][]byte, 0)
|
|
|
|
expectedCToEnd := make([][]byte, 0)
|
|
|
|
for _, row := range data {
|
|
|
|
expectedAll = append(expectedAll, row.key)
|
|
|
|
if bytes.HasPrefix(row.key, []byte("b")) {
|
|
|
|
expectedBToC = append(expectedBToC, row.key)
|
|
|
|
}
|
|
|
|
if bytes.HasPrefix(row.key, []byte("c")) && !bytes.HasSuffix(row.key, []byte("2")) {
|
|
|
|
expectedCToDSeek3 = append(expectedCToDSeek3, row.key)
|
|
|
|
}
|
|
|
|
if bytes.Compare(row.key, []byte("c")) > 0 {
|
|
|
|
expectedCToEnd = append(expectedCToEnd, row.key)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-20 19:21:01 +02:00
|
|
|
err := batchWriteRows(s, data)
|
2015-09-23 20:25:47 +02:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// open a reader
|
|
|
|
reader, err := s.Reader()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// get a range iterator (all)
|
|
|
|
all := make([][]byte, 0)
|
|
|
|
iter := reader.RangeIterator(nil, nil)
|
|
|
|
for iter.Valid() {
|
|
|
|
// if we want to keep bytes from iteration we must copy
|
|
|
|
k := iter.Key()
|
|
|
|
copyk := make([]byte, len(k))
|
|
|
|
copy(copyk, k)
|
|
|
|
all = append(all, copyk)
|
|
|
|
iter.Next()
|
|
|
|
}
|
|
|
|
err = iter.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// check that we found all
|
|
|
|
if !reflect.DeepEqual(all, expectedAll) {
|
|
|
|
t.Fatalf("expected all %v, got %v", expectedAll, all)
|
|
|
|
}
|
|
|
|
|
|
|
|
// get range iterator from b - c
|
|
|
|
bToC := make([][]byte, 0)
|
|
|
|
iter = reader.RangeIterator([]byte("b"), []byte("c"))
|
|
|
|
for iter.Valid() {
|
|
|
|
// if we want to keep bytes from iteration we must copy
|
|
|
|
k := iter.Key()
|
|
|
|
copyk := make([]byte, len(k))
|
|
|
|
copy(copyk, k)
|
|
|
|
bToC = append(bToC, copyk)
|
|
|
|
iter.Next()
|
|
|
|
}
|
|
|
|
err = iter.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// check that we found b-c
|
|
|
|
if !reflect.DeepEqual(bToC, expectedBToC) {
|
|
|
|
t.Fatalf("expected b-c %v, got %v", expectedBToC, bToC)
|
|
|
|
}
|
|
|
|
|
|
|
|
// get range iterator from c - d, but seek to 'c3'
|
|
|
|
cToDSeek3 := make([][]byte, 0)
|
|
|
|
iter = reader.RangeIterator([]byte("c"), []byte("d"))
|
|
|
|
for iter.Valid() {
|
|
|
|
// if we want to keep bytes from iteration we must copy
|
|
|
|
k := iter.Key()
|
|
|
|
copyk := make([]byte, len(k))
|
|
|
|
copy(copyk, k)
|
|
|
|
cToDSeek3 = append(cToDSeek3, copyk)
|
|
|
|
if len(cToDSeek3) < 2 {
|
|
|
|
iter.Seek([]byte("c3"))
|
|
|
|
} else {
|
|
|
|
iter.Next()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
err = iter.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// check that we found c-d with seek to c3
|
|
|
|
if !reflect.DeepEqual(cToDSeek3, expectedCToDSeek3) {
|
|
|
|
t.Fatalf("expected b-c %v, got %v", expectedCToDSeek3, cToDSeek3)
|
|
|
|
}
|
|
|
|
|
|
|
|
// get range iterator from c to the end
|
|
|
|
cToEnd := make([][]byte, 0)
|
|
|
|
iter = reader.RangeIterator([]byte("c"), nil)
|
|
|
|
for iter.Valid() {
|
|
|
|
// if we want to keep bytes from iteration we must copy
|
|
|
|
k := iter.Key()
|
|
|
|
copyk := make([]byte, len(k))
|
|
|
|
copy(copyk, k)
|
|
|
|
cToEnd = append(cToEnd, copyk)
|
|
|
|
iter.Next()
|
|
|
|
}
|
|
|
|
err = iter.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// check that we found c to end
|
|
|
|
if !reflect.DeepEqual(cToEnd, expectedCToEnd) {
|
|
|
|
t.Fatalf("expected b-c %v, got %v", expectedCToEnd, cToEnd)
|
|
|
|
}
|
|
|
|
|
|
|
|
// close the reader
|
|
|
|
err = reader.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
2015-10-20 19:05:53 +02:00
|
|
|
|
|
|
|
func CommonTestRangeIteratorSeek(t *testing.T, s store.KVStore) {
|
|
|
|
|
2015-10-20 19:21:01 +02:00
|
|
|
data := []testRow{
|
2015-10-20 19:05:53 +02:00
|
|
|
{[]byte("a1"), []byte("val")},
|
|
|
|
{[]byte("b1"), []byte("val")},
|
|
|
|
{[]byte("c1"), []byte("val")},
|
|
|
|
{[]byte("d1"), []byte("val")},
|
|
|
|
{[]byte("e1"), []byte("val")},
|
|
|
|
}
|
|
|
|
|
2015-10-20 19:21:01 +02:00
|
|
|
err := batchWriteRows(s, data)
|
2015-10-20 19:05:53 +02:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|