|
16 | 16 |
|
17 | 17 | package app;
|
18 | 18 |
|
| 19 | +import java.util.ArrayList; |
| 20 | +import java.util.List; |
19 | 21 | import org.springframework.stereotype.Controller;
|
20 | 22 | import redis.clients.jedis.Jedis;
|
21 | 23 | import redis.clients.jedis.resps.Tuple;
|
22 |
| -import java.util.ArrayList; |
23 |
| -import java.util.List; |
24 | 24 |
|
25 | 25 | @Controller
|
26 | 26 | public class DataController {
|
27 | 27 |
|
28 |
| - /** Repository for persisting leaderboard entries. */ |
29 |
| - private final LeaderboardRepository leaderboardRepository; |
30 |
| - |
31 |
| - /** Redis client for caching leaderboard data. */ |
32 |
| - private final Jedis jedis; |
33 |
| - |
34 |
| - /** |
35 |
| - * Constructs a new DataController. |
36 |
| - * |
37 |
| - * @param redisClient Redis client for caching |
38 |
| - * @param repository Repository for persistence |
39 |
| - */ |
40 |
| - public DataController(final Jedis redisClient, |
41 |
| - final LeaderboardRepository repository) { |
42 |
| - this.leaderboardRepository = repository; |
43 |
| - this.jedis = redisClient; |
| 28 | + /** Repository for persisting leaderboard entries. */ |
| 29 | + private final LeaderboardRepository leaderboardRepository; |
| 30 | + |
| 31 | + /** Redis client for caching leaderboard data. */ |
| 32 | + private final Jedis jedis; |
| 33 | + |
| 34 | + /** |
| 35 | + * Constructs a new DataController. |
| 36 | + * |
| 37 | + * @param redisClient Redis client for caching |
| 38 | + * @param repository Repository for persistence |
| 39 | + */ |
| 40 | + public DataController(final Jedis redisClient, |
| 41 | + final LeaderboardRepository repository) { |
| 42 | + this.leaderboardRepository = repository; |
| 43 | + this.jedis = redisClient; |
| 44 | + } |
| 45 | + |
| 46 | + /** |
| 47 | + * Get the leaderboard entries starting from the given position. |
| 48 | + * |
| 49 | + * @param position The starting position of the entries to search. |
| 50 | + * @param orderBy The order of the entries. |
| 51 | + * @param pageSize The number of entries to return. |
| 52 | + * @param username The username to check the rank of. |
| 53 | + * @return The leaderboard entries. |
| 54 | + */ |
| 55 | + public LeaderboardResponse getLeaderboard( |
| 56 | + final long position, |
| 57 | + final OrderByType orderBy, |
| 58 | + final long pageSize, |
| 59 | + final String username) { |
| 60 | + String cacheKey = Global.LEADERBOARD_ENTRIES_KEY; |
| 61 | + long maxPosition = position + pageSize - 1; |
| 62 | + |
| 63 | + // Initialize the cache if it's empty |
| 64 | + boolean cacheUpdated = this.initializeCache(); |
| 65 | + |
| 66 | + // Set the cache status for the front end |
| 67 | + int cacheStatus = cacheUpdated |
| 68 | + ? FromCacheType.FROM_DB.getValue() |
| 69 | + : FromCacheType.FULL_CACHE.getValue(); |
| 70 | + |
| 71 | + // If we have a username, search for the user's rank |
| 72 | + if (username != null) { |
| 73 | + Long userRank = jedis.zrevrank(cacheKey, username); |
| 74 | + if (userRank != null) { |
| 75 | + long pos = userRank; |
| 76 | + long maxPos = userRank + pageSize - 1; |
| 77 | + |
| 78 | + return new LeaderboardResponse( |
| 79 | + getEntries(cacheKey, pos, maxPos, true), |
| 80 | + cacheStatus); |
| 81 | + } |
44 | 82 | }
|
45 | 83 |
|
46 |
| - /** |
47 |
| - * Get the leaderboard entries starting from the given position. |
48 |
| - * |
49 |
| - * @param position The starting position of the entries to search. |
50 |
| - * @param orderBy The order of the entries. |
51 |
| - * @param pageSize The number of entries to return. |
52 |
| - * @param username The username to check the rank of. |
53 |
| - * @return The leaderboard entries. |
54 |
| - */ |
55 |
| - public LeaderboardResponse getLeaderboard( |
56 |
| - final long position, |
57 |
| - final OrderByType orderBy, |
58 |
| - final long pageSize, |
59 |
| - final String username) { |
60 |
| - String cacheKey = Global.LEADERBOARD_ENTRIES_KEY; |
61 |
| - long maxPosition = position + pageSize - 1; |
62 |
| - |
63 |
| - // Initialize the cache if it's empty |
64 |
| - boolean cacheUpdated = this.initializeCache(); |
65 |
| - |
66 |
| - // Set the cache status for the front end |
67 |
| - int cacheStatus = cacheUpdated |
68 |
| - ? FromCacheType.FROM_DB.getValue() |
69 |
| - : FromCacheType.FULL_CACHE.getValue(); |
70 |
| - |
71 |
| - // If we have a username, search for the user's rank |
72 |
| - if (username != null) { |
73 |
| - Long userRank = jedis.zrevrank(cacheKey, username); |
74 |
| - if (userRank != null) { |
75 |
| - long pos = userRank; |
76 |
| - long maxPos = userRank + pageSize - 1; |
77 |
| - |
78 |
| - return new LeaderboardResponse( |
79 |
| - getEntries(cacheKey, pos, maxPos, true), |
80 |
| - cacheStatus); |
81 |
| - } |
82 |
| - } |
83 |
| - |
84 |
| - // Get the leaderboard entries depending on the order |
85 |
| - List<LeaderboardEntry> leaderboardList = getEntries( |
86 |
| - cacheKey, position, maxPosition, |
87 |
| - orderBy == OrderByType.HIGH_TO_LOW); |
88 |
| - |
89 |
| - return new LeaderboardResponse(leaderboardList, cacheStatus); |
| 84 | + // Get the leaderboard entries depending on the order |
| 85 | + List<LeaderboardEntry> leaderboardList = getEntries( |
| 86 | + cacheKey, position, maxPosition, |
| 87 | + orderBy == OrderByType.HIGH_TO_LOW); |
| 88 | + |
| 89 | + return new LeaderboardResponse(leaderboardList, cacheStatus); |
| 90 | + } |
| 91 | + |
| 92 | + private List<LeaderboardEntry> getEntries( |
| 93 | + final String cacheKey, |
| 94 | + final long position, |
| 95 | + final long maxPosition, |
| 96 | + final boolean isDescending) { |
| 97 | + // Define an object |
| 98 | + List<Tuple> entries = new ArrayList<>(); |
| 99 | + |
| 100 | + // Use zrevrangeWithScores to get the entries in descending order |
| 101 | + if (isDescending) { |
| 102 | + entries = new ArrayList<>( |
| 103 | + jedis.zrevrangeWithScores(cacheKey, position, maxPosition)); |
90 | 104 | }
|
91 | 105 |
|
92 |
| - private List<LeaderboardEntry> getEntries( |
93 |
| - final String cacheKey, |
94 |
| - final long position, |
95 |
| - final long maxPosition, |
96 |
| - final boolean isDescending) { |
97 |
| - // Define an object |
98 |
| - List<Tuple> entries = new ArrayList<>(); |
99 |
| - |
100 |
| - // Use zrevrangeWithScores to get the entries in descending order |
101 |
| - if (isDescending) { |
102 |
| - entries = new ArrayList<>( |
103 |
| - jedis.zrevrangeWithScores(cacheKey, position, maxPosition)); |
104 |
| - } |
105 |
| - |
106 |
| - // If zrangeWithScores is used, the entries are in ascending order |
107 |
| - if (!isDescending) { |
108 |
| - entries = new ArrayList<>( |
109 |
| - jedis.zrangeWithScores(cacheKey, position, maxPosition)); |
110 |
| - } |
111 |
| - |
112 |
| - List<LeaderboardEntry> newEntries = new ArrayList<>(); |
113 |
| - for (int i = 0; i < entries.size(); i++) { |
114 |
| - Tuple e = entries.get(i); |
115 |
| - |
116 |
| - // Calculate overall position |
117 |
| - long overallPosition = position + i; |
118 |
| - if (!isDescending) { |
119 |
| - overallPosition = jedis.zcard(cacheKey) - overallPosition - 1; |
120 |
| - } |
121 |
| - |
122 |
| - newEntries.add( |
123 |
| - new LeaderboardEntry( |
124 |
| - e.getElement(), e.getScore(), overallPosition |
125 |
| - ) |
126 |
| - ); |
127 |
| - } |
128 |
| - |
129 |
| - return newEntries; |
| 106 | + // If zrangeWithScores is used, the entries are in ascending order |
| 107 | + if (!isDescending) { |
| 108 | + entries = new ArrayList<>( |
| 109 | + jedis.zrangeWithScores(cacheKey, position, maxPosition)); |
130 | 110 | }
|
131 | 111 |
|
132 |
| - private boolean initializeCache() { |
133 |
| - if (this.jedis.zcard(Global.LEADERBOARD_ENTRIES_KEY) > 0) { |
134 |
| - return false; |
135 |
| - } |
| 112 | + List<LeaderboardEntry> newEntries = new ArrayList<>(); |
| 113 | + for (int i = 0; i < entries.size(); i++) { |
| 114 | + Tuple e = entries.get(i); |
| 115 | + |
| 116 | + // Calculate overall position |
| 117 | + long overallPosition = position + i; |
| 118 | + if (!isDescending) { |
| 119 | + overallPosition = jedis.zcard(cacheKey) - overallPosition - 1; |
| 120 | + } |
| 121 | + |
| 122 | + newEntries.add( |
| 123 | + new LeaderboardEntry( |
| 124 | + e.getElement(), e.getScore(), overallPosition |
| 125 | + ) |
| 126 | + ); |
| 127 | + } |
| 128 | + |
| 129 | + return newEntries; |
| 130 | + } |
136 | 131 |
|
137 |
| - List<LeaderboardEntry> entries = this.leaderboardRepository |
138 |
| - .getEntries(); |
| 132 | + private boolean initializeCache() { |
| 133 | + if (this.jedis.zcard(Global.LEADERBOARD_ENTRIES_KEY) > 0) { |
| 134 | + return false; |
| 135 | + } |
139 | 136 |
|
140 |
| - if (!entries.isEmpty()) { |
141 |
| - for (LeaderboardEntry entry : entries) { |
142 |
| - this.jedis.zadd( |
143 |
| - Global.LEADERBOARD_ENTRIES_KEY, |
144 |
| - entry.getScore(), |
145 |
| - entry.getUsername()); |
146 |
| - } |
147 |
| - } |
| 137 | + List<LeaderboardEntry> entries = this.leaderboardRepository |
| 138 | + .getEntries(); |
148 | 139 |
|
149 |
| - return true; |
| 140 | + if (!entries.isEmpty()) { |
| 141 | + for (LeaderboardEntry entry : entries) { |
| 142 | + this.jedis.zadd( |
| 143 | + Global.LEADERBOARD_ENTRIES_KEY, |
| 144 | + entry.getScore(), |
| 145 | + entry.getUsername()); |
| 146 | + } |
150 | 147 | }
|
151 | 148 |
|
152 |
| - /** |
153 |
| - * Creates or updates a leaderboard entry with the given username and score. |
154 |
| - * Only updates the entry if the new score is higher than the current score. |
155 |
| - * |
156 |
| - * @param username The username of the entry |
157 |
| - * @param score The score to set |
158 |
| - */ |
159 |
| - public void createOrUpdate(final String username, final Double score) { |
160 |
| - // See if score is higher than the current score |
161 |
| - Double currentScore = this.jedis.zscore( |
162 |
| - Global.LEADERBOARD_ENTRIES_KEY, username); |
163 |
| - if (currentScore != null && currentScore >= score) { |
164 |
| - return; |
165 |
| - } |
166 |
| - |
167 |
| - this.leaderboardRepository.update(username, score); |
168 |
| - this.jedis.zadd(Global.LEADERBOARD_ENTRIES_KEY, score, username); |
| 149 | + return true; |
| 150 | + } |
| 151 | + |
| 152 | + /** |
| 153 | + * Creates or updates a leaderboard entry with the given username and score. |
| 154 | + * Only updates the entry if the new score is higher than the current score. |
| 155 | + * |
| 156 | + * @param username The username of the entry |
| 157 | + * @param score The score to set |
| 158 | + */ |
| 159 | + public void createOrUpdate(final String username, final Double score) { |
| 160 | + // See if score is higher than the current score |
| 161 | + Double currentScore = this.jedis.zscore( |
| 162 | + Global.LEADERBOARD_ENTRIES_KEY, username); |
| 163 | + if (currentScore != null && currentScore >= score) { |
| 164 | + return; |
169 | 165 | }
|
| 166 | + |
| 167 | + this.leaderboardRepository.update(username, score); |
| 168 | + this.jedis.zadd(Global.LEADERBOARD_ENTRIES_KEY, score, username); |
| 169 | + } |
170 | 170 | }
|
0 commit comments