agoragames / leaderboard

Leaderboards backed by Redis in Ruby
https://rubygems.org/gems/leaderboard
MIT License
480 stars 65 forks source link

Way to get ranking of a score #13

Closed jgadbois closed 12 years ago

jgadbois commented 12 years ago

Is there a way that I could pass leaderboard a number and get back the rank that score would be at?

czarneckid commented 12 years ago

Not currently. There would be a range of ranks that a given score could refer to as members in the leaderboard can have the same score.

jgadbois commented 12 years ago

That would be fine for my purposes. I don't see how to do that in Redis, though, without manually iterating through the results in the sorted sets. This would be a very useful feature for me, because on some pages I want to show how a user's score stacks up in terms of the leaderboard. Happy to help implement if you have any good ideas on how to do so.

jletourneau commented 12 years ago

In theory I suppose to be guaranteed a unique answer, the question could be posed not as "what rank would a score of 12345 give me" but "how many items in the leaderboard currently rank ahead of the score 12345" (the answer could be 0 if that's higher than anything currently in the leaderboard).

jgadbois commented 12 years ago

@jletourneau yes, I am actually doing that now in my current Postgres implementation.

rank = number of scores that are greater than my score + 1

czarneckid commented 12 years ago

@jgadbois Would the percentile_for(member) method work for you here?

jgadbois commented 12 years ago

@czarneckid no, because I'm talking about a non-high score for a user (so it won't actually be in the sorted set)

jgadbois commented 12 years ago

sorry accidentally closed

czarneckid commented 12 years ago

You could look at zrangebyscore or zrevrangebyscore to get the members at a score and then grab the ranks for those members or maybe just the first member.

czarneckid commented 12 years ago

Not sure if this would do what you want, but check out the leaders-from-score-range branch. In particular, https://github.com/agoragames/leaderboard/commit/858af9ac599543245cd6ec9dec2a33fd6d79157d.

leaders = highscore_lb.leaders_from_score_range(4, 19)
 => [{:member=>"member_10", :rank=>47, :score=>10.0}, {:member=>"member_9", :rank=>48, :score=>9.0}, {:member=>"member_8", :rank=>49, :score=>8.0}, {:member=>"member_7", :rank=>50, :score=>7.0}, {:member=>"member_6", :rank=>51, :score=>6.0}, {:member=>"member_5", :rank=>52, :score=>5.0}, {:member=>"member_4", :rank=>53, :score=>4.0}] 
jgadbois commented 12 years ago

That would definitely get me close. Say particular score I was looking at was 17. I could grab leaders for some range of scores around that (say 7-27), manually filter down to see who was closest to 17, and grab their rank.

There would be an issue if no members were found within a certain range because I would have no ranks to compare against.

czarneckid commented 12 years ago

The only other seemingly applicable method here is total_members_in_score_range which uses the Redis sorted set command zcount to get the total # of elements within a score range. I guess you could have custom logic which centered around a given score, opened up the range slightly until finding some # of members > 0 and then grabbing the members from the given score range.

jgadbois commented 12 years ago

Yeah that's kind of what I was thinking. I think if you added these functions, then I could do what I wanted to by writing the custom logic you describe.

czarneckid commented 12 years ago

Well, total_members_in_score_range is already available. I'll merge the leaders-from-score-range branch in next week. I want to make sure I've noodled on that for a bit.

jgadbois commented 12 years ago

Ok, thanks!

jgadbois commented 12 years ago

Would you be interested in having this algorithm inside of the leaderboard gem vs in my app logic?

czarneckid commented 12 years ago

I think a good compromise here would be a wiki page demonstrating that particular use-case. I believe you should be able to edit the main page in the wiki and go over the use-case and your code in a sub-page.