mirror of https://github.com/tidwall/tile38.git
Merge branch 'always_knn_nearby' of https://github.com/rshura/tile38 into rshura-always_knn_nearby
This commit is contained in:
commit
b17bbbd829
|
@ -227,7 +227,7 @@
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:6fc1b77dedecd24d417a38c4ffc580c0c4de1540b60453efee44f1a41b205d27"
|
digest = "1:4be7626fb8f801eb85aa7494ce3a504c9b4121a07f4ec19d7d204185bd6397d5"
|
||||||
name = "github.com/tidwall/geojson"
|
name = "github.com/tidwall/geojson"
|
||||||
packages = [
|
packages = [
|
||||||
".",
|
".",
|
||||||
|
@ -235,7 +235,7 @@
|
||||||
"geometry",
|
"geometry",
|
||||||
]
|
]
|
||||||
pruneopts = ""
|
pruneopts = ""
|
||||||
revision = "581e33d25c96a70d4006cbeca5fcd716caad7766"
|
revision = "928ede3da18d831dea0af0bb26adeb025145c23b"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:3ddca2bd5496c6922a2a9e636530e178a43c2a534ea6634211acdc7d10222794"
|
digest = "1:3ddca2bd5496c6922a2a9e636530e178a43c2a534ea6634211acdc7d10222794"
|
||||||
|
|
|
@ -24,7 +24,6 @@ type liveFenceSwitches struct {
|
||||||
obj geojson.Object
|
obj geojson.Object
|
||||||
cmd string
|
cmd string
|
||||||
roam roamSwitches
|
roam roamSwitches
|
||||||
knn bool
|
|
||||||
groups map[string]string
|
groups map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,19 +118,6 @@ func (c *Controller) cmdSearchArgs(
|
||||||
err = errInvalidNumberOfArguments
|
err = errInvalidNumberOfArguments
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
umeters := true
|
|
||||||
if vs, smeters, ok = tokenval(vs); !ok || smeters == "" {
|
|
||||||
umeters = false
|
|
||||||
if cmd == "nearby" {
|
|
||||||
// possible that this is KNN search
|
|
||||||
s.knn = s.searchScanBaseTokens.ulimit && // must be true
|
|
||||||
!s.searchScanBaseTokens.usparse // must be false
|
|
||||||
}
|
|
||||||
if !s.knn {
|
|
||||||
err = errInvalidArgument(slat)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var lat, lon, meters float64
|
var lat, lon, meters float64
|
||||||
if lat, err = strconv.ParseFloat(slat, 64); err != nil {
|
if lat, err = strconv.ParseFloat(slat, 64); err != nil {
|
||||||
err = errInvalidArgument(slat)
|
err = errInvalidArgument(slat)
|
||||||
|
@ -141,7 +127,25 @@ func (c *Controller) cmdSearchArgs(
|
||||||
err = errInvalidArgument(slon)
|
err = errInvalidArgument(slon)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if umeters {
|
// radius is optional for nearby, but mandatory for others
|
||||||
|
if cmd == "nearby" {
|
||||||
|
if vs, smeters, ok = tokenval(vs); ok && smeters != "" {
|
||||||
|
if meters, err = strconv.ParseFloat(smeters, 64); err != nil {
|
||||||
|
err = errInvalidArgument(smeters)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if meters < 0 {
|
||||||
|
err = errInvalidArgument(smeters)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
meters = -1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if vs, smeters, ok = tokenval(vs); !ok || smeters == "" {
|
||||||
|
err = errInvalidNumberOfArguments
|
||||||
|
return
|
||||||
|
}
|
||||||
if meters, err = strconv.ParseFloat(smeters, 64); err != nil {
|
if meters, err = strconv.ParseFloat(smeters, 64); err != nil {
|
||||||
err = errInvalidArgument(smeters)
|
err = errInvalidArgument(smeters)
|
||||||
return
|
return
|
||||||
|
@ -151,12 +155,7 @@ func (c *Controller) cmdSearchArgs(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if s.knn {
|
s.obj = geojson.NewCircle(geometry.Point{X: lon, Y: lat}, meters, defaultCircleSteps)
|
||||||
s.obj = geojson.NewPoint(geometry.Point{X: lon, Y: lat})
|
|
||||||
} else {
|
|
||||||
s.obj = geojson.NewCircle(geometry.Point{X: lon, Y: lat},
|
|
||||||
meters, defaultCircleSteps)
|
|
||||||
}
|
|
||||||
case "object":
|
case "object":
|
||||||
if s.clip {
|
if s.clip {
|
||||||
err = errInvalidArgument("cannnot clip with object")
|
err = errInvalidArgument("cannnot clip with object")
|
||||||
|
@ -388,21 +387,10 @@ func (c *Controller) cmdNearby(msg *server.Message) (res resp.Value, err error)
|
||||||
fields: fields,
|
fields: fields,
|
||||||
distance: distance,
|
distance: distance,
|
||||||
noLock: true,
|
noLock: true,
|
||||||
ignoreGlobMatch: s.knn,
|
ignoreGlobMatch: true,
|
||||||
})
|
|
||||||
}
|
|
||||||
if s.knn {
|
|
||||||
c.nearestNeighbors(&s, sw, s.obj, &matched, iter)
|
|
||||||
} else {
|
|
||||||
sw.col.Intersects(s.obj, s.sparse, func(
|
|
||||||
id string, o geojson.Object, fields []float64,
|
|
||||||
) bool {
|
|
||||||
if c.hasExpired(s.key, id) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return iter(id, o, fields, nil)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
c.nearestNeighbors(&s, sw, s.obj.(*geojson.Circle), &matched, iter)
|
||||||
}
|
}
|
||||||
sw.writeFoot()
|
sw.writeFoot()
|
||||||
if msg.OutputType == server.JSON {
|
if msg.OutputType == server.JSON {
|
||||||
|
@ -420,7 +408,7 @@ type iterItem struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) nearestNeighbors(
|
func (c *Controller) nearestNeighbors(
|
||||||
s *liveFenceSwitches, sw *scanWriter, target geojson.Object, matched *uint32,
|
s *liveFenceSwitches, sw *scanWriter, target *geojson.Circle, matched *uint32,
|
||||||
iter func(id string, o geojson.Object, fields []float64, dist *float64,
|
iter func(id string, o geojson.Object, fields []float64, dist *float64,
|
||||||
) bool) {
|
) bool) {
|
||||||
limit := int(sw.cursor + sw.limit)
|
limit := int(sw.cursor + sw.limit)
|
||||||
|
@ -437,6 +425,9 @@ func (c *Controller) nearestNeighbors(
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
dist := o.Distance(target)
|
dist := o.Distance(target)
|
||||||
|
if target.Meters() > 0 && dist > target.Meters() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
items = append(items, iterItem{id: id, o: o, fields: fields, dist: dist})
|
items = append(items, iterItem{id: id, o: o, fields: fields, dist: dist})
|
||||||
if !keepGoing {
|
if !keepGoing {
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package tests
|
package tests
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
func subTestSearch(t *testing.T, mc *mockServer) {
|
func subTestSearch(t *testing.T, mc *mockServer) {
|
||||||
runStep(t, mc, "KNN", keys_KNN_test)
|
runStep(t, mc, "KNN", keys_KNN_test)
|
||||||
|
@ -17,16 +19,10 @@ func keys_KNN_test(mc *mockServer) error {
|
||||||
{"SET", "mykey", "3", "POINT", 12, 19}, {"OK"},
|
{"SET", "mykey", "3", "POINT", 12, 19}, {"OK"},
|
||||||
{"SET", "mykey", "4", "POINT", -5, 5}, {"OK"},
|
{"SET", "mykey", "4", "POINT", -5, 5}, {"OK"},
|
||||||
{"SET", "mykey", "5", "POINT", 33, 21}, {"OK"},
|
{"SET", "mykey", "5", "POINT", 33, 21}, {"OK"},
|
||||||
{"NEARBY", "mykey", "LIMIT", 10, "DISTANCE", "POINTS", "POINT", 20, 20}, {
|
{"NEARBY", "mykey", "LIMIT", 10, "POINTS", "POINT", 20, 20}, {
|
||||||
"" +
|
"[0 [[2 [19 19]] [3 [12 19]] [5 [33 21]] [1 [5 5]] [4 [-5 5]]]]"},
|
||||||
"[0 [" +
|
{"NEARBY", "mykey", "LIMIT", 10, "IDS", "POINT", 20, 20, 4000000}, {"[0 [2 3 5 1 4]]"},
|
||||||
("" +
|
{"NEARBY", "mykey", "LIMIT", 10, "IDS", "POINT", 20, 20, 1500000}, {"[0 [2 3 5]]"},
|
||||||
"[2 [19 19] 152808.67164036975] " +
|
|
||||||
"[3 [12 19] 895945.1409106685] " +
|
|
||||||
"[5 [33 21] 1448929.5916252395] " +
|
|
||||||
"[1 [5 5] 2327116.1069888202] " +
|
|
||||||
"[4 [-5 5] 3227402.6159841116]") +
|
|
||||||
"]]"},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,3 +70,11 @@ func (g *Circle) JSON() string {
|
||||||
func (g *Circle) String() string {
|
func (g *Circle) String() string {
|
||||||
return string(g.AppendJSON(nil))
|
return string(g.AppendJSON(nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *Circle) Meters() float64 {
|
||||||
|
return g.meters
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Circle) Center() geometry.Point {
|
||||||
|
return g.center
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue