474 lines
12 KiB
Go
474 lines
12 KiB
Go
package index_test
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/asdine/storm"
|
|
"github.com/asdine/storm/codec/gob"
|
|
"github.com/asdine/storm/index"
|
|
"github.com/coreos/bbolt"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestListIndex(t *testing.T) {
|
|
dir, _ := ioutil.TempDir(os.TempDir(), "storm")
|
|
defer os.RemoveAll(dir)
|
|
db, _ := storm.Open(filepath.Join(dir, "storm.db"))
|
|
defer db.Close()
|
|
|
|
err := db.Bolt.Update(func(tx *bolt.Tx) error {
|
|
b, err := tx.CreateBucket([]byte("test"))
|
|
require.NoError(t, err)
|
|
|
|
idx, err := index.NewListIndex(b, []byte("lindex1"))
|
|
require.NoError(t, err)
|
|
|
|
err = idx.Add([]byte("hello"), []byte("id1"))
|
|
require.NoError(t, err)
|
|
|
|
err = idx.Add([]byte("hello"), []byte("id1"))
|
|
require.NoError(t, err)
|
|
|
|
err = idx.Add([]byte("hello"), []byte("id2"))
|
|
require.NoError(t, err)
|
|
|
|
err = idx.Add([]byte("goodbye"), []byte("id2"))
|
|
require.NoError(t, err)
|
|
|
|
err = idx.Add(nil, []byte("id2"))
|
|
require.Error(t, err)
|
|
require.Equal(t, index.ErrNilParam, err)
|
|
|
|
err = idx.Add([]byte("hi"), nil)
|
|
require.Error(t, err)
|
|
require.Equal(t, index.ErrNilParam, err)
|
|
|
|
ids, err := idx.All([]byte("hello"), nil)
|
|
require.NoError(t, err)
|
|
require.Len(t, ids, 1)
|
|
require.Equal(t, []byte("id1"), ids[0])
|
|
|
|
ids, err = idx.All([]byte("goodbye"), nil)
|
|
require.Len(t, ids, 1)
|
|
require.Equal(t, []byte("id2"), ids[0])
|
|
|
|
ids, err = idx.All([]byte("yo"), nil)
|
|
require.Nil(t, ids)
|
|
|
|
err = idx.RemoveID([]byte("id2"))
|
|
require.NoError(t, err)
|
|
|
|
ids, err = idx.All([]byte("goodbye"), nil)
|
|
require.Len(t, ids, 0)
|
|
|
|
err = idx.RemoveID(nil)
|
|
require.NoError(t, err)
|
|
|
|
err = idx.RemoveID([]byte("id1"))
|
|
require.NoError(t, err)
|
|
err = idx.RemoveID([]byte("id2"))
|
|
require.NoError(t, err)
|
|
err = idx.RemoveID([]byte("id3"))
|
|
require.NoError(t, err)
|
|
|
|
ids, err = idx.All([]byte("hello"), nil)
|
|
require.NoError(t, err)
|
|
require.Nil(t, ids)
|
|
|
|
err = idx.Add([]byte("hello"), []byte("id1"))
|
|
require.NoError(t, err)
|
|
|
|
err = idx.Add([]byte("hi"), []byte("id2"))
|
|
require.NoError(t, err)
|
|
|
|
err = idx.Add([]byte("yo"), []byte("id3"))
|
|
require.NoError(t, err)
|
|
|
|
err = idx.RemoveID([]byte("id2"))
|
|
require.NoError(t, err)
|
|
|
|
ids, err = idx.All([]byte("hello"), nil)
|
|
require.Len(t, ids, 1)
|
|
require.Equal(t, []byte("id1"), ids[0])
|
|
ids, err = idx.All([]byte("hi"), nil)
|
|
require.Len(t, ids, 0)
|
|
ids, err = idx.All([]byte("yo"), nil)
|
|
require.Len(t, ids, 1)
|
|
require.Equal(t, []byte("id3"), ids[0])
|
|
|
|
err = idx.RemoveID([]byte("id2"))
|
|
require.NoError(t, err)
|
|
err = idx.RemoveID([]byte("id4"))
|
|
require.NoError(t, err)
|
|
|
|
err = idx.Add([]byte("hey"), []byte("id1"))
|
|
err = idx.Add([]byte("hey"), []byte("id2"))
|
|
err = idx.Add([]byte("hey"), []byte("id3"))
|
|
err = idx.Add([]byte("hey"), []byte("id4"))
|
|
ids, err = idx.All([]byte("hey"), nil)
|
|
require.Len(t, ids, 4)
|
|
|
|
opts := index.NewOptions()
|
|
opts.Limit = 1
|
|
ids, err = idx.All([]byte("hey"), opts)
|
|
require.Len(t, ids, 1)
|
|
|
|
opts = index.NewOptions()
|
|
opts.Skip = 2
|
|
ids, err = idx.All([]byte("hey"), opts)
|
|
require.Len(t, ids, 2)
|
|
|
|
opts = index.NewOptions()
|
|
opts.Skip = 2
|
|
opts.Limit = 3
|
|
opts.Reverse = true
|
|
ids, err = idx.All([]byte("hey"), opts)
|
|
require.Len(t, ids, 2)
|
|
require.Equal(t, []byte("id2"), ids[0])
|
|
|
|
id := idx.Get([]byte("hey"))
|
|
require.Equal(t, []byte("id1"), id)
|
|
|
|
err = idx.Remove([]byte("hey"))
|
|
require.NoError(t, err)
|
|
ids, err = idx.All([]byte("hey"), nil)
|
|
require.NoError(t, err)
|
|
require.Len(t, ids, 0)
|
|
|
|
ids, err = idx.All([]byte("hey"), nil)
|
|
require.NoError(t, err)
|
|
require.Len(t, ids, 0)
|
|
return nil
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestListIndexReverse(t *testing.T) {
|
|
dir, _ := ioutil.TempDir(os.TempDir(), "storm")
|
|
defer os.RemoveAll(dir)
|
|
db, _ := storm.Open(filepath.Join(dir, "storm.db"))
|
|
defer db.Close()
|
|
|
|
err := db.Bolt.Update(func(tx *bolt.Tx) error {
|
|
b, err := tx.CreateBucket([]byte("test"))
|
|
require.NoError(t, err)
|
|
|
|
idx, err := index.NewListIndex(b, []byte("lindex1"))
|
|
require.NoError(t, err)
|
|
|
|
err = idx.Add([]byte("hello"), []byte("id1"))
|
|
require.NoError(t, err)
|
|
|
|
opts := index.NewOptions()
|
|
ids, err := idx.All([]byte("hello"), opts)
|
|
require.Len(t, ids, 1)
|
|
require.Equal(t, []byte("id1"), ids[0])
|
|
|
|
opts = index.NewOptions()
|
|
opts.Reverse = true
|
|
ids, err = idx.All([]byte("hello"), opts)
|
|
require.Len(t, ids, 1)
|
|
require.Equal(t, []byte("id1"), ids[0])
|
|
|
|
err = idx.Add([]byte("hello"), []byte("id2"))
|
|
require.NoError(t, err)
|
|
|
|
opts = index.NewOptions()
|
|
opts.Reverse = true
|
|
ids, err = idx.All([]byte("hello"), opts)
|
|
require.Len(t, ids, 2)
|
|
require.Equal(t, []byte("id2"), ids[0])
|
|
require.Equal(t, []byte("id1"), ids[1])
|
|
return nil
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestListIndexAddRemoveID(t *testing.T) {
|
|
dir, _ := ioutil.TempDir(os.TempDir(), "storm")
|
|
defer os.RemoveAll(dir)
|
|
db, _ := storm.Open(filepath.Join(dir, "storm.db"))
|
|
defer db.Close()
|
|
|
|
db.Bolt.Update(func(tx *bolt.Tx) error {
|
|
b, err := tx.CreateBucket([]byte("test"))
|
|
require.NoError(t, err)
|
|
|
|
idx, err := index.NewListIndex(b, []byte("lindex1"))
|
|
require.NoError(t, err)
|
|
|
|
err = idx.Add([]byte("hello"), []byte("id1"))
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, countItems(t, idx.IndexBucket))
|
|
|
|
err = idx.Add([]byte("hello"), []byte("id2"))
|
|
require.NoError(t, err)
|
|
require.Equal(t, 2, countItems(t, idx.IndexBucket))
|
|
|
|
err = idx.Add([]byte("goodbye"), []byte("id1"))
|
|
require.NoError(t, err)
|
|
require.Equal(t, 2, countItems(t, idx.IndexBucket))
|
|
|
|
err = idx.Add([]byte("hello"), []byte("id3"))
|
|
require.NoError(t, err)
|
|
require.Equal(t, 3, countItems(t, idx.IndexBucket))
|
|
|
|
err = idx.RemoveID([]byte("id1"))
|
|
require.NoError(t, err)
|
|
require.Equal(t, 2, countItems(t, idx.IndexBucket))
|
|
|
|
err = idx.RemoveID([]byte("id2"))
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, countItems(t, idx.IndexBucket))
|
|
|
|
err = idx.RemoveID([]byte("id3"))
|
|
require.NoError(t, err)
|
|
require.Equal(t, 0, countItems(t, idx.IndexBucket))
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func TestListIndexAllRecords(t *testing.T) {
|
|
dir, _ := ioutil.TempDir(os.TempDir(), "storm")
|
|
defer os.RemoveAll(dir)
|
|
db, _ := storm.Open(filepath.Join(dir, "storm.db"))
|
|
defer db.Close()
|
|
|
|
db.Bolt.Update(func(tx *bolt.Tx) error {
|
|
b, err := tx.CreateBucket([]byte("test"))
|
|
require.NoError(t, err)
|
|
|
|
idx, err := index.NewListIndex(b, []byte("lindex1"))
|
|
require.NoError(t, err)
|
|
|
|
ids, err := idx.AllRecords(nil)
|
|
require.NoError(t, err)
|
|
require.Len(t, ids, 0)
|
|
|
|
err = idx.Add([]byte("goodbye"), []byte("id2"))
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, countItems(t, idx.IndexBucket))
|
|
|
|
err = idx.Add([]byte("goodbye"), []byte("id1"))
|
|
require.NoError(t, err)
|
|
require.Equal(t, 2, countItems(t, idx.IndexBucket))
|
|
|
|
err = idx.Add([]byte("hello"), []byte("id4"))
|
|
require.NoError(t, err)
|
|
require.Equal(t, 3, countItems(t, idx.IndexBucket))
|
|
|
|
err = idx.Add([]byte("hello"), []byte("id3"))
|
|
require.NoError(t, err)
|
|
require.Equal(t, 4, countItems(t, idx.IndexBucket))
|
|
|
|
ids, err = idx.AllRecords(nil)
|
|
require.NoError(t, err)
|
|
require.Len(t, ids, 4)
|
|
require.Equal(t, []byte("id1"), ids[0])
|
|
require.Equal(t, []byte("id2"), ids[1])
|
|
require.Equal(t, []byte("id3"), ids[2])
|
|
require.Equal(t, []byte("id4"), ids[3])
|
|
|
|
err = idx.RemoveID([]byte("id1"))
|
|
require.NoError(t, err)
|
|
require.Equal(t, 3, countItems(t, idx.IndexBucket))
|
|
|
|
ids, err = idx.AllRecords(nil)
|
|
require.NoError(t, err)
|
|
require.Len(t, ids, 3)
|
|
require.Equal(t, []byte("id2"), ids[0])
|
|
|
|
err = idx.Add([]byte("goodbye"), []byte("id1"))
|
|
require.NoError(t, err)
|
|
require.Equal(t, 4, countItems(t, idx.IndexBucket))
|
|
|
|
opts := index.NewOptions()
|
|
opts.Limit = 1
|
|
ids, err = idx.AllRecords(opts)
|
|
require.Len(t, ids, 1)
|
|
|
|
opts = index.NewOptions()
|
|
opts.Skip = 2
|
|
ids, err = idx.AllRecords(opts)
|
|
require.Len(t, ids, 2)
|
|
|
|
opts = index.NewOptions()
|
|
opts.Skip = 2
|
|
opts.Limit = 3
|
|
opts.Reverse = true
|
|
ids, err = idx.AllRecords(opts)
|
|
require.NoError(t, err)
|
|
require.Len(t, ids, 2)
|
|
require.Equal(t, []byte("id2"), ids[0])
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func TestListIndexRange(t *testing.T) {
|
|
dir, _ := ioutil.TempDir(os.TempDir(), "storm")
|
|
defer os.RemoveAll(dir)
|
|
db, _ := storm.Open(filepath.Join(dir, "storm.db"))
|
|
defer db.Close()
|
|
|
|
db.Bolt.Update(func(tx *bolt.Tx) error {
|
|
b, err := tx.CreateBucket([]byte("test"))
|
|
require.NoError(t, err)
|
|
|
|
idx, err := index.NewListIndex(b, []byte("index1"))
|
|
require.NoError(t, err)
|
|
|
|
for i := 0; i < 10; i++ {
|
|
val, _ := gob.Codec.Marshal(i)
|
|
err = idx.Add(val, val)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
min, _ := gob.Codec.Marshal(3)
|
|
max, _ := gob.Codec.Marshal(5)
|
|
list, err := idx.Range(min, max, nil)
|
|
require.Len(t, list, 3)
|
|
require.NoError(t, err)
|
|
assertEncodedIntListEqual(t, []int{3, 4, 5}, list)
|
|
|
|
min, _ = gob.Codec.Marshal(11)
|
|
max, _ = gob.Codec.Marshal(20)
|
|
list, err = idx.Range(min, max, nil)
|
|
require.Len(t, list, 0)
|
|
require.NoError(t, err)
|
|
|
|
min, _ = gob.Codec.Marshal(7)
|
|
max, _ = gob.Codec.Marshal(2)
|
|
list, err = idx.Range(min, max, nil)
|
|
require.Len(t, list, 0)
|
|
require.NoError(t, err)
|
|
|
|
min, _ = gob.Codec.Marshal(-5)
|
|
max, _ = gob.Codec.Marshal(2)
|
|
list, err = idx.Range(min, max, nil)
|
|
require.Len(t, list, 0)
|
|
require.NoError(t, err)
|
|
|
|
min, _ = gob.Codec.Marshal(3)
|
|
max, _ = gob.Codec.Marshal(7)
|
|
opts := index.NewOptions()
|
|
opts.Skip = 2
|
|
list, err = idx.Range(min, max, opts)
|
|
require.Len(t, list, 3)
|
|
require.NoError(t, err)
|
|
assertEncodedIntListEqual(t, []int{5, 6, 7}, list)
|
|
|
|
opts = index.NewOptions()
|
|
opts.Limit = 2
|
|
list, err = idx.Range(min, max, opts)
|
|
require.Len(t, list, 2)
|
|
require.NoError(t, err)
|
|
assertEncodedIntListEqual(t, []int{3, 4}, list)
|
|
|
|
opts = index.NewOptions()
|
|
opts.Reverse = true
|
|
opts.Skip = 2
|
|
opts.Limit = 2
|
|
list, err = idx.Range(min, max, opts)
|
|
require.Len(t, list, 2)
|
|
require.NoError(t, err)
|
|
assertEncodedIntListEqual(t, []int{5, 4}, list)
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func TestListIndexPrefix(t *testing.T) {
|
|
dir, _ := ioutil.TempDir(os.TempDir(), "storm")
|
|
defer os.RemoveAll(dir)
|
|
db, _ := storm.Open(filepath.Join(dir, "storm.db"))
|
|
defer db.Close()
|
|
|
|
db.Bolt.Update(func(tx *bolt.Tx) error {
|
|
b, err := tx.CreateBucket([]byte("test"))
|
|
require.NoError(t, err)
|
|
|
|
idx, err := index.NewListIndex(b, []byte("lindex1"))
|
|
require.NoError(t, err)
|
|
|
|
for i := 0; i < 10; i++ {
|
|
val := []byte(fmt.Sprintf("a%d", i%2))
|
|
id := []byte(fmt.Sprintf("%d", i))
|
|
err = idx.Add(val, id)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
for i := 10; i < 20; i++ {
|
|
val := []byte(fmt.Sprintf("b%d", i%2))
|
|
id := []byte(fmt.Sprintf("%d", i))
|
|
err = idx.Add(val, id)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
list, err := idx.Prefix([]byte("a"), nil)
|
|
require.Len(t, list, 10)
|
|
require.NoError(t, err)
|
|
require.Equal(t, []byte("0"), list[0])
|
|
require.Equal(t, []byte("9"), list[9])
|
|
|
|
list, err = idx.Prefix([]byte("b"), nil)
|
|
require.Len(t, list, 10)
|
|
require.NoError(t, err)
|
|
require.Equal(t, []byte("10"), list[0])
|
|
require.Equal(t, []byte("19"), list[9])
|
|
|
|
opts := index.NewOptions()
|
|
opts.Reverse = true
|
|
list, err = idx.Prefix([]byte("a"), opts)
|
|
require.Len(t, list, 10)
|
|
require.NoError(t, err)
|
|
require.Equal(t, []byte("9"), list[0])
|
|
require.Equal(t, []byte("0"), list[9])
|
|
|
|
opts = index.NewOptions()
|
|
opts.Reverse = true
|
|
list, err = idx.Prefix([]byte("b"), opts)
|
|
require.Len(t, list, 10)
|
|
require.NoError(t, err)
|
|
require.Equal(t, []byte("19"), list[0])
|
|
require.Equal(t, []byte("10"), list[9])
|
|
|
|
opts = index.NewOptions()
|
|
opts.Skip = 9
|
|
opts.Limit = 5
|
|
list, err = idx.Prefix([]byte("a"), opts)
|
|
require.Len(t, list, 1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, []byte("9"), list[0])
|
|
|
|
opts = index.NewOptions()
|
|
opts.Reverse = true
|
|
opts.Skip = 9
|
|
opts.Limit = 5
|
|
list, err = idx.Prefix([]byte("a"), opts)
|
|
require.Len(t, list, 1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, []byte("0"), list[0])
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func countItems(t *testing.T, bucket *bolt.Bucket) int {
|
|
c := bucket.Cursor()
|
|
count := 0
|
|
for k, id := c.First(); k != nil; k, id = c.Next() {
|
|
if id == nil || bytes.Equal(k, []byte("storm__ids")) {
|
|
continue
|
|
}
|
|
count++
|
|
}
|
|
|
|
return count
|
|
}
|