From 3a9d3d145acd9e01ffb4c07520d7f270c7f41a99 Mon Sep 17 00:00:00 2001 From: Josh Baker Date: Fri, 8 Jun 2018 09:52:26 -0700 Subject: [PATCH] Fix sporadic kNN results when TTL is used fixes #326 --- pkg/controller/search.go | 95 ++++++++++++++++++++++++++++------------ 1 file changed, 68 insertions(+), 27 deletions(-) diff --git a/pkg/controller/search.go b/pkg/controller/search.go index a1e83c70..86b8e174 100644 --- a/pkg/controller/search.go +++ b/pkg/controller/search.go @@ -87,6 +87,7 @@ 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 @@ -127,6 +128,41 @@ 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 @@ -292,7 +328,8 @@ 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"} +var withinOrIntersectsTypes = []string{ + "geo", "bounds", "hash", "tile", "quadkey", "get", "object", "circle"} func (c *Controller) cmdNearby(msg *server.Message) (res resp.Value, err error) { start := time.Now() @@ -376,32 +413,28 @@ type iterItem struct { dist float64 } -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, -) { +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 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 - }, - ) + 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 }) @@ -456,7 +489,11 @@ 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, minZ, maxZ, + sw.col.Within(s.sparse, + s.o, + s.minLat, s.minLon, s.maxLat, s.maxLon, + 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 @@ -470,7 +507,11 @@ 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, minZ, maxZ, + sw.col.Intersects(s.sparse, + s.o, + s.minLat, s.minLon, s.maxLat, s.maxLon, + 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