mirror of https://github.com/tidwall/tile38.git
scan order asc/desc
This commit is contained in:
parent
4fd7e1f821
commit
75adea6a9c
|
@ -150,7 +150,7 @@ func (c *Controller) aofshrink() {
|
||||||
objs := make(map[string]objFields)
|
objs := make(map[string]objFields)
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
fnames := col.FieldArr() // reload an array of field names to match each object
|
fnames := col.FieldArr() // reload an array of field names to match each object
|
||||||
col.ScanGreaterOrEqual(nextID, 0, collection.TypeAll,
|
col.ScanGreaterOrEqual(nextID, 1, collection.TypeAll, false,
|
||||||
func(id string, obj geojson.Object, fields []float64) bool {
|
func(id string, obj geojson.Object, fields []float64) bool {
|
||||||
if id != nextID {
|
if id != nextID {
|
||||||
objs[id] = objFields{obj, fields}
|
objs[id] = objFields{obj, fields}
|
||||||
|
|
|
@ -236,32 +236,69 @@ func (c *Collection) FieldArr() []string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scan iterates though the collection. A cursor can be used for paging.
|
// Scan iterates though the collection. A cursor can be used for paging.
|
||||||
func (c *Collection) Scan(cursor uint64, stype ScanType, iterator func(id string, obj geojson.Object, fields []float64) bool) (ncursor uint64) {
|
func (c *Collection) Scan(cursor uint64, stype ScanType, desc bool,
|
||||||
|
iterator func(id string, obj geojson.Object, fields []float64) bool,
|
||||||
|
) (ncursor uint64) {
|
||||||
var i uint64
|
var i uint64
|
||||||
var active = true
|
var active = true
|
||||||
c.items.Ascend(func(item btree.Item) bool {
|
iter := func(item btree.Item) bool {
|
||||||
if i >= cursor {
|
if i >= cursor {
|
||||||
iitm := item.(*itemT)
|
iitm := item.(*itemT)
|
||||||
active = iterator(iitm.id, iitm.object, iitm.fields)
|
active = iterator(iitm.id, iitm.object, iitm.fields)
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
return active
|
return active
|
||||||
})
|
}
|
||||||
|
if desc {
|
||||||
|
c.items.Descend(iter)
|
||||||
|
} else {
|
||||||
|
c.items.Ascend(iter)
|
||||||
|
}
|
||||||
return i
|
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, iterator func(id string, obj geojson.Object, fields []float64) bool) (ncursor uint64) {
|
func (c *Collection) ScanRange(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 i uint64
|
||||||
var active = true
|
var active = true
|
||||||
c.items.AscendGreaterOrEqual(&itemT{id: id}, func(item btree.Item) bool {
|
iter := func(item btree.Item) bool {
|
||||||
if i >= cursor {
|
if i >= cursor {
|
||||||
iitm := item.(*itemT)
|
iitm := item.(*itemT)
|
||||||
active = iterator(iitm.id, iitm.object, iitm.fields)
|
active = iterator(iitm.id, iitm.object, iitm.fields)
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
return active
|
return active
|
||||||
})
|
}
|
||||||
|
|
||||||
|
if desc {
|
||||||
|
c.items.DescendRange(&itemT{id: start}, &itemT{id: end}, iter)
|
||||||
|
} else {
|
||||||
|
c.items.AscendRange(&itemT{id: start}, &itemT{id: end}, iter)
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
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.items.DescendLessOrEqual(&itemT{id: id}, iter)
|
||||||
|
} else {
|
||||||
|
c.items.AscendGreaterOrEqual(&itemT{id: id}, iter)
|
||||||
|
}
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tidwall/resp"
|
"github.com/tidwall/resp"
|
||||||
|
"github.com/tidwall/tile38/controller/glob"
|
||||||
"github.com/tidwall/tile38/controller/server"
|
"github.com/tidwall/tile38/controller/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -147,7 +148,7 @@ func (c *Controller) setConfigProperty(name, value string, fromLoad bool) error
|
||||||
func (c *Controller) getConfigProperties(pattern string) map[string]interface{} {
|
func (c *Controller) getConfigProperties(pattern string) map[string]interface{} {
|
||||||
m := make(map[string]interface{})
|
m := make(map[string]interface{})
|
||||||
for _, name := range validProperties {
|
for _, name := range validProperties {
|
||||||
matched, _ := globMatch(pattern, name)
|
matched, _ := glob.Match(pattern, name)
|
||||||
if matched {
|
if matched {
|
||||||
m[name] = c.getConfigProperty(name)
|
m[name] = c.getConfigProperty(name)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"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"
|
||||||
)
|
)
|
||||||
|
@ -14,13 +15,13 @@ var tmfmt = "2006-01-02T15:04:05.999999999Z07:00"
|
||||||
func FenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, details *commandDetailsT) []string {
|
func FenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, details *commandDetailsT) []string {
|
||||||
jshookName := jsonString(hookName)
|
jshookName := jsonString(hookName)
|
||||||
jstime := jsonString(details.timestamp.Format(tmfmt))
|
jstime := jsonString(details.timestamp.Format(tmfmt))
|
||||||
glob := fence.glob
|
pattern := fence.glob
|
||||||
if details.command == "drop" {
|
if details.command == "drop" {
|
||||||
return []string{`{"cmd":"drop","hook":` + jshookName + `,"time":` + jstime + `}`}
|
return []string{`{"cmd":"drop","hook":` + jshookName + `,"time":` + jstime + `}`}
|
||||||
}
|
}
|
||||||
match := true
|
match := true
|
||||||
if glob != "" && glob != "*" {
|
if pattern != "" && pattern != "*" {
|
||||||
match, _ = globMatch(glob, details.id)
|
match, _ = glob.Match(pattern, details.id)
|
||||||
}
|
}
|
||||||
if !match {
|
if !match {
|
||||||
return nil
|
return nil
|
||||||
|
@ -202,7 +203,7 @@ func fenceMatchRoam(c *Controller, fence *liveFenceSwitches, tkey, tid string, o
|
||||||
return true // skip self
|
return true // skip self
|
||||||
}
|
}
|
||||||
if fence.roam.pattern {
|
if fence.roam.pattern {
|
||||||
match, _ = globMatch(fence.roam.id, id)
|
match, _ = glob.Match(fence.roam.id, id)
|
||||||
} else {
|
} else {
|
||||||
match = fence.roam.id == id
|
match = fence.roam.id == id
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
package controller
|
|
||||||
|
|
||||||
import "path"
|
|
||||||
|
|
||||||
func globMatch(pattern, name string) (matched bool, err error) {
|
|
||||||
return path.Match(pattern, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func globIsGlob(pattern string) bool {
|
|
||||||
for i := 0; i < len(pattern); i++ {
|
|
||||||
switch pattern[i] {
|
|
||||||
case '[', '*', '?':
|
|
||||||
_, err := globMatch(pattern, "whatever")
|
|
||||||
return err == nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tidwall/resp"
|
"github.com/tidwall/resp"
|
||||||
|
"github.com/tidwall/tile38/controller/glob"
|
||||||
"github.com/tidwall/tile38/controller/log"
|
"github.com/tidwall/tile38/controller/log"
|
||||||
"github.com/tidwall/tile38/controller/server"
|
"github.com/tidwall/tile38/controller/server"
|
||||||
)
|
)
|
||||||
|
@ -329,7 +330,7 @@ func (c *Controller) cmdHooks(msg *server.Message) (res string, err error) {
|
||||||
|
|
||||||
var hooks []*Hook
|
var hooks []*Hook
|
||||||
for name, hook := range c.hooks {
|
for name, hook := range c.hooks {
|
||||||
match, _ := globMatch(pattern, name)
|
match, _ := glob.Match(pattern, name)
|
||||||
if match {
|
if match {
|
||||||
hooks = append(hooks, hook)
|
hooks = append(hooks, hook)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"github.com/tidwall/btree"
|
"github.com/tidwall/btree"
|
||||||
"github.com/tidwall/resp"
|
"github.com/tidwall/resp"
|
||||||
|
"github.com/tidwall/tile38/controller/glob"
|
||||||
"github.com/tidwall/tile38/controller/server"
|
"github.com/tidwall/tile38/controller/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -44,7 +45,7 @@ func (c *Controller) cmdKeys(msg *server.Message) (res string, err error) {
|
||||||
}
|
}
|
||||||
match = true
|
match = true
|
||||||
} else {
|
} else {
|
||||||
match, _ = globMatch(pattern, key)
|
match, _ = glob.Match(pattern, key)
|
||||||
}
|
}
|
||||||
if match {
|
if match {
|
||||||
if once {
|
if once {
|
||||||
|
@ -69,14 +70,14 @@ func (c *Controller) cmdKeys(msg *server.Message) (res string, err error) {
|
||||||
} else {
|
} else {
|
||||||
if strings.HasSuffix(pattern, "*") {
|
if strings.HasSuffix(pattern, "*") {
|
||||||
greaterPivot = pattern[:len(pattern)-1]
|
greaterPivot = pattern[:len(pattern)-1]
|
||||||
if globIsGlob(greaterPivot) {
|
if glob.IsGlob(greaterPivot) {
|
||||||
greater = false
|
greater = false
|
||||||
c.cols.Ascend(iterator)
|
c.cols.Ascend(iterator)
|
||||||
} else {
|
} else {
|
||||||
greater = true
|
greater = true
|
||||||
c.cols.AscendGreaterOrEqual(&collectionT{Key: greaterPivot}, iterator)
|
c.cols.AscendGreaterOrEqual(&collectionT{Key: greaterPivot}, iterator)
|
||||||
}
|
}
|
||||||
} else if globIsGlob(pattern) {
|
} else if glob.IsGlob(pattern) {
|
||||||
greater = false
|
greater = false
|
||||||
c.cols.Ascend(iterator)
|
c.cols.Ascend(iterator)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -2,11 +2,11 @@ package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tidwall/resp"
|
"github.com/tidwall/resp"
|
||||||
"github.com/tidwall/tile38/controller/collection"
|
"github.com/tidwall/tile38/controller/collection"
|
||||||
|
"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"
|
||||||
)
|
)
|
||||||
|
@ -48,23 +48,16 @@ func (c *Controller) cmdScan(msg *server.Message) (res string, err error) {
|
||||||
}
|
}
|
||||||
sw.count = uint64(count)
|
sw.count = uint64(count)
|
||||||
} else {
|
} else {
|
||||||
if strings.HasSuffix(sw.glob, "*") {
|
g := glob.Parse(sw.glob, s.desc)
|
||||||
greaterGlob := sw.glob[:len(sw.glob)-1]
|
if g.Limits[0] == "" && g.Limits[1] == "" {
|
||||||
if globIsGlob(greaterGlob) {
|
s.cursor = sw.col.Scan(s.cursor, stype, s.desc,
|
||||||
s.cursor = sw.col.Scan(s.cursor, stype,
|
|
||||||
func(id string, o geojson.Object, fields []float64) bool {
|
func(id string, o geojson.Object, fields []float64) bool {
|
||||||
return sw.writeObject(id, o, fields, false)
|
return sw.writeObject(id, o, fields, false)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
s.cursor = sw.col.ScanGreaterOrEqual(greaterGlob, s.cursor, stype,
|
s.cursor = sw.col.ScanRange(
|
||||||
func(id string, o geojson.Object, fields []float64) bool {
|
s.cursor, stype, g.Limits[0], g.Limits[1], s.desc,
|
||||||
return sw.writeObject(id, o, fields, false)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
s.cursor = sw.col.Scan(s.cursor, stype,
|
|
||||||
func(id string, o geojson.Object, fields []float64) bool {
|
func(id string, o geojson.Object, fields []float64) bool {
|
||||||
return sw.writeObject(id, o, fields, false)
|
return sw.writeObject(id, o, fields, false)
|
||||||
},
|
},
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
"github.com/tidwall/resp"
|
"github.com/tidwall/resp"
|
||||||
"github.com/tidwall/tile38/controller/collection"
|
"github.com/tidwall/tile38/controller/collection"
|
||||||
|
"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"
|
||||||
)
|
)
|
||||||
|
@ -53,7 +54,7 @@ type scanWriter struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) newScanWriter(
|
func (c *Controller) newScanWriter(
|
||||||
wr *bytes.Buffer, msg *server.Message, 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, globPattern string, limit uint64, wheres []whereT, nofields bool,
|
||||||
) (
|
) (
|
||||||
*scanWriter, error,
|
*scanWriter, error,
|
||||||
) {
|
) {
|
||||||
|
@ -75,13 +76,13 @@ func (c *Controller) newScanWriter(
|
||||||
wheres: wheres,
|
wheres: wheres,
|
||||||
precision: precision,
|
precision: precision,
|
||||||
nofields: nofields,
|
nofields: nofields,
|
||||||
glob: glob,
|
glob: globPattern,
|
||||||
limit: limit,
|
limit: limit,
|
||||||
}
|
}
|
||||||
if glob == "*" || glob == "" {
|
if globPattern == "*" || globPattern == "" {
|
||||||
sw.globEverything = true
|
sw.globEverything = true
|
||||||
} else {
|
} else {
|
||||||
if !globIsGlob(glob) {
|
if !glob.IsGlob(globPattern) {
|
||||||
sw.globSingle = true
|
sw.globSingle = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -241,7 +242,7 @@ 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, _ := globMatch(sw.glob, id)
|
ok, _ := glob.Match(sw.glob, id)
|
||||||
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/glob"
|
||||||
"github.com/tidwall/tile38/controller/server"
|
"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"
|
||||||
|
@ -230,7 +231,7 @@ func (c *Controller) cmdSearchArgs(cmd string, vs []resp.Value, types []string)
|
||||||
err = errInvalidNumberOfArguments
|
err = errInvalidNumberOfArguments
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.roam.pattern = globIsGlob(s.roam.id)
|
s.roam.pattern = glob.IsGlob(s.roam.id)
|
||||||
var smeters string
|
var smeters string
|
||||||
if vs, smeters, ok = tokenval(vs); !ok || smeters == "" {
|
if vs, smeters, ok = tokenval(vs); !ok || smeters == "" {
|
||||||
err = errInvalidNumberOfArguments
|
err = errInvalidNumberOfArguments
|
||||||
|
|
|
@ -130,6 +130,7 @@ type searchScanBaseTokens struct {
|
||||||
nofields bool
|
nofields bool
|
||||||
limit uint64
|
limit uint64
|
||||||
sparse uint8
|
sparse uint8
|
||||||
|
desc bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseSearchScanBaseTokens(cmd string, vs []resp.Value) (vsout []resp.Value, t searchScanBaseTokens, err error) {
|
func parseSearchScanBaseTokens(cmd string, vs []resp.Value) (vsout []resp.Value, t searchScanBaseTokens, err error) {
|
||||||
|
@ -141,6 +142,7 @@ func parseSearchScanBaseTokens(cmd string, vs []resp.Value) (vsout []resp.Value,
|
||||||
var slimit string
|
var slimit string
|
||||||
var ssparse string
|
var ssparse string
|
||||||
var scursor string
|
var scursor string
|
||||||
|
var asc bool
|
||||||
for {
|
for {
|
||||||
nvs, wtok, ok := tokenval(vs)
|
nvs, wtok, ok := tokenval(vs)
|
||||||
if ok && len(wtok) > 0 {
|
if ok && len(wtok) > 0 {
|
||||||
|
@ -273,7 +275,22 @@ func parseSearchScanBaseTokens(cmd string, vs []resp.Value) (vsout []resp.Value,
|
||||||
"cross": true,
|
"cross": true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
continue
|
||||||
|
} else if (wtok[0] == 'D' || wtok[0] == 'd') && strings.ToLower(wtok) == "desc" {
|
||||||
|
vs = nvs
|
||||||
|
if t.desc || asc {
|
||||||
|
err = errDuplicateArgument(strings.ToUpper(wtok))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.desc = true
|
||||||
|
continue
|
||||||
|
} else if (wtok[0] == 'A' || wtok[0] == 'a') && strings.ToLower(wtok) == "asc" {
|
||||||
|
vs = nvs
|
||||||
|
if t.desc || asc {
|
||||||
|
err = errDuplicateArgument(strings.ToUpper(wtok))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
asc = 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" {
|
||||||
vs = nvs
|
vs = nvs
|
||||||
|
@ -301,6 +318,15 @@ func parseSearchScanBaseTokens(cmd string, vs []resp.Value) (vsout []resp.Value,
|
||||||
err = errors.New("FENCE is not allowed for SCAN")
|
err = errors.New("FENCE is not allowed for SCAN")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if t.desc {
|
||||||
|
err = errors.New("DESC is not allowed for " + strings.ToUpper(cmd))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if asc {
|
||||||
|
err = errors.New("ASC is not allowed for " + strings.ToUpper(cmd))
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ssparse != "" && slimit != "" {
|
if ssparse != "" && slimit != "" {
|
||||||
err = errors.New("LIMIT is not allowed when SPARSE is specified")
|
err = errors.New("LIMIT is not allowed when SPARSE is specified")
|
||||||
|
@ -353,7 +379,6 @@ func parseSearchScanBaseTokens(cmd string, vs []resp.Value) (vsout []resp.Value,
|
||||||
vs = nvs
|
vs = nvs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if scursor != "" {
|
if scursor != "" {
|
||||||
if t.cursor, err = strconv.ParseUint(scursor, 10, 64); err != nil {
|
if t.cursor, err = strconv.ParseUint(scursor, 10, 64); err != nil {
|
||||||
err = errInvalidArgument(scursor)
|
err = errInvalidArgument(scursor)
|
||||||
|
|
|
@ -216,7 +216,6 @@
|
||||||
"since": "1.0.0",
|
"since": "1.0.0",
|
||||||
"group": "keys"
|
"group": "keys"
|
||||||
},
|
},
|
||||||
|
|
||||||
"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",
|
||||||
|
@ -243,6 +242,18 @@
|
||||||
"type": "pattern",
|
"type": "pattern",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "order",
|
||||||
|
"optional": true,
|
||||||
|
"enumargs": [
|
||||||
|
{
|
||||||
|
"name": "ASC"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "DESC"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"command": "WHERE",
|
"command": "WHERE",
|
||||||
"name": ["field","min","max"],
|
"name": ["field","min","max"],
|
||||||
|
|
|
@ -378,7 +378,6 @@ var commandsJSON = `{
|
||||||
"since": "1.0.0",
|
"since": "1.0.0",
|
||||||
"group": "keys"
|
"group": "keys"
|
||||||
},
|
},
|
||||||
|
|
||||||
"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",
|
||||||
|
@ -405,6 +404,18 @@ var commandsJSON = `{
|
||||||
"type": "pattern",
|
"type": "pattern",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "order",
|
||||||
|
"optional": true,
|
||||||
|
"enumargs": [
|
||||||
|
{
|
||||||
|
"name": "ASC"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "DESC"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"command": "WHERE",
|
"command": "WHERE",
|
||||||
"name": ["field","min","max"],
|
"name": ["field","min","max"],
|
||||||
|
|
Loading…
Reference in New Issue