1
0
mirror of https://github.com/fluencelabs/redis synced 2025-03-18 00:20:50 +00:00

ZRANGEBYSCORE now supports open intervals, prefixing double values with a open paren. Added ZCOUNT that can count the elements inside an interval of scores, this supports open intervals too

This commit is contained in:
antirez 2010-02-07 21:52:35 +01:00
parent 3a3978b10b
commit f44dd42872
3 changed files with 75 additions and 22 deletions

1
TODO

@ -8,6 +8,7 @@ VERSION 2.0 TODO
* Save dataset / fsync() on SIGTERM * Save dataset / fsync() on SIGTERM
* MULTI/EXEC should support the "EXEC FSYNC" form? * MULTI/EXEC should support the "EXEC FSYNC" form?
* BLPOP & C. tests (write a non blocking Tcl client as first step) * BLPOP & C. tests (write a non blocking Tcl client as first step)
* ZCOUNT sortedset min max
Virtual Memory sub-TODO: Virtual Memory sub-TODO:
* Check if the page selection algorithm is working well * Check if the page selection algorithm is working well

@ -101,6 +101,7 @@ static struct redisCommand cmdTable[] = {
{"zremrangebyscore",4,REDIS_CMD_INLINE}, {"zremrangebyscore",4,REDIS_CMD_INLINE},
{"zrange",-4,REDIS_CMD_INLINE}, {"zrange",-4,REDIS_CMD_INLINE},
{"zrangebyscore",-4,REDIS_CMD_INLINE}, {"zrangebyscore",-4,REDIS_CMD_INLINE},
{"zcount",4,REDIS_CMD_INLINE},
{"zrevrange",-4,REDIS_CMD_INLINE}, {"zrevrange",-4,REDIS_CMD_INLINE},
{"zcard",2,REDIS_CMD_INLINE}, {"zcard",2,REDIS_CMD_INLINE},
{"zscore",3,REDIS_CMD_BULK}, {"zscore",3,REDIS_CMD_BULK},

95
redis.c

@ -641,6 +641,7 @@ static void zaddCommand(redisClient *c);
static void zincrbyCommand(redisClient *c); static void zincrbyCommand(redisClient *c);
static void zrangeCommand(redisClient *c); static void zrangeCommand(redisClient *c);
static void zrangebyscoreCommand(redisClient *c); static void zrangebyscoreCommand(redisClient *c);
static void zcountCommand(redisClient *c);
static void zrevrangeCommand(redisClient *c); static void zrevrangeCommand(redisClient *c);
static void zcardCommand(redisClient *c); static void zcardCommand(redisClient *c);
static void zremCommand(redisClient *c); static void zremCommand(redisClient *c);
@ -699,6 +700,7 @@ static struct redisCommand cmdTable[] = {
{"zremrangebyscore",zremrangebyscoreCommand,4,REDIS_CMD_INLINE}, {"zremrangebyscore",zremrangebyscoreCommand,4,REDIS_CMD_INLINE},
{"zrange",zrangeCommand,-4,REDIS_CMD_INLINE}, {"zrange",zrangeCommand,-4,REDIS_CMD_INLINE},
{"zrangebyscore",zrangebyscoreCommand,-4,REDIS_CMD_INLINE}, {"zrangebyscore",zrangebyscoreCommand,-4,REDIS_CMD_INLINE},
{"zcount",zcountCommand,4,REDIS_CMD_INLINE},
{"zrevrange",zrevrangeCommand,-4,REDIS_CMD_INLINE}, {"zrevrange",zrevrangeCommand,-4,REDIS_CMD_INLINE},
{"zcard",zcardCommand,2,REDIS_CMD_INLINE}, {"zcard",zcardCommand,2,REDIS_CMD_INLINE},
{"zscore",zscoreCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM}, {"zscore",zscoreCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM},
@ -2408,6 +2410,14 @@ static void addReplyDouble(redisClient *c, double d) {
(unsigned long) strlen(buf),buf)); (unsigned long) strlen(buf),buf));
} }
static void addReplyLong(redisClient *c, long l) {
char buf[128];
size_t len;
len = snprintf(buf,sizeof(buf),":%ld\r\n",l);
addReplySds(c,sdsnewlen(buf,len));
}
static void addReplyBulkLen(redisClient *c, robj *obj) { static void addReplyBulkLen(redisClient *c, robj *obj) {
size_t len; size_t len;
@ -5192,30 +5202,51 @@ static void zrevrangeCommand(redisClient *c) {
zrangeGenericCommand(c,1); zrangeGenericCommand(c,1);
} }
static void zrangebyscoreCommand(redisClient *c) { /* This command implements both ZRANGEBYSCORE and ZCOUNT.
* If justcount is non-zero, just the count is returned. */
static void genericZrangebyscoreCommand(redisClient *c, int justcount) {
robj *o; robj *o;
double min = strtod(c->argv[2]->ptr,NULL); double min, max;
double max = strtod(c->argv[3]->ptr,NULL); int minex = 0, maxex = 0; /* are min or max exclusive? */
int offset = 0, limit = -1; int offset = 0, limit = -1;
int withscores = 0; int withscores = 0;
int badsyntax = 0; int badsyntax = 0;
/* Parse the min-max interval. If one of the values is prefixed
* by the "(" character, it's considered "open". For instance
* ZRANGEBYSCORE zset (1.5 (2.5 will match min < x < max
* ZRANGEBYSCORE zset 1.5 2.5 will instead match min <= x <= max */
if (((char*)c->argv[2]->ptr)[0] == '(') {
min = strtod((char*)c->argv[2]->ptr+1,NULL);
minex = 1;
} else {
min = strtod(c->argv[2]->ptr,NULL);
}
if (((char*)c->argv[3]->ptr)[0] == '(') {
max = strtod((char*)c->argv[3]->ptr+1,NULL);
maxex = 1;
} else {
max = strtod(c->argv[3]->ptr,NULL);
}
/* Parse "WITHSCORES": note that if the command was called with
* the name ZCOUNT then we are sure that c->argc == 4, so we'll never
* enter the following paths to parse WITHSCORES and LIMIT. */
if (c->argc == 5 || c->argc == 8) { if (c->argc == 5 || c->argc == 8) {
if (strcasecmp(c->argv[c->argc-1]->ptr,"withscores") == 0) if (strcasecmp(c->argv[c->argc-1]->ptr,"withscores") == 0)
withscores = 1; withscores = 1;
else else
badsyntax = 1; badsyntax = 1;
} }
if (c->argc != (4 + withscores) && c->argc != (7 + withscores)) if (c->argc != (4 + withscores) && c->argc != (7 + withscores))
badsyntax = 1; badsyntax = 1;
if (badsyntax) { if (badsyntax) {
addReplySds(c, addReplySds(c,
sdsnew("-ERR wrong number of arguments for ZRANGEBYSCORE\r\n")); sdsnew("-ERR wrong number of arguments for ZRANGEBYSCORE\r\n"));
return; return;
} }
/* Parse "LIMIT" */
if (c->argc == (7 + withscores) && strcasecmp(c->argv[4]->ptr,"limit")) { if (c->argc == (7 + withscores) && strcasecmp(c->argv[4]->ptr,"limit")) {
addReply(c,shared.syntaxerr); addReply(c,shared.syntaxerr);
return; return;
@ -5225,9 +5256,10 @@ static void zrangebyscoreCommand(redisClient *c) {
if (offset < 0) offset = 0; if (offset < 0) offset = 0;
} }
/* Ok, lookup the key and get the range */
o = lookupKeyRead(c->db,c->argv[1]); o = lookupKeyRead(c->db,c->argv[1]);
if (o == NULL) { if (o == NULL) {
addReply(c,shared.nullmultibulk); addReply(c,justcount ? shared.czero : shared.nullmultibulk);
} else { } else {
if (o->type != REDIS_ZSET) { if (o->type != REDIS_ZSET) {
addReply(c,shared.wrongtypeerr); addReply(c,shared.wrongtypeerr);
@ -5235,14 +5267,17 @@ static void zrangebyscoreCommand(redisClient *c) {
zset *zsetobj = o->ptr; zset *zsetobj = o->ptr;
zskiplist *zsl = zsetobj->zsl; zskiplist *zsl = zsetobj->zsl;
zskiplistNode *ln; zskiplistNode *ln;
robj *ele, *lenobj; robj *ele, *lenobj = NULL;
unsigned int rangelen = 0; unsigned long rangelen = 0;
/* Get the first node with the score >= min */ /* Get the first node with the score >= min, or with
* score > min if 'minex' is true. */
ln = zslFirstWithScore(zsl,min); ln = zslFirstWithScore(zsl,min);
while (minex && ln && ln->score == min) ln = ln->forward[0];
if (ln == NULL) { if (ln == NULL) {
/* No element matching the speciifed interval */ /* No element matching the speciifed interval */
addReply(c,shared.emptymultibulk); addReply(c,justcount ? shared.czero : shared.emptymultibulk);
return; return;
} }
@ -5250,33 +5285,49 @@ static void zrangebyscoreCommand(redisClient *c) {
* are in the list, so we push this object that will represent * are in the list, so we push this object that will represent
* the multi-bulk length in the output buffer, and will "fix" * the multi-bulk length in the output buffer, and will "fix"
* it later */ * it later */
lenobj = createObject(REDIS_STRING,NULL); if (!justcount) {
addReply(c,lenobj); lenobj = createObject(REDIS_STRING,NULL);
decrRefCount(lenobj); addReply(c,lenobj);
decrRefCount(lenobj);
}
while(ln && ln->score <= max) { while(ln && (maxex ? (ln->score < max) : (ln->score <= max))) {
if (offset) { if (offset) {
offset--; offset--;
ln = ln->forward[0]; ln = ln->forward[0];
continue; continue;
} }
if (limit == 0) break; if (limit == 0) break;
ele = ln->obj; if (!justcount) {
addReplyBulkLen(c,ele); ele = ln->obj;
addReply(c,ele); addReplyBulkLen(c,ele);
addReply(c,shared.crlf); addReply(c,ele);
if (withscores) addReply(c,shared.crlf);
addReplyDouble(c,ln->score); if (withscores)
addReplyDouble(c,ln->score);
}
ln = ln->forward[0]; ln = ln->forward[0];
rangelen++; rangelen++;
if (limit > 0) limit--; if (limit > 0) limit--;
} }
lenobj->ptr = sdscatprintf(sdsempty(),"*%d\r\n", if (justcount) {
withscores ? (rangelen*2) : rangelen); addReplyLong(c,(long)rangelen);
} else {
lenobj->ptr = sdscatprintf(sdsempty(),"*%lu\r\n",
withscores ? (rangelen*2) : rangelen);
}
} }
} }
} }
static void zrangebyscoreCommand(redisClient *c) {
genericZrangebyscoreCommand(c,0);
}
static void zcountCommand(redisClient *c) {
genericZrangebyscoreCommand(c,1);
}
static void zcardCommand(redisClient *c) { static void zcardCommand(redisClient *c) {
robj *o; robj *o;
zset *zs; zset *zs;