Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions command.go
Original file line number Diff line number Diff line change
Expand Up @@ -4725,6 +4725,66 @@ 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
}

// -------------------------------------------

type ACLLogEntry struct {
Expand Down
53 changes: 51 additions & 2 deletions commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -499,6 +501,8 @@ type Cmdable interface {
ACLDryRun(ctx context.Context, username string, command ...interface{}) *StringCmd
ACLLog(ctx context.Context, count ...int64) *ACLLogCmd
ACLLogReset(ctx context.Context) *StatusCmd

ModuleLoadex(ctx context.Context, conf *ModuleLoadexConfig) *StringCmd
}

type StatefulCmdable interface {
Expand Down Expand Up @@ -2884,6 +2888,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"
Expand Down Expand Up @@ -2924,6 +2936,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)
Expand Down Expand Up @@ -2974,6 +2988,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)
Expand Down Expand Up @@ -3891,8 +3911,37 @@ func (c cmdable) ACLDryRun(ctx context.Context, username string, command ...inte
return cmd
}

// ModuleLoadexConfig struct is used to specify the arguments for the MODULE LOADEX command of redis.
// `MODULE LOADEX path [CONFIG name value [CONFIG name value ...]] [ARGS args [args ...]]`
type ModuleLoadexConfig struct {
Path string
Conf map[string]interface{}
Args []interface{}
}

func (c *ModuleLoadexConfig) toArgs() []interface{} {
args := make([]interface{}, 3, 3+len(c.Conf)*3+len(c.Args)*2)
args[0] = "MODULE"
args[1] = "LOADEX"
args[2] = c.Path
for k, v := range c.Conf {
args = append(args, "CONFIG", k, v)
}
for _, arg := range c.Args {
args = append(args, "ARGS", arg)
}
return args
}

// ModuleLoadex Redis `MODULE LOADEX path [CONFIG name value [CONFIG name value ...]] [ARGS args [args ...]]` command.
func (c cmdable) ModuleLoadex(ctx context.Context, conf *ModuleLoadexConfig) *StringCmd {
cmd := NewStringCmd(ctx, conf.toArgs()...)
_ = c(ctx, cmd)
return cmd
}

func (c cmdable) ACLLog(ctx context.Context, count ...int64) *ACLLogCmd {
args := make([]interface{}, 0, 1)
args := make([]interface{}, 0, 2+len(count))
args = append(args, "acl", "log")

if len(count) > 0 {
Expand All @@ -3902,7 +3951,7 @@ func (c cmdable) ACLLog(ctx context.Context, count ...int64) *ACLLogCmd {
args = append(args, count[0])
}

cmd := NewACLLogCmd(ctx, args)
cmd := NewACLLogCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
Expand Down
99 changes: 98 additions & 1 deletion commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1932,6 +1932,54 @@ var _ = Describe("Commands", func() {
Expect(dryRun.Val()).To(Equal("OK"))
})

It("should fail module loadex", func() {
dryRun := client.ModuleLoadex(ctx, &redis.ModuleLoadexConfig{
Path: "/path/to/non-existent-library.so",
Conf: map[string]interface{}{
"param1": "value1",
},
Args: []interface{}{
"arg1",
},
})
Expect(dryRun.Err()).To(HaveOccurred())
Expect(dryRun.Err().Error()).To(Equal("ERR Error loading the extension. Please check the server logs."))
})

It("converts the module loadex configuration to a slice of arguments correctly", func() {
conf := &redis.ModuleLoadexConfig{
Path: "/path/to/your/module.so",
Conf: map[string]interface{}{
"param1": "value1",
},
Args: []interface{}{
"arg1",
"arg2",
3,
},
}

args := conf.ToArgs()

// Test if the arguments are in the correct order
expectedArgs := []interface{}{
"MODULE",
"LOADEX",
"/path/to/your/module.so",
"CONFIG",
"param1",
"value1",
"ARGS",
"arg1",
"ARGS",
"arg2",
"ARGS",
3,
}

Expect(args).To(Equal(expectedArgs))
})

It("should ACL LOG", func() {
// Test without the count parameter
logs := client.ACLLog(ctx)
Expand Down Expand Up @@ -1991,7 +2039,6 @@ var _ = Describe("Commands", func() {
logEntries := logCmd.Val()
Expect(len(logEntries)).To(Equal(0))
})

})

Describe("hashes", func() {
Expand Down Expand Up @@ -4750,6 +4797,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())
Expand Down Expand Up @@ -5021,6 +5093,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())
Expand Down
4 changes: 4 additions & 0 deletions export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,7 @@ func (c *Ring) ShardByName(name string) *ringShard {
shard, _ := c.sharding.GetByName(name)
return shard
}

func (c *ModuleLoadexConfig) ToArgs() []interface{} {
return c.toArgs()
}
2 changes: 1 addition & 1 deletion main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ func startRedis(port string, args ...string) (*redisProcess, error) {
return nil, err
}

baseArgs := []string{filepath.Join(dir, "redis.conf"), "--port", port, "--dir", dir}
baseArgs := []string{filepath.Join(dir, "redis.conf"), "--port", port, "--dir", dir, "--enable-module-command", "yes"}
process, err := execCmd(redisServerBin, append(baseArgs, args...)...)
if err != nil {
return nil, err
Expand Down