diff --git a/lib/redis.rb b/lib/redis.rb index 3029f469e..72b5344bb 100644 --- a/lib/redis.rb +++ b/lib/redis.rb @@ -1897,6 +1897,46 @@ def zmscore(key, *members) end end + # Get one or more random members from a sorted set. + # + # @example Get one random member + # redis.zrandmember("zset") + # # => "a" + # @example Get multiple random members + # redis.zrandmember("zset", 2) + # # => ["a", "b"] + # @example Gem multiple random members with scores + # redis.zrandmember("zset", 2, with_scores: true) + # # => [["a", 2.0], ["b", 3.0]] + # + # @param [String] key + # @param [Integer] count + # @param [Hash] options + # - `:with_scores => true`: include scores in output + # + # @return [nil, String, Array, Array<[String, Float]>] + # - when `key` does not exist or set is empty, `nil` + # - when `count` is not specified, a member + # - when `count` is specified and `:with_scores` is not specified, an array of members + # - when `:with_scores` is specified, an array with `[member, score]` pairs + def zrandmember(key, count = nil, withscores: false, with_scores: withscores) + if with_scores && count.nil? + raise ArgumentError, "count argument must be specified" + end + + args = [:zrandmember, key] + args << count if count + + if with_scores + args << "WITHSCORES" + block = FloatifyPairs + end + + synchronize do |client| + client.call(args, &block) + end + end + # Return a range of members in a sorted set, by index. # # @example Retrieve all members from a sorted set diff --git a/lib/redis/distributed.rb b/lib/redis/distributed.rb index 62059bac4..0ae33d9f1 100644 --- a/lib/redis/distributed.rb +++ b/lib/redis/distributed.rb @@ -656,6 +656,11 @@ def zscore(key, member) node_for(key).zscore(key, member) end + # Get one or more random members from a sorted set. + def zrandmember(key, count = nil, **options) + node_for(key).zrandmember(key, count, **options) + end + # Get the scores associated with the given members in a sorted set. def zmscore(key, *members) node_for(key).zmscore(key, *members) diff --git a/test/lint/sorted_sets.rb b/test/lint/sorted_sets.rb index 869be4bfd..e1863d80c 100644 --- a/test/lint/sorted_sets.rb +++ b/test/lint/sorted_sets.rb @@ -358,6 +358,34 @@ def test_zmscore end end + def test_zrandmember + target_version("6.2") do + assert_nil r.zrandmember("foo") + + r.zadd "foo", 1.0, "s1" + r.zrem "foo", "s1" + assert_nil r.zrandmember("foo") + assert_equal [], r.zrandmember("foo", 1) + + r.zadd "foo", 1.0, "s1" + r.zadd "foo", 2.0, "s2" + r.zadd "foo", 3.0, "s3" + + 3.times do + assert ["s1", "s2", "s3"].include?(r.zrandmember("foo")) + end + + assert_equal 2, r.zrandmember("foo", 2).size + assert_equal 3, r.zrandmember("foo", 4).size + assert_equal 5, r.zrandmember("foo", -5).size + + r.zrandmember("foo", 2, with_scores: true).each do |(member, score)| + assert ["s1", "s2", "s3"].include?(member) + assert_instance_of Float, score + end + end + end + def test_zremrangebyrank r.zadd "foo", 10, "s1" r.zadd "foo", 20, "s2"