forked from mirror/ledisdb
add rev scan support
This commit is contained in:
parent
c527d8ebcb
commit
f09e43ad99
|
@ -1,4 +1,4 @@
|
||||||
//This file was generated by .tools/generate_commands.py on Fri Oct 17 2014 11:39:51 +0800
|
//This file was generated by .tools/generate_commands.py on Mon Oct 20 2014 22:35:33 +0800
|
||||||
package main
|
package main
|
||||||
|
|
||||||
var helpCommands = [][]string{
|
var helpCommands = [][]string{
|
||||||
|
@ -16,6 +16,7 @@ var helpCommands = [][]string{
|
||||||
{"BRPOP", "key [key ...] timeout", "List"},
|
{"BRPOP", "key [key ...] timeout", "List"},
|
||||||
{"BSETBIT", "key offset value", "Bitmap"},
|
{"BSETBIT", "key offset value", "Bitmap"},
|
||||||
{"BTTL", "key", "Bitmap"},
|
{"BTTL", "key", "Bitmap"},
|
||||||
|
{"BXREVSCAN", "key [MATCH match] [COUNT count]", "Bitmap"},
|
||||||
{"BXSCAN", "key [MATCH match] [COUNT count]", "Bitmap"},
|
{"BXSCAN", "key [MATCH match] [COUNT count]", "Bitmap"},
|
||||||
{"COMMIT", "-", "Transaction"},
|
{"COMMIT", "-", "Transaction"},
|
||||||
{"CONFIG REWRITE", "-", "Server"},
|
{"CONFIG REWRITE", "-", "Server"},
|
||||||
|
@ -50,6 +51,7 @@ var helpCommands = [][]string{
|
||||||
{"HSET", "key field value", "Hash"},
|
{"HSET", "key field value", "Hash"},
|
||||||
{"HTTL", "key", "Hash"},
|
{"HTTL", "key", "Hash"},
|
||||||
{"HVALS", "key", "Hash"},
|
{"HVALS", "key", "Hash"},
|
||||||
|
{"HXREVSCAN", "key [MATCH match] [COUNT count]", "Hash"},
|
||||||
{"HXSCAN", "key [MATCH match] [COUNT count]", "Hash"},
|
{"HXSCAN", "key [MATCH match] [COUNT count]", "Hash"},
|
||||||
{"INCR", "key", "KV"},
|
{"INCR", "key", "KV"},
|
||||||
{"INCRBY", "key increment", "KV"},
|
{"INCRBY", "key increment", "KV"},
|
||||||
|
@ -65,6 +67,7 @@ var helpCommands = [][]string{
|
||||||
{"LPUSH", "key value [value ...]", "List"},
|
{"LPUSH", "key value [value ...]", "List"},
|
||||||
{"LRANGE", "key start stop", "List"},
|
{"LRANGE", "key start stop", "List"},
|
||||||
{"LTTL", "key", "List"},
|
{"LTTL", "key", "List"},
|
||||||
|
{"LXREVSCAN", "key [MATCH match] [COUNT count]", "List"},
|
||||||
{"LXSCAN", "key [MATCH match] [COUNT count]", "List"},
|
{"LXSCAN", "key [MATCH match] [COUNT count]", "List"},
|
||||||
{"MGET", "key [key ...]", "KV"},
|
{"MGET", "key [key ...]", "KV"},
|
||||||
{"MSET", "key value [key value ...]", "KV"},
|
{"MSET", "key value [key value ...]", "KV"},
|
||||||
|
@ -97,10 +100,12 @@ var helpCommands = [][]string{
|
||||||
{"STTL", "key", "Set"},
|
{"STTL", "key", "Set"},
|
||||||
{"SUNION", "key [key ...]", "Set"},
|
{"SUNION", "key [key ...]", "Set"},
|
||||||
{"SUNIONSTORE", "destination key [key ...]", "Set"},
|
{"SUNIONSTORE", "destination key [key ...]", "Set"},
|
||||||
|
{"SXREVSCAN", "key [MATCH match] [COUNT count]", "Set"},
|
||||||
{"SXSCAN", "key [MATCH match] [COUNT count]", "Set"},
|
{"SXSCAN", "key [MATCH match] [COUNT count]", "Set"},
|
||||||
{"SYNC", "logid", "Replication"},
|
{"SYNC", "logid", "Replication"},
|
||||||
{"TIME", "-", "Server"},
|
{"TIME", "-", "Server"},
|
||||||
{"TTL", "key", "KV"},
|
{"TTL", "key", "KV"},
|
||||||
|
{"XREVSCAN", "key [MATCH match] [COUNT count]", "KV"},
|
||||||
{"XSCAN", "key [MATCH match] [COUNT count]", "KV"},
|
{"XSCAN", "key [MATCH match] [COUNT count]", "KV"},
|
||||||
{"ZADD", "key score member [score member ...]", "ZSet"},
|
{"ZADD", "key score member [score member ...]", "ZSet"},
|
||||||
{"ZCARD", "key", "ZSet"},
|
{"ZCARD", "key", "ZSet"},
|
||||||
|
@ -127,5 +132,6 @@ var helpCommands = [][]string{
|
||||||
{"ZSCORE", "key member", "ZSet"},
|
{"ZSCORE", "key member", "ZSet"},
|
||||||
{"ZTTL", "key", "ZSet"},
|
{"ZTTL", "key", "ZSet"},
|
||||||
{"ZUNIONSTORE", "destkey numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]", "ZSet"},
|
{"ZUNIONSTORE", "destkey numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]", "ZSet"},
|
||||||
|
{"ZXREVSCAN", "key [MATCH match] [COUNT count]", "ZSet"},
|
||||||
{"ZXSCAN", "key [MATCH match] [COUNT count]", "ZSet"},
|
{"ZXSCAN", "key [MATCH match] [COUNT count]", "ZSet"},
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,14 +47,14 @@ Transaction API:
|
||||||
|
|
||||||
## Scan
|
## Scan
|
||||||
|
|
||||||
LedisDB supplies `xscan`, etc, to fetch data iteratively.
|
LedisDB supplies `xscan`, `xrevscan`, etc, to fetch data iteratively and reverse iteratively.
|
||||||
|
|
||||||
+ KV: `xscan`
|
+ KV: `xscan`, `xrevscan`
|
||||||
+ Hash: `hxscan`
|
+ Hash: `hxscan`, `hxrevscan`
|
||||||
+ List: `lxscan`
|
+ List: `lxscan`, `lxrevscan`
|
||||||
+ Set: `sxscan`
|
+ Set: `sxscan` , `sxrevscan`
|
||||||
+ Zset: `zxscan`
|
+ Zset: `zxscan`, `zxrevscan`
|
||||||
+ Bitmap: `bxscan`
|
+ Bitmap: `bxscan`, `bxrevscan`
|
||||||
|
|
||||||
|
|
||||||
Of course, LedisDB has not implemented all APIs in Redis, you can see full commands in commands.json, commands.doc or [wiki](https://github.com/siddontang/ledisdb/wiki/Commands).
|
Of course, LedisDB has not implemented all APIs in Redis, you can see full commands in commands.json, commands.doc or [wiki](https://github.com/siddontang/ledisdb/wiki/Commands).
|
|
@ -591,6 +591,42 @@
|
||||||
"group": "Bitmap",
|
"group": "Bitmap",
|
||||||
"readonly": true
|
"readonly": true
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"XREVSCAN": {
|
||||||
|
"arguments": "key [MATCH match] [COUNT count]",
|
||||||
|
"group": "KV",
|
||||||
|
"readonly": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"HXREVSCAN": {
|
||||||
|
"arguments": "key [MATCH match] [COUNT count]",
|
||||||
|
"group": "Hash",
|
||||||
|
"readonly": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"LXREVSCAN": {
|
||||||
|
"arguments": "key [MATCH match] [COUNT count]",
|
||||||
|
"group": "List",
|
||||||
|
"readonly": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"SXREVSCAN": {
|
||||||
|
"arguments": "key [MATCH match] [COUNT count]",
|
||||||
|
"group": "Set",
|
||||||
|
"readonly": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"ZXREVSCAN": {
|
||||||
|
"arguments": "key [MATCH match] [COUNT count]",
|
||||||
|
"group": "ZSet",
|
||||||
|
"readonly": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"BXREVSCAN": {
|
||||||
|
"arguments": "key [MATCH match] [COUNT count]",
|
||||||
|
"group": "Bitmap",
|
||||||
|
"readonly": true
|
||||||
|
},
|
||||||
|
|
||||||
"FLUSHALL": {
|
"FLUSHALL": {
|
||||||
"arguments": "-",
|
"arguments": "-",
|
||||||
|
|
|
@ -27,6 +27,7 @@ Table of Contents
|
||||||
- [TTL key](#ttl-key)
|
- [TTL key](#ttl-key)
|
||||||
- [PERSIST key](#persist-key)
|
- [PERSIST key](#persist-key)
|
||||||
- [XSCAN key [MATCH match] [COUNT count]](#xscan-key-match-match-count-count)
|
- [XSCAN key [MATCH match] [COUNT count]](#xscan-key-match-match-count-count)
|
||||||
|
- [XREVSCAN key [MATCH match] [COUNT count]](#xrevscan-key-match-match-count-count)
|
||||||
- [Hash](#hash)
|
- [Hash](#hash)
|
||||||
- [HDEL key field [field ...]](#hdel-key-field-field-)
|
- [HDEL key field [field ...]](#hdel-key-field-field-)
|
||||||
- [HEXISTS key field](#hexists-key-field)
|
- [HEXISTS key field](#hexists-key-field)
|
||||||
|
@ -46,6 +47,7 @@ Table of Contents
|
||||||
- [HTTL key](#httl-key)
|
- [HTTL key](#httl-key)
|
||||||
- [HPERSIST key](#hpersist-key)
|
- [HPERSIST key](#hpersist-key)
|
||||||
- [HXSCAN key [MATCH match] [COUNT count]](#hxscan-key-match-match-count-count)
|
- [HXSCAN key [MATCH match] [COUNT count]](#hxscan-key-match-match-count-count)
|
||||||
|
- [HXREVSCAN key [MATCH match] [COUNT count]](#hxrevscan-key-match-match-count-count)
|
||||||
- [List](#list)
|
- [List](#list)
|
||||||
- [BLPOP key [key ...] timeout](#blpop-key-key--timeout)
|
- [BLPOP key [key ...] timeout](#blpop-key-key--timeout)
|
||||||
- [BRPOP key [key ...] timeout](#brpop-key-key--timeout)
|
- [BRPOP key [key ...] timeout](#brpop-key-key--timeout)
|
||||||
|
@ -63,6 +65,7 @@ Table of Contents
|
||||||
- [LTTL key](#lttl-key)
|
- [LTTL key](#lttl-key)
|
||||||
- [LPERSIST key](#lpersist-key)
|
- [LPERSIST key](#lpersist-key)
|
||||||
- [LXSCAN key [MATCH match] [COUNT count]](#lxscan-key-match-match-count-count)
|
- [LXSCAN key [MATCH match] [COUNT count]](#lxscan-key-match-match-count-count)
|
||||||
|
- [LXREVSCAN key [MATCH match] [COUNT count]](#lxrevscan-key-match-match-count-count)
|
||||||
- [Set](#set)
|
- [Set](#set)
|
||||||
- [SADD key member [member ...]](#sadd-key-member-member-)
|
- [SADD key member [member ...]](#sadd-key-member-member-)
|
||||||
- [SCARD key](#scard-key)
|
- [SCARD key](#scard-key)
|
||||||
|
@ -82,6 +85,7 @@ Table of Contents
|
||||||
- [STTL key](#sttl-key)
|
- [STTL key](#sttl-key)
|
||||||
- [SPERSIST key](#spersist-key)
|
- [SPERSIST key](#spersist-key)
|
||||||
- [SXSCAN key [MATCH match] [COUNT count]](#sxscan-key-match-match-count-count)
|
- [SXSCAN key [MATCH match] [COUNT count]](#sxscan-key-match-match-count-count)
|
||||||
|
- [SXREVSCAN key [MATCH match] [COUNT count]](#sxrevscan-key-match-match-count-count)
|
||||||
- [ZSet](#zset)
|
- [ZSet](#zset)
|
||||||
- [ZADD key score member [score member ...]](#zadd-key-score-member-score-member-)
|
- [ZADD key score member [score member ...]](#zadd-key-score-member-score-member-)
|
||||||
- [ZCARD key](#zcard-key)
|
- [ZCARD key](#zcard-key)
|
||||||
|
@ -108,6 +112,7 @@ Table of Contents
|
||||||
- [ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
|
- [ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
|
||||||
](#zinterstore-destination-numkeys-key-key--weights-weight-weight--aggregate-summinmax)
|
](#zinterstore-destination-numkeys-key-key--weights-weight-weight--aggregate-summinmax)
|
||||||
- [ZXSCAN key [MATCH match] [COUNT count]](#zxscan-key-match-match-count-count)
|
- [ZXSCAN key [MATCH match] [COUNT count]](#zxscan-key-match-match-count-count)
|
||||||
|
- [ZXREVSCAN key [MATCH match] [COUNT count]](#zxrevscan-key-match-match-count-count)
|
||||||
- [ZRANGEBYLEX key min max [LIMIT offset count]](#zrangebylex-key-min-max-limit-offset-count)
|
- [ZRANGEBYLEX key min max [LIMIT offset count]](#zrangebylex-key-min-max-limit-offset-count)
|
||||||
- [ZREMRANGEBYLEX key min max](#zremrangebylex-key-min-max)
|
- [ZREMRANGEBYLEX key min max](#zremrangebylex-key-min-max)
|
||||||
- [ZLEXCOUNT key min max](#zlexcount-key-min-max)
|
- [ZLEXCOUNT key min max](#zlexcount-key-min-max)
|
||||||
|
@ -123,6 +128,7 @@ Table of Contents
|
||||||
- [BTTL key](#bttl-key)
|
- [BTTL key](#bttl-key)
|
||||||
- [BPERSIST key](#bpersist-key)
|
- [BPERSIST key](#bpersist-key)
|
||||||
- [BXSCAN key [MATCH match] [COUNT count]](#bxscan-key-match-match-count-count)
|
- [BXSCAN key [MATCH match] [COUNT count]](#bxscan-key-match-match-count-count)
|
||||||
|
- [BXREVSCAN key [MATCH match] [COUNT count]](#bxrevscan-key-match-match-count-count)
|
||||||
- [Replication](#replication)
|
- [Replication](#replication)
|
||||||
- [SLAVEOF host port [RESTART] [READONLY]](#slaveof-host-port-restart-readonly)
|
- [SLAVEOF host port [RESTART] [READONLY]](#slaveof-host-port-restart-readonly)
|
||||||
- [FULLSYNC [NEW]](#fullsync-new)
|
- [FULLSYNC [NEW]](#fullsync-new)
|
||||||
|
@ -514,6 +520,45 @@ ledis>xscan "c" count 1
|
||||||
2) []
|
2) []
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### XREVSCAN key [MATCH match] [COUNT count]
|
||||||
|
|
||||||
|
Reverse iterate KV keys incrementally.
|
||||||
|
|
||||||
|
Key is the start for the current iteration.
|
||||||
|
Match is the regexp for checking matched key.
|
||||||
|
Count is the maximum retrieved elememts number, default is 10.
|
||||||
|
|
||||||
|
**Return value**
|
||||||
|
|
||||||
|
an array of two values, first value is the key for next iteration, second value is an array of elements.
|
||||||
|
|
||||||
|
**Examples**
|
||||||
|
|
||||||
|
```
|
||||||
|
ledis>set a 1
|
||||||
|
OK
|
||||||
|
ledis>set b 2
|
||||||
|
OK
|
||||||
|
ledis>set c 3
|
||||||
|
OK
|
||||||
|
127.0.0.1:6380>xrevscan ""
|
||||||
|
1) ""
|
||||||
|
2) ["c" "b" "a"]
|
||||||
|
ledis>xrevscan "" count 1
|
||||||
|
1) "c"
|
||||||
|
2) ["c"]
|
||||||
|
ledis>xrevscan "c" count 1
|
||||||
|
1) "b"
|
||||||
|
2) ["b"]
|
||||||
|
ledis>xrevscan "b" count 1
|
||||||
|
1) "a"
|
||||||
|
2) ["a"]
|
||||||
|
ledis>xrevscan "a" count 1
|
||||||
|
1) ""
|
||||||
|
2) []
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Hash
|
## Hash
|
||||||
|
|
||||||
### HDEL key field [field ...]
|
### HDEL key field [field ...]
|
||||||
|
@ -883,6 +928,12 @@ Iterate Hash keys incrementally.
|
||||||
|
|
||||||
See [XSCAN](#xscan-key-match-match-count-count) for more information.
|
See [XSCAN](#xscan-key-match-match-count-count) for more information.
|
||||||
|
|
||||||
|
### HXREVSCAN key [MATCH match] [COUNT count]
|
||||||
|
|
||||||
|
Reverse iterate Hash keys incrementally.
|
||||||
|
|
||||||
|
See [XREVSCAN](#xrevscan-key-match-match-count-count) for more information.
|
||||||
|
|
||||||
## List
|
## List
|
||||||
|
|
||||||
### BLPOP key [key ...] timeout
|
### BLPOP key [key ...] timeout
|
||||||
|
@ -1211,6 +1262,12 @@ Iterate list keys incrementally.
|
||||||
|
|
||||||
See [XSCAN](#xscan-key-match-match-count-count) for more information.
|
See [XSCAN](#xscan-key-match-match-count-count) for more information.
|
||||||
|
|
||||||
|
### LXREVSCAN key [MATCH match] [COUNT count]
|
||||||
|
|
||||||
|
Reverse iterate list keys incrementally.
|
||||||
|
|
||||||
|
See [XREVSCAN](#xrevscan-key-match-match-count-count) for more information.
|
||||||
|
|
||||||
|
|
||||||
## Set
|
## Set
|
||||||
|
|
||||||
|
@ -1640,6 +1697,12 @@ Iterate Set keys incrementally.
|
||||||
See [XSCAN](#xscan-key-match-match-count-count) for more information.
|
See [XSCAN](#xscan-key-match-match-count-count) for more information.
|
||||||
|
|
||||||
|
|
||||||
|
### SXREVSCAN key [MATCH match] [COUNT count]
|
||||||
|
|
||||||
|
Reverse iterate Set keys incrementally.
|
||||||
|
|
||||||
|
See [XREVSCAN](#xrevscan-key-match-match-count-count) for more information.
|
||||||
|
|
||||||
## ZSet
|
## ZSet
|
||||||
|
|
||||||
### ZADD key score member [score member ...]
|
### ZADD key score member [score member ...]
|
||||||
|
@ -2265,6 +2328,12 @@ Iterate ZSet keys incrementally.
|
||||||
|
|
||||||
See [XSCAN](#xscan-key-match-match-count-count) for more information.
|
See [XSCAN](#xscan-key-match-match-count-count) for more information.
|
||||||
|
|
||||||
|
### ZXREVSCAN key [MATCH match] [COUNT count]
|
||||||
|
|
||||||
|
Reverse iterate ZSet keys incrementally.
|
||||||
|
|
||||||
|
See [XREVSCAN](#xrevscan-key-match-match-count-count) for more information.
|
||||||
|
|
||||||
### ZRANGEBYLEX key min max [LIMIT offset count]
|
### ZRANGEBYLEX key min max [LIMIT offset count]
|
||||||
|
|
||||||
When all the elements in a sorted set are inserted with the same score, in order to force lexicographical ordering, this command returns all the elements in the sorted set at key with a value between min and max.
|
When all the elements in a sorted set are inserted with the same score, in order to force lexicographical ordering, this command returns all the elements in the sorted set at key with a value between min and max.
|
||||||
|
@ -2496,6 +2565,12 @@ Iterate Bitmap keys incrementally.
|
||||||
|
|
||||||
See [XSCAN](#xscan-key-match-match-count-count) for more information.
|
See [XSCAN](#xscan-key-match-match-count-count) for more information.
|
||||||
|
|
||||||
|
### BXREVSCAN key [MATCH match] [COUNT count]
|
||||||
|
|
||||||
|
Reverse iterate Bitmap keys incrementally.
|
||||||
|
|
||||||
|
See [XREVSCAN](#xrevscan-key-match-match-count-count) for more information.
|
||||||
|
|
||||||
|
|
||||||
## Replication
|
## Replication
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package ledis
|
package ledis
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/siddontang/ledisdb/store"
|
||||||
"regexp"
|
"regexp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -10,6 +10,15 @@ var errDataType = errors.New("error data type")
|
||||||
var errMetaKey = errors.New("error meta key")
|
var errMetaKey = errors.New("error meta key")
|
||||||
|
|
||||||
func (db *DB) scan(dataType byte, key []byte, count int, inclusive bool, match string) ([][]byte, error) {
|
func (db *DB) scan(dataType byte, key []byte, count int, inclusive bool, match string) ([][]byte, error) {
|
||||||
|
return db.scanGeneric(dataType, key, count, inclusive, match, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) revscan(dataType byte, key []byte, count int, inclusive bool, match string) ([][]byte, error) {
|
||||||
|
return db.scanGeneric(dataType, key, count, inclusive, match, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) scanGeneric(dataType byte, key []byte, count int,
|
||||||
|
inclusive bool, match string, reverse bool) ([][]byte, error) {
|
||||||
var minKey, maxKey []byte
|
var minKey, maxKey []byte
|
||||||
var err error
|
var err error
|
||||||
var r *regexp.Regexp
|
var r *regexp.Regexp
|
||||||
|
@ -20,40 +29,46 @@ func (db *DB) scan(dataType byte, key []byte, count int, inclusive bool, match s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(key) > 0 {
|
tp := store.RangeOpen
|
||||||
if err = checkKeySize(key); err != nil {
|
|
||||||
|
if !reverse {
|
||||||
|
if minKey, err = db.encodeScanMinKey(dataType, key); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if minKey, err = db.encodeScanKey(dataType, key); err != nil {
|
if maxKey, err = db.encodeScanMaxKey(dataType, nil); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if inclusive {
|
||||||
|
tp = store.RangeROpen
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if minKey, err = db.encodeScanMinKey(dataType); err != nil {
|
if minKey, err = db.encodeScanMinKey(dataType, nil); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if maxKey, err = db.encodeScanMaxKey(dataType, key); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if maxKey, err = db.encodeScanMaxKey(dataType); err != nil {
|
if inclusive {
|
||||||
return nil, err
|
tp = store.RangeLOpen
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if count <= 0 {
|
if count <= 0 {
|
||||||
count = defaultScanCount
|
count = defaultScanCount
|
||||||
}
|
}
|
||||||
|
|
||||||
v := make([][]byte, 0, count)
|
var it *store.RangeLimitIterator
|
||||||
|
if !reverse {
|
||||||
it := db.bucket.NewIterator()
|
it = db.bucket.RangeIterator(minKey, maxKey, tp)
|
||||||
it.Seek(minKey)
|
} else {
|
||||||
|
it = db.bucket.RevRangeIterator(minKey, maxKey, tp)
|
||||||
if !inclusive {
|
|
||||||
if it.Valid() && bytes.Equal(it.RawKey(), minKey) {
|
|
||||||
it.Next()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; it.Valid() && i < count && bytes.Compare(it.RawKey(), maxKey) < 0; it.Next() {
|
v := make([][]byte, 0, count)
|
||||||
|
|
||||||
|
for i := 0; it.Valid() && i < count; it.Next() {
|
||||||
if k, err := db.decodeScanKey(dataType, it.Key()); err != nil {
|
if k, err := db.decodeScanKey(dataType, it.Key()); err != nil {
|
||||||
continue
|
continue
|
||||||
} else if r != nil && !r.Match(k) {
|
} else if r != nil && !r.Match(k) {
|
||||||
|
@ -67,11 +82,26 @@ func (db *DB) scan(dataType byte, key []byte, count int, inclusive bool, match s
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) encodeScanMinKey(dataType byte) ([]byte, error) {
|
func (db *DB) encodeScanMinKey(dataType byte, key []byte) ([]byte, error) {
|
||||||
return db.encodeScanKey(dataType, nil)
|
if len(key) == 0 {
|
||||||
|
return db.encodeScanKey(dataType, nil)
|
||||||
|
} else {
|
||||||
|
if err := checkKeySize(key); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return db.encodeScanKey(dataType, key)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) encodeScanMaxKey(dataType byte) ([]byte, error) {
|
func (db *DB) encodeScanMaxKey(dataType byte, key []byte) ([]byte, error) {
|
||||||
|
if len(key) > 0 {
|
||||||
|
if err := checkKeySize(key); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return db.encodeScanKey(dataType, key)
|
||||||
|
}
|
||||||
|
|
||||||
k, err := db.encodeScanKey(dataType, nil)
|
k, err := db.encodeScanKey(dataType, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -4,6 +4,18 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func checkTestScan(t *testing.T, v [][]byte, args ...string) {
|
||||||
|
if len(v) != len(args) {
|
||||||
|
t.Fatal(len(v), len(args))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range v {
|
||||||
|
if string(v[i]) != args[i] {
|
||||||
|
t.Fatalf("%q %q", v, args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestDBScan(t *testing.T) {
|
func TestDBScan(t *testing.T) {
|
||||||
db := getTestDB()
|
db := getTestDB()
|
||||||
|
|
||||||
|
@ -15,44 +27,86 @@ func TestDBScan(t *testing.T) {
|
||||||
t.Fatal(len(v))
|
t.Fatal(len(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v, err := db.RevScan(nil, 10, true, ""); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if len(v) != 0 {
|
||||||
|
t.Fatal(len(v))
|
||||||
|
}
|
||||||
|
|
||||||
db.Set([]byte("a"), []byte{})
|
db.Set([]byte("a"), []byte{})
|
||||||
db.Set([]byte("b"), []byte{})
|
db.Set([]byte("b"), []byte{})
|
||||||
db.Set([]byte("c"), []byte{})
|
db.Set([]byte("c"), []byte{})
|
||||||
|
|
||||||
if v, err := db.Scan(nil, 1, true, ""); err != nil {
|
if v, err := db.Scan(nil, 1, true, ""); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if len(v) != 1 {
|
} else {
|
||||||
t.Fatal(len(v))
|
checkTestScan(t, v, "a")
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := db.Scan([]byte("a"), 2, false, ""); err != nil {
|
if v, err := db.Scan([]byte("a"), 2, false, ""); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if len(v) != 2 {
|
} else {
|
||||||
t.Fatal(len(v))
|
checkTestScan(t, v, "b", "c")
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := db.Scan(nil, 3, true, ""); err != nil {
|
if v, err := db.Scan(nil, 3, true, ""); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if len(v) != 3 {
|
} else {
|
||||||
t.Fatal(len(v))
|
checkTestScan(t, v, "a", "b", "c")
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := db.Scan(nil, 3, true, "b"); err != nil {
|
if v, err := db.Scan(nil, 3, true, "b"); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if len(v) != 1 {
|
} else {
|
||||||
t.Fatal(len(v))
|
checkTestScan(t, v, "b")
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := db.Scan(nil, 3, true, "."); err != nil {
|
if v, err := db.Scan(nil, 3, true, "."); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if len(v) != 3 {
|
} else {
|
||||||
t.Fatal(len(v))
|
checkTestScan(t, v, "a", "b", "c")
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := db.Scan(nil, 3, true, "a+"); err != nil {
|
if v, err := db.Scan(nil, 3, true, "a+"); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if len(v) != 1 {
|
} else {
|
||||||
t.Fatal(len(v))
|
checkTestScan(t, v, "a")
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, err := db.RevScan(nil, 1, true, ""); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else {
|
||||||
|
checkTestScan(t, v, "c")
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, err := db.RevScan([]byte("c"), 2, false, ""); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else {
|
||||||
|
checkTestScan(t, v, "b", "a")
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, err := db.RevScan(nil, 3, true, ""); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else {
|
||||||
|
checkTestScan(t, v, "c", "b", "a")
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, err := db.RevScan(nil, 3, true, "b"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else {
|
||||||
|
checkTestScan(t, v, "b")
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, err := db.RevScan(nil, 3, true, "."); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else {
|
||||||
|
checkTestScan(t, v, "c", "b", "a")
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, err := db.RevScan(nil, 3, true, "c+"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else {
|
||||||
|
checkTestScan(t, v, "c")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -913,6 +913,10 @@ func (db *DB) BScan(key []byte, count int, inclusive bool, match string) ([][]by
|
||||||
return db.scan(BitMetaType, key, count, inclusive, match)
|
return db.scan(BitMetaType, key, count, inclusive, match)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *DB) BRevScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) {
|
||||||
|
return db.revscan(BitMetaType, key, count, inclusive, match)
|
||||||
|
}
|
||||||
|
|
||||||
func (db *DB) bFlush() (drop int64, err error) {
|
func (db *DB) bFlush() (drop int64, err error) {
|
||||||
t := db.binBatch
|
t := db.binBatch
|
||||||
t.Lock()
|
t.Lock()
|
||||||
|
|
|
@ -464,6 +464,10 @@ func (db *DB) HScan(key []byte, count int, inclusive bool, match string) ([][]by
|
||||||
return db.scan(HSizeType, key, count, inclusive, match)
|
return db.scan(HSizeType, key, count, inclusive, match)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *DB) HRevScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) {
|
||||||
|
return db.revscan(HSizeType, key, count, inclusive, match)
|
||||||
|
}
|
||||||
|
|
||||||
func (db *DB) HExpire(key []byte, duration int64) (int64, error) {
|
func (db *DB) HExpire(key []byte, duration int64) (int64, error) {
|
||||||
if duration <= 0 {
|
if duration <= 0 {
|
||||||
return 0, errExpireValue
|
return 0, errExpireValue
|
||||||
|
|
|
@ -312,6 +312,11 @@ func (db *DB) Scan(key []byte, count int, inclusive bool, match string) ([][]byt
|
||||||
return db.scan(KVType, key, count, inclusive, match)
|
return db.scan(KVType, key, count, inclusive, match)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//if inclusive is true, revscan range (-inf, key] else (inf, key)
|
||||||
|
func (db *DB) RevScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) {
|
||||||
|
return db.revscan(KVType, key, count, inclusive, match)
|
||||||
|
}
|
||||||
|
|
||||||
func (db *DB) Expire(key []byte, duration int64) (int64, error) {
|
func (db *DB) Expire(key []byte, duration int64) (int64, error) {
|
||||||
if duration <= 0 {
|
if duration <= 0 {
|
||||||
return 0, errExpireValue
|
return 0, errExpireValue
|
||||||
|
|
|
@ -484,6 +484,10 @@ func (db *DB) LScan(key []byte, count int, inclusive bool, match string) ([][]by
|
||||||
return db.scan(LMetaType, key, count, inclusive, match)
|
return db.scan(LMetaType, key, count, inclusive, match)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *DB) LRevScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) {
|
||||||
|
return db.revscan(LMetaType, key, count, inclusive, match)
|
||||||
|
}
|
||||||
|
|
||||||
func (db *DB) lEncodeMinKey() []byte {
|
func (db *DB) lEncodeMinKey() []byte {
|
||||||
return db.lEncodeMetaKey(nil)
|
return db.lEncodeMetaKey(nil)
|
||||||
}
|
}
|
||||||
|
|
|
@ -599,3 +599,7 @@ func (db *DB) SPersist(key []byte) (int64, error) {
|
||||||
func (db *DB) SScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) {
|
func (db *DB) SScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) {
|
||||||
return db.scan(SSizeType, key, count, inclusive, match)
|
return db.scan(SSizeType, key, count, inclusive, match)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *DB) SRevScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) {
|
||||||
|
return db.revscan(SSizeType, key, count, inclusive, match)
|
||||||
|
}
|
||||||
|
|
|
@ -940,6 +940,10 @@ func (db *DB) ZScan(key []byte, count int, inclusive bool, match string) ([][]by
|
||||||
return db.scan(ZSizeType, key, count, inclusive, match)
|
return db.scan(ZSizeType, key, count, inclusive, match)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *DB) ZRevScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) {
|
||||||
|
return db.revscan(ZSizeType, key, count, inclusive, match)
|
||||||
|
}
|
||||||
|
|
||||||
func (db *DB) ZRangeByLex(key []byte, min []byte, max []byte, rangeType uint8, offset int, count int) ([][]byte, error) {
|
func (db *DB) ZRangeByLex(key []byte, min []byte, max []byte, rangeType uint8, offset int, count int) ([][]byte, error) {
|
||||||
if min == nil {
|
if min == nil {
|
||||||
min = db.zEncodeStartSetKey(key)
|
min = db.zEncodeStartSetKey(key)
|
||||||
|
|
|
@ -275,24 +275,11 @@ func bpersistCommand(c *client) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func bxscanCommand(c *client) error {
|
func bxscanCommand(c *client) error {
|
||||||
key, match, count, err := parseScanArgs(c)
|
return xscanGeneric(c, c.db.BScan)
|
||||||
if err != nil {
|
}
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if ay, err := c.db.BScan(key, count, false, match); err != nil {
|
func bxrevscanCommand(c *client) error {
|
||||||
return err
|
return xscanGeneric(c, c.db.BRevScan)
|
||||||
} else {
|
|
||||||
data := make([]interface{}, 2)
|
|
||||||
if len(ay) < count {
|
|
||||||
data[0] = []byte("")
|
|
||||||
} else {
|
|
||||||
data[0] = ay[len(ay)-1]
|
|
||||||
}
|
|
||||||
data[1] = ay
|
|
||||||
c.resp.writeArray(data)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -308,4 +295,5 @@ func init() {
|
||||||
register("bttl", bttlCommand)
|
register("bttl", bttlCommand)
|
||||||
register("bpersist", bpersistCommand)
|
register("bpersist", bpersistCommand)
|
||||||
register("bxscan", bxscanCommand)
|
register("bxscan", bxscanCommand)
|
||||||
|
register("bxrevscan", bxrevscanCommand)
|
||||||
}
|
}
|
||||||
|
|
|
@ -293,24 +293,11 @@ func hpersistCommand(c *client) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func hxscanCommand(c *client) error {
|
func hxscanCommand(c *client) error {
|
||||||
key, match, count, err := parseScanArgs(c)
|
return xscanGeneric(c, c.db.HScan)
|
||||||
if err != nil {
|
}
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if ay, err := c.db.HScan(key, count, false, match); err != nil {
|
func hxrevscanCommand(c *client) error {
|
||||||
return err
|
return xscanGeneric(c, c.db.HRevScan)
|
||||||
} else {
|
|
||||||
data := make([]interface{}, 2)
|
|
||||||
if len(ay) < count {
|
|
||||||
data[0] = []byte("")
|
|
||||||
} else {
|
|
||||||
data[0] = ay[len(ay)-1]
|
|
||||||
}
|
|
||||||
data[1] = ay
|
|
||||||
c.resp.writeArray(data)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -335,4 +322,5 @@ func init() {
|
||||||
register("httl", httlCommand)
|
register("httl", httlCommand)
|
||||||
register("hpersist", hpersistCommand)
|
register("hpersist", hpersistCommand)
|
||||||
register("hxscan", hxscanCommand)
|
register("hxscan", hxscanCommand)
|
||||||
|
register("hxrevscan", hxrevscanCommand)
|
||||||
}
|
}
|
||||||
|
|
|
@ -322,13 +322,14 @@ func parseScanArgs(c *client) (key []byte, match string, count int, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func xscanCommand(c *client) error {
|
func xscanGeneric(c *client,
|
||||||
|
f func(key []byte, count int, inclusive bool, match string) ([][]byte, error)) error {
|
||||||
key, match, count, err := parseScanArgs(c)
|
key, match, count, err := parseScanArgs(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if ay, err := c.db.Scan(key, count, false, match); err != nil {
|
if ay, err := f(key, count, false, match); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
data := make([]interface{}, 2)
|
data := make([]interface{}, 2)
|
||||||
|
@ -343,6 +344,14 @@ func xscanCommand(c *client) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func xscanCommand(c *client) error {
|
||||||
|
return xscanGeneric(c, c.db.Scan)
|
||||||
|
}
|
||||||
|
|
||||||
|
func xrevscanCommand(c *client) error {
|
||||||
|
return xscanGeneric(c, c.db.RevScan)
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
register("decr", decrCommand)
|
register("decr", decrCommand)
|
||||||
register("decrby", decrbyCommand)
|
register("decrby", decrbyCommand)
|
||||||
|
@ -361,4 +370,5 @@ func init() {
|
||||||
register("ttl", ttlCommand)
|
register("ttl", ttlCommand)
|
||||||
register("persist", persistCommand)
|
register("persist", persistCommand)
|
||||||
register("xscan", xscanCommand)
|
register("xscan", xscanCommand)
|
||||||
|
register("xrevscan", xrevscanCommand)
|
||||||
}
|
}
|
||||||
|
|
|
@ -232,24 +232,11 @@ func lpersistCommand(c *client) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func lxscanCommand(c *client) error {
|
func lxscanCommand(c *client) error {
|
||||||
key, match, count, err := parseScanArgs(c)
|
return xscanGeneric(c, c.db.LScan)
|
||||||
if err != nil {
|
}
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if ay, err := c.db.LScan(key, count, false, match); err != nil {
|
func lxrevscanCommand(c *client) error {
|
||||||
return err
|
return xscanGeneric(c, c.db.LRevScan)
|
||||||
} else {
|
|
||||||
data := make([]interface{}, 2)
|
|
||||||
if len(ay) < count {
|
|
||||||
data[0] = []byte("")
|
|
||||||
} else {
|
|
||||||
data[0] = ay[len(ay)-1]
|
|
||||||
}
|
|
||||||
data[1] = ay
|
|
||||||
c.resp.writeArray(data)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func blpopCommand(c *client) error {
|
func blpopCommand(c *client) error {
|
||||||
|
@ -319,4 +306,5 @@ func init() {
|
||||||
register("lttl", lttlCommand)
|
register("lttl", lttlCommand)
|
||||||
register("lpersist", lpersistCommand)
|
register("lpersist", lpersistCommand)
|
||||||
register("lxscan", lxscanCommand)
|
register("lxscan", lxscanCommand)
|
||||||
|
register("lxrevscan", lxrevscanCommand)
|
||||||
}
|
}
|
||||||
|
|
|
@ -263,24 +263,11 @@ func spersistCommand(c *client) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func sxscanCommand(c *client) error {
|
func sxscanCommand(c *client) error {
|
||||||
key, match, count, err := parseScanArgs(c)
|
return xscanGeneric(c, c.db.SScan)
|
||||||
if err != nil {
|
}
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if ay, err := c.db.SScan(key, count, false, match); err != nil {
|
func sxrevscanCommand(c *client) error {
|
||||||
return err
|
return xscanGeneric(c, c.db.SRevScan)
|
||||||
} else {
|
|
||||||
data := make([]interface{}, 2)
|
|
||||||
if len(ay) < count {
|
|
||||||
data[0] = []byte("")
|
|
||||||
} else {
|
|
||||||
data[0] = ay[len(ay)-1]
|
|
||||||
}
|
|
||||||
data[1] = ay
|
|
||||||
c.resp.writeArray(data)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -302,4 +289,5 @@ func init() {
|
||||||
register("sttl", sttlCommand)
|
register("sttl", sttlCommand)
|
||||||
register("spersist", spersistCommand)
|
register("spersist", spersistCommand)
|
||||||
register("sxscan", sxscanCommand)
|
register("sxscan", sxscanCommand)
|
||||||
|
register("sxrevscan", sxrevscanCommand)
|
||||||
}
|
}
|
||||||
|
|
|
@ -642,24 +642,11 @@ func zinterstoreCommand(c *client) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func zxscanCommand(c *client) error {
|
func zxscanCommand(c *client) error {
|
||||||
key, match, count, err := parseScanArgs(c)
|
return xscanGeneric(c, c.db.ZScan)
|
||||||
if err != nil {
|
}
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if ay, err := c.db.ZScan(key, count, false, match); err != nil {
|
func zxrevscanCommand(c *client) error {
|
||||||
return err
|
return xscanGeneric(c, c.db.ZRevScan)
|
||||||
} else {
|
|
||||||
data := make([]interface{}, 2)
|
|
||||||
if len(ay) < count {
|
|
||||||
data[0] = []byte("")
|
|
||||||
} else {
|
|
||||||
data[0] = ay[len(ay)-1]
|
|
||||||
}
|
|
||||||
data[1] = ay
|
|
||||||
c.resp.writeArray(data)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func zparseMemberRange(minBuf []byte, maxBuf []byte) (min []byte, max []byte, rangeType uint8, err error) {
|
func zparseMemberRange(minBuf []byte, maxBuf []byte) (min []byte, max []byte, rangeType uint8, err error) {
|
||||||
|
@ -816,4 +803,5 @@ func init() {
|
||||||
register("zttl", zttlCommand)
|
register("zttl", zttlCommand)
|
||||||
register("zpersist", zpersistCommand)
|
register("zpersist", zpersistCommand)
|
||||||
register("zxscan", zxscanCommand)
|
register("zxscan", zxscanCommand)
|
||||||
|
register("zxrevscan", zxrevscanCommand)
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,29 @@ func checkScan(t *testing.T, c *ledis.Client, cmd string) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func checkRevScan(t *testing.T, c *ledis.Client, cmd string) {
|
||||||
|
if ay, err := ledis.Values(c.Do(cmd, "", "count", 5)); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if len(ay) != 2 {
|
||||||
|
t.Fatal(len(ay))
|
||||||
|
} else if n := ay[0].([]byte); string(n) != "5" {
|
||||||
|
t.Fatal(string(n))
|
||||||
|
} else {
|
||||||
|
checkScanValues(t, ay[1], 9, 8, 7, 6, 5)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ay, err := ledis.Values(c.Do(cmd, "5", "count", 6)); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if len(ay) != 2 {
|
||||||
|
t.Fatal(len(ay))
|
||||||
|
} else if n := ay[0].([]byte); string(n) != "" {
|
||||||
|
t.Fatal(string(n))
|
||||||
|
} else {
|
||||||
|
checkScanValues(t, ay[1], 4, 3, 2, 1, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func testKVScan(t *testing.T, c *ledis.Client) {
|
func testKVScan(t *testing.T, c *ledis.Client) {
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
if _, err := c.Do("set", fmt.Sprintf("%d", i), []byte("value")); err != nil {
|
if _, err := c.Do("set", fmt.Sprintf("%d", i), []byte("value")); err != nil {
|
||||||
|
@ -85,6 +108,7 @@ func testKVScan(t *testing.T, c *ledis.Client) {
|
||||||
}
|
}
|
||||||
|
|
||||||
checkScan(t, c, "xscan")
|
checkScan(t, c, "xscan")
|
||||||
|
checkRevScan(t, c, "xrevscan")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testHashScan(t *testing.T, c *ledis.Client) {
|
func testHashScan(t *testing.T, c *ledis.Client) {
|
||||||
|
@ -95,6 +119,7 @@ func testHashScan(t *testing.T, c *ledis.Client) {
|
||||||
}
|
}
|
||||||
|
|
||||||
checkScan(t, c, "hxscan")
|
checkScan(t, c, "hxscan")
|
||||||
|
checkRevScan(t, c, "hxrevscan")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testListScan(t *testing.T, c *ledis.Client) {
|
func testListScan(t *testing.T, c *ledis.Client) {
|
||||||
|
@ -105,6 +130,7 @@ func testListScan(t *testing.T, c *ledis.Client) {
|
||||||
}
|
}
|
||||||
|
|
||||||
checkScan(t, c, "lxscan")
|
checkScan(t, c, "lxscan")
|
||||||
|
checkRevScan(t, c, "lxrevscan")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testZSetScan(t *testing.T, c *ledis.Client) {
|
func testZSetScan(t *testing.T, c *ledis.Client) {
|
||||||
|
@ -115,6 +141,7 @@ func testZSetScan(t *testing.T, c *ledis.Client) {
|
||||||
}
|
}
|
||||||
|
|
||||||
checkScan(t, c, "zxscan")
|
checkScan(t, c, "zxscan")
|
||||||
|
checkRevScan(t, c, "zxrevscan")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSetScan(t *testing.T, c *ledis.Client) {
|
func testSetScan(t *testing.T, c *ledis.Client) {
|
||||||
|
@ -125,6 +152,7 @@ func testSetScan(t *testing.T, c *ledis.Client) {
|
||||||
}
|
}
|
||||||
|
|
||||||
checkScan(t, c, "sxscan")
|
checkScan(t, c, "sxscan")
|
||||||
|
checkRevScan(t, c, "sxrevscan")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testBitScan(t *testing.T, c *ledis.Client) {
|
func testBitScan(t *testing.T, c *ledis.Client) {
|
||||||
|
@ -135,4 +163,5 @@ func testBitScan(t *testing.T, c *ledis.Client) {
|
||||||
}
|
}
|
||||||
|
|
||||||
checkScan(t, c, "bxscan")
|
checkScan(t, c, "bxscan")
|
||||||
|
checkRevScan(t, c, "bxrevscan")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue