Skip to content

Commit

Permalink
✨ ZMPOP
Browse files Browse the repository at this point in the history
Fix: #1189
  • Loading branch information
JerrodCarpenter authored and byroot committed May 16, 2023
1 parent a1e9c17 commit 5904289
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 0 deletions.
36 changes: 36 additions & 0 deletions lib/redis/commands/sorted_sets.rb
Expand Up @@ -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<String>] 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<String, Array<String, Float>>] list of popped elements and scores
def zmpop(*keys, modifier: "MIN", count: nil)
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.
#
Expand Down
7 changes: 7 additions & 0 deletions lib/redis/distributed.rb
Expand Up @@ -694,6 +694,13 @@ def zmscore(key, *members)
node_for(key).zmscore(key, *members)
end

# Iterate over keys, removing members from the first non empty sorted set found.
def zmpop(*keys, modifier: "MIN", count: nil)
ensure_same_node(:zmpop, keys) do |node|
node.zmpop(*keys, modifier: modifier, count: count)
end
end

# Return a range of members in a sorted set, by index, score or lexicographical ordering.
def zrange(key, start, stop, **options)
node_for(key).zrange(key, start, stop, **options)
Expand Down
14 changes: 14 additions & 0 deletions test/lint/sorted_sets.rb
Expand Up @@ -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('{1}foo')

r.zadd('{1}foo', %w[0 a 1 b 2 c 3 d])
assert_equal ['{1}foo', [['a', 0.0]]], r.zmpop('{1}foo')
assert_equal ['{1}foo', [['b', 1.0], ['c', 2.0], ['d', 3.0]]], r.zmpop('{1}foo', count: 4)

r.zadd('{1}foo', %w[0 a 1 b 2 c 3 d])
r.zadd('{1}foo2', %w[0 a 1 b 2 c 3 d])
assert_equal ['{1}foo', [['d', 3.0]]], r.zmpop('{1}foo', '{1}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')
Expand Down

0 comments on commit 5904289

Please sign in to comment.