update scan support

This commit is contained in:
siddontang 2014-08-27 10:29:17 +08:00
parent cb5a61240d
commit fa6adeabee
9 changed files with 200 additions and 53 deletions

View File

@ -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
}
}

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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")
}