diff --git a/docker/base/Dockerfile b/docker/base/Dockerfile index ab973861da..8e952b8e4d 100644 --- a/docker/base/Dockerfile +++ b/docker/base/Dockerfile @@ -1 +1 @@ -FROM redis:6.0.5-buster +FROM redis:6.0.6-buster diff --git a/redis/client.py b/redis/client.py index 9653f7d294..5c95e9fa65 100755 --- a/redis/client.py +++ b/redis/client.py @@ -2030,6 +2030,42 @@ def rpushx(self, name, value): "Push ``value`` onto the tail of the list ``name`` if ``name`` exists" return self.execute_command('RPUSHX', name, value) + def lpos(self, name, value, rank=None, count=None, maxlen=None): + """ + Get position of ``value`` within the list ``name`` + + If specified, ``rank`` indicates the "rank" of the first element to + return in case there are multiple copies of ``value`` in the list. + By default, LPOS returns the position of the first occurrence of + ``value`` in the list. When ``rank`` 2, LPOS returns the position of + the second ``value`` in the list. If ``rank`` is negative, LPOS + searches the list in reverse. For example, -1 would return the + position of the last occurrence of ``value`` and -2 would return the + position of the next to last occurrence of ``value``. + + If specified, ``count`` indicates that LPOS should return a list of + up to ``count`` positions. A ``count`` of 2 would return a list of + up to 2 positions. A ``count`` of 0 returns a list of all positions + matching ``value``. When ``count`` is specified and but ``value`` + does not exist in the list, an empty list is returned. + + If specified, ``maxlen`` indicates the maximum number of list + elements to scan. A ``maxlen`` of 1000 will only return the + position(s) of items within the first 1000 entries in the list. + A ``maxlen`` of 0 (the default) will scan the entire list. + """ + pieces = [name, value] + if rank is not None: + pieces.extend(['RANK', rank]) + + if count is not None: + pieces.extend(['COUNT', count]) + + if maxlen is not None: + pieces.extend(['MAXLEN', maxlen]) + + return self.execute_command('LPOS', *pieces) + def sort(self, name, start=None, num=None, by=None, get=None, desc=False, alpha=False, store=None, groups=False): """ diff --git a/tests/test_commands.py b/tests/test_commands.py index c68f14c2c4..91bcbb3098 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -1052,6 +1052,38 @@ def test_rpush(self, r): assert r.rpush('a', '3', '4') == 4 assert r.lrange('a', 0, -1) == [b'1', b'2', b'3', b'4'] + @skip_if_server_version_lt('6.0.6') + def test_lpos(self, r): + assert r.rpush('a', 'a', 'b', 'c', '1', '2', '3', 'c', 'c') == 8 + assert r.lpos('a', 'a') == 0 + assert r.lpos('a', 'c') == 2 + + assert r.lpos('a', 'c', rank=1) == 2 + assert r.lpos('a', 'c', rank=2) == 6 + assert r.lpos('a', 'c', rank=4) is None + assert r.lpos('a', 'c', rank=-1) == 7 + assert r.lpos('a', 'c', rank=-2) == 6 + + assert r.lpos('a', 'c', count=0) == [2, 6, 7] + assert r.lpos('a', 'c', count=1) == [2] + assert r.lpos('a', 'c', count=2) == [2, 6] + assert r.lpos('a', 'c', count=100) == [2, 6, 7] + + assert r.lpos('a', 'c', count=0, rank=2) == [6, 7] + assert r.lpos('a', 'c', count=2, rank=-1) == [7, 6] + + assert r.lpos('axxx', 'c', count=0, rank=2) == [] + assert r.lpos('axxx', 'c') is None + + assert r.lpos('a', 'x', count=2) == [] + assert r.lpos('a', 'x') is None + + assert r.lpos('a', 'a', count=0, maxlen=1) == [0] + assert r.lpos('a', 'c', count=0, maxlen=1) == [] + assert r.lpos('a', 'c', count=0, maxlen=3) == [2] + assert r.lpos('a', 'c', count=0, maxlen=3, rank=-1) == [7, 6] + assert r.lpos('a', 'c', count=0, maxlen=7, rank=2) == [6] + def test_rpushx(self, r): assert r.rpushx('a', 'b') == 0 assert r.lrange('a', 0, -1) == []