Skip to content

Commit f3b952b

Browse files
Add noscore option to zscan.
1 parent 2ec8f63 commit f3b952b

4 files changed

Lines changed: 42 additions & 13 deletions

File tree

src/commands.def

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9080,6 +9080,7 @@ struct COMMAND_ARG ZSCAN_Args[] = {
90809080
{MAKE_ARG("cursor",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
90819081
{MAKE_ARG("pattern",ARG_TYPE_PATTERN,-1,"MATCH",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
90829082
{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
9083+
{MAKE_ARG("noscores",ARG_TYPE_PURE_TOKEN,-1,"NOSCORES",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
90839084
};
90849085

90859086
/********** ZSCORE ********************/
@@ -10864,7 +10865,7 @@ struct COMMAND_STRUCT serverCommandTable[] = {
1086410865
{MAKE_CMD("zrevrangebylex","Returns members in a sorted set within a lexicographical range in reverse order.","O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).","2.8.9",CMD_DOC_DEPRECATED,"`ZRANGE` with the `REV` and `BYLEX` arguments","6.2.0","sorted_set",COMMAND_GROUP_SORTED_SET,ZREVRANGEBYLEX_History,0,ZREVRANGEBYLEX_Tips,0,zrevrangebylexCommand,-4,CMD_READONLY,ACL_CATEGORY_SORTEDSET,ZREVRANGEBYLEX_Keyspecs,1,NULL,4),.args=ZREVRANGEBYLEX_Args},
1086510866
{MAKE_CMD("zrevrangebyscore","Returns members in a sorted set within a range of scores in reverse order.","O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).","2.2.0",CMD_DOC_DEPRECATED,"`ZRANGE` with the `REV` and `BYSCORE` arguments","6.2.0","sorted_set",COMMAND_GROUP_SORTED_SET,ZREVRANGEBYSCORE_History,1,ZREVRANGEBYSCORE_Tips,0,zrevrangebyscoreCommand,-4,CMD_READONLY,ACL_CATEGORY_SORTEDSET,ZREVRANGEBYSCORE_Keyspecs,1,NULL,5),.args=ZREVRANGEBYSCORE_Args},
1086610867
{MAKE_CMD("zrevrank","Returns the index of a member in a sorted set ordered by descending scores.","O(log(N))","2.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZREVRANK_History,1,ZREVRANK_Tips,0,zrevrankCommand,-3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_SORTEDSET,ZREVRANK_Keyspecs,1,NULL,3),.args=ZREVRANK_Args},
10867-
{MAKE_CMD("zscan","Iterates over members and scores of a sorted set.","O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.","2.8.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZSCAN_History,0,ZSCAN_Tips,1,zscanCommand,-3,CMD_READONLY,ACL_CATEGORY_SORTEDSET,ZSCAN_Keyspecs,1,NULL,4),.args=ZSCAN_Args},
10868+
{MAKE_CMD("zscan","Iterates over members and scores of a sorted set.","O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.","2.8.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZSCAN_History,0,ZSCAN_Tips,1,zscanCommand,-3,CMD_READONLY,ACL_CATEGORY_SORTEDSET,ZSCAN_Keyspecs,1,NULL,5),.args=ZSCAN_Args},
1086810869
{MAKE_CMD("zscore","Returns the score of a member in a sorted set.","O(1)","1.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZSCORE_History,0,ZSCORE_Tips,0,zscoreCommand,3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_SORTEDSET,ZSCORE_Keyspecs,1,NULL,2),.args=ZSCORE_Args},
1086910870
{MAKE_CMD("zunion","Returns the union of multiple sorted sets.","O(N)+O(M*log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.","6.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZUNION_History,0,ZUNION_Tips,0,zunionCommand,-3,CMD_READONLY,ACL_CATEGORY_SORTEDSET,ZUNION_Keyspecs,1,zunionInterDiffGetKeys,5),.args=ZUNION_Args},
1087010871
{MAKE_CMD("zunionstore","Stores the union of multiple sorted sets in a key.","O(N)+O(M log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.","2.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZUNIONSTORE_History,0,ZUNIONSTORE_Tips,0,zunionstoreCommand,-4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SORTEDSET,ZUNIONSTORE_Keyspecs,2,zunionInterDiffStoreGetKeys,5),.args=ZUNIONSTORE_Args},

src/commands/zscan.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,12 @@
5656
"name": "count",
5757
"type": "integer",
5858
"optional": true
59+
},
60+
{
61+
"token": "NOSCORES",
62+
"name": "noscores",
63+
"type": "pure-token",
64+
"optional": true
5965
}
6066
],
6167
"reply_schema": {
@@ -69,7 +75,7 @@
6975
"type": "string"
7076
},
7177
{
72-
"description": "List of elements of the sorted set, where each even element is the member, and each odd value is its associated score.",
78+
"description": "List of elements of the sorted set, where each even element is the member, and each odd value is its associated score, or when noscores option is on, a list of members from the sorted set.",
7379
"type": "array",
7480
"items": {
7581
"type": "string"

src/db.c

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -853,7 +853,7 @@ typedef struct {
853853
long long type; /* the particular type when scan the db */
854854
sds pattern; /* pattern string, NULL means no pattern */
855855
long sampled; /* cumulative number of keys sampled */
856-
int no_values; /* set to 1 means to return keys only */
856+
int only_keys; /* set to 1 means to return keys only */
857857
} scanData;
858858

859859
/* Helper function to compare key type in scan commands */
@@ -905,18 +905,22 @@ void scanCallback(void *privdata, const dictEntry *de) {
905905
key = keysds;
906906
} else if (o->type == OBJ_HASH) {
907907
key = keysds;
908-
val = dictGetVal(de);
908+
if (!data->only_keys) {
909+
val = dictGetVal(de);
910+
}
909911
} else if (o->type == OBJ_ZSET) {
910-
char buf[MAX_LONG_DOUBLE_CHARS];
911-
int len = ld2string(buf, sizeof(buf), *(double *)dictGetVal(de), LD_STR_AUTO);
912912
key = sdsdup(keysds);
913-
val = sdsnewlen(buf, len);
913+
if (!data->only_keys) {
914+
char buf[MAX_LONG_DOUBLE_CHARS];
915+
int len = ld2string(buf, sizeof(buf), *(double *)dictGetVal(de), LD_STR_AUTO);
916+
val = sdsnewlen(buf, len);
917+
}
914918
} else {
915919
serverPanic("Type not handled in SCAN callback.");
916920
}
917921

918922
listAddNodeTail(keys, key);
919-
if (val && !data->no_values) listAddNodeTail(keys, val);
923+
if (val) listAddNodeTail(keys, val);
920924
}
921925

922926
/* Try to parse a SCAN cursor stored at object 'o':
@@ -989,7 +993,7 @@ void scanGenericCommand(client *c, robj *o, unsigned long long cursor) {
989993
sds pat = NULL;
990994
sds typename = NULL;
991995
long long type = LLONG_MAX;
992-
int patlen = 0, use_pattern = 0, no_values = 0;
996+
int patlen = 0, use_pattern = 0, only_keys = 0;
993997
dict *ht;
994998

995999
/* Object must be NULL (to iterate keys names), or the type of the object
@@ -1040,7 +1044,14 @@ void scanGenericCommand(client *c, robj *o, unsigned long long cursor) {
10401044
addReplyError(c, "NOVALUES option can only be used in HSCAN");
10411045
return;
10421046
}
1043-
no_values = 1;
1047+
only_keys = 1;
1048+
i++;
1049+
} else if (!strcasecmp(c->argv[i]->ptr, "noscores")) {
1050+
if (!o || o->type != OBJ_ZSET) {
1051+
addReplyError(c, "NOSCORES option can only be used in ZSCAN");
1052+
return;
1053+
}
1054+
only_keys = 1;
10441055
i++;
10451056
} else {
10461057
addReplyErrorObject(c,shared.syntaxerr);
@@ -1101,15 +1112,15 @@ void scanGenericCommand(client *c, robj *o, unsigned long long cursor) {
11011112
* working on an empty dict, one with a lot of empty buckets, and
11021113
* for the buckets are not empty, we need to limit the spampled number
11031114
* to prevent a long hang time caused by filtering too many keys;
1104-
* 6. data.no_values: to control whether values will be returned or
1115+
* 6. data.only_keys: to control whether values will be returned or
11051116
* only keys are returned. */
11061117
scanData data = {
11071118
.keys = keys,
11081119
.o = o,
11091120
.type = type,
11101121
.pattern = use_pattern ? pat : NULL,
11111122
.sampled = 0,
1112-
.no_values = no_values,
1123+
.only_keys = only_keys,
11131124
};
11141125

11151126
/* A pattern may restrict all matching keys to one cluster slot. */
@@ -1164,7 +1175,7 @@ void scanGenericCommand(client *c, robj *o, unsigned long long cursor) {
11641175
/* add key object */
11651176
listAddNodeTail(keys, sdsnewlen(str, len));
11661177
/* add value object */
1167-
if (!no_values) {
1178+
if (!only_keys) {
11681179
str = lpGet(p, &len, intbuf);
11691180
listAddNodeTail(keys, sdsnewlen(str, len));
11701181
}

tests/unit/scan.tcl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,10 @@ proc test_scan {type} {
316316

317317
set keys2 [lsort -unique $keys2]
318318
assert_equal $count [llength $keys2]
319+
320+
# Test NOSCORES
321+
set res [r zscan zset 0 count 1000 noscores]
322+
assert_equal [lsort $keys2] [lsort [lindex $res 1]]
319323
}
320324
}
321325

@@ -386,6 +390,13 @@ proc test_scan {type} {
386390
lsort -unique [lindex $res 1]
387391
}
388392

393+
test "{$type} ZSCAN with NOSCORES" {
394+
r del mykey
395+
r zadd mykey 1 foo 2 fab 3 fiz 10 foobar
396+
set res [r zscan mykey 0 NOSCORES]
397+
lsort -unique [lindex $res 1]
398+
} {fab fiz foo foobar}
399+
389400
test "{$type} ZSCAN scores: regression test for issue #2175" {
390401
r del mykey
391402
for {set j 0} {$j < 500} {incr j} {

0 commit comments

Comments
 (0)