diff --git a/lib/redis/commands/sorted_sets.rb b/lib/redis/commands/sorted_sets.rb index d7423050..600f959f 100644 --- a/lib/redis/commands/sorted_sets.rb +++ b/lib/redis/commands/sorted_sets.rb @@ -167,6 +167,42 @@ def zpopmin(key, count = nil) end end + # Removes and returns up to count members with scores in the sorted set stored at key. + # + # @example Popping a member + # redis.zmpop('zset') + # #=> ['zset', ['a', 1.0]] + # @example With count option + # redis.zmpop('zset', count: 2) + # #=> ['zset', [['a', 1.0], ['b', 2.0]] + # + # @params key [String, Array] one or more keys with sorted sets + # @params modifier [String] + # - when `"MIN"` - the elements popped are those with lowest scores + # - when `"MAX"` - the elements popped are those with the highest scores + # @params count [Integer] a number of members to pop + # + # @return [Array>] list of popped elements and scores + def zmpop(*keys, modifier: "MIN", count: 1) + raise ArgumentError, "Pick either MIN or MAX" unless modifier == "MIN" || modifier == "MAX" + + args = [:zmpop, keys.size, *keys, modifier] + + if count + args << "COUNT" + args << Integer(count) + end + + send_command(args) do |response| + response&.map do |entry| + case entry + when String then entry + when Array then entry.map { |pair| FloatifyPairs.call(pair) }.flatten(1) + end + end + end + end + # Removes and returns up to count members with the highest scores in the sorted set stored at keys, # or block until one is available. # diff --git a/test/lint/sorted_sets.rb b/test/lint/sorted_sets.rb index c523dc3f..c41a94a6 100644 --- a/test/lint/sorted_sets.rb +++ b/test/lint/sorted_sets.rb @@ -479,6 +479,20 @@ def test_zpopmin assert_equal [['d', 3.0]], r.zrange('foo', 0, -1, with_scores: true) end + def test_zmpop + target_version('7.0') do + assert_nil r.zmpop('foo') + + r.zadd('foo', %w[0 a 1 b 2 c 3 d]) + assert_equal ['foo', [['a', 0.0]]], r.zmpop('foo') + assert_equal ['foo', [['b', 1.0], ['c', 2.0], ['d', 3.0]]], r.zmpop('foo', count: 4) + + r.zadd('foo', %w[0 a 1 b 2 c 3 d]) + r.zadd('foo2', %w[0 a 1 b 2 c 3 d]) + assert_equal ['foo', [['d', 3.0]]], r.zmpop('foo', 'foo2', modifier: "MAX") + end + end + def test_zremrangebylex r.zadd('foo', %w[0 a 0 b 0 c 0 d 0 e 0 f 0 g]) assert_equal 5, r.zremrangebylex('foo', '(b', '[g')