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)
// case "server":
// resp, err = c.cmdServer(nline)
// case "scan":
// err = c.cmdScan(nline, w)
// case "nearby":
// err = c.cmdNearby(nline, w)
// case "within":
// err = c.cmdWithin(nline, w)
// case "intersects":
// err = c.cmdIntersects(nline, w)
case "scan":
res, err = c.cmdScan(msg)
case "nearby":
res, err = c.cmdNearby(msg)
case "within":
res, err = c.cmdWithin(msg)
case "intersects":
res, err = c.cmdIntersects(msg)
case "get":
res, err = c.cmdGet(msg)
// case "keys":

View File

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

View File

@ -13,7 +13,9 @@ import (
"time"
"github.com/garyburd/redigo/redis"
"github.com/tidwall/resp"
"github.com/tidwall/tile38/controller/log"
"github.com/tidwall/tile38/controller/server"
)
type EndpointProtocol string
@ -186,7 +188,9 @@ func (c *Controller) cmdSetHook(line string) (err error) {
case "within", "intersects":
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 {
return err
}
@ -202,7 +206,9 @@ func (c *Controller) cmdSetHook(line string) (err error) {
Command: command,
}
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 {
return err
}

View File

@ -11,6 +11,7 @@ import (
"github.com/tidwall/tile38/client"
"github.com/tidwall/tile38/controller/log"
"github.com/tidwall/tile38/controller/server"
)
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.fence = &s
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()
}
// everything below if for live SCAN, NEARBY, WITHIN, INTERSECTS

View File

@ -2,41 +2,50 @@ package controller
import (
"bytes"
"io"
"strings"
"time"
"github.com/tidwall/resp"
"github.com/tidwall/tile38/controller/server"
"github.com/tidwall/tile38/geojson"
)
func cmdScanArgs(line string) (
s liveFenceSwitches, err error,
) {
if line, s.searchScanBaseTokens, err = parseSearchScanBaseTokens("scan", line); err != nil {
func cmdScanArgs(vs []resp.Value) (s liveFenceSwitches, err error) {
if vs, s.searchScanBaseTokens, err = parseSearchScanBaseTokens("scan", vs); err != nil {
return
}
if line != "" {
if len(vs) != 0 {
err = errInvalidNumberOfArguments
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()
vs := msg.Values[1:]
wr := &bytes.Buffer{}
s, err := cmdScanArgs(line)
s, err := cmdScanArgs(vs)
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 {
return err
return "", err
}
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()
if sw.col != nil {
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)
wr.WriteString(`,"elapsed":"` + time.Now().Sub(start).String() + "\"}")
w.Write(wr.Bytes())
return nil
if msg.OutputType == server.JSON {
wr.WriteString(`,"elapsed":"` + time.Now().Sub(start).String() + "\"}")
}
return string(wr.Bytes()), nil
}

View File

@ -6,7 +6,9 @@ import (
"math"
"strconv"
"github.com/tidwall/resp"
"github.com/tidwall/tile38/controller/collection"
"github.com/tidwall/tile38/controller/server"
"github.com/tidwall/tile38/geojson"
)
@ -27,6 +29,7 @@ const (
type scanWriter struct {
wr *bytes.Buffer
msg *server.Message
col *collection.Collection
fmap map[string]int
farr []string
@ -44,10 +47,11 @@ type scanWriter struct {
globEverything bool
globSingle bool
fullFields bool
values []resp.Value
}
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,
) {
@ -63,6 +67,7 @@ func (c *Controller) newScanWriter(
}
sw := &scanWriter{
wr: wr,
msg: msg,
output: output,
wheres: wheres,
precision: precision,
@ -96,29 +101,33 @@ func (sw *scanWriter) hasFieldsOutput() bool {
}
func (sw *scanWriter) writeHead() {
if len(sw.farr) > 0 && sw.hasFieldsOutput() {
sw.wr.WriteString(`,"fields":[`)
for i, field := range sw.farr {
if i > 0 {
sw.wr.WriteByte(',')
switch sw.msg.OutputType {
case server.JSON:
if len(sw.farr) > 0 && sw.hasFieldsOutput() {
sw.wr.WriteString(`,"fields":[`)
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:
sw.wr.WriteString(`,"ids":[`)
case outputObjects:
sw.wr.WriteString(`,"objects":[`)
case outputPoints:
sw.wr.WriteString(`,"points":[`)
case outputBounds:
sw.wr.WriteString(`,"bounds":[`)
case outputHashes:
sw.wr.WriteString(`,"hashes":[`)
case outputCount:
switch sw.output {
case outputIDs:
sw.wr.WriteString(`,"ids":[`)
case outputObjects:
sw.wr.WriteString(`,"objects":[`)
case outputPoints:
sw.wr.WriteString(`,"points":[`)
case outputBounds:
sw.wr.WriteString(`,"bounds":[`)
case outputHashes:
sw.wr.WriteString(`,"hashes":[`)
case outputCount:
}
case server.RESP:
}
}
@ -126,14 +135,28 @@ func (sw *scanWriter) writeFoot(cursor uint64) {
if !sw.hitLimit {
cursor = 0
}
switch sw.output {
default:
sw.wr.WriteByte(']')
case outputCount:
switch sw.msg.OutputType {
case server.JSON:
switch sw.output {
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) {
@ -213,7 +236,6 @@ func (sw *scanWriter) writeObject(id string, o geojson.Object, fields []float64)
}
}
}
nfields, ok := sw.fieldMatch(fields, o)
if !ok {
return true
@ -222,65 +244,124 @@ func (sw *scanWriter) writeObject(id string, o geojson.Object, fields []float64)
if sw.output == outputCount {
return true
}
var wr bytes.Buffer
if sw.once {
wr.WriteByte(',')
} else {
sw.once = true
}
var jsfields string
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 += `,`
switch sw.msg.OutputType {
case server.JSON:
var wr bytes.Buffer
var jsfields string
if sw.once {
wr.WriteByte(',')
} else {
sw.once = true
}
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 {
jsfields = `,"fields":[`
for i, field := range nfields {
if i > 0 {
jsfields += ","
} else if len(sw.farr) > 0 {
jsfields = `,"fields":[`
for i, field := range nfields {
if i > 0 {
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++
if sw.numberItems == sw.limit {
sw.hitLimit = true

View File

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

View File

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