From 47f2e25d27b5077122246cb1614d72365a238f8b Mon Sep 17 00:00:00 2001 From: Josh Baker Date: Fri, 8 Jun 2018 09:45:18 -0700 Subject: [PATCH] Fix sporadic kNN results when TTL is used fixes #326 --- pkg/controller/search.go | 100 ++++++++++++--------------------------- 1 file changed, 31 insertions(+), 69 deletions(-) diff --git a/pkg/controller/search.go b/pkg/controller/search.go index ddd503c8..a1e83c70 100644 --- a/pkg/controller/search.go +++ b/pkg/controller/search.go @@ -87,7 +87,6 @@ func (c *Controller) cmdSearchArgs(cmd string, vs []resp.Value, types []string) err = errInvalidArgument(typ) return } - s.meters = -1 // this will become non-negative if search is within a circle switch ltyp { case "point": var slat, slon, smeters string @@ -128,41 +127,6 @@ func (c *Controller) cmdSearchArgs(cmd string, vs []resp.Value, types []string) err = errInvalidArgument(smeters) return } - if s.meters < 0 { - err = errInvalidArgument(smeters) - return - } - } - case "circle": - var slat, slon, smeters string - if vs, slat, ok = tokenval(vs); !ok || slat == "" { - err = errInvalidNumberOfArguments - return - } - if vs, slon, ok = tokenval(vs); !ok || slon == "" { - err = errInvalidNumberOfArguments - return - } - if vs, smeters, ok = tokenval(vs); !ok || smeters == "" { - err = errInvalidArgument(slat) - return - } - - if s.lat, err = strconv.ParseFloat(slat, 64); err != nil { - err = errInvalidArgument(slat) - return - } - if s.lon, err = strconv.ParseFloat(slon, 64); err != nil { - err = errInvalidArgument(slon) - return - } - if s.meters, err = strconv.ParseFloat(smeters, 64); err != nil { - err = errInvalidArgument(smeters) - return - } - if s.meters < 0 { - err = errInvalidArgument(smeters) - return } case "object": var obj string @@ -328,8 +292,7 @@ func (c *Controller) cmdSearchArgs(cmd string, vs []resp.Value, types []string) } var nearbyTypes = []string{"point"} -var withinOrIntersectsTypes = []string{ - "geo", "bounds", "hash", "tile", "quadkey", "get", "object", "circle"} +var withinOrIntersectsTypes = []string{"geo", "bounds", "hash", "tile", "quadkey", "get", "object"} func (c *Controller) cmdNearby(msg *server.Message) (res resp.Value, err error) { start := time.Now() @@ -367,9 +330,6 @@ func (c *Controller) cmdNearby(msg *server.Message) (res resp.Value, err error) if sw.col != nil { var matched uint32 iter := func(id string, o geojson.Object, fields []float64, dist *float64) bool { - if c.hasExpired(s.key, id) { - return true - } // Calculate distance if we need to distance := 0.0 if s.distance { @@ -389,10 +349,13 @@ func (c *Controller) cmdNearby(msg *server.Message) (res resp.Value, err error) }) } if s.knn { - nearestNeighbors(sw, s.lat, s.lon, &matched, iter) + c.nearestNeighbors(&s, sw, s.lat, s.lon, &matched, iter) } else { sw.col.Nearby(s.sparse, s.lat, s.lon, s.meters, minZ, maxZ, func(id string, o geojson.Object, fields []float64) bool { + if c.hasExpired(s.key, id) { + return true + } return iter(id, o, fields, nil) }, ) @@ -413,25 +376,32 @@ type iterItem struct { dist float64 } -func nearestNeighbors(sw *scanWriter, lat, lon float64, matched *uint32, - iter func(id string, o geojson.Object, fields []float64, dist *float64) bool) { +func (c *Controller) nearestNeighbors( + s *liveFenceSwitches, sw *scanWriter, lat, lon float64, matched *uint32, + iter func(id string, o geojson.Object, fields []float64, dist *float64) bool, +) { limit := int(sw.cursor + sw.limit) var items []iterItem - sw.col.NearestNeighbors(lat, lon, func(id string, o geojson.Object, fields []float64) bool { - if _, ok := sw.fieldMatch(fields, o); !ok { - return true - } - match, keepGoing := sw.globMatch(id, o) - if !match { - return true - } - dist := o.CalculatedPoint().DistanceTo(geojson.Position{X: lon, Y: lat, Z: 0}) - items = append(items, iterItem{id: id, o: o, fields: fields, dist: dist}) - if !keepGoing { - return false - } - return len(items) < limit - }) + sw.col.NearestNeighbors(lat, lon, + func(id string, o geojson.Object, fields []float64) bool { + if c.hasExpired(s.key, id) { + return true + } + if _, ok := sw.fieldMatch(fields, o); !ok { + return true + } + match, keepGoing := sw.globMatch(id, o) + if !match { + return true + } + dist := o.CalculatedPoint().DistanceTo(geojson.Position{X: lon, Y: lat, Z: 0}) + items = append(items, iterItem{id: id, o: o, fields: fields, dist: dist}) + if !keepGoing { + return false + } + return len(items) < limit + }, + ) sort.Slice(items, func(i, j int) bool { return items[i].dist < items[j].dist }) @@ -486,11 +456,7 @@ func (c *Controller) cmdWithinOrIntersects(cmd string, msg *server.Message) (res if sw.col != nil { minZ, maxZ := zMinMaxFromWheres(s.wheres) if cmd == "within" { - sw.col.Within(s.sparse, - s.o, - s.minLat, s.minLon, s.maxLat, s.maxLon, - s.lat, s.lon, s.meters, - minZ, maxZ, + sw.col.Within(s.sparse, s.o, s.minLat, s.minLon, s.maxLat, s.maxLon, minZ, maxZ, func(id string, o geojson.Object, fields []float64) bool { if c.hasExpired(s.key, id) { return true @@ -504,11 +470,7 @@ func (c *Controller) cmdWithinOrIntersects(cmd string, msg *server.Message) (res }, ) } else if cmd == "intersects" { - sw.col.Intersects(s.sparse, - s.o, - s.minLat, s.minLon, s.maxLat, s.maxLon, - s.lat, s.lon, s.meters, - minZ, maxZ, + sw.col.Intersects(s.sparse, s.o, s.minLat, s.minLon, s.maxLat, s.maxLon, minZ, maxZ, func(id string, o geojson.Object, fields []float64) bool { if c.hasExpired(s.key, id) { return true