Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reverse Pagination Through A Redis Sorted Set

Consider a Redis sorted set with the following members:

ZADD mySortedSet 11 "A"
ZADD mySortedSet 21 "B"
ZADD mySortedSet 32 "C"
ZADD mySortedSet 46 "D"
ZADD mySortedSet 53 "E"
ZADD mySortedSet 68 "F"
ZADD mySortedSet 72 "G"
ZADD mySortedSet 82 "H" 
ZADD mySortedSet 94 "I"
ZADD mySortedSet 104 "J"
ZADD mySortedSet 113 "K"

If I want to do pagination in reverse order, starting at an arbitrary slice, I can start with this:

// Returns G, F, E, as expected.
ZREVRANGEBYSCORE mySortedSet 72 (46

Now, knowing only that my upper bound is 46, I can get the previous 3 items in the set, D, C, and B, without knowing the lower bound by doing:

ZREVRANGEBYSCORE mySortedSet 46 -inf LIMIT 0, 3

My question is, how can I get the next 3 items in the set, J, I and H, in that order, knowing only that the upper bound is 72?

// Good start, returns K, J, I, H
ZREVRANGEBYSCORE mySortedSet +inf (72

// Returns K, J, I, due to the offset of 0.  I don't know what the correct offset is because it's from the start of the range, not the end.
ZREVRANGEBYSCORE mySortedSet +inf (72 LIMIT 0, 3

What I think I want is a negative offset, which I don't think is supported.

// Would return J, I, H, but actually returns an empty set.
ZREVRANGEBYSCORE mySortedSet +inf (72 LIMIT -1, 3

I can fake it with a forward range, and then reversing those items, but I'm looking for a native Redis solution, if one exists.

// Returns H, I, J - the items I want, but reversed.
ZRANGEBYSCORE mySortedSet (72 +inf LIMIT 0, 3

Any ideas?

To be clear, I know there is ZRANGE and ZREVRANGE, but in this querying profile, I won't know the actual index, just the score.

like image 625
majelbstoat Avatar asked Nov 14 '22 01:11

majelbstoat


1 Answers

It's trivial to get the rank for an element, and then work by indexes. Presuming that the only inputs available to your application are the initial score bounds of 72 and 46, you can do this:

redis 127.0.0.1:6379> ZREVRANGEBYSCORE mySortedSet 72 (46
1) "G"
2) "F"
3) "E"
redis 127.0.0.1:6379> ZREVRANK mySortedSet G
(integer) 4
redis 127.0.0.1:6379> ZREVRANGE mySortedSet 1 3
1) "J"
2) "I"
3) "H"
redis 127.0.0.1:6379> 

The only additional call is the O(log(N)) ZREVRANK call. From there, it's a bit of client side math to get the new indexes for the range you're interested in, and ZREVRANGE to get the values you want.

I tested this on Redis 2.6rc5, but it should work on any version over 2.0.

like image 176
Mark Tozzi Avatar answered Nov 27 '22 07:11

Mark Tozzi