2022-07-26 12:02:32 +00:00
|
|
|
package mdb
|
|
|
|
|
2022-07-26 15:13:16 +00:00
|
|
|
/*
|
2022-07-26 12:02:32 +00:00
|
|
|
func TestBTreeIndex(t *testing.T) {
|
|
|
|
type Item struct {
|
|
|
|
ID uint64
|
|
|
|
Name string
|
|
|
|
}
|
|
|
|
|
|
|
|
checkIndexOne := func(idx *BTreeIndex[Item], expected ...Item) error {
|
|
|
|
if idx.Len() != len(expected) {
|
|
|
|
return fmt.Errorf("Expected %d items but found %d.", len(expected), idx.Len())
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(expected) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, item := range expected {
|
|
|
|
item2, ok := idx.Get(item)
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("Missing expected item: %v", item)
|
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(item, item2) {
|
|
|
|
return fmt.Errorf("Items not equal: %v != %v", item2, item)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
item, ok := idx.Min()
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("Min item not found, expected: %v", expected[0])
|
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(item, expected[0]) {
|
|
|
|
return fmt.Errorf("Min items not equal: %v != %v", item, expected[0])
|
|
|
|
}
|
|
|
|
|
|
|
|
item, ok = idx.Max()
|
|
|
|
i := len(expected) - 1
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("Max item not found, expected: %v", expected[i])
|
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(item, expected[i]) {
|
|
|
|
return fmt.Errorf("Max items not equal: %v != %v", item, expected[i])
|
|
|
|
}
|
|
|
|
|
|
|
|
i = 0
|
|
|
|
|
|
|
|
iter := idx.Ascend()
|
|
|
|
defer iter.Close()
|
|
|
|
for iter.Next() {
|
|
|
|
if !reflect.DeepEqual(iter.Value(), expected[i]) {
|
|
|
|
return fmt.Errorf("Items not equal (%d): %v != %v", i, iter.Value(), expected[i])
|
|
|
|
}
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
|
|
|
|
i = len(expected) - 1
|
|
|
|
iter = idx.Descend()
|
|
|
|
defer iter.Close()
|
|
|
|
for iter.Next() {
|
|
|
|
if !reflect.DeepEqual(iter.Value(), expected[i]) {
|
|
|
|
return fmt.Errorf("Items not equal (%d): %v != %v", i, iter.Value(), expected[i])
|
|
|
|
}
|
|
|
|
i--
|
|
|
|
}
|
|
|
|
|
|
|
|
i = 1
|
|
|
|
iter = idx.AscendAfter(expected[1])
|
|
|
|
defer iter.Close()
|
|
|
|
for iter.Next() {
|
|
|
|
if !reflect.DeepEqual(iter.Value(), expected[i]) {
|
|
|
|
return fmt.Errorf("Items not equal (%d): %v != %v", i, iter.Value(), expected[i])
|
|
|
|
}
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
|
|
|
|
i = len(expected) - 2
|
|
|
|
iter = idx.DescendAfter(expected[len(expected)-2])
|
|
|
|
defer iter.Close()
|
|
|
|
for iter.Next() {
|
|
|
|
if !reflect.DeepEqual(iter.Value(), expected[i]) {
|
|
|
|
return fmt.Errorf("Items not equal (%d): %v != %v", i, iter.Value(), expected[i])
|
|
|
|
}
|
|
|
|
i--
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
checkIndex := func(idx *BTreeIndex[Item], expected ...Item) error {
|
|
|
|
idx.c.db.waitForWAL()
|
|
|
|
|
|
|
|
if err := checkIndexOne(idx, expected...); err != nil {
|
|
|
|
return fmt.Errorf("%w: original", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
db := NewPrimary(idx.c.db.root)
|
|
|
|
defer db.Close()
|
|
|
|
c := NewCollection(db, "collection", func(i *Item) uint64 { return i.ID })
|
|
|
|
idx = NewBTreeIndex(c,
|
|
|
|
func(i, j *Item) bool { return i.Name < j.Name },
|
|
|
|
func(i *Item) bool { return i.Name != "" })
|
|
|
|
db.Start()
|
|
|
|
|
|
|
|
return checkIndexOne(idx, expected...)
|
|
|
|
}
|
|
|
|
|
|
|
|
run := func(name string, inner func(t *testing.T, c *Collection[Item], idx *BTreeIndex[Item])) {
|
|
|
|
t.Run(name, func(t *testing.T) {
|
|
|
|
root := filepath.Join(os.TempDir(), randString())
|
|
|
|
//defer os.RemoveAll(root)
|
|
|
|
|
|
|
|
db := NewPrimary(root)
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
c := NewCollection(db, "collection", func(i *Item) uint64 { return i.ID })
|
|
|
|
idx := NewBTreeIndex(c,
|
|
|
|
func(i, j *Item) bool { return i.Name < j.Name },
|
|
|
|
func(i *Item) bool { return i.Name != "" })
|
|
|
|
|
|
|
|
db.Start()
|
|
|
|
|
|
|
|
inner(t, c, idx)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
run("no items", func(t *testing.T, c *Collection[Item], idx *BTreeIndex[Item]) {
|
|
|
|
if err := checkIndex(idx); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
run("insert some", func(t *testing.T, c *Collection[Item], idx *BTreeIndex[Item]) {
|
|
|
|
item1 := Item{1, "one"}
|
|
|
|
item2 := Item{2, ""}
|
|
|
|
item3 := Item{3, "three"}
|
|
|
|
item4 := Item{4, ""}
|
|
|
|
item5 := Item{5, "five"}
|
|
|
|
|
|
|
|
c.Insert(item1)
|
|
|
|
c.Insert(item2)
|
|
|
|
c.Insert(item3)
|
|
|
|
c.Insert(item4)
|
|
|
|
c.Insert(item5)
|
|
|
|
|
|
|
|
if err := checkIndex(idx, item5, item1, item3); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
run("partial iteration", func(t *testing.T, c *Collection[Item], idx *BTreeIndex[Item]) {
|
|
|
|
item1 := Item{1, "one"}
|
|
|
|
item2 := Item{2, ""}
|
|
|
|
item3 := Item{3, "three"}
|
|
|
|
item4 := Item{4, ""}
|
|
|
|
item5 := Item{5, "five"}
|
|
|
|
|
|
|
|
c.Insert(item1)
|
|
|
|
c.Insert(item2)
|
|
|
|
c.Insert(item3)
|
|
|
|
c.Insert(item4)
|
|
|
|
c.Insert(item5)
|
|
|
|
|
|
|
|
iter := idx.Ascend()
|
|
|
|
defer iter.Close()
|
|
|
|
|
|
|
|
if !iter.Next() {
|
|
|
|
t.Fatal("Expected", item5)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(iter.Value(), item5) {
|
|
|
|
t.Fatal(iter.Value(), item5)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
run("get", func(t *testing.T, c *Collection[Item], idx *BTreeIndex[Item]) {
|
|
|
|
item1 := Item{1, "one"}
|
|
|
|
item2 := Item{2, ""}
|
|
|
|
c.Insert(item1)
|
|
|
|
c.Insert(item2)
|
|
|
|
|
|
|
|
item, ok := idx.Get(Item{0, "one"})
|
|
|
|
if !ok || !reflect.DeepEqual(item, item1) {
|
|
|
|
t.Fatal(ok, item, item1)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
run("get not found", func(t *testing.T, c *Collection[Item], idx *BTreeIndex[Item]) {
|
|
|
|
item1 := Item{1, "one"}
|
|
|
|
item2 := Item{2, ""}
|
|
|
|
c.Insert(item1)
|
|
|
|
c.Insert(item2)
|
|
|
|
|
|
|
|
if item, ok := idx.Get(Item{0, "three"}); ok {
|
|
|
|
t.Fatal(item)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
run("min max on empty", func(t *testing.T, c *Collection[Item], idx *BTreeIndex[Item]) {
|
|
|
|
item2 := Item{2, ""}
|
|
|
|
c.Insert(item2)
|
|
|
|
|
|
|
|
if item, ok := idx.Min(); ok {
|
|
|
|
t.Fatal(item)
|
|
|
|
}
|
|
|
|
if item, ok := idx.Max(); ok {
|
|
|
|
t.Fatal(item)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
run("min max with one item", func(t *testing.T, c *Collection[Item], idx *BTreeIndex[Item]) {
|
|
|
|
item1 := Item{1, "one"}
|
|
|
|
item2 := Item{2, ""}
|
|
|
|
|
|
|
|
c.Insert(item1)
|
|
|
|
c.Insert(item2)
|
|
|
|
|
|
|
|
i1, ok := idx.Min()
|
|
|
|
if !ok {
|
|
|
|
t.Fatal(ok)
|
|
|
|
}
|
|
|
|
i2, ok := idx.Max()
|
|
|
|
if !ok {
|
|
|
|
t.Fatal(ok)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(i1, i2) {
|
|
|
|
t.Fatal(i1, i2)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
run("update outside of index", func(t *testing.T, c *Collection[Item], idx *BTreeIndex[Item]) {
|
|
|
|
item1 := Item{1, "one"}
|
|
|
|
item2 := Item{2, ""}
|
|
|
|
item3 := Item{3, "three"}
|
|
|
|
item4 := Item{4, ""}
|
|
|
|
item5 := Item{5, "five"}
|
|
|
|
|
|
|
|
c.Insert(item1)
|
|
|
|
c.Insert(item2)
|
|
|
|
c.Insert(item3)
|
|
|
|
c.Insert(item4)
|
|
|
|
c.Insert(item5)
|
|
|
|
|
|
|
|
c.Update(2, func(in Item) (Item, error) {
|
|
|
|
return in, nil
|
|
|
|
})
|
|
|
|
|
|
|
|
if err := checkIndex(idx, item5, item1, item3); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
run("update into index", func(t *testing.T, c *Collection[Item], idx *BTreeIndex[Item]) {
|
|
|
|
item1 := Item{1, "one"}
|
|
|
|
item2 := Item{2, ""}
|
|
|
|
item3 := Item{3, "three"}
|
|
|
|
item4 := Item{4, ""}
|
|
|
|
item5 := Item{5, "five"}
|
|
|
|
|
|
|
|
c.Insert(item1)
|
|
|
|
c.Insert(item2)
|
|
|
|
c.Insert(item3)
|
|
|
|
c.Insert(item4)
|
|
|
|
c.Insert(item5)
|
|
|
|
|
|
|
|
err := c.Update(2, func(in Item) (Item, error) {
|
|
|
|
in.Name = "two"
|
|
|
|
return in, nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
item2.Name = "two"
|
|
|
|
|
|
|
|
if err := checkIndex(idx, item5, item1, item3, item2); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
run("update out of index", func(t *testing.T, c *Collection[Item], idx *BTreeIndex[Item]) {
|
|
|
|
item1 := Item{1, "one"}
|
|
|
|
item2 := Item{2, ""}
|
|
|
|
item3 := Item{3, "three"}
|
|
|
|
item4 := Item{4, ""}
|
|
|
|
item5 := Item{5, "five"}
|
|
|
|
|
|
|
|
c.Insert(item1)
|
|
|
|
c.Insert(item2)
|
|
|
|
c.Insert(item3)
|
|
|
|
c.Insert(item4)
|
|
|
|
c.Insert(item5)
|
|
|
|
|
|
|
|
err := c.Update(1, func(in Item) (Item, error) {
|
|
|
|
in.Name = ""
|
|
|
|
return in, nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := checkIndex(idx, item5, item3); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
run("update within index", func(t *testing.T, c *Collection[Item], idx *BTreeIndex[Item]) {
|
|
|
|
item1 := Item{1, "one"}
|
|
|
|
item2 := Item{2, ""}
|
|
|
|
item3 := Item{3, "three"}
|
|
|
|
item4 := Item{4, ""}
|
|
|
|
item5 := Item{5, "five"}
|
|
|
|
|
|
|
|
c.Insert(item1)
|
|
|
|
c.Insert(item2)
|
|
|
|
c.Insert(item3)
|
|
|
|
c.Insert(item4)
|
|
|
|
c.Insert(item5)
|
|
|
|
|
|
|
|
err := c.Update(1, func(in Item) (Item, error) {
|
|
|
|
in.Name = "xone"
|
|
|
|
return in, nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
item1.Name = "xone"
|
|
|
|
|
|
|
|
if err := checkIndex(idx, item5, item3, item1); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
run("delete outside index", func(t *testing.T, c *Collection[Item], idx *BTreeIndex[Item]) {
|
|
|
|
item1 := Item{1, "one"}
|
|
|
|
item2 := Item{2, ""}
|
|
|
|
item3 := Item{3, "three"}
|
|
|
|
item4 := Item{4, ""}
|
|
|
|
item5 := Item{5, "five"}
|
|
|
|
|
|
|
|
c.Insert(item1)
|
|
|
|
c.Insert(item2)
|
|
|
|
c.Insert(item3)
|
|
|
|
c.Insert(item4)
|
|
|
|
c.Insert(item5)
|
|
|
|
|
|
|
|
c.Delete(item2.ID)
|
|
|
|
|
|
|
|
if err := checkIndex(idx, item5, item1, item3); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
run("delete within index", func(t *testing.T, c *Collection[Item], idx *BTreeIndex[Item]) {
|
|
|
|
item1 := Item{1, "one"}
|
|
|
|
item2 := Item{2, ""}
|
|
|
|
item3 := Item{3, "three"}
|
|
|
|
item4 := Item{4, ""}
|
|
|
|
item5 := Item{5, "five"}
|
|
|
|
|
|
|
|
c.Insert(item1)
|
|
|
|
c.Insert(item2)
|
|
|
|
c.Insert(item3)
|
|
|
|
c.Insert(item4)
|
|
|
|
c.Insert(item5)
|
|
|
|
|
|
|
|
c.Delete(item1.ID)
|
|
|
|
|
|
|
|
if err := checkIndex(idx, item5, item3); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2022-07-26 15:13:16 +00:00
|
|
|
*/
|