diff --git a/internal/collection/collection.go b/internal/collection/collection.go index 86bc4a41..b9120cf7 100644 --- a/internal/collection/collection.go +++ b/internal/collection/collection.go @@ -497,14 +497,21 @@ func (c *Collection) geoSparseInner( // Within returns all object that are fully contained within an object or // bounding box. Set obj to nil in order to use the bounding box. func (c *Collection) Within( - obj geojson.Object, sparse uint8, + obj geojson.Object, + offset uint64, + sparse uint8, iter func(id string, obj geojson.Object, fields []float64) bool, ) bool { + var count uint64 if sparse > 0 { return c.geoSparse(obj, sparse, func(id string, o geojson.Object, fields []float64) ( match, ok bool, ) { + count++ + if count <= offset { + return false, true + } if match = o.Within(obj); match { ok = iter(id, o, fields) } @@ -514,6 +521,10 @@ func (c *Collection) Within( } return c.geoSearch(obj.Rect(), func(id string, o geojson.Object, fields []float64) bool { + count++ + if count <= offset { + return true + } if o.Within(obj) { return iter(id, o, fields) } @@ -525,14 +536,21 @@ func (c *Collection) Within( // Intersects returns all object that are intersect an object or bounding box. // Set obj to nil in order to use the bounding box. func (c *Collection) Intersects( - obj geojson.Object, sparse uint8, + obj geojson.Object, + offset uint64, + sparse uint8, iter func(id string, obj geojson.Object, fields []float64) bool, ) bool { + var count uint64 if sparse > 0 { return c.geoSparse(obj, sparse, func(id string, o geojson.Object, fields []float64) ( match, ok bool, ) { + count++ + if count <= offset { + return false, true + } if match = o.Intersects(obj); match { ok = iter(id, o, fields) } @@ -542,6 +560,10 @@ func (c *Collection) Intersects( } return c.geoSearch(obj.Rect(), func(id string, o geojson.Object, fields []float64) bool { + count++ + if count <= offset { + return true + } if o.Intersects(obj) { return iter(id, o, fields) } @@ -553,14 +575,20 @@ func (c *Collection) Intersects( // Nearby returns the nearest neighbors func (c *Collection) Nearby( target geojson.Object, + offset uint64, iter func(id string, obj geojson.Object, fields []float64) bool, ) bool { alive := true center := target.Center() + var count uint64 c.index.Nearby( []float64{center.X, center.Y}, []float64{center.X, center.Y}, func(_, _ []float64, itemv interface{}) bool { + count++ + if count <= offset { + return true + } item := itemv.(*itemT) alive = iter(item.id, item.obj, c.getFieldValues(item.id)) return alive diff --git a/internal/server/fence.go b/internal/server/fence.go index 0390f93f..aedeaf12 100644 --- a/internal/server/fence.go +++ b/internal/server/fence.go @@ -364,7 +364,7 @@ func fenceMatchRoam( prevNearbys := fence.roam.nearbys[tid] var newNearbys map[string]bool - col.Intersects(obj, 0, func( + col.Intersects(obj, 0, 0, func( id string, obj2 geojson.Object, fields []float64, ) bool { if c.hasExpired(fence.roam.key, id) { diff --git a/internal/server/scanner.go b/internal/server/scanner.go index 4c8c2b67..3215f123 100644 --- a/internal/server/scanner.go +++ b/internal/server/scanner.go @@ -68,6 +68,7 @@ type ScanWriterParams struct { noLock bool ignoreGlobMatch bool clip geojson.Object + skipTesting bool } func (c *Server) newScanWriter( @@ -324,28 +325,38 @@ func (sw *scanWriter) globMatch(id string, o geojson.Object) (ok, keepGoing bool return true, true } +// ok is whether the object passes the test and should be written +// keepGoing is whether there could be more objects to test +func (sw *scanWriter) testObject(id string, o geojson.Object, fields []float64, ignoreGlobMatch bool) ( + ok, keepGoing bool, fieldVals []float64) { + if !ignoreGlobMatch { + match, kg := sw.globMatch(id, o) + if !match { + return true, kg, fieldVals + } + } + nf, ok := sw.fieldMatch(fields, o) + return ok,true, nf +} + //id string, o geojson.Object, fields []float64, noLock bool func (sw *scanWriter) writeObject(opts ScanWriterParams) bool { if !opts.noLock { sw.mu.Lock() defer sw.mu.Unlock() } + var fieldVals []float64 + var ok bool keepGoing := true - if !opts.ignoreGlobMatch { - var match bool - match, keepGoing = sw.globMatch(opts.id, opts.o) - if !match { - return true + if opts.skipTesting { + fieldVals = sw.fvals + } else { + ok, keepGoing, fieldVals = sw.testObject(opts.id, opts.o, opts.fields, opts.ignoreGlobMatch) + if !ok { + return keepGoing } } - nfields, ok := sw.fieldMatch(opts.fields, opts.o) - if !ok { - return true - } sw.count++ - if sw.count <= sw.cursor { - return true - } if sw.output == outputCount { return sw.count < sw.limit } @@ -382,7 +393,7 @@ func (sw *scanWriter) writeObject(opts ScanWriterParams) bool { } else if len(sw.farr) > 0 { jsfields = `,"fields":[` - for i, field := range nfields { + for i, field := range fieldVals { if i > 0 { jsfields += "," } diff --git a/internal/server/search.go b/internal/server/search.go index cb9ebda7..1a099b58 100644 --- a/internal/server/search.go +++ b/internal/server/search.go @@ -383,6 +383,7 @@ func (server *Server) cmdNearby(msg *Message) (res resp.Value, err error) { distance: distance, noLock: true, ignoreGlobMatch: true, + skipTesting: true, }) } server.nearestNeighbors(&s, sw, s.obj.(*geojson.Circle), &matched, iter) @@ -406,18 +407,15 @@ func (server *Server) nearestNeighbors( s *liveFenceSwitches, sw *scanWriter, target *geojson.Circle, matched *uint32, iter func(id string, o geojson.Object, fields []float64, dist *float64, ) bool) { - limit := int(sw.cursor + sw.limit) maxDist := target.Haversine() + limit := int(sw.limit) var items []iterItem - sw.col.Nearby(target, func(id string, o geojson.Object, fields []float64) bool { + sw.col.Nearby(target, sw.cursor, func(id string, o geojson.Object, fields []float64) bool { if server.hasExpired(s.key, id) { return true } - if _, ok := sw.fieldMatch(fields, o); !ok { - return true - } - match, keepGoing := sw.globMatch(id, o) - if !match { + ok, keepGoing, _ := sw.testObject(id, o, fields,true) + if !ok { return true } dist := target.HaversineTo(o.Center()) @@ -483,7 +481,7 @@ func (server *Server) cmdWithinOrIntersects(cmd string, msg *Message) (res resp. sw.writeHead() if sw.col != nil { if cmd == "within" { - sw.col.Within(s.obj, s.sparse, func( + sw.col.Within(s.obj, s.cursor, s.sparse, func( id string, o geojson.Object, fields []float64, ) bool { if server.hasExpired(s.key, id) { @@ -497,7 +495,7 @@ func (server *Server) cmdWithinOrIntersects(cmd string, msg *Message) (res resp. }) }) } else if cmd == "intersects" { - sw.col.Intersects(s.obj, s.sparse, func( + sw.col.Intersects(s.obj, s.cursor, s.sparse, func( id string, o geojson.Object, fields []float64,