mirror of https://github.com/tidwall/tile38.git
Minor optimization to avoid unneeded field merging
This commit is contained in:
parent
a452d45a1e
commit
a824d58419
|
@ -51,8 +51,8 @@ func byExpires(a, b *object.Object) bool {
|
||||||
|
|
||||||
// Collection represents a collection of geojson objects.
|
// Collection represents a collection of geojson objects.
|
||||||
type Collection struct {
|
type Collection struct {
|
||||||
items *btree.BTreeG[*object.Object] // sorted by id
|
objs btree.Map[string, *object.Object] // sorted by id
|
||||||
spatial *rtree.RTreeGN[float32, *object.Object] // geospatially indexed
|
spatial rtree.RTreeGN[float32, *object.Object] // geospatially indexed
|
||||||
values *btree.BTreeG[*object.Object] // sorted by value+id
|
values *btree.BTreeG[*object.Object] // sorted by value+id
|
||||||
expires *btree.BTreeG[*object.Object] // sorted by ex+id
|
expires *btree.BTreeG[*object.Object] // sorted by ex+id
|
||||||
weight int
|
weight int
|
||||||
|
@ -66,10 +66,8 @@ var optsNoLock = btree.Options{NoLocks: true}
|
||||||
// New creates an empty collection
|
// New creates an empty collection
|
||||||
func New() *Collection {
|
func New() *Collection {
|
||||||
col := &Collection{
|
col := &Collection{
|
||||||
items: btree.NewBTreeGOptions(byID, optsNoLock),
|
|
||||||
values: btree.NewBTreeGOptions(byValue, optsNoLock),
|
values: btree.NewBTreeGOptions(byValue, optsNoLock),
|
||||||
expires: btree.NewBTreeGOptions(byExpires, optsNoLock),
|
expires: btree.NewBTreeGOptions(byExpires, optsNoLock),
|
||||||
spatial: &rtree.RTreeGN[float32, *object.Object]{},
|
|
||||||
}
|
}
|
||||||
return col
|
return col
|
||||||
}
|
}
|
||||||
|
@ -163,7 +161,43 @@ func rtreeRect(rect geometry.Rect) (min, max [2]float32) {
|
||||||
// Set adds or replaces an object in the collection and returns the fields
|
// Set adds or replaces an object in the collection and returns the fields
|
||||||
// array.
|
// array.
|
||||||
func (c *Collection) Set(obj *object.Object) (prev *object.Object) {
|
func (c *Collection) Set(obj *object.Object) (prev *object.Object) {
|
||||||
prev, _ = c.items.Set(obj)
|
prev, _ = c.objs.Set(obj.ID(), obj)
|
||||||
|
c.setFill(prev, obj)
|
||||||
|
return prev
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMerged works just like Set but it will merge the new object fields and
|
||||||
|
// the previous object fields and create a newer object that is then set into
|
||||||
|
// the collection. The newer object is returned.
|
||||||
|
func (c *Collection) SetMerged(obj *object.Object,
|
||||||
|
) (prev, newObj *object.Object) {
|
||||||
|
prev, _ = c.objs.Set(obj.ID(), obj)
|
||||||
|
if prev != nil {
|
||||||
|
// Check if at least one field exists from the previous object and
|
||||||
|
// merge the two field lists, then re-set the new object. Otherwise,
|
||||||
|
// we stick with the current object.
|
||||||
|
// TODO: check if the old object has fields that new object does not
|
||||||
|
// and only reset those.
|
||||||
|
ofields := prev.Fields()
|
||||||
|
var reset bool
|
||||||
|
ofields.Scan(func(f field.Field) bool {
|
||||||
|
reset = true
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
if reset {
|
||||||
|
obj.Fields().Scan(func(f field.Field) bool {
|
||||||
|
ofields = ofields.Set(f)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
obj = object.New(obj.ID(), obj.Geo(), obj.Expires(), ofields)
|
||||||
|
c.objs.Set(obj.ID(), obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.setFill(prev, obj)
|
||||||
|
return prev, obj
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Collection) setFill(prev, obj *object.Object) {
|
||||||
if prev != nil {
|
if prev != nil {
|
||||||
if prev.IsSpatial() {
|
if prev.IsSpatial() {
|
||||||
c.indexDelete(prev)
|
c.indexDelete(prev)
|
||||||
|
@ -190,14 +224,12 @@ func (c *Collection) Set(obj *object.Object) (prev *object.Object) {
|
||||||
}
|
}
|
||||||
c.points += obj.Geo().NumPoints()
|
c.points += obj.Geo().NumPoints()
|
||||||
c.weight += obj.Weight()
|
c.weight += obj.Weight()
|
||||||
return prev
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete removes an object and returns it.
|
// Delete removes an object and returns it.
|
||||||
// If the object does not exist then the 'ok' return value will be false.
|
// If the object does not exist then the 'ok' return value will be false.
|
||||||
func (c *Collection) Delete(id string) (prev *object.Object) {
|
func (c *Collection) Delete(id string) (prev *object.Object) {
|
||||||
key := object.New(id, nil, 0, field.List{})
|
prev, _ = c.objs.Delete(id)
|
||||||
prev, _ = c.items.Delete(key)
|
|
||||||
if prev == nil {
|
if prev == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -221,8 +253,7 @@ func (c *Collection) Delete(id string) (prev *object.Object) {
|
||||||
// Get returns an object.
|
// Get returns an object.
|
||||||
// If the object does not exist then the 'ok' return value will be false.
|
// If the object does not exist then the 'ok' return value will be false.
|
||||||
func (c *Collection) Get(id string) *object.Object {
|
func (c *Collection) Get(id string) *object.Object {
|
||||||
key := object.New(id, nil, 0, field.List{})
|
obj, _ := c.objs.Get(id)
|
||||||
obj, _ := c.items.Get(key)
|
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,7 +271,7 @@ func (c *Collection) Scan(
|
||||||
offset = cursor.Offset()
|
offset = cursor.Offset()
|
||||||
cursor.Step(offset)
|
cursor.Step(offset)
|
||||||
}
|
}
|
||||||
iter := func(obj *object.Object) bool {
|
iter := func(_ string, obj *object.Object) bool {
|
||||||
count++
|
count++
|
||||||
if count <= offset {
|
if count <= offset {
|
||||||
return true
|
return true
|
||||||
|
@ -250,9 +281,9 @@ func (c *Collection) Scan(
|
||||||
return keepon
|
return keepon
|
||||||
}
|
}
|
||||||
if desc {
|
if desc {
|
||||||
c.items.Reverse(iter)
|
c.objs.Reverse(iter)
|
||||||
} else {
|
} else {
|
||||||
c.items.Scan(iter)
|
c.objs.Scan(iter)
|
||||||
}
|
}
|
||||||
return keepon
|
return keepon
|
||||||
}
|
}
|
||||||
|
@ -272,7 +303,7 @@ func (c *Collection) ScanRange(
|
||||||
offset = cursor.Offset()
|
offset = cursor.Offset()
|
||||||
cursor.Step(offset)
|
cursor.Step(offset)
|
||||||
}
|
}
|
||||||
iter := func(o *object.Object) bool {
|
iter := func(_ string, o *object.Object) bool {
|
||||||
count++
|
count++
|
||||||
if count <= offset {
|
if count <= offset {
|
||||||
return true
|
return true
|
||||||
|
@ -291,11 +322,10 @@ func (c *Collection) ScanRange(
|
||||||
return keepon
|
return keepon
|
||||||
}
|
}
|
||||||
|
|
||||||
pstart := object.New(start, nil, 0, field.List{})
|
|
||||||
if desc {
|
if desc {
|
||||||
c.items.Descend(pstart, iter)
|
c.objs.Descend(start, iter)
|
||||||
} else {
|
} else {
|
||||||
c.items.Ascend(pstart, iter)
|
c.objs.Ascend(start, iter)
|
||||||
}
|
}
|
||||||
return keepon
|
return keepon
|
||||||
}
|
}
|
||||||
|
@ -385,7 +415,7 @@ func (c *Collection) ScanGreaterOrEqual(id string, desc bool,
|
||||||
offset = cursor.Offset()
|
offset = cursor.Offset()
|
||||||
cursor.Step(offset)
|
cursor.Step(offset)
|
||||||
}
|
}
|
||||||
iter := func(o *object.Object) bool {
|
iter := func(_ string, o *object.Object) bool {
|
||||||
count++
|
count++
|
||||||
if count <= offset {
|
if count <= offset {
|
||||||
return true
|
return true
|
||||||
|
@ -394,11 +424,10 @@ func (c *Collection) ScanGreaterOrEqual(id string, desc bool,
|
||||||
keepon = iterator(o)
|
keepon = iterator(o)
|
||||||
return keepon
|
return keepon
|
||||||
}
|
}
|
||||||
pstart := object.New(id, nil, 0, field.List{})
|
|
||||||
if desc {
|
if desc {
|
||||||
c.items.Descend(pstart, iter)
|
c.objs.Descend(id, iter)
|
||||||
} else {
|
} else {
|
||||||
c.items.Ascend(pstart, iter)
|
c.objs.Ascend(id, iter)
|
||||||
}
|
}
|
||||||
return keepon
|
return keepon
|
||||||
}
|
}
|
||||||
|
|
|
@ -351,11 +351,12 @@ func (fields List) Weight() int {
|
||||||
return x + n
|
return x + n
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bytes returns the raw bytes (including the header)
|
// MakeList returns a field list from an array of fields.
|
||||||
func (fields List) Bytes() []byte {
|
func MakeList(fields []Field) List {
|
||||||
if fields.p == nil {
|
// TODO: optimize to reduce allocations.
|
||||||
return nil
|
var list List
|
||||||
|
for _, f := range fields {
|
||||||
|
list = list.Set(f)
|
||||||
}
|
}
|
||||||
x, n := uvarint(*(*[]byte)(unsafe.Pointer(&bytes{fields.p, 10, 10})))
|
return list
|
||||||
return (*(*[]byte)(unsafe.Pointer(&bytes{fields.p, 0, n + x})))
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -677,31 +677,7 @@ func (s *Server) cmdSET(msg *Message) (resp.Value, commandDetails, error) {
|
||||||
|
|
||||||
// >> Operation
|
// >> Operation
|
||||||
|
|
||||||
var nada bool
|
nada := func() (resp.Value, commandDetails, error) {
|
||||||
col, ok := s.cols.Get(key)
|
|
||||||
if !ok {
|
|
||||||
if xx {
|
|
||||||
nada = true
|
|
||||||
} else {
|
|
||||||
col = collection.New()
|
|
||||||
s.cols.Set(key, col)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var ofields field.List
|
|
||||||
if !nada {
|
|
||||||
o := col.Get(id)
|
|
||||||
if o != nil {
|
|
||||||
ofields = o.Fields()
|
|
||||||
}
|
|
||||||
if xx || nx {
|
|
||||||
if (nx && ok) || (xx && !ok) {
|
|
||||||
nada = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if nada {
|
|
||||||
// exclude operation due to 'xx' or 'nx' match
|
// exclude operation due to 'xx' or 'nx' match
|
||||||
switch msg.OutputType {
|
switch msg.OutputType {
|
||||||
default:
|
default:
|
||||||
|
@ -717,12 +693,29 @@ func (s *Server) cmdSET(msg *Message) (resp.Value, commandDetails, error) {
|
||||||
return retwerr(errors.New("nada unknown output"))
|
return retwerr(errors.New("nada unknown output"))
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, f := range fields {
|
col, ok := s.cols.Get(key)
|
||||||
ofields = ofields.Set(f)
|
if !ok {
|
||||||
|
if xx {
|
||||||
|
return nada()
|
||||||
|
}
|
||||||
|
col = collection.New()
|
||||||
|
s.cols.Set(key, col)
|
||||||
}
|
}
|
||||||
|
|
||||||
obj := object.New(id, oobj, ex, ofields)
|
if xx || nx {
|
||||||
old := col.Set(obj)
|
if col.Get(id) == nil {
|
||||||
|
if xx {
|
||||||
|
return nada()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if nx {
|
||||||
|
return nada()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
obj := object.New(id, oobj, ex, field.MakeList(fields))
|
||||||
|
old, obj := col.SetMerged(obj)
|
||||||
|
|
||||||
// >> Response
|
// >> Response
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue