Added distance to NEARBY command

This commit is contained in:
w1n2k 2017-01-10 19:49:48 +03:00
parent c02609ad44
commit 1b20a4c590
5 changed files with 98 additions and 29 deletions

View File

@ -149,7 +149,13 @@ func fenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, metas
sw.fmap = details.fmap sw.fmap = details.fmap
sw.fullFields = true sw.fullFields = true
sw.msg.OutputType = server.JSON sw.msg.OutputType = server.JSON
sw.writeObject(details.id, details.obj, details.fields, true) sw.writeObject(ScanWriterParams{
id:details.id,
o: details.obj,
fields: details.fields,
noLock: true,
})
if sw.wr.Len() == 0 { if sw.wr.Len() == 0 {
sw.mu.Unlock() sw.mu.Unlock()
return nil return nil

View File

@ -50,14 +50,22 @@ func (c *Controller) cmdScan(msg *server.Message) (res string, err error) {
if g.Limits[0] == "" && g.Limits[1] == "" { if g.Limits[0] == "" && g.Limits[1] == "" {
s.cursor = sw.col.Scan(s.cursor, s.desc, s.cursor = sw.col.Scan(s.cursor, s.desc,
func(id string, o geojson.Object, fields []float64) bool { func(id string, o geojson.Object, fields []float64) bool {
return sw.writeObject(id, o, fields, false) return sw.writeObject(ScanWriterParams{
id: id,
o: o,
fields: fields,
})
}, },
) )
} else { } else {
s.cursor = sw.col.ScanRange( s.cursor = sw.col.ScanRange(
s.cursor, g.Limits[0], g.Limits[1], s.desc, s.cursor, g.Limits[0], g.Limits[1], s.desc,
func(id string, o geojson.Object, fields []float64) bool { func(id string, o geojson.Object, fields []float64) bool {
return sw.writeObject(id, o, fields, false) return sw.writeObject(ScanWriterParams{
id: id,
o: o,
fields: fields,
})
}, },
) )
} }

View File

@ -54,6 +54,14 @@ type scanWriter struct {
matchValues bool matchValues bool
} }
type ScanWriterParams struct {
id string
o geojson.Object
fields []float64
distance float64
noLock bool
}
func (c *Controller) newScanWriter( func (c *Controller) newScanWriter(
wr *bytes.Buffer, msg *server.Message, key string, output outputT, wr *bytes.Buffer, msg *server.Message, key string, output outputT,
precision uint64, globPattern string, matchValues bool, precision uint64, globPattern string, matchValues bool,
@ -233,24 +241,25 @@ func (sw *scanWriter) fieldMatch(fields []float64, o geojson.Object) ([]float64,
return sw.fvals, true return sw.fvals, true
} }
func (sw *scanWriter) writeObject(id string, o geojson.Object, fields []float64, noLock bool) bool { //id string, o geojson.Object, fields []float64, noLock bool
if !noLock { func (sw *scanWriter) writeObject(opts ScanWriterParams) bool {
if !opts.noLock {
sw.mu.Lock() sw.mu.Lock()
defer sw.mu.Unlock() defer sw.mu.Unlock()
} }
keepGoing := true keepGoing := true
if !sw.globEverything { if !sw.globEverything {
if sw.globSingle { if sw.globSingle {
if sw.globPattern != id { if sw.globPattern != opts.id {
return true return true
} }
keepGoing = false // return current object and stop iterating keepGoing = false // return current object and stop iterating
} else { } else {
var val string var val string
if sw.matchValues { if sw.matchValues {
val = o.String() val = opts.o.String()
} else { } else {
val = id val = opts.id
} }
ok, _ := glob.Match(sw.globPattern, val) ok, _ := glob.Match(sw.globPattern, val)
if !ok { if !ok {
@ -258,7 +267,7 @@ func (sw *scanWriter) writeObject(id string, o geojson.Object, fields []float64,
} }
} }
} }
nfields, ok := sw.fieldMatch(fields, o) nfields, ok := sw.fieldMatch(opts.fields, opts.o)
if !ok { if !ok {
return true return true
} }
@ -282,12 +291,12 @@ func (sw *scanWriter) writeObject(id string, o geojson.Object, fields []float64,
jsfields = `,"fields":{` jsfields = `,"fields":{`
var i int var i int
for field, idx := range sw.fmap { for field, idx := range sw.fmap {
if len(fields) > idx { if len(opts.fields) > idx {
if fields[idx] != 0 { if opts.fields[idx] != 0 {
if i > 0 { if i > 0 {
jsfields += `,` jsfields += `,`
} }
jsfields += jsonString(field) + ":" + strconv.FormatFloat(fields[idx], 'f', -1, 64) jsfields += jsonString(field) + ":" + strconv.FormatFloat(opts.fields[idx], 'f', -1, 64)
i++ i++
} }
} }
@ -307,38 +316,44 @@ func (sw *scanWriter) writeObject(id string, o geojson.Object, fields []float64,
} }
} }
if sw.output == outputIDs { if sw.output == outputIDs {
wr.WriteString(jsonString(id)) wr.WriteString(jsonString(opts.id))
} else { } else {
wr.WriteString(`{"id":` + jsonString(id)) wr.WriteString(`{"id":` + jsonString(opts.id))
switch sw.output { switch sw.output {
case outputObjects: case outputObjects:
wr.WriteString(`,"object":` + o.JSON()) wr.WriteString(`,"object":` + opts.o.JSON())
case outputPoints: case outputPoints:
wr.WriteString(`,"point":` + o.CalculatedPoint().ExternalJSON()) wr.WriteString(`,"point":` + opts.o.CalculatedPoint().ExternalJSON())
case outputHashes: case outputHashes:
p, err := o.Geohash(int(sw.precision)) p, err := opts.o.Geohash(int(sw.precision))
if err != nil { if err != nil {
p = "" p = ""
} }
wr.WriteString(`,"hash":"` + p + `"`) wr.WriteString(`,"hash":"` + p + `"`)
case outputBounds: case outputBounds:
wr.WriteString(`,"bounds":` + o.CalculatedBBox().ExternalJSON()) wr.WriteString(`,"bounds":` + opts.o.CalculatedBBox().ExternalJSON())
} }
wr.WriteString(jsfields) wr.WriteString(jsfields)
if opts.distance > 0 {
wr.WriteString(`,"distance":` + strconv.FormatFloat(opts.distance, 'f', 2, 64))
}
wr.WriteString(`}`) wr.WriteString(`}`)
} }
sw.wr.Write(wr.Bytes()) sw.wr.Write(wr.Bytes())
case server.RESP: case server.RESP:
vals := make([]resp.Value, 1, 3) vals := make([]resp.Value, 1, 3)
vals[0] = resp.StringValue(id) vals[0] = resp.StringValue(opts.id)
if sw.output == outputIDs { if sw.output == outputIDs {
sw.values = append(sw.values, vals[0]) sw.values = append(sw.values, vals[0])
} else { } else {
switch sw.output { switch sw.output {
case outputObjects: case outputObjects:
vals = append(vals, resp.StringValue(o.String())) vals = append(vals, resp.StringValue(opts.o.String()))
case outputPoints: case outputPoints:
point := o.CalculatedPoint() point := opts.o.CalculatedPoint()
if point.Z != 0 { if point.Z != 0 {
vals = append(vals, resp.ArrayValue([]resp.Value{ vals = append(vals, resp.ArrayValue([]resp.Value{
resp.FloatValue(point.Y), resp.FloatValue(point.Y),
@ -352,13 +367,13 @@ func (sw *scanWriter) writeObject(id string, o geojson.Object, fields []float64,
})) }))
} }
case outputHashes: case outputHashes:
p, err := o.Geohash(int(sw.precision)) p, err := opts.o.Geohash(int(sw.precision))
if err != nil { if err != nil {
p = "" p = ""
} }
vals = append(vals, resp.StringValue(p)) vals = append(vals, resp.StringValue(p))
case outputBounds: case outputBounds:
bbox := o.CalculatedBBox() bbox := opts.o.CalculatedBBox()
vals = append(vals, resp.ArrayValue([]resp.Value{ vals = append(vals, resp.ArrayValue([]resp.Value{
resp.ArrayValue([]resp.Value{ resp.ArrayValue([]resp.Value{
resp.FloatValue(bbox.Min.Y), resp.FloatValue(bbox.Min.Y),
@ -371,7 +386,7 @@ func (sw *scanWriter) writeObject(id string, o geojson.Object, fields []float64,
})) }))
} }
fvs := orderFields(sw.fmap, fields) fvs := orderFields(sw.fmap, opts.fields)
if len(fvs) > 0 { if len(fvs) > 0 {
fvals := make([]resp.Value, 0, len(fvs)*2) fvals := make([]resp.Value, 0, len(fvs)*2)
for i, fv := range fvs { for i, fv := range fvs {
@ -381,6 +396,10 @@ func (sw *scanWriter) writeObject(id string, o geojson.Object, fields []float64,
vals = append(vals, resp.ArrayValue(fvals)) vals = append(vals, resp.ArrayValue(fvals))
} }
if opts.distance > 0 {
vals = append(vals, resp.FloatValue(opts.distance))
}
sw.values = append(sw.values, resp.ArrayValue(vals)) sw.values = append(sw.values, resp.ArrayValue(vals))
} }
} }

View File

@ -291,7 +291,18 @@ func (c *Controller) cmdNearby(msg *server.Message) (res string, err error) {
sw.writeHead() sw.writeHead()
if sw.col != nil { if sw.col != nil {
s.cursor = sw.col.Nearby(s.cursor, s.sparse, s.lat, s.lon, s.meters, minZ, maxZ, func(id string, o geojson.Object, fields []float64) bool { s.cursor = sw.col.Nearby(s.cursor, s.sparse, s.lat, s.lon, s.meters, minZ, maxZ, func(id string, o geojson.Object, fields []float64) bool {
return sw.writeObject(id, o, fields, false) // Calculate distance if we need to
distance := 0.0
if s.distance {
distance = o.CalculatedPoint().DistanceTo(geojson.Position{X: s.lon, Y: s.lat, Z: 0})
}
return sw.writeObject(ScanWriterParams{
id: id,
o: o,
fields: fields,
distance: distance,
})
}) })
} }
sw.writeFoot(s.cursor) sw.writeFoot(s.cursor)
@ -337,13 +348,21 @@ func (c *Controller) cmdWithinOrIntersects(cmd string, msg *server.Message) (res
if cmd == "within" { if cmd == "within" {
s.cursor = sw.col.Within(s.cursor, s.sparse, s.o, s.minLat, s.minLon, s.maxLat, s.maxLon, minZ, maxZ, s.cursor = sw.col.Within(s.cursor, s.sparse, s.o, s.minLat, s.minLon, s.maxLat, s.maxLon, minZ, maxZ,
func(id string, o geojson.Object, fields []float64) bool { func(id string, o geojson.Object, fields []float64) bool {
return sw.writeObject(id, o, fields, false) return sw.writeObject(ScanWriterParams{
id: id,
o: o,
fields: fields,
})
}, },
) )
} else if cmd == "intersects" { } else if cmd == "intersects" {
s.cursor = sw.col.Intersects(s.cursor, s.sparse, s.o, s.minLat, s.minLon, s.maxLat, s.maxLon, minZ, maxZ, s.cursor = sw.col.Intersects(s.cursor, s.sparse, s.o, s.minLat, s.minLon, s.maxLat, s.maxLon, minZ, maxZ,
func(id string, o geojson.Object, fields []float64) bool { func(id string, o geojson.Object, fields []float64) bool {
return sw.writeObject(id, o, fields, false) return sw.writeObject(ScanWriterParams{
id: id,
o: o,
fields: fields,
})
}, },
) )
} }
@ -394,7 +413,11 @@ func (c *Controller) cmdSearch(msg *server.Message) (res string, err error) {
if g.Limits[0] == "" && g.Limits[1] == "" { if g.Limits[0] == "" && g.Limits[1] == "" {
s.cursor = sw.col.SearchValues(s.cursor, s.desc, s.cursor = sw.col.SearchValues(s.cursor, s.desc,
func(id string, o geojson.Object, fields []float64) bool { func(id string, o geojson.Object, fields []float64) bool {
return sw.writeObject(id, o, fields, false) return sw.writeObject(ScanWriterParams{
id: id,
o: o,
fields: fields,
})
}, },
) )
} else { } else {
@ -404,7 +427,11 @@ func (c *Controller) cmdSearch(msg *server.Message) (res string, err error) {
s.cursor = sw.col.SearchValuesRange( s.cursor = sw.col.SearchValuesRange(
s.cursor, g.Limits[0], g.Limits[1], s.desc, s.cursor, g.Limits[0], g.Limits[1], s.desc,
func(id string, o geojson.Object, fields []float64) bool { func(id string, o geojson.Object, fields []float64) bool {
return sw.writeObject(id, o, fields, false) return sw.writeObject(ScanWriterParams{
id: id,
o: o,
fields: fields,
})
}, },
) )
} }

View File

@ -163,6 +163,7 @@ type searchScanBaseTokens struct {
precision uint64 precision uint64
lineout string lineout string
fence bool fence bool
distance bool
detect map[string]bool detect map[string]bool
accept map[string]bool accept map[string]bool
glob string glob string
@ -304,6 +305,14 @@ func parseSearchScanBaseTokens(cmd string, vs []resp.Value) (vsout []resp.Value,
t.accept = nil t.accept = nil
} }
continue continue
} else if (wtok[0] == 'D' || wtok[0] == 'd') && strings.ToLower(wtok) == "distance" {
vs = nvs
if t.distance {
err = errDuplicateArgument(strings.ToUpper(wtok))
return
}
t.distance = true
continue
} else if (wtok[0] == 'D' || wtok[0] == 'd') && strings.ToLower(wtok) == "detect" { } else if (wtok[0] == 'D' || wtok[0] == 'd') && strings.ToLower(wtok) == "detect" {
vs = nvs vs = nvs
if t.detect != nil { if t.detect != nil {