diff options
| author | Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> | 2023-09-02 16:12:46 +0300 |
|---|---|---|
| committer | Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> | 2023-09-02 16:12:46 +0300 |
| commit | f2bfade4812a9f7b1f97593754c95359ce99e2f1 (patch) | |
| tree | 49dddf4df3a80ccf5961bd2719b72707eb19bcf2 /backend/handlers | |
| parent | fix: remove redundant log (diff) | |
| download | lphub-f2bfade4812a9f7b1f97593754c95359ce99e2f1.tar.gz lphub-f2bfade4812a9f7b1f97593754c95359ce99e2f1.tar.bz2 lphub-f2bfade4812a9f7b1f97593754c95359ce99e2f1.zip | |
fix: optimize rankings
Former-commit-id: 16b247861acea7b5c70a872db7b0397f9dc5c879
Diffstat (limited to 'backend/handlers')
| -rw-r--r-- | backend/handlers/home.go | 149 |
1 files changed, 78 insertions, 71 deletions
diff --git a/backend/handlers/home.go b/backend/handlers/home.go index 6e9a0df..53be1de 100644 --- a/backend/handlers/home.go +++ b/backend/handlers/home.go | |||
| @@ -3,6 +3,7 @@ package handlers | |||
| 3 | import ( | 3 | import ( |
| 4 | "log" | 4 | "log" |
| 5 | "net/http" | 5 | "net/http" |
| 6 | "sort" | ||
| 6 | "strings" | 7 | "strings" |
| 7 | 8 | ||
| 8 | "github.com/gin-gonic/gin" | 9 | "github.com/gin-gonic/gin" |
| @@ -16,8 +17,9 @@ type SearchResponse struct { | |||
| 16 | } | 17 | } |
| 17 | 18 | ||
| 18 | type RankingsResponse struct { | 19 | type RankingsResponse struct { |
| 19 | RankingsSP []models.UserRanking `json:"rankings_sp"` | 20 | Overall []models.UserRanking `json:"rankings_overall"` |
| 20 | RankingsMP []models.UserRanking `json:"rankings_mp"` | 21 | Singleplayer []models.UserRanking `json:"rankings_singleplayer"` |
| 22 | Multiplayer []models.UserRanking `json:"rankings_multiplayer"` | ||
| 21 | } | 23 | } |
| 22 | 24 | ||
| 23 | func Home(c *gin.Context) { | 25 | func Home(c *gin.Context) { |
| @@ -40,96 +42,101 @@ func Home(c *gin.Context) { | |||
| 40 | // @Failure 400 {object} models.Response | 42 | // @Failure 400 {object} models.Response |
| 41 | // @Router /rankings [get] | 43 | // @Router /rankings [get] |
| 42 | func Rankings(c *gin.Context) { | 44 | func Rankings(c *gin.Context) { |
| 43 | rows, err := database.DB.Query(`SELECT steam_id, user_name FROM users`) | 45 | response := RankingsResponse{ |
| 46 | Overall: []models.UserRanking{}, | ||
| 47 | Singleplayer: []models.UserRanking{}, | ||
| 48 | Multiplayer: []models.UserRanking{}, | ||
| 49 | } | ||
| 50 | // Singleplayer rankings | ||
| 51 | sql := `SELECT u.steam_id, u.user_name, COUNT(DISTINCT map_id), | ||
| 52 | (SELECT COUNT(maps.name) FROM maps INNER JOIN games g ON maps.game_id = g.id WHERE g.is_coop = FALSE AND is_disabled = false), | ||
| 53 | (SELECT SUM(min_score_count) AS total_min_score_count FROM ( | ||
| 54 | SELECT | ||
| 55 | user_id, | ||
| 56 | MIN(score_count) AS min_score_count | ||
| 57 | FROM records_sp | ||
| 58 | GROUP BY user_id, map_id | ||
| 59 | ) AS subquery | ||
| 60 | WHERE user_id = u.steam_id) | ||
| 61 | FROM records_sp sp JOIN users u ON u.steam_id = sp.user_id GROUP BY u.steam_id, u.user_name` | ||
| 62 | rows, err := database.DB.Query(sql) | ||
| 44 | if err != nil { | 63 | if err != nil { |
| 45 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | 64 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) |
| 46 | return | 65 | return |
| 47 | } | 66 | } |
| 48 | var spRankings []models.UserRanking | ||
| 49 | var mpRankings []models.UserRanking | ||
| 50 | for rows.Next() { | 67 | for rows.Next() { |
| 51 | var userID, username string | 68 | ranking := models.UserRanking{} |
| 52 | err := rows.Scan(&userID, &username) | 69 | var currentCount int |
| 70 | var totalCount int | ||
| 71 | err = rows.Scan(&ranking.UserID, &ranking.UserName, ¤tCount, &totalCount, &ranking.TotalScore) | ||
| 53 | if err != nil { | 72 | if err != nil { |
| 54 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | 73 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) |
| 55 | return | 74 | return |
| 56 | } | 75 | } |
| 57 | // Getting all sp records for each user | 76 | if currentCount != totalCount { |
| 58 | var uniqueSingleUserRecords, totalSingleMaps int | 77 | continue |
| 59 | sql := `SELECT COUNT(DISTINCT map_id), (SELECT COUNT(map_name) FROM maps | ||
| 60 | WHERE is_coop = FALSE AND is_disabled = false) FROM records_sp WHERE user_id = $1` | ||
| 61 | err = database.DB.QueryRow(sql, userID).Scan(&uniqueSingleUserRecords, &totalSingleMaps) | ||
| 62 | if err != nil { | ||
| 63 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | ||
| 64 | return | ||
| 65 | } | 78 | } |
| 66 | // Has all singleplayer records | 79 | response.Singleplayer = append(response.Singleplayer, ranking) |
| 67 | if uniqueSingleUserRecords == totalSingleMaps { | 80 | } |
| 68 | var ranking models.UserRanking | 81 | // Multiplayer rankings |
| 69 | ranking.UserID = userID | 82 | sql = `SELECT u.steam_id, u.user_name, COUNT(DISTINCT map_id), |
| 70 | ranking.UserName = username | 83 | (SELECT COUNT(maps.name) FROM maps INNER JOIN games g ON maps.game_id = g.id WHERE g.is_coop = FALSE AND is_disabled = false), |
| 71 | sql := `SELECT DISTINCT map_id, score_count FROM records_sp WHERE user_id = $1 ORDER BY map_id, score_count` | 84 | (SELECT SUM(min_score_count) AS total_min_score_count FROM ( |
| 72 | rows, err := database.DB.Query(sql, userID) | 85 | SELECT |
| 73 | if err != nil { | 86 | host_id, |
| 74 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | 87 | partner_id, |
| 75 | return | 88 | MIN(score_count) AS min_score_count |
| 76 | } | 89 | FROM records_mp |
| 77 | totalScore := 0 | 90 | GROUP BY host_id, partner_id, map_id |
| 78 | var maps []int | 91 | ) AS subquery |
| 79 | for rows.Next() { | 92 | WHERE host_id = u.steam_id OR partner_id = u.steam_id) |
| 80 | var mapID, scoreCount int | 93 | FROM records_mp mp JOIN users u ON u.steam_id = mp.host_id OR u.steam_id = mp.partner_id GROUP BY u.steam_id, u.user_name` |
| 81 | rows.Scan(&mapID, &scoreCount) | 94 | rows, err = database.DB.Query(sql) |
| 82 | if len(maps) != 0 && maps[len(maps)-1] == mapID { | 95 | if err != nil { |
| 83 | continue | 96 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) |
| 84 | } | 97 | return |
| 85 | totalScore += scoreCount | 98 | } |
| 86 | maps = append(maps, mapID) | 99 | for rows.Next() { |
| 87 | } | 100 | ranking := models.UserRanking{} |
| 88 | ranking.TotalScore = totalScore | 101 | var currentCount int |
| 89 | spRankings = append(spRankings, ranking) | 102 | var totalCount int |
| 90 | } | 103 | err = rows.Scan(&ranking.UserID, &ranking.UserName, ¤tCount, &totalCount, &ranking.TotalScore) |
| 91 | // Getting all mp records for each user | ||
| 92 | var uniqueMultiUserRecords, totalMultiMaps int | ||
| 93 | sql = `SELECT COUNT(DISTINCT map_id), (SELECT COUNT(map_name) FROM maps | ||
| 94 | WHERE is_coop = TRUE AND is_disabled = false) FROM records_mp WHERE host_id = $1 OR partner_id = $2` | ||
| 95 | err = database.DB.QueryRow(sql, userID, userID).Scan(&uniqueMultiUserRecords, &totalMultiMaps) | ||
| 96 | if err != nil { | 104 | if err != nil { |
| 97 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | 105 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) |
| 98 | return | 106 | return |
| 99 | } | 107 | } |
| 100 | // Has all singleplayer records | 108 | if currentCount != totalCount { |
| 101 | if uniqueMultiUserRecords == totalMultiMaps { | 109 | continue |
| 102 | var ranking models.UserRanking | 110 | } |
| 103 | ranking.UserID = userID | 111 | response.Multiplayer = append(response.Multiplayer, ranking) |
| 104 | ranking.UserName = username | 112 | } |
| 105 | sql := `SELECT DISTINCT map_id, score_count FROM records_mp WHERE host_id = $1 OR partner_id = $2 ORDER BY map_id, score_count` | 113 | // Has both so they are qualified for overall ranking |
| 106 | rows, err := database.DB.Query(sql, userID, userID) | 114 | for _, spRanking := range response.Singleplayer { |
| 107 | if err != nil { | 115 | for _, mpRanking := range response.Multiplayer { |
| 108 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | 116 | if spRanking.UserID == mpRanking.UserID { |
| 109 | return | 117 | totalScore := spRanking.TotalScore + mpRanking.TotalScore |
| 110 | } | 118 | overallRanking := models.UserRanking{ |
| 111 | totalScore := 0 | 119 | UserID: spRanking.UserID, |
| 112 | var maps []int | 120 | UserName: spRanking.UserName, |
| 113 | for rows.Next() { | 121 | TotalScore: totalScore, |
| 114 | var mapID, scoreCount int | ||
| 115 | rows.Scan(&mapID, &scoreCount) | ||
| 116 | if len(maps) != 0 && maps[len(maps)-1] == mapID { | ||
| 117 | continue | ||
| 118 | } | 122 | } |
| 119 | totalScore += scoreCount | 123 | response.Overall = append(response.Overall, overallRanking) |
| 120 | maps = append(maps, mapID) | ||
| 121 | } | 124 | } |
| 122 | ranking.TotalScore = totalScore | ||
| 123 | mpRankings = append(mpRankings, ranking) | ||
| 124 | } | 125 | } |
| 125 | } | 126 | } |
| 127 | sort.Slice(response.Singleplayer, func(i, j int) bool { | ||
| 128 | return response.Singleplayer[i].TotalScore < response.Singleplayer[j].TotalScore | ||
| 129 | }) | ||
| 130 | sort.Slice(response.Multiplayer, func(i, j int) bool { | ||
| 131 | return response.Multiplayer[i].TotalScore < response.Multiplayer[j].TotalScore | ||
| 132 | }) | ||
| 133 | sort.Slice(response.Overall, func(i, j int) bool { | ||
| 134 | return response.Overall[i].TotalScore < response.Overall[j].TotalScore | ||
| 135 | }) | ||
| 126 | c.JSON(http.StatusOK, models.Response{ | 136 | c.JSON(http.StatusOK, models.Response{ |
| 127 | Success: true, | 137 | Success: true, |
| 128 | Message: "Successfully retrieved rankings.", | 138 | Message: "Successfully retrieved rankings.", |
| 129 | Data: RankingsResponse{ | 139 | Data: response, |
| 130 | RankingsSP: spRankings, | ||
| 131 | RankingsMP: mpRankings, | ||
| 132 | }, | ||
| 133 | }) | 140 | }) |
| 134 | } | 141 | } |
| 135 | 142 | ||