Skip to content

Commit

Permalink
Merge pull request #1191 from JerrodCarpenter/master
Browse files Browse the repository at this point in the history
✨ Add BZMPOP
  • Loading branch information
byroot authored May 29, 2023
2 parents ac2a651 + faf054d commit 1f4041a
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 5 deletions.
40 changes: 35 additions & 5 deletions lib/redis/commands/sorted_sets.rb
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,40 @@ 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.bzmpop('zset')
# #=> ['zset', ['a', 1.0]]
# @example With count option
# redis.bzmpop('zset', count: 2)
# #=> ['zset', [['a', 1.0], ['b', 2.0]]
#
# @params timeout [Float] a float value specifying the maximum number of seconds to block) elapses.
# A timeout of zero can be used to block indefinitely.
# @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 bzmpop(timeout, *keys, modifier: "MIN", count: nil)
raise ArgumentError, "Pick either MIN or MAX" unless modifier == "MIN" || modifier == "MAX"

args = [:bzmpop, timeout, keys.size, *keys, modifier]
args << "COUNT" << Integer(count) if count

send_blocking_command(args, timeout) 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 scores in the sorted set stored at key.
#
# @example Popping a member
Expand All @@ -187,11 +221,7 @@ 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
args << "COUNT" << Integer(count) if count

send_command(args) do |response|
response&.map do |entry|
Expand Down
7 changes: 7 additions & 0 deletions lib/redis/distributed.rb
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,13 @@ def zmscore(key, *members)
node_for(key).zmscore(key, *members)
end

# Iterate over keys, blocking and removing members from the first non empty sorted set found.
def bzmpop(timeout, *keys, modifier: "MIN", count: nil)
ensure_same_node(:bzmpop, keys) do |node|
node.bzmpop(timeout, *keys, modifier: modifier, count: count)
end
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|
Expand Down
14 changes: 14 additions & 0 deletions test/lint/sorted_sets.rb
Original file line number Diff line number Diff line change
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_bzmpop
target_version('7.0') do
assert_nil r.bzmpop(1.0, '{1}foo')

r.zadd('{1}foo', %w[0 a 1 b 2 c 3 d])
assert_equal ['{1}foo', [['a', 0.0]]], r.bzmpop(1.0, '{1}foo')
assert_equal ['{1}foo', [['b', 1.0], ['c', 2.0], ['d', 3.0]]], r.bzmpop(1.0, '{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.bzmpop(1.0, '{1}foo', '{1}foo2', modifier: "MAX")
end
end

def test_zmpop
target_version('7.0') do
assert_nil r.zmpop('{1}foo')
Expand Down

0 comments on commit 1f4041a

Please sign in to comment.