Skip to content

Commit 4f4adad

Browse files
add support to ZRANGE and ZRANGESTORE parameters (#1603)
1 parent 42227d2 commit 4f4adad

File tree

2 files changed

+181
-122
lines changed

2 files changed

+181
-122
lines changed

redis/commands.py

Lines changed: 130 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,7 +1073,7 @@ def hrandfield(self, key, count=None, withvalues=False):
10731073
return self.execute_command("HRANDFIELD", key, *params)
10741074

10751075
def randomkey(self):
1076-
"Returns the name of a random key"
1076+
"""Returns the name of a random key"""
10771077
return self.execute_command('RANDOMKEY')
10781078

10791079
def rename(self, src, dst):
@@ -1083,7 +1083,7 @@ def rename(self, src, dst):
10831083
return self.execute_command('RENAME', src, dst)
10841084

10851085
def renamenx(self, src, dst):
1086-
"Rename key ``src`` to ``dst`` if ``dst`` doesn't already exist"
1086+
"""Rename key ``src`` to ``dst`` if ``dst`` doesn't already exist"""
10871087
return self.execute_command('RENAMENX', src, dst)
10881088

10891089
def restore(self, name, ttl, value, replace=False, absttl=False,
@@ -1545,32 +1545,25 @@ def sort(self, name, start=None, num=None, by=None, get=None,
15451545

15461546
pieces = [name]
15471547
if by is not None:
1548-
pieces.append(b'BY')
1549-
pieces.append(by)
1548+
pieces.extend([b'BY', by])
15501549
if start is not None and num is not None:
1551-
pieces.append(b'LIMIT')
1552-
pieces.append(start)
1553-
pieces.append(num)
1550+
pieces.extend([b'LIMIT', start, num])
15541551
if get is not None:
15551552
# If get is a string assume we want to get a single value.
15561553
# Otherwise assume it's an interable and we want to get multiple
15571554
# values. We can't just iterate blindly because strings are
15581555
# iterable.
15591556
if isinstance(get, (bytes, str)):
1560-
pieces.append(b'GET')
1561-
pieces.append(get)
1557+
pieces.extend([b'GET', get])
15621558
else:
15631559
for g in get:
1564-
pieces.append(b'GET')
1565-
pieces.append(g)
1560+
pieces.extend([b'GET', g])
15661561
if desc:
15671562
pieces.append(b'DESC')
15681563
if alpha:
15691564
pieces.append(b'ALPHA')
15701565
if store is not None:
1571-
pieces.append(b'STORE')
1572-
pieces.append(store)
1573-
1566+
pieces.extend([b'STORE', store])
15741567
if groups:
15751568
if not get or isinstance(get, (bytes, str)) or len(get) < 2:
15761569
raise DataError('when using "groups" the "get" argument '
@@ -1729,15 +1722,15 @@ def zscan_iter(self, name, match=None, count=None,
17291722

17301723
# SET COMMANDS
17311724
def sadd(self, name, *values):
1732-
"Add ``value(s)`` to set ``name``"
1725+
"""Add ``value(s)`` to set ``name``"""
17331726
return self.execute_command('SADD', name, *values)
17341727

17351728
def scard(self, name):
1736-
"Return the number of elements in set ``name``"
1729+
"""Return the number of elements in set ``name``"""
17371730
return self.execute_command('SCARD', name)
17381731

17391732
def sdiff(self, keys, *args):
1740-
"Return the difference of sets specified by ``keys``"
1733+
"""Return the difference of sets specified by ``keys``"""
17411734
args = list_or_args(keys, args)
17421735
return self.execute_command('SDIFF', *args)
17431736

@@ -1750,7 +1743,7 @@ def sdiffstore(self, dest, keys, *args):
17501743
return self.execute_command('SDIFFSTORE', dest, *args)
17511744

17521745
def sinter(self, keys, *args):
1753-
"Return the intersection of sets specified by ``keys``"
1746+
"""Return the intersection of sets specified by ``keys``"""
17541747
args = list_or_args(keys, args)
17551748
return self.execute_command('SINTER', *args)
17561749

@@ -1763,15 +1756,17 @@ def sinterstore(self, dest, keys, *args):
17631756
return self.execute_command('SINTERSTORE', dest, *args)
17641757

17651758
def sismember(self, name, value):
1766-
"Return a boolean indicating if ``value`` is a member of set ``name``"
1759+
"""
1760+
Return a boolean indicating if ``value`` is a member of set ``name``
1761+
"""
17671762
return self.execute_command('SISMEMBER', name, value)
17681763

17691764
def smembers(self, name):
1770-
"Return all members of the set ``name``"
1765+
"""Return all members of the set ``name``"""
17711766
return self.execute_command('SMEMBERS', name)
17721767

17731768
def smove(self, src, dst, value):
1774-
"Move ``value`` from set ``src`` to set ``dst`` atomically"
1769+
"""Move ``value`` from set ``src`` to set ``dst`` atomically"""
17751770
return self.execute_command('SMOVE', src, dst, value)
17761771

17771772
def spop(self, name, count=None):
@@ -1850,8 +1845,7 @@ def xadd(self, name, fields, id='*', maxlen=None, approximate=True,
18501845
pieces.append(b'~')
18511846
pieces.append(minid)
18521847
if limit is not None:
1853-
pieces.append(b"LIMIT")
1854-
pieces.append(limit)
1848+
pieces.extend([b'LIMIT', limit])
18551849
if nomkstream:
18561850
pieces.append(b'NOMKSTREAM')
18571851
pieces.append(id)
@@ -2440,41 +2434,113 @@ def bzpopmin(self, keys, timeout=0):
24402434
keys.append(timeout)
24412435
return self.execute_command('BZPOPMIN', *keys)
24422436

2437+
def _zrange(self, command, dest, name, start, end, desc=False,
2438+
byscore=False, bylex=False, withscores=False,
2439+
score_cast_func=float, offset=None, num=None):
2440+
if byscore and bylex:
2441+
raise DataError("``byscore`` and ``bylex`` can not be "
2442+
"specified together.")
2443+
if (offset is not None and num is None) or \
2444+
(num is not None and offset is None):
2445+
raise DataError("``offset`` and ``num`` must both be specified.")
2446+
if bylex and withscores:
2447+
raise DataError("``withscores`` not supported in combination "
2448+
"with ``bylex``.")
2449+
pieces = [command]
2450+
if dest:
2451+
pieces.append(dest)
2452+
pieces.extend([name, start, end])
2453+
if byscore:
2454+
pieces.append('BYSCORE')
2455+
if bylex:
2456+
pieces.append('BYLEX')
2457+
if desc:
2458+
pieces.append('REV')
2459+
if offset is not None and num is not None:
2460+
pieces.extend(['LIMIT', offset, num])
2461+
if withscores:
2462+
pieces.append('WITHSCORES')
2463+
options = {
2464+
'withscores': withscores,
2465+
'score_cast_func': score_cast_func
2466+
}
2467+
return self.execute_command(*pieces, **options)
2468+
24432469
def zrange(self, name, start, end, desc=False, withscores=False,
2444-
score_cast_func=float):
2470+
score_cast_func=float, byscore=False, bylex=False,
2471+
offset=None, num=None):
24452472
"""
24462473
Return a range of values from sorted set ``name`` between
24472474
``start`` and ``end`` sorted in ascending order.
24482475
24492476
``start`` and ``end`` can be negative, indicating the end of the range.
24502477
2451-
``desc`` a boolean indicating whether to sort the results descendingly
2478+
``desc`` a boolean indicating whether to sort the results in reversed
2479+
order.
24522480
24532481
``withscores`` indicates to return the scores along with the values.
2482+
The return type is a list of (value, score) pairs.
2483+
2484+
``score_cast_func`` a callable used to cast the score return value.
2485+
2486+
``byscore`` when set to True, returns the range of elements from the
2487+
sorted set having scores equal or between ``start`` and ``end``.
2488+
2489+
``bylex`` when set to True, returns the range of elements from the
2490+
sorted set between the ``start`` and ``end`` lexicographical closed
2491+
range intervals.
2492+
Valid ``start`` and ``end`` must start with ( or [, in order to specify
2493+
whether the range interval is exclusive or inclusive, respectively.
2494+
2495+
``offset`` and ``num`` are specified, then return a slice of the range.
2496+
Can't be provided when using ``bylex``.
2497+
"""
2498+
return self._zrange('ZRANGE', None, name, start, end, desc, byscore,
2499+
bylex, withscores, score_cast_func, offset, num)
2500+
2501+
def zrevrange(self, name, start, end, withscores=False,
2502+
score_cast_func=float):
2503+
"""
2504+
Return a range of values from sorted set ``name`` between
2505+
``start`` and ``end`` sorted in descending order.
2506+
2507+
``start`` and ``end`` can be negative, indicating the end of the range.
2508+
2509+
``withscores`` indicates to return the scores along with the values
24542510
The return type is a list of (value, score) pairs
24552511
24562512
``score_cast_func`` a callable used to cast the score return value
24572513
"""
2458-
if desc:
2459-
return self.zrevrange(name, start, end, withscores,
2460-
score_cast_func)
2461-
pieces = ['ZRANGE', name, start, end]
2462-
if withscores:
2463-
pieces.append(b'WITHSCORES')
2464-
options = {
2465-
'withscores': withscores,
2466-
'score_cast_func': score_cast_func
2467-
}
2468-
return self.execute_command(*pieces, **options)
2514+
return self.zrange(name, start, end, desc=True,
2515+
withscores=withscores,
2516+
score_cast_func=score_cast_func)
24692517

2470-
def zrangestore(self, dest, name, start, end):
2518+
def zrangestore(self, dest, name, start, end,
2519+
byscore=False, bylex=False, desc=False,
2520+
offset=None, num=None):
24712521
"""
24722522
Stores in ``dest`` the result of a range of values from sorted set
24732523
``name`` between ``start`` and ``end`` sorted in ascending order.
24742524
24752525
``start`` and ``end`` can be negative, indicating the end of the range.
2526+
2527+
``byscore`` when set to True, returns the range of elements from the
2528+
sorted set having scores equal or between ``start`` and ``end``.
2529+
2530+
``bylex`` when set to True, returns the range of elements from the
2531+
sorted set between the ``start`` and ``end`` lexicographical closed
2532+
range intervals.
2533+
Valid ``start`` and ``end`` must start with ( or [, in order to specify
2534+
whether the range interval is exclusive or inclusive, respectively.
2535+
2536+
``desc`` a boolean indicating whether to sort the results in reversed
2537+
order.
2538+
2539+
``offset`` and ``num`` are specified, then return a slice of the range.
2540+
Can't be provided when using ``bylex``.
24762541
"""
2477-
return self.execute_command('ZRANGESTORE', dest, name, start, end)
2542+
return self._zrange('ZRANGESTORE', dest, name, start, end, desc,
2543+
byscore, bylex, False, None, offset, num)
24782544

24792545
def zrangebylex(self, name, min, max, start=None, num=None):
24802546
"""
@@ -2484,13 +2550,7 @@ def zrangebylex(self, name, min, max, start=None, num=None):
24842550
If ``start`` and ``num`` are specified, then return a slice of the
24852551
range.
24862552
"""
2487-
if (start is not None and num is None) or \
2488-
(num is not None and start is None):
2489-
raise DataError("``start`` and ``num`` must both be specified")
2490-
pieces = ['ZRANGEBYLEX', name, min, max]
2491-
if start is not None and num is not None:
2492-
pieces.extend([b'LIMIT', start, num])
2493-
return self.execute_command(*pieces)
2553+
return self.zrange(name, min, max, bylex=True, offset=start, num=num)
24942554

24952555
def zrevrangebylex(self, name, max, min, start=None, num=None):
24962556
"""
@@ -2500,13 +2560,8 @@ def zrevrangebylex(self, name, max, min, start=None, num=None):
25002560
If ``start`` and ``num`` are specified, then return a slice of the
25012561
range.
25022562
"""
2503-
if (start is not None and num is None) or \
2504-
(num is not None and start is None):
2505-
raise DataError("``start`` and ``num`` must both be specified")
2506-
pieces = ['ZREVRANGEBYLEX', name, max, min]
2507-
if start is not None and num is not None:
2508-
pieces.extend([b'LIMIT', start, num])
2509-
return self.execute_command(*pieces)
2563+
return self.zrange(name, max, min, desc=True,
2564+
bylex=True, offset=start, num=num)
25102565

25112566
def zrangebyscore(self, name, min, max, start=None, num=None,
25122567
withscores=False, score_cast_func=float):
@@ -2522,19 +2577,29 @@ def zrangebyscore(self, name, min, max, start=None, num=None,
25222577
25232578
`score_cast_func`` a callable used to cast the score return value
25242579
"""
2525-
if (start is not None and num is None) or \
2526-
(num is not None and start is None):
2527-
raise DataError("``start`` and ``num`` must both be specified")
2528-
pieces = ['ZRANGEBYSCORE', name, min, max]
2529-
if start is not None and num is not None:
2530-
pieces.extend([b'LIMIT', start, num])
2531-
if withscores:
2532-
pieces.append(b'WITHSCORES')
2533-
options = {
2534-
'withscores': withscores,
2535-
'score_cast_func': score_cast_func
2536-
}
2537-
return self.execute_command(*pieces, **options)
2580+
return self.zrange(name, min, max, byscore=True,
2581+
offset=start, num=num,
2582+
withscores=withscores,
2583+
score_cast_func=score_cast_func)
2584+
2585+
def zrevrangebyscore(self, name, max, min, start=None, num=None,
2586+
withscores=False, score_cast_func=float):
2587+
"""
2588+
Return a range of values from the sorted set ``name`` with scores
2589+
between ``min`` and ``max`` in descending order.
2590+
2591+
If ``start`` and ``num`` are specified, then return a slice
2592+
of the range.
2593+
2594+
``withscores`` indicates to return the scores along with the values.
2595+
The return type is a list of (value, score) pairs
2596+
2597+
``score_cast_func`` a callable used to cast the score return value
2598+
"""
2599+
return self.zrange(name, max, min, desc=True,
2600+
byscore=True, offset=start,
2601+
num=num, withscores=withscores,
2602+
score_cast_func=score_cast_func)
25382603

25392604
def zrank(self, name, value):
25402605
"""
@@ -2572,56 +2637,6 @@ def zremrangebyscore(self, name, min, max):
25722637
"""
25732638
return self.execute_command('ZREMRANGEBYSCORE', name, min, max)
25742639

2575-
def zrevrange(self, name, start, end, withscores=False,
2576-
score_cast_func=float):
2577-
"""
2578-
Return a range of values from sorted set ``name`` between
2579-
``start`` and ``end`` sorted in descending order.
2580-
2581-
``start`` and ``end`` can be negative, indicating the end of the range.
2582-
2583-
``withscores`` indicates to return the scores along with the values
2584-
The return type is a list of (value, score) pairs
2585-
2586-
``score_cast_func`` a callable used to cast the score return value
2587-
"""
2588-
pieces = ['ZREVRANGE', name, start, end]
2589-
if withscores:
2590-
pieces.append(b'WITHSCORES')
2591-
options = {
2592-
'withscores': withscores,
2593-
'score_cast_func': score_cast_func
2594-
}
2595-
return self.execute_command(*pieces, **options)
2596-
2597-
def zrevrangebyscore(self, name, max, min, start=None, num=None,
2598-
withscores=False, score_cast_func=float):
2599-
"""
2600-
Return a range of values from the sorted set ``name`` with scores
2601-
between ``min`` and ``max`` in descending order.
2602-
2603-
If ``start`` and ``num`` are specified, then return a slice
2604-
of the range.
2605-
2606-
``withscores`` indicates to return the scores along with the values.
2607-
The return type is a list of (value, score) pairs
2608-
2609-
``score_cast_func`` a callable used to cast the score return value
2610-
"""
2611-
if (start is not None and num is None) or \
2612-
(num is not None and start is None):
2613-
raise DataError("``start`` and ``num`` must both be specified")
2614-
pieces = ['ZREVRANGEBYSCORE', name, max, min]
2615-
if start is not None and num is not None:
2616-
pieces.extend([b'LIMIT', start, num])
2617-
if withscores:
2618-
pieces.append(b'WITHSCORES')
2619-
options = {
2620-
'withscores': withscores,
2621-
'score_cast_func': score_cast_func
2622-
}
2623-
return self.execute_command(*pieces, **options)
2624-
26252640
def zrevrank(self, name, value):
26262641
"""
26272642
Returns a 0-based value indicating the descending rank of

0 commit comments

Comments
 (0)