Add a ZMSCORE command returning an array of scores. (#7593)
Syntax: `ZMSCORE KEY MEMBER [MEMBER ...]` This is an extension of #2359 amended by Tyson Andre to work with the changed unstable API, add more tests, and consistently return an array. - It seemed as if it would be more likely to get reviewed after updating the implementation. Currently, multi commands or lua scripting to call zscore multiple times would almost definitely be less efficient than a native ZMSCORE for the following reasons: - Need to fetch the set from the string every time instead of reusing the C pointer. - Using pipelining or multi-commands would result in more bytes sent by the client for the repeated `ZMSCORE KEY` sections. - Need to specially encode the data and decode it from the client for lua-based solutions. - The fastest solution I've seen for large sets(thousands or millions) involves lua and a variadic ZADD, then a ZINTERSECT, then a ZRANGE 0 -1, then UNLINK of a temporary set (or lua). This is still inefficient. Co-authored-by: Tyson Andre <tysonandre775@hotmail.com>
This commit is contained in:
parent
824bd2ac11
commit
f11f26cc53
@ -470,6 +470,10 @@ struct redisCommand redisCommandTable[] = {
|
||||
"read-only fast @sortedset",
|
||||
0,NULL,1,1,1,0,0,0},
|
||||
|
||||
{"zmscore",zmscoreCommand,-3,
|
||||
"read-only fast @sortedset",
|
||||
0,NULL,1,1,1,0,0,0},
|
||||
|
||||
{"zrank",zrankCommand,3,
|
||||
"read-only fast @sortedset",
|
||||
0,NULL,1,1,1,0,0,0},
|
||||
|
@ -2321,6 +2321,7 @@ void zrevrangeCommand(client *c);
|
||||
void zcardCommand(client *c);
|
||||
void zremCommand(client *c);
|
||||
void zscoreCommand(client *c);
|
||||
void zmscoreCommand(client *c);
|
||||
void zremrangebyscoreCommand(client *c);
|
||||
void zremrangebylexCommand(client *c);
|
||||
void zpopminCommand(client *c);
|
||||
|
18
src/t_zset.c
18
src/t_zset.c
@ -3082,6 +3082,24 @@ void zscoreCommand(client *c) {
|
||||
}
|
||||
}
|
||||
|
||||
void zmscoreCommand(client *c) {
|
||||
robj *key = c->argv[1];
|
||||
robj *zobj;
|
||||
double score;
|
||||
zobj = lookupKeyRead(c->db,key);
|
||||
if (zobj != NULL && checkType(c,zobj,OBJ_ZSET)) return;
|
||||
|
||||
addReplyArrayLen(c,c->argc - 2);
|
||||
for (int j = 2; j < c->argc; j++) {
|
||||
/* Treat a missing set the same way as an empty set */
|
||||
if (zobj == NULL || zsetScore(zobj,c->argv[j]->ptr,&score) == C_ERR) {
|
||||
addReplyNull(c);
|
||||
} else {
|
||||
addReplyDouble(c,score);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void zrankGenericCommand(client *c, int reverse) {
|
||||
robj *key = c->argv[1];
|
||||
robj *ele = c->argv[2];
|
||||
|
@ -27,7 +27,7 @@ set ::redis_cluster::plain_commands {
|
||||
sadd srem sismember scard spop srandmember smembers sscan zadd
|
||||
zincrby zrem zremrangebyscore zremrangebyrank zremrangebylex zrange
|
||||
zrangebyscore zrevrangebyscore zrangebylex zrevrangebylex zcount
|
||||
zlexcount zrevrange zcard zscore zrank zrevrank zscan hset hsetnx
|
||||
zlexcount zrevrange zcard zscore zmscore zrank zrevrank zscan hset hsetnx
|
||||
hget hmset hmget hincrby hincrbyfloat hdel hlen hkeys hvals
|
||||
hgetall hexists hscan incrby decrby incrbyfloat getset move
|
||||
expire expireat pexpire pexpireat type ttl pttl persist restore
|
||||
|
@ -781,6 +781,44 @@ start_server {tags {"zset"}} {
|
||||
}
|
||||
}
|
||||
|
||||
test {ZMSCORE retrieve} {
|
||||
r del zmscoretest
|
||||
r zadd zmscoretest 10 x
|
||||
r zadd zmscoretest 20 y
|
||||
|
||||
r zmscore zmscoretest x y
|
||||
} {10 20}
|
||||
|
||||
test {ZMSCORE retrieve from empty set} {
|
||||
r del zmscoretest
|
||||
|
||||
r zmscore zmscoretest x y
|
||||
} {{} {}}
|
||||
|
||||
test {ZMSCORE retrieve with missing member} {
|
||||
r del zmscoretest
|
||||
r zadd zmscoretest 10 x
|
||||
|
||||
r zmscore zmscoretest x y
|
||||
} {10 {}}
|
||||
|
||||
test {ZMSCORE retrieve single member} {
|
||||
r del zmscoretest
|
||||
r zadd zmscoretest 10 x
|
||||
r zadd zmscoretest 20 y
|
||||
|
||||
r zmscore zmscoretest x
|
||||
} {10}
|
||||
|
||||
test {ZMSCORE retrieve requires one or more members} {
|
||||
r del zmscoretest
|
||||
r zadd zmscoretest 10 x
|
||||
r zadd zmscoretest 20 y
|
||||
|
||||
catch {r zmscore zmscoretest} e
|
||||
assert_match {*ERR*wrong*number*arg*} $e
|
||||
}
|
||||
|
||||
test "ZSET commands don't accept the empty strings as valid score" {
|
||||
assert_error "*not*float*" {r zadd myzset "" abc}
|
||||
}
|
||||
@ -815,6 +853,21 @@ start_server {tags {"zset"}} {
|
||||
}
|
||||
}
|
||||
|
||||
test "ZMSCORE - $encoding" {
|
||||
r del zscoretest
|
||||
set aux {}
|
||||
for {set i 0} {$i < $elements} {incr i} {
|
||||
set score [expr rand()]
|
||||
lappend aux $score
|
||||
r zadd zscoretest $score $i
|
||||
}
|
||||
|
||||
assert_encoding $encoding zscoretest
|
||||
for {set i 0} {$i < $elements} {incr i} {
|
||||
assert_equal [lindex $aux $i] [r zmscore zscoretest $i]
|
||||
}
|
||||
}
|
||||
|
||||
test "ZSCORE after a DEBUG RELOAD - $encoding" {
|
||||
r del zscoretest
|
||||
set aux {}
|
||||
|
Loading…
x
Reference in New Issue
Block a user