From 3f2977b300ae48d2bff64ff9687958f81d4102f5 Mon Sep 17 00:00:00 2001 From: Josh Baker Date: Mon, 28 Mar 2016 14:16:21 -0700 Subject: [PATCH] resp search --- controller/controller.go | 17 ++- controller/crud.go | 12 +- controller/hooks.go | 10 +- controller/live.go | 5 +- controller/scan.go | 42 ++++--- controller/scanner.go | 237 ++++++++++++++++++++++++++------------- controller/search.go | 97 ++++++++-------- controller/token.go | 47 ++++---- 8 files changed, 289 insertions(+), 178 deletions(-) diff --git a/controller/controller.go b/controller/controller.go index 3a9f80fb..2f29c572 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -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": diff --git a/controller/crud.go b/controller/crud.go index dbe92c49..aabdc7c7 100644 --- a/controller/crud.go +++ b/controller/crud.go @@ -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), + }), })) } } diff --git a/controller/hooks.go b/controller/hooks.go index a32934ad..58e03567 100644 --- a/controller/hooks.go +++ b/controller/hooks.go @@ -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 } diff --git a/controller/live.go b/controller/live.go index 4d564397..111b7196 100644 --- a/controller/live.go +++ b/controller/live.go @@ -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 diff --git a/controller/scan.go b/controller/scan.go index 49b03726..21f9692e 100644 --- a/controller/scan.go +++ b/controller/scan.go @@ -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 } diff --git a/controller/scanner.go b/controller/scanner.go index c199ae87..7bf79b6a 100644 --- a/controller/scanner.go +++ b/controller/scanner.go @@ -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 diff --git a/controller/search.go b/controller/search.go index 75ebf558..abbb70b1 100644 --- a/controller/search.go +++ b/controller/search.go @@ -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 } diff --git a/controller/token.go b/controller/token.go index 6376e59a..80d9cf59 100644 --- a/controller/token.go +++ b/controller/token.go @@ -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 }