Skip to content

Commit 8d588ca

Browse files
author
Gabriel Erzse
committed
Support NOVALUES parameter for HSCAN
Issue #3153 The NOVALUES parameter instructs HSCAN to only return the hash keys, without values.
1 parent 70e2a13 commit 8d588ca

File tree

3 files changed

+47
-5
lines changed

3 files changed

+47
-5
lines changed

redis/_parsers/helpers.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,12 @@ def parse_scan(response, **options):
354354

355355
def parse_hscan(response, **options):
356356
cursor, r = response
357-
return int(cursor), r and pairs_to_dict(r) or {}
357+
no_values = options.get("no_values", False)
358+
if no_values:
359+
payload = r or []
360+
else:
361+
payload = r and pairs_to_dict(r) or {}
362+
return int(cursor), payload
358363

359364

360365
def parse_zscan(response, **options):

redis/commands/core.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3102,6 +3102,7 @@ def hscan(
31023102
cursor: int = 0,
31033103
match: Union[PatternT, None] = None,
31043104
count: Union[int, None] = None,
3105+
no_values: Union[bool, None] = None,
31053106
) -> ResponseT:
31063107
"""
31073108
Incrementally return key/value slices in a hash. Also return a cursor
@@ -3111,20 +3112,25 @@ def hscan(
31113112
31123113
``count`` allows for hint the minimum number of returns
31133114
3115+
``no_values`` indicates to return only the keys, without values
3116+
31143117
For more information see https://redis.io/commands/hscan
31153118
"""
31163119
pieces: list[EncodableT] = [name, cursor]
31173120
if match is not None:
31183121
pieces.extend([b"MATCH", match])
31193122
if count is not None:
31203123
pieces.extend([b"COUNT", count])
3121-
return self.execute_command("HSCAN", *pieces)
3124+
if no_values is not None:
3125+
pieces.extend([b"NOVALUES"])
3126+
return self.execute_command("HSCAN", *pieces, no_values=no_values)
31223127

31233128
def hscan_iter(
31243129
self,
31253130
name: str,
31263131
match: Union[PatternT, None] = None,
31273132
count: Union[int, None] = None,
3133+
no_values: Union[bool, None] = None,
31283134
) -> Iterator:
31293135
"""
31303136
Make an iterator using the HSCAN command so that the client doesn't
@@ -3133,11 +3139,18 @@ def hscan_iter(
31333139
``match`` allows for filtering the keys by pattern
31343140
31353141
``count`` allows for hint the minimum number of returns
3142+
3143+
``no_values`` indicates to return only the keys, without values
31363144
"""
31373145
cursor = "0"
31383146
while cursor != 0:
3139-
cursor, data = self.hscan(name, cursor=cursor, match=match, count=count)
3140-
yield from data.items()
3147+
cursor, data = self.hscan(
3148+
name, cursor=cursor, match=match, count=count, no_values=no_values
3149+
)
3150+
if no_values:
3151+
yield from data
3152+
else:
3153+
yield from data.items()
31413154

31423155
def zscan(
31433156
self,
@@ -3253,6 +3266,7 @@ async def hscan_iter(
32533266
name: str,
32543267
match: Union[PatternT, None] = None,
32553268
count: Union[int, None] = None,
3269+
no_values: Union[bool, None] = None,
32563270
) -> AsyncIterator:
32573271
"""
32583272
Make an iterator using the HSCAN command so that the client doesn't
@@ -3261,11 +3275,13 @@ async def hscan_iter(
32613275
``match`` allows for filtering the keys by pattern
32623276
32633277
``count`` allows for hint the minimum number of returns
3278+
3279+
``no_values`` indicates to return only the keys, without values
32643280
"""
32653281
cursor = "0"
32663282
while cursor != 0:
32673283
cursor, data = await self.hscan(
3268-
name, cursor=cursor, match=match, count=count
3284+
name, cursor=cursor, match=match, count=count, no_values=no_values
32693285
)
32703286
for it in data.items():
32713287
yield it

tests/test_commands.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2163,6 +2163,17 @@ def test_hscan(self, r):
21632163
_, dic = r.hscan("a", match="a")
21642164
assert dic == {b"a": b"1"}
21652165

2166+
@skip_if_server_version_lt("7.2.6")
2167+
def test_hscan_novalues(self, r):
2168+
r.hset("a", mapping={"a": 1, "b": 2, "c": 3})
2169+
cursor, keys = r.hscan("a", no_values=True)
2170+
assert cursor == 0
2171+
assert keys == [b"a", b"b", b"c"]
2172+
_, keys = r.hscan("a", match="a", no_values=True)
2173+
assert keys == [b"a"]
2174+
_, keys = r.hscan("a_notset", no_values=True)
2175+
assert keys == []
2176+
21662177
@skip_if_server_version_lt("2.8.0")
21672178
def test_hscan_iter(self, r):
21682179
r.hset("a", mapping={"a": 1, "b": 2, "c": 3})
@@ -2171,6 +2182,16 @@ def test_hscan_iter(self, r):
21712182
dic = dict(r.hscan_iter("a", match="a"))
21722183
assert dic == {b"a": b"1"}
21732184

2185+
@skip_if_server_version_lt("7.2.6")
2186+
def test_hscan_iter_novalues(self, r):
2187+
r.hset("a", mapping={"a": 1, "b": 2, "c": 3})
2188+
keys = list(r.hscan_iter("a", no_values=True))
2189+
assert keys == [b"a", b"b", b"c"]
2190+
keys = list(r.hscan_iter("a", match="a", no_values=True))
2191+
assert keys == [b"a"]
2192+
keys = list(r.hscan_iter("a_notset", no_values=True))
2193+
assert keys == []
2194+
21742195
@skip_if_server_version_lt("2.8.0")
21752196
def test_zscan(self, r):
21762197
r.zadd("a", {"a": 1, "b": 2, "c": 3})

0 commit comments

Comments
 (0)