I use awesome Redis sorted sets to score users and, then, quickly get user rating by score. Also, my score has "weight", so that one score can give 5 points to user, and another vote can give 2 points, etc. Now if somebody votes for user, I call
ZINCRBY user:votes <vote_weight> <userId>
but now I need to calculate users ratings for the last week, month, year from the current timestamp (like 'moving window')
What is the best way to do it in Redis?
Your current approach would only work if you're interested in counting all votes from the beginning of time until this instant..
Lets focus on the problem of doing this for today - this could be easily addressed by adding a new sorted, e.g. votes:today
, and doing the ZINCR
on its elements.
What happens when today becomes tomorrow? Simple - either RENAME
the key, e.g. to votes:yesterday
, or just use the timestamp to begin with so you'll always be updating today's vote key, i.e. votes:<timestamp day value>
If you use the timestamp approach, after a week you'll end up with 7 keys - on for each day - with scores-per-user. Getting the results for the last week is a simple matter of getting these 7 keys' members and summing up their scores. You could even do it on the fly. The same goes for 1m0, 3mo, 6mo, 12mo and so forth... but.
But, if you want to do it for 12mo (~= 365 keys), you'll need more RAM (for storing these keys in Redis) and it will take longer to complete the aggregation, naturally. You can combat this by combining Redis' key expiry capabilities (e.g. set the TTL of a day's key to 12mo to keep only one year of history) and keeping running aggregates that are updated either on the fly (i.e. with every vote) or periodically (daily, weekly, monthly, etc...). Note that the same scripts can do housekeeping and delete/archive old data, potentially solving the need to expire it explicitly.