package mdb /* 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) } }) } */