How to cap a leaderboard in Redis to only N elements?
Asked Answered
R

2

7

I need to create a leaderboard with maximum number of elements, say N ? I know how to use LPUSH + LTRIM to cap a list size to N elements. How to implement this for leaderboard using a Redis sortedset

So far my approach is to do this in 3 steps: a) ZADD to add a score+ item to the leaderboard

b) find the rank of the Nth element (I am not sure how to do this)

c) do a ZREMRANGEBYRANK leaderboard 0 rank_of_the_nth_element.

Is there a better way ?

Rotenone answered 11/10, 2012 at 18:16 Comment(0)
S
8

I actually have a working leaderboard app which uses redis. You can check it out here. In my app the leader boards are limited by the top n scores, and also old scores drop off when they are too old (so there can be day, week, month, year high score boards).

Anyways, I think what you are looking to do is an overall leader board, so low scores get pushed off. In the sense of Redis Sorted Set scores, if you have it set up so high scores should stay (they are at the bottom) and low scores should leave (they are at the top) then you would do something like:

ZREMRANGBYRANK leaderboard 0 -100

That example assumes you want to keep the last 100 scores.

Maybe you invert the scores, so a "high score" of 1000 is stored as -1000 in redis, so its first in the sorted set. In that case it would be the same as above, but use ZREMRANGEBYRANK leaderboard 100 -1 to delete all the items after the first 100.

Update: Realized there is ZREMRANGEBYRANK so simplified my example.

Your proposed solution should work as well. If you want to find the score of the nth element, you can use this:

hundredth_entry = ZRANGE leaderboard 100 100
hundredth_score = ZSCORE leaderboard hundredth_entry
ZREMRANGEBYSCORE leaderboard -inf (hundredth_score

The ( is there to make the range NOT inclusive, so anything LESS than the hundredth score will be deleted but not the hundredth score itself.

Sod answered 11/10, 2012 at 20:5 Comment(0)
D
2

A lua implementation of OP's algorithm:

redis.call("ZADD", KEYS[1], ARGV[1]+0, ARGV[2])
local n = redis.call("ZCARD", KEYS[1])
if n > ARGV[3]+0 then 
    redis.call("ZREMRANGEBYRANK", KEYS[1], 0, n-ARGV[3]-1) 
end

Then replace

ZADD key score member

with

EVAL script 1 key score member cap
Dispense answered 5/11, 2013 at 11:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.