func TestIndexEntries(t *testing.T) { t.Parallel() Convey("Test indexEntriesWithBuiltins", t, func() { for _, tc := range rowGenTestCases { if tc.collections == nil { Convey(tc.name, nil) // shows up as 'skipped' continue } Convey(tc.name, func() { store := (*memStore)(nil) if tc.withBuiltin { store = indexEntriesWithBuiltins(fakeKey, tc.pmap, tc.idxs) } else { sip := serialize.PropertyMapPartially(fakeKey, tc.pmap) store = indexEntries(sip, fakeKey.Namespace(), tc.idxs) } for colName, vals := range tc.collections { i := 0 coll := store.GetCollection(colName) numItems, _ := coll.GetTotals() So(numItems, ShouldEqual, len(tc.collections[colName])) coll.VisitItemsAscend(nil, true, func(itm *gkvlite.Item) bool { So(itm.Key, ShouldResemble, vals[i]) i++ return true }) So(i, ShouldEqual, len(vals)) } }) } }) }
func addIndexes(store *memStore, aid, ns string, compIdx []*ds.IndexDefinition) { normalized := make([]*ds.IndexDefinition, len(compIdx)) idxColl := store.SetCollection("idx", nil) for i, idx := range compIdx { normalized[i] = idx.Normalize() idxColl.Set(serialize.ToBytes(*normalized[i].PrepForIdxTable()), []byte{}) } if allEnts := store.GetCollection("ents:" + ns); allEnts != nil { allEnts.VisitItemsAscend(nil, true, func(i *gkvlite.Item) bool { pm, err := rpm(i.Val) memoryCorruption(err) prop, err := serialize.ReadProperty(bytes.NewBuffer(i.Key), serialize.WithoutContext, aid, ns) memoryCorruption(err) k := prop.Value().(*ds.Key) sip := serialize.PropertyMapPartially(k, pm) mergeIndexes(ns, store, newMemStore(), indexEntries(sip, ns, normalized)) return true }) } }
// toComparableString computes the byte-sortable 'order' string for the given // key/PropertyMap. // // * start/end are byte sequences which are the inequality bounds of the // query, if any. These are a serialized datastore.Property. If the // inequality column is inverted, then start and end are also inverted and // swapped with each other. // * order is the list of sort orders in the actual executing queries. // * k / pm are the data to derive a sortable string for. // // The result of this function is the series of serialized properties, one per // order column, which represent this key/pm's first entry in the composite // index that would point to it (e.g. the one with `order` sort orders). func toComparableString(start, end []byte, order []ds.IndexColumn, k *ds.Key, pm ds.PropertyMap) (row, key []byte) { doCmp := true soFar := []byte{} ps := serialize.PropertyMapPartially(k, nil) for _, ord := range order { row, ok := ps[ord.Property] if !ok { if vals, ok := pm[ord.Property]; ok { row = serialize.PropertySlice(vals) } } sort.Sort(row) foundOne := false for _, serialized := range row { if ord.Descending { serialized = serialize.Invert(serialized) } if doCmp { maybe := serialize.Join(soFar, serialized) cmp := bytes.Compare(maybe, start) if cmp >= 0 { foundOne = true soFar = maybe doCmp = len(soFar) < len(start) break } } else { foundOne = true soFar = serialize.Join(soFar, serialized) break } } if !foundOne { return nil, nil } } if end != nil && bytes.Compare(soFar, end) >= 0 { return nil, nil } return soFar, ps["__key__"][0] }
func TestIndexRowGen(t *testing.T) { t.Parallel() Convey("Test Index Row Generation", t, func() { for _, tc := range rowGenTestCases { if tc.expected == nil { Convey(tc.name, nil) // shows up as 'skipped' continue } Convey(tc.name, func() { mvals := serialize.PropertyMapPartially(fakeKey, tc.pmap) idxs := []*ds.IndexDefinition(nil) if tc.withBuiltin { idxs = append(defaultIndexes("coolKind", tc.pmap), tc.idxs...) } else { idxs = tc.idxs } m := matcher{} for i, idx := range idxs { Convey(idx.String(), func() { iGen, ok := m.match(idx.GetFullSortOrder(), mvals) if len(tc.expected[i]) > 0 { So(ok, ShouldBeTrue) actual := make(serialize.SerializedPslice, 0, len(tc.expected[i])) iGen.permute(func(row, _ []byte) { actual = append(actual, row) }) So(len(actual), ShouldEqual, len(tc.expected[i])) sort.Sort(actual) for j, act := range actual { So(act, ShouldResemble, tc.expected[i][j]) } } else { So(ok, ShouldBeFalse) } }) } }) } }) Convey("default indexes", t, func() { Convey("nil collated", func() { Convey("defaultIndexes (nil)", func() { idxs := defaultIndexes("knd", ds.PropertyMap(nil)) So(len(idxs), ShouldEqual, 1) So(idxs[0].String(), ShouldEqual, "B:knd") }) Convey("indexEntries", func() { sip := serialize.PropertyMapPartially(fakeKey, nil) s := indexEntries(sip, "ns", defaultIndexes("knd", ds.PropertyMap(nil))) numItems, _ := s.GetCollection("idx").GetTotals() So(numItems, ShouldEqual, 1) itm := s.GetCollection("idx").MinItem(false) So(itm.Key, ShouldResemble, cat(indx("knd").PrepForIdxTable())) numItems, _ = s.GetCollection("idx:ns:" + string(itm.Key)).GetTotals() So(numItems, ShouldEqual, 1) }) Convey("defaultIndexes", func() { pm := ds.PropertyMap{ "wat": {propNI("thing"), prop("hat"), prop(100)}, "nerd": {prop(103.7)}, "spaz": {propNI(false)}, } idxs := defaultIndexes("knd", pm) So(len(idxs), ShouldEqual, 5) So(idxs[0].String(), ShouldEqual, "B:knd") So(idxs[1].String(), ShouldEqual, "B:knd/nerd") So(idxs[2].String(), ShouldEqual, "B:knd/wat") So(idxs[3].String(), ShouldEqual, "B:knd/-nerd") So(idxs[4].String(), ShouldEqual, "B:knd/-wat") }) }) }) }
func indexEntriesWithBuiltins(k *ds.Key, pm ds.PropertyMap, complexIdxs []*ds.IndexDefinition) *memStore { sip := serialize.PropertyMapPartially(k, pm) return indexEntries(sip, k.Namespace(), append(defaultIndexes(k.Kind(), pm), complexIdxs...)) }