mirror of https://github.com/tidwall/tile38.git
resp search
This commit is contained in:
parent
ba9139be02
commit
3f2977b300
|
@ -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":
|
||||||
|
|
|
@ -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),
|
||||||
|
}),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue