From cc9320e246afb683bcbe96c7d11273f5a64936dd Mon Sep 17 00:00:00 2001 From: tidwall Date: Fri, 26 Aug 2022 16:23:28 -0700 Subject: [PATCH 1/2] Allow for WHERE for on geofence detection --- internal/collection/collection.go | 4 +++- internal/server/fence.go | 12 +++++++++++- internal/server/scanner.go | 12 +++++++++--- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/internal/collection/collection.go b/internal/collection/collection.go index 2bce6966..63c54713 100644 --- a/internal/collection/collection.go +++ b/internal/collection/collection.go @@ -191,8 +191,10 @@ func (c *Collection) Set( oldFieldValues = c.fieldValues.get(oldItem.fieldValuesSlot) newFieldValues = oldFieldValues newItem.fieldValuesSlot = oldItem.fieldValuesSlot + if len(oldFieldValues) > 0 { + oldFieldValues = append([]float64{}, oldFieldValues...) + } } - if fields == nil { if len(values) > 0 { newFieldValues = values diff --git a/internal/server/fence.go b/internal/server/fence.go index eb0de389..94bc6de5 100644 --- a/internal/server/fence.go +++ b/internal/server/fence.go @@ -105,9 +105,18 @@ func fenceMatch( } detect = "roam" } else { + var nocross bool // not using roaming match1 := fenceMatchObject(fence, details.oldObj) + if match1 { + match1, _, _ = sw.testObject(details.id, details.oldObj, details.oldFields) + nocross = !match1 + } match2 := fenceMatchObject(fence, details.obj) + if match2 { + match2, _, _ = sw.testObject(details.id, details.obj, details.fields) + nocross = !match2 + } if match1 && match2 { detect = "inside" } else if match1 && !match2 { @@ -121,7 +130,7 @@ func fenceMatch( if details.command != "fset" { // Maybe the old object and new object create a line that crosses the fence. // Must detect for that possibility. - if details.oldObj != nil { + if !nocross && details.oldObj != nil { ls := geojson.NewLineString(geometry.NewLine( []geometry.Point{ details.oldObj.Center(), @@ -176,6 +185,7 @@ func fenceMatch( o: details.obj, fields: details.fields, noLock: true, + noTest: true, distance: distance, distOutput: fence.distance, }) diff --git a/internal/server/scanner.go b/internal/server/scanner.go index 7f73a649..839870d8 100644 --- a/internal/server/scanner.go +++ b/internal/server/scanner.go @@ -68,6 +68,7 @@ type ScanWriterParams struct { distance float64 distOutput bool // query or fence requested distance output noLock bool + noTest bool ignoreGlobMatch bool clip geojson.Object skipTesting bool @@ -373,9 +374,14 @@ func (sw *scanWriter) writeObject(opts ScanWriterParams) bool { sw.mu.Lock() defer sw.mu.Unlock() } - ok, keepGoing, _ := sw.testObject(opts.id, opts.o, opts.fields) - if !ok { - return keepGoing + + keepGoing := true + if !opts.noTest { + var ok bool + ok, keepGoing, _ = sw.testObject(opts.id, opts.o, opts.fields) + if !ok { + return keepGoing + } } sw.count++ if sw.output == outputCount { From 67916f38f8aee0abacdd94032deba2102ed807fe Mon Sep 17 00:00:00 2001 From: tidwall Date: Tue, 30 Aug 2022 16:50:19 -0700 Subject: [PATCH 2/2] Reset wheres while geofencing --- internal/server/aof.go | 1 + internal/server/scanner.go | 36 +++++++++++++++++++++++++++--------- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/internal/server/aof.go b/internal/server/aof.go index f90ab6e6..47e92f40 100644 --- a/internal/server/aof.go +++ b/internal/server/aof.go @@ -294,6 +294,7 @@ func (s *Server) queueHooks(d *commandDetails) error { for _, hook := range candidates { // Calculate all matching fence messages for all candidates and append // them to the appropriate message slice + hook.ScanWriter.loadWheres() msgs := FenceMatch(hook.Name, hook.ScanWriter, hook.Fence, hook.Metas, d) if len(msgs) > 0 { if hook.channel { diff --git a/internal/server/scanner.go b/internal/server/scanner.go index 839870d8..b189cd56 100644 --- a/internal/server/scanner.go +++ b/internal/server/scanner.go @@ -33,6 +33,7 @@ type scanWriter struct { mu sync.Mutex s *Server wr *bytes.Buffer + key string msg *Message col *collection.Collection fmap map[string]int @@ -58,6 +59,8 @@ type scanWriter struct { values []resp.Value matchValues bool respOut resp.Value + orgWheres []whereT + orgWhereins []whereinT } // ScanWriterParams ... @@ -97,6 +100,7 @@ func (s *Server) newScanWriter( sw := &scanWriter{ s: s, wr: wr, + key: key, msg: msg, limit: limit, cursor: cursor, @@ -114,34 +118,48 @@ func (s *Server) newScanWriter( sw.globSingle = true } } - sw.col = s.getCol(key) + sw.orgWheres = wheres + sw.orgWhereins = whereins + sw.loadWheres() + return sw, nil +} + +func (sw *scanWriter) loadWheres() { + sw.fmap = nil + sw.farr = nil + sw.wheres = nil + sw.whereins = nil + sw.fvals = nil + sw.col = sw.s.getCol(sw.key) if sw.col != nil { sw.fmap = sw.col.FieldMap() sw.farr = sw.col.FieldArr() // This fills index value in wheres/whereins // so we don't have to map string field names for each tested object var ok bool - if len(wheres) > 0 { - sw.wheres = make([]whereT, len(wheres)) - for i, where := range wheres { + if len(sw.orgWheres) > 0 { + sw.wheres = make([]whereT, len(sw.orgWheres)) + for i, where := range sw.orgWheres { if where.index, ok = sw.fmap[where.field]; !ok { where.index = math.MaxInt32 } sw.wheres[i] = where } } - if len(whereins) > 0 { - sw.whereins = make([]whereinT, len(whereins)) - for i, wherein := range whereins { + if len(sw.orgWhereins) > 0 { + sw.whereins = make([]whereinT, len(sw.orgWhereins)) + for i, wherein := range sw.orgWhereins { if wherein.index, ok = sw.fmap[wherein.field]; !ok { wherein.index = math.MaxInt32 } sw.whereins[i] = wherein } } + if len(sw.farr) > 0 { + sw.fvals = make([]float64, len(sw.farr)) + } } - sw.fvals = make([]float64, len(sw.farr)) - return sw, nil + } func (sw *scanWriter) hasFieldsOutput() bool {