SEARCH command
This commit is contained in:
parent
7bbe7adbd5
commit
b08c686c64
@ -235,7 +235,7 @@ func (c *Collection) FieldArr() []string {
|
|||||||
return arr
|
return arr
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scan iterates though the collection. A cursor can be used for paging.
|
// Scan iterates though the collection ids. A cursor can be used for paging.
|
||||||
func (c *Collection) Scan(cursor uint64, stype ScanType, desc bool,
|
func (c *Collection) Scan(cursor uint64, stype ScanType, desc bool,
|
||||||
iterator func(id string, obj geojson.Object, fields []float64) bool,
|
iterator func(id string, obj geojson.Object, fields []float64) bool,
|
||||||
) (ncursor uint64) {
|
) (ncursor uint64) {
|
||||||
@ -280,6 +280,50 @@ func (c *Collection) ScanRange(cursor uint64, stype ScanType, start, end string,
|
|||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SearchValues iterates though the collection values. A cursor can be used for paging.
|
||||||
|
func (c *Collection) SearchValues(cursor uint64, stype ScanType, desc bool,
|
||||||
|
iterator func(id string, obj geojson.Object, fields []float64) bool,
|
||||||
|
) (ncursor uint64) {
|
||||||
|
var i uint64
|
||||||
|
var active = true
|
||||||
|
iter := func(item btree.Item) bool {
|
||||||
|
if i >= cursor {
|
||||||
|
iitm := item.(*itemT)
|
||||||
|
active = iterator(iitm.id, iitm.object, iitm.fields)
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
return active
|
||||||
|
}
|
||||||
|
if desc {
|
||||||
|
c.values.Descend(iter)
|
||||||
|
} else {
|
||||||
|
c.values.Ascend(iter)
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchValuesRange iterates though the collection values. A cursor can be used for paging.
|
||||||
|
func (c *Collection) SearchValuesRange(cursor uint64, stype ScanType, start, end string, desc bool,
|
||||||
|
iterator func(id string, obj geojson.Object, fields []float64) bool,
|
||||||
|
) (ncursor uint64) {
|
||||||
|
var i uint64
|
||||||
|
var active = true
|
||||||
|
iter := func(item btree.Item) bool {
|
||||||
|
if i >= cursor {
|
||||||
|
iitm := item.(*itemT)
|
||||||
|
active = iterator(iitm.id, iitm.object, iitm.fields)
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
return active
|
||||||
|
}
|
||||||
|
if desc {
|
||||||
|
c.values.DescendRange(&itemT{object: geojson.String(start)}, &itemT{object: geojson.String(end)}, iter)
|
||||||
|
} else {
|
||||||
|
c.values.AscendRange(&itemT{object: geojson.String(start)}, &itemT{object: geojson.String(end)}, iter)
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
// ScanGreaterOrEqual iterates though the collection starting with specified id. A cursor can be used for paging.
|
// ScanGreaterOrEqual iterates though the collection starting with specified id. A cursor can be used for paging.
|
||||||
func (c *Collection) ScanGreaterOrEqual(id string, cursor uint64, stype ScanType, desc bool,
|
func (c *Collection) ScanGreaterOrEqual(id string, cursor uint64, stype ScanType, desc bool,
|
||||||
iterator func(id string, obj geojson.Object, fields []float64) bool,
|
iterator func(id string, obj geojson.Object, fields []float64) bool,
|
||||||
@ -448,5 +492,3 @@ func (c *Collection) Intersects(cursor uint64, sparse uint8, obj geojson.Object,
|
|||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
func (c *Collection) SearchValues(pivot string, desc bool, iterator func(id string, obj geojson.Object, fields []float64) bool) {
|
|
||||||
}
|
|
||||||
|
@ -329,7 +329,7 @@ func (c *Controller) handleInputCommand(conn *server.Conn, msg *server.Message,
|
|||||||
if c.config.ReadOnly {
|
if c.config.ReadOnly {
|
||||||
return writeErr(errors.New("read only"))
|
return writeErr(errors.New("read only"))
|
||||||
}
|
}
|
||||||
case "get", "keys", "scan", "nearby", "within", "intersects", "hooks": //, "search":
|
case "get", "keys", "scan", "nearby", "within", "intersects", "hooks", "search":
|
||||||
// read operations
|
// read operations
|
||||||
c.mu.RLock()
|
c.mu.RLock()
|
||||||
defer c.mu.RUnlock()
|
defer c.mu.RUnlock()
|
||||||
|
@ -231,7 +231,7 @@ func (c *Controller) cmdSetHook(msg *server.Message) (res string, d commandDetai
|
|||||||
Message: cmsg,
|
Message: cmsg,
|
||||||
}
|
}
|
||||||
var wr bytes.Buffer
|
var wr bytes.Buffer
|
||||||
hook.ScanWriter, err = c.newScanWriter(&wr, cmsg, s.key, s.output, s.precision, s.glob, s.limit, s.wheres, s.nofields)
|
hook.ScanWriter, err = c.newScanWriter(&wr, cmsg, s.key, s.output, s.precision, s.glob, false, s.limit, s.wheres, s.nofields)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", d, err
|
return "", d, err
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ func (c *Controller) goLive(inerr error, conn net.Conn, rd *server.AnyReaderWrit
|
|||||||
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, msg, 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, false, 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
|
||||||
|
@ -31,7 +31,7 @@ func (c *Controller) cmdScan(msg *server.Message) (res string, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
sw, err := c.newScanWriter(wr, msg, 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, false, s.limit, s.wheres, s.nofields)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -51,10 +51,13 @@ type scanWriter struct {
|
|||||||
globSingle bool
|
globSingle bool
|
||||||
fullFields bool
|
fullFields bool
|
||||||
values []resp.Value
|
values []resp.Value
|
||||||
|
matchValues bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) newScanWriter(
|
func (c *Controller) newScanWriter(
|
||||||
wr *bytes.Buffer, msg *server.Message, key string, output outputT, precision uint64, globPattern string, limit uint64, wheres []whereT, nofields bool,
|
wr *bytes.Buffer, msg *server.Message, key string, output outputT,
|
||||||
|
precision uint64, globPattern string, matchValues bool,
|
||||||
|
limit uint64, wheres []whereT, nofields bool,
|
||||||
) (
|
) (
|
||||||
*scanWriter, error,
|
*scanWriter, error,
|
||||||
) {
|
) {
|
||||||
@ -69,15 +72,16 @@ func (c *Controller) newScanWriter(
|
|||||||
case outputIDs, outputObjects, outputCount, outputBounds, outputPoints, outputHashes:
|
case outputIDs, outputObjects, outputCount, outputBounds, outputPoints, outputHashes:
|
||||||
}
|
}
|
||||||
sw := &scanWriter{
|
sw := &scanWriter{
|
||||||
c: c,
|
c: c,
|
||||||
wr: wr,
|
wr: wr,
|
||||||
msg: msg,
|
msg: msg,
|
||||||
output: output,
|
output: output,
|
||||||
wheres: wheres,
|
wheres: wheres,
|
||||||
precision: precision,
|
precision: precision,
|
||||||
nofields: nofields,
|
nofields: nofields,
|
||||||
glob: globPattern,
|
glob: globPattern,
|
||||||
limit: limit,
|
limit: limit,
|
||||||
|
matchValues: matchValues,
|
||||||
}
|
}
|
||||||
if globPattern == "*" || globPattern == "" {
|
if globPattern == "*" || globPattern == "" {
|
||||||
sw.globEverything = true
|
sw.globEverything = true
|
||||||
@ -242,7 +246,13 @@ func (sw *scanWriter) writeObject(id string, o geojson.Object, fields []float64,
|
|||||||
}
|
}
|
||||||
keepGoing = false // return current object and stop iterating
|
keepGoing = false // return current object and stop iterating
|
||||||
} else {
|
} else {
|
||||||
ok, _ := glob.Match(sw.glob, id)
|
var val string
|
||||||
|
if sw.matchValues {
|
||||||
|
val = o.String()
|
||||||
|
} else {
|
||||||
|
val = id
|
||||||
|
}
|
||||||
|
ok, _ := glob.Match(sw.glob, val)
|
||||||
if !ok {
|
if !ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/tidwall/resp"
|
"github.com/tidwall/resp"
|
||||||
"github.com/tidwall/tile38/controller/bing"
|
"github.com/tidwall/tile38/controller/bing"
|
||||||
|
"github.com/tidwall/tile38/controller/collection"
|
||||||
"github.com/tidwall/tile38/controller/glob"
|
"github.com/tidwall/tile38/controller/glob"
|
||||||
"github.com/tidwall/tile38/controller/server"
|
"github.com/tidwall/tile38/controller/server"
|
||||||
"github.com/tidwall/tile38/geojson"
|
"github.com/tidwall/tile38/geojson"
|
||||||
@ -264,7 +265,7 @@ func (c *Controller) cmdNearby(msg *server.Message) (res string, err error) {
|
|||||||
if s.fence {
|
if s.fence {
|
||||||
return "", s
|
return "", s
|
||||||
}
|
}
|
||||||
sw, err := c.newScanWriter(wr, msg, 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, false, s.limit, s.wheres, s.nofields)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -305,7 +306,7 @@ func (c *Controller) cmdWithinOrIntersects(cmd string, msg *server.Message) (res
|
|||||||
if s.fence {
|
if s.fence {
|
||||||
return "", s
|
return "", s
|
||||||
}
|
}
|
||||||
sw, err := c.newScanWriter(wr, msg, 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, false, s.limit, s.wheres, s.nofields)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -336,95 +337,63 @@ func (c *Controller) cmdWithinOrIntersects(cmd string, msg *server.Message) (res
|
|||||||
return string(wr.Bytes()), nil
|
return string(wr.Bytes()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) cmdSearch(msg *server.Message) (res string, err error) {
|
func cmdSeachValuesArgs(vs []resp.Value) (s liveFenceSwitches, err error) {
|
||||||
start := time.Now()
|
if vs, s.searchScanBaseTokens, err = parseSearchScanBaseTokens("search", vs); err != nil {
|
||||||
vs := msg.Values[1:]
|
return
|
||||||
var ok bool
|
}
|
||||||
var key string
|
if len(vs) != 0 {
|
||||||
if vs, key, ok = tokenval(vs); !ok || key == "" {
|
|
||||||
err = errInvalidNumberOfArguments
|
err = errInvalidNumberOfArguments
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
col := c.getCol(key)
|
return
|
||||||
if col == nil {
|
}
|
||||||
err = errKeyNotFound
|
|
||||||
return
|
func (c *Controller) cmdSearch(msg *server.Message) (res string, err error) {
|
||||||
}
|
start := time.Now()
|
||||||
var tok string
|
vs := msg.Values[1:]
|
||||||
var pivot string
|
|
||||||
var pivoton bool
|
|
||||||
var limiton bool
|
|
||||||
var limit int
|
|
||||||
var descon bool
|
|
||||||
var desc bool
|
|
||||||
for {
|
|
||||||
if vs, tok, ok = tokenval(vs); !ok || tok == "" {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
switch strings.ToLower(tok) {
|
|
||||||
default:
|
|
||||||
err = errInvalidArgument(tok)
|
|
||||||
return
|
|
||||||
case "pivot":
|
|
||||||
if pivoton {
|
|
||||||
err = errInvalidArgument(tok)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pivoton = true
|
|
||||||
if vs, pivot, ok = tokenval(vs); !ok || pivot == "" {
|
|
||||||
err = errInvalidNumberOfArguments
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case "limit":
|
|
||||||
if limiton {
|
|
||||||
err = errInvalidArgument(tok)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
limiton = true
|
|
||||||
if vs, tok, ok = tokenval(vs); !ok || tok == "" {
|
|
||||||
err = errInvalidNumberOfArguments
|
|
||||||
return
|
|
||||||
}
|
|
||||||
n, err2 := strconv.ParseUint(tok, 10, 64)
|
|
||||||
if err2 != nil {
|
|
||||||
err = errInvalidArgument(tok)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
limit = int(n)
|
|
||||||
case "asc", "desc":
|
|
||||||
if descon {
|
|
||||||
err = errInvalidArgument(tok)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
descon = true
|
|
||||||
switch strings.ToLower(tok) {
|
|
||||||
case "asc":
|
|
||||||
desc = false
|
|
||||||
case "desc":
|
|
||||||
desc = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println(pivoton, pivot)
|
|
||||||
println(limiton, limit)
|
|
||||||
println(descon, desc)
|
|
||||||
wr := &bytes.Buffer{}
|
wr := &bytes.Buffer{}
|
||||||
if msg.OutputType == server.JSON {
|
s, err := cmdSeachValuesArgs(vs)
|
||||||
wr.WriteString(`{"ok":true,"objects":[`)
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
sw, err := c.newScanWriter(wr, msg, s.key, s.output, s.precision, s.glob, true, s.limit, s.wheres, s.nofields)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
}
|
}
|
||||||
n := 0
|
|
||||||
col.SearchValues(pivot, desc, func(id string, obj geojson.Object, fields []float64) bool {
|
|
||||||
if msg.OutputType == server.JSON {
|
|
||||||
if n > 0 {
|
|
||||||
wr.WriteString(`,`)
|
|
||||||
}
|
|
||||||
wr.WriteString(`{"id":` + jsonString(id) + `,"object":` + obj.JSON() + `}`)
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
if msg.OutputType == server.JSON {
|
if msg.OutputType == server.JSON {
|
||||||
wr.WriteString(`],"elapsed":"` + time.Now().Sub(start).String() + "\"}")
|
wr.WriteString(`{"ok":true`)
|
||||||
|
}
|
||||||
|
sw.writeHead()
|
||||||
|
if sw.col != nil {
|
||||||
|
stype := collection.TypeAll
|
||||||
|
if sw.output == outputCount && len(sw.wheres) == 0 && sw.globEverything == true {
|
||||||
|
count := sw.col.Count(stype) - int(s.cursor)
|
||||||
|
if count < 0 {
|
||||||
|
count = 0
|
||||||
|
}
|
||||||
|
sw.count = uint64(count)
|
||||||
|
} else {
|
||||||
|
g := glob.Parse(sw.glob, s.desc)
|
||||||
|
if g.Limits[0] == "" && g.Limits[1] == "" {
|
||||||
|
s.cursor = sw.col.SearchValues(s.cursor, stype, s.desc,
|
||||||
|
func(id string, o geojson.Object, fields []float64) bool {
|
||||||
|
return sw.writeObject(id, o, fields, false)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
s.cursor = sw.col.SearchValuesRange(
|
||||||
|
s.cursor, stype, g.Limits[0], g.Limits[1], s.desc,
|
||||||
|
func(id string, o geojson.Object, fields []float64) bool {
|
||||||
|
return sw.writeObject(id, o, fields, false)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sw.writeFoot(s.cursor)
|
||||||
|
if msg.OutputType == server.JSON {
|
||||||
|
wr.WriteString(`,"elapsed":"` + time.Now().Sub(start).String() + "\"}")
|
||||||
}
|
}
|
||||||
return string(wr.Bytes()), nil
|
return string(wr.Bytes()), nil
|
||||||
}
|
}
|
||||||
|
@ -309,13 +309,13 @@ func parseSearchScanBaseTokens(cmd string, vs []resp.Value) (vsout []resp.Value,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check to make sure that there aren't any conflicts
|
// check to make sure that there aren't any conflicts
|
||||||
if cmd == "scan" {
|
if cmd == "scan" || cmd == "search" {
|
||||||
if ssparse != "" {
|
if ssparse != "" {
|
||||||
err = errors.New("SPARSE is not allowed for SCAN")
|
err = errors.New("SPARSE is not allowed for " + strings.ToUpper(cmd))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if t.fence {
|
if t.fence {
|
||||||
err = errors.New("FENCE is not allowed for SCAN")
|
err = errors.New("FENCE is not allowed for " + strings.ToUpper(cmd))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -216,6 +216,91 @@
|
|||||||
"since": "1.0.0",
|
"since": "1.0.0",
|
||||||
"group": "keys"
|
"group": "keys"
|
||||||
},
|
},
|
||||||
|
"SEARCH": {
|
||||||
|
"summary": "Search for string values in a key",
|
||||||
|
"complexity": "O(N) where N is the number of values in the key",
|
||||||
|
"arguments":[
|
||||||
|
{
|
||||||
|
"name": "key",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "CURSOR",
|
||||||
|
"name": "start",
|
||||||
|
"type": "integer",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "LIMIT",
|
||||||
|
"name": "count",
|
||||||
|
"type": "integer",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "MATCH",
|
||||||
|
"name": "pattern",
|
||||||
|
"type": "pattern",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "order",
|
||||||
|
"optional": true,
|
||||||
|
"enumargs": [
|
||||||
|
{
|
||||||
|
"name": "ASC"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "DESC"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "WHERE",
|
||||||
|
"name": ["field","min","max"],
|
||||||
|
"type": ["string","double","double"],
|
||||||
|
"optional": true,
|
||||||
|
"multiple": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "NOFIELDS",
|
||||||
|
"name": [],
|
||||||
|
"type": [],
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "type",
|
||||||
|
"optional": true,
|
||||||
|
"enumargs": [
|
||||||
|
{
|
||||||
|
"name": "COUNT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "IDS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "OBJECTS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "POINTS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "BOUNDS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "HASHES",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "precision",
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"since": "1.0.0",
|
||||||
|
"group": "search"
|
||||||
|
},
|
||||||
"SCAN": {
|
"SCAN": {
|
||||||
"summary": "Incrementally iterate though a key",
|
"summary": "Incrementally iterate though a key",
|
||||||
"complexity": "O(N) where N is the number of ids in the key",
|
"complexity": "O(N) where N is the number of ids in the key",
|
||||||
|
@ -378,6 +378,91 @@ var commandsJSON = `{
|
|||||||
"since": "1.0.0",
|
"since": "1.0.0",
|
||||||
"group": "keys"
|
"group": "keys"
|
||||||
},
|
},
|
||||||
|
"SEARCH": {
|
||||||
|
"summary": "Search for string values in a key",
|
||||||
|
"complexity": "O(N) where N is the number of values in the key",
|
||||||
|
"arguments":[
|
||||||
|
{
|
||||||
|
"name": "key",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "CURSOR",
|
||||||
|
"name": "start",
|
||||||
|
"type": "integer",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "LIMIT",
|
||||||
|
"name": "count",
|
||||||
|
"type": "integer",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "MATCH",
|
||||||
|
"name": "pattern",
|
||||||
|
"type": "pattern",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "order",
|
||||||
|
"optional": true,
|
||||||
|
"enumargs": [
|
||||||
|
{
|
||||||
|
"name": "ASC"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "DESC"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "WHERE",
|
||||||
|
"name": ["field","min","max"],
|
||||||
|
"type": ["string","double","double"],
|
||||||
|
"optional": true,
|
||||||
|
"multiple": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "NOFIELDS",
|
||||||
|
"name": [],
|
||||||
|
"type": [],
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "type",
|
||||||
|
"optional": true,
|
||||||
|
"enumargs": [
|
||||||
|
{
|
||||||
|
"name": "COUNT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "IDS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "OBJECTS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "POINTS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "BOUNDS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "HASHES",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "precision",
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"since": "1.0.0",
|
||||||
|
"group": "search"
|
||||||
|
},
|
||||||
"SCAN": {
|
"SCAN": {
|
||||||
"summary": "Incrementally iterate though a key",
|
"summary": "Incrementally iterate though a key",
|
||||||
"complexity": "O(N) where N is the number of ids in the key",
|
"complexity": "O(N) where N is the number of ids in the key",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user