Adding support for ZMPOP command (#2408)

* feat: add ZMPOP command

* fix: reply reading string

* fix: evaluating a test tweak

* fix: test fix

* fix: reverting to debug

* fix: test fix

* fix: remove old implementation

* feat: adding ZMPOP and tests

* feat: modifying ZMpopCmd

* fix: fix test

* fix: test removal check

* fix: fix testS

* Adding more tests

* fix: using redis.Nil instead of string

* fix: renaming command to ZArrayWithKeyCmd to match the standard

* feat: updated ZArrayWithKeyCmd to ZSliceWithKeyCmd

* feat: adding help strings

---------

Co-authored-by: Anuragkillswitch <70265851+Anuragkillswitch@users.noreply.github.com>
Co-authored-by: Chayim <chayim@users.noreply.github.com>
This commit is contained in:
Anurag Bandyopadhyay 2023-02-22 23:08:51 +05:30 committed by GitHub
parent f95bdb8a8b
commit 621c02c583
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 179 additions and 0 deletions

View File

@ -3752,3 +3752,86 @@ func (cmd *KeyValuesCmd) readReply(rd *proto.Reader) (err error) {
return nil
}
//------------------------------------------------------------------------------
type ZSliceWithKeyCmd struct {
baseCmd
key string
val []Z
}
var _ Cmder = (*ZSliceWithKeyCmd)(nil)
func NewZSliceWithKeyCmd(ctx context.Context, args ...interface{}) *ZSliceWithKeyCmd {
return &ZSliceWithKeyCmd{
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
}
}
func (cmd *ZSliceWithKeyCmd) SetVal(key string, val []Z) {
cmd.key = key
cmd.val = val
}
func (cmd *ZSliceWithKeyCmd) Val() (string, []Z) {
return cmd.key, cmd.val
}
func (cmd *ZSliceWithKeyCmd) Result() (string, []Z, error) {
return cmd.key, cmd.val, cmd.err
}
func (cmd *ZSliceWithKeyCmd) String() string {
return cmdString(cmd, cmd.val)
}
func (cmd *ZSliceWithKeyCmd) readReply(rd *proto.Reader) (err error) {
if err = rd.ReadFixedArrayLen(2); err != nil {
return err
}
cmd.key, err = rd.ReadString()
if err != nil {
return err
}
n, err := rd.ReadArrayLen()
if err != nil {
return err
}
typ, err := rd.PeekReplyType()
if err != nil {
return err
}
array := typ == proto.RespArray
if array {
cmd.val = make([]Z, n)
} else {
cmd.val = make([]Z, n/2)
}
for i := 0; i < len(cmd.val); i++ {
if array {
if err = rd.ReadFixedArrayLen(2); err != nil {
return err
}
}
if cmd.val[i].Member, err = rd.ReadString(); err != nil {
return err
}
if cmd.val[i].Score, err = rd.ReadFloat(); err != nil {
return err
}
}
return nil
}

View File

@ -313,6 +313,7 @@ type Cmdable interface {
ZInterWithScores(ctx context.Context, store *ZStore) *ZSliceCmd
ZInterCard(ctx context.Context, limit int64, keys ...string) *IntCmd
ZInterStore(ctx context.Context, destination string, store *ZStore) *IntCmd
ZMPop(ctx context.Context, order string, count int64, keys ...string) *ZSliceWithKeyCmd
ZMScore(ctx context.Context, key string, members ...string) *FloatSliceCmd
ZPopMax(ctx context.Context, key string, count ...int64) *ZSliceCmd
ZPopMin(ctx context.Context, key string, count ...int64) *ZSliceCmd
@ -2473,6 +2474,22 @@ func (c cmdable) ZInterCard(ctx context.Context, limit int64, keys ...string) *I
return cmd
}
// ZMPop Pops one or more elements with the highest or lowest score from the first non-empty sorted set key from the list of provided key names.
// direction: "max" (highest score) or "min" (lowest score), count: > 0
// example: client.ZMPop(ctx, "max", 5, "set1", "set2")
func (c cmdable) ZMPop(ctx context.Context, order string, count int64, keys ...string) *ZSliceWithKeyCmd {
args := make([]interface{}, 2+len(keys), 5+len(keys))
args[0] = "zmpop"
args[1] = len(keys)
for i, key := range keys {
args[2+i] = key
}
args = append(args, strings.ToLower(order), "count", count)
cmd := NewZSliceWithKeyCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
func (c cmdable) ZMScore(ctx context.Context, key string, members ...string) *FloatSliceCmd {
args := make([]interface{}, 2+len(members))
args[0] = "zmscore"

View File

@ -3752,6 +3752,85 @@ var _ = Describe("Commands", func() {
}}))
})
It("should ZMPop", func() {
err := client.ZAdd(ctx, "zset", redis.Z{Score: 1, Member: "one"}).Err()
Expect(err).NotTo(HaveOccurred())
err = client.ZAdd(ctx, "zset", redis.Z{Score: 2, Member: "two"}).Err()
Expect(err).NotTo(HaveOccurred())
err = client.ZAdd(ctx, "zset", redis.Z{Score: 3, Member: "three"}).Err()
Expect(err).NotTo(HaveOccurred())
err = client.ZAdd(ctx, "zset2", redis.Z{Score: 1, Member: "one"}).Err()
Expect(err).NotTo(HaveOccurred())
err = client.ZAdd(ctx, "zset2", redis.Z{Score: 2, Member: "two"}).Err()
Expect(err).NotTo(HaveOccurred())
err = client.ZAdd(ctx, "zset2", redis.Z{Score: 3, Member: "three"}).Err()
Expect(err).NotTo(HaveOccurred())
key, elems, err := client.ZMPop(ctx, "min", 1, "zset").Result()
Expect(err).NotTo(HaveOccurred())
Expect(key).To(Equal("zset"))
Expect(elems).To(Equal([]redis.Z{{
Score: 1,
Member: "one",
}}))
_, _, err = client.ZMPop(ctx, "min", 1, "nosuchkey").Result()
Expect(err).To(Equal(redis.Nil))
err = client.ZAdd(ctx, "myzset", redis.Z{Score: 1, Member: "one"}).Err()
Expect(err).NotTo(HaveOccurred())
err = client.ZAdd(ctx, "myzset", redis.Z{Score: 2, Member: "two"}).Err()
Expect(err).NotTo(HaveOccurred())
err = client.ZAdd(ctx, "myzset", redis.Z{Score: 3, Member: "three"}).Err()
Expect(err).NotTo(HaveOccurred())
key, elems, err = client.ZMPop(ctx, "min", 1, "myzset").Result()
Expect(err).NotTo(HaveOccurred())
Expect(key).To(Equal("myzset"))
Expect(elems).To(Equal([]redis.Z{{
Score:1,
Member:"one",
}}))
key, elems, err = client.ZMPop(ctx, "max", 10, "myzset").Result()
Expect(err).NotTo(HaveOccurred())
Expect(key).To(Equal("myzset"))
Expect(elems).To(Equal([]redis.Z{{
Score:3,
Member:"three",
},{
Score: 2,
Member: "two",
}}))
err = client.ZAdd(ctx, "myzset2", redis.Z{Score: 4, Member: "four"}).Err()
Expect(err).NotTo(HaveOccurred())
err = client.ZAdd(ctx, "myzset2", redis.Z{Score: 5, Member: "five"}).Err()
Expect(err).NotTo(HaveOccurred())
err = client.ZAdd(ctx, "myzset2", redis.Z{Score: 6, Member: "six"}).Err()
Expect(err).NotTo(HaveOccurred())
key, elems, err = client.ZMPop(ctx, "min", 10, "myzset","myzset2").Result()
Expect(err).NotTo(HaveOccurred())
Expect(key).To(Equal("myzset2"))
Expect(elems).To(Equal([]redis.Z{{
Score:4,
Member:"four",
},{
Score: 5,
Member: "five",
},{
Score:6,
Member: "six",
}}))
})
It("should ZMScore", func() {
zmScore := client.ZMScore(ctx, "zset", "one", "three")
Expect(zmScore.Err()).NotTo(HaveOccurred())