resp search

This commit is contained in:
Josh Baker 2016-03-28 14:16:21 -07:00
parent ba9139be02
commit 3f2977b300
8 changed files with 289 additions and 178 deletions

View File

@ -359,15 +359,14 @@ func (c *Controller) command(msg *server.Message, w io.Writer) (res string, d co
// resp, err = c.cmdStats(nline) // resp, err = c.cmdStats(nline)
// case "server": // case "server":
// resp, err = c.cmdServer(nline) // resp, err = c.cmdServer(nline)
// case "scan": case "scan":
// err = c.cmdScan(nline, w) res, err = c.cmdScan(msg)
// case "nearby": case "nearby":
// err = c.cmdNearby(nline, w) res, err = c.cmdNearby(msg)
// case "within": case "within":
// err = c.cmdWithin(nline, w) res, err = c.cmdWithin(msg)
// case "intersects": case "intersects":
// err = c.cmdIntersects(nline, w) res, err = c.cmdIntersects(msg)
case "get": case "get":
res, err = c.cmdGet(msg) res, err = c.cmdGet(msg)
// case "keys": // case "keys":

View File

@ -138,10 +138,14 @@ func (c *Controller) cmdGet(msg *server.Message) (string, error) {
buf.WriteString(bbox.ExternalJSON()) buf.WriteString(bbox.ExternalJSON())
} else { } else {
vals = append(vals, resp.ArrayValue([]resp.Value{ vals = append(vals, resp.ArrayValue([]resp.Value{
resp.StringValue(strconv.FormatFloat(bbox.Min.Y, 'f', -1, 64)), resp.ArrayValue([]resp.Value{
resp.StringValue(strconv.FormatFloat(bbox.Min.X, 'f', -1, 64)), resp.FloatValue(bbox.Min.Y),
resp.StringValue(strconv.FormatFloat(bbox.Max.Y, 'f', -1, 64)), resp.FloatValue(bbox.Min.X),
resp.StringValue(strconv.FormatFloat(bbox.Max.X, 'f', -1, 64)), }),
resp.ArrayValue([]resp.Value{
resp.FloatValue(bbox.Max.Y),
resp.FloatValue(bbox.Max.X),
}),
})) }))
} }
} }

View File

@ -13,7 +13,9 @@ import (
"time" "time"
"github.com/garyburd/redigo/redis" "github.com/garyburd/redigo/redis"
"github.com/tidwall/resp"
"github.com/tidwall/tile38/controller/log" "github.com/tidwall/tile38/controller/log"
"github.com/tidwall/tile38/controller/server"
) )
type EndpointProtocol string type EndpointProtocol string
@ -186,7 +188,9 @@ func (c *Controller) cmdSetHook(line string) (err error) {
case "within", "intersects": case "within", "intersects":
types = withinOrIntersectsTypes types = withinOrIntersectsTypes
} }
s, err := c.cmdSearchArgs(cmdlc, line, types) var vs []resp.Value
panic("todo: assign vs correctly")
s, err := c.cmdSearchArgs(cmdlc, vs, types)
if err != nil { if err != nil {
return err return err
} }
@ -202,7 +206,9 @@ func (c *Controller) cmdSetHook(line string) (err error) {
Command: command, Command: command,
} }
var wr bytes.Buffer var wr bytes.Buffer
hook.ScanWriter, err = c.newScanWriter(&wr, s.key, s.output, s.precision, s.glob, s.limit, s.wheres, s.nofields) var msg *server.Message
panic("todo: cmdSetHook message must be defined")
hook.ScanWriter, err = c.newScanWriter(&wr, msg, s.key, s.output, s.precision, s.glob, s.limit, s.wheres, s.nofields)
if err != nil { if err != nil {
return err return err
} }

View File

@ -11,6 +11,7 @@ import (
"github.com/tidwall/tile38/client" "github.com/tidwall/tile38/client"
"github.com/tidwall/tile38/controller/log" "github.com/tidwall/tile38/controller/log"
"github.com/tidwall/tile38/controller/server"
) )
type liveBuffer struct { type liveBuffer struct {
@ -74,7 +75,9 @@ func (c *Controller) goLive(inerr error, conn net.Conn, rd *bufio.Reader, websoc
lb.key = s.key lb.key = s.key
lb.fence = &s lb.fence = &s
c.mu.RLock() c.mu.RLock()
sw, err = c.newScanWriter(&wr, s.key, s.output, s.precision, s.glob, s.limit, s.wheres, s.nofields) var msg *server.Message
panic("todo: goLive message must be defined")
sw, err = c.newScanWriter(&wr, msg, s.key, s.output, s.precision, s.glob, s.limit, s.wheres, s.nofields)
c.mu.RUnlock() c.mu.RUnlock()
} }
// everything below if for live SCAN, NEARBY, WITHIN, INTERSECTS // everything below if for live SCAN, NEARBY, WITHIN, INTERSECTS

View File

@ -2,41 +2,50 @@ package controller
import ( import (
"bytes" "bytes"
"io"
"strings" "strings"
"time" "time"
"github.com/tidwall/resp"
"github.com/tidwall/tile38/controller/server"
"github.com/tidwall/tile38/geojson" "github.com/tidwall/tile38/geojson"
) )
func cmdScanArgs(line string) ( func cmdScanArgs(vs []resp.Value) (s liveFenceSwitches, err error) {
s liveFenceSwitches, err error, if vs, s.searchScanBaseTokens, err = parseSearchScanBaseTokens("scan", vs); err != nil {
) {
if line, s.searchScanBaseTokens, err = parseSearchScanBaseTokens("scan", line); err != nil {
return return
} }
if line != "" { if len(vs) != 0 {
err = errInvalidNumberOfArguments err = errInvalidNumberOfArguments
return return
} }
return return
} }
func (c *Controller) cmdScan(line string, w io.Writer) error { func (c *Controller) cmdScan(msg *server.Message) (res string, err error) {
start := time.Now() start := time.Now()
vs := msg.Values[1:]
wr := &bytes.Buffer{} wr := &bytes.Buffer{}
s, err := cmdScanArgs(line) s, err := cmdScanArgs(vs)
if err != nil { if err != nil {
return err return "", err
} }
sw, err := c.newScanWriter(wr, s.key, s.output, s.precision, s.glob, s.limit, s.wheres, s.nofields) sw, err := c.newScanWriter(wr, msg, s.key, s.output, s.precision, s.glob, s.limit, s.wheres, s.nofields)
if err != nil { if err != nil {
return err return "", err
} }
if s.sparse > 0 && sw.col != nil { if s.sparse > 0 && sw.col != nil {
return c.cmdWithinOrIntersects("within", line+" BOUNDS -90 -180 90 180", w) msg.Values = append(msg.Values,
resp.StringValue("BOUNDS"),
resp.StringValue("-90"),
resp.StringValue("-180"),
resp.StringValue("180"),
)
return c.cmdWithinOrIntersects("within", msg)
}
if msg.OutputType == server.JSON {
wr.WriteString(`{"ok":true`)
} }
wr.WriteString(`{"ok":true`)
sw.writeHead() sw.writeHead()
if sw.col != nil { if sw.col != nil {
if sw.output == outputCount && len(sw.wheres) == 0 && sw.globEverything == true { if sw.output == outputCount && len(sw.wheres) == 0 && sw.globEverything == true {
@ -65,7 +74,8 @@ func (c *Controller) cmdScan(line string, w io.Writer) error {
} }
} }
sw.writeFoot(s.cursor) sw.writeFoot(s.cursor)
wr.WriteString(`,"elapsed":"` + time.Now().Sub(start).String() + "\"}") if msg.OutputType == server.JSON {
w.Write(wr.Bytes()) wr.WriteString(`,"elapsed":"` + time.Now().Sub(start).String() + "\"}")
return nil }
return string(wr.Bytes()), nil
} }

View File

@ -6,7 +6,9 @@ import (
"math" "math"
"strconv" "strconv"
"github.com/tidwall/resp"
"github.com/tidwall/tile38/controller/collection" "github.com/tidwall/tile38/controller/collection"
"github.com/tidwall/tile38/controller/server"
"github.com/tidwall/tile38/geojson" "github.com/tidwall/tile38/geojson"
) )
@ -27,6 +29,7 @@ const (
type scanWriter struct { type scanWriter struct {
wr *bytes.Buffer wr *bytes.Buffer
msg *server.Message
col *collection.Collection col *collection.Collection
fmap map[string]int fmap map[string]int
farr []string farr []string
@ -44,10 +47,11 @@ type scanWriter struct {
globEverything bool globEverything bool
globSingle bool globSingle bool
fullFields bool fullFields bool
values []resp.Value
} }
func (c *Controller) newScanWriter( func (c *Controller) newScanWriter(
wr *bytes.Buffer, key string, output outputT, precision uint64, glob string, limit uint64, wheres []whereT, nofields bool, wr *bytes.Buffer, msg *server.Message, key string, output outputT, precision uint64, glob string, limit uint64, wheres []whereT, nofields bool,
) ( ) (
*scanWriter, error, *scanWriter, error,
) { ) {
@ -63,6 +67,7 @@ func (c *Controller) newScanWriter(
} }
sw := &scanWriter{ sw := &scanWriter{
wr: wr, wr: wr,
msg: msg,
output: output, output: output,
wheres: wheres, wheres: wheres,
precision: precision, precision: precision,
@ -96,29 +101,33 @@ func (sw *scanWriter) hasFieldsOutput() bool {
} }
func (sw *scanWriter) writeHead() { func (sw *scanWriter) writeHead() {
if len(sw.farr) > 0 && sw.hasFieldsOutput() { switch sw.msg.OutputType {
sw.wr.WriteString(`,"fields":[`) case server.JSON:
for i, field := range sw.farr { if len(sw.farr) > 0 && sw.hasFieldsOutput() {
if i > 0 { sw.wr.WriteString(`,"fields":[`)
sw.wr.WriteByte(',') for i, field := range sw.farr {
if i > 0 {
sw.wr.WriteByte(',')
}
sw.wr.WriteString(jsonString(field))
} }
sw.wr.WriteString(jsonString(field)) sw.wr.WriteByte(']')
} }
sw.wr.WriteByte(']') switch sw.output {
} case outputIDs:
switch sw.output { sw.wr.WriteString(`,"ids":[`)
case outputIDs: case outputObjects:
sw.wr.WriteString(`,"ids":[`) sw.wr.WriteString(`,"objects":[`)
case outputObjects: case outputPoints:
sw.wr.WriteString(`,"objects":[`) sw.wr.WriteString(`,"points":[`)
case outputPoints: case outputBounds:
sw.wr.WriteString(`,"points":[`) sw.wr.WriteString(`,"bounds":[`)
case outputBounds: case outputHashes:
sw.wr.WriteString(`,"bounds":[`) sw.wr.WriteString(`,"hashes":[`)
case outputHashes: case outputCount:
sw.wr.WriteString(`,"hashes":[`)
case outputCount:
}
case server.RESP:
} }
} }
@ -126,14 +135,28 @@ func (sw *scanWriter) writeFoot(cursor uint64) {
if !sw.hitLimit { if !sw.hitLimit {
cursor = 0 cursor = 0
} }
switch sw.output { switch sw.msg.OutputType {
default: case server.JSON:
sw.wr.WriteByte(']') switch sw.output {
case outputCount: default:
sw.wr.WriteByte(']')
case outputCount:
}
sw.wr.WriteString(`,"count":` + strconv.FormatUint(sw.count, 10))
sw.wr.WriteString(`,"cursor":` + strconv.FormatUint(cursor, 10))
case server.RESP:
sw.wr.Reset()
values := []resp.Value{
resp.IntegerValue(int(cursor)),
resp.ArrayValue(sw.values),
}
data, err := resp.ArrayValue(values).MarshalRESP()
if err != nil {
panic("Eek this is bad. Marshal resp should not fail.")
}
sw.wr.Write(data)
} }
sw.wr.WriteString(`,"count":` + strconv.FormatUint(sw.count, 10))
sw.wr.WriteString(`,"cursor":` + strconv.FormatUint(cursor, 10))
} }
func (sw *scanWriter) fieldMatch(fields []float64, o geojson.Object) ([]float64, bool) { func (sw *scanWriter) fieldMatch(fields []float64, o geojson.Object) ([]float64, bool) {
@ -213,7 +236,6 @@ func (sw *scanWriter) writeObject(id string, o geojson.Object, fields []float64)
} }
} }
} }
nfields, ok := sw.fieldMatch(fields, o) nfields, ok := sw.fieldMatch(fields, o)
if !ok { if !ok {
return true return true
@ -222,65 +244,124 @@ func (sw *scanWriter) writeObject(id string, o geojson.Object, fields []float64)
if sw.output == outputCount { if sw.output == outputCount {
return true return true
} }
var wr bytes.Buffer
if sw.once {
wr.WriteByte(',')
} else {
sw.once = true
}
var jsfields string
if sw.hasFieldsOutput() { switch sw.msg.OutputType {
if sw.fullFields { case server.JSON:
if len(sw.fmap) > 0 { var wr bytes.Buffer
jsfields = `,"fields":{` var jsfields string
var i int if sw.once {
for field, idx := range sw.fmap { wr.WriteByte(',')
if len(fields) > idx { } else {
if !math.IsNaN(fields[idx]) { sw.once = true
if i > 0 { }
jsfields += `,` if sw.hasFieldsOutput() {
if sw.fullFields {
if len(sw.fmap) > 0 {
jsfields = `,"fields":{`
var i int
for field, idx := range sw.fmap {
if len(fields) > idx {
if !math.IsNaN(fields[idx]) {
if i > 0 {
jsfields += `,`
}
jsfields += jsonString(field) + ":" + strconv.FormatFloat(fields[idx], 'f', -1, 64)
i++
} }
jsfields += jsonString(field) + ":" + strconv.FormatFloat(fields[idx], 'f', -1, 64)
i++
} }
} }
jsfields += `}`
} }
jsfields += `}`
} } else if len(sw.farr) > 0 {
} else if len(sw.farr) > 0 { jsfields = `,"fields":[`
jsfields = `,"fields":[` for i, field := range nfields {
for i, field := range nfields { if i > 0 {
if i > 0 { jsfields += ","
jsfields += "," }
jsfields += strconv.FormatFloat(field, 'f', -1, 64)
} }
jsfields += strconv.FormatFloat(field, 'f', -1, 64) jsfields += `]`
} }
jsfields += `]` }
if sw.output == outputIDs {
wr.WriteString(jsonString(id))
} else {
wr.WriteString(`{"id":` + jsonString(id))
switch sw.output {
case outputObjects:
wr.WriteString(`,"object":` + o.JSON())
case outputPoints:
wr.WriteString(`,"point":` + o.CalculatedPoint().ExternalJSON())
case outputHashes:
p, err := o.Geohash(int(sw.precision))
if err != nil {
p = ""
}
wr.WriteString(`,"hash":"` + p + `"`)
case outputBounds:
wr.WriteString(`,"bounds":` + o.CalculatedBBox().ExternalJSON())
}
wr.WriteString(jsfields)
wr.WriteString(`}`)
}
sw.wr.Write(wr.Bytes())
case server.RESP:
vals := make([]resp.Value, 1, 3)
vals[0] = resp.StringValue(id)
if sw.output == outputIDs {
sw.values = append(sw.values, vals[0])
} else {
switch sw.output {
case outputObjects:
vals = append(vals, resp.StringValue(o.JSON()))
case outputPoints:
point := o.CalculatedPoint()
if point.Z != 0 {
vals = append(vals, resp.ArrayValue([]resp.Value{
resp.FloatValue(point.Y),
resp.FloatValue(point.X),
resp.FloatValue(point.Z),
}))
} else {
vals = append(vals, resp.ArrayValue([]resp.Value{
resp.FloatValue(point.Y),
resp.FloatValue(point.X),
}))
}
case outputHashes:
p, err := o.Geohash(int(sw.precision))
if err != nil {
p = ""
}
vals = append(vals, resp.StringValue(p))
case outputBounds:
bbox := o.CalculatedBBox()
vals = append(vals, resp.ArrayValue([]resp.Value{
resp.ArrayValue([]resp.Value{
resp.FloatValue(bbox.Min.Y),
resp.FloatValue(bbox.Min.X),
}),
resp.ArrayValue([]resp.Value{
resp.FloatValue(bbox.Max.Y),
resp.FloatValue(bbox.Max.X),
}),
}))
}
fvs := orderFields(sw.fmap, fields)
if len(fvs) > 0 {
fvals := make([]resp.Value, 0, len(fvs)*2)
for i, fv := range fvs {
fvals = append(fvals, resp.StringValue(fv.field), resp.StringValue(strconv.FormatFloat(fv.value, 'f', -1, 64)))
i++
}
vals = append(vals, resp.ArrayValue(fvals))
}
sw.values = append(sw.values, resp.ArrayValue(vals))
} }
} }
if sw.output == outputIDs {
wr.WriteString(jsonString(id))
} else {
wr.WriteString(`{"id":` + jsonString(id))
switch sw.output {
case outputObjects:
wr.WriteString(`,"object":` + o.JSON())
case outputPoints:
wr.WriteString(`,"point":` + o.CalculatedPoint().ExternalJSON())
case outputHashes:
p, err := o.Geohash(int(sw.precision))
if err != nil {
p = ""
}
wr.WriteString(`,"hash":"` + p + `"`)
case outputBounds:
wr.WriteString(`,"bounds":` + o.CalculatedBBox().ExternalJSON())
}
wr.WriteString(jsfields)
wr.WriteString(`}`)
}
sw.wr.Write(wr.Bytes())
sw.numberItems++ sw.numberItems++
if sw.numberItems == sw.limit { if sw.numberItems == sw.limit {
sw.hitLimit = true sw.hitLimit = true

View File

@ -2,12 +2,13 @@ package controller
import ( import (
"bytes" "bytes"
"io"
"strconv" "strconv"
"strings" "strings"
"time" "time"
"github.com/tidwall/resp"
"github.com/tidwall/tile38/controller/bing" "github.com/tidwall/tile38/controller/bing"
"github.com/tidwall/tile38/controller/server"
"github.com/tidwall/tile38/geojson" "github.com/tidwall/tile38/geojson"
"github.com/tidwall/tile38/geojson/geohash" "github.com/tidwall/tile38/geojson/geohash"
) )
@ -25,12 +26,13 @@ func (s liveFenceSwitches) Error() string {
return "going live" return "going live"
} }
func (c *Controller) cmdSearchArgs(cmd, line string, types []string) (s liveFenceSwitches, err error) { func (c *Controller) cmdSearchArgs(cmd string, vs []resp.Value, types []string) (s liveFenceSwitches, err error) {
if line, s.searchScanBaseTokens, err = parseSearchScanBaseTokens(cmd, line); err != nil { if vs, s.searchScanBaseTokens, err = parseSearchScanBaseTokens(cmd, vs); err != nil {
return return
} }
var typ string var typ string
if line, typ = token(line); typ == "" { var ok bool
if vs, typ, ok = tokenval(vs); !ok || typ == "" {
err = errInvalidNumberOfArguments err = errInvalidNumberOfArguments
return return
} }
@ -39,7 +41,7 @@ func (c *Controller) cmdSearchArgs(cmd, line string, types []string) (s liveFenc
if _, err := strconv.ParseFloat(typ, 64); err == nil { if _, err := strconv.ParseFloat(typ, 64); err == nil {
// It's likely that the output was not specified, but rather the search bounds. // It's likely that the output was not specified, but rather the search bounds.
s.searchScanBaseTokens.output = defaultSearchOutput s.searchScanBaseTokens.output = defaultSearchOutput
line = typ + " " + line vs = append([]resp.Value{resp.StringValue(typ)}, vs...)
typ = "BOUNDS" typ = "BOUNDS"
} }
} }
@ -58,15 +60,15 @@ func (c *Controller) cmdSearchArgs(cmd, line string, types []string) (s liveFenc
switch strings.ToLower(typ) { switch strings.ToLower(typ) {
case "point": case "point":
var slat, slon, smeters string var slat, slon, smeters string
if line, slat = token(line); slat == "" { if vs, slat, ok = tokenval(vs); !ok || slat == "" {
err = errInvalidNumberOfArguments err = errInvalidNumberOfArguments
return return
} }
if line, slon = token(line); slon == "" { if vs, slon, ok = tokenval(vs); !ok || slon == "" {
err = errInvalidNumberOfArguments err = errInvalidNumberOfArguments
return return
} }
if line, smeters = token(line); smeters == "" { if vs, smeters, ok = tokenval(vs); !ok || smeters == "" {
err = errInvalidNumberOfArguments err = errInvalidNumberOfArguments
return return
} }
@ -83,30 +85,30 @@ func (c *Controller) cmdSearchArgs(cmd, line string, types []string) (s liveFenc
return return
} }
case "object": case "object":
if line == "" { var obj string
if vs, obj, ok = tokenval(vs); !ok || obj == "" {
err = errInvalidNumberOfArguments err = errInvalidNumberOfArguments
return return
} }
s.o, err = geojson.ObjectJSON(line) s.o, err = geojson.ObjectJSON(obj)
if err != nil { if err != nil {
return return
} }
line = "" // since we read the remaining bytes
case "bounds": case "bounds":
var sminLat, sminLon, smaxlat, smaxlon string var sminLat, sminLon, smaxlat, smaxlon string
if line, sminLat = token(line); sminLat == "" { if vs, sminLat, ok = tokenval(vs); !ok || sminLat == "" {
err = errInvalidNumberOfArguments err = errInvalidNumberOfArguments
return return
} }
if line, sminLon = token(line); sminLon == "" { if vs, sminLon, ok = tokenval(vs); !ok || sminLon == "" {
err = errInvalidNumberOfArguments err = errInvalidNumberOfArguments
return return
} }
if line, smaxlat = token(line); smaxlat == "" { if vs, smaxlat, ok = tokenval(vs); !ok || smaxlat == "" {
err = errInvalidNumberOfArguments err = errInvalidNumberOfArguments
return return
} }
if line, smaxlon = token(line); smaxlon == "" { if vs, smaxlon, ok = tokenval(vs); !ok || smaxlon == "" {
err = errInvalidNumberOfArguments err = errInvalidNumberOfArguments
return return
} }
@ -128,7 +130,7 @@ func (c *Controller) cmdSearchArgs(cmd, line string, types []string) (s liveFenc
} }
case "hash": case "hash":
var hash string var hash string
if line, hash = token(line); hash == "" { if vs, hash, ok = tokenval(vs); !ok || hash == "" {
err = errInvalidNumberOfArguments err = errInvalidNumberOfArguments
return return
} }
@ -138,7 +140,7 @@ func (c *Controller) cmdSearchArgs(cmd, line string, types []string) (s liveFenc
} }
case "quadkey": case "quadkey":
var key string var key string
if line, key = token(line); key == "" { if vs, key, ok = tokenval(vs); !ok || key == "" {
err = errInvalidNumberOfArguments err = errInvalidNumberOfArguments
return return
} }
@ -148,15 +150,15 @@ func (c *Controller) cmdSearchArgs(cmd, line string, types []string) (s liveFenc
} }
case "tile": case "tile":
var sx, sy, sz string var sx, sy, sz string
if line, sx = token(line); sx == "" { if vs, sx, ok = tokenval(vs); !ok || sx == "" {
err = errInvalidNumberOfArguments err = errInvalidNumberOfArguments
return return
} }
if line, sy = token(line); sy == "" { if vs, sy, ok = tokenval(vs); !ok || sy == "" {
err = errInvalidNumberOfArguments err = errInvalidNumberOfArguments
return return
} }
if line, sz = token(line); sz == "" { if vs, sz, ok = tokenval(vs); !ok || sz == "" {
err = errInvalidNumberOfArguments err = errInvalidNumberOfArguments
return return
} }
@ -177,11 +179,11 @@ func (c *Controller) cmdSearchArgs(cmd, line string, types []string) (s liveFenc
s.minLat, s.minLon, s.maxLat, s.maxLon = bing.TileXYToBounds(x, y, z) s.minLat, s.minLon, s.maxLat, s.maxLon = bing.TileXYToBounds(x, y, z)
case "get": case "get":
var key, id string var key, id string
if line, key = token(line); key == "" { if vs, key, ok = tokenval(vs); !ok || key == "" {
err = errInvalidNumberOfArguments err = errInvalidNumberOfArguments
return return
} }
if line, id = token(line); id == "" { if vs, id, ok = tokenval(vs); !ok || id == "" {
err = errInvalidNumberOfArguments err = errInvalidNumberOfArguments
return return
} }
@ -205,7 +207,7 @@ func (c *Controller) cmdSearchArgs(cmd, line string, types []string) (s liveFenc
s.o = o s.o = o
} }
} }
if line != "" { if len(vs) != 0 {
err = errInvalidNumberOfArguments err = errInvalidNumberOfArguments
return return
} }
@ -215,22 +217,25 @@ func (c *Controller) cmdSearchArgs(cmd, line string, types []string) (s liveFenc
var nearbyTypes = []string{"point"} var nearbyTypes = []string{"point"}
var withinOrIntersectsTypes = []string{"geo", "bounds", "hash", "tile", "quadkey", "get"} var withinOrIntersectsTypes = []string{"geo", "bounds", "hash", "tile", "quadkey", "get"}
func (c *Controller) cmdNearby(line string, w io.Writer) error { func (c *Controller) cmdNearby(msg *server.Message) (res string, err error) {
start := time.Now() start := time.Now()
vs := msg.Values[1:]
wr := &bytes.Buffer{} wr := &bytes.Buffer{}
s, err := c.cmdSearchArgs("nearby", line, nearbyTypes) s, err := c.cmdSearchArgs("nearby", vs, nearbyTypes)
if err != nil { if err != nil {
return err return "", err
} }
s.cmd = "nearby" s.cmd = "nearby"
if s.fence { if s.fence {
return s return "", s
} }
sw, err := c.newScanWriter(wr, s.key, s.output, s.precision, s.glob, s.limit, s.wheres, s.nofields) sw, err := c.newScanWriter(wr, msg, s.key, s.output, s.precision, s.glob, s.limit, s.wheres, s.nofields)
if err != nil { if err != nil {
return err return "", err
}
if msg.OutputType == server.JSON {
wr.WriteString(`{"ok":true`)
} }
wr.WriteString(`{"ok":true`)
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, func(id string, o geojson.Object, fields []float64) bool { s.cursor = sw.col.Nearby(s.cursor, s.sparse, s.lat, s.lon, s.meters, func(id string, o geojson.Object, fields []float64) bool {
@ -238,33 +243,36 @@ func (c *Controller) cmdNearby(line string, w io.Writer) error {
}) })
} }
sw.writeFoot(s.cursor) sw.writeFoot(s.cursor)
wr.WriteString(`,"elapsed":"` + time.Now().Sub(start).String() + "\"}") if msg.OutputType == server.JSON {
w.Write(wr.Bytes()) wr.WriteString(`,"elapsed":"` + time.Now().Sub(start).String() + "\"}")
return nil }
return string(wr.Bytes()), nil
} }
func (c *Controller) cmdWithin(line string, w io.Writer) error { func (c *Controller) cmdWithin(msg *server.Message) (res string, err error) {
return c.cmdWithinOrIntersects("within", line, w) return c.cmdWithinOrIntersects("within", msg)
} }
func (c *Controller) cmdIntersects(line string, w io.Writer) error { func (c *Controller) cmdIntersects(msg *server.Message) (res string, err error) {
return c.cmdWithinOrIntersects("intersects", line, w) return c.cmdWithinOrIntersects("intersects", msg)
} }
func (c *Controller) cmdWithinOrIntersects(cmd string, line string, w io.Writer) error { func (c *Controller) cmdWithinOrIntersects(cmd string, msg *server.Message) (res string, err error) {
start := time.Now() start := time.Now()
vs := msg.Values[1:]
wr := &bytes.Buffer{} wr := &bytes.Buffer{}
s, err := c.cmdSearchArgs(cmd, line, withinOrIntersectsTypes) s, err := c.cmdSearchArgs(cmd, vs, withinOrIntersectsTypes)
if err != nil { if err != nil {
return err return "", err
} }
s.cmd = cmd s.cmd = cmd
if s.fence { if s.fence {
return s return "", s
} }
sw, err := c.newScanWriter(wr, s.key, s.output, s.precision, s.glob, s.limit, s.wheres, s.nofields) sw, err := c.newScanWriter(wr, msg, s.key, s.output, s.precision, s.glob, s.limit, s.wheres, s.nofields)
if err != nil { if err != nil {
return err return "", err
} }
wr.WriteString(`{"ok":true`) wr.WriteString(`{"ok":true`)
sw.writeHead() sw.writeHead()
@ -283,6 +291,5 @@ func (c *Controller) cmdWithinOrIntersects(cmd string, line string, w io.Writer)
} }
sw.writeFoot(s.cursor) sw.writeFoot(s.cursor)
wr.WriteString(`,"elapsed":"` + time.Now().Sub(start).String() + "\"}") wr.WriteString(`,"elapsed":"` + time.Now().Sub(start).String() + "\"}")
w.Write(wr.Bytes()) return string(wr.Bytes()), nil
return nil
} }

View File

@ -131,8 +131,9 @@ type searchScanBaseTokens struct {
sparse uint8 sparse uint8
} }
func parseSearchScanBaseTokens(cmd, line string) (lineout string, t searchScanBaseTokens, err error) { func parseSearchScanBaseTokens(cmd string, vs []resp.Value) (vsout []resp.Value, t searchScanBaseTokens, err error) {
if line, t.key = token(line); t.key == "" { var ok bool
if vs, t.key, ok = tokenval(vs); !ok || t.key == "" {
err = errInvalidNumberOfArguments err = errInvalidNumberOfArguments
return return
} }
@ -140,31 +141,31 @@ func parseSearchScanBaseTokens(cmd, line string) (lineout string, t searchScanBa
var ssparse string var ssparse string
var scursor string var scursor string
for { for {
nline, wtok := token(line) nvs, wtok, ok := tokenval(vs)
if len(wtok) > 0 { if ok && len(wtok) > 0 {
if (wtok[0] == 'C' || wtok[0] == 'c') && strings.ToLower(wtok) == "cursor" { if (wtok[0] == 'C' || wtok[0] == 'c') && strings.ToLower(wtok) == "cursor" {
line = nline vs = nvs
if scursor != "" { if scursor != "" {
err = errDuplicateArgument(strings.ToUpper(wtok)) err = errDuplicateArgument(strings.ToUpper(wtok))
return return
} }
if line, scursor = token(line); scursor == "" { if vs, scursor, ok = tokenval(vs); !ok || scursor == "" {
err = errInvalidNumberOfArguments err = errInvalidNumberOfArguments
return return
} }
continue continue
} else if (wtok[0] == 'W' || wtok[0] == 'w') && strings.ToLower(wtok) == "where" { } else if (wtok[0] == 'W' || wtok[0] == 'w') && strings.ToLower(wtok) == "where" {
line = nline vs = nvs
var field, smin, smax string var field, smin, smax string
if line, field = token(line); field == "" { if vs, field, ok = tokenval(vs); !ok || field == "" {
err = errInvalidNumberOfArguments err = errInvalidNumberOfArguments
return return
} }
if line, smin = token(line); smin == "" { if vs, smin, ok = tokenval(vs); !ok || smin == "" {
err = errInvalidNumberOfArguments err = errInvalidNumberOfArguments
return return
} }
if line, smax = token(line); smax == "" { if vs, smax, ok = tokenval(vs); !ok || smax == "" {
err = errInvalidNumberOfArguments err = errInvalidNumberOfArguments
return return
} }
@ -199,7 +200,7 @@ func parseSearchScanBaseTokens(cmd, line string) (lineout string, t searchScanBa
t.wheres = append(t.wheres, whereT{field, minx, min, maxx, max}) t.wheres = append(t.wheres, whereT{field, minx, min, maxx, max})
continue continue
} else if (wtok[0] == 'N' || wtok[0] == 'n') && strings.ToLower(wtok) == "nofields" { } else if (wtok[0] == 'N' || wtok[0] == 'n') && strings.ToLower(wtok) == "nofields" {
line = nline vs = nvs
if t.nofields { if t.nofields {
err = errDuplicateArgument(strings.ToUpper(wtok)) err = errDuplicateArgument(strings.ToUpper(wtok))
return return
@ -207,29 +208,29 @@ func parseSearchScanBaseTokens(cmd, line string) (lineout string, t searchScanBa
t.nofields = true t.nofields = true
continue continue
} else if (wtok[0] == 'L' || wtok[0] == 'l') && strings.ToLower(wtok) == "limit" { } else if (wtok[0] == 'L' || wtok[0] == 'l') && strings.ToLower(wtok) == "limit" {
line = nline vs = nvs
if slimit != "" { if slimit != "" {
err = errDuplicateArgument(strings.ToUpper(wtok)) err = errDuplicateArgument(strings.ToUpper(wtok))
return return
} }
if line, slimit = token(line); slimit == "" { if vs, slimit, ok = tokenval(vs); !ok || slimit == "" {
err = errInvalidNumberOfArguments err = errInvalidNumberOfArguments
return return
} }
continue continue
} else if (wtok[0] == 'S' || wtok[0] == 's') && strings.ToLower(wtok) == "sparse" { } else if (wtok[0] == 'S' || wtok[0] == 's') && strings.ToLower(wtok) == "sparse" {
line = nline vs = nvs
if ssparse != "" { if ssparse != "" {
err = errDuplicateArgument(strings.ToUpper(wtok)) err = errDuplicateArgument(strings.ToUpper(wtok))
return return
} }
if line, ssparse = token(line); ssparse == "" { if vs, ssparse, ok = tokenval(vs); !ok || ssparse == "" {
err = errInvalidNumberOfArguments err = errInvalidNumberOfArguments
return return
} }
continue continue
} else if (wtok[0] == 'F' || wtok[0] == 'f') && strings.ToLower(wtok) == "fence" { } else if (wtok[0] == 'F' || wtok[0] == 'f') && strings.ToLower(wtok) == "fence" {
line = nline vs = nvs
if t.fence { if t.fence {
err = errDuplicateArgument(strings.ToUpper(wtok)) err = errDuplicateArgument(strings.ToUpper(wtok))
return return
@ -237,12 +238,12 @@ func parseSearchScanBaseTokens(cmd, line string) (lineout string, t searchScanBa
t.fence = true t.fence = true
continue continue
} else if (wtok[0] == 'M' || wtok[0] == 'm') && strings.ToLower(wtok) == "match" { } else if (wtok[0] == 'M' || wtok[0] == 'm') && strings.ToLower(wtok) == "match" {
line = nline vs = nvs
if t.glob != "" { if t.glob != "" {
err = errDuplicateArgument(strings.ToUpper(wtok)) err = errDuplicateArgument(strings.ToUpper(wtok))
return return
} }
if line, t.glob = token(line); t.glob == "" { if vs, t.glob, ok = tokenval(vs); !ok || t.glob == "" {
err = errInvalidNumberOfArguments err = errInvalidNumberOfArguments
return return
} }
@ -277,10 +278,10 @@ func parseSearchScanBaseTokens(cmd, line string) (lineout string, t searchScanBa
} }
t.output = defaultSearchOutput t.output = defaultSearchOutput
var nline string var nvs []resp.Value
var sprecision string var sprecision string
var which string var which string
if nline, which = token(line); which != "" { if nvs, which, ok = tokenval(vs); ok && which != "" {
updline := true updline := true
switch strings.ToLower(which) { switch strings.ToLower(which) {
default: default:
@ -297,7 +298,7 @@ func parseSearchScanBaseTokens(cmd, line string) (lineout string, t searchScanBa
t.output = outputPoints t.output = outputPoints
case "hashes": case "hashes":
t.output = outputHashes t.output = outputHashes
if nline, sprecision = token(nline); sprecision == "" { if nvs, sprecision, ok = tokenval(nvs); !ok || sprecision == "" {
err = errInvalidNumberOfArguments err = errInvalidNumberOfArguments
return return
} }
@ -307,7 +308,7 @@ func parseSearchScanBaseTokens(cmd, line string) (lineout string, t searchScanBa
t.output = outputIDs t.output = outputIDs
} }
if updline { if updline {
line = nline vs = nvs
} }
} }
@ -338,6 +339,6 @@ func parseSearchScanBaseTokens(cmd, line string) (lineout string, t searchScanBa
t.sparse = uint8(sparse) t.sparse = uint8(sparse)
t.limit = math.MaxUint64 t.limit = math.MaxUint64
} }
lineout = line vsout = vs
return return
} }