/
competition_ranking_leaderboard.rb
131 lines (112 loc) · 4.42 KB
/
competition_ranking_leaderboard.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
require 'leaderboard'
class CompetitionRankingLeaderboard < Leaderboard
# Retrieve the rank for a member in the named leaderboard.
#
# @param leaderboard_name [String] Name of the leaderboard.
# @param member [String] Member name.
#
# @return the rank for a member in the leaderboard.
def rank_for_in(leaderboard_name, member)
member_score = score_for_in(leaderboard_name, member)
if @reverse
return @redis_connection.zcount(leaderboard_name, '-inf', "(#{member_score}") + 1 rescue nil
else
return @redis_connection.zcount(leaderboard_name, "(#{member_score}", '+inf') + 1 rescue nil
end
end
# Retrieve the score and rank for a member in the named leaderboard.
#
# @param leaderboard_name [String]Name of the leaderboard.
# @param member [String] Member name.
#
# @return the score and rank for a member in the named leaderboard as a Hash.
def score_and_rank_for_in(leaderboard_name, member)
responses = @redis_connection.multi do |transaction|
transaction.zscore(leaderboard_name, member)
if @reverse
transaction.zrank(leaderboard_name, member)
else
transaction.zrevrank(leaderboard_name, member)
end
end
responses[0] = responses[0].to_f if responses[0]
responses[1] =
if @reverse
@redis_connection.zcount(leaderboard_name, '-inf', "(#{responses[0]}") + 1 rescue nil
else
@redis_connection.zcount(leaderboard_name, "(#{responses[0]}", '+inf') + 1 rescue nil
end
{@member_key => member, @score_key => responses[0], @rank_key => responses[1]}
end
# Retrieve a page of leaders from the named leaderboard for a given list of members.
#
# @param leaderboard_name [String] Name of the leaderboard.
# @param members [Array] Member names.
# @param options [Hash] Options to be used when retrieving the page from the named leaderboard.
#
# @return a page of leaders from the named leaderboard for a given list of members.
def ranked_in_list_in(leaderboard_name, members, options = {})
leaderboard_options = DEFAULT_LEADERBOARD_REQUEST_OPTIONS.dup
leaderboard_options.merge!(options)
ranks_for_members = []
responses = @redis_connection.multi do |transaction|
members.each do |member|
if @reverse
transaction.zrank(leaderboard_name, member)
else
transaction.zrevrank(leaderboard_name, member)
end
transaction.zscore(leaderboard_name, member)
end
end unless leaderboard_options[:members_only]
included_members = []
scores = []
members.each_with_index do |member, index|
data = {}
data[@member_key] = member
unless leaderboard_options[:members_only]
data[@score_key] = responses[index * 2 + 1].to_f if responses[index * 2 + 1]
if data[@score_key] == nil
next unless leaderboard_options[:include_missing]
end
end
included_members << member
scores << data[@score_key]
ranks_for_members << data
end
if leaderboard_options[:with_member_data]
members_data_for_in(leaderboard_name, included_members).each_with_index do |member_data, index|
ranks_for_members[index][@member_data_key] = member_data
end
end
rankings_for_members_having_scores_in(leaderboard_name, included_members, scores).each_with_index do |rank, index|
ranks_for_members[index][@rank_key] = rank
end
case leaderboard_options[:sort_by]
when :rank
ranks_for_members = ranks_for_members.sort_by { |member| member[@rank_key] }
when :score
ranks_for_members = ranks_for_members.sort_by { |member| member[@score_key] }
end
ranks_for_members
end
protected
# Retrieve a list of the rankings of leaders given their member names and scores
#
# @param leaderboard_name [String] Name of the leaderboard.
# @param members [Array] Member names.
# @param scores [Array] a list of scores for the members, aligned with the member names
#
# @return a list of the rankings for the passed in members and scores
def rankings_for_members_having_scores_in(leaderboard_name, members, scores)
@redis_connection.multi do |transaction|
members.each_with_index do |member, index|
if @reverse
transaction.zcount(leaderboard_name, '-inf', "(#{scores[index]}")
else
transaction.zcount(leaderboard_name, "(#{scores[index]}", '+inf')
end
end
end.map{|rank| rank ? rank + 1 : rank}
end
end