mirror of https://github.com/ledisdb/ledisdb.git
update scan support
This commit is contained in:
parent
cb5a61240d
commit
fa6adeabee
|
@ -526,5 +526,41 @@
|
|||
"arguments": "-",
|
||||
"group": "Transaction",
|
||||
"readonly": false
|
||||
},
|
||||
|
||||
"SCAN": {
|
||||
"arguments": "key [MATCH match] [COUNT count]",
|
||||
"group": "KV",
|
||||
"readonly": false
|
||||
},
|
||||
|
||||
"HSCAN": {
|
||||
"arguments": "key [MATCH match] [COUNT count]",
|
||||
"group": "Hash",
|
||||
"readonly": false
|
||||
},
|
||||
|
||||
"LSCAN": {
|
||||
"arguments": "key [MATCH match] [COUNT count]",
|
||||
"group": "List",
|
||||
"readonly": false
|
||||
},
|
||||
|
||||
"SSCAN": {
|
||||
"arguments": "key [MATCH match] [COUNT count]",
|
||||
"group": "Set",
|
||||
"readonly": false
|
||||
},
|
||||
|
||||
"ZSCAN": {
|
||||
"arguments": "key [MATCH match] [COUNT count]",
|
||||
"group": "ZSet",
|
||||
"readonly": false
|
||||
},
|
||||
|
||||
"BSCAN": {
|
||||
"arguments": "key [MATCH match] [COUNT count]",
|
||||
"group": "Bitmap",
|
||||
"readonly": false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ Table of Contents
|
|||
- [EXPIREAT key timestamp](#expireat-key-timestamp)
|
||||
- [TTL key](#ttl-key)
|
||||
- [PERSIST key](#persist-key)
|
||||
- [SCAN key [MATCH match] [COUNT count]](#scan-key-match-match-count-count)
|
||||
- [Hash](#hash)
|
||||
- [HDEL key field [field ...]](#hdel-key-field-field-)
|
||||
- [HEXISTS key field](#hexists-key-field)
|
||||
|
@ -44,6 +45,7 @@ Table of Contents
|
|||
- [HEXPIREAT key timestamp](#hexpireat-key-timestamp)
|
||||
- [HTTL key](#httl-key)
|
||||
- [HPERSIST key](#hpersist-key)
|
||||
- [HSCAN key [MATCH match] [COUNT count]](#hscan-key-match-match-count-count)
|
||||
- [List](#list)
|
||||
- [LINDEX key index](#lindex-key-index)
|
||||
- [LLEN key](#llen-key)
|
||||
|
@ -58,6 +60,7 @@ Table of Contents
|
|||
- [LEXPIREAT key timestamp](#lexpireat-key-timestamp)
|
||||
- [LTTL key](#lttl-key)
|
||||
- [LPERSIST key](#lpersist-key)
|
||||
- [LSCAN key [MATCH match] [COUNT count]](#lscan-key-match-match-count-count)
|
||||
- [Set](#set)
|
||||
- [SADD key member [member ...]](#sadd-key-member-member-)
|
||||
- [SCARD key](#scard-key)
|
||||
|
@ -76,7 +79,7 @@ Table of Contents
|
|||
- [SEXPIREAT key timestamp](#sexpireat-key-timestamp)
|
||||
- [STTL key](#sttl-key)
|
||||
- [SPERSIST key](#spersist-key)
|
||||
|
||||
- [SSCAN key [MATCH match] [COUNT count]](#sscan-key-match-match-count-count)
|
||||
- [ZSet](#zset)
|
||||
- [ZADD key score member [score member ...]](#zadd-key-score-member-score-member-)
|
||||
- [ZCARD key](#zcard-key)
|
||||
|
@ -102,9 +105,8 @@ 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)
|
||||
|
||||
- [ZSCAN key [MATCH match] [COUNT count]](#zscan-key-match-match-count-count)
|
||||
- [Bitmap](#bitmap)
|
||||
|
||||
- [BGET key](#bget-key)
|
||||
- [BGETBIT key offset](#bgetbit-key-offset)
|
||||
- [BSETBIT key offset value](#bsetbit-key-offset-value)
|
||||
|
@ -115,7 +117,7 @@ Table of Contents
|
|||
- [BEXPIREAT key timestamp](#bexpireat-key-timestamp)
|
||||
- [BTTL key](#bttl-key)
|
||||
- [BPERSIST key](#bpersist-key)
|
||||
|
||||
- [BSCAN key [MATCH match] [COUNT count]](#bscan-key-match-match-count-count)
|
||||
- [Replication](#replication)
|
||||
- [SLAVEOF host port](#slaveof-host-port)
|
||||
- [FULLSYNC](#fullsync)
|
||||
|
@ -459,6 +461,43 @@ ledis> TTL mykey
|
|||
(integer) -1
|
||||
```
|
||||
|
||||
### SCAN 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>scan ""
|
||||
1) ""
|
||||
2) ["a" "b" "c"]
|
||||
ledis>scan "" count 1
|
||||
1) "a"
|
||||
2) ["a"]
|
||||
ledis>scan "a" count 1
|
||||
1) "b"
|
||||
2) ["b"]
|
||||
ledis>scan "b" count 1
|
||||
1) "c"
|
||||
2) ["c"]
|
||||
ledis>scan "c" count 1
|
||||
1) ""
|
||||
2) []
|
||||
```
|
||||
|
||||
## Hash
|
||||
|
||||
|
@ -823,6 +862,11 @@ ledis> HPERSIST not_exists_key
|
|||
(integer) 0
|
||||
```
|
||||
|
||||
### HSCAN key [MATCH match] [COUNT count]
|
||||
|
||||
Iterate Hash keys incrementally.
|
||||
|
||||
See `SCAN` for more information.
|
||||
|
||||
## List
|
||||
|
||||
|
@ -1115,6 +1159,12 @@ ledis> LPERSIST b
|
|||
(integer) 0
|
||||
```
|
||||
|
||||
### LSCAN key [MATCH match] [COUNT count]
|
||||
|
||||
Iterate list keys incrementally.
|
||||
|
||||
See `SCAN` for more information.
|
||||
|
||||
|
||||
## Set
|
||||
|
||||
|
@ -1537,6 +1587,13 @@ ledis> STTL key
|
|||
(integer) -1
|
||||
```
|
||||
|
||||
### SSCAN key [MATCH match] [COUNT count]
|
||||
|
||||
Iterate Set keys incrementally.
|
||||
|
||||
See `SCAN` for more information.
|
||||
|
||||
|
||||
## ZSet
|
||||
|
||||
### ZADD key score member [score member ...]
|
||||
|
@ -2156,6 +2213,12 @@ ledis> ZRANGE out 0 -1 WITHSCORES
|
|||
4) "10"
|
||||
```
|
||||
|
||||
### ZSCAN key [MATCH match] [COUNT count]
|
||||
|
||||
Iterate ZSet keys incrementally.
|
||||
|
||||
See `SCAN` for more information.
|
||||
|
||||
|
||||
## Bitmap
|
||||
|
||||
|
@ -2316,6 +2379,13 @@ ledis> BCOUNT flag 5 6
|
|||
(refer to [PERSIST](#persist-key) api for other types)
|
||||
|
||||
|
||||
### BSCAN key [MATCH match] [COUNT count]
|
||||
|
||||
Iterate Bitmap keys incrementally.
|
||||
|
||||
See `SCAN` for more information.
|
||||
|
||||
|
||||
## Replication
|
||||
|
||||
### SLAVEOF host port
|
||||
|
|
|
@ -273,19 +273,19 @@ func bpersistCommand(c *client) error {
|
|||
}
|
||||
|
||||
func bscanCommand(c *client) error {
|
||||
key, inclusive, match, count, err := parseScanArgs(c)
|
||||
key, match, count, err := parseScanArgs(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ay, err := c.db.BScan(key, count, inclusive, match); err != nil {
|
||||
if ay, err := c.db.BScan(key, count, false, match); err != nil {
|
||||
return err
|
||||
} else {
|
||||
data := make([]interface{}, 2)
|
||||
if len(ay) < count {
|
||||
data[0] = ""
|
||||
data[0] = []byte("")
|
||||
} else {
|
||||
data[0] = append([]byte{'('}, ay[len(ay)-1]...)
|
||||
data[0] = ay[len(ay)-1]
|
||||
}
|
||||
data[1] = ay
|
||||
c.resp.writeArray(data)
|
||||
|
|
|
@ -293,19 +293,19 @@ func hpersistCommand(c *client) error {
|
|||
}
|
||||
|
||||
func hscanCommand(c *client) error {
|
||||
key, inclusive, match, count, err := parseScanArgs(c)
|
||||
key, match, count, err := parseScanArgs(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ay, err := c.db.HScan(key, count, inclusive, match); err != nil {
|
||||
if ay, err := c.db.HScan(key, count, false, match); err != nil {
|
||||
return err
|
||||
} else {
|
||||
data := make([]interface{}, 2)
|
||||
if len(ay) < count {
|
||||
data[0] = ""
|
||||
data[0] = []byte("")
|
||||
} else {
|
||||
data[0] = append([]byte{'('}, ay[len(ay)-1]...)
|
||||
data[0] = ay[len(ay)-1]
|
||||
}
|
||||
data[1] = ay
|
||||
c.resp.writeArray(data)
|
||||
|
|
|
@ -275,10 +275,9 @@ func persistCommand(c *client) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func parseScanArgs(c *client) (key []byte, inclusive bool, match string, count int, err error) {
|
||||
func parseScanArgs(c *client) (key []byte, match string, count int, err error) {
|
||||
args := c.args
|
||||
count = 10
|
||||
inclusive = false
|
||||
|
||||
switch len(args) {
|
||||
case 0:
|
||||
|
@ -287,14 +286,6 @@ func parseScanArgs(c *client) (key []byte, inclusive bool, match string, count i
|
|||
case 1, 3, 5:
|
||||
key = args[0]
|
||||
break
|
||||
case 2, 4, 6:
|
||||
key = args[0]
|
||||
if strings.ToLower(ledis.String(args[len(args)-1])) != "inclusive" {
|
||||
err = ErrCmdParams
|
||||
return
|
||||
}
|
||||
inclusive = true
|
||||
args = args[0 : len(args)-1]
|
||||
default:
|
||||
err = ErrCmdParams
|
||||
return
|
||||
|
@ -330,12 +321,12 @@ func parseScanArgs(c *client) (key []byte, inclusive bool, match string, count i
|
|||
}
|
||||
|
||||
func scanCommand(c *client) error {
|
||||
key, inclusive, match, count, err := parseScanArgs(c)
|
||||
key, match, count, err := parseScanArgs(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ay, err := c.db.Scan(key, count, inclusive, match); err != nil {
|
||||
if ay, err := c.db.Scan(key, count, false, match); err != nil {
|
||||
return err
|
||||
} else {
|
||||
data := make([]interface{}, 2)
|
||||
|
|
|
@ -229,19 +229,19 @@ func lpersistCommand(c *client) error {
|
|||
}
|
||||
|
||||
func lscanCommand(c *client) error {
|
||||
key, inclusive, match, count, err := parseScanArgs(c)
|
||||
key, match, count, err := parseScanArgs(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ay, err := c.db.LScan(key, count, inclusive, match); err != nil {
|
||||
if ay, err := c.db.LScan(key, count, false, match); err != nil {
|
||||
return err
|
||||
} else {
|
||||
data := make([]interface{}, 2)
|
||||
if len(ay) < count {
|
||||
data[0] = ""
|
||||
data[0] = []byte("")
|
||||
} else {
|
||||
data[0] = append([]byte{'('}, ay[len(ay)-1]...)
|
||||
data[0] = ay[len(ay)-1]
|
||||
}
|
||||
data[1] = ay
|
||||
c.resp.writeArray(data)
|
||||
|
|
|
@ -263,19 +263,19 @@ func spersistCommand(c *client) error {
|
|||
}
|
||||
|
||||
func sscanCommand(c *client) error {
|
||||
key, inclusive, match, count, err := parseScanArgs(c)
|
||||
key, match, count, err := parseScanArgs(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ay, err := c.db.SScan(key, count, inclusive, match); err != nil {
|
||||
if ay, err := c.db.SScan(key, count, false, match); err != nil {
|
||||
return err
|
||||
} else {
|
||||
data := make([]interface{}, 2)
|
||||
if len(ay) < count {
|
||||
data[0] = ""
|
||||
data[0] = []byte("")
|
||||
} else {
|
||||
data[0] = append([]byte{'('}, ay[len(ay)-1]...)
|
||||
data[0] = ay[len(ay)-1]
|
||||
}
|
||||
data[1] = ay
|
||||
c.resp.writeArray(data)
|
||||
|
|
|
@ -639,19 +639,19 @@ func zinterstoreCommand(c *client) error {
|
|||
}
|
||||
|
||||
func zscanCommand(c *client) error {
|
||||
key, inclusive, match, count, err := parseScanArgs(c)
|
||||
key, match, count, err := parseScanArgs(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ay, err := c.db.ZScan(key, count, inclusive, match); err != nil {
|
||||
if ay, err := c.db.ZScan(key, count, false, match); err != nil {
|
||||
return err
|
||||
} else {
|
||||
data := make([]interface{}, 2)
|
||||
if len(ay) < count {
|
||||
data[0] = ""
|
||||
data[0] = []byte("")
|
||||
} else {
|
||||
data[0] = append([]byte{'('}, ay[len(ay)-1]...)
|
||||
data[0] = ay[len(ay)-1]
|
||||
}
|
||||
data[1] = ay
|
||||
c.resp.writeArray(data)
|
||||
|
|
|
@ -29,6 +29,12 @@ func TestScan(t *testing.T) {
|
|||
defer c.Close()
|
||||
|
||||
testKVScan(t, c)
|
||||
testHashScan(t, c)
|
||||
testListScan(t, c)
|
||||
testZSetScan(t, c)
|
||||
testSetScan(t, c)
|
||||
testBitScan(t, c)
|
||||
|
||||
}
|
||||
|
||||
func checkScanValues(t *testing.T, ay interface{}, values ...int) {
|
||||
|
@ -48,14 +54,8 @@ func checkScanValues(t *testing.T, ay interface{}, values ...int) {
|
|||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if ay, err := ledis.Values(c.Do("scan", "", "count", 5)); err != nil {
|
||||
func checkScan(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))
|
||||
|
@ -65,7 +65,7 @@ func testKVScan(t *testing.T, c *ledis.Client) {
|
|||
checkScanValues(t, ay[1], 0, 1, 2, 3, 4)
|
||||
}
|
||||
|
||||
if ay, err := ledis.Values(c.Do("scan", "4", "count", 6)); err != nil {
|
||||
if ay, err := ledis.Values(c.Do(cmd, "4", "count", 6)); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(ay) != 2 {
|
||||
t.Fatal(len(ay))
|
||||
|
@ -75,14 +75,64 @@ func testKVScan(t *testing.T, c *ledis.Client) {
|
|||
checkScanValues(t, ay[1], 5, 6, 7, 8, 9)
|
||||
}
|
||||
|
||||
if ay, err := ledis.Values(c.Do("scan", "4", "count", 6, "inclusive")); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(ay) != 2 {
|
||||
t.Fatal(len(ay))
|
||||
} else if n := ay[0].([]byte); string(n) != "9" {
|
||||
t.Fatal(string(n))
|
||||
} else {
|
||||
checkScanValues(t, ay[1], 4, 5, 6, 7, 8, 9)
|
||||
}
|
||||
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
checkScan(t, c, "scan")
|
||||
}
|
||||
|
||||
func testHashScan(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, "hscan")
|
||||
}
|
||||
|
||||
func testListScan(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, "lscan")
|
||||
}
|
||||
|
||||
func testZSetScan(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, "zscan")
|
||||
}
|
||||
|
||||
func testSetScan(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, "sscan")
|
||||
}
|
||||
|
||||
func testBitScan(t *testing.T, c *ledis.Client) {
|
||||
for i := 0; i < 10; i++ {
|
||||
if _, err := c.Do("bsetbit", fmt.Sprintf("%d", i), 1024, 1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
checkScan(t, c, "bscan")
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue