Skip to content

Commit faf054d

Browse files
✨ Add BZMPOP
1 parent 5904289 commit faf054d

File tree

3 files changed

+56
-5
lines changed

3 files changed

+56
-5
lines changed

lib/redis/commands/sorted_sets.rb

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,40 @@ 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.bzmpop('zset')
174+
# #=> ['zset', ['a', 1.0]]
175+
# @example With count option
176+
# redis.bzmpop('zset', count: 2)
177+
# #=> ['zset', [['a', 1.0], ['b', 2.0]]
178+
#
179+
# @params timeout [Float] a float value specifying the maximum number of seconds to block) elapses.
180+
# A timeout of zero can be used to block indefinitely.
181+
# @params key [String, Array<String>] one or more keys with sorted sets
182+
# @params modifier [String]
183+
# - when `"MIN"` - the elements popped are those with lowest scores
184+
# - when `"MAX"` - the elements popped are those with the highest scores
185+
# @params count [Integer] a number of members to pop
186+
#
187+
# @return [Array<String, Array<String, Float>>] list of popped elements and scores
188+
def bzmpop(timeout, *keys, modifier: "MIN", count: nil)
189+
raise ArgumentError, "Pick either MIN or MAX" unless modifier == "MIN" || modifier == "MAX"
190+
191+
args = [:bzmpop, timeout, keys.size, *keys, modifier]
192+
args << "COUNT" << Integer(count) if count
193+
194+
send_blocking_command(args, timeout) do |response|
195+
response&.map do |entry|
196+
case entry
197+
when String then entry
198+
when Array then entry.map { |pair| FloatifyPairs.call(pair) }.flatten(1)
199+
end
200+
end
201+
end
202+
end
203+
170204
# Removes and returns up to count members with scores in the sorted set stored at key.
171205
#
172206
# @example Popping a member
@@ -187,11 +221,7 @@ def zmpop(*keys, modifier: "MIN", count: nil)
187221
raise ArgumentError, "Pick either MIN or MAX" unless modifier == "MIN" || modifier == "MAX"
188222

189223
args = [:zmpop, keys.size, *keys, modifier]
190-
191-
if count
192-
args << "COUNT"
193-
args << Integer(count)
194-
end
224+
args << "COUNT" << Integer(count) if count
195225

196226
send_command(args) do |response|
197227
response&.map do |entry|

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, blocking and removing members from the first non empty sorted set found.
698+
def bzmpop(timeout, *keys, modifier: "MIN", count: nil)
699+
ensure_same_node(:bzmpop, keys) do |node|
700+
node.bzmpop(timeout, *keys, modifier: modifier, count: count)
701+
end
702+
end
703+
697704
# Iterate over keys, removing members from the first non empty sorted set found.
698705
def zmpop(*keys, modifier: "MIN", count: nil)
699706
ensure_same_node(:zmpop, keys) do |node|

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_bzmpop
483+
target_version('7.0') do
484+
assert_nil r.bzmpop(1.0, '{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.bzmpop(1.0, '{1}foo')
488+
assert_equal ['{1}foo', [['b', 1.0], ['c', 2.0], ['d', 3.0]]], r.bzmpop(1.0, '{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.bzmpop(1.0, '{1}foo', '{1}foo2', modifier: "MAX")
493+
end
494+
end
495+
482496
def test_zmpop
483497
target_version('7.0') do
484498
assert_nil r.zmpop('{1}foo')

0 commit comments

Comments
 (0)