summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy McCurdy <andy@andymccurdy.com>2018-11-01 14:09:42 -0700
committerGitHub <noreply@github.com>2018-11-01 14:09:42 -0700
commit4ef6dd0384d944e585e5fbfc16130d2bc2ba543d (patch)
tree80bdf54d3b049f1603436c5a0e0fa763f37e4ff8
parent724af050bc5a8a0c1d0975efd936c5fedb7b5d88 (diff)
parent6c4cd12425a6c840113696407b06e82424e87a72 (diff)
downloadredis-py-4ef6dd0384d944e585e5fbfc16130d2bc2ba543d.tar.gz
Merge pull request #1037 from itamarhaber/zpop
Adds support for ZPOPMAX, ZPOPMIN and their respective blocking variants
-rwxr-xr-xredis/client.py66
-rw-r--r--tests/test_commands.py50
2 files changed, 115 insertions, 1 deletions
diff --git a/redis/client.py b/redis/client.py
index a4d1cab..0cb6fca 100755
--- a/redis/client.py
+++ b/redis/client.py
@@ -448,9 +448,11 @@ class StrictRedis(object):
lambda r: r and set(r) or set()
),
string_keys_to_dict(
- 'ZRANGE ZRANGEBYSCORE ZREVRANGE ZREVRANGEBYSCORE',
+ 'ZPOPMAX ZPOPMIN ZRANGE ZRANGEBYSCORE ZREVRANGE ZREVRANGEBYSCORE',
zset_score_pairs
),
+ string_keys_to_dict('BZPOPMIN BZPOPMAX', \
+ lambda r: r and (r[0], r[1], float(r[2])) or None),
string_keys_to_dict('ZRANK ZREVRANK', int_or_none),
string_keys_to_dict('XREVRANGE XRANGE', parse_stream_list),
string_keys_to_dict('XREAD XREADGROUP', parse_xread),
@@ -2115,6 +2117,68 @@ class StrictRedis(object):
"""
return self.execute_command('ZLEXCOUNT', name, min, max)
+ def zpopmax(self, name, count=None):
+ """
+ Remove and return up to ``count`` members with the highest scores
+ from the sorted set ``name``.
+ """
+ args = (count is not None) and [count] or []
+ options = {
+ 'withscores': True
+ }
+ return self.execute_command('ZPOPMAX', name, *args, **options)
+
+ def zpopmin(self, name, count=None):
+ """
+ Remove and return up to ``count`` members with the lowest scores
+ from the sorted set ``name``.
+ """
+ args = (count is not None) and [count] or []
+ options = {
+ 'withscores': True
+ }
+ return self.execute_command('ZPOPMIN', name, *args, **options)
+
+ def bzpopmax(self, keys, timeout=0):
+ """
+ ZPOPMAX a value off of the first non-empty sorted set
+ named in the ``keys`` list.
+
+ If none of the sorted sets in ``keys`` has a value to ZPOPMAX,
+ then block for ``timeout`` seconds, or until a member gets added
+ to one of the sorted sets.
+
+ If timeout is 0, then block indefinitely.
+ """
+ if timeout is None:
+ timeout = 0
+ if isinstance(keys, basestring):
+ keys = [keys]
+ else:
+ keys = list(keys)
+ keys.append(timeout)
+ return self.execute_command('BZPOPMAX', *keys)
+
+ def bzpopmin(self, keys, timeout=0):
+ """
+ ZPOPMIN a value off of the first non-empty sorted set
+ named in the ``keys`` list.
+
+ If none of the sorted sets in ``keys`` has a value to ZPOPMIN,
+ then block for ``timeout`` seconds, or until a member gets added
+ to one of the sorted sets.
+
+ If timeout is 0, then block indefinitely.
+ """
+ if timeout is None:
+ timeout = 0
+ if isinstance(keys, basestring):
+ keys = [keys]
+ else:
+ keys = list(keys)
+ keys.append(timeout)
+ return self.execute_command('BZPOPMIN', *keys)
+
def zrange(self, name, start, end, desc=False, withscores=False,
score_cast_func=float):
"""
diff --git a/tests/test_commands.py b/tests/test_commands.py
index 22aafd3..579a85e 100644
--- a/tests/test_commands.py
+++ b/tests/test_commands.py
@@ -976,6 +976,56 @@ class TestRedisCommands(object):
assert r.zrange('d', 0, -1, withscores=True) == \
[(b('a3'), 20), (b('a1'), 23)]
+ @skip_if_server_version_lt('4.9.0')
+ def test_zpopmax(self, r):
+ r.delete('a')
+ r.zadd('a', a1=1, a2=2, a3=3)
+ assert r.zpopmax('a') == [(b('a3'), 3)]
+
+ # with count
+ assert r.zpopmax('a', count=2) == \
+ [(b('a2'), 2), (b('a1'), 1)]
+
+ @skip_if_server_version_lt('4.9.0')
+ def test_zpopmin(self, r):
+ r.delete('a')
+ r.zadd('a', a1=1, a2=2, a3=3)
+ assert r.zpopmin('a') == [(b('a1'), 1)]
+
+ # with count
+ assert r.zpopmin('a', count=2) == \
+ [(b('a2'), 2), (b('a3'), 3)]
+
+ @skip_if_server_version_lt('4.9.0')
+ def test_bzpopmax(self, r):
+ r.delete('a')
+ r.delete('b')
+ r.delete('c')
+ r.zadd('a', a1=1, a2=2)
+ r.zadd('b', b1=10, b2=20)
+ assert r.bzpopmax(['b', 'a'], timeout=1) == (b('b'), b('b2'), 20)
+ assert r.bzpopmax(['b', 'a'], timeout=1) == (b('b'), b('b1'), 10)
+ assert r.bzpopmax(['b', 'a'], timeout=1) == (b('a'), b('a2'), 2)
+ assert r.bzpopmax(['b', 'a'], timeout=1) == (b('a'), b('a1'), 1)
+ assert r.bzpopmax(['b', 'a'], timeout=1) is None
+ r.zadd('c', c1=100)
+ assert r.bzpopmax('c', timeout=1) == (b('c'), b('c1'), 100)
+
+ @skip_if_server_version_lt('4.9.0')
+ def test_bzpopmin(self, r):
+ r.delete('a')
+ r.delete('b')
+ r.delete('c')
+ r.zadd('a', a1=1, a2=2)
+ r.zadd('b', b1=10, b2=20)
+ assert r.bzpopmin(['b', 'a'], timeout=1) == (b('b'), b('b1'), 10)
+ assert r.bzpopmin(['b', 'a'], timeout=1) == (b('b'), b('b2'), 20)
+ assert r.bzpopmin(['b', 'a'], timeout=1) == (b('a'), b('a1'), 1)
+ assert r.bzpopmin(['b', 'a'], timeout=1) == (b('a'), b('a2'), 2)
+ assert r.bzpopmin(['b', 'a'], timeout=1) is None
+ r.zadd('c', c1=100)
+ assert r.bzpopmin('c', timeout=1) == (b('c'), b('c1'), 100)
+
def test_zrange(self, r):
r.zadd('a', a1=1, a2=2, a3=3)
assert r.zrange('a', 0, 1) == [b('a1'), b('a2')]