Merge branch 'set-feature' into develop

This commit is contained in:
holys 2014-08-15 18:09:55 +08:00
commit 22161f91ba
13 changed files with 2460 additions and 138 deletions

View File

@ -1,12 +1,12 @@
# LedisDB
Ledisdb is a high performance NoSQL like Redis written by go. It supports some advanced data structure like kv, list, hash, zset, bitmap, and may be alternative for Redis.
Ledisdb is a high performance NoSQL like Redis written by go. It supports some advanced data structure like kv, list, hash, zset, bitmap,set, and may be alternative for Redis.
LedisDB now supports multiple databases as backend to store data, you can test and choose the proper one for you.
## Features
+ Rich advanced data structure: KV, List, Hash, ZSet, Bitmap.
+ Rich advanced data structure: KV, List, Hash, ZSet, Bitmap, Set.
+ Stores lots of data, over the memory limit.
+ Various backend database to use: LevelDB, goleveldb, LMDB, RocksDB, BoltDB, HyperLevelDB.
+ Supports expiration and ttl.

View File

@ -1,56 +1,96 @@
//This file was generated by ./generate.py on Fri Aug 15 2014 16:40:03 +0800
package main
var helpCommands = [][]string{
{"BCOUNT", "key [start end]", "Bitmap"},
{"BDELETE", "key", "ZSet"},
{"BEXPIRE", "key seconds", "Bitmap"},
{"BEXPIREAT", "key timestamp", "Bitmap"},
{"BGET", "key", "Bitmap"},
{"BGETBIT", "key offset", "Bitmap"},
{"BMSETBIT", "key offset value [offset value ...]", "Bitmap"},
{"BOPT", "operation destkey key [key ...]", "Bitmap"},
{"BPERSIST", "key", "Bitmap"},
{"BSETBIT", "key offset value", "Bitmap"},
{"BTTL", "key", "Bitmap"},
{"DECR", "key", "KV"},
{"DECRBY", "key decrement", "KV"},
{"DEL", "key [key ...]", "KV"},
{"ECHO", "message", "Server"},
{"EXISTS", "key", "KV"},
{"EXPIRE", "key seconds", "KV"},
{"EXPIREAT", "key timestamp", "KV"},
{"FULLSYNC", "-", "Replication"},
{"GET", "key", "KV"},
{"GETSET", " key value", "KV"},
{"INCR", "key", "KV"},
{"INCRBY", "key increment", "KV"},
{"MGET", "key [key ...]", "KV"},
{"MSET", "key value [key value ...]", "KV"},
{"SET", "key value", "KV"},
{"SETNX", "key value", "KV"},
{"TTL", "key", "KV"},
{"PERSIST", "key", "KV"},
{"HCLEAR", "key", "Hash"},
{"HDEL", "key field [field ...]", "Hash"},
{"HEXISTS", "key field", "Hash"},
{"HEXPIRE", "key seconds", "Hash"},
{"HEXPIREAT", "key timestamp", "Hash"},
{"HGET", "key field", "Hash"},
{"HGETALL", "key", "Hash"},
{"HINCRBY", "key field increment", "Hash"},
{"HKEYS", "key", "Hash"},
{"HLEN", "key", "Hash"},
{"HMCLEAR", "key [key ...]", "Hash"},
{"HMGET", "key field [field ...]", "Hash"},
{"HMSET", "key field value [field value ...]", "Hash"},
{"HSET", "key field value", "Hash"},
{"HVALS", "key", "Hash"},
{"HCLEAR", "key", "Hash"},
{"HMCLEAR", "key [key ...]", "Hash"},
{"HEXPIRE", "key seconds", "Hash"},
{"HEXPIREAT", "key timestamp", "Hash"},
{"HTTL", "key", "Hash"},
{"HPERSIST", "key", "Hash"},
{"HSET", "key field value", "Hash"},
{"HTTL", "key", "Hash"},
{"HVALS", "key", "Hash"},
{"INCR", "key", "KV"},
{"INCRBY", "key increment", "KV"},
{"LCLEAR", "key", "List"},
{"LEXPIRE", "key seconds", "List"},
{"LEXPIREAT", "key timestamp", "List"},
{"LINDEX", "key index", "List"},
{"LLEN", "key", "List"},
{"LMCLEAR", "key [key ...]", "List"},
{"LPERSIST", "key", "List"},
{"LPOP", "key", "List"},
{"LPUSH", "key value [value ...]", "List"},
{"LRANGE", "key start stop", "List"},
{"LTTL", "key", "List"},
{"MGET", "key [key ...]", "KV"},
{"MSET", "key value [key value ...]", "KV"},
{"PERSIST", "key", "KV"},
{"PING", "-", "Server"},
{"RPOP", "key", "List"},
{"RPUSH", "key value [value ...]", "List"},
{"LCLEAR", "key", "List"},
{"LMCLEAR", "key [key ...]", "List"},
{"LEXPIRE", "key seconds", "List"},
{"LEXPIREAT", "key timestamp", "List"},
{"LTTL", "key", "List"},
{"LPERSIST", "key", "List"},
{"SADD", "key member [member ...]", "Set"},
{"SCARD", "key", "Set"},
{"SCLEAR", "key", "Set"},
{"SDIFF", "key [key ...]", "Set"},
{"SDIFFSTORE", "destination key [key ...]", "Set"},
{"SELECT", "index", "Server"},
{"SET", "key value", "KV"},
{"SETNX", "key value", "KV"},
{"SEXPIRE", "key seconds", "Set"},
{"SEXPIREAT", "key timestamp", "Set"},
{"SINTER", "key [key ...]", "Set"},
{"SINTERSTORE", "destination key [key ...]", "Set"},
{"SISMEMBER", "key member", "Set"},
{"SLAVEOF", "host port", "Replication"},
{"SMCLEAR", "key [key ...]", "Set"},
{"SMEMBERS", "key", "Set"},
{"SPERSIST", "key", "Set"},
{"SREM", "key member [member ...]", "Set"},
{"STTL", "key", "Set"},
{"SUNION", "key [key ...]", "Set"},
{"SUNIONSTORE", "destination key [key ...]", "Set"},
{"SYNC", "index offset", "Replication"},
{"TTL", "key", "KV"},
{"ZADD", "key score member [score member ...]", "ZSet"},
{"ZCARD", "key", "ZSet"},
{"ZCLEAR", "key", "ZSet"},
{"ZCOUNT", "key min max", "ZSet"},
{"ZEXPIRE", "key seconds", "ZSet"},
{"ZEXPIREAT", "key timestamp", "ZSet"},
{"ZINCRBY", "key increment member", "ZSet"},
{"ZMCLEAR", "key [key ...]", "ZSet"},
{"ZPERSIST", "key", "ZSet"},
{"ZRANGE", "key start stop [WITHSCORES]", "ZSet"},
{"ZRANGEBYSCORE", "key min max [WITHSCORES] [LIMIT offset count]", "ZSet"},
{"ZRANK", "key member", "ZSet"},
@ -61,27 +101,5 @@ var helpCommands = [][]string{
{"ZREVRANGEBYSCORE", "key max min [WITHSCORES][LIMIT offset count]", "ZSet"},
{"ZREVRANK", "key member", "ZSet"},
{"ZSCORE", "key member", "ZSet"},
{"ZCLEAR", "key", "ZSet"},
{"ZMCLEAR", "key [key ...]", "ZSet"},
{"ZEXPIRE", "key seconds", "ZSet"},
{"ZEXPIREAT", "key timestamp", "ZSet"},
{"ZTTL", "key", "ZSet"},
{"ZPERSIST", "key", "ZSet"},
{"BDELETE", "key", "ZSet"},
{"BGET", "key", "Bitmap"},
{"BGETBIT", "key offset", "Bitmap"},
{"BSETBIT", "key offset value", "Bitmap"},
{"BMSETBIT", "key offset value [offset value ...]", "Bitmap"},
{"BOPT", "operation destkey key [key ...]", "Bitmap"},
{"BCOUNT", "key [start end]", "Bitmap"},
{"BEXPIRE", "key seconds", "Bitmap"},
{"BEXPIREAT", "key timestamp", "Bitmap"},
{"BTTL", "key", "Bitmap"},
{"BPERSIST", "key", "Bitmap"},
{"SLAVEOF", "host port", "Replication"},
{"FULLSYNC", "-", "Replication"},
{"SYNC", "index offset", "Replication"},
{"PING", "-", "Server"},
{"ECHO", "message", "Server"},
{"SELECT", "index", "Server"},
}

View File

@ -310,6 +310,91 @@
"group": "Replication",
"readonly": false
},
"SADD" :{
"arguments": "key member [member ...]",
"group": "Set",
"readonly": false
},
"SCARD": {
"arguments": "key",
"group": "Set",
"readonly": true
},
"SDIFF": {
"arguments": "key [key ...]",
"group": "Set",
"readonly": true
},
"SDIFFSTORE": {
"arguments": "destination key [key ...]",
"group": "Set",
"readonly": false
},
"SINTER": {
"arguments": "key [key ...]",
"group": "Set",
"readonly": true
},
"SINTERSTORE": {
"arguments": "destination key [key ...]",
"group": "Set",
"readonly": false
},
"SISMEMBER": {
"arguments": "key member",
"group": "Set",
"readonly": true
},
"SMEMBERS": {
"arguments": "key",
"group": "Set",
"readonly": true
},
"SREM": {
"arguments": "key member [member ...]",
"group": "Set",
"readonly": false
},
"SUNION": {
"arguments": "key [key ...]",
"group": "Set",
"readonly": true
},
"SUNIONSTORE": {
"arguments": "destination key [key ...]",
"group": "Set",
"readonly": false
},
"SCLEAR": {
"arguments": "key",
"group": "Set",
"readonly": false
},
"SMCLEAR": {
"arguments": "key [key ...]",
"group": "Set",
"readonly": false
},
"SEXPIRE": {
"arguments": "key seconds",
"group": "Set",
"readonly": false
},
"SEXPIREAT": {
"arguments": "key timestamp",
"group": "Set",
"readonly": false
},
"STTL": {
"arguments": "key",
"group": "Set",
"readonly": true
},
"SPERSIST": {
"arguments": "key",
"group": "Set",
"readonly": false
},
"TTL": {
"arguments": "key",
"group": "KV",

View File

@ -58,6 +58,25 @@ Table of Contents
- [LEXPIREAT key timestamp](#lexpireat-key-timestamp)
- [LTTL key](#lttl-key)
- [LPERSIST key](#lpersist-key)
- [Set](#set)
- [SADD key member [member ...]](#sadd-key-member-member-)
- [SCARD key](#scard-key)
- [SDIFF key [key ...]](#sdiff-key-key-)
- [SDIFFSTORE destination key [key ...]](#sdiffstore-destination-key-key-)
- [SINTER key [key ...]](#sinter-key-key-)
- [SINTERSTORE destination key [key ...]](#sinterstore-destination-key-key-)
- [SISMEMBER key member](#sismember-key-member)
- [SMEMBERS key](#smembers-key)
- [SREM key member [member]](#srem-key-member-member-)
- [SUNION key [key ...]](#sunion-key-key-)
- [SUNIONSTORE destination key [key ...]](#sunionstore-destination-key-key-)
- [SCLEAR key](#sclear-key)
- [SMCLEAR key [key...]](#smclear-key-key)
- [SEXPIRE key seconds](#sexpire-key-seconds)
- [SEXPIREAT key timestamp](#sexpireat-key-timestamp)
- [STTL key](#sttl-key)
- [SPERSIST key](#spersist-key)
- [ZSet](#zset)
- [ZADD key score member [score member ...]](#zadd-key-score-member-score-member-)
- [ZCARD key](#zcard-key)
@ -665,7 +684,7 @@ ledis> HVALS myhash
### HCLEAR key
Deletes the specified hash keys
Deletes the specified hash key
**Return value**
@ -1093,6 +1112,427 @@ ledis> LPERSIST b
```
## Set
### SADD key member [member ...]
Add the specified members to the set stored at key. Specified members that are already a member of this set are ignored. If key does not exist, a new set is created before adding the specified members.
**Return value**
int64: the number of elements that were added to the set, not including all the elements already present into the set.
**Examples**
```
ledis> SADD myset hello
(integer) 1
ledis> SADD myset world
(integer) 1
ledis> SADD myset hello
(integer) 0
ledis> SMEMBERS myset
1) "hello"
2) "world"
```
### SCARD key
Returns the set cardinality (number of elements) of the set stored at key.
**Return value**
int64: the cardinality (number of elements) of the set, or 0 if key does not exist.
**Examples**
```
ledis> SADD myset hello
(integer) 1
ledis> SADD myset world
(integer) 1
ledis> SADD myset hello
(integer) 0
ledis> SCARD myset
(integer) 2
```
### SDIFF key [key ...]
Returns the members of the set resulting from the difference between the first set and all the successive sets.
For example:
```
key1 = {a,b,c,d}
key2 = {c}
key3 = {a,c,e}
SDIFF key1 key2 key3 = {b,d}
```
Keys that do not exist are considered to be empty sets.
**Return value**
bulk: list with members of the resulting set.
**Examples**
```
ledis> SADD key1 a b c
(integer) 3
ledis> SADD key2 c d e
(integer) 3
ledis> SDIFF key1 key2
1) "a"
2) "b"
ledis> SDIFF key2 key1
1) "d"
2) "e"
```
### SDIFFSTORE destination key [key ...]
This command is equal to `SDIFF`, but instead of returning the resulting set, it is stored in destination.
If destination already exists, it is overwritten.
**Return value**
int64: the number of elements in the resulting set.
**Examples**
```
ledis> SADD key1 a b c
(integer) 3
ledis> SADD key2 c d e
(integer) 3
ledis> SDIFF key1 key2
1) "a"
2) "b"
ledis> SDIFFSTORE key key1 key2
(integer) 2
ledis> SMEMBERS key
1) "a"
2) "b"
```
### SINTER key [key ...]
Returns the members of the set resulting from the intersection of all the given sets.
For example:
```
key1 = {a,b,c,d}
key2 = {c}
key3 = {a,c,e}
SINTER key1 key2 key3 = {c}
```
Keys that do not exist are considered to be empty sets. With one of the keys being an empty set, the resulting set is also empty (since set intersection with an empty set always results in an empty set).
**Return value**
bulk: list with members of the resulting set.
**Examples**
```
ledis> SADD key1 a b c
(integer) 3
ledis> SADD key2 c d e
(integer) 3
ledis> SINTER key1 key2
1) "c"
ledis> SINTER key2 key_empty
(nil)
```
### SINTERSTORE destination key [key ...]
This command is equal to `SINTER`, but instead of returning the resulting set, it is stored in destination.
If destination already exists, it is overwritten.
**Return value**
int64: the number of elements in the resulting set.
**Examples**
```
ledis> SADD key1 a b c
(integer) 3
ledis> SADD key2 c d e
(integer) 3
ledis> SINTERSTORE key key1 key2
(integer) 1
ledis> SMEMBERS key
1) "c"
```
### SISMEMBER key member
Returns if member is a member of the set stored at key.
**Return value**
Int64 reply, specifically:
- 1 if the element is a member of the set.
- 0 if the element is not a member of the set, or if key does not exist.
**Examples**
```
ledis> SADD myset hello
(integer) 1
ledis> SISMEMBER myset hello
(integer) 1
ledis> SISMEMBER myset hell
(integer) 0
```
### SMEMBERS key
Returns all the members of the set value stored at key.
This has the same effect as running `SINTER` with one argument key.
**Return value**
bulk: all elements of the set.
**Examples**
```
ledis> SADD myset hello
(integer) 1
ledis> SADD myset world
(integer) 1
ledis> SMEMBERS myset
1) "hello"
2) "world"
```
### SREM key member [member ...]
Remove the specified members from the set stored at key. Specified members that are not a member of this set are ignored. If key does not exist, it is treated as an empty set and this command returns 0.
**Return value**
int64: the number of members that were removed from the set, not including non existing members.
**Examples**
```
ledis> SADD myset one
(integer) 1
ledis> SADD myset two
(integer) 1
ledis> SADD myset three
(integer) 1
ledis> SREM myset one
(integer) 1
ledis> SREM myset four
(integer) 0
ledis> SMEMBERS myset
1) "three"
2) "two"
```
### SUNION key [key ...]
Returns the members of the set resulting from the union of all the given sets.
For example:
```
key1 = {a,b,c,d}
key2 = {c}
key3 = {a,c,e}
SUNION key1 key2 key3 = {a,b,c,d,e}
```
Keys that do not exist are considered to be empty sets.
**Return value**
bulk: list with members of the resulting set.
**Examples**
```
ledis> SMEMBERS key1
1) "a"
2) "b"
3) "c"
ledis> SMEMBERS key2
1) "c"
2) "d"
3) "e"
ledis> SUNION key1 key2
1) "a"
2) "b"
3) "c"
4) "d"
5) "e"
```
### SUNIONSTORE destination key [key]
This command is equal to SUNION, but instead of returning the resulting set, it is stored in destination.
If destination already exists, it is overwritten.
**Return value**
int64: the number of elements in the resulting set.
**Examples**
```
ledis> SMEMBERS key1
1) "a"
2) "b"
3) "c"
ledis> SMEMBERS key2
1) "c"
2) "d"
3) "e"
ledis> SUNIONSTORE key key1 key2
(integer) 5
ledis> SMEMBERS key
1) "a"
2) "b"
3) "c"
4) "d"
5) "e"
```
### SCLEAR key
Deletes the specified set key
**Return value**
int64: the number of fields in the hash stored at key
**Examples**
```
ledis> SMEMBERS key
1) "a"
2) "b"
3) "c"
4) "d"
5) "e"
ledis> SCLEAR key
(integer) 5
```
### SMCLEAR key [key ...]
Deletes the specified set keys.
**Return value**
int64: the number of input keys
**Examples**
```
ledis> SMCLEAR key1 key2
(integer) 2
ledis> SMCLEAR em1 em2
(integer) 2
```
### SEXPIRE key seconds
Sets a set keys time to live in seconds, like expire similarly.
**Return value**
int64:
- 1 if the timeout was set
- 0 if key does not exist or the timeout could not be set
**Examples**
```
ledis> SADD key 1 2
(integer) 2
ledis> SEXPIRE key 100
(integer) 1
ledis> STTL key
(integer) 95
```
### SEXPIREAT key timestamp
Sets the expiration for a set key as a unix timestamp, like expireat similarly.
**Return value**
int64:
- 1 if the timeout was set
- 0 if key does not exist or the timeout could not be set
**Examples**
```
ledis> SADD key 1 2
(integer) 2
ledis> SEXPIREAT key 1408094999
(integer) 1
ledis> STTL key
(integer) 908
```
### STTL key
Returns the remaining time to live of a key that has a timeout. If the key was not set a timeout, -1 returns.
**Return value**
int64: TTL in seconds
**Examples**
```
ledis> SADD key 1 2
(integer) 2
ledis> SEXPIREAT key 1408094999
(integer) 1
ledis> STTL key
(integer) 908
```
### SPERSIST key
Remove the expiration from a set key, like persist similarly. Remove the existing timeout on key.
**Return value**
int64:
- 1 if the timeout was removed
- 0 if key does not exist or does not have an timeout
**Examples**
```
ledis> SEXPIREAT key 1408094999
(integer) 1
ledis> STTL key
(integer) 908
ledis> SPERSIST key
(integer) 1
ledis> STTL key
(integer) -1
```
## ZSet
### ZADD key score member [score member ...]
@ -1114,13 +1554,13 @@ The number of elements added to the sorted sets, **not** including elements alre
**Examples**
```
ledis> ZADD myset 1 'one'
ledis> ZADD myzset 1 'one'
(integer) 1
ledis> ZADD myset 1 'uno'
ledis> ZADD myzset 1 'uno'
(integer) 1
ledis> ZADD myset 2 'two' 3 'three'
ledis> ZADD myzset 2 'two' 3 'three'
(integer) 2
ledis> ZRANGE myset 0 -1 WITHSCORES
ledis> ZRANGE myzset 0 -1 WITHSCORES
1) "one"
2) "1"
3) "uno"
@ -1141,13 +1581,13 @@ int64: the cardinality (number of elements) of the sorted set, or `0` if key doe
**Examples**
```
edis > ZADD myset 1 'one'
edis > ZADD myzset 1 'one'
(integer) 1
ledis> ZADD myset 1 'uno'
ledis> ZADD myzset 1 'uno'
(integer) 1
ledis> ZADD myset 2 'two' 3 'three'
ledis> ZADD myzset 2 'two' 3 'three'
(integer) 2
ledis> ZRANGE myset 0 -1 WITHSCORES
ledis> ZRANGE myzset 0 -1 WITHSCORES
1) "one"
2) "1"
3) "uno"
@ -1156,7 +1596,7 @@ ledis> ZRANGE myset 0 -1 WITHSCORES
6) "2"
7) "three"
8) "3"
ledis> ZCARD myset
ledis> ZCARD myzset
(integer) 4
```
@ -1171,13 +1611,13 @@ int64: the number of elements in the specified score range.
**Examples**
```
ledis> ZADD myset 1 'one'
ledis> ZADD myzset 1 'one'
(integer) 1
ledis> ZADD myset 1 'uno'
ledis> ZADD myzset 1 'uno'
(integer) 1
ledis> ZADD myset 2 'two' 3 'three'
ledis> ZADD myzset 2 'two' 3 'three'
(integer) 2
ledis> ZRANGE myset 0 -1 WITHSCORES
ledis> ZRANGE myzset 0 -1 WITHSCORES
1) "one"
2) "1"
3) "uno"
@ -1186,9 +1626,9 @@ ledis> ZRANGE myset 0 -1 WITHSCORES
6) "2"
7) "three"
8) "3"
ledis> ZCOUNT myset -inf +inf
ledis> ZCOUNT myzset -inf +inf
(integer) 4
ledis> ZCOUNT myset (1 3
ledis> ZCOUNT myzset (1 3
(integer) 2
```
@ -1205,13 +1645,13 @@ bulk: the new score of member (an int64 number), represented as string.
**Examples**
```
ledis> ZADD myset 1 'one'
ledis> ZADD myzset 1 'one'
(integer) 1
ledis> ZADD myset 2 'two'
ledis> ZADD myzset 2 'two'
(integer) 1
ledis> ZINCRBY myset 2 'one'
ledis> ZINCRBY myzset 2 'one'
3
ledis> ZRANGE myset 0 -1 WITHSCORES
ledis> ZRANGE myzset 0 -1 WITHSCORES
1) "two"
2) "2"
3) "one"
@ -1228,19 +1668,19 @@ array: list of elements in the specified range (optionally with their scores).
**Examples**
```
ledis> ZADD myset 1 'one'
ledis> ZADD myzset 1 'one'
(integer) 1
ledis> ZADD myset 2 'two'
ledis> ZADD myzset 2 'two'
(integer) 1
ledis> ZADD myset 3 'three'
ledis> ZADD myzset 3 'three'
(integer) 1
ledis> ZRANGE myset 0 -1
ledis> ZRANGE myzset 0 -1
1) "one"
2) "two"
3) "three"
ledis> ZRANGE myset 2 3
ledis> ZRANGE myzset 2 3
1) "three"
ledis> ZRANGE myset -2 -1
ledis> ZRANGE myzset -2 -1
1) "two"
2) "three"
```
@ -1340,16 +1780,16 @@ The number of members removed from the sorted set, not including non existing me
**Examples**
```
ledis> ZADD myset 1 one 2 two 3 three 4 four
ledis> ZADD myzset 1 one 2 two 3 three 4 four
(integer) 3
ledis> ZRANGE myset 0 -1
ledis> ZRANGE myzset 0 -1
1) "one"
2) "two"
3) "three"
4) "four"
ledis> ZREM myset three
ledis> ZREM myzset three
(integer) 1
ledis> ZREM myset one four three
ledis> ZREM myzset one four three
(integer) 2
```
@ -1363,11 +1803,11 @@ int64: the number of elements removed.
**Examples**
```
ledis> ZADD myset 1 one 2 two 3 three 4 four
ledis> ZADD myzset 1 one 2 two 3 three 4 four
(integer) 3
ledis> ZREMRANGEBYRANK myset 0 2
ledis> ZREMRANGEBYRANK myzset 0 2
(integer) 3
ledis> ZRANGE myset 0 -1 WITHSCORES
ledis> ZRANGE myzset 0 -1 WITHSCORES
1) "four"
2) "4"
```
@ -1383,11 +1823,11 @@ int64: the number of elements removed.
**Examples**
```
ledis> ZADD myset 1 one 2 two 3 three 4 four
ledis> ZADD myzset 1 one 2 two 3 three 4 four
(integer) 4
ledis> ZREMRANGEBYSCORE myset -inf (2
ledis> ZREMRANGEBYSCORE myzset -inf (2
(integer) 1
ledis> ZRANGE myset 0 -1 WITHSCORES
ledis> ZRANGE myzset 0 -1 WITHSCORES
1) "two"
2) "2"
3) "three"
@ -1407,9 +1847,9 @@ array: list of elements in the specified range (optionally with their scores).
**Examples**
```
ledis> ZADD myset 1 one 2 two 3 three 4 four
ledis> ZADD myzset 1 one 2 two 3 three 4 four
(integer) 4
ledis> ZREVRANGE myset 0 -1
ledis> ZREVRANGE myzset 0 -1
1) "four"
2) "three"
3) "two"
@ -1428,21 +1868,21 @@ array: list of elements in the specified score range (optionally with their scor
**Examples**
```
ledis> ZADD myset 1 one 2 two 3 three 4 four
ledis> ZADD myzset 1 one 2 two 3 three 4 four
(integer) 4
ledis> ZREVRANGEBYSCORE myset +inf -inf
ledis> ZREVRANGEBYSCORE myzset +inf -inf
1) "four"
2) "three"
3) "two"
4) "one"
ledis> ZREVRANGEBYSCORE myset 2 1
ledis> ZREVRANGEBYSCORE myzset 2 1
1) "two"
2) "one"
ledis> ZREVRANGEBYSCORE myset 2 (1
ledis> ZREVRANGEBYSCORE myzset 2 (1
1) "two"
ledis> ZREVRANGEBYSCORE myset (2 (1
ledis> ZREVRANGEBYSCORE myzset (2 (1
(empty list or set)
ledis> ZREVRANGEBYSCORE myset +inf -inf WITHSCORES LIMIT 1 2
ledis> ZREVRANGEBYSCORE myzset +inf -inf WITHSCORES LIMIT 1 2
1) "three"
2) "3"
3) "two"
@ -1462,13 +1902,13 @@ Use ZRANK to get the rank of an element with the scores ordered from low to high
**Examples**
```
ledis> ZADD myset 1 one
ledis> ZADD myzset 1 one
(integer) 1
ledis> ZADD myset 2 two
ledis> ZADD myzset 2 two
(integer) 1
ledis> ZREVRANK myset one
ledis> ZREVRANK myzset one
(integer) 1
ledis> ZREVRANK myset three
ledis> ZREVRANK myzset three
(nil)
```
@ -1484,9 +1924,9 @@ bulk: the score of member (an `int64` number), represented as string.
**Examples**
```
ledis> ZADD myset 1 'one'
ledis> ZADD myzset 1 'one'
(integer) 1
ledis> ZSCORE myset 'one'
ledis> ZSCORE myzset 'one'
1
```
@ -1500,17 +1940,17 @@ int64: the number of members in the zset stored at key
**Examples**
```
ledis> ZADD myset 1 'one'
ledis> ZADD myzset 1 'one'
(integer) 1
ledis> ZADD myset 2 'two'
ledis> ZADD myzset 2 'two'
(integer) 1
ledis> ZADD myset 3 'three'
ledis> ZADD myzset 3 'three'
(integer) 1
ledis> ZRANGE myset 0 -1
ledis> ZRANGE myzset 0 -1
1) "one"
2) "two"
3) "three"
ledis> ZCLEAR myset
ledis> ZCLEAR myzset
(integer) 3
```
@ -1524,11 +1964,11 @@ int64: the number of input keys
**Examples**
```
ledis> ZADD myset1 1 'one'
ledis> ZADD myzset1 1 'one'
(integer) 1
ledis> ZADD myset2 2 'two'
ledis> ZADD myzset2 2 'two'
(integer) 1
ledis> ZMCLEAR myset1 myset2
ledis> ZMCLEAR myzset1 myzset2
(integer) 2
```
@ -1547,17 +1987,17 @@ int64:
**Examples**
```
ledis> ZADD myset 1 'one'
ledis> ZADD myzset 1 'one'
(integer) 1
ledis> ZEXPIRE myset 100
ledis> ZEXPIRE myzset 100
(integer) 1
ledis> ZTTL myset
ledis> ZTTL myzset
(integer) 97
ledis> ZPERSIST myset
ledis> ZPERSIST myzset
(integer) 1
ledis> ZTTL mset
(integer) -1
ledis> ZEXPIRE myset1 100
ledis> ZEXPIRE myzset1 100
(integer) 0
```
@ -1574,17 +2014,17 @@ int64:
**Examples**
```
ledis> ZADD myset 1 'one'
ledis> ZADD myzset 1 'one'
(integer) 1
ledis> ZEXPIREAT myset 1404149999
ledis> ZEXPIREAT myzset 1404149999
(integer) 1
ledis> ZTTL myset
ledis> ZTTL myzset
(integer) 7155
ledis> ZPERSIST myset
ledis> ZPERSIST myzset
(integer) 1
ledis> ZTTL mset
(integer) -1
ledis> ZEXPIREAT myset1 1404149999
ledis> ZEXPIREAT myzset1 1404149999
(integer) 0
```
@ -1599,13 +2039,13 @@ int64: TTL in seconds
**Examples**
```
ledis> ZADD myset 1 'one'
ledis> ZADD myzset 1 'one'
(integer) 1
ledis> ZEXPIRE myset 100
ledis> ZEXPIRE myzset 100
(integer) 1
ledis> ZTTL myset
ledis> ZTTL myzset
(integer) 97
ledis> ZTTL myset2
ledis> ZTTL myzset2
(integer) -1
```
@ -1622,13 +2062,13 @@ int64:
**Examples**
```
ledis> ZADD myset 1 'one'
ledis> ZADD myzset 1 'one'
(integer) 1
ledis> ZEXPIRE myset 100
ledis> ZEXPIRE myzset 100
(integer) 1
ledis> ZTTL myset
ledis> ZTTL myzset
(integer) 97
ledis> ZPERSIST myset
ledis> ZPERSIST myzset
(integer) 1
ledis> ZTTL mset
(integer) -1

View File

@ -16,6 +16,8 @@ const (
ZScoreType byte = 8
BitType byte = 9
BitMetaType byte = 10
SetType byte = 11
SSizeType byte = 12
maxDataType byte = 100
@ -35,6 +37,8 @@ var (
ZScoreType: "zscore",
BitType: "bit",
BitMetaType: "bitmeta",
SetType: "set",
SSizeType: "ssize",
ExpTimeType: "exptime",
ExpMetaType: "expmeta",
}
@ -48,6 +52,7 @@ var (
errKeySize = errors.New("invalid key size")
errValueSize = errors.New("invalid value size")
errHashFieldSize = errors.New("invalid hash field size")
errSetMemberSize = errors.New("invalid set member size")
errZSetMemberSize = errors.New("invalid zset member size")
errExpireValue = errors.New("invalid expire value")
)
@ -65,6 +70,9 @@ const (
//max zset member size
MaxZSetMemberSize int = 1024
//max set member size
MaxSetMemberSize int = 1024
//max value size
MaxValueSize int = 10 * 1024 * 1024
)

View File

@ -21,6 +21,7 @@ type DB struct {
hashTx *tx
zsetTx *tx
binTx *tx
setTx *tx
}
type Ledis struct {
@ -88,6 +89,7 @@ func newDB(l *Ledis, index uint8) *DB {
d.hashTx = newTx(l)
d.zsetTx = newTx(l)
d.binTx = newTx(l)
d.setTx = newTx(l)
return d
}

View File

@ -10,7 +10,8 @@ func (db *DB) FlushAll() (drop int64, err error) {
db.lFlush,
db.hFlush,
db.zFlush,
db.bFlush}
db.bFlush,
db.sFlush}
for _, flush := range all {
if n, e := flush(); e != nil {

609
ledis/t_set.go Normal file
View File

@ -0,0 +1,609 @@
package ledis
import (
"encoding/binary"
"errors"
"github.com/siddontang/ledisdb/store"
"time"
)
var errSetKey = errors.New("invalid set key")
var errSSizeKey = errors.New("invalid ssize key")
const (
setStartSep byte = ':'
setStopSep byte = setStartSep + 1
UnionType byte = 51
DiffType byte = 52
InterType byte = 53
)
func checkSetKMSize(key []byte, member []byte) error {
if len(key) > MaxKeySize || len(key) == 0 {
return errKeySize
} else if len(member) > MaxSetMemberSize || len(member) == 0 {
return errSetMemberSize
}
return nil
}
func (db *DB) sEncodeSizeKey(key []byte) []byte {
buf := make([]byte, len(key)+2)
buf[0] = db.index
buf[1] = SSizeType
copy(buf[2:], key)
return buf
}
func (db *DB) sDecodeSizeKey(ek []byte) ([]byte, error) {
if len(ek) < 2 || ek[0] != db.index || ek[1] != SSizeType {
return nil, errSSizeKey
}
return ek[2:], nil
}
func (db *DB) sEncodeSetKey(key []byte, member []byte) []byte {
buf := make([]byte, len(key)+len(member)+1+1+2+1)
pos := 0
buf[pos] = db.index
pos++
buf[pos] = SetType
pos++
binary.BigEndian.PutUint16(buf[pos:], uint16(len(key)))
pos += 2
copy(buf[pos:], key)
pos += len(key)
buf[pos] = setStartSep
pos++
copy(buf[pos:], member)
return buf
}
func (db *DB) sDecodeSetKey(ek []byte) ([]byte, []byte, error) {
if len(ek) < 5 || ek[0] != db.index || ek[1] != SetType {
return nil, nil, errSetKey
}
pos := 2
keyLen := int(binary.BigEndian.Uint16(ek[pos:]))
pos += 2
if keyLen+5 > len(ek) {
return nil, nil, errSetKey
}
key := ek[pos : pos+keyLen]
pos += keyLen
if ek[pos] != hashStartSep {
return nil, nil, errSetKey
}
pos++
member := ek[pos:]
return key, member, nil
}
func (db *DB) sEncodeStartKey(key []byte) []byte {
return db.sEncodeSetKey(key, nil)
}
func (db *DB) sEncodeStopKey(key []byte) []byte {
k := db.sEncodeSetKey(key, nil)
k[len(k)-1] = setStopSep
return k
}
func (db *DB) sFlush() (drop int64, err error) {
minKey := make([]byte, 2)
minKey[0] = db.index
minKey[1] = SetType
maxKey := make([]byte, 2)
maxKey[0] = db.index
maxKey[1] = SSizeType + 1
t := db.setTx
t.Lock()
defer t.Unlock()
drop, err = db.flushRegion(t, minKey, maxKey)
err = db.expFlush(t, SetType)
err = t.Commit()
return
}
func (db *DB) sDelete(t *tx, key []byte) int64 {
sk := db.sEncodeSizeKey(key)
start := db.sEncodeStartKey(key)
stop := db.sEncodeStopKey(key)
var num int64 = 0
it := db.db.RangeLimitIterator(start, stop, store.RangeROpen, 0, -1)
for ; it.Valid(); it.Next() {
t.Delete(it.RawKey())
num++
}
it.Close()
t.Delete(sk)
return num
}
func (db *DB) sIncrSize(key []byte, delta int64) (int64, error) {
t := db.setTx
sk := db.sEncodeSizeKey(key)
var err error
var size int64 = 0
if size, err = Int64(db.db.Get(sk)); err != nil {
return 0, err
} else {
size += delta
if size <= 0 {
size = 0
t.Delete(sk)
db.rmExpire(t, SetType, key)
} else {
t.Put(sk, PutInt64(size))
}
}
return size, nil
}
func (db *DB) sExpireAt(key []byte, when int64) (int64, error) {
t := db.setTx
t.Lock()
defer t.Unlock()
if scnt, err := db.SCard(key); err != nil || scnt == 0 {
return 0, err
} else {
db.expireAt(t, SetType, key, when)
if err := t.Commit(); err != nil {
return 0, err
}
}
return 1, nil
}
func (db *DB) sSetItem(key []byte, member []byte) (int64, error) {
t := db.setTx
ek := db.sEncodeSetKey(key, member)
var n int64 = 1
if v, _ := db.db.Get(ek); v != nil {
n = 0
} else {
if _, err := db.sIncrSize(key, 1); err != nil {
return 0, err
}
}
t.Put(ek, nil)
return n, nil
}
func (db *DB) SAdd(key []byte, args ...[]byte) (int64, error) {
t := db.setTx
t.Lock()
defer t.Unlock()
var err error
var ek []byte
var num int64 = 0
for i := 0; i < len(args); i++ {
if err := checkSetKMSize(key, args[i]); err != nil {
return 0, err
}
ek = db.sEncodeSetKey(key, args[i])
if v, err := db.db.Get(ek); err != nil {
return 0, err
} else if v == nil {
num++
}
t.Put(ek, nil)
}
if _, err = db.sIncrSize(key, num); err != nil {
return 0, err
}
err = t.Commit()
return num, err
}
func (db *DB) SCard(key []byte) (int64, error) {
if err := checkKeySize(key); err != nil {
return 0, err
}
sk := db.sEncodeSizeKey(key)
return Int64(db.db.Get(sk))
}
func (db *DB) sDiffGeneric(keys ...[]byte) ([][]byte, error) {
destMap := make(map[string]bool)
members, err := db.SMembers(keys[0])
if err != nil {
return nil, err
}
for _, m := range members {
destMap[String(m)] = true
}
for _, k := range keys[1:] {
members, err := db.SMembers(k)
if err != nil {
return nil, err
}
for _, m := range members {
if _, ok := destMap[String(m)]; !ok {
continue
} else if ok {
delete(destMap, String(m))
}
}
// O - A = O, O is zero set.
if len(destMap) == 0 {
return nil, nil
}
}
slice := make([][]byte, len(destMap))
idx := 0
for k, v := range destMap {
if !v {
continue
}
slice[idx] = []byte(k)
idx++
}
return slice, nil
}
func (db *DB) SDiff(keys ...[]byte) ([][]byte, error) {
v, err := db.sDiffGeneric(keys...)
return v, err
}
func (db *DB) SDiffStore(dstKey []byte, keys ...[]byte) (int64, error) {
n, err := db.sStoreGeneric(dstKey, DiffType, keys...)
return n, err
}
func (db *DB) sInterGeneric(keys ...[]byte) ([][]byte, error) {
destMap := make(map[string]bool)
members, err := db.SMembers(keys[0])
if err != nil {
return nil, err
}
for _, m := range members {
destMap[String(m)] = true
}
for _, key := range keys[1:] {
if err := checkKeySize(key); err != nil {
return nil, err
}
members, err := db.SMembers(key)
if err != nil {
return nil, err
} else if len(members) == 0 {
return nil, err
}
tempMap := make(map[string]bool)
for _, member := range members {
if err := checkKeySize(member); err != nil {
return nil, err
}
if _, ok := destMap[String(member)]; ok {
tempMap[String(member)] = true //mark this item as selected
}
}
destMap = tempMap //reduce the size of the result set
if len(destMap) == 0 {
return nil, nil
}
}
slice := make([][]byte, len(destMap))
idx := 0
for k, v := range destMap {
if !v {
continue
}
slice[idx] = []byte(k)
idx++
}
return slice, nil
}
func (db *DB) SInter(keys ...[]byte) ([][]byte, error) {
v, err := db.sInterGeneric(keys...)
return v, err
}
func (db *DB) SInterStore(dstKey []byte, keys ...[]byte) (int64, error) {
n, err := db.sStoreGeneric(dstKey, InterType, keys...)
return n, err
}
func (db *DB) SIsMember(key []byte, member []byte) (int64, error) {
ek := db.sEncodeSetKey(key, member)
var n int64 = 1
if v, err := db.db.Get(ek); err != nil {
return 0, err
} else if v == nil {
n = 0
}
return n, nil
}
func (db *DB) SMembers(key []byte) ([][]byte, error) {
if err := checkKeySize(key); err != nil {
return nil, err
}
start := db.sEncodeStartKey(key)
stop := db.sEncodeStopKey(key)
v := make([][]byte, 0, 16)
it := db.db.RangeLimitIterator(start, stop, store.RangeROpen, 0, -1)
for ; it.Valid(); it.Next() {
_, m, err := db.sDecodeSetKey(it.Key())
if err != nil {
return nil, err
}
v = append(v, m)
}
it.Close()
return v, nil
}
func (db *DB) SRem(key []byte, args ...[]byte) (int64, error) {
t := db.setTx
t.Lock()
defer t.Unlock()
var ek []byte
var v []byte
var err error
it := db.db.NewIterator()
defer it.Close()
var num int64 = 0
for i := 0; i < len(args); i++ {
if err := checkSetKMSize(key, args[i]); err != nil {
return 0, err
}
ek = db.sEncodeSetKey(key, args[i])
v = it.RawFind(ek)
if v == nil {
continue
} else {
num++
t.Delete(ek)
}
}
if _, err = db.sIncrSize(key, -num); err != nil {
return 0, err
}
err = t.Commit()
return num, err
}
func (db *DB) sUnionGeneric(keys ...[]byte) ([][]byte, error) {
dstMap := make(map[string]bool)
for _, key := range keys {
if err := checkKeySize(key); err != nil {
return nil, err
}
members, err := db.SMembers(key)
if err != nil {
return nil, err
}
for _, member := range members {
dstMap[String(member)] = true
}
}
slice := make([][]byte, len(dstMap))
idx := 0
for k, v := range dstMap {
if !v {
continue
}
slice[idx] = []byte(k)
idx++
}
return slice, nil
}
func (db *DB) SUnion(keys ...[]byte) ([][]byte, error) {
v, err := db.sUnionGeneric(keys...)
return v, err
}
func (db *DB) SUnionStore(dstKey []byte, keys ...[]byte) (int64, error) {
n, err := db.sStoreGeneric(dstKey, UnionType, keys...)
return n, err
}
func (db *DB) sStoreGeneric(dstKey []byte, optType byte, keys ...[]byte) (int64, error) {
if err := checkKeySize(dstKey); err != nil {
return 0, err
}
t := db.setTx
t.Lock()
defer t.Unlock()
db.sDelete(t, dstKey)
var err error
var ek []byte
var num int64 = 0
var v [][]byte
switch optType {
case UnionType:
v, err = db.sUnionGeneric(keys...)
case DiffType:
v, err = db.sDiffGeneric(keys...)
case InterType:
v, err = db.sInterGeneric(keys...)
}
if err != nil {
return 0, err
}
for _, m := range v {
if err := checkSetKMSize(dstKey, m); err != nil {
return 0, err
}
ek = db.sEncodeSetKey(dstKey, m)
if v, err := db.db.Get(ek); err != nil {
return 0, err
} else if v == nil {
num++
}
t.Put(ek, nil)
}
if _, err = db.sIncrSize(dstKey, num); err != nil {
return 0, err
}
err = t.Commit()
return num, err
}
func (db *DB) SClear(key []byte) (int64, error) {
if err := checkKeySize(key); err != nil {
return 0, err
}
t := db.setTx
t.Lock()
defer t.Unlock()
num := db.sDelete(t, key)
db.rmExpire(t, SetType, key)
err := t.Commit()
return num, err
}
func (db *DB) SMclear(keys ...[]byte) (int64, error) {
t := db.setTx
t.Lock()
defer t.Unlock()
for _, key := range keys {
if err := checkKeySize(key); err != nil {
return 0, err
}
db.sDelete(t, key)
db.rmExpire(t, SetType, key)
}
err := t.Commit()
return int64(len(keys)), err
}
func (db *DB) SExpire(key []byte, duration int64) (int64, error) {
if duration <= 0 {
return 0, errExpireValue
}
return db.sExpireAt(key, time.Now().Unix()+duration)
}
func (db *DB) SExpireAt(key []byte, when int64) (int64, error) {
if when <= time.Now().Unix() {
return 0, errExpireValue
}
return db.sExpireAt(key, when)
}
func (db *DB) STTL(key []byte) (int64, error) {
if err := checkKeySize(key); err != nil {
return -1, err
}
return db.ttl(SetType, key)
}
func (db *DB) SPersist(key []byte) (int64, error) {
if err := checkKeySize(key); err != nil {
return 0, err
}
t := db.setTx
t.Lock()
defer t.Unlock()
n, err := db.rmExpire(t, SetType, key)
if err != nil {
return 0, err
}
err = t.Commit()
return n, err
}

339
ledis/t_set_test.go Normal file
View File

@ -0,0 +1,339 @@
package ledis
import (
"testing"
"time"
)
func TestSetCodec(t *testing.T) {
db := getTestDB()
key := []byte("key")
member := []byte("member")
ek := db.sEncodeSizeKey(key)
if k, err := db.sDecodeSizeKey(ek); err != nil {
t.Fatal(err)
} else if string(k) != "key" {
t.Fatal(string(k))
}
ek = db.sEncodeSetKey(key, member)
if k, m, err := db.sDecodeSetKey(ek); err != nil {
t.Fatal(err)
} else if string(k) != "key" {
t.Fatal(string(k))
} else if string(m) != "member" {
t.Fatal(string(m))
}
}
func TestDBSet(t *testing.T) {
db := getTestDB()
key := []byte("testdb_set_a")
member := []byte("member")
key1 := []byte("testdb_set_a1")
key2 := []byte("testdb_set_a2")
member1 := []byte("testdb_set_m1")
member2 := []byte("testdb_set_m2")
// if n, err := db.sSetItem(key, []byte("m1")); err != nil {
// t.Fatal(err)
// } else if n != 1 {
// t.Fatal(n)
// }
// if size, err := db.sIncrSize(key, 1); err != nil {
// t.Fatal(err)
// } else if size != 1 {
// t.Fatal(size)
// }
if n, err := db.SAdd(key, member); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if cnt, err := db.SCard(key); err != nil {
t.Fatal(err)
} else if cnt != 1 {
t.Fatal(cnt)
}
if n, err := db.SIsMember(key, member); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if v, err := db.SMembers(key); err != nil {
t.Fatal(err)
} else if string(v[0]) != "member" {
t.Fatal(string(v[0]))
}
if n, err := db.SRem(key, member); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
db.SAdd(key1, member1, member2)
if n, err := db.SClear(key1); err != nil {
t.Fatal(err)
} else if n != 2 {
t.Fatal(n)
}
db.SAdd(key1, member1, member2)
db.SAdd(key2, member1, member2, []byte("xxx"))
if n, _ := db.SCard(key2); n != 3 {
t.Fatal(n)
}
if n, err := db.SMclear(key1, key2); err != nil {
t.Fatal(err)
} else if n != 2 {
t.Fatal(n)
}
db.SAdd(key2, member1, member2)
if n, err := db.SExpire(key2, 3600); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := db.SExpireAt(key2, time.Now().Unix()+3600); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := db.STTL(key2); err != nil {
t.Fatal(err)
} else if n < 0 {
t.Fatal(n)
}
if n, err := db.SPersist(key2); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
}
func TestSetOperation(t *testing.T) {
db := getTestDB()
testUnion(db, t)
testInter(db, t)
testDiff(db, t)
}
func testUnion(db *DB, t *testing.T) {
key := []byte("testdb_set_union_1")
key1 := []byte("testdb_set_union_2")
key2 := []byte("testdb_set_union_2")
// member1 := []byte("testdb_set_m1")
// member2 := []byte("testdb_set_m2")
m1 := []byte("m1")
m2 := []byte("m2")
m3 := []byte("m3")
db.SAdd(key, m1, m2)
db.SAdd(key1, m1, m3)
db.SAdd(key2, m2, m3)
if _, err := db.sUnionGeneric(key, key2); err != nil {
t.Fatal(err)
}
if _, err := db.SUnion(key, key2); err != nil {
t.Fatal(err)
}
dstkey := []byte("union_dsk")
if num, err := db.SUnionStore(dstkey, key1, key2); err != nil {
t.Fatal(err)
} else if num != 3 {
t.Fatal(num)
}
if _, err := db.SMembers(dstkey); err != nil {
t.Fatal(err)
}
if n, err := db.SCard(dstkey); err != nil {
t.Fatal(err)
} else if n != 3 {
t.Fatal(n)
}
v1, _ := db.SUnion(key1, key2)
v2, _ := db.SUnion(key2, key1)
if len(v1) != len(v2) {
t.Fatal(v1, v2)
}
v1, _ = db.SUnion(key, key1, key2)
v2, _ = db.SUnion(key, key2, key1)
if len(v1) != len(v2) {
t.Fatal(v1, v2)
}
if v, err := db.SUnion(key, key); err != nil {
t.Fatal(err)
} else if len(v) != 2 {
t.Fatal(v)
}
empKey := []byte("0")
if v, err := db.SUnion(key, empKey); err != nil {
t.Fatal(err)
} else if len(v) != 2 {
t.Fatal(v)
}
}
func testInter(db *DB, t *testing.T) {
key1 := []byte("testdb_set_inter_1")
key2 := []byte("testdb_set_inter_2")
key3 := []byte("testdb_set_inter_3")
m1 := []byte("m1")
m2 := []byte("m2")
m3 := []byte("m3")
m4 := []byte("m4")
db.SAdd(key1, m1, m2)
db.SAdd(key2, m2, m3, m4)
db.SAdd(key3, m2, m4)
if v, err := db.sInterGeneric(key1, key2); err != nil {
t.Fatal(err)
} else if len(v) != 1 {
t.Fatal(v)
}
if v, err := db.SInter(key1, key2); err != nil {
t.Fatal(err)
} else if len(v) != 1 {
t.Fatal(v)
}
dstKey := []byte("inter_dsk")
if n, err := db.SInterStore(dstKey, key1, key2); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
k1 := []byte("set_k1")
k2 := []byte("set_k2")
db.SAdd(k1, m1, m3, m4)
db.SAdd(k2, m2, m3)
if n, err := db.SInterStore([]byte("set_xxx"), k1, k2); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
v1, _ := db.SInter(key1, key2)
v2, _ := db.SInter(key2, key1)
if len(v1) != len(v2) {
t.Fatal(v1, v2)
}
v1, _ = db.SInter(key1, key2, key3)
v2, _ = db.SInter(key2, key3, key1)
if len(v1) != len(v2) {
t.Fatal(v1, v2)
}
if v, err := db.SInter(key1, key1); err != nil {
t.Fatal(err)
} else if len(v) != 2 {
t.Fatal(v)
}
empKey := []byte("0")
if v, err := db.SInter(key1, empKey); err != nil {
t.Fatal(err)
} else if len(v) != 0 {
t.Fatal(v)
}
if v, err := db.SInter(empKey, key2); err != nil {
t.Fatal(err)
} else if len(v) != 0 {
t.Fatal(v)
}
}
func testDiff(db *DB, t *testing.T) {
key0 := []byte("testdb_set_diff_0")
key1 := []byte("testdb_set_diff_1")
key2 := []byte("testdb_set_diff_2")
key3 := []byte("testdb_set_diff_3")
m1 := []byte("m1")
m2 := []byte("m2")
m3 := []byte("m3")
m4 := []byte("m4")
db.SAdd(key1, m1, m2)
db.SAdd(key2, m2, m3, m4)
db.SAdd(key3, m3)
if _, err := db.sDiffGeneric(key1, key2); err != nil {
t.Fatal(err)
}
if v, err := db.SDiff(key1, key2); err != nil {
t.Fatal(err)
} else if len(v) != 1 {
t.Fatal(v)
}
dstKey := []byte("diff_dsk")
if n, err := db.SDiffStore(dstKey, key1, key2); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if v, err := db.SDiff(key2, key1); err != nil {
t.Fatal(err)
} else if len(v) != 2 {
t.Fatal(v)
}
if v, err := db.SDiff(key1, key2, key3); err != nil {
t.Fatal(err)
} else if len(v) != 1 {
t.Fatal(v) //return 1
}
if v, err := db.SDiff(key2, key2); err != nil {
t.Fatal(err)
} else if len(v) != 0 {
t.Fatal(v)
}
if v, err := db.SDiff(key0, key1); err != nil {
t.Fatal(err)
} else if len(v) != 0 {
t.Fatal(v)
}
if v, err := db.SDiff(key1, key0); err != nil {
t.Fatal(err)
} else if len(v) != 2 {
t.Fatal(v)
}
}

View File

@ -344,22 +344,6 @@ func TestBitErrorParams(t *testing.T) {
t.Fatal("invalid err of %v", err)
}
if _, err := c.Do("bexpire", "test_bexpire"); err == nil {
t.Fatal("invalid err of %v", err)
}
if _, err := c.Do("bexpireat", "test_bexpireat"); err == nil {
t.Fatal("invalid err of %v", err)
}
if _, err := c.Do("bttl"); err == nil {
t.Fatal("invalid err of %v", err)
}
if _, err := c.Do("bpersist"); err == nil {
t.Fatal("invalid err of %v", err)
}
//bexpire
if _, err := c.Do("bexpire", "test_bexpire"); err == nil {
t.Fatal("invalid err of %v", err)

283
server/cmd_set.go Normal file
View File

@ -0,0 +1,283 @@
package server
import (
"github.com/siddontang/ledisdb/ledis"
)
func saddCommand(req *requestContext) error {
args := req.args
if len(args) < 2 {
return ErrCmdParams
}
if n, err := req.db.SAdd(args[0], args[1:]...); err != nil {
return err
} else {
req.resp.writeInteger(n)
}
return nil
}
func soptGeneric(req *requestContext, optType byte) error {
args := req.args
if len(args) < 1 {
return ErrCmdParams
}
var v [][]byte
var err error
switch optType {
case ledis.UnionType:
v, err = req.db.SUnion(args...)
case ledis.DiffType:
v, err = req.db.SDiff(args...)
case ledis.InterType:
v, err = req.db.SInter(args...)
}
if err != nil {
return err
} else {
req.resp.writeSliceArray(v)
}
return nil
}
func soptStoreGeneric(req *requestContext, optType byte) error {
args := req.args
if len(args) < 2 {
return ErrCmdParams
}
var n int64
var err error
switch optType {
case ledis.UnionType:
n, err = req.db.SUnionStore(args[0], args[1:]...)
case ledis.DiffType:
n, err = req.db.SDiffStore(args[0], args[1:]...)
case ledis.InterType:
n, err = req.db.SInterStore(args[0], args[1:]...)
}
if err != nil {
return err
} else {
req.resp.writeInteger(n)
}
return nil
}
func scardCommand(req *requestContext) error {
args := req.args
if len(args) != 1 {
return ErrCmdParams
}
if n, err := req.db.SCard(args[0]); err != nil {
return err
} else {
req.resp.writeInteger(n)
}
return nil
}
func sdiffCommand(req *requestContext) error {
return soptGeneric(req, ledis.DiffType)
}
func sdiffstoreCommand(req *requestContext) error {
return soptStoreGeneric(req, ledis.DiffType)
}
func sinterCommand(req *requestContext) error {
return soptGeneric(req, ledis.InterType)
}
func sinterstoreCommand(req *requestContext) error {
return soptStoreGeneric(req, ledis.InterType)
}
func sismemberCommand(req *requestContext) error {
args := req.args
if len(args) != 2 {
return ErrCmdParams
}
if n, err := req.db.SIsMember(args[0], args[1]); err != nil {
return err
} else {
req.resp.writeInteger(n)
}
return nil
}
func smembersCommand(req *requestContext) error {
args := req.args
if len(args) != 1 {
return ErrCmdParams
}
if v, err := req.db.SMembers(args[0]); err != nil {
return err
} else {
req.resp.writeSliceArray(v)
}
return nil
}
func sremCommand(req *requestContext) error {
args := req.args
if len(args) < 2 {
return ErrCmdParams
}
if n, err := req.db.SRem(args[0], args[1:]...); err != nil {
return err
} else {
req.resp.writeInteger(n)
}
return nil
}
func sunionCommand(req *requestContext) error {
return soptGeneric(req, ledis.UnionType)
}
func sunionstoreCommand(req *requestContext) error {
return soptStoreGeneric(req, ledis.UnionType)
}
func sclearCommand(req *requestContext) error {
args := req.args
if len(args) != 1 {
return ErrCmdParams
}
if n, err := req.db.SClear(args[0]); err != nil {
return err
} else {
req.resp.writeInteger(n)
}
return nil
}
func smclearCommand(req *requestContext) error {
args := req.args
if len(args) < 1 {
return ErrCmdParams
}
if n, err := req.db.SMclear(args...); err != nil {
return err
} else {
req.resp.writeInteger(n)
}
return nil
}
func sexpireCommand(req *requestContext) error {
args := req.args
if len(args) != 2 {
return ErrCmdParams
}
duration, err := ledis.StrInt64(args[1], nil)
if err != nil {
return ErrValue
}
if v, err := req.db.SExpire(args[0], duration); err != nil {
return err
} else {
req.resp.writeInteger(v)
}
return nil
}
func sexpireAtCommand(req *requestContext) error {
args := req.args
if len(args) != 2 {
return ErrCmdParams
}
when, err := ledis.StrInt64(args[1], nil)
if err != nil {
return ErrValue
}
if v, err := req.db.SExpireAt(args[0], when); err != nil {
return err
} else {
req.resp.writeInteger(v)
}
return nil
}
func sttlCommand(req *requestContext) error {
args := req.args
if len(args) != 1 {
return ErrCmdParams
}
if v, err := req.db.STTL(args[0]); err != nil {
return err
} else {
req.resp.writeInteger(v)
}
return nil
}
func spersistCommand(req *requestContext) error {
args := req.args
if len(args) != 1 {
return ErrCmdParams
}
if n, err := req.db.SPersist(args[0]); err != nil {
return err
} else {
req.resp.writeInteger(n)
}
return nil
}
func init() {
register("sadd", saddCommand)
register("scard", scardCommand)
register("sdiff", sdiffCommand)
register("sdiffstore", sdiffstoreCommand)
register("sinter", sinterCommand)
register("sinterstore", sinterstoreCommand)
register("sismember", sismemberCommand)
register("smembers", smembersCommand)
register("srem", sremCommand)
register("sunion", sunionCommand)
register("sunionstore", sunionstoreCommand)
register("sclear", sclearCommand)
register("smclear", smclearCommand)
register("sexpire", sexpireCommand)
register("sexpireat", sexpireAtCommand)
register("sttl", sttlCommand)
register("spersist", spersistCommand)
}

203
server/cmd_set_test.go Normal file
View File

@ -0,0 +1,203 @@
package server
import (
"github.com/siddontang/ledisdb/client/go/ledis"
"testing"
)
func TestSet(t *testing.T) {
c := getTestConn()
defer c.Close()
key1 := "testdb_cmd_set_1"
key2 := "testdb_cmd_set_2"
if n, err := ledis.Int(c.Do("sadd", key1, 0, 1)); err != nil {
t.Fatal(err)
} else if n != 2 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("sadd", key2, 0, 1, 2, 3)); err != nil {
t.Fatal(err)
} else if n != 4 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("scard", key1)); err != nil {
t.Fatal(err)
} else if n != 2 {
t.Fatal(n)
}
if n, err := ledis.MultiBulk(c.Do("sdiff", key2, key1)); err != nil {
t.Fatal(err)
} else if len(n) != 2 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("sdiffstore", []byte("cmd_set_em1"), key2, key1)); err != nil {
t.Fatal(err)
} else if n != 2 {
t.Fatal(n)
}
if n, err := ledis.MultiBulk(c.Do("sunion", key1, key2)); err != nil {
t.Fatal(err)
} else if len(n) != 4 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("sunionstore", []byte("cmd_set_em2"), key1, key2)); err != nil {
t.Fatal(err)
} else if n != 4 {
t.Fatal(n)
}
if n, err := ledis.MultiBulk(c.Do("sinter", key1, key2)); err != nil {
t.Fatal(err)
} else if len(n) != 2 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("sinterstore", []byte("cmd_set_em3"), key1, key2)); err != nil {
t.Fatal(err)
} else if n != 2 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("srem", key1, 0, 1)); err != nil {
t.Fatal(err)
} else if n != 2 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("sismember", key2, 0)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := ledis.MultiBulk(c.Do("smembers", key2)); err != nil {
t.Fatal(err)
} else if len(n) != 4 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("sclear", key2)); err != nil {
t.Fatal(err)
} else if n != 4 {
t.Fatal(n)
}
c.Do("sadd", key1, 0)
c.Do("sadd", key2, 1)
if n, err := ledis.Int(c.Do("smclear", key1, key2)); err != nil {
t.Fatal(err)
} else if n != 2 {
t.Fatal(n)
}
}
func TestSetErrorParams(t *testing.T) {
c := getTestConn()
defer c.Close()
if _, err := c.Do("sadd", "test_sadd"); err == nil {
t.Fatal("invalid err of %v", err)
}
if _, err := c.Do("scard"); err == nil {
t.Fatal("invalid err of %v", err)
}
if _, err := c.Do("scard", "k1", "k2"); err == nil {
t.Fatal("invalid err of %v", err)
}
if _, err := c.Do("sdiff"); err == nil {
t.Fatal("invalid err of %v", err)
}
if _, err := c.Do("sdiffstore", "dstkey"); err == nil {
t.Fatal("invalid err of %v", err)
}
if _, err := c.Do("sinter"); err == nil {
t.Fatal("invalid err of %v", err)
}
if _, err := c.Do("sinterstore", "dstkey"); err == nil {
t.Fatal("invalid err of %v", err)
}
if _, err := c.Do("sunion"); err == nil {
t.Fatal("invalid err of %v", err)
}
if _, err := c.Do("sunionstore", "dstkey"); err == nil {
t.Fatal("invalid err of %v", err)
}
if _, err := c.Do("sismember", "k1"); err == nil {
t.Fatal("invalid err of %v", err)
}
if _, err := c.Do("sismember", "k1", "m1", "m2"); err == nil {
t.Fatal("invalid err of %v", err)
}
if _, err := c.Do("smembers"); err == nil {
t.Fatal("invalid err of %v", err)
}
if _, err := c.Do("smembers", "k1", "k2"); err == nil {
t.Fatal("invalid err of %v", err)
}
if _, err := c.Do("srem"); err == nil {
t.Fatal("invalid err of %v", err)
}
if _, err := c.Do("srem", "key"); err == nil {
t.Fatal("invalid err of %v", err)
}
if _, err := c.Do("sclear"); err == nil {
t.Fatal("invalid err of %v", err)
}
if _, err := c.Do("sclear", "k1", "k2"); err == nil {
t.Fatal("invalid err of %v", err)
}
if _, err := c.Do("smclear"); err == nil {
t.Fatal("invalid err of %v", err)
}
if _, err := c.Do("sexpire", "set_expire"); err == nil {
t.Fatal("invalid err of %v", err)
}
if _, err := c.Do("sexpire", "set_expire", "aaa"); err == nil {
t.Fatal("invalid err of %v", err)
}
if _, err := c.Do("sexpireat", "set_expireat"); err == nil {
t.Fatal("invalid err of %v", err)
}
if _, err := c.Do("sexpireat", "set_expireat", "aaa"); err == nil {
t.Fatal("invalid err of %v", err)
}
if _, err := c.Do("sttl"); err == nil {
t.Fatal("invalid err of %v", err)
}
if _, err := c.Do("spersist"); err == nil {
t.Fatal("invalid err of %v", err)
}
}

View File

@ -79,3 +79,353 @@ func TestKVExpire(t *testing.T) {
}
}
func TestSetExpire(t *testing.T) {
c := getTestConn()
defer c.Close()
k := "set_ttl"
c.Do("sadd", k, "123")
// expire + ttl
exp := int64(10)
if n, err := ledis.Int(c.Do("sexpire", k, exp)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if ttl, err := ledis.Int64(c.Do("sttl", k)); err != nil {
t.Fatal(err)
} else if ttl != exp {
t.Fatal(ttl)
}
// expireat + ttl
tm := now() + 3
if n, err := ledis.Int(c.Do("sexpireat", k, tm)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if ttl, err := ledis.Int64(c.Do("sttl", k)); err != nil {
t.Fatal(err)
} else if ttl != 3 {
t.Fatal(ttl)
}
kErr := "not_exist_ttl"
// err - expire, expireat
if n, err := ledis.Int(c.Do("sexpire", kErr, tm)); err != nil || n != 0 {
t.Fatal(false)
}
if n, err := ledis.Int(c.Do("sexpireat", kErr, tm)); err != nil || n != 0 {
t.Fatal(false)
}
if n, err := ledis.Int(c.Do("sttl", kErr)); err != nil || n != -1 {
t.Fatal(false)
}
if n, err := ledis.Int(c.Do("spersist", k)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("sexpire", k, 10)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("spersist", k)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
}
func TestListExpire(t *testing.T) {
c := getTestConn()
defer c.Close()
k := "list_ttl"
c.Do("rpush", k, "123")
// expire + ttl
exp := int64(10)
if n, err := ledis.Int(c.Do("lexpire", k, exp)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if ttl, err := ledis.Int64(c.Do("lttl", k)); err != nil {
t.Fatal(err)
} else if ttl != exp {
t.Fatal(ttl)
}
// expireat + ttl
tm := now() + 3
if n, err := ledis.Int(c.Do("lexpireat", k, tm)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if ttl, err := ledis.Int64(c.Do("lttl", k)); err != nil {
t.Fatal(err)
} else if ttl != 3 {
t.Fatal(ttl)
}
kErr := "not_exist_ttl"
// err - expire, expireat
if n, err := ledis.Int(c.Do("lexpire", kErr, tm)); err != nil || n != 0 {
t.Fatal(false)
}
if n, err := ledis.Int(c.Do("lexpireat", kErr, tm)); err != nil || n != 0 {
t.Fatal(false)
}
if n, err := ledis.Int(c.Do("lttl", kErr)); err != nil || n != -1 {
t.Fatal(false)
}
if n, err := ledis.Int(c.Do("lpersist", k)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("lexpire", k, 10)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("lpersist", k)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
}
func TestHashExpire(t *testing.T) {
c := getTestConn()
defer c.Close()
k := "hash_ttl"
c.Do("hset", k, "f", 123)
// expire + ttl
exp := int64(10)
if n, err := ledis.Int(c.Do("hexpire", k, exp)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if ttl, err := ledis.Int64(c.Do("httl", k)); err != nil {
t.Fatal(err)
} else if ttl != exp {
t.Fatal(ttl)
}
// expireat + ttl
tm := now() + 3
if n, err := ledis.Int(c.Do("hexpireat", k, tm)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if ttl, err := ledis.Int64(c.Do("httl", k)); err != nil {
t.Fatal(err)
} else if ttl != 3 {
t.Fatal(ttl)
}
kErr := "not_exist_ttl"
// err - expire, expireat
if n, err := ledis.Int(c.Do("hexpire", kErr, tm)); err != nil || n != 0 {
t.Fatal(false)
}
if n, err := ledis.Int(c.Do("hexpireat", kErr, tm)); err != nil || n != 0 {
t.Fatal(false)
}
if n, err := ledis.Int(c.Do("httl", kErr)); err != nil || n != -1 {
t.Fatal(false)
}
if n, err := ledis.Int(c.Do("hpersist", k)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("hexpire", k, 10)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("hpersist", k)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
}
func TestZsetExpire(t *testing.T) {
c := getTestConn()
defer c.Close()
k := "zset_ttl"
c.Do("zadd", k, 123, "m")
// expire + ttl
exp := int64(10)
if n, err := ledis.Int(c.Do("zexpire", k, exp)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if ttl, err := ledis.Int64(c.Do("zttl", k)); err != nil {
t.Fatal(err)
} else if ttl != exp {
t.Fatal(ttl)
}
// expireat + ttl
tm := now() + 3
if n, err := ledis.Int(c.Do("zexpireat", k, tm)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if ttl, err := ledis.Int64(c.Do("zttl", k)); err != nil {
t.Fatal(err)
} else if ttl != 3 {
t.Fatal(ttl)
}
kErr := "not_exist_ttl"
// err - expire, expireat
if n, err := ledis.Int(c.Do("zexpire", kErr, tm)); err != nil || n != 0 {
t.Fatal(false)
}
if n, err := ledis.Int(c.Do("zexpireat", kErr, tm)); err != nil || n != 0 {
t.Fatal(false)
}
if n, err := ledis.Int(c.Do("zttl", kErr)); err != nil || n != -1 {
t.Fatal(false)
}
if n, err := ledis.Int(c.Do("zpersist", k)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("zexpire", k, 10)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("zpersist", k)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
}
func TestBitmapExpire(t *testing.T) {
c := getTestConn()
defer c.Close()
k := "bit_ttl"
c.Do("bsetbit", k, 0, 1)
// expire + ttl
exp := int64(10)
if n, err := ledis.Int(c.Do("bexpire", k, exp)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if ttl, err := ledis.Int64(c.Do("bttl", k)); err != nil {
t.Fatal(err)
} else if ttl != exp {
t.Fatal(ttl)
}
// expireat + ttl
tm := now() + 3
if n, err := ledis.Int(c.Do("bexpireat", k, tm)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if ttl, err := ledis.Int64(c.Do("bttl", k)); err != nil {
t.Fatal(err)
} else if ttl != 3 {
t.Fatal(ttl)
}
kErr := "not_exist_ttl"
// err - expire, expireat
if n, err := ledis.Int(c.Do("bexpire", kErr, tm)); err != nil || n != 0 {
t.Fatal(false)
}
if n, err := ledis.Int(c.Do("bexpireat", kErr, tm)); err != nil || n != 0 {
t.Fatal(false)
}
if n, err := ledis.Int(c.Do("bttl", kErr)); err != nil || n != -1 {
t.Fatal(false)
}
if n, err := ledis.Int(c.Do("bpersist", k)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("bexpire", k, 10)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
if n, err := ledis.Int(c.Do("bpersist", k)); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(n)
}
}