summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvital Fine <79420960+AvitalFineRedis@users.noreply.github.com>2021-09-01 13:13:42 +0300
committerGitHub <noreply@github.com>2021-09-01 13:13:42 +0300
commit0f8d0dcbb3a7f759843f3f89e413f9333d027c98 (patch)
tree52436a142a53673173f59d530394d0d09137ee19
parente53227cf68c065b4d31f39cdde7c85c5e91dd1bf (diff)
downloadredis-py-0f8d0dcbb3a7f759843f3f89e413f9333d027c98.tar.gz
GEOSEARCH and GEOSEARCHSTORE (#1526)
* GEOSEARCH and GEOSEARCHSTORE * negative test * change georadius_generic to geosearch_generic * add documentations to the functions * add docstring to the parser * farest
-rwxr-xr-xredis/client.py16
-rw-r--r--redis/commands.py130
-rw-r--r--tests/test_commands.py310
3 files changed, 370 insertions, 86 deletions
diff --git a/redis/client.py b/redis/client.py
index a4d7f6b..019939a 100755
--- a/redis/client.py
+++ b/redis/client.py
@@ -472,10 +472,15 @@ def parse_cluster_nodes(response, **options):
return dict(_parse_node_line(line) for line in raw_lines)
-def parse_georadius_generic(response, **options):
+def parse_geosearch_generic(response, **options):
+ """
+ Parse the response of 'GEOSEARCH', GEORADIUS' and 'GEORADIUSBYMEMBER'
+ commands according to 'withdist', 'withhash' and 'withcoord' labels.
+ """
if options['store'] or options['store_dist']:
- # `store` and `store_diff` cant be combined
+ # `store` and `store_dist` cant be combined
# with other command arguments.
+ # relevant to 'GEORADIUS' and 'GEORADIUSBYMEMBER'
return response
if type(response) != list:
@@ -483,7 +488,7 @@ def parse_georadius_generic(response, **options):
else:
response_list = response
- if not options['withdist'] and not options['withcoord']\
+ if not options['withdist'] and not options['withcoord'] \
and not options['withhash']:
# just a bunch of places
return response_list
@@ -695,8 +700,9 @@ class Redis(Commands, object):
'GEOPOS': lambda r: list(map(lambda ll: (float(ll[0]),
float(ll[1]))
if ll is not None else None, r)),
- 'GEORADIUS': parse_georadius_generic,
- 'GEORADIUSBYMEMBER': parse_georadius_generic,
+ 'GEOSEARCH': parse_geosearch_generic,
+ 'GEORADIUS': parse_geosearch_generic,
+ 'GEORADIUSBYMEMBER': parse_geosearch_generic,
'HGETALL': lambda r: r and pairs_to_dict(r) or {},
'HSCAN': parse_hscan,
'INFO': parse_info,
diff --git a/redis/commands.py b/redis/commands.py
index 6e816de..4148f98 100644
--- a/redis/commands.py
+++ b/redis/commands.py
@@ -2924,6 +2924,136 @@ class Commands:
return self.execute_command(command, *pieces, **kwargs)
+ def geosearch(self, name, member=None, longitude=None, latitude=None,
+ unit='m', radius=None, width=None, height=None, sort=None,
+ count=None, any=False, withcoord=False,
+ withdist=False, withhash=False):
+ """
+ Return the members of specified key identified by the
+ ``name`` argument, which are within the borders of the
+ area specified by a given shape. This command extends the
+ GEORADIUS command, so in addition to searching within circular
+ areas, it supports searching within rectangular areas.
+ This command should be used in place of the deprecated
+ GEORADIUS and GEORADIUSBYMEMBER commands.
+ ``member`` Use the position of the given existing
+ member in the sorted set. Can't be given with ``longitude``
+ and ``latitude``.
+ ``longitude`` and ``latitude`` Use the position given by
+ this coordinates. Can't be given with ``member``
+ ``radius`` Similar to GEORADIUS, search inside circular
+ area according the given radius. Can't be given with
+ ``height`` and ``width``.
+ ``height`` and ``width`` Search inside an axis-aligned
+ rectangle, determined by the given height and width.
+ Can't be given with ``radius``
+ ``unit`` must be one of the following : m, km, mi, ft.
+ `m` for meters (the default value), `km` for kilometers,
+ `mi` for miles and `ft` for feet.
+ ``sort`` indicates to return the places in a sorted way,
+ ASC for nearest to farest and DESC for farest to nearest.
+ ``count`` limit the results to the first count matching items.
+ ``any`` is set to True, the command will return as soon as
+ enough matches are found. Can't be provided without ``count``
+ ``withdist`` indicates to return the distances of each place.
+ ``withcoord`` indicates to return the latitude and longitude of
+ each place.
+ ``withhash`` indicates to return the geohash string of each place.
+ """
+
+ return self._geosearchgeneric('GEOSEARCH',
+ name, member=member, longitude=longitude,
+ latitude=latitude, unit=unit,
+ radius=radius, width=width,
+ height=height, sort=sort, count=count,
+ any=any, withcoord=withcoord,
+ withdist=withdist, withhash=withhash,
+ store=None, store_dist=None)
+
+ def geosearchstore(self, dest, name, member=None, longitude=None,
+ latitude=None, unit='m', radius=None, width=None,
+ height=None, sort=None, count=None, any=False,
+ storedist=False):
+ """
+ This command is like GEOSEARCH, but stores the result in
+ ``dest``. By default, it stores the results in the destination
+ sorted set with their geospatial information.
+ if ``store_dist`` set to True, the command will stores the
+ items in a sorted set populated with their distance from the
+ center of the circle or box, as a floating-point number.
+ """
+ return self._geosearchgeneric('GEOSEARCHSTORE',
+ dest, name, member=member,
+ longitude=longitude, latitude=latitude,
+ unit=unit, radius=radius, width=width,
+ height=height, sort=sort, count=count,
+ any=any, withcoord=None,
+ withdist=None, withhash=None,
+ store=None, store_dist=storedist)
+
+ def _geosearchgeneric(self, command, *args, **kwargs):
+ pieces = list(args)
+
+ # FROMMEMBER or FROMLONLAT
+ if kwargs['member'] is None:
+ if kwargs['longitude'] is None or kwargs['latitude'] is None:
+ raise DataError("GEOSEARCH must have member or"
+ " longitude and latitude")
+ if kwargs['member']:
+ if kwargs['longitude'] or kwargs['latitude']:
+ raise DataError("GEOSEARCH member and longitude or latitude"
+ " cant be set together")
+ pieces.extend([b'FROMMEMBER', kwargs['member']])
+ if kwargs['longitude'] and kwargs['latitude']:
+ pieces.extend([b'FROMLONLAT',
+ kwargs['longitude'], kwargs['latitude']])
+
+ # BYRADIUS or BYBOX
+ if kwargs['radius'] is None:
+ if kwargs['width'] is None or kwargs['height'] is None:
+ raise DataError("GEOSEARCH must have radius or"
+ " width and height")
+ if kwargs['unit'] is None:
+ raise DataError("GEOSEARCH must have unit")
+ if kwargs['unit'].lower() not in ('m', 'km', 'mi', 'ft'):
+ raise DataError("GEOSEARCH invalid unit")
+ if kwargs['radius']:
+ if kwargs['width'] or kwargs['height']:
+ raise DataError("GEOSEARCH radius and width or height"
+ " cant be set together")
+ pieces.extend([b'BYRADIUS', kwargs['radius'], kwargs['unit']])
+ if kwargs['width'] and kwargs['height']:
+ pieces.extend([b'BYBOX',
+ kwargs['width'], kwargs['height'], kwargs['unit']])
+
+ # sort
+ if kwargs['sort']:
+ if kwargs['sort'].upper() == 'ASC':
+ pieces.append(b'ASC')
+ elif kwargs['sort'].upper() == 'DESC':
+ pieces.append(b'DESC')
+ else:
+ raise DataError("GEOSEARCH invalid sort")
+
+ # count any
+ if kwargs['count']:
+ pieces.extend([b'COUNT', kwargs['count']])
+ if kwargs['any']:
+ pieces.append(b'ANY')
+ elif kwargs['any']:
+ raise DataError("GEOSEARCH any can't be provided without count")
+
+ # other properties
+ for arg_name, byte_repr in (
+ ('withdist', b'WITHDIST'),
+ ('withcoord', b'WITHCOORD'),
+ ('withhash', b'WITHHASH'),
+ ('store_dist', b'STOREDIST')):
+ if kwargs[arg_name]:
+ pieces.append(byte_repr)
+
+ return self.execute_command(command, *pieces, **kwargs)
+
# MODULE COMMANDS
def module_load(self, path):
"""
diff --git a/tests/test_commands.py b/tests/test_commands.py
index 1698106..30ad5d5 100644
--- a/tests/test_commands.py
+++ b/tests/test_commands.py
@@ -1237,7 +1237,7 @@ class TestRedisCommands:
assert r.lrange('a', 0, -1) == [b'1', b'2', b'2.5', b'3']
assert r.linsert('a', 'before', '2', '1.5') == 5
assert r.lrange('a', 0, -1) == \
- [b'1', b'1.5', b'2', b'2.5', b'3']
+ [b'1', b'1.5', b'2', b'2.5', b'3']
def test_llen(self, r):
r.rpush('a', '1', '2', '3')
@@ -1567,7 +1567,7 @@ class TestRedisCommands:
mapping = {'a1': 1.0, 'a2': 2.0, 'a3': 3.0}
r.zadd('a', mapping)
assert r.zrange('a', 0, -1, withscores=True) == \
- [(b'a1', 1.0), (b'a2', 2.0), (b'a3', 3.0)]
+ [(b'a1', 1.0), (b'a2', 2.0), (b'a3', 3.0)]
# error cases
with pytest.raises(exceptions.DataError):
@@ -1585,19 +1585,19 @@ class TestRedisCommands:
assert r.zadd('a', {'a1': 1}) == 1
assert r.zadd('a', {'a1': 99, 'a2': 2}, nx=True) == 1
assert r.zrange('a', 0, -1, withscores=True) == \
- [(b'a1', 1.0), (b'a2', 2.0)]
+ [(b'a1', 1.0), (b'a2', 2.0)]
def test_zadd_xx(self, r):
assert r.zadd('a', {'a1': 1}) == 1
assert r.zadd('a', {'a1': 99, 'a2': 2}, xx=True) == 0
assert r.zrange('a', 0, -1, withscores=True) == \
- [(b'a1', 99.0)]
+ [(b'a1', 99.0)]
def test_zadd_ch(self, r):
assert r.zadd('a', {'a1': 1}) == 1
assert r.zadd('a', {'a1': 99, 'a2': 2}, ch=True) == 2
assert r.zrange('a', 0, -1, withscores=True) == \
- [(b'a2', 2.0), (b'a1', 99.0)]
+ [(b'a2', 2.0), (b'a1', 99.0)]
def test_zadd_incr(self, r):
assert r.zadd('a', {'a1': 1}) == 1
@@ -1694,7 +1694,7 @@ class TestRedisCommands:
r.zadd('c', {'a1': 6, 'a3': 5, 'a4': 4})
assert r.zinterstore('d', ['a', 'b', 'c']) == 2
assert r.zrange('d', 0, -1, withscores=True) == \
- [(b'a3', 8), (b'a1', 9)]
+ [(b'a3', 8), (b'a1', 9)]
def test_zinterstore_max(self, r):
r.zadd('a', {'a1': 1, 'a2': 1, 'a3': 1})
@@ -1702,7 +1702,7 @@ class TestRedisCommands:
r.zadd('c', {'a1': 6, 'a3': 5, 'a4': 4})
assert r.zinterstore('d', ['a', 'b', 'c'], aggregate='MAX') == 2
assert r.zrange('d', 0, -1, withscores=True) == \
- [(b'a3', 5), (b'a1', 6)]
+ [(b'a3', 5), (b'a1', 6)]
def test_zinterstore_min(self, r):
r.zadd('a', {'a1': 1, 'a2': 2, 'a3': 3})
@@ -1710,7 +1710,7 @@ class TestRedisCommands:
r.zadd('c', {'a1': 6, 'a3': 5, 'a4': 4})
assert r.zinterstore('d', ['a', 'b', 'c'], aggregate='MIN') == 2
assert r.zrange('d', 0, -1, withscores=True) == \
- [(b'a1', 1), (b'a3', 3)]
+ [(b'a1', 1), (b'a3', 3)]
def test_zinterstore_with_weight(self, r):
r.zadd('a', {'a1': 1, 'a2': 1, 'a3': 1})
@@ -1718,7 +1718,7 @@ class TestRedisCommands:
r.zadd('c', {'a1': 6, 'a3': 5, 'a4': 4})
assert r.zinterstore('d', {'a': 1, 'b': 2, 'c': 3}) == 2
assert r.zrange('d', 0, -1, withscores=True) == \
- [(b'a3', 20), (b'a1', 23)]
+ [(b'a3', 20), (b'a1', 23)]
@skip_if_server_version_lt('4.9.0')
def test_zpopmax(self, r):
@@ -1727,7 +1727,7 @@ class TestRedisCommands:
# with count
assert r.zpopmax('a', count=2) == \
- [(b'a2', 2), (b'a1', 1)]
+ [(b'a2', 2), (b'a1', 1)]
@skip_if_server_version_lt('4.9.0')
def test_zpopmin(self, r):
@@ -1736,7 +1736,7 @@ class TestRedisCommands:
# with count
assert r.zpopmin('a', count=2) == \
- [(b'a2', 2), (b'a3', 3)]
+ [(b'a2', 2), (b'a3', 3)]
@skip_if_server_version_lt('6.2.0')
def test_zrandemember(self, r):
@@ -1781,13 +1781,13 @@ class TestRedisCommands:
# withscores
assert r.zrange('a', 0, 1, withscores=True) == \
- [(b'a1', 1.0), (b'a2', 2.0)]
+ [(b'a1', 1.0), (b'a2', 2.0)]
assert r.zrange('a', 1, 2, withscores=True) == \
- [(b'a2', 2.0), (b'a3', 3.0)]
+ [(b'a2', 2.0), (b'a3', 3.0)]
# custom score function
assert r.zrange('a', 0, 1, withscores=True, score_cast_func=int) == \
- [(b'a1', 1), (b'a2', 2)]
+ [(b'a1', 1), (b'a2', 2)]
@skip_if_server_version_lt('6.2.0')
def test_zrangestore(self, r):
@@ -1805,7 +1805,7 @@ class TestRedisCommands:
assert r.zrangebylex('a', '-', '[c') == [b'a', b'b', b'c']
assert r.zrangebylex('a', '-', '(c') == [b'a', b'b']
assert r.zrangebylex('a', '[aaa', '(g') == \
- [b'b', b'c', b'd', b'e', b'f']
+ [b'b', b'c', b'd', b'e', b'f']
assert r.zrangebylex('a', '[f', '+') == [b'f', b'g']
assert r.zrangebylex('a', '-', '+', start=3, num=2) == [b'd', b'e']
@@ -1815,10 +1815,10 @@ class TestRedisCommands:
assert r.zrevrangebylex('a', '[c', '-') == [b'c', b'b', b'a']
assert r.zrevrangebylex('a', '(c', '-') == [b'b', b'a']
assert r.zrevrangebylex('a', '(g', '[aaa') == \
- [b'f', b'e', b'd', b'c', b'b']
+ [b'f', b'e', b'd', b'c', b'b']
assert r.zrevrangebylex('a', '+', '[f') == [b'g', b'f']
assert r.zrevrangebylex('a', '+', '-', start=3, num=2) == \
- [b'd', b'c']
+ [b'd', b'c']
def test_zrangebyscore(self, r):
r.zadd('a', {'a1': 1, 'a2': 2, 'a3': 3, 'a4': 4, 'a5': 5})
@@ -1826,16 +1826,16 @@ class TestRedisCommands:
# slicing with start/num
assert r.zrangebyscore('a', 2, 4, start=1, num=2) == \
- [b'a3', b'a4']
+ [b'a3', b'a4']
# withscores
assert r.zrangebyscore('a', 2, 4, withscores=True) == \
- [(b'a2', 2.0), (b'a3', 3.0), (b'a4', 4.0)]
+ [(b'a2', 2.0), (b'a3', 3.0), (b'a4', 4.0)]
# custom score function
assert r.zrangebyscore('a', 2, 4, withscores=True,
score_cast_func=int) == \
- [(b'a2', 2), (b'a3', 3), (b'a4', 4)]
+ [(b'a2', 2), (b'a3', 3), (b'a4', 4)]
def test_zrank(self, r):
r.zadd('a', {'a1': 1, 'a2': 2, 'a3': 3, 'a4': 4, 'a5': 5})
@@ -1884,14 +1884,14 @@ class TestRedisCommands:
# withscores
assert r.zrevrange('a', 0, 1, withscores=True) == \
- [(b'a3', 3.0), (b'a2', 2.0)]
+ [(b'a3', 3.0), (b'a2', 2.0)]
assert r.zrevrange('a', 1, 2, withscores=True) == \
- [(b'a2', 2.0), (b'a1', 1.0)]
+ [(b'a2', 2.0), (b'a1', 1.0)]
# custom score function
assert r.zrevrange('a', 0, 1, withscores=True,
score_cast_func=int) == \
- [(b'a3', 3.0), (b'a2', 2.0)]
+ [(b'a3', 3.0), (b'a2', 2.0)]
def test_zrevrangebyscore(self, r):
r.zadd('a', {'a1': 1, 'a2': 2, 'a3': 3, 'a4': 4, 'a5': 5})
@@ -1899,16 +1899,16 @@ class TestRedisCommands:
# slicing with start/num
assert r.zrevrangebyscore('a', 4, 2, start=1, num=2) == \
- [b'a3', b'a2']
+ [b'a3', b'a2']
# withscores
assert r.zrevrangebyscore('a', 4, 2, withscores=True) == \
- [(b'a4', 4.0), (b'a3', 3.0), (b'a2', 2.0)]
+ [(b'a4', 4.0), (b'a3', 3.0), (b'a2', 2.0)]
# custom score function
assert r.zrevrangebyscore('a', 4, 2, withscores=True,
score_cast_func=int) == \
- [(b'a4', 4), (b'a3', 3), (b'a2', 2)]
+ [(b'a4', 4), (b'a3', 3), (b'a2', 2)]
def test_zrevrank(self, r):
r.zadd('a', {'a1': 1, 'a2': 2, 'a3': 3, 'a4': 4, 'a5': 5})
@@ -1948,7 +1948,7 @@ class TestRedisCommands:
r.zadd('c', {'a1': 6, 'a3': 5, 'a4': 4})
assert r.zunionstore('d', ['a', 'b', 'c']) == 4
assert r.zrange('d', 0, -1, withscores=True) == \
- [(b'a2', 3), (b'a4', 4), (b'a3', 8), (b'a1', 9)]
+ [(b'a2', 3), (b'a4', 4), (b'a3', 8), (b'a1', 9)]
def test_zunionstore_max(self, r):
r.zadd('a', {'a1': 1, 'a2': 1, 'a3': 1})
@@ -1956,7 +1956,7 @@ class TestRedisCommands:
r.zadd('c', {'a1': 6, 'a3': 5, 'a4': 4})
assert r.zunionstore('d', ['a', 'b', 'c'], aggregate='MAX') == 4
assert r.zrange('d', 0, -1, withscores=True) == \
- [(b'a2', 2), (b'a4', 4), (b'a3', 5), (b'a1', 6)]
+ [(b'a2', 2), (b'a4', 4), (b'a3', 5), (b'a1', 6)]
def test_zunionstore_min(self, r):
r.zadd('a', {'a1': 1, 'a2': 2, 'a3': 3})
@@ -1964,7 +1964,7 @@ class TestRedisCommands:
r.zadd('c', {'a1': 6, 'a3': 5, 'a4': 4})
assert r.zunionstore('d', ['a', 'b', 'c'], aggregate='MIN') == 4
assert r.zrange('d', 0, -1, withscores=True) == \
- [(b'a1', 1), (b'a2', 2), (b'a3', 3), (b'a4', 4)]
+ [(b'a1', 1), (b'a2', 2), (b'a3', 3), (b'a4', 4)]
def test_zunionstore_with_weight(self, r):
r.zadd('a', {'a1': 1, 'a2': 1, 'a3': 1})
@@ -1972,7 +1972,7 @@ class TestRedisCommands:
r.zadd('c', {'a1': 6, 'a3': 5, 'a4': 4})
assert r.zunionstore('d', {'a': 1, 'b': 2, 'c': 3}) == 4
assert r.zrange('d', 0, -1, withscores=True) == \
- [(b'a2', 5), (b'a4', 12), (b'a3', 20), (b'a1', 23)]
+ [(b'a2', 5), (b'a4', 12), (b'a3', 20), (b'a1', 23)]
@skip_if_server_version_lt('6.1.240')
def test_zmscore(self, r):
@@ -2153,7 +2153,7 @@ class TestRedisCommands:
r['user:3'] = 'u3'
r.rpush('a', '2', '3', '1')
assert r.sort('a', get=('user:*', '#')) == \
- [b'u1', b'1', b'u2', b'2', b'u3', b'3']
+ [b'u1', b'1', b'u2', b'2', b'u3', b'3']
def test_sort_get_groups_two(self, r):
r['user:1'] = 'u1'
@@ -2161,7 +2161,7 @@ class TestRedisCommands:
r['user:3'] = 'u3'
r.rpush('a', '2', '3', '1')
assert r.sort('a', get=('user:*', '#'), groups=True) == \
- [(b'u1', b'1'), (b'u2', b'2'), (b'u3', b'3')]
+ [(b'u1', b'1'), (b'u2', b'2'), (b'u3', b'3')]
def test_sort_groups_string_get(self, r):
r['user:1'] = 'u1'
@@ -2196,11 +2196,11 @@ class TestRedisCommands:
r['door:3'] = 'd3'
r.rpush('a', '2', '3', '1')
assert r.sort('a', get=('user:*', 'door:*', '#'), groups=True) == \
- [
- (b'u1', b'd1', b'1'),
- (b'u2', b'd2', b'2'),
- (b'u3', b'd3', b'3')
- ]
+ [
+ (b'u1', b'd1', b'1'),
+ (b'u2', b'd2', b'2'),
+ (b'u3', b'd3', b'3')
+ ]
def test_sort_desc(self, r):
r.rpush('a', '2', '3', '1')
@@ -2209,7 +2209,7 @@ class TestRedisCommands:
def test_sort_alpha(self, r):
r.rpush('a', 'e', 'c', 'b', 'd', 'a')
assert r.sort('a', alpha=True) == \
- [b'a', b'b', b'c', b'd', b'e']
+ [b'a', b'b', b'c', b'd', b'e']
def test_sort_store(self, r):
r.rpush('a', '2', '3', '1')
@@ -2241,7 +2241,7 @@ class TestRedisCommands:
store='sorted')
assert num == 4
assert r.lrange('sorted', 0, 10) == \
- [b'vodka', b'milk', b'gin', b'apple juice']
+ [b'vodka', b'milk', b'gin', b'apple juice']
def test_sort_issue_924(self, r):
# Tests for issue https://github.com/andymccurdy/redis-py/issues/924
@@ -2314,7 +2314,7 @@ class TestRedisCommands:
# GEO COMMANDS
@skip_if_server_version_lt('3.2.0')
def test_geoadd(self, r):
- values = (2.1909389952632, 41.433791470673, 'place1') +\
+ values = (2.1909389952632, 41.433791470673, 'place1') + \
(2.1873744593677, 41.406342043777, 'place2')
assert r.geoadd('barcelona', *values) == 2
@@ -2327,7 +2327,7 @@ class TestRedisCommands:
@skip_if_server_version_lt('3.2.0')
def test_geodist(self, r):
- values = (2.1909389952632, 41.433791470673, 'place1') +\
+ values = (2.1909389952632, 41.433791470673, 'place1') + \
(2.1873744593677, 41.406342043777, 'place2')
assert r.geoadd('barcelona', *values) == 2
@@ -2335,7 +2335,7 @@ class TestRedisCommands:
@skip_if_server_version_lt('3.2.0')
def test_geodist_units(self, r):
- values = (2.1909389952632, 41.433791470673, 'place1') +\
+ values = (2.1909389952632, 41.433791470673, 'place1') + \
(2.1873744593677, 41.406342043777, 'place2')
r.geoadd('barcelona', *values)
@@ -2354,24 +2354,24 @@ class TestRedisCommands:
@skip_if_server_version_lt('3.2.0')
def test_geohash(self, r):
- values = (2.1909389952632, 41.433791470673, 'place1') +\
+ values = (2.1909389952632, 41.433791470673, 'place1') + \
(2.1873744593677, 41.406342043777, 'place2')
r.geoadd('barcelona', *values)
- assert r.geohash('barcelona', 'place1', 'place2', 'place3') ==\
- ['sp3e9yg3kd0', 'sp3e9cbc3t0', None]
+ assert r.geohash('barcelona', 'place1', 'place2', 'place3') == \
+ ['sp3e9yg3kd0', 'sp3e9cbc3t0', None]
@skip_unless_arch_bits(64)
@skip_if_server_version_lt('3.2.0')
def test_geopos(self, r):
- values = (2.1909389952632, 41.433791470673, 'place1') +\
+ values = (2.1909389952632, 41.433791470673, 'place1') + \
(2.1873744593677, 41.406342043777, 'place2')
r.geoadd('barcelona', *values)
# redis uses 52 bits precision, hereby small errors may be introduced.
- assert r.geopos('barcelona', 'place1', 'place2') ==\
- [(2.19093829393386841, 41.43379028184083523),
- (2.18737632036209106, 41.40634178640635099)]
+ assert r.geopos('barcelona', 'place1', 'place2') == \
+ [(2.19093829393386841, 41.43379028184083523),
+ (2.18737632036209106, 41.40634178640635099)]
@skip_if_server_version_lt('4.0.0')
def test_geopos_no_value(self, r):
@@ -2382,9 +2382,157 @@ class TestRedisCommands:
def test_old_geopos_no_value(self, r):
assert r.geopos('barcelona', 'place1', 'place2') == []
+ @skip_if_server_version_lt('6.2.0')
+ def test_geosearch(self, r):
+ values = (2.1909389952632, 41.433791470673, 'place1') + \
+ (2.1873744593677, 41.406342043777, b'\x80place2') + \
+ (2.583333, 41.316667, 'place3')
+ r.geoadd('barcelona', *values)
+ assert r.geosearch('barcelona', longitude=2.191,
+ latitude=41.433, radius=1000) == [b'place1']
+ assert r.geosearch('barcelona', longitude=2.187,
+ latitude=41.406, radius=1000) == [b'\x80place2']
+ assert r.geosearch('barcelona', longitude=2.191, latitude=41.433,
+ height=1000, width=1000) == [b'place1']
+ assert r.geosearch('barcelona', member='place3', radius=100,
+ unit='km') == [b'\x80place2', b'place1', b'place3']
+ # test count
+ assert r.geosearch('barcelona', member='place3', radius=100,
+ unit='km', count=2) == [b'place3', b'\x80place2']
+ assert r.geosearch('barcelona', member='place3', radius=100,
+ unit='km', count=1, any=1)[0] \
+ in [b'place1', b'place3', b'\x80place2']
+
+ @skip_unless_arch_bits(64)
+ @skip_if_server_version_lt('6.2.0')
+ def test_geosearch_member(self, r):
+ values = (2.1909389952632, 41.433791470673, 'place1') + \
+ (2.1873744593677, 41.406342043777, b'\x80place2')
+
+ r.geoadd('barcelona', *values)
+ assert r.geosearch('barcelona', member='place1', radius=4000) == \
+ [b'\x80place2', b'place1']
+ assert r.geosearch('barcelona', member='place1', radius=10) == \
+ [b'place1']
+
+ assert r.geosearch('barcelona', member='place1', radius=4000,
+ withdist=True,
+ withcoord=True,
+ withhash=True) == \
+ [[b'\x80place2', 3067.4157, 3471609625421029,
+ (2.187376320362091, 41.40634178640635)],
+ [b'place1', 0.0, 3471609698139488,
+ (2.1909382939338684, 41.433790281840835)]]
+
+ @skip_if_server_version_lt('6.2.0')
+ def test_geosearch_sort(self, r):
+ values = (2.1909389952632, 41.433791470673, 'place1') + \
+ (2.1873744593677, 41.406342043777, 'place2')
+ r.geoadd('barcelona', *values)
+ assert r.geosearch('barcelona', longitude=2.191,
+ latitude=41.433, radius=3000, sort='ASC') == \
+ [b'place1', b'place2']
+ assert r.geosearch('barcelona', longitude=2.191,
+ latitude=41.433, radius=3000, sort='DESC') == \
+ [b'place2', b'place1']
+
+ @skip_unless_arch_bits(64)
+ @skip_if_server_version_lt('6.2.0')
+ def test_geosearch_with(self, r):
+ values = (2.1909389952632, 41.433791470673, 'place1') + \
+ (2.1873744593677, 41.406342043777, 'place2')
+ r.geoadd('barcelona', *values)
+
+ # test a bunch of combinations to test the parse response
+ # function.
+ assert r.geosearch('barcelona', longitude=2.191, latitude=41.433,
+ radius=1, unit='km', withdist=True,
+ withcoord=True, withhash=True) == \
+ [[b'place1', 0.0881, 3471609698139488,
+ (2.19093829393386841, 41.43379028184083523)]]
+ assert r.geosearch('barcelona', longitude=2.191, latitude=41.433,
+ radius=1, unit='km',
+ withdist=True, withcoord=True) == \
+ [[b'place1', 0.0881,
+ (2.19093829393386841, 41.43379028184083523)]]
+ assert r.geosearch('barcelona', longitude=2.191, latitude=41.433,
+ radius=1, unit='km',
+ withhash=True, withcoord=True) == \
+ [[b'place1', 3471609698139488,
+ (2.19093829393386841, 41.43379028184083523)]]
+ # test no values.
+ assert r.geosearch('barcelona', longitude=2, latitude=1,
+ radius=1, unit='km', withdist=True,
+ withcoord=True, withhash=True) == []
+
+ @skip_if_server_version_lt('6.2.0')
+ def test_geosearch_negative(self, r):
+ # not specifying member nor longitude and latitude
+ with pytest.raises(exceptions.DataError):
+ assert r.geosearch('barcelona')
+ # specifying member and longitude and latitude
+ with pytest.raises(exceptions.DataError):
+ assert r.geosearch('barcelona',
+ member="Paris", longitude=2, latitude=1)
+ # specifying one of longitude and latitude
+ with pytest.raises(exceptions.DataError):
+ assert r.geosearch('barcelona', longitude=2)
+ with pytest.raises(exceptions.DataError):
+ assert r.geosearch('barcelona', latitude=2)
+
+ # not specifying radius nor width and height
+ with pytest.raises(exceptions.DataError):
+ assert r.geosearch('barcelona', member="Paris")
+ # specifying radius and width and height
+ with pytest.raises(exceptions.DataError):
+ assert r.geosearch('barcelona', member="Paris",
+ radius=3, width=2, height=1)
+ # specifying one of width and height
+ with pytest.raises(exceptions.DataError):
+ assert r.geosearch('barcelona', member="Paris", width=2)
+ with pytest.raises(exceptions.DataError):
+ assert r.geosearch('barcelona', member="Paris", height=2)
+
+ # invalid sort
+ with pytest.raises(exceptions.DataError):
+ assert r.geosearch('barcelona',
+ member="Paris", width=2, height=2, sort="wrong")
+
+ # invalid unit
+ with pytest.raises(exceptions.DataError):
+ assert r.geosearch('barcelona',
+ member="Paris", width=2, height=2, unit="miles")
+
+ # use any without count
+ with pytest.raises(exceptions.DataError):
+ assert r.geosearch('barcelona', member='place3', radius=100, any=1)
+
+ @skip_if_server_version_lt('6.2.0')
+ def test_geosearchstore(self, r):
+ values = (2.1909389952632, 41.433791470673, 'place1') + \
+ (2.1873744593677, 41.406342043777, 'place2')
+
+ r.geoadd('barcelona', *values)
+ r.geosearchstore('places_barcelona', 'barcelona',
+ longitude=2.191, latitude=41.433, radius=1000)
+ assert r.zrange('places_barcelona', 0, -1) == [b'place1']
+
+ @skip_unless_arch_bits(64)
+ @skip_if_server_version_lt('6.2.0')
+ def test_geosearchstore_dist(self, r):
+ values = (2.1909389952632, 41.433791470673, 'place1') + \
+ (2.1873744593677, 41.406342043777, 'place2')
+
+ r.geoadd('barcelona', *values)
+ r.geosearchstore('places_barcelona', 'barcelona',
+ longitude=2.191, latitude=41.433,
+ radius=1000, storedist=True)
+ # instead of save the geo score, the distance is saved.
+ assert r.zscore('places_barcelona', 'place1') == 88.05060698409301
+
@skip_if_server_version_lt('3.2.0')
def test_georadius(self, r):
- values = (2.1909389952632, 41.433791470673, 'place1') +\
+ values = (2.1909389952632, 41.433791470673, 'place1') + \
(2.1873744593677, 41.406342043777, b'\x80place2')
r.geoadd('barcelona', *values)
@@ -2393,7 +2541,7 @@ class TestRedisCommands:
@skip_if_server_version_lt('3.2.0')
def test_georadius_no_values(self, r):
- values = (2.1909389952632, 41.433791470673, 'place1') +\
+ values = (2.1909389952632, 41.433791470673, 'place1') + \
(2.1873744593677, 41.406342043777, 'place2')
r.geoadd('barcelona', *values)
@@ -2401,17 +2549,17 @@ class TestRedisCommands:
@skip_if_server_version_lt('3.2.0')
def test_georadius_units(self, r):
- values = (2.1909389952632, 41.433791470673, 'place1') +\
+ values = (2.1909389952632, 41.433791470673, 'place1') + \
(2.1873744593677, 41.406342043777, 'place2')
r.geoadd('barcelona', *values)
- assert r.georadius('barcelona', 2.191, 41.433, 1, unit='km') ==\
- [b'place1']
+ assert r.georadius('barcelona', 2.191, 41.433, 1, unit='km') == \
+ [b'place1']
@skip_unless_arch_bits(64)
@skip_if_server_version_lt('3.2.0')
def test_georadius_with(self, r):
- values = (2.1909389952632, 41.433791470673, 'place1') +\
+ values = (2.1909389952632, 41.433791470673, 'place1') + \
(2.1873744593677, 41.406342043777, 'place2')
r.geoadd('barcelona', *values)
@@ -2419,19 +2567,19 @@ class TestRedisCommands:
# test a bunch of combinations to test the parse response
# function.
assert r.georadius('barcelona', 2.191, 41.433, 1, unit='km',
- withdist=True, withcoord=True, withhash=True) ==\
- [[b'place1', 0.0881, 3471609698139488,
- (2.19093829393386841, 41.43379028184083523)]]
+ withdist=True, withcoord=True, withhash=True) == \
+ [[b'place1', 0.0881, 3471609698139488,
+ (2.19093829393386841, 41.43379028184083523)]]
assert r.georadius('barcelona', 2.191, 41.433, 1, unit='km',
- withdist=True, withcoord=True) ==\
- [[b'place1', 0.0881,
- (2.19093829393386841, 41.43379028184083523)]]
+ withdist=True, withcoord=True) == \
+ [[b'place1', 0.0881,
+ (2.19093829393386841, 41.43379028184083523)]]
assert r.georadius('barcelona', 2.191, 41.433, 1, unit='km',
- withhash=True, withcoord=True) ==\
- [[b'place1', 3471609698139488,
- (2.19093829393386841, 41.43379028184083523)]]
+ withhash=True, withcoord=True) == \
+ [[b'place1', 3471609698139488,
+ (2.19093829393386841, 41.43379028184083523)]]
# test no values.
assert r.georadius('barcelona', 2, 1, 1, unit='km',
@@ -2439,27 +2587,27 @@ class TestRedisCommands:
@skip_if_server_version_lt('3.2.0')
def test_georadius_count(self, r):
- values = (2.1909389952632, 41.433791470673, 'place1') +\
+ values = (2.1909389952632, 41.433791470673, 'place1') + \
(2.1873744593677, 41.406342043777, 'place2')
r.geoadd('barcelona', *values)
- assert r.georadius('barcelona', 2.191, 41.433, 3000, count=1) ==\
- [b'place1']
+ assert r.georadius('barcelona', 2.191, 41.433, 3000, count=1) == \
+ [b'place1']
@skip_if_server_version_lt('3.2.0')
def test_georadius_sort(self, r):
- values = (2.1909389952632, 41.433791470673, 'place1') +\
+ values = (2.1909389952632, 41.433791470673, 'place1') + \
(2.1873744593677, 41.406342043777, 'place2')
r.geoadd('barcelona', *values)
- assert r.georadius('barcelona', 2.191, 41.433, 3000, sort='ASC') ==\
- [b'place1', b'place2']
- assert r.georadius('barcelona', 2.191, 41.433, 3000, sort='DESC') ==\
- [b'place2', b'place1']
+ assert r.georadius('barcelona', 2.191, 41.433, 3000, sort='ASC') == \
+ [b'place1', b'place2']
+ assert r.georadius('barcelona', 2.191, 41.433, 3000, sort='DESC') == \
+ [b'place2', b'place1']
@skip_if_server_version_lt('3.2.0')
def test_georadius_store(self, r):
- values = (2.1909389952632, 41.433791470673, 'place1') +\
+ values = (2.1909389952632, 41.433791470673, 'place1') + \
(2.1873744593677, 41.406342043777, 'place2')
r.geoadd('barcelona', *values)
@@ -2469,7 +2617,7 @@ class TestRedisCommands:
@skip_unless_arch_bits(64)
@skip_if_server_version_lt('3.2.0')
def test_georadius_store_dist(self, r):
- values = (2.1909389952632, 41.433791470673, 'place1') +\
+ values = (2.1909389952632, 41.433791470673, 'place1') + \
(2.1873744593677, 41.406342043777, 'place2')
r.geoadd('barcelona', *values)
@@ -2481,20 +2629,20 @@ class TestRedisCommands:
@skip_unless_arch_bits(64)
@skip_if_server_version_lt('3.2.0')
def test_georadiusmember(self, r):
- values = (2.1909389952632, 41.433791470673, 'place1') +\
+ values = (2.1909389952632, 41.433791470673, 'place1') + \
(2.1873744593677, 41.406342043777, b'\x80place2')
r.geoadd('barcelona', *values)
- assert r.georadiusbymember('barcelona', 'place1', 4000) ==\
- [b'\x80place2', b'place1']
+ assert r.georadiusbymember('barcelona', 'place1', 4000) == \
+ [b'\x80place2', b'place1']
assert r.georadiusbymember('barcelona', 'place1', 10) == [b'place1']
assert r.georadiusbymember('barcelona', 'place1', 4000,
withdist=True, withcoord=True,
- withhash=True) ==\
- [[b'\x80place2', 3067.4157, 3471609625421029,
- (2.187376320362091, 41.40634178640635)],
- [b'place1', 0.0, 3471609698139488,
+ withhash=True) == \
+ [[b'\x80place2', 3067.4157, 3471609625421029,
+ (2.187376320362091, 41.40634178640635)],
+ [b'place1', 0.0, 3471609698139488,
(2.1909382939338684, 41.433790281840835)]]
@skip_if_server_version_lt('5.0.0')