diff --git a/command.go b/command.go index dab9fc3c..8dceb325 100644 --- a/command.go +++ b/command.go @@ -361,7 +361,9 @@ var ok = []byte("OK") func (cmd *BoolCmd) parseReply(rd *bufio.Reader) error { v, err := parseReply(rd, nil) - // `SET key value NX` returns nil when key already exists. + // `SET key value NX` returns nil when key already exists, which + // is inconsistent with `SETNX key value`. + // TODO: is this okay? if err == Nil { cmd.val = false return nil @@ -476,6 +478,10 @@ func (cmd *FloatCmd) Val() float64 { return cmd.val } +func (cmd *FloatCmd) Result() (float64, error) { + return cmd.Val(), cmd.Err() +} + func (cmd *FloatCmd) String() string { return cmdString(cmd, cmd.val) } diff --git a/commands.go b/commands.go index c273e108..44a77d0d 100644 --- a/commands.go +++ b/commands.go @@ -1008,19 +1008,98 @@ type ZStore struct { Aggregate string } -func (c *commandable) ZAdd(key string, members ...Z) *IntCmd { - args := make([]interface{}, 2+2*len(members)) - args[0] = "ZADD" - args[1] = key +func (c *commandable) zAdd(a []interface{}, n int, members ...Z) *IntCmd { for i, m := range members { - args[2+2*i] = formatFloat(m.Score) - args[2+2*i+1] = m.Member + a[n+2*i] = formatFloat(m.Score) + a[n+2*i+1] = m.Member } - cmd := NewIntCmd(args...) + cmd := NewIntCmd(a...) c.Process(cmd) return cmd } +// Redis `ZADD key score member [score member ...]` command. +func (c *commandable) ZAdd(key string, members ...Z) *IntCmd { + const n = 2 + a := make([]interface{}, n+2*len(members)) + a[0], a[1] = "ZADD", key + return c.zAdd(a, n, members...) +} + +// Redis `ZADD key NX score member [score member ...]` command. +func (c *commandable) ZAddNX(key string, members ...Z) *IntCmd { + const n = 3 + a := make([]interface{}, n+2*len(members)) + a[0], a[1], a[2] = "ZADD", key, "NX" + return c.zAdd(a, n, members...) +} + +// Redis `ZADD key XX score member [score member ...]` command. +func (c *commandable) ZAddXX(key string, members ...Z) *IntCmd { + const n = 3 + a := make([]interface{}, n+2*len(members)) + a[0], a[1], a[2] = "ZADD", key, "XX" + return c.zAdd(a, n, members...) +} + +// Redis `ZADD key CH score member [score member ...]` command. +func (c *commandable) ZAddCh(key string, members ...Z) *IntCmd { + const n = 3 + a := make([]interface{}, n+2*len(members)) + a[0], a[1], a[2] = "ZADD", key, "CH" + return c.zAdd(a, n, members...) +} + +// Redis `ZADD key NX CH score member [score member ...]` command. +func (c *commandable) ZAddNXCh(key string, members ...Z) *IntCmd { + const n = 4 + a := make([]interface{}, n+2*len(members)) + a[0], a[1], a[2], a[3] = "ZADD", key, "NX", "CH" + return c.zAdd(a, n, members...) +} + +// Redis `ZADD key XX CH score member [score member ...]` command. +func (c *commandable) ZAddXXCh(key string, members ...Z) *IntCmd { + const n = 4 + a := make([]interface{}, n+2*len(members)) + a[0], a[1], a[2], a[3] = "ZADD", key, "XX", "CH" + return c.zAdd(a, n, members...) +} + +func (c *commandable) zIncr(a []interface{}, n int, members ...Z) *FloatCmd { + for i, m := range members { + a[n+2*i] = formatFloat(m.Score) + a[n+2*i+1] = m.Member + } + cmd := NewFloatCmd(a...) + c.Process(cmd) + return cmd +} + +// Redis `ZADD key INCR score member` command. +func (c *commandable) ZIncr(key string, member Z) *FloatCmd { + const n = 3 + a := make([]interface{}, n+2) + a[0], a[1], a[2] = "ZADD", key, "INCR" + return c.zIncr(a, n, member) +} + +// Redis `ZADD key NX INCR score member` command. +func (c *commandable) ZIncrNX(key string, member Z) *FloatCmd { + const n = 4 + a := make([]interface{}, n+2) + a[0], a[1], a[2], a[3] = "ZADD", key, "INCR", "NX" + return c.zIncr(a, n, member) +} + +// Redis `ZADD key XX INCR score member` command. +func (c *commandable) ZIncrXX(key string, member Z) *FloatCmd { + const n = 4 + a := make([]interface{}, n+2) + a[0], a[1], a[2], a[3] = "ZADD", key, "INCR", "XX" + return c.zIncr(a, n, member) +} + func (c *commandable) ZCard(key string) *IntCmd { cmd := NewIntCmd("ZCARD", key) c.Process(cmd) diff --git a/commands_test.go b/commands_test.go index b14f5663..448e0423 100644 --- a/commands_test.go +++ b/commands_test.go @@ -32,8 +32,6 @@ var _ = Describe("Commands", func() { Expect(client.Close()).NotTo(HaveOccurred()) }) - //------------------------------------------------------------------------------ - Describe("server", func() { It("should Auth", func() { @@ -158,8 +156,6 @@ var _ = Describe("Commands", func() { }) - //------------------------------------------------------------------------------ - Describe("debugging", func() { It("should DebugObject", func() { @@ -175,8 +171,6 @@ var _ = Describe("Commands", func() { }) - //------------------------------------------------------------------------------ - Describe("keys", func() { It("should Del", func() { @@ -539,8 +533,6 @@ var _ = Describe("Commands", func() { }) - //------------------------------------------------------------------------------ - Describe("scanning", func() { It("should Scan", func() { @@ -593,8 +585,6 @@ var _ = Describe("Commands", func() { }) - //------------------------------------------------------------------------------ - Describe("strings", func() { It("should Append", func() { @@ -1004,8 +994,6 @@ var _ = Describe("Commands", func() { }) - //------------------------------------------------------------------------------ - Describe("hashes", func() { It("should HDel", func() { @@ -1192,8 +1180,6 @@ var _ = Describe("Commands", func() { }) - //------------------------------------------------------------------------------ - Describe("lists", func() { It("should BLPop", func() { @@ -1546,8 +1532,6 @@ var _ = Describe("Commands", func() { }) - //------------------------------------------------------------------------------ - Describe("sets", func() { It("should SAdd", func() { @@ -1821,8 +1805,6 @@ var _ = Describe("Commands", func() { }) - //------------------------------------------------------------------------------ - Describe("sorted sets", func() { It("should ZAdd", func() { @@ -1842,9 +1824,9 @@ var _ = Describe("Commands", func() { Expect(err).NotTo(HaveOccurred()) Expect(added).To(Equal(int64(0))) - val, err := client.ZRangeWithScores("zset", 0, -1).Result() + vals, err := client.ZRangeWithScores("zset", 0, -1).Result() Expect(err).NotTo(HaveOccurred()) - Expect(val).To(Equal([]redis.Z{{1, "one"}, {1, "uno"}, {3, "two"}})) + Expect(vals).To(Equal([]redis.Z{{1, "one"}, {1, "uno"}, {3, "two"}})) }) It("should ZAdd bytes", func() { @@ -1869,6 +1851,154 @@ var _ = Describe("Commands", func() { Expect(val).To(Equal([]redis.Z{{1, "one"}, {1, "uno"}, {3, "two"}})) }) + It("should ZAddNX", func() { + added, err := client.ZAddNX("zset", redis.Z{1, "one"}).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(added).To(Equal(int64(1))) + + vals, err := client.ZRangeWithScores("zset", 0, -1).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(Equal([]redis.Z{{1, "one"}})) + + added, err = client.ZAddNX("zset", redis.Z{2, "one"}).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(added).To(Equal(int64(0))) + + vals, err = client.ZRangeWithScores("zset", 0, -1).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(Equal([]redis.Z{{1, "one"}})) + }) + + It("should ZAddXX", func() { + added, err := client.ZAddXX("zset", redis.Z{1, "one"}).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(added).To(Equal(int64(0))) + + vals, err := client.ZRangeWithScores("zset", 0, -1).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(BeEmpty()) + + added, err = client.ZAdd("zset", redis.Z{1, "one"}).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(added).To(Equal(int64(1))) + + added, err = client.ZAddXX("zset", redis.Z{2, "one"}).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(added).To(Equal(int64(0))) + + vals, err = client.ZRangeWithScores("zset", 0, -1).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(Equal([]redis.Z{{2, "one"}})) + }) + + It("should ZAddCh", func() { + changed, err := client.ZAddCh("zset", redis.Z{1, "one"}).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(changed).To(Equal(int64(1))) + + changed, err = client.ZAddCh("zset", redis.Z{1, "one"}).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(changed).To(Equal(int64(0))) + }) + + It("should ZAddNXCh", func() { + changed, err := client.ZAddNXCh("zset", redis.Z{1, "one"}).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(changed).To(Equal(int64(1))) + + vals, err := client.ZRangeWithScores("zset", 0, -1).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(Equal([]redis.Z{{1, "one"}})) + + changed, err = client.ZAddNXCh("zset", redis.Z{2, "one"}).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(changed).To(Equal(int64(0))) + + vals, err = client.ZRangeWithScores("zset", 0, -1).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(Equal([]redis.Z{{1, "one"}})) + }) + + It("should ZAddXXCh", func() { + changed, err := client.ZAddXXCh("zset", redis.Z{1, "one"}).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(changed).To(Equal(int64(0))) + + vals, err := client.ZRangeWithScores("zset", 0, -1).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(BeEmpty()) + + added, err := client.ZAdd("zset", redis.Z{1, "one"}).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(added).To(Equal(int64(1))) + + changed, err = client.ZAddXXCh("zset", redis.Z{2, "one"}).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(changed).To(Equal(int64(1))) + + vals, err = client.ZRangeWithScores("zset", 0, -1).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(Equal([]redis.Z{{2, "one"}})) + }) + + It("should ZIncr", func() { + score, err := client.ZIncr("zset", redis.Z{1, "one"}).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(score).To(Equal(float64(1))) + + vals, err := client.ZRangeWithScores("zset", 0, -1).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(Equal([]redis.Z{{1, "one"}})) + + score, err = client.ZIncr("zset", redis.Z{1, "one"}).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(score).To(Equal(float64(2))) + + vals, err = client.ZRangeWithScores("zset", 0, -1).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(Equal([]redis.Z{{2, "one"}})) + }) + + It("should ZIncrNX", func() { + score, err := client.ZIncrNX("zset", redis.Z{1, "one"}).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(score).To(Equal(float64(1))) + + vals, err := client.ZRangeWithScores("zset", 0, -1).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(Equal([]redis.Z{{1, "one"}})) + + score, err = client.ZIncrNX("zset", redis.Z{1, "one"}).Result() + Expect(err).To(Equal(redis.Nil)) + Expect(score).To(Equal(float64(0))) + + vals, err = client.ZRangeWithScores("zset", 0, -1).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(Equal([]redis.Z{{1, "one"}})) + }) + + It("should ZIncrXX", func() { + score, err := client.ZIncrXX("zset", redis.Z{1, "one"}).Result() + Expect(err).To(Equal(redis.Nil)) + Expect(score).To(Equal(float64(0))) + + vals, err := client.ZRangeWithScores("zset", 0, -1).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(BeEmpty()) + + added, err := client.ZAdd("zset", redis.Z{1, "one"}).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(added).To(Equal(int64(1))) + + score, err = client.ZIncrXX("zset", redis.Z{1, "one"}).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(score).To(Equal(float64(2))) + + vals, err = client.ZRangeWithScores("zset", 0, -1).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(Equal([]redis.Z{{2, "one"}})) + }) + It("should ZCard", func() { zAdd := client.ZAdd("zset", redis.Z{1, "one"}) Expect(zAdd.Err()).NotTo(HaveOccurred()) @@ -2334,8 +2464,6 @@ var _ = Describe("Commands", func() { }) - //------------------------------------------------------------------------------ - Describe("watch/unwatch", func() { It("should WatchUnwatch", func() {