forked from mirror/ledisdb
Merge branch 'scan-feature' into develop
This commit is contained in:
commit
b1ccbb9c75
|
@ -1,4 +1,4 @@
|
|||
//This file was generated by .tools/generate_commands.py on Fri Feb 06 2015 09:15:18 +0800
|
||||
//This file was generated by .tools/generate_commands.py on Mon Mar 02 2015 14:43:25 +0800
|
||||
package main
|
||||
|
||||
var helpCommands = [][]string{
|
||||
|
@ -20,8 +20,6 @@ var helpCommands = [][]string{
|
|||
{"BRPOP", "key [key ...] timeout", "List"},
|
||||
{"BSETBIT", "key offset value", "Bitmap"},
|
||||
{"BTTL", "key", "Bitmap"},
|
||||
{"BXREVSCAN", "key [MATCH match] [COUNT count]", "Bitmap"},
|
||||
{"BXSCAN", "key [MATCH match] [COUNT count]", "Bitmap"},
|
||||
{"COMMIT", "-", "Transaction"},
|
||||
{"CONFIG REWRITE", "-", "Server"},
|
||||
{"DECR", "key", "KV"},
|
||||
|
@ -59,8 +57,6 @@ var helpCommands = [][]string{
|
|||
{"HSET", "key field value", "Hash"},
|
||||
{"HTTL", "key", "Hash"},
|
||||
{"HVALS", "key", "Hash"},
|
||||
{"HXREVSCAN", "key [MATCH match] [COUNT count]", "Hash"},
|
||||
{"HXSCAN", "key [MATCH match] [COUNT count]", "Hash"},
|
||||
{"INCR", "key", "KV"},
|
||||
{"INCRBY", "key increment", "KV"},
|
||||
{"INFO", "[section]", "Server"},
|
||||
|
@ -76,8 +72,6 @@ var helpCommands = [][]string{
|
|||
{"LPUSH", "key value [value ...]", "List"},
|
||||
{"LRANGE", "key start stop", "List"},
|
||||
{"LTTL", "key", "List"},
|
||||
{"LXREVSCAN", "key [MATCH match] [COUNT count]", "List"},
|
||||
{"LXSCAN", "key [MATCH match] [COUNT count]", "List"},
|
||||
{"MGET", "key [key ...]", "KV"},
|
||||
{"MSET", "key value [key value ...]", "KV"},
|
||||
{"PERSIST", "key", "KV"},
|
||||
|
@ -116,23 +110,13 @@ var helpCommands = [][]string{
|
|||
{"STTL", "key", "Set"},
|
||||
{"SUNION", "key [key ...]", "Set"},
|
||||
{"SUNIONSTORE", "destination key [key ...]", "Set"},
|
||||
{"SXREVSCAN", "key [MATCH match] [COUNT count]", "Set"},
|
||||
{"SXSCAN", "key [MATCH match] [COUNT count]", "Set"},
|
||||
{"SYNC", "logid", "Replication"},
|
||||
{"TIME", "-", "Server"},
|
||||
{"TTL", "key", "KV"},
|
||||
{"XBREVSCAN", "key [MATCH match] [COUNT count]", "Bitmap"},
|
||||
{"XBSCAN", "key [MATCH match] [COUNT count]", "Bitmap"},
|
||||
{"XHREVSCAN", "key [MATCH match] [COUNT count]", "Hash"},
|
||||
{"XHSCAN", "key [MATCH match] [COUNT count]", "Hash"},
|
||||
{"XLREVSCAN", "key [MATCH match] [COUNT count]", "List"},
|
||||
{"XLSCAN", "key [MATCH match] [COUNT count]", "List"},
|
||||
{"XREVSCAN", "key [MATCH match] [COUNT count]", "KV"},
|
||||
{"XSCAN", "key [MATCH match] [COUNT count]", "KV"},
|
||||
{"XSREVSCAN", "key [MATCH match] [COUNT count]", "Set"},
|
||||
{"XSSCAN", "key [MATCH match] [COUNT count]", "Set"},
|
||||
{"XZREVSCAN", "key [MATCH match] [COUNT count]", "ZSet"},
|
||||
{"XZSCAN", "key [MATCH match] [COUNT count]", "ZSet"},
|
||||
{"XHSCAN", "key cursor [MATCH match] [COUNT count]", "Hash"},
|
||||
{"XSCAN", "type cursor [MATCH match] [COUNT count]", "Server"},
|
||||
{"XSSCAN", "key cursor [MATCH match] [COUNT count]", "Set"},
|
||||
{"XZSCAN", "key cursor [MATCH match] [COUNT count]", "ZSet"},
|
||||
{"ZADD", "key score member [score member ...]", "ZSet"},
|
||||
{"ZCARD", "key", "ZSet"},
|
||||
{"ZCLEAR", "key", "ZSet"},
|
||||
|
@ -159,6 +143,4 @@ var helpCommands = [][]string{
|
|||
{"ZSCORE", "key member", "ZSet"},
|
||||
{"ZTTL", "key", "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"},
|
||||
}
|
||||
|
|
|
@ -560,79 +560,7 @@
|
|||
"group": "Transaction",
|
||||
"readonly": false
|
||||
},
|
||||
|
||||
"XSCAN": {
|
||||
"arguments": "key [MATCH match] [COUNT count]",
|
||||
"group": "KV",
|
||||
"readonly": true
|
||||
},
|
||||
|
||||
"HXSCAN": {
|
||||
"arguments": "key [MATCH match] [COUNT count]",
|
||||
"group": "Hash",
|
||||
"readonly": true
|
||||
},
|
||||
|
||||
"LXSCAN": {
|
||||
"arguments": "key [MATCH match] [COUNT count]",
|
||||
"group": "List",
|
||||
"readonly": true
|
||||
},
|
||||
|
||||
"SXSCAN": {
|
||||
"arguments": "key [MATCH match] [COUNT count]",
|
||||
"group": "Set",
|
||||
"readonly": true
|
||||
},
|
||||
|
||||
"ZXSCAN": {
|
||||
"arguments": "key [MATCH match] [COUNT count]",
|
||||
"group": "ZSet",
|
||||
"readonly": true
|
||||
},
|
||||
|
||||
"BXSCAN": {
|
||||
"arguments": "key [MATCH match] [COUNT count]",
|
||||
"group": "Bitmap",
|
||||
"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": {
|
||||
"arguments": "-",
|
||||
"group": "Server",
|
||||
|
@ -724,66 +652,30 @@
|
|||
"readonly": true
|
||||
},
|
||||
|
||||
"XBSCAN": {
|
||||
"arguments": "key [MATCH match] [COUNT count]",
|
||||
"group": "Bitmap",
|
||||
"readonly": true
|
||||
},
|
||||
|
||||
"XLSCAN": {
|
||||
"arguments": "key [MATCH match] [COUNT count]",
|
||||
"group": "List",
|
||||
"XSCAN": {
|
||||
"arguments": "type cursor [MATCH match] [COUNT count]",
|
||||
"group": "Server",
|
||||
"readonly": true
|
||||
},
|
||||
|
||||
"XHSCAN": {
|
||||
"arguments": "key [MATCH match] [COUNT count]",
|
||||
"arguments": "key cursor [MATCH match] [COUNT count]",
|
||||
"group": "Hash",
|
||||
"readonly": true
|
||||
},
|
||||
|
||||
"XSSCAN": {
|
||||
"arguments": "key [MATCH match] [COUNT count]",
|
||||
"arguments": "key cursor [MATCH match] [COUNT count]",
|
||||
"group": "Set",
|
||||
"readonly": true
|
||||
},
|
||||
|
||||
"XZSCAN": {
|
||||
"arguments": "key [MATCH match] [COUNT count]",
|
||||
"arguments": "key cursor [MATCH match] [COUNT count]",
|
||||
"group": "ZSet",
|
||||
"readonly": true
|
||||
},
|
||||
|
||||
"XHREVSCAN": {
|
||||
"arguments": "key [MATCH match] [COUNT count]",
|
||||
"group": "Hash",
|
||||
"readonly": true
|
||||
},
|
||||
|
||||
"XLREVSCAN": {
|
||||
"arguments": "key [MATCH match] [COUNT count]",
|
||||
"group": "List",
|
||||
"readonly": true
|
||||
},
|
||||
|
||||
"XSREVSCAN": {
|
||||
"arguments": "key [MATCH match] [COUNT count]",
|
||||
"group": "Set",
|
||||
"readonly": true
|
||||
},
|
||||
|
||||
"XZREVSCAN": {
|
||||
"arguments": "key [MATCH match] [COUNT count]",
|
||||
"group": "ZSet",
|
||||
"readonly": true
|
||||
},
|
||||
|
||||
"XBREVSCAN": {
|
||||
"arguments": "key [MATCH match] [COUNT count]",
|
||||
"group": "Bitmap",
|
||||
"readonly": true
|
||||
},
|
||||
|
||||
"RESTORE": {
|
||||
"arguments" : "key ttl value",
|
||||
"group" : "Server",
|
||||
|
|
259
doc/commands.md
259
doc/commands.md
|
@ -27,8 +27,6 @@ Table of Contents
|
|||
- [EXPIREAT key timestamp](#expireat-key-timestamp)
|
||||
- [TTL key](#ttl-key)
|
||||
- [PERSIST key](#persist-key)
|
||||
- [XSCAN key [MATCH match] [COUNT count]](#xscan-key-match-match-count-count)
|
||||
- [XREVSCAN key [MATCH match] [COUNT count]](#xrevscan-key-match-match-count-count)
|
||||
- [DUMP key](#dump-key)
|
||||
- [Hash](#hash)
|
||||
- [HDEL key field [field ...]](#hdel-key-field-field-)
|
||||
|
@ -48,10 +46,6 @@ Table of Contents
|
|||
- [HEXPIREAT key timestamp](#hexpireat-key-timestamp)
|
||||
- [HTTL key](#httl-key)
|
||||
- [HPERSIST key](#hpersist-key)
|
||||
- [HXSCAN key [MATCH match] [COUNT count]](#hxscan-key-match-match-count-count)
|
||||
- [HXREVSCAN key [MATCH match] [COUNT count]](#hxrevscan-key-match-match-count-count)
|
||||
- [XHSCAN key [MATCH match] [COUNT count]](#xhscan-key-match-match-count-count)
|
||||
- [XHREVSCAN key [MATCH match] [COUNT count]](#xhrevscan-key-match-match-count-count)
|
||||
- [HDUMP key](#hdump-key)
|
||||
- [List](#list)
|
||||
- [BLPOP key [key ...] timeout](#blpop-key-key--timeout)
|
||||
|
@ -69,10 +63,6 @@ Table of Contents
|
|||
- [LEXPIREAT key timestamp](#lexpireat-key-timestamp)
|
||||
- [LTTL key](#lttl-key)
|
||||
- [LPERSIST key](#lpersist-key)
|
||||
- [LXSCAN key [MATCH match] [COUNT count]](#lxscan-key-match-match-count-count)
|
||||
- [LXREVSCAN key [MATCH match] [COUNT count]](#lxrevscan-key-match-match-count-count)
|
||||
- [XLSCAN key [MATCH match] [COUNT count]](#xlscan-key-match-match-count-count)
|
||||
- [XLREVSCAN key [MATCH match] [COUNT count]](#xlrevscan-key-match-match-count-count)
|
||||
- [LDUMP key](#ldump-key)
|
||||
- [Set](#set)
|
||||
- [SADD key member [member ...]](#sadd-key-member-member-)
|
||||
|
@ -92,10 +82,6 @@ Table of Contents
|
|||
- [SEXPIREAT key timestamp](#sexpireat-key-timestamp)
|
||||
- [STTL key](#sttl-key)
|
||||
- [SPERSIST key](#spersist-key)
|
||||
- [SXSCAN key [MATCH match] [COUNT count]](#sxscan-key-match-match-count-count)
|
||||
- [SXREVSCAN key [MATCH match] [COUNT count]](#sxrevscan-key-match-match-count-count)
|
||||
- [XSSCAN key [MATCH match] [COUNT count]](#xsscan-key-match-match-count-count)
|
||||
- [XSREVSCAN key [MATCH match] [COUNT count]](#xsrevscan-key-match-match-count-count)
|
||||
- [SDUMP key](#sdump-key)
|
||||
- [ZSet](#zset)
|
||||
- [ZADD key score member [score member ...]](#zadd-key-score-member-score-member-)
|
||||
|
@ -122,10 +108,6 @@ Table of Contents
|
|||
](#zunionstore-destination-numkeys-key-key--weights-weight-weight--aggregate-summinmax)
|
||||
- [ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
|
||||
](#zinterstore-destination-numkeys-key-key--weights-weight-weight--aggregate-summinmax)
|
||||
- [ZXSCAN key [MATCH match] [COUNT count]](#zxscan-key-match-match-count-count)
|
||||
- [ZXREVSCAN key [MATCH match] [COUNT count]](#zxrevscan-key-match-match-count-count)
|
||||
- [XZSCAN key [MATCH match] [COUNT count]](#xzscan-key-match-match-count-count)
|
||||
- [XZREVSCAN key [MATCH match] [COUNT count]](#xzrevscan-key-match-match-count-count)
|
||||
- [ZRANGEBYLEX key min max [LIMIT offset count]](#zrangebylex-key-min-max-limit-offset-count)
|
||||
- [ZREMRANGEBYLEX key min max](#zremrangebylex-key-min-max)
|
||||
- [ZLEXCOUNT key min max](#zlexcount-key-min-max)
|
||||
|
@ -141,10 +123,11 @@ Table of Contents
|
|||
- [BEXPIREAT key timestamp](#bexpireat-key-timestamp)
|
||||
- [BTTL key](#bttl-key)
|
||||
- [BPERSIST key](#bpersist-key)
|
||||
- [BXSCAN key [MATCH match] [COUNT count]](#bxscan-key-match-match-count-count)
|
||||
- [BXREVSCAN key [MATCH match] [COUNT count]](#bxrevscan-key-match-match-count-count)
|
||||
- [XBSCAN key [MATCH match] [COUNT count]](#xbscan-key-match-match-count-count)
|
||||
- [XBREVSCAN key [MATCH match] [COUNT count]](#xbrevscan-key-match-match-count-count)
|
||||
- [Scan](#scan)
|
||||
- [XSCAN type cursor [MATCH match] [COUNT count]](#xscan-type-cursor-match-match-count-count)
|
||||
- [XHSCAN key cursor [MATCH match] [COUNT count]](#xhscan-key-cursor-match-match-count-count)
|
||||
- [XSSCAN key cursor [MATCH match] [COUNT count]](#xsscan-key-cursor-match-match-count-count)
|
||||
- [XZSCAN key cursor [MATCH match] [COUNT count]](#xzscan-key-cursor-match-match-count-count)
|
||||
- [Replication](#replication)
|
||||
- [SLAVEOF host port [RESTART] [READONLY]](#slaveof-host-port-restart-readonly)
|
||||
- [FULLSYNC [NEW]](#fullsync-new)
|
||||
|
@ -524,82 +507,6 @@ ledis> TTL mykey
|
|||
(integer) -1
|
||||
```
|
||||
|
||||
### XSCAN key [MATCH match] [COUNT count]
|
||||
|
||||
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>xscan ""
|
||||
1) ""
|
||||
2) ["a" "b" "c"]
|
||||
ledis>xscan "" count 1
|
||||
1) "a"
|
||||
2) ["a"]
|
||||
ledis>xscan "a" count 1
|
||||
1) "b"
|
||||
2) ["b"]
|
||||
ledis>xscan "b" count 1
|
||||
1) "c"
|
||||
2) ["c"]
|
||||
ledis>xscan "c" count 1
|
||||
1) ""
|
||||
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) []
|
||||
```
|
||||
|
||||
### DUMP key
|
||||
|
||||
Serialize the value stored at key with KV type in a Redis-specific format like RDB and return it to the user. The returned value can be synthesized back into a key using the RESTORE command.
|
||||
|
@ -980,31 +887,6 @@ ledis> HPERSIST not_exists_key
|
|||
(integer) 0
|
||||
```
|
||||
|
||||
### HXSCAN key [MATCH match] [COUNT count]
|
||||
|
||||
Iterate Hash keys incrementally.
|
||||
|
||||
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.
|
||||
|
||||
### XHSCAN key [MATCH match] [COUNT count]
|
||||
|
||||
Iterate Hash keys incrementally.
|
||||
|
||||
See [XSCAN](#xscan-key-match-match-count-count) for more information.
|
||||
|
||||
### XHREVSCAN key [MATCH match] [COUNT count]
|
||||
|
||||
Reverse iterate Hash keys incrementally.
|
||||
|
||||
See [XREVSCAN](#xrevscan-key-match-match-count-count) for more information.
|
||||
|
||||
|
||||
### HDUMP key
|
||||
|
||||
See [DUMP](#dump-key) for more information.
|
||||
|
@ -1331,31 +1213,6 @@ ledis> LPERSIST b
|
|||
(integer) 0
|
||||
```
|
||||
|
||||
### LXSCAN key [MATCH match] [COUNT count]
|
||||
|
||||
Iterate list keys incrementally.
|
||||
|
||||
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.
|
||||
|
||||
### XLSCAN key [MATCH match] [COUNT count]
|
||||
|
||||
Iterate list keys incrementally.
|
||||
|
||||
See [XSCAN](#xscan-key-match-match-count-count) for more information.
|
||||
|
||||
### XLREVSCAN key [MATCH match] [COUNT count]
|
||||
|
||||
Reverse iterate list keys incrementally.
|
||||
|
||||
See [XREVSCAN](#xrevscan-key-match-match-count-count) for more information.
|
||||
|
||||
|
||||
### LDUMP key
|
||||
|
||||
See [DUMP](#dump-key) for more information.
|
||||
|
@ -1782,33 +1639,6 @@ ledis> STTL key
|
|||
(integer) -1
|
||||
```
|
||||
|
||||
### SXSCAN key [MATCH match] [COUNT count]
|
||||
|
||||
Iterate Set keys incrementally.
|
||||
|
||||
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.
|
||||
|
||||
### XSSCAN key [MATCH match] [COUNT count]
|
||||
|
||||
Iterate Set keys incrementally.
|
||||
|
||||
See [XSCAN](#xscan-key-match-match-count-count) for more information.
|
||||
|
||||
|
||||
### XSREVSCAN key [MATCH match] [COUNT count]
|
||||
|
||||
Reverse iterate Set keys incrementally.
|
||||
|
||||
See [XREVSCAN](#xrevscan-key-match-match-count-count) for more information.
|
||||
|
||||
|
||||
### SDUMP key
|
||||
|
||||
See [DUMP](#dump-key) for more information.
|
||||
|
@ -2432,30 +2262,6 @@ ledis> ZRANGE out 0 -1 WITHSCORES
|
|||
4) "10"
|
||||
```
|
||||
|
||||
### ZXSCAN key [MATCH match] [COUNT count]
|
||||
|
||||
Iterate ZSet keys incrementally.
|
||||
|
||||
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.
|
||||
|
||||
### XZSCAN key [MATCH match] [COUNT count]
|
||||
|
||||
Iterate ZSet keys incrementally.
|
||||
|
||||
See [XSCAN](#xscan-key-match-match-count-count) for more information.
|
||||
|
||||
### XZREVSCAN 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]
|
||||
|
||||
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.
|
||||
|
@ -2684,29 +2490,60 @@ ledis> BCOUNT flag 5 6
|
|||
(refer to [PERSIST](#persist-key) api for other types)
|
||||
|
||||
|
||||
### BXSCAN key [MATCH match] [COUNT count]
|
||||
## Scan
|
||||
|
||||
Iterate Bitmap keys incrementally.
|
||||
### XSCAN type cursor [MATCH match] [COUNT count]
|
||||
|
||||
See [XSCAN](#xscan-key-match-match-count-count) for more information.
|
||||
Iterate data type keys incrementally.
|
||||
|
||||
### BXREVSCAN key [MATCH match] [COUNT count]
|
||||
Type is "KV", "LIST", "HASH", "SET" or "ZSET".
|
||||
Cursor 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.
|
||||
|
||||
Reverse iterate Bitmap keys incrementally.
|
||||
**Return value**
|
||||
|
||||
See [XREVSCAN](#xrevscan-key-match-match-count-count) for more information.
|
||||
an array of two values, first value is the cursor for next iteration, second value is an array of elements.
|
||||
|
||||
### XBSCAN key [MATCH match] [COUNT count]
|
||||
**Examples**
|
||||
|
||||
Iterate Bitmap keys incrementally.
|
||||
```
|
||||
ledis>set a 1
|
||||
OK
|
||||
ledis>set b 2
|
||||
OK
|
||||
ledis>set c 3
|
||||
OK
|
||||
127.0.0.1:6380>xscan "KV" ""
|
||||
1) ""
|
||||
2) ["a" "b" "c"]
|
||||
ledis>xscan "KV" "" count 1
|
||||
1) "a"
|
||||
2) ["a"]
|
||||
ledis>xscan "KV" "a" count 1
|
||||
1) "b"
|
||||
2) ["b"]
|
||||
ledis>xscan "KV" "b" count 1
|
||||
1) "c"
|
||||
2) ["c"]
|
||||
ledis>xscan "KV" "c" count 1
|
||||
1) ""
|
||||
2) []
|
||||
```
|
||||
|
||||
See [XSCAN](#xscan-key-match-match-count-count) for more information.
|
||||
### XHSCAN key cursor [MATCH match] [COUNT count]
|
||||
|
||||
### XBREVSCAN key [MATCH match] [COUNT count]
|
||||
Same like [XSCAN type cursor [MATCH match] [COUNT count]](#xscan-type-cursor-match-match-count-count), but return array of elements
|
||||
contains two elements, a field and a value.
|
||||
|
||||
Reverse iterate Bitmap keys incrementally.
|
||||
### XSSCAN key cursor [MATCH match] [COUNT count]
|
||||
|
||||
See [XREVSCAN](#xrevscan-key-match-match-count-count) for more information.
|
||||
Same like [XSCAN type cursor [MATCH match] [COUNT count]](#xscan-type-cursor-match-match-count-count)
|
||||
|
||||
### XZSCAN key cursor [MATCH match] [COUNT count]
|
||||
|
||||
Same like [XSCAN type cursor [MATCH match] [COUNT count]](#xscan-type-cursor-match-match-count-count), but return array of elements
|
||||
contains two elements, a member and its associated score.
|
||||
|
||||
|
||||
## Replication
|
||||
|
|
|
@ -6,6 +6,43 @@ import (
|
|||
|
||||
const Version = "0.4"
|
||||
|
||||
type DataType byte
|
||||
|
||||
// for out use
|
||||
const (
|
||||
KV DataType = iota
|
||||
LIST
|
||||
HASH
|
||||
SET
|
||||
ZSET
|
||||
)
|
||||
|
||||
func (d DataType) String() string {
|
||||
switch d {
|
||||
case KV:
|
||||
return KVName
|
||||
case LIST:
|
||||
return ListName
|
||||
case HASH:
|
||||
return HashName
|
||||
case SET:
|
||||
return SetName
|
||||
case ZSET:
|
||||
return ZSetName
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
KVName = "KV"
|
||||
ListName = "LIST"
|
||||
HashName = "HASH"
|
||||
SetName = "SET"
|
||||
ZSetName = "ZSET"
|
||||
)
|
||||
|
||||
// for backend store
|
||||
const (
|
||||
NoneType byte = 0
|
||||
KVType byte = 1
|
||||
|
|
|
@ -128,7 +128,7 @@ func (db *DB) flushType(t *batch, dataType byte) (drop int64, err error) {
|
|||
}
|
||||
|
||||
var keys [][]byte
|
||||
keys, err = db.scan(metaDataType, nil, 1024, false, "")
|
||||
keys, err = db.scanGeneric(metaDataType, nil, 1024, false, "", false)
|
||||
for len(keys) != 0 || err != nil {
|
||||
for _, key := range keys {
|
||||
deleteFunc(t, key)
|
||||
|
@ -141,7 +141,7 @@ func (db *DB) flushType(t *batch, dataType byte) (drop int64, err error) {
|
|||
} else {
|
||||
drop += int64(len(keys))
|
||||
}
|
||||
keys, err = db.scan(metaDataType, nil, 1024, false, "")
|
||||
keys, err = db.scanGeneric(metaDataType, nil, 1024, false, "", false)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
212
ledis/scan.go
212
ledis/scan.go
|
@ -9,19 +9,48 @@ import (
|
|||
var errDataType = errors.New("error data type")
|
||||
var errMetaKey = errors.New("error meta key")
|
||||
|
||||
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)
|
||||
//fif inclusive is true, scan range [cursor, inf) else (cursor, inf)
|
||||
func (db *DB) Scan(dataType DataType, cursor []byte, count int, inclusive bool, match string) ([][]byte, error) {
|
||||
storeDataType, err := getDataStoreType(dataType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return db.scanGeneric(storeDataType, cursor, 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)
|
||||
//if inclusive is true, revscan range (-inf, cursor] else (inf, cursor)
|
||||
func (db *DB) RevScan(dataType DataType, cursor []byte, count int, inclusive bool, match string) ([][]byte, error) {
|
||||
storeDataType, err := getDataStoreType(dataType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return db.scanGeneric(storeDataType, cursor, 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
|
||||
func getDataStoreType(dataType DataType) (byte, error) {
|
||||
var storeDataType byte
|
||||
switch dataType {
|
||||
case KV:
|
||||
storeDataType = KVType
|
||||
case LIST:
|
||||
storeDataType = LMetaType
|
||||
case HASH:
|
||||
storeDataType = HSizeType
|
||||
case SET:
|
||||
storeDataType = SSizeType
|
||||
case ZSET:
|
||||
storeDataType = ZSizeType
|
||||
default:
|
||||
return 0, errDataType
|
||||
}
|
||||
return storeDataType, nil
|
||||
}
|
||||
|
||||
func buildMatchRegexp(match string) (*regexp.Regexp, error) {
|
||||
var err error
|
||||
var r *regexp.Regexp
|
||||
var r *regexp.Regexp = nil
|
||||
|
||||
if len(match) > 0 {
|
||||
if r, err = regexp.Compile(match); err != nil {
|
||||
|
@ -29,13 +58,24 @@ func (db *DB) scanGeneric(dataType byte, key []byte, count int,
|
|||
}
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (db *DB) scanGeneric(storeDataType byte, key []byte, count int,
|
||||
inclusive bool, match string, reverse bool) ([][]byte, error) {
|
||||
var minKey, maxKey []byte
|
||||
r, err := buildMatchRegexp(match)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tp := store.RangeOpen
|
||||
|
||||
if !reverse {
|
||||
if minKey, err = db.encodeScanMinKey(dataType, key); err != nil {
|
||||
if minKey, err = db.encodeScanMinKey(storeDataType, key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if maxKey, err = db.encodeScanMaxKey(dataType, nil); err != nil {
|
||||
if maxKey, err = db.encodeScanMaxKey(storeDataType, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -43,10 +83,10 @@ func (db *DB) scanGeneric(dataType byte, key []byte, count int,
|
|||
tp = store.RangeROpen
|
||||
}
|
||||
} else {
|
||||
if minKey, err = db.encodeScanMinKey(dataType, nil); err != nil {
|
||||
if minKey, err = db.encodeScanMinKey(storeDataType, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if maxKey, err = db.encodeScanMaxKey(dataType, key); err != nil {
|
||||
if maxKey, err = db.encodeScanMaxKey(storeDataType, key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -69,7 +109,7 @@ func (db *DB) scanGeneric(dataType byte, key []byte, count int,
|
|||
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(storeDataType, it.Key()); err != nil {
|
||||
continue
|
||||
} else if r != nil && !r.Match(k) {
|
||||
continue
|
||||
|
@ -82,36 +122,36 @@ func (db *DB) scanGeneric(dataType byte, key []byte, count int,
|
|||
return v, nil
|
||||
}
|
||||
|
||||
func (db *DB) encodeScanMinKey(dataType byte, key []byte) ([]byte, error) {
|
||||
func (db *DB) encodeScanMinKey(storeDataType byte, key []byte) ([]byte, error) {
|
||||
if len(key) == 0 {
|
||||
return db.encodeScanKey(dataType, nil)
|
||||
return db.encodeScanKey(storeDataType, nil)
|
||||
} else {
|
||||
if err := checkKeySize(key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return db.encodeScanKey(dataType, key)
|
||||
return db.encodeScanKey(storeDataType, key)
|
||||
}
|
||||
}
|
||||
|
||||
func (db *DB) encodeScanMaxKey(dataType byte, key []byte) ([]byte, error) {
|
||||
func (db *DB) encodeScanMaxKey(storeDataType byte, key []byte) ([]byte, error) {
|
||||
if len(key) > 0 {
|
||||
if err := checkKeySize(key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return db.encodeScanKey(dataType, key)
|
||||
return db.encodeScanKey(storeDataType, key)
|
||||
}
|
||||
|
||||
k, err := db.encodeScanKey(dataType, nil)
|
||||
k, err := db.encodeScanKey(storeDataType, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
k[len(k)-1] = dataType + 1
|
||||
k[len(k)-1] = storeDataType + 1
|
||||
return k, nil
|
||||
}
|
||||
|
||||
func (db *DB) encodeScanKey(dataType byte, key []byte) ([]byte, error) {
|
||||
switch dataType {
|
||||
func (db *DB) encodeScanKey(storeDataType byte, key []byte) ([]byte, error) {
|
||||
switch storeDataType {
|
||||
case KVType:
|
||||
return db.encodeKVKey(key), nil
|
||||
case LMetaType:
|
||||
|
@ -120,17 +160,137 @@ func (db *DB) encodeScanKey(dataType byte, key []byte) ([]byte, error) {
|
|||
return db.hEncodeSizeKey(key), nil
|
||||
case ZSizeType:
|
||||
return db.zEncodeSizeKey(key), nil
|
||||
case BitMetaType:
|
||||
return db.bEncodeMetaKey(key), nil
|
||||
case SSizeType:
|
||||
return db.sEncodeSizeKey(key), nil
|
||||
case BitMetaType:
|
||||
return db.bEncodeMetaKey(key), nil
|
||||
default:
|
||||
return nil, errDataType
|
||||
}
|
||||
}
|
||||
func (db *DB) decodeScanKey(dataType byte, ek []byte) ([]byte, error) {
|
||||
if len(ek) < 2 || ek[0] != db.index || ek[1] != dataType {
|
||||
func (db *DB) decodeScanKey(storeDataType byte, ek []byte) ([]byte, error) {
|
||||
if len(ek) < 2 || ek[0] != db.index || ek[1] != storeDataType {
|
||||
return nil, errMetaKey
|
||||
}
|
||||
return ek[2:], nil
|
||||
}
|
||||
|
||||
// for specail data scan
|
||||
|
||||
func (db *DB) buildDataScanIterator(start []byte, stop []byte, inclusive bool) *store.RangeLimitIterator {
|
||||
tp := store.RangeROpen
|
||||
|
||||
if !inclusive {
|
||||
tp = store.RangeOpen
|
||||
}
|
||||
it := db.bucket.RangeIterator(start, stop, tp)
|
||||
return it
|
||||
|
||||
}
|
||||
|
||||
func (db *DB) HScan(key []byte, cursor []byte, count int, inclusive bool, match string) ([]FVPair, error) {
|
||||
if err := checkKeySize(key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
start := db.hEncodeHashKey(key, cursor)
|
||||
stop := db.hEncodeStopKey(key)
|
||||
|
||||
v := make([]FVPair, 0, 16)
|
||||
|
||||
r, err := buildMatchRegexp(match)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
it := db.buildDataScanIterator(start, stop, inclusive)
|
||||
defer it.Close()
|
||||
|
||||
for i := 0; it.Valid() && i < count; it.Next() {
|
||||
_, f, err := db.hDecodeHashKey(it.Key())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if r != nil && !r.Match(f) {
|
||||
continue
|
||||
}
|
||||
|
||||
v = append(v, FVPair{Field: f, Value: it.Value()})
|
||||
|
||||
i++
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (db *DB) SScan(key []byte, cursor []byte, count int, inclusive bool, match string) ([][]byte, error) {
|
||||
if err := checkKeySize(key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
start := db.sEncodeSetKey(key, cursor)
|
||||
stop := db.sEncodeStopKey(key)
|
||||
|
||||
v := make([][]byte, 0, 16)
|
||||
|
||||
r, err := buildMatchRegexp(match)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
it := db.buildDataScanIterator(start, stop, inclusive)
|
||||
defer it.Close()
|
||||
|
||||
for i := 0; it.Valid() && i < count; it.Next() {
|
||||
_, m, err := db.sDecodeSetKey(it.Key())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if r != nil && !r.Match(m) {
|
||||
continue
|
||||
}
|
||||
|
||||
v = append(v, m)
|
||||
|
||||
i++
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (db *DB) ZScan(key []byte, cursor []byte, count int, inclusive bool, match string) ([]ScorePair, error) {
|
||||
if err := checkKeySize(key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
start := db.zEncodeSetKey(key, cursor)
|
||||
stop := db.zEncodeStopSetKey(key)
|
||||
|
||||
v := make([]ScorePair, 0, 16)
|
||||
|
||||
r, err := buildMatchRegexp(match)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
it := db.buildDataScanIterator(start, stop, inclusive)
|
||||
defer it.Close()
|
||||
|
||||
for i := 0; it.Valid() && i < count; it.Next() {
|
||||
_, m, err := db.zDecodeSetKey(it.Key())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if r != nil && !r.Match(m) {
|
||||
continue
|
||||
}
|
||||
|
||||
score, err := Int64(it.Value(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v = append(v, ScorePair{Score: score, Member: m})
|
||||
|
||||
i++
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
|
|
@ -21,13 +21,13 @@ func TestDBScan(t *testing.T) {
|
|||
|
||||
db.FlushAll()
|
||||
|
||||
if v, err := db.Scan(nil, 10, true, ""); err != nil {
|
||||
if v, err := db.Scan(KV, nil, 10, true, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(v) != 0 {
|
||||
t.Fatal(len(v))
|
||||
}
|
||||
|
||||
if v, err := db.RevScan(nil, 10, true, ""); err != nil {
|
||||
if v, err := db.RevScan(KV, nil, 10, true, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(v) != 0 {
|
||||
t.Fatal(len(v))
|
||||
|
@ -37,73 +37,73 @@ func TestDBScan(t *testing.T) {
|
|||
db.Set([]byte("b"), []byte{})
|
||||
db.Set([]byte("c"), []byte{})
|
||||
|
||||
if v, err := db.Scan(nil, 1, true, ""); err != nil {
|
||||
if v, err := db.Scan(KV, nil, 1, true, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
checkTestScan(t, v, "a")
|
||||
}
|
||||
|
||||
if v, err := db.Scan([]byte("a"), 2, false, ""); err != nil {
|
||||
if v, err := db.Scan(KV, []byte("a"), 2, false, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
checkTestScan(t, v, "b", "c")
|
||||
}
|
||||
|
||||
if v, err := db.Scan(nil, 3, true, ""); err != nil {
|
||||
if v, err := db.Scan(KV, nil, 3, true, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
checkTestScan(t, v, "a", "b", "c")
|
||||
}
|
||||
|
||||
if v, err := db.Scan(nil, 3, true, "b"); err != nil {
|
||||
if v, err := db.Scan(KV, nil, 3, true, "b"); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
checkTestScan(t, v, "b")
|
||||
}
|
||||
|
||||
if v, err := db.Scan(nil, 3, true, "."); err != nil {
|
||||
if v, err := db.Scan(KV, nil, 3, true, "."); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
checkTestScan(t, v, "a", "b", "c")
|
||||
}
|
||||
|
||||
if v, err := db.Scan(nil, 3, true, "a+"); err != nil {
|
||||
if v, err := db.Scan(KV, nil, 3, true, "a+"); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
checkTestScan(t, v, "a")
|
||||
}
|
||||
|
||||
if v, err := db.RevScan(nil, 1, true, ""); err != nil {
|
||||
if v, err := db.RevScan(KV, nil, 1, true, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
checkTestScan(t, v, "c")
|
||||
}
|
||||
|
||||
if v, err := db.RevScan([]byte("c"), 2, false, ""); err != nil {
|
||||
if v, err := db.RevScan(KV, []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 {
|
||||
if v, err := db.RevScan(KV, 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 {
|
||||
if v, err := db.RevScan(KV, nil, 3, true, "b"); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
checkTestScan(t, v, "b")
|
||||
}
|
||||
|
||||
if v, err := db.RevScan(nil, 3, true, "."); err != nil {
|
||||
if v, err := db.RevScan(KV, 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 {
|
||||
if v, err := db.RevScan(KV, nil, 3, true, "c+"); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
checkTestScan(t, v, "c")
|
||||
|
@ -111,7 +111,7 @@ func TestDBScan(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
func TestDBHScan(t *testing.T) {
|
||||
func TestDBHKeyScan(t *testing.T) {
|
||||
db := getTestDB()
|
||||
|
||||
db.hFlush()
|
||||
|
@ -125,7 +125,7 @@ func TestDBHScan(t *testing.T) {
|
|||
k3 := []byte("k3")
|
||||
db.HSet(k3, []byte("3"), []byte{})
|
||||
|
||||
if v, err := db.HScan(nil, 1, true, ""); err != nil {
|
||||
if v, err := db.Scan(HASH, nil, 1, true, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(v) != 1 {
|
||||
t.Fatal("invalid length ", len(v))
|
||||
|
@ -133,7 +133,7 @@ func TestDBHScan(t *testing.T) {
|
|||
t.Fatal("invalid value ", string(v[0]))
|
||||
}
|
||||
|
||||
if v, err := db.HScan(k1, 2, true, ""); err != nil {
|
||||
if v, err := db.Scan(HASH, k1, 2, true, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(v) != 2 {
|
||||
t.Fatal("invalid length ", len(v))
|
||||
|
@ -143,7 +143,7 @@ func TestDBHScan(t *testing.T) {
|
|||
t.Fatal("invalid value ", string(v[1]))
|
||||
}
|
||||
|
||||
if v, err := db.HScan(k1, 2, false, ""); err != nil {
|
||||
if v, err := db.Scan(HASH, k1, 2, false, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(v) != 2 {
|
||||
t.Fatal("invalid length ", len(v))
|
||||
|
@ -155,7 +155,7 @@ func TestDBHScan(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
func TestDBZScan(t *testing.T) {
|
||||
func TestDBZKeyScan(t *testing.T) {
|
||||
db := getTestDB()
|
||||
|
||||
db.zFlush()
|
||||
|
@ -169,7 +169,7 @@ func TestDBZScan(t *testing.T) {
|
|||
k3 := []byte("k3")
|
||||
db.ZAdd(k3, ScorePair{3, []byte("m")})
|
||||
|
||||
if v, err := db.ZScan(nil, 1, true, ""); err != nil {
|
||||
if v, err := db.Scan(ZSET, nil, 1, true, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(v) != 1 {
|
||||
t.Fatal("invalid length ", len(v))
|
||||
|
@ -177,7 +177,7 @@ func TestDBZScan(t *testing.T) {
|
|||
t.Fatal("invalid value ", string(v[0]))
|
||||
}
|
||||
|
||||
if v, err := db.ZScan(k1, 2, true, ""); err != nil {
|
||||
if v, err := db.Scan(ZSET, k1, 2, true, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(v) != 2 {
|
||||
t.Fatal("invalid length ", len(v))
|
||||
|
@ -187,7 +187,7 @@ func TestDBZScan(t *testing.T) {
|
|||
t.Fatal("invalid value ", string(v[1]))
|
||||
}
|
||||
|
||||
if v, err := db.ZScan(k1, 2, false, ""); err != nil {
|
||||
if v, err := db.Scan(ZSET, k1, 2, false, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(v) != 2 {
|
||||
t.Fatal("invalid length ", len(v))
|
||||
|
@ -199,7 +199,7 @@ func TestDBZScan(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
func TestDBLScan(t *testing.T) {
|
||||
func TestDBLKeyScan(t *testing.T) {
|
||||
db := getTestDB()
|
||||
|
||||
db.lFlush()
|
||||
|
@ -219,7 +219,7 @@ func TestDBLScan(t *testing.T) {
|
|||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
if v, err := db.LScan(nil, 1, true, ""); err != nil {
|
||||
if v, err := db.Scan(LIST, nil, 1, true, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(v) != 1 {
|
||||
t.Fatal("invalid length ", len(v))
|
||||
|
@ -227,7 +227,7 @@ func TestDBLScan(t *testing.T) {
|
|||
t.Fatal("invalid value ", string(v[0]))
|
||||
}
|
||||
|
||||
if v, err := db.LScan(k1, 2, true, ""); err != nil {
|
||||
if v, err := db.Scan(LIST, k1, 2, true, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(v) != 2 {
|
||||
t.Fatal("invalid length ", len(v))
|
||||
|
@ -237,7 +237,7 @@ func TestDBLScan(t *testing.T) {
|
|||
t.Fatal("invalid value ", string(v[1]))
|
||||
}
|
||||
|
||||
if v, err := db.LScan(k1, 2, false, ""); err != nil {
|
||||
if v, err := db.Scan(LIST, k1, 2, false, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(v) != 2 {
|
||||
t.Fatal("invalid length ", len(v))
|
||||
|
@ -249,60 +249,10 @@ func TestDBLScan(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
func TestDBBScan(t *testing.T) {
|
||||
// db := getTestDB()
|
||||
|
||||
// db.bFlush()
|
||||
|
||||
// k1 := []byte("k1")
|
||||
// if _, err := db.BSetBit(k1, 1, 1); err != nil {
|
||||
// t.Fatal(err.Error())
|
||||
// }
|
||||
|
||||
// k2 := []byte("k2")
|
||||
// if _, err := db.BSetBit(k2, 1, 1); err != nil {
|
||||
// t.Fatal(err.Error())
|
||||
// }
|
||||
// k3 := []byte("k3")
|
||||
|
||||
// if _, err := db.BSetBit(k3, 1, 0); err != nil {
|
||||
// t.Fatal(err.Error())
|
||||
// }
|
||||
|
||||
// if v, err := db.BScan(nil, 1, true, ""); err != nil {
|
||||
// t.Fatal(err)
|
||||
// } else if len(v) != 1 {
|
||||
// t.Fatal("invalid length ", len(v))
|
||||
// } else if string(v[0]) != "k1" {
|
||||
// t.Fatal("invalid value ", string(v[0]))
|
||||
// }
|
||||
|
||||
// if v, err := db.BScan(k1, 2, true, ""); err != nil {
|
||||
// t.Fatal(err)
|
||||
// } else if len(v) != 2 {
|
||||
// t.Fatal("invalid length ", len(v))
|
||||
// } else if string(v[0]) != "k1" {
|
||||
// t.Fatal("invalid value ", string(v[0]))
|
||||
// } else if string(v[1]) != "k2" {
|
||||
// t.Fatal("invalid value ", string(v[1]))
|
||||
// }
|
||||
|
||||
// if v, err := db.BScan(k1, 2, false, ""); err != nil {
|
||||
// t.Fatal(err)
|
||||
// } else if len(v) != 2 {
|
||||
// t.Fatal("invalid length ", len(v))
|
||||
// } else if string(v[0]) != "k2" {
|
||||
// t.Fatal("invalid value ", string(v[0]))
|
||||
// } else if string(v[1]) != "k3" {
|
||||
// t.Fatal("invalid value ", string(v[1]))
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
func TestDBSScan(t *testing.T) {
|
||||
func TestDBSKeyScan(t *testing.T) {
|
||||
db := getTestDB()
|
||||
|
||||
db.bFlush()
|
||||
db.sFlush()
|
||||
|
||||
k1 := []byte("k1")
|
||||
if _, err := db.SAdd(k1, []byte("1")); err != nil {
|
||||
|
@ -319,7 +269,7 @@ func TestDBSScan(t *testing.T) {
|
|||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
if v, err := db.SScan(nil, 1, true, ""); err != nil {
|
||||
if v, err := db.Scan(SET, nil, 1, true, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(v) != 1 {
|
||||
t.Fatal("invalid length ", len(v))
|
||||
|
@ -327,7 +277,7 @@ func TestDBSScan(t *testing.T) {
|
|||
t.Fatal("invalid value ", string(v[0]))
|
||||
}
|
||||
|
||||
if v, err := db.SScan(k1, 2, true, ""); err != nil {
|
||||
if v, err := db.Scan(SET, k1, 2, true, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(v) != 2 {
|
||||
t.Fatal("invalid length ", len(v))
|
||||
|
@ -337,7 +287,7 @@ func TestDBSScan(t *testing.T) {
|
|||
t.Fatal("invalid value ", string(v[1]))
|
||||
}
|
||||
|
||||
if v, err := db.SScan(k1, 2, false, ""); err != nil {
|
||||
if v, err := db.Scan(SET, k1, 2, false, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(v) != 2 {
|
||||
t.Fatal("invalid length ", len(v))
|
||||
|
@ -346,5 +296,77 @@ func TestDBSScan(t *testing.T) {
|
|||
} else if string(v[1]) != "k3" {
|
||||
t.Fatal("invalid value ", string(v[1]))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestDBHScan(t *testing.T) {
|
||||
db := getTestDB()
|
||||
|
||||
key := []byte("scan_h_key")
|
||||
value := []byte("hello world")
|
||||
db.HSet(key, []byte("1"), value)
|
||||
db.HSet(key, []byte("222"), value)
|
||||
db.HSet(key, []byte("19"), value)
|
||||
db.HSet(key, []byte("1234"), value)
|
||||
|
||||
v, err := db.HScan(key, nil, 100, true, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(v) != 4 {
|
||||
t.Fatal("invalid count", len(v))
|
||||
}
|
||||
|
||||
v, err = db.HScan(key, []byte("19"), 1, false, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(v) != 1 {
|
||||
t.Fatal("invalid count", len(v))
|
||||
} else if string(v[0].Field) != "222" {
|
||||
t.Fatal(string(v[0].Field))
|
||||
}
|
||||
}
|
||||
|
||||
func TestDBSScan(t *testing.T) {
|
||||
db := getTestDB()
|
||||
key := []byte("scan_s_key")
|
||||
|
||||
db.SAdd(key, []byte("1"), []byte("222"), []byte("19"), []byte("1234"))
|
||||
|
||||
v, err := db.SScan(key, nil, 100, true, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(v) != 4 {
|
||||
t.Fatal("invalid count", len(v))
|
||||
}
|
||||
|
||||
v, err = db.SScan(key, []byte("19"), 1, false, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(v) != 1 {
|
||||
t.Fatal("invalid count", len(v))
|
||||
} else if string(v[0]) != "222" {
|
||||
t.Fatal(string(v[0]))
|
||||
}
|
||||
}
|
||||
|
||||
func TestDBZScan(t *testing.T) {
|
||||
db := getTestDB()
|
||||
key := []byte("scan_z_key")
|
||||
|
||||
db.ZAdd(key, ScorePair{1, []byte("1")}, ScorePair{2, []byte("222")}, ScorePair{3, []byte("19")}, ScorePair{4, []byte("1234")})
|
||||
|
||||
v, err := db.ZScan(key, nil, 100, true, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(v) != 4 {
|
||||
t.Fatal("invalid count", len(v))
|
||||
}
|
||||
|
||||
v, err = db.ZScan(key, []byte("19"), 1, false, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(v) != 1 {
|
||||
t.Fatal("invalid count", len(v))
|
||||
} else if string(v[0].Member) != "222" {
|
||||
t.Fatal(string(v[0].Member))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -922,16 +922,6 @@ func (db *DB) BPersist(key []byte) (int64, error) {
|
|||
return n, err
|
||||
}
|
||||
|
||||
func (db *DB) BScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) {
|
||||
log.Error("bitmap type will be deprecated later, please use bit operations in kv type")
|
||||
return db.scan(BitMetaType, key, count, inclusive, match)
|
||||
}
|
||||
|
||||
func (db *DB) BRevScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) {
|
||||
log.Error("bitmap type will be deprecated later, please use bit operations in kv type")
|
||||
return db.revscan(BitMetaType, key, count, inclusive, match)
|
||||
}
|
||||
|
||||
func (db *DB) bFlush() (drop int64, err error) {
|
||||
t := db.binBatch
|
||||
t.Lock()
|
||||
|
|
|
@ -354,6 +354,8 @@ func (db *DB) HGetAll(key []byte) ([]FVPair, error) {
|
|||
v := make([]FVPair, 0, 16)
|
||||
|
||||
it := db.bucket.RangeLimitIterator(start, stop, store.RangeROpen, 0, -1)
|
||||
defer it.Close()
|
||||
|
||||
for ; it.Valid(); it.Next() {
|
||||
_, f, err := db.hDecodeHashKey(it.Key())
|
||||
if err != nil {
|
||||
|
@ -363,8 +365,6 @@ func (db *DB) HGetAll(key []byte) ([]FVPair, error) {
|
|||
v = append(v, FVPair{Field: f, Value: it.Value()})
|
||||
}
|
||||
|
||||
it.Close()
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
|
@ -379,6 +379,8 @@ func (db *DB) HKeys(key []byte) ([][]byte, error) {
|
|||
v := make([][]byte, 0, 16)
|
||||
|
||||
it := db.bucket.RangeLimitIterator(start, stop, store.RangeROpen, 0, -1)
|
||||
defer it.Close()
|
||||
|
||||
for ; it.Valid(); it.Next() {
|
||||
_, f, err := db.hDecodeHashKey(it.Key())
|
||||
if err != nil {
|
||||
|
@ -387,8 +389,6 @@ func (db *DB) HKeys(key []byte) ([][]byte, error) {
|
|||
v = append(v, f)
|
||||
}
|
||||
|
||||
it.Close()
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
|
@ -403,6 +403,8 @@ func (db *DB) HValues(key []byte) ([][]byte, error) {
|
|||
v := make([][]byte, 0, 16)
|
||||
|
||||
it := db.bucket.RangeLimitIterator(start, stop, store.RangeROpen, 0, -1)
|
||||
defer it.Close()
|
||||
|
||||
for ; it.Valid(); it.Next() {
|
||||
_, _, err := db.hDecodeHashKey(it.Key())
|
||||
if err != nil {
|
||||
|
@ -412,8 +414,6 @@ func (db *DB) HValues(key []byte) ([][]byte, error) {
|
|||
v = append(v, it.Value())
|
||||
}
|
||||
|
||||
it.Close()
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
|
@ -460,14 +460,6 @@ func (db *DB) hFlush() (drop int64, err error) {
|
|||
return db.flushType(t, HashType)
|
||||
}
|
||||
|
||||
func (db *DB) HScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) {
|
||||
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) {
|
||||
if duration <= 0 {
|
||||
return 0, errExpireValue
|
||||
|
|
|
@ -114,7 +114,7 @@ func TestHFlush(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
if v, err := db.HScan(nil, 3000, true, ""); err != nil {
|
||||
if v, err := db.Scan(HASH, nil, 3000, true, ""); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
} else if len(v) != 2000 {
|
||||
t.Fatal("invalid value ", len(v))
|
||||
|
@ -135,7 +135,7 @@ func TestHFlush(t *testing.T) {
|
|||
t.Fatal("invalid value ", n)
|
||||
}
|
||||
|
||||
if v, err := db.HScan(nil, 3000, true, ""); err != nil {
|
||||
if v, err := db.Scan(HASH, nil, 3000, true, ""); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
} else if len(v) != 0 {
|
||||
t.Fatal("invalid value length ", len(v))
|
||||
|
|
|
@ -347,16 +347,6 @@ func (db *DB) flush() (drop int64, err error) {
|
|||
return db.flushType(t, KVType)
|
||||
}
|
||||
|
||||
//if inclusive is true, scan range [key, inf) else (key, inf)
|
||||
func (db *DB) Scan(key []byte, count int, inclusive bool, match string) ([][]byte, error) {
|
||||
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) {
|
||||
if duration <= 0 {
|
||||
return 0, errExpireValue
|
||||
|
|
|
@ -282,7 +282,7 @@ func TestKVFlush(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
if v, err := db.Scan(nil, 3000, true, ""); err != nil {
|
||||
if v, err := db.Scan(KV, nil, 3000, true, ""); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
} else if len(v) != 2000 {
|
||||
t.Fatal("invalid value ", len(v))
|
||||
|
@ -303,7 +303,7 @@ func TestKVFlush(t *testing.T) {
|
|||
t.Fatal("invalid value ", n)
|
||||
}
|
||||
|
||||
if v, err := db.Scan(nil, 3000, true, ""); err != nil {
|
||||
if v, err := db.Scan(KV, nil, 3000, true, ""); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
} else if len(v) != 0 {
|
||||
t.Fatal("invalid value length ", len(v))
|
||||
|
|
|
@ -480,14 +480,6 @@ func (db *DB) LPersist(key []byte) (int64, error) {
|
|||
return n, err
|
||||
}
|
||||
|
||||
func (db *DB) LScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) {
|
||||
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 {
|
||||
return db.lEncodeMetaKey(nil)
|
||||
}
|
||||
|
|
|
@ -145,7 +145,7 @@ func TestLFlush(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
if v, err := db.LScan(nil, 3000, true, ""); err != nil {
|
||||
if v, err := db.Scan(LIST, nil, 3000, true, ""); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
} else if len(v) != 2000 {
|
||||
t.Fatal("invalid value ", len(v))
|
||||
|
@ -157,7 +157,7 @@ func TestLFlush(t *testing.T) {
|
|||
t.Fatal("invalid value ", n)
|
||||
}
|
||||
|
||||
if v, err := db.LScan(nil, 3000, true, ""); err != nil {
|
||||
if v, err := db.Scan(LIST, nil, 3000, true, ""); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
} else if len(v) != 0 {
|
||||
t.Fatal("invalid value length ", len(v))
|
||||
|
|
|
@ -607,11 +607,3 @@ func (db *DB) SPersist(key []byte) (int64, error) {
|
|||
err = t.Commit()
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (db *DB) SScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) {
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -352,7 +352,7 @@ func TestSFlush(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
if v, err := db.SScan(nil, 3000, true, ""); err != nil {
|
||||
if v, err := db.Scan(SET, nil, 3000, true, ""); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
} else if len(v) != 2000 {
|
||||
t.Fatal("invalid value ", len(v))
|
||||
|
@ -364,7 +364,7 @@ func TestSFlush(t *testing.T) {
|
|||
t.Fatal("invalid value ", n)
|
||||
}
|
||||
|
||||
if v, err := db.SScan(nil, 3000, true, ""); err != nil {
|
||||
if v, err := db.Scan(SET, nil, 3000, true, ""); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
} else if len(v) != 0 {
|
||||
t.Fatal("invalid value length ", len(v))
|
||||
|
|
|
@ -936,14 +936,6 @@ func (db *DB) ZInterStore(destKey []byte, srcKeys [][]byte, weights []int64, agg
|
|||
return n, nil
|
||||
}
|
||||
|
||||
func (db *DB) ZScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) {
|
||||
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) {
|
||||
if min == nil {
|
||||
min = db.zEncodeStartSetKey(key)
|
||||
|
|
|
@ -391,7 +391,7 @@ func TestZScan(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
if v, err := db.ZScan(nil, 3000, true, ""); err != nil {
|
||||
if v, err := db.Scan(ZSET, nil, 3000, true, ""); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
} else if len(v) != 2000 {
|
||||
t.Fatal("invalid value ", len(v))
|
||||
|
@ -403,7 +403,7 @@ func TestZScan(t *testing.T) {
|
|||
t.Fatal("invalid value ", n)
|
||||
}
|
||||
|
||||
if v, err := db.ZScan(nil, 3000, true, ""); err != nil {
|
||||
if v, err := db.Scan(ZSET, nil, 3000, true, ""); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
} else if len(v) != 0 {
|
||||
t.Fatal("invalid value length ", len(v))
|
||||
|
|
|
@ -274,14 +274,6 @@ func bpersistCommand(c *client) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func bxscanCommand(c *client) error {
|
||||
return xscanGeneric(c, c.db.BScan)
|
||||
}
|
||||
|
||||
func bxrevscanCommand(c *client) error {
|
||||
return xscanGeneric(c, c.db.BRevScan)
|
||||
}
|
||||
|
||||
func init() {
|
||||
register("bget", bgetCommand)
|
||||
register("bdelete", bdeleteCommand)
|
||||
|
@ -294,8 +286,4 @@ func init() {
|
|||
register("bexpireat", bexpireAtCommand)
|
||||
register("bttl", bttlCommand)
|
||||
register("bpersist", bpersistCommand)
|
||||
register("bxscan", bxscanCommand)
|
||||
register("bxrevscan", bxrevscanCommand)
|
||||
register("xbscan", bxscanCommand)
|
||||
register("xbrevscan", bxrevscanCommand)
|
||||
}
|
||||
|
|
|
@ -292,14 +292,6 @@ func hpersistCommand(c *client) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func hxscanCommand(c *client) error {
|
||||
return xscanGeneric(c, c.db.HScan)
|
||||
}
|
||||
|
||||
func hxrevscanCommand(c *client) error {
|
||||
return xscanGeneric(c, c.db.HRevScan)
|
||||
}
|
||||
|
||||
func xhexistsCommand(c *client) error {
|
||||
args := c.args
|
||||
if len(args) != 1 {
|
||||
|
@ -334,9 +326,5 @@ func init() {
|
|||
register("hexpireat", hexpireAtCommand)
|
||||
register("httl", httlCommand)
|
||||
register("hpersist", hpersistCommand)
|
||||
register("hxscan", hxscanCommand)
|
||||
register("hxrevscan", hxrevscanCommand)
|
||||
register("xhscan", hxscanCommand)
|
||||
register("xhrevscan", hxrevscanCommand)
|
||||
register("xhexists", xhexistsCommand)
|
||||
}
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"github.com/siddontang/go/hack"
|
||||
"github.com/siddontang/ledisdb/ledis"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// func getCommand(c *client) error {
|
||||
|
@ -315,82 +313,6 @@ func persistCommand(c *client) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func parseScanArgs(c *client) (key []byte, match string, count int, err error) {
|
||||
args := c.args
|
||||
count = 10
|
||||
|
||||
switch len(args) {
|
||||
case 0:
|
||||
key = nil
|
||||
return
|
||||
case 1, 3, 5:
|
||||
key = args[0]
|
||||
break
|
||||
default:
|
||||
err = ErrCmdParams
|
||||
return
|
||||
}
|
||||
|
||||
if len(args) == 3 {
|
||||
switch strings.ToLower(hack.String(args[1])) {
|
||||
case "match":
|
||||
match = hack.String(args[2])
|
||||
case "count":
|
||||
count, err = strconv.Atoi(hack.String(args[2]))
|
||||
default:
|
||||
err = ErrCmdParams
|
||||
return
|
||||
}
|
||||
} else if len(args) == 5 {
|
||||
if strings.ToLower(hack.String(args[1])) != "match" {
|
||||
err = ErrCmdParams
|
||||
return
|
||||
} else if strings.ToLower(hack.String(args[3])) != "count" {
|
||||
err = ErrCmdParams
|
||||
return
|
||||
}
|
||||
|
||||
match = hack.String(args[2])
|
||||
count, err = strconv.Atoi(hack.String(args[4]))
|
||||
}
|
||||
|
||||
if count <= 0 {
|
||||
err = ErrCmdParams
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func xscanGeneric(c *client,
|
||||
f func(key []byte, count int, inclusive bool, match string) ([][]byte, error)) error {
|
||||
key, match, count, err := parseScanArgs(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ay, err := f(key, count, false, match); err != nil {
|
||||
return err
|
||||
} 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 xscanCommand(c *client) error {
|
||||
return xscanGeneric(c, c.db.Scan)
|
||||
}
|
||||
|
||||
func xrevscanCommand(c *client) error {
|
||||
return xscanGeneric(c, c.db.RevScan)
|
||||
}
|
||||
|
||||
func appendCommand(c *client) error {
|
||||
args := c.args
|
||||
if len(args) != 2 {
|
||||
|
@ -619,6 +541,4 @@ func init() {
|
|||
register("expireat", expireAtCommand)
|
||||
register("ttl", ttlCommand)
|
||||
register("persist", persistCommand)
|
||||
register("xscan", xscanCommand)
|
||||
register("xrevscan", xrevscanCommand)
|
||||
}
|
||||
|
|
|
@ -231,14 +231,6 @@ func lpersistCommand(c *client) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func lxscanCommand(c *client) error {
|
||||
return xscanGeneric(c, c.db.LScan)
|
||||
}
|
||||
|
||||
func lxrevscanCommand(c *client) error {
|
||||
return xscanGeneric(c, c.db.LRevScan)
|
||||
}
|
||||
|
||||
func blpopCommand(c *client) error {
|
||||
keys, timeout, err := lParseBPopArgs(c)
|
||||
if err != nil {
|
||||
|
@ -317,9 +309,5 @@ func init() {
|
|||
register("lexpireat", lexpireAtCommand)
|
||||
register("lttl", lttlCommand)
|
||||
register("lpersist", lpersistCommand)
|
||||
register("lxscan", lxscanCommand)
|
||||
register("lxrevscan", lxrevscanCommand)
|
||||
register("xlscan", lxscanCommand)
|
||||
register("xlrevscan", lxrevscanCommand)
|
||||
register("xlexists", xlexistsCommand)
|
||||
}
|
||||
|
|
|
@ -165,15 +165,15 @@ func xttl(db *ledis.DB, tp string, key []byte) (int64, error) {
|
|||
func xscan(db *ledis.DB, tp string, count int) ([][]byte, error) {
|
||||
switch strings.ToUpper(tp) {
|
||||
case "KV":
|
||||
return db.Scan(nil, count, false, "")
|
||||
return db.Scan(KV, nil, count, false, "")
|
||||
case "HASH":
|
||||
return db.HScan(nil, count, false, "")
|
||||
return db.Scan(HASH, nil, count, false, "")
|
||||
case "LIST":
|
||||
return db.LScan(nil, count, false, "")
|
||||
return db.Scan(LIST, nil, count, false, "")
|
||||
case "SET":
|
||||
return db.SScan(nil, count, false, "")
|
||||
return db.Scan(SET, nil, count, false, "")
|
||||
case "ZSET":
|
||||
return db.ZScan(nil, count, false, "")
|
||||
return db.Scan(ZSET, nil, count, false, "")
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid key type %s", tp)
|
||||
}
|
||||
|
|
|
@ -14,8 +14,8 @@ func checkDataEqual(master *App, slave *App) error {
|
|||
mdb, _ := master.ldb.Select(0)
|
||||
sdb, _ := slave.ldb.Select(0)
|
||||
|
||||
mkeys, _ := mdb.Scan(nil, 100, true, "")
|
||||
skeys, _ := sdb.Scan(nil, 100, true, "")
|
||||
mkeys, _ := mdb.Scan(KV, nil, 100, true, "")
|
||||
skeys, _ := sdb.Scan(KV, nil, 100, true, "")
|
||||
|
||||
if len(mkeys) != len(skeys) {
|
||||
return fmt.Errorf("keys number not equal %d != %d", len(mkeys), len(skeys))
|
||||
|
|
|
@ -0,0 +1,215 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/siddontang/go/hack"
|
||||
"github.com/siddontang/go/num"
|
||||
"github.com/siddontang/ledisdb/ledis"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func parseScanArgs(args [][]byte) (cursor []byte, match string, count int, err error) {
|
||||
cursor = args[0]
|
||||
|
||||
args = args[1:]
|
||||
|
||||
count = 10
|
||||
|
||||
for i := 0; i < len(args); {
|
||||
switch strings.ToUpper(hack.String(args[i])) {
|
||||
case "MATCH":
|
||||
if i+1 >= len(args) {
|
||||
err = ErrCmdParams
|
||||
return
|
||||
}
|
||||
|
||||
match = hack.String(args[i+1])
|
||||
i = i + 2
|
||||
case "COUNT":
|
||||
if i+1 >= len(args) {
|
||||
err = ErrCmdParams
|
||||
return
|
||||
}
|
||||
|
||||
count, err = strconv.Atoi(hack.String(args[i+1]))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
i = i + 2
|
||||
default:
|
||||
err = fmt.Errorf("invalid argument %s", args[i])
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// XSCAN type cursor [MATCH match] [COUNT count]
|
||||
func xscanCommand(c *client) error {
|
||||
args := c.args
|
||||
|
||||
if len(args) < 2 {
|
||||
return ErrCmdParams
|
||||
}
|
||||
|
||||
var dataType ledis.DataType
|
||||
switch strings.ToUpper(hack.String(args[0])) {
|
||||
case "KV":
|
||||
dataType = ledis.KV
|
||||
case "HASH":
|
||||
dataType = ledis.HASH
|
||||
case "LIST":
|
||||
dataType = ledis.LIST
|
||||
case "SET":
|
||||
dataType = ledis.SET
|
||||
case "ZSET":
|
||||
dataType = ledis.ZSET
|
||||
default:
|
||||
return fmt.Errorf("invalid key type %s", args[0])
|
||||
}
|
||||
|
||||
cursor, match, count, err := parseScanArgs(args[1:])
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ay, err := c.db.Scan(dataType, cursor, count, false, match)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// XHSCAN key cursor [MATCH match] [COUNT count]
|
||||
func xhscanCommand(c *client) error {
|
||||
args := c.args
|
||||
|
||||
if len(args) < 2 {
|
||||
return ErrCmdParams
|
||||
}
|
||||
|
||||
key := args[0]
|
||||
|
||||
cursor, match, count, err := parseScanArgs(args[1:])
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ay, err := c.db.HScan(key, cursor, count, false, match)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := make([]interface{}, 2)
|
||||
if len(ay) < count {
|
||||
data[0] = []byte("")
|
||||
} else {
|
||||
data[0] = ay[len(ay)-1].Field
|
||||
}
|
||||
|
||||
vv := make([][]byte, 0, len(ay)*2)
|
||||
|
||||
for _, v := range ay {
|
||||
vv = append(vv, v.Field, v.Value)
|
||||
}
|
||||
|
||||
data[1] = vv
|
||||
|
||||
c.resp.writeArray(data)
|
||||
return nil
|
||||
}
|
||||
|
||||
// XSSCAN key cursor [MATCH match] [COUNT count]
|
||||
func xsscanCommand(c *client) error {
|
||||
args := c.args
|
||||
|
||||
if len(args) < 2 {
|
||||
return ErrCmdParams
|
||||
}
|
||||
|
||||
key := args[0]
|
||||
|
||||
cursor, match, count, err := parseScanArgs(args[1:])
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ay, err := c.db.SScan(key, cursor, count, false, match)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// XZSCAN key cursor [MATCH match] [COUNT count]
|
||||
func xzscanCommand(c *client) error {
|
||||
args := c.args
|
||||
|
||||
if len(args) < 2 {
|
||||
return ErrCmdParams
|
||||
}
|
||||
|
||||
key := args[0]
|
||||
|
||||
cursor, match, count, err := parseScanArgs(args[1:])
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ay, err := c.db.ZScan(key, cursor, count, false, match)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := make([]interface{}, 2)
|
||||
if len(ay) < count {
|
||||
data[0] = []byte("")
|
||||
} else {
|
||||
data[0] = ay[len(ay)-1].Member
|
||||
}
|
||||
|
||||
vv := make([][]byte, 0, len(ay)*2)
|
||||
|
||||
for _, v := range ay {
|
||||
vv = append(vv, v.Member, num.FormatInt64ToSlice(v.Score))
|
||||
}
|
||||
|
||||
data[1] = vv
|
||||
|
||||
c.resp.writeArray(data)
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
register("xscan", xscanCommand)
|
||||
register("xhscan", xhscanCommand)
|
||||
register("xsscan", xsscanCommand)
|
||||
register("xzscan", xzscanCommand)
|
||||
}
|
|
@ -29,14 +29,14 @@ func TestScan(t *testing.T) {
|
|||
defer c.Close()
|
||||
|
||||
testKVScan(t, c)
|
||||
testHashScan(t, c)
|
||||
testListScan(t, c)
|
||||
testZSetScan(t, c)
|
||||
testSetScan(t, c)
|
||||
testHashKeyScan(t, c)
|
||||
testListKeyScan(t, c)
|
||||
testZSetKeyScan(t, c)
|
||||
testSetKeyScan(t, c)
|
||||
|
||||
}
|
||||
|
||||
func checkScanValues(t *testing.T, ay interface{}, values ...int) {
|
||||
func checkScanValues(t *testing.T, ay interface{}, values ...interface{}) {
|
||||
a, err := ledis.Strings(ay, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -47,14 +47,14 @@ func checkScanValues(t *testing.T, ay interface{}, values ...int) {
|
|||
}
|
||||
|
||||
for i, v := range a {
|
||||
if string(v) != fmt.Sprintf("%d", values[i]) {
|
||||
t.Fatal(fmt.Sprintf("%d %s != %d", string(v), values[i]))
|
||||
if string(v) != fmt.Sprintf("%v", values[i]) {
|
||||
t.Fatal(fmt.Sprintf("%d %s != %v", string(v), values[i]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkScan(t *testing.T, c *ledis.Client, cmd string) {
|
||||
if ay, err := ledis.Values(c.Do(cmd, "", "count", 5)); err != nil {
|
||||
func checkScan(t *testing.T, c *ledis.Client, tp string) {
|
||||
if ay, err := ledis.Values(c.Do("XSCAN", tp, "", "count", 5)); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(ay) != 2 {
|
||||
t.Fatal(len(ay))
|
||||
|
@ -64,7 +64,7 @@ func checkScan(t *testing.T, c *ledis.Client, cmd string) {
|
|||
checkScanValues(t, ay[1], 0, 1, 2, 3, 4)
|
||||
}
|
||||
|
||||
if ay, err := ledis.Values(c.Do(cmd, "4", "count", 6)); err != nil {
|
||||
if ay, err := ledis.Values(c.Do("XSCAN", tp, "4", "count", 6)); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(ay) != 2 {
|
||||
t.Fatal(len(ay))
|
||||
|
@ -76,29 +76,6 @@ 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) {
|
||||
for i := 0; i < 10; i++ {
|
||||
if _, err := c.Do("set", fmt.Sprintf("%d", i), []byte("value")); err != nil {
|
||||
|
@ -106,50 +83,95 @@ func testKVScan(t *testing.T, c *ledis.Client) {
|
|||
}
|
||||
}
|
||||
|
||||
checkScan(t, c, "xscan")
|
||||
checkRevScan(t, c, "xrevscan")
|
||||
checkScan(t, c, "KV")
|
||||
}
|
||||
|
||||
func testHashScan(t *testing.T, c *ledis.Client) {
|
||||
func testHashKeyScan(t *testing.T, c *ledis.Client) {
|
||||
for i := 0; i < 10; i++ {
|
||||
if _, err := c.Do("hset", fmt.Sprintf("%d", i), fmt.Sprintf("%d", i), []byte("value")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
checkScan(t, c, "xhscan")
|
||||
checkRevScan(t, c, "xhrevscan")
|
||||
checkScan(t, c, "HASH")
|
||||
}
|
||||
|
||||
func testListScan(t *testing.T, c *ledis.Client) {
|
||||
func testListKeyScan(t *testing.T, c *ledis.Client) {
|
||||
for i := 0; i < 10; i++ {
|
||||
if _, err := c.Do("lpush", fmt.Sprintf("%d", i), fmt.Sprintf("%d", i)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
checkScan(t, c, "xlscan")
|
||||
checkRevScan(t, c, "xlrevscan")
|
||||
checkScan(t, c, "LIST")
|
||||
}
|
||||
|
||||
func testZSetScan(t *testing.T, c *ledis.Client) {
|
||||
func testZSetKeyScan(t *testing.T, c *ledis.Client) {
|
||||
for i := 0; i < 10; i++ {
|
||||
if _, err := c.Do("zadd", fmt.Sprintf("%d", i), i, []byte("value")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
checkScan(t, c, "zxscan")
|
||||
checkRevScan(t, c, "zxrevscan")
|
||||
checkScan(t, c, "ZSET")
|
||||
}
|
||||
|
||||
func testSetScan(t *testing.T, c *ledis.Client) {
|
||||
func testSetKeyScan(t *testing.T, c *ledis.Client) {
|
||||
for i := 0; i < 10; i++ {
|
||||
if _, err := c.Do("sadd", fmt.Sprintf("%d", i), fmt.Sprintf("%d", i)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
checkScan(t, c, "xsscan")
|
||||
checkRevScan(t, c, "xsrevscan")
|
||||
checkScan(t, c, "SET")
|
||||
}
|
||||
|
||||
func TestHashScan(t *testing.T) {
|
||||
c := getTestConn()
|
||||
defer c.Close()
|
||||
|
||||
key := "scan_hash"
|
||||
c.Do("HMSET", key, "a", 1, "b", 2)
|
||||
|
||||
if ay, err := ledis.Values(c.Do("XHSCAN", key, "")); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(ay) != 2 {
|
||||
t.Fatal(len(ay))
|
||||
} else {
|
||||
checkScanValues(t, ay[1], "a", 1, "b", 2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetScan(t *testing.T) {
|
||||
c := getTestConn()
|
||||
defer c.Close()
|
||||
|
||||
key := "scan_set"
|
||||
c.Do("SADD", key, "a", "b")
|
||||
|
||||
if ay, err := ledis.Values(c.Do("XSSCAN", key, "")); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(ay) != 2 {
|
||||
t.Fatal(len(ay))
|
||||
} else {
|
||||
checkScanValues(t, ay[1], "a", "b")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestZSetScan(t *testing.T) {
|
||||
c := getTestConn()
|
||||
defer c.Close()
|
||||
|
||||
key := "scan_zset"
|
||||
c.Do("ZADD", key, 1, "a", 2, "b")
|
||||
|
||||
if ay, err := ledis.Values(c.Do("XZSCAN", key, "")); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(ay) != 2 {
|
||||
t.Fatal(len(ay))
|
||||
} else {
|
||||
checkScanValues(t, ay[1], "a", 1, "b", 2)
|
||||
}
|
||||
|
||||
}
|
|
@ -262,14 +262,6 @@ func spersistCommand(c *client) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func sxscanCommand(c *client) error {
|
||||
return xscanGeneric(c, c.db.SScan)
|
||||
}
|
||||
|
||||
func sxrevscanCommand(c *client) error {
|
||||
return xscanGeneric(c, c.db.SRevScan)
|
||||
}
|
||||
|
||||
func xsexistsCommand(c *client) error {
|
||||
args := c.args
|
||||
if len(args) != 1 {
|
||||
|
@ -301,10 +293,6 @@ func init() {
|
|||
register("sexpireat", sexpireAtCommand)
|
||||
register("sttl", sttlCommand)
|
||||
register("spersist", spersistCommand)
|
||||
register("sxscan", sxscanCommand)
|
||||
register("sxrevscan", sxrevscanCommand)
|
||||
register("xsscan", sxscanCommand)
|
||||
register("xsrevscan", sxrevscanCommand)
|
||||
register("xsexists", xsexistsCommand)
|
||||
|
||||
}
|
||||
|
|
|
@ -641,14 +641,6 @@ func zinterstoreCommand(c *client) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func zxscanCommand(c *client) error {
|
||||
return xscanGeneric(c, c.db.ZScan)
|
||||
}
|
||||
|
||||
func zxrevscanCommand(c *client) error {
|
||||
return xscanGeneric(c, c.db.ZRevScan)
|
||||
}
|
||||
|
||||
func zparseMemberRange(minBuf []byte, maxBuf []byte) (min []byte, max []byte, rangeType uint8, err error) {
|
||||
rangeType = store.RangeClose
|
||||
if strings.ToLower(hack.String(minBuf)) == "-" {
|
||||
|
@ -815,9 +807,5 @@ func init() {
|
|||
register("zexpireat", zexpireAtCommand)
|
||||
register("zttl", zttlCommand)
|
||||
register("zpersist", zpersistCommand)
|
||||
register("zxscan", zxscanCommand)
|
||||
register("zxrevscan", zxrevscanCommand)
|
||||
register("xzscan", zxscanCommand)
|
||||
register("xzrevscan", zxrevscanCommand)
|
||||
register("xzexists", xzexistsCommand)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package server
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/siddontang/ledisdb/ledis"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -26,12 +27,11 @@ var (
|
|||
)
|
||||
|
||||
const (
|
||||
KV = iota
|
||||
LIST
|
||||
HASH
|
||||
SET
|
||||
ZSET
|
||||
BIT
|
||||
KV ledis.DataType = ledis.KV
|
||||
LIST = ledis.LIST
|
||||
HASH = ledis.HASH
|
||||
SET = ledis.SET
|
||||
ZSET = ledis.ZSET
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
Loading…
Reference in New Issue