]> git.saurik.com Git - redis.git/commitdiff
ZRANGEBYSCORE implemented. Redis got range queries!
authorantirez <antirez@gmail.com>
Mon, 26 Oct 2009 22:02:56 +0000 (23:02 +0100)
committerantirez <antirez@gmail.com>
Mon, 26 Oct 2009 22:02:56 +0000 (23:02 +0100)
redis-cli.c
redis.c

index 54867fabdae8cac6d121f0accd9b375fe7035308..46450f6b3669057cde0a3fca37271c8b7df4e039 100644 (file)
@@ -92,6 +92,7 @@ static struct redisCommand cmdTable[] = {
     {"zadd",4,REDIS_CMD_BULK},
     {"zrem",3,REDIS_CMD_BULK},
     {"zrange",4,REDIS_CMD_INLINE},
+    {"zrangebyscore",4,REDIS_CMD_INLINE},
     {"zrevrange",4,REDIS_CMD_INLINE},
     {"zlen",2,REDIS_CMD_INLINE},
     {"incrby",3,REDIS_CMD_INLINE},
diff --git a/redis.c b/redis.c
index 1c07dbc2f1e02145acb1582df9b3bf176680f5d8..09237345f2e98a455a0d39af2c6e300aff31daa4 100644 (file)
--- a/redis.c
+++ b/redis.c
@@ -444,6 +444,7 @@ static void msetCommand(redisClient *c);
 static void msetnxCommand(redisClient *c);
 static void zaddCommand(redisClient *c);
 static void zrangeCommand(redisClient *c);
+static void zrangebyscoreCommand(redisClient *c);
 static void zrevrangeCommand(redisClient *c);
 static void zlenCommand(redisClient *c);
 static void zremCommand(redisClient *c);
@@ -488,6 +489,7 @@ static struct redisCommand cmdTable[] = {
     {"zadd",zaddCommand,4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM},
     {"zrem",zremCommand,3,REDIS_CMD_BULK},
     {"zrange",zrangeCommand,4,REDIS_CMD_INLINE},
+    {"zrangebyscore",zrangebyscoreCommand,4,REDIS_CMD_INLINE},
     {"zrevrange",zrevrangeCommand,4,REDIS_CMD_INLINE},
     {"zlen",zlenCommand,2,REDIS_CMD_INLINE},
     {"incrby",incrbyCommand,3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM},
@@ -3885,6 +3887,7 @@ static void zslInsert(zskiplist *zsl, double score, robj *obj) {
     zsl->length++;
 }
 
+/* Delete an element with matching score/object from the skiplist. */
 static int zslDelete(zskiplist *zsl, double score, robj *obj) {
     zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;
     int i;
@@ -3901,7 +3904,7 @@ static int zslDelete(zskiplist *zsl, double score, robj *obj) {
     /* We may have multiple elements with the same score, what we need
      * is to find the element with both the right score and object. */
     x = x->forward[0];
-    if (compareStringObjects(x->obj,obj) == 0) {
+    if (x && score == x->score && compareStringObjects(x->obj,obj) == 0) {
         for (i = 0; i < zsl->level; i++) {
             if (update[i]->forward[i] != x) break;
             update[i]->forward[i] = x->forward[i];
@@ -3923,6 +3926,22 @@ static int zslDelete(zskiplist *zsl, double score, robj *obj) {
     return 0; /* not found */
 }
 
+/* Find the first node having a score equal or greater than the specified one.
+ * Returns NULL if there is no match. */
+static zskiplistNode *zslFirstWithScore(zskiplist *zsl, double score) {
+    zskiplistNode *x;
+    int i;
+
+    x = zsl->header;
+    for (i = zsl->level-1; i >= 0; i--) {
+        while (x->forward[i] && x->forward[i]->score < score)
+            x = x->forward[i];
+    }
+    /* We may have multiple elements with the same score, what we need
+     * is to find the element with both the right score and object. */
+    return x->forward[0];
+}
+
 /* The actual Z-commands implementations */
 
 static void zaddCommand(redisClient *c) {
@@ -4076,6 +4095,52 @@ static void zrevrangeCommand(redisClient *c) {
     zrangeGenericCommand(c,1);
 }
 
+static void zrangebyscoreCommand(redisClient *c) {
+    robj *o;
+    double min = strtod(c->argv[2]->ptr,NULL);
+    double max = strtod(c->argv[3]->ptr,NULL);
+
+    o = lookupKeyRead(c->db,c->argv[1]);
+    if (o == NULL) {
+        addReply(c,shared.nullmultibulk);
+    } else {
+        if (o->type != REDIS_ZSET) {
+            addReply(c,shared.wrongtypeerr);
+        } else {
+            zset *zsetobj = o->ptr;
+            zskiplist *zsl = zsetobj->zsl;
+            zskiplistNode *ln;
+            robj *ele, *lenobj;
+            unsigned int rangelen = 0;
+
+            /* Get the first node with the score >= min */
+            ln = zslFirstWithScore(zsl,min);
+            if (ln == NULL) {
+                /* No element matching the speciifed interval */
+                addReply(c,shared.emptymultibulk);
+                return;
+            }
+
+            /* We don't know in advance how many matching elements there
+             * are in the list, so we push this object that will represent
+             * the multi-bulk length in the output buffer, and will "fix"
+             * it later */
+            lenobj = createObject(REDIS_STRING,NULL);
+            addReply(c,lenobj);
+
+            while(ln->score <= max) {
+                ele = ln->obj;
+                addReplyBulkLen(c,ele);
+                addReply(c,ele);
+                addReply(c,shared.crlf);
+                ln = ln->forward[0];
+                rangelen++;
+            }
+            lenobj->ptr = sdscatprintf(sdsempty(),"*%d\r\n",rangelen);
+        }
+    }
+}
+
 static void zlenCommand(redisClient *c) {
     robj *o;
     zset *zs;