Skip to content

Commit 5904289

Browse files
JerrodCarpenterbyroot
authored andcommitted
✨ ZMPOP
Fix: #1189
1 parent a1e9c17 commit 5904289

File tree

3 files changed

+57
-0
lines changed

3 files changed

+57
-0
lines changed

lib/redis/commands/sorted_sets.rb

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,42 @@ def zpopmin(key, count = nil)
167167
end
168168
end
169169

170+
# Removes and returns up to count members with scores in the sorted set stored at key.
171+
#
172+
# @example Popping a member
173+
# redis.zmpop('zset')
174+
# #=> ['zset', ['a', 1.0]]
175+
# @example With count option
176+
# redis.zmpop('zset', count: 2)
177+
# #=> ['zset', [['a', 1.0], ['b', 2.0]]
178+
#
179+
# @params key [String, Array<String>] one or more keys with sorted sets
180+
# @params modifier [String]
181+
# - when `"MIN"` - the elements popped are those with lowest scores
182+
# - when `"MAX"` - the elements popped are those with the highest scores
183+
# @params count [Integer] a number of members to pop
184+
#
185+
# @return [Array<String, Array<String, Float>>] list of popped elements and scores
186+
def zmpop(*keys, modifier: "MIN", count: nil)
187+
raise ArgumentError, "Pick either MIN or MAX" unless modifier == "MIN" || modifier == "MAX"
188+
189+
args = [:zmpop, keys.size, *keys, modifier]
190+
191+
if count
192+
args << "COUNT"
193+
args << Integer(count)
194+
end
195+
196+
send_command(args) do |response|
197+
response&.map do |entry|
198+
case entry
199+
when String then entry
200+
when Array then entry.map { |pair| FloatifyPairs.call(pair) }.flatten(1)
201+
end
202+
end
203+
end
204+
end
205+
170206
# Removes and returns up to count members with the highest scores in the sorted set stored at keys,
171207
# or block until one is available.
172208
#

lib/redis/distributed.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,13 @@ def zmscore(key, *members)
694694
node_for(key).zmscore(key, *members)
695695
end
696696

697+
# Iterate over keys, removing members from the first non empty sorted set found.
698+
def zmpop(*keys, modifier: "MIN", count: nil)
699+
ensure_same_node(:zmpop, keys) do |node|
700+
node.zmpop(*keys, modifier: modifier, count: count)
701+
end
702+
end
703+
697704
# Return a range of members in a sorted set, by index, score or lexicographical ordering.
698705
def zrange(key, start, stop, **options)
699706
node_for(key).zrange(key, start, stop, **options)

test/lint/sorted_sets.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,20 @@ def test_zpopmin
479479
assert_equal [['d', 3.0]], r.zrange('foo', 0, -1, with_scores: true)
480480
end
481481

482+
def test_zmpop
483+
target_version('7.0') do
484+
assert_nil r.zmpop('{1}foo')
485+
486+
r.zadd('{1}foo', %w[0 a 1 b 2 c 3 d])
487+
assert_equal ['{1}foo', [['a', 0.0]]], r.zmpop('{1}foo')
488+
assert_equal ['{1}foo', [['b', 1.0], ['c', 2.0], ['d', 3.0]]], r.zmpop('{1}foo', count: 4)
489+
490+
r.zadd('{1}foo', %w[0 a 1 b 2 c 3 d])
491+
r.zadd('{1}foo2', %w[0 a 1 b 2 c 3 d])
492+
assert_equal ['{1}foo', [['d', 3.0]]], r.zmpop('{1}foo', '{1}foo2', modifier: "MAX")
493+
end
494+
end
495+
482496
def test_zremrangebylex
483497
r.zadd('foo', %w[0 a 0 b 0 c 0 d 0 e 0 f 0 g])
484498
assert_equal 5, r.zremrangebylex('foo', '(b', '[g')

0 commit comments

Comments
 (0)