From 6ecbcf6c90919350c42181ce34c1cbdfbd5d1463 Mon Sep 17 00:00:00 2001 From: Anurag Bandyopadhyay Date: Wed, 19 Apr 2023 20:07:41 +0530 Subject: [PATCH] Add ZRANK, ZREVRANK WITHSCORE (#2531) * feat: adding zrankwithscore and zrevrankwithscore commands : redis 7.2 * fix: test for non-existing members * fix: Error check * fix: string to float * add ZRankWithScore API for Cmdable interface Signed-off-by: monkey92t * add notes Signed-off-by: monkey92t --------- Signed-off-by: monkey92t Co-authored-by: Anuragkillswitch <70265851+Anuragkillswitch@users.noreply.github.com> Co-authored-by: monkey92t --- command.go | 60 ++++++++++++++++++++++++++++++++++++++++++++++++ commands.go | 18 +++++++++++++++ commands_test.go | 50 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+) diff --git a/command.go b/command.go index fc1ee8ac..c2655485 100644 --- a/command.go +++ b/command.go @@ -4724,3 +4724,63 @@ func (cmd *ClusterShardsCmd) readReply(rd *proto.Reader) error { return nil } + +// ----------------------------------------- + +type RankScore struct { + Rank int64 + Score float64 +} + +type RankWithScoreCmd struct { + baseCmd + + val RankScore +} + +var _ Cmder = (*RankWithScoreCmd)(nil) + +func NewRankWithScoreCmd(ctx context.Context, args ...interface{}) *RankWithScoreCmd { + return &RankWithScoreCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *RankWithScoreCmd) SetVal(val RankScore) { + cmd.val = val +} + +func (cmd *RankWithScoreCmd) Val() RankScore { + return cmd.val +} + +func (cmd *RankWithScoreCmd) Result() (RankScore, error) { + return cmd.val, cmd.err +} + +func (cmd *RankWithScoreCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *RankWithScoreCmd) readReply(rd *proto.Reader) error { + if err := rd.ReadFixedArrayLen(2); err != nil { + return err + } + + rank, err := rd.ReadInt() + if err != nil { + return err + } + + score, err := rd.ReadFloat() + if err != nil { + return err + } + + cmd.val = RankScore{Rank: rank, Score: score} + + return nil +} diff --git a/commands.go b/commands.go index 3721df4e..03055c90 100644 --- a/commands.go +++ b/commands.go @@ -373,6 +373,7 @@ type Cmdable interface { ZRangeArgsWithScores(ctx context.Context, z ZRangeArgs) *ZSliceCmd ZRangeStore(ctx context.Context, dst string, z ZRangeArgs) *IntCmd ZRank(ctx context.Context, key, member string) *IntCmd + ZRankWithScore(ctx context.Context, key, member string) *RankWithScoreCmd ZRem(ctx context.Context, key string, members ...interface{}) *IntCmd ZRemRangeByRank(ctx context.Context, key string, start, stop int64) *IntCmd ZRemRangeByScore(ctx context.Context, key, min, max string) *IntCmd @@ -383,6 +384,7 @@ type Cmdable interface { ZRevRangeByLex(ctx context.Context, key string, opt *ZRangeBy) *StringSliceCmd ZRevRangeByScoreWithScores(ctx context.Context, key string, opt *ZRangeBy) *ZSliceCmd ZRevRank(ctx context.Context, key, member string) *IntCmd + ZRevRankWithScore(ctx context.Context, key, member string) *RankWithScoreCmd ZScore(ctx context.Context, key, member string) *FloatCmd ZUnionStore(ctx context.Context, dest string, store *ZStore) *IntCmd ZRandMember(ctx context.Context, key string, count int) *StringSliceCmd @@ -2884,6 +2886,14 @@ func (c cmdable) ZRank(ctx context.Context, key, member string) *IntCmd { return cmd } +// ZRankWithScore according to the Redis documentation, if member does not exist +// in the sorted set or key does not exist, it will return a redis.Nil error. +func (c cmdable) ZRankWithScore(ctx context.Context, key, member string) *RankWithScoreCmd { + cmd := NewRankWithScoreCmd(ctx, "zrank", key, member, "withscore") + _ = c(ctx, cmd) + return cmd +} + func (c cmdable) ZRem(ctx context.Context, key string, members ...interface{}) *IntCmd { args := make([]interface{}, 2, 2+len(members)) args[0] = "zrem" @@ -2924,6 +2934,8 @@ func (c cmdable) ZRevRange(ctx context.Context, key string, start, stop int64) * return cmd } +// ZRevRangeWithScores according to the Redis documentation, if member does not exist +// in the sorted set or key does not exist, it will return a redis.Nil error. func (c cmdable) ZRevRangeWithScores(ctx context.Context, key string, start, stop int64) *ZSliceCmd { cmd := NewZSliceCmd(ctx, "zrevrange", key, start, stop, "withscores") _ = c(ctx, cmd) @@ -2974,6 +2986,12 @@ func (c cmdable) ZRevRank(ctx context.Context, key, member string) *IntCmd { return cmd } +func (c cmdable) ZRevRankWithScore(ctx context.Context, key, member string) *RankWithScoreCmd { + cmd := NewRankWithScoreCmd(ctx, "zrevrank", key, member, "withscore") + _ = c(ctx, cmd) + return cmd +} + func (c cmdable) ZScore(ctx context.Context, key, member string) *FloatCmd { cmd := NewFloatCmd(ctx, "zscore", key, member) _ = c(ctx, cmd) diff --git a/commands_test.go b/commands_test.go index e20470f2..ea01ae92 100644 --- a/commands_test.go +++ b/commands_test.go @@ -4737,6 +4737,31 @@ var _ = Describe("Commands", func() { Expect(zRank.Val()).To(Equal(int64(0))) }) + It("should ZRankWithScore", 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()) + + zRankWithScore := client.ZRankWithScore(ctx, "zset", "one") + Expect(zRankWithScore.Err()).NotTo(HaveOccurred()) + Expect(zRankWithScore.Result()).To(Equal(redis.RankScore{Rank: 0, Score: 1})) + + zRankWithScore = client.ZRankWithScore(ctx, "zset", "two") + Expect(zRankWithScore.Err()).NotTo(HaveOccurred()) + Expect(zRankWithScore.Result()).To(Equal(redis.RankScore{Rank: 1, Score: 2})) + + zRankWithScore = client.ZRankWithScore(ctx, "zset", "three") + Expect(zRankWithScore.Err()).NotTo(HaveOccurred()) + Expect(zRankWithScore.Result()).To(Equal(redis.RankScore{Rank: 2, Score: 3})) + + zRankWithScore = client.ZRankWithScore(ctx, "zset", "four") + Expect(zRankWithScore.Err()).To(HaveOccurred()) + Expect(zRankWithScore.Err()).To(Equal(redis.Nil)) + }) + It("should ZRem", func() { err := client.ZAdd(ctx, "zset", redis.Z{Score: 1, Member: "one"}).Err() Expect(err).NotTo(HaveOccurred()) @@ -5008,6 +5033,31 @@ var _ = Describe("Commands", func() { Expect(zRevRank.Val()).To(Equal(int64(0))) }) + It("should ZRevRankWithScore", 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()) + + zRevRankWithScore := client.ZRevRankWithScore(ctx, "zset", "one") + Expect(zRevRankWithScore.Err()).NotTo(HaveOccurred()) + Expect(zRevRankWithScore.Result()).To(Equal(redis.RankScore{Rank: 2, Score: 1})) + + zRevRankWithScore = client.ZRevRankWithScore(ctx, "zset", "two") + Expect(zRevRankWithScore.Err()).NotTo(HaveOccurred()) + Expect(zRevRankWithScore.Result()).To(Equal(redis.RankScore{Rank: 1, Score: 2})) + + zRevRankWithScore = client.ZRevRankWithScore(ctx, "zset", "three") + Expect(zRevRankWithScore.Err()).NotTo(HaveOccurred()) + Expect(zRevRankWithScore.Result()).To(Equal(redis.RankScore{Rank: 0, Score: 3})) + + zRevRankWithScore = client.ZRevRankWithScore(ctx, "zset", "four") + Expect(zRevRankWithScore.Err()).To(HaveOccurred()) + Expect(zRevRankWithScore.Err()).To(Equal(redis.Nil)) + }) + It("should ZScore", func() { zAdd := client.ZAdd(ctx, "zset", redis.Z{Score: 1.001, Member: "one"}) Expect(zAdd.Err()).NotTo(HaveOccurred())