Permalink
Browse files

implement sorted sets commands

  • Loading branch information...
1 parent abbf5b0 commit dbfda0d82d1179b3c5178574351a2324ca03b81f @cofyc committed Sep 5, 2012
Showing with 139 additions and 18 deletions.
  1. +96 −16 lib/Redis.pm
  2. +43 −2 t/02-sortedsets.t
View
@@ -24,7 +24,7 @@ has Str $.encoding = "UTF-8"; # Use this encoding to decode Str
# If True, decode Buf response into Str, except following methods:
# dump
# which, must return Buf
-has Bool $.decode_response = False;
+has Bool $.decode_response = True;
has $.conn is rw;
# Predefined callbacks
@@ -40,7 +40,7 @@ for "AUTH QUIT SET MSET PSETEX SETEX MIGRATE RENAME RENAMENX RESTORE HMSET SELEC
for "EXISTS SETNX EXPIRE EXPIREAT MOVE PERSIST PEXPIRE PEXPIREAT HSET HEXISTS HSETNX SISMEMBER SMOVE".split(" ") -> $c {
%command_callbacks{$c} = &integer_reply_cb;
}
-for "INCRBYFLOAT HINCRBYFLOAT".split(" ") -> $c {
+for "INCRBYFLOAT HINCRBYFLOAT ZINCRBY ZSCORE".split(" ") -> $c {
%command_callbacks{$c} = &buf_to_float_cb;
}
# TODO so ugly...
@@ -621,51 +621,131 @@ method sunionstore(Str $destination, *@keys) returns Int {
###### Commands/SortedSets #######
method zadd(Str $key, *@args, *%named) returns Int {
+ my @newargs = Array.new;
+ @args = @args.reverse;
+ for @args {
+ if $_.WHAT === Pair {
+ @newargs.push(.value);
+ @newargs.push(.key);
+ } else {
+ @newargs.push($_);
+ }
+ }
+ for %named {
+ @newargs.push(.value);
+ @newargs.push(.key);
+ }
+ if @newargs.elems % 2 != 0 {
+ die "ZADD requires an equal number of values and scores";
+ }
+ return self!exec_command("ZADD", $key, |@newargs);
}
method zcard(Str $key) returns Int {
+ return self!exec_command("ZCARD", $key);
}
-method zcount(Str $key, $min, $max) returns Int {
+# TODO support (1, -inf, +inf syntax, http://redis.io/commands/zcount
+method zcount(Str $key, Real $min, Real $max) returns Int {
+ return self!exec_command("ZCOUNT", $key, $min, $max);
}
-method zincrby(Str $key, $increment, $member) {
+method zincrby(Str $key, Real $increment, $member) returns Real {
+ return self!exec_command("ZINCRBY", $key, $increment, $member);
}
-method zinterstore() {
+method zinterstore(Str $destination, *@keys, :WEIGHTS(@weights)?, :AGGREGATE(@aggregate)?) returns Int {
+ my @args = Array.new;
+ if @weights.elems > 0 {
+ @args.push("WEIGHTS");
+ for @weights {
+ @args.push($_);
+ }
+ }
+ if @aggregate.elems > 0 {
+ @args.push("AGGREGATE");
+ for @aggregate {
+ @args.push($_);
+ }
+ }
+ return self!exec_command("ZINTERSTORE", $destination, @keys.elems, |@keys, |@args);
}
-method zrange(Str $key, $start, $stop, :$WITHSCORES?) {
+# TODO return array of paires if WITHSCORES is set
+method zrange(Str $key, Int $start, Int $stop, :WITHSCORES($withscores)?) {
+ return self!exec_command("ZRANGE", $key, $start, $stop, $withscores.defined ?? "WITHSCORES" !! Nil);
}
-method zrangebyscore(Str $key, $min, $max, :$WITHSCORES, :$offset, :$count) {
+# TODO return array of paires if WITHSCORES is set
+method zrangebyscore(Str $key, Real $min, Real $max, :WITHSCORES($withscores), Int :OFFSET($offset)?, Int :COUNT($count)?) returns List {
+ if ($offset.defined and !$count.defined) or (!$offset.defined and $count.defined) {
+ die "`offset` and `count` must both be specified.";
+ }
+ return self!exec_command("ZRANGEBYSCORE", $key, $min, $max,
+ $withscores.defined ?? "WITHSCORES" !! Nil,
+ ($offset.defined and $count.defined) ?? "LIMIT" !! Nil,
+ $offset.defined ?? $offset !! Nil,
+ $count.defined ?? $count !! Nil
+ );
}
-method zrank(Str $key, $member) {
+method zrank(Str $key, $member) returns Any {
+ return self!exec_command("ZRANK", $key, $member);
}
-method zrem(Str $key, *@members) {
+method zrem(Str $key, *@members) returns Int {
+ return self!exec_command("ZREM", $key, |@members);
}
-method zremrangbyrank(Str $key, $start, $stop) {
+method zremrangbyrank(Str $key, Int $start, Int $stop) returns Int {
+ return self!exec_command("ZREMRANGEBYRANK", $key, $start, $stop);
}
-method zremrangebyscore(Str $key, $min, $max) {
+method zremrangebyscore(Str $key, Real $min, Real $max) returns Int {
+ return self!exec_command("ZREMRANGEBYSCORE", $key, $min, $max);
}
-method zrevrange(Str $key, $start, $stop, :$WITHSCORES) {
+# TODO return array of paires if WITHSCORES is set
+method zrevrange(Str $key, $start, $stop, :WITHSCORES($withscores)?) {
+ return self!exec_command("ZREVRANGE", $key, $start, $stop, $withscores.defined ?? "WITHSCORES" !! Nil);
}
-method zrevrangebyscore(Str $key, $max, $min, :$WITHSCORES, :$offset, :$count) {
+# TODO return array of paires if WITHSCORES is set
+method zrevrangebyscore(Str $key, Real $min, Real $max, :WITHSCORES($withscores), Int :OFFSET($offset)?, Int :COUNT($count)?) returns List {
+ if ($offset.defined and !$count.defined) or (!$offset.defined and $count.defined) {
+ die "`offset` and `count` must both be specified.";
+ }
+ return self!exec_command("ZREVRANGEBYSCORE", $key, $min, $max,
+ $withscores.defined ?? "WITHSCORES" !! Nil,
+ ($offset.defined and $count.defined) ?? "LIMIT" !! Nil,
+ $offset.defined ?? $offset !! Nil,
+ $count.defined ?? $count !! Nil
+ );
}
-method zrevrank(Str $key, $member) {
+method zrevrank(Str $key, $member) returns Any {
+ return self!exec_command("ZREVRANK", $key, $member);
}
-method zscore(Str $key, $member) {
+method zscore(Str $key, $member) returns Real {
+ return self!exec_command("ZSCORE", $key, $member);
}
-method zunionstore(Str $destination, $numkeys, *@keys) {
+method zunionstore(Str $destination, *@keys, :WEIGHTS(@weights)?, :AGGREGATE(@aggregate)?) returns Int {
+ my @args = Array.new;
+ if @weights.elems > 0 {
+ @args.push("WEIGHTS");
+ for @weights {
+ @args.push($_);
+ }
+ }
+ if @aggregate.elems > 0 {
+ @args.push("AGGREGATE");
+ for @aggregate {
+ @args.push($_);
+ }
+ }
+ return self!exec_command("ZUNIONSTORE", $destination, @keys.elems, |@keys, |@args);
}
###### ! Commands/SortedSets #######
View
@@ -5,8 +5,49 @@ use Redis;
use Test;
use Test::SpawnRedisServer;
-my $r = Redis.new();
+my $r = Redis.new(decode_response => True);
$r.connect;
$r.flushall;
-#plan 1;
+plan 8;
+
+is_deeply $r.zadd("myzset", ONE => 1, TWO => 2, THREE=> 3), 3;
+is_deeply $r.zadd("myzset", "ZERO", 0, "ONE" => 1, TWO => 2, THREE=> 3), 1;
+dies_ok { $r.zadd("myzset", "ZERO", ONE => 1, TWO => 2, THREE=> 3) }
+
+is_deeply $r.zcard("myzset"), 4;
+
+is_deeply $r.zcount("myzset", 2, 3), 2;
+
+is_deeply $r.zincrby("myzset", 1.1, "THREE"), 4.1;
+
+# zinterstore & zrange & zrangebyscore & zrevrange & zrevrangebyscore
+$r.zadd("zset1", "one" => 1, "two" => 2);
+$r.zadd("zset2", "one" => 1, "two" => 2, "three" => 3);
+is_deeply $r.zinterstore("out", "zset1", "zset2", weights => (2,3)), 2;
+is_deeply $r.zinterstore("out", "zset1", "zset2", WEIGHTS => (2,3)), 2;
+is_deeply $r.zrange("out", 0, -1), ["one", "two"];
+is_deeply $r.zrange("out", 0, -1, :WITHSCORES), ["one", "5", "two", "10"];
+is_deeply $r.zrevrange("out", 0, -1, :WITHSCORES), ["two", "10", "one", "5"];
+is_deeply $r.zrangebyscore("out", 6, 10), ["two"];
+is_deeply $r.zrangebyscore("out", 6, 10, :WITHSCORES), ["two", "10"];
+is_deeply $r.zrangebyscore("out", 0, 10, :WITHSCORES, OFFSET=>0, COUNT=>1), ["one", "5"];
+is_deeply $r.zrevrangebyscore("out", 10, 0, :WITHSCORES, OFFSET=>0, COUNT=>1), ["two", "10"];
+
+# zrank & zrem & zremrangbyrank & zrevrank
+$r.flushall;
+$r.zadd("myzset", one=>1, two=>2, three=>3, four=>4);
+is_deeply $r.zrank("myzset", "one"), 0;
+is_deeply $r.zrevrank("myzset", "one"), 3;
+is_deeply $r.zrank("myzset", "other"), Nil;
+is_deeply $r.zrem("myzset", "other", "one"), 1;
+is_deeply $r.zremrangbyrank("myzset", 0, 1), 2;
+is_deeply $r.zremrangebyscore("myzset", 2, 3), 0;
+
+# zscore & zunionstore
+$r.flushall;
+$r.zadd("myzset", one=>1, two=>2, three=>3, four=>4);
+$r.zadd("myzset2", five=>5, six=>6);
+is_deeply $r.zscore("myzset", "three"), 3;
+is_deeply $r.zunionstore("newset", "myzset", "myzset2"), 6;
+

0 comments on commit dbfda0d

Please sign in to comment.