diff --git a/README.md b/README.md index a55fb94..9428d2a 100644 --- a/README.md +++ b/README.md @@ -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 multi 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. + Supports expiration and ttl. diff --git a/ledis/t_set_test.go b/ledis/t_set_test.go index d308bd5..c5f6523 100644 --- a/ledis/t_set_test.go +++ b/ledis/t_set_test.go @@ -129,9 +129,9 @@ func TestDBSet(t *testing.T) { func TestSetOperation(t *testing.T) { db := getTestDB() - // testUnion(db, t) + testUnion(db, t) testInter(db, t) - // testDiff(db, t) + testDiff(db, t) } @@ -232,6 +232,17 @@ func testInter(db *DB, t *testing.T) { 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) { diff --git a/server/cmd_set.go b/server/cmd_set.go new file mode 100644 index 0000000..815c3a8 --- /dev/null +++ b/server/cmd_set.go @@ -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) +} diff --git a/server/cmd_set_test.go b/server/cmd_set_test.go new file mode 100644 index 0000000..0e76520 --- /dev/null +++ b/server/cmd_set_test.go @@ -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) + } + +}