Merge pull request #652 from tidwall/geofence-where

Allow WHERE for geofence detection
This commit is contained in:
Josh Baker 2022-09-01 18:18:23 -07:00 committed by GitHub
commit d2953307a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 51 additions and 14 deletions

View File

@ -191,8 +191,10 @@ func (c *Collection) Set(
oldFieldValues = c.fieldValues.get(oldItem.fieldValuesSlot) oldFieldValues = c.fieldValues.get(oldItem.fieldValuesSlot)
newFieldValues = oldFieldValues newFieldValues = oldFieldValues
newItem.fieldValuesSlot = oldItem.fieldValuesSlot newItem.fieldValuesSlot = oldItem.fieldValuesSlot
if len(oldFieldValues) > 0 {
oldFieldValues = append([]float64{}, oldFieldValues...)
}
} }
if fields == nil { if fields == nil {
if len(values) > 0 { if len(values) > 0 {
newFieldValues = values newFieldValues = values

View File

@ -294,6 +294,7 @@ func (s *Server) queueHooks(d *commandDetails) error {
for _, hook := range candidates { for _, hook := range candidates {
// Calculate all matching fence messages for all candidates and append // Calculate all matching fence messages for all candidates and append
// them to the appropriate message slice // them to the appropriate message slice
hook.ScanWriter.loadWheres()
msgs := FenceMatch(hook.Name, hook.ScanWriter, hook.Fence, hook.Metas, d) msgs := FenceMatch(hook.Name, hook.ScanWriter, hook.Fence, hook.Metas, d)
if len(msgs) > 0 { if len(msgs) > 0 {
if hook.channel { if hook.channel {

View File

@ -105,9 +105,18 @@ func fenceMatch(
} }
detect = "roam" detect = "roam"
} else { } else {
var nocross bool
// not using roaming // not using roaming
match1 := fenceMatchObject(fence, details.oldObj) match1 := fenceMatchObject(fence, details.oldObj)
if match1 {
match1, _, _ = sw.testObject(details.id, details.oldObj, details.oldFields)
nocross = !match1
}
match2 := fenceMatchObject(fence, details.obj) match2 := fenceMatchObject(fence, details.obj)
if match2 {
match2, _, _ = sw.testObject(details.id, details.obj, details.fields)
nocross = !match2
}
if match1 && match2 { if match1 && match2 {
detect = "inside" detect = "inside"
} else if match1 && !match2 { } else if match1 && !match2 {
@ -121,7 +130,7 @@ func fenceMatch(
if details.command != "fset" { if details.command != "fset" {
// Maybe the old object and new object create a line that crosses the fence. // Maybe the old object and new object create a line that crosses the fence.
// Must detect for that possibility. // Must detect for that possibility.
if details.oldObj != nil { if !nocross && details.oldObj != nil {
ls := geojson.NewLineString(geometry.NewLine( ls := geojson.NewLineString(geometry.NewLine(
[]geometry.Point{ []geometry.Point{
details.oldObj.Center(), details.oldObj.Center(),
@ -176,6 +185,7 @@ func fenceMatch(
o: details.obj, o: details.obj,
fields: details.fields, fields: details.fields,
noLock: true, noLock: true,
noTest: true,
distance: distance, distance: distance,
distOutput: fence.distance, distOutput: fence.distance,
}) })

View File

@ -33,6 +33,7 @@ type scanWriter struct {
mu sync.Mutex mu sync.Mutex
s *Server s *Server
wr *bytes.Buffer wr *bytes.Buffer
key string
msg *Message msg *Message
col *collection.Collection col *collection.Collection
fmap map[string]int fmap map[string]int
@ -58,6 +59,8 @@ type scanWriter struct {
values []resp.Value values []resp.Value
matchValues bool matchValues bool
respOut resp.Value respOut resp.Value
orgWheres []whereT
orgWhereins []whereinT
} }
// ScanWriterParams ... // ScanWriterParams ...
@ -68,6 +71,7 @@ type ScanWriterParams struct {
distance float64 distance float64
distOutput bool // query or fence requested distance output distOutput bool // query or fence requested distance output
noLock bool noLock bool
noTest bool
ignoreGlobMatch bool ignoreGlobMatch bool
clip geojson.Object clip geojson.Object
skipTesting bool skipTesting bool
@ -96,6 +100,7 @@ func (s *Server) newScanWriter(
sw := &scanWriter{ sw := &scanWriter{
s: s, s: s,
wr: wr, wr: wr,
key: key,
msg: msg, msg: msg,
limit: limit, limit: limit,
cursor: cursor, cursor: cursor,
@ -113,34 +118,48 @@ func (s *Server) newScanWriter(
sw.globSingle = true 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 { if sw.col != nil {
sw.fmap = sw.col.FieldMap() sw.fmap = sw.col.FieldMap()
sw.farr = sw.col.FieldArr() sw.farr = sw.col.FieldArr()
// This fills index value in wheres/whereins // This fills index value in wheres/whereins
// so we don't have to map string field names for each tested object // so we don't have to map string field names for each tested object
var ok bool var ok bool
if len(wheres) > 0 { if len(sw.orgWheres) > 0 {
sw.wheres = make([]whereT, len(wheres)) sw.wheres = make([]whereT, len(sw.orgWheres))
for i, where := range wheres { for i, where := range sw.orgWheres {
if where.index, ok = sw.fmap[where.field]; !ok { if where.index, ok = sw.fmap[where.field]; !ok {
where.index = math.MaxInt32 where.index = math.MaxInt32
} }
sw.wheres[i] = where sw.wheres[i] = where
} }
} }
if len(whereins) > 0 { if len(sw.orgWhereins) > 0 {
sw.whereins = make([]whereinT, len(whereins)) sw.whereins = make([]whereinT, len(sw.orgWhereins))
for i, wherein := range whereins { for i, wherein := range sw.orgWhereins {
if wherein.index, ok = sw.fmap[wherein.field]; !ok { if wherein.index, ok = sw.fmap[wherein.field]; !ok {
wherein.index = math.MaxInt32 wherein.index = math.MaxInt32
} }
sw.whereins[i] = wherein 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 { func (sw *scanWriter) hasFieldsOutput() bool {
@ -373,10 +392,15 @@ func (sw *scanWriter) writeObject(opts ScanWriterParams) bool {
sw.mu.Lock() sw.mu.Lock()
defer sw.mu.Unlock() defer sw.mu.Unlock()
} }
ok, keepGoing, _ := sw.testObject(opts.id, opts.o, opts.fields)
keepGoing := true
if !opts.noTest {
var ok bool
ok, keepGoing, _ = sw.testObject(opts.id, opts.o, opts.fields)
if !ok { if !ok {
return keepGoing return keepGoing
} }
}
sw.count++ sw.count++
if sw.output == outputCount { if sw.output == outputCount {
return sw.count < sw.limit return sw.count < sw.limit