From a2bac9d8a6b828ff37716d8a4d49f7fad70c6c25 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 19:09:22 +0300 Subject: [PATCH 01/58] :art: refactor: move matches route handler to separate module --- routes/handlers/matches.js | 17 +++++++++++++++++ routes/spec.js | 14 ++------------ 2 files changed, 19 insertions(+), 12 deletions(-) create mode 100644 routes/handlers/matches.js diff --git a/routes/handlers/matches.js b/routes/handlers/matches.js new file mode 100644 index 000000000..3a8f6d164 --- /dev/null +++ b/routes/handlers/matches.js @@ -0,0 +1,17 @@ +const buildMatch = require("../../store/buildMatch"); + +async function getMatchById(req, res, cb) { + try { + const match = await buildMatch(req.params.match_id, req.query); + if (!match) { + return cb(); + } + return res.json(match); + } catch (err) { + return cb(err); + } +} + +module.exports = { + getMatchById, +}; diff --git a/routes/spec.js b/routes/spec.js index a87adff40..b198337d6 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -9,7 +9,6 @@ const queue = require("../store/queue"); const queries = require("../store/queries"); const search = require("../store/search"); const searchES = require("../store/searchES"); -const buildMatch = require("../store/buildMatch"); const buildStatus = require("../store/buildStatus"); const playerFields = require("./playerFields.json"); // const getGcData = require("../util/getGcData"); @@ -21,6 +20,7 @@ const cacheFunctions = require("../store/cacheFunctions"); const params = require("./requests/importParams"); const responses = require("./responses/schemas/importResponseSchemas"); const generateOperationId = require("./generateOperationId"); +const matchesHandler = require('./handlers/matches') const { redisCount, countPeers, isContributor, matchupToString } = utility; const { subkeys, countCats } = playerFields; @@ -118,17 +118,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/matches/:match_id/:info?", - func: async (req, res, cb) => { - try { - const match = await buildMatch(req.params.match_id, req.query); - if (!match) { - return cb(); - } - return res.json(match); - } catch (err) { - return cb(err); - } - }, + func: matchesHandler.getMatchById, }, }, "/playersByRank": { From 3fb904fef75d9652700ad85af4de50d7eb4ab670 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 19:15:50 +0300 Subject: [PATCH 02/58] :art: refactor: move playersByRank route handler to separate module --- routes/handlers/players.js | 24 ++++++++++++++++++++++++ routes/spec.js | 20 ++------------------ 2 files changed, 26 insertions(+), 18 deletions(-) create mode 100644 routes/handlers/players.js diff --git a/routes/handlers/players.js b/routes/handlers/players.js new file mode 100644 index 000000000..7754fea19 --- /dev/null +++ b/routes/handlers/players.js @@ -0,0 +1,24 @@ +const db = require("../../store/db"); + +async function getPlayersByRank(req, res, cb) { + try { + const result = await db.raw( + ` + SELECT account_id, rating, fh_unavailable + FROM players + JOIN rank_tier + USING (account_id) + ORDER BY rating DESC + LIMIT 100 + `, + [] + ); + return res.json(result.rows); + } catch (err) { + return cb(err); + } +} + +module.exports = { + getPlayersByRank, +}; diff --git a/routes/spec.js b/routes/spec.js index b198337d6..7ff441c86 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -21,6 +21,7 @@ const params = require("./requests/importParams"); const responses = require("./responses/schemas/importResponseSchemas"); const generateOperationId = require("./generateOperationId"); const matchesHandler = require('./handlers/matches') +const playersHandler = require('./handlers/players'); const { redisCount, countPeers, isContributor, matchupToString } = utility; const { subkeys, countCats } = playerFields; @@ -141,24 +142,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/playersByRank", - func: (req, res, cb) => { - db.raw( - ` - SELECT account_id, rating, fh_unavailable - FROM players - JOIN rank_tier - USING (account_id) - ORDER BY rating DESC - LIMIT 100 - `, - [] - ).asCallback((err, result) => { - if (err) { - return cb(err); - } - return res.json(result.rows); - }); - }, + func: playersHandler.getPlayersByRank, }, }, "/players/{account_id}": { From 83c56b98eab850a5c91317c7b6070591e14e0644 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 19:24:27 +0300 Subject: [PATCH 03/58] :art: refactor: move getPlayersByAccountId route handler to players module --- routes/handlers/players.js | 64 ++++++++++++++++++++++++++++++++++++ routes/spec.js | 67 +++----------------------------------- 2 files changed, 68 insertions(+), 63 deletions(-) diff --git a/routes/handlers/players.js b/routes/handlers/players.js index 7754fea19..7451f38d0 100644 --- a/routes/handlers/players.js +++ b/routes/handlers/players.js @@ -1,4 +1,7 @@ +const async = require("async"); const db = require("../../store/db"); +const queries = require("../../store/queries"); +const utility = require("../../util/utility"); async function getPlayersByRank(req, res, cb) { try { @@ -19,6 +22,67 @@ async function getPlayersByRank(req, res, cb) { } } +async function getPlayersByAccountId(req, res, cb) { + const accountId = Number(req.params.account_id); + async.parallel( + { + profile(cb) { + queries.getPlayer(db, accountId, (err, playerData) => { + if (playerData !== null && playerData !== undefined) { + playerData.is_contributor = utility.isContributor(accountId); + playerData.is_subscriber = Boolean(playerData?.status); + } + cb(err, playerData); + }); + }, + solo_competitive_rank(cb) { + db.first() + .from("solo_competitive_rank") + .where({ account_id: accountId }) + .asCallback((err, row) => { + cb(err, row ? row.rating : null); + }); + }, + competitive_rank(cb) { + db.first() + .from("competitive_rank") + .where({ account_id: accountId }) + .asCallback((err, row) => { + cb(err, row ? row.rating : null); + }); + }, + rank_tier(cb) { + db.first() + .from("rank_tier") + .where({ account_id: accountId }) + .asCallback((err, row) => { + cb(err, row ? row.rating : null); + }); + }, + leaderboard_rank(cb) { + db.first() + .from("leaderboard_rank") + .where({ account_id: accountId }) + .asCallback((err, row) => { + cb(err, row ? row.rating : null); + }); + }, + mmr_estimate(cb) { + queries.getMmrEstimate(accountId, (err, est) => + cb(err, est || {}) + ); + }, + }, + (err, result) => { + if (err) { + return cb(err); + } + return res.json(result); + } + ); +} + module.exports = { getPlayersByRank, + getPlayersByAccountId, }; diff --git a/routes/spec.js b/routes/spec.js index 7ff441c86..9d1e1c491 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -1,4 +1,3 @@ -const async = require("async"); const constants = require("dotaconstants"); const moment = require("moment"); const { Client } = require("pg"); @@ -20,10 +19,10 @@ const cacheFunctions = require("../store/cacheFunctions"); const params = require("./requests/importParams"); const responses = require("./responses/schemas/importResponseSchemas"); const generateOperationId = require("./generateOperationId"); -const matchesHandler = require('./handlers/matches') -const playersHandler = require('./handlers/players'); +const matchesHandler = require("./handlers/matches") +const playersHandler = require("./handlers/players"); -const { redisCount, countPeers, isContributor, matchupToString } = utility; +const { redisCount, countPeers, matchupToString } = utility; const { subkeys, countCats } = playerFields; const parameters = Object.values(params).reduce( @@ -165,65 +164,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/players/:account_id", - func: (req, res, cb) => { - const accountId = Number(req.params.account_id); - async.parallel( - { - profile(cb) { - queries.getPlayer(db, accountId, (err, playerData) => { - if (playerData !== null && playerData !== undefined) { - playerData.is_contributor = isContributor(accountId); - playerData.is_subscriber = Boolean(playerData?.status); - } - cb(err, playerData); - }); - }, - solo_competitive_rank(cb) { - db.first() - .from("solo_competitive_rank") - .where({ account_id: accountId }) - .asCallback((err, row) => { - cb(err, row ? row.rating : null); - }); - }, - competitive_rank(cb) { - db.first() - .from("competitive_rank") - .where({ account_id: accountId }) - .asCallback((err, row) => { - cb(err, row ? row.rating : null); - }); - }, - rank_tier(cb) { - db.first() - .from("rank_tier") - .where({ account_id: accountId }) - .asCallback((err, row) => { - cb(err, row ? row.rating : null); - }); - }, - leaderboard_rank(cb) { - db.first() - .from("leaderboard_rank") - .where({ account_id: accountId }) - .asCallback((err, row) => { - cb(err, row ? row.rating : null); - }); - }, - mmr_estimate(cb) { - queries.getMmrEstimate(accountId, (err, est) => - cb(err, est || {}) - ); - }, - }, - (err, result) => { - if (err) { - return cb(err); - } - return res.json(result); - } - ); - }, + func: playersHandler.getPlayersByAccountId, }, }, "/players/{account_id}/wl": { From da1ede6c2f1a37405877c630b76073bbfd83fbbf Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 19:57:20 +0300 Subject: [PATCH 04/58] :art: refactor: move getPlayersByAccountIdWl route handler to players module --- routes/handlers/players.js | 30 ++++++++++++++++++++++++++++++ routes/spec.js | 28 +--------------------------- 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/routes/handlers/players.js b/routes/handlers/players.js index 7451f38d0..6a46e097f 100644 --- a/routes/handlers/players.js +++ b/routes/handlers/players.js @@ -1,4 +1,5 @@ const async = require("async"); +const cacheFunctions = require("../../store/cacheFunctions"); const db = require("../../store/db"); const queries = require("../../store/queries"); const utility = require("../../util/utility"); @@ -82,7 +83,36 @@ async function getPlayersByAccountId(req, res, cb) { ); } +async function getPlayersByAccountIdWl(req, res, cb) { + const result = { + win: 0, + lose: 0, + }; + req.queryObj.project = req.queryObj.project.concat( + "player_slot", + "radiant_win" + ); + queries.getPlayerMatches( + req.params.account_id, + req.queryObj, + (err, cache) => { + if (err) { + return cb(err); + } + cache.forEach((m) => { + if (utility.isRadiant(m) === m.radiant_win) { + result.win += 1; + } else { + result.lose += 1; + } + }); + return cacheFunctions.sendDataWithCache(req, res, result, "wl"); + } + ); +} + module.exports = { getPlayersByRank, getPlayersByAccountId, + getPlayersByAccountIdWl, }; diff --git a/routes/spec.js b/routes/spec.js index 9d1e1c491..f05fcb2f3 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -187,33 +187,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/players/:account_id/wl", - func: (req, res, cb) => { - const result = { - win: 0, - lose: 0, - }; - req.queryObj.project = req.queryObj.project.concat( - "player_slot", - "radiant_win" - ); - queries.getPlayerMatches( - req.params.account_id, - req.queryObj, - (err, cache) => { - if (err) { - return cb(err); - } - cache.forEach((m) => { - if (utility.isRadiant(m) === m.radiant_win) { - result.win += 1; - } else { - result.lose += 1; - } - }); - return cacheFunctions.sendDataWithCache(req, res, result, "wl"); - } - ); - }, + func: playersHandler.getPlayersByAccountIdWl, }, }, "/players/{account_id}/recentMatches": { From e5bda04f0bfe53e6512c8e533c46e15973bff9a2 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 20:22:07 +0300 Subject: [PATCH 05/58] :art: refactor: move getPlayersByAccountIdRecentMatches route handler to players module --- routes/handlers/players.js | 42 ++++++++++++++++++++++++++++++++++++++ routes/spec.js | 42 +------------------------------------- 2 files changed, 43 insertions(+), 41 deletions(-) diff --git a/routes/handlers/players.js b/routes/handlers/players.js index 6a46e097f..0a70ecdc1 100644 --- a/routes/handlers/players.js +++ b/routes/handlers/players.js @@ -111,6 +111,48 @@ async function getPlayersByAccountIdWl(req, res, cb) { ); } +async function getPlayersByAccountIdRecentMatches(req, res, cb) { + queries.getPlayerMatches( + req.params.account_id, + { + project: req.queryObj.project.concat([ + "hero_id", + "start_time", + "duration", + "player_slot", + "radiant_win", + "game_mode", + "lobby_type", + "version", + "kills", + "deaths", + "assists", + "skill", + "average_rank", + "xp_per_min", + "gold_per_min", + "hero_damage", + "tower_damage", + "hero_healing", + "last_hits", + "lane", + "lane_role", + "is_roaming", + "cluster", + "leaver_status", + "party_size", + ]), + dbLimit: 20, + }, + (err, cache) => { + if (err) { + return cb(err); + } + return res.json(cache.filter((match) => match.duration)); + } + ); +} + module.exports = { getPlayersByRank, getPlayersByAccountId, diff --git a/routes/spec.js b/routes/spec.js index f05fcb2f3..f60f4fdd7 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -219,47 +219,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/players/:account_id/recentMatches", - func: (req, res, cb) => { - queries.getPlayerMatches( - req.params.account_id, - { - project: req.queryObj.project.concat([ - "hero_id", - "start_time", - "duration", - "player_slot", - "radiant_win", - "game_mode", - "lobby_type", - "version", - "kills", - "deaths", - "assists", - "skill", - "average_rank", - "xp_per_min", - "gold_per_min", - "hero_damage", - "tower_damage", - "hero_healing", - "last_hits", - "lane", - "lane_role", - "is_roaming", - "cluster", - "leaver_status", - "party_size", - ]), - dbLimit: 20, - }, - (err, cache) => { - if (err) { - return cb(err); - } - return res.json(cache.filter((match) => match.duration)); - } - ); - }, + func: playersHandler.getPlayersByAccountIdRecentMatches, }, }, "/players/{account_id}/matches": { From 427f04e7e494b9c320f473f6a0f09db255d4f874 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 20:22:43 +0300 Subject: [PATCH 06/58] :art: refactor: move getPlayersByAccountIdMatches route handler to players module --- routes/handlers/players.js | 32 ++++++++++++++++++++++++++++++++ routes/spec.js | 32 +------------------------------- 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/routes/handlers/players.js b/routes/handlers/players.js index 0a70ecdc1..664abe990 100644 --- a/routes/handlers/players.js +++ b/routes/handlers/players.js @@ -153,6 +153,38 @@ async function getPlayersByAccountIdRecentMatches(req, res, cb) { ); } +async function getPlayersByAccountIdMatches(req, res, cb) { + // Use passed fields as additional fields, if available + const additionalFields = req.query.project || [ + "hero_id", + "start_time", + "duration", + "player_slot", + "radiant_win", + "game_mode", + "lobby_type", + "version", + "kills", + "deaths", + "assists", + "skill", + "average_rank", + "leaver_status", + "party_size", + ]; + req.queryObj.project = req.queryObj.project.concat(additionalFields); + queries.getPlayerMatches( + req.params.account_id, + req.queryObj, + (err, cache) => { + if (err) { + return cb(err); + } + return res.json(cache); + } + ); +} + module.exports = { getPlayersByRank, getPlayersByAccountId, diff --git a/routes/spec.js b/routes/spec.js index f60f4fdd7..0cebf8dfd 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -253,37 +253,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/players/:account_id/matches", - func: (req, res, cb) => { - // Use passed fields as additional fields, if available - const additionalFields = req.query.project || [ - "hero_id", - "start_time", - "duration", - "player_slot", - "radiant_win", - "game_mode", - "lobby_type", - "version", - "kills", - "deaths", - "assists", - "skill", - "average_rank", - "leaver_status", - "party_size", - ]; - req.queryObj.project = req.queryObj.project.concat(additionalFields); - queries.getPlayerMatches( - req.params.account_id, - req.queryObj, - (err, cache) => { - if (err) { - return cb(err); - } - return res.json(cache); - } - ); - }, + func: playersHandler.getPlayersByAccountIdMatches, }, }, "/players/{account_id}/heroes": { From a7064d0282f61a98143d1d606619b4cd526becb0 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 20:24:09 +0300 Subject: [PATCH 07/58] :art: refactor: move getPlayersByAccountIdHeroes route handler to players module --- routes/handlers/players.js | 76 ++++++++++++++++++++++++++++++++++++++ routes/spec.js | 76 +------------------------------------- 2 files changed, 77 insertions(+), 75 deletions(-) diff --git a/routes/handlers/players.js b/routes/handlers/players.js index 664abe990..87811b064 100644 --- a/routes/handlers/players.js +++ b/routes/handlers/players.js @@ -185,6 +185,82 @@ async function getPlayersByAccountIdMatches(req, res, cb) { ); } +async function getPlayersByAccountIdHeroes(req, res, cb) { + const heroes = {}; + // prefill heroes with every hero + Object.keys(constants.heroes).forEach((heroId) => { + hero_id_int = parseInt(heroId); + const hero = { + hero_id: hero_id_int, + last_played: 0, + games: 0, + win: 0, + with_games: 0, + with_win: 0, + against_games: 0, + against_win: 0, + }; + heroes[hero_id_int] = hero; + }); + req.queryObj.project = req.queryObj.project.concat( + "heroes", + "account_id", + "start_time", + "player_slot", + "radiant_win" + ); + queries.getPlayerMatches( + req.params.account_id, + req.queryObj, + (err, cache) => { + if (err) { + return cb(err); + } + cache.forEach((m) => { + const { isRadiant } = utility; + const playerWin = isRadiant(m) === m.radiant_win; + const group = m.heroes || {}; + Object.keys(group).forEach((key) => { + const tm = group[key]; + const tmHero = tm.hero_id; + // don't count invalid heroes + if (tmHero in heroes) { + if (isRadiant(tm) === isRadiant(m)) { + if (tm.account_id === m.account_id) { + heroes[tmHero].games += 1; + heroes[tmHero].win += playerWin ? 1 : 0; + if (m.start_time > heroes[tmHero].last_played) { + heroes[tmHero].last_played = m.start_time; + } + } else { + heroes[tmHero].with_games += 1; + heroes[tmHero].with_win += playerWin ? 1 : 0; + } + } else { + heroes[tmHero].against_games += 1; + heroes[tmHero].against_win += playerWin ? 1 : 0; + } + } + }); + }); + const result = Object.keys(heroes) + .map((k) => heroes[k]) + .filter( + (hero) => + !req.queryObj.having || + hero.games >= Number(req.queryObj.having) + ) + .sort((a, b) => b.games - a.games); + return cacheFunctions.sendDataWithCache( + req, + res, + result, + "heroes" + ); + } + ); +} + module.exports = { getPlayersByRank, getPlayersByAccountId, diff --git a/routes/spec.js b/routes/spec.js index 0cebf8dfd..2d0192c40 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -279,81 +279,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/players/:account_id/heroes", - func: (req, res, cb) => { - const heroes = {}; - // prefill heroes with every hero - Object.keys(constants.heroes).forEach((heroId) => { - hero_id_int = parseInt(heroId); - const hero = { - hero_id: hero_id_int, - last_played: 0, - games: 0, - win: 0, - with_games: 0, - with_win: 0, - against_games: 0, - against_win: 0, - }; - heroes[hero_id_int] = hero; - }); - req.queryObj.project = req.queryObj.project.concat( - "heroes", - "account_id", - "start_time", - "player_slot", - "radiant_win" - ); - queries.getPlayerMatches( - req.params.account_id, - req.queryObj, - (err, cache) => { - if (err) { - return cb(err); - } - cache.forEach((m) => { - const { isRadiant } = utility; - const playerWin = isRadiant(m) === m.radiant_win; - const group = m.heroes || {}; - Object.keys(group).forEach((key) => { - const tm = group[key]; - const tmHero = tm.hero_id; - // don't count invalid heroes - if (tmHero in heroes) { - if (isRadiant(tm) === isRadiant(m)) { - if (tm.account_id === m.account_id) { - heroes[tmHero].games += 1; - heroes[tmHero].win += playerWin ? 1 : 0; - if (m.start_time > heroes[tmHero].last_played) { - heroes[tmHero].last_played = m.start_time; - } - } else { - heroes[tmHero].with_games += 1; - heroes[tmHero].with_win += playerWin ? 1 : 0; - } - } else { - heroes[tmHero].against_games += 1; - heroes[tmHero].against_win += playerWin ? 1 : 0; - } - } - }); - }); - const result = Object.keys(heroes) - .map((k) => heroes[k]) - .filter( - (hero) => - !req.queryObj.having || - hero.games >= Number(req.queryObj.having) - ) - .sort((a, b) => b.games - a.games); - return cacheFunctions.sendDataWithCache( - req, - res, - result, - "heroes" - ); - } - ); - }, + func: playersHandler.getPlayersByAccountIdHeroes, }, }, "/players/{account_id}/peers": { From 733473161236a1e0e65bf4c0f484db587d4ca373 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 20:24:34 +0300 Subject: [PATCH 08/58] :art: refactor: move getPlayersByAccountIdPeers route handler to players module --- routes/handlers/players.js | 39 ++++++++++++++++++++++++++++++++++++++ routes/spec.js | 39 +------------------------------------- 2 files changed, 40 insertions(+), 38 deletions(-) diff --git a/routes/handlers/players.js b/routes/handlers/players.js index 87811b064..0d526a605 100644 --- a/routes/handlers/players.js +++ b/routes/handlers/players.js @@ -261,6 +261,45 @@ async function getPlayersByAccountIdHeroes(req, res, cb) { ); } +async function getPlayersByAccountIdPeers(req, res, cb) { + req.queryObj.project = req.queryObj.project.concat( + "heroes", + "start_time", + "player_slot", + "radiant_win", + "gold_per_min", + "xp_per_min" + ); + queries.getPlayerMatches( + req.params.account_id, + req.queryObj, + (err, cache) => { + if (err) { + return cb(err); + } + const teammates = utility.countPeers(cache); + return queries.getPeers( + db, + teammates, + { + account_id: req.params.account_id, + }, + (err, result) => { + if (err) { + return cb(err); + } + return cacheFunctions.sendDataWithCache( + req, + res, + result, + "peers" + ); + } + ); + } + ); +} + module.exports = { getPlayersByRank, getPlayersByAccountId, diff --git a/routes/spec.js b/routes/spec.js index 2d0192c40..4188e199f 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -305,44 +305,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/players/:account_id/peers", - func: (req, res, cb) => { - req.queryObj.project = req.queryObj.project.concat( - "heroes", - "start_time", - "player_slot", - "radiant_win", - "gold_per_min", - "xp_per_min" - ); - queries.getPlayerMatches( - req.params.account_id, - req.queryObj, - (err, cache) => { - if (err) { - return cb(err); - } - const teammates = countPeers(cache); - return queries.getPeers( - db, - teammates, - { - account_id: req.params.account_id, - }, - (err, result) => { - if (err) { - return cb(err); - } - return cacheFunctions.sendDataWithCache( - req, - res, - result, - "peers" - ); - } - ); - } - ); - }, + func: playersHandler.getPlayersByAccountIdPeers, }, }, "/players/{account_id}/pros": { From 2632f2d8f9651cd393f04eab21cdf78636ab2009 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 20:24:49 +0300 Subject: [PATCH 09/58] :art: refactor: move getPlayersByAccountIdPros route handler to players module --- routes/handlers/players.js | 32 ++++++++++++++++++++++++++++++++ routes/spec.js | 32 +------------------------------- 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/routes/handlers/players.js b/routes/handlers/players.js index 0d526a605..b42599f4f 100644 --- a/routes/handlers/players.js +++ b/routes/handlers/players.js @@ -300,6 +300,38 @@ async function getPlayersByAccountIdPeers(req, res, cb) { ); } +async function getPlayersByAccountIdPros(req, res, cb) { + req.queryObj.project = req.queryObj.project.concat( + "heroes", + "start_time", + "player_slot", + "radiant_win" + ); + queries.getPlayerMatches( + req.params.account_id, + req.queryObj, + (err, cache) => { + if (err) { + return cb(err); + } + const teammates = countPeers(cache); + return queries.getProPeers( + db, + teammates, + { + account_id: req.params.account_id, + }, + (err, result) => { + if (err) { + return cb(err); + } + return res.json(result); + } + ); + } + ); +} + module.exports = { getPlayersByRank, getPlayersByAccountId, diff --git a/routes/spec.js b/routes/spec.js index 4188e199f..2e716accb 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -331,37 +331,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/players/:account_id/pros", - func: (req, res, cb) => { - req.queryObj.project = req.queryObj.project.concat( - "heroes", - "start_time", - "player_slot", - "radiant_win" - ); - queries.getPlayerMatches( - req.params.account_id, - req.queryObj, - (err, cache) => { - if (err) { - return cb(err); - } - const teammates = countPeers(cache); - return queries.getProPeers( - db, - teammates, - { - account_id: req.params.account_id, - }, - (err, result) => { - if (err) { - return cb(err); - } - return res.json(result); - } - ); - } - ); - }, + func: playersHandler.getPlayersByAccountIdPros, }, }, "/players/{account_id}/totals": { From 31bc13b3d9451932a397f55ceca9ae58e5fa8390 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 20:25:03 +0300 Subject: [PATCH 10/58] :art: refactor: move getPlayersByAccountIdTotals route handler to players module --- routes/handlers/players.js | 32 ++++++++++++++++++++++++++++++++ routes/spec.js | 32 +------------------------------- 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/routes/handlers/players.js b/routes/handlers/players.js index b42599f4f..23fea8c58 100644 --- a/routes/handlers/players.js +++ b/routes/handlers/players.js @@ -332,6 +332,38 @@ async function getPlayersByAccountIdPros(req, res, cb) { ); } +async function getPlayersByAccountIdTotals(req, res, cb) { + const result = {}; + Object.keys(subkeys).forEach((key) => { + result[key] = { + field: key, + n: 0, + sum: 0, + }; + }); + req.queryObj.project = req.queryObj.project.concat( + Object.keys(subkeys) + ); + queries.getPlayerMatches( + req.params.account_id, + req.queryObj, + (err, cache) => { + if (err) { + return cb(err); + } + cache.forEach((m) => { + Object.keys(subkeys).forEach((key) => { + if (m[key] !== null && m[key] !== undefined) { + result[key].n += 1; + result[key].sum += Number(m[key]); + } + }); + }); + return res.json(Object.keys(result).map((key) => result[key])); + } + ); +} + module.exports = { getPlayersByRank, getPlayersByAccountId, diff --git a/routes/spec.js b/routes/spec.js index 2e716accb..a234b882c 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -357,37 +357,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/players/:account_id/totals", - func: (req, res, cb) => { - const result = {}; - Object.keys(subkeys).forEach((key) => { - result[key] = { - field: key, - n: 0, - sum: 0, - }; - }); - req.queryObj.project = req.queryObj.project.concat( - Object.keys(subkeys) - ); - queries.getPlayerMatches( - req.params.account_id, - req.queryObj, - (err, cache) => { - if (err) { - return cb(err); - } - cache.forEach((m) => { - Object.keys(subkeys).forEach((key) => { - if (m[key] !== null && m[key] !== undefined) { - result[key].n += 1; - result[key].sum += Number(m[key]); - } - }); - }); - return res.json(Object.keys(result).map((key) => result[key])); - } - ); - }, + func: playersHandler.getPlayersByAccountIdTotals, }, }, "/players/{account_id}/counts": { From f1d72744242ba4452a0c1fb283e01340f76ff8ca Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 20:25:25 +0300 Subject: [PATCH 11/58] :art: refactor: move getPlayersByAccountIdCounts route handler to players module --- routes/handlers/players.js | 34 ++++++++++++++++++++++++++++++++++ routes/spec.js | 34 +--------------------------------- 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/routes/handlers/players.js b/routes/handlers/players.js index 23fea8c58..4f8d27a0e 100644 --- a/routes/handlers/players.js +++ b/routes/handlers/players.js @@ -364,6 +364,40 @@ async function getPlayersByAccountIdTotals(req, res, cb) { ); } +async function getPlayersByAccountIdCounts(req, res, cb) { + const result = {}; + Object.keys(countCats).forEach((key) => { + result[key] = {}; + }); + req.queryObj.project = req.queryObj.project.concat( + Object.keys(countCats) + ); + queries.getPlayerMatches( + req.params.account_id, + req.queryObj, + (err, cache) => { + if (err) { + return cb(err); + } + cache.forEach((m) => { + m.is_radiant = utility.isRadiant(m); + Object.keys(countCats).forEach((key) => { + if (!result[key][Math.floor(m[key])]) { + result[key][Math.floor(m[key])] = { + games: 0, + win: 0, + }; + } + result[key][Math.floor(m[key])].games += 1; + const won = Number(m.radiant_win === utility.isRadiant(m)); + result[key][Math.floor(m[key])].win += won; + }); + }); + return res.json(result); + } + ); +} + module.exports = { getPlayersByRank, getPlayersByAccountId, diff --git a/routes/spec.js b/routes/spec.js index a234b882c..6b34e9ce4 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -380,39 +380,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/players/:account_id/counts", - func: (req, res, cb) => { - const result = {}; - Object.keys(countCats).forEach((key) => { - result[key] = {}; - }); - req.queryObj.project = req.queryObj.project.concat( - Object.keys(countCats) - ); - queries.getPlayerMatches( - req.params.account_id, - req.queryObj, - (err, cache) => { - if (err) { - return cb(err); - } - cache.forEach((m) => { - m.is_radiant = utility.isRadiant(m); - Object.keys(countCats).forEach((key) => { - if (!result[key][Math.floor(m[key])]) { - result[key][Math.floor(m[key])] = { - games: 0, - win: 0, - }; - } - result[key][Math.floor(m[key])].games += 1; - const won = Number(m.radiant_win === utility.isRadiant(m)); - result[key][Math.floor(m[key])].win += won; - }); - }); - return res.json(result); - } - ); - }, + func: playersHandler.getPlayersByAccountIdCounts, }, }, "/players/{account_id}/histograms/{field}": { From df7f01d7afe08133f0d4fa276d29a0dd05b0833c Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 20:25:38 +0300 Subject: [PATCH 12/58] :art: refactor: move getPlayersByAccountIdHistogramsByField route handler to players module --- routes/handlers/players.js | 42 ++++++++++++++++++++++++++++++++++++++ routes/spec.js | 42 +------------------------------------- 2 files changed, 43 insertions(+), 41 deletions(-) diff --git a/routes/handlers/players.js b/routes/handlers/players.js index 4f8d27a0e..dce20e0d8 100644 --- a/routes/handlers/players.js +++ b/routes/handlers/players.js @@ -398,6 +398,48 @@ async function getPlayersByAccountIdCounts(req, res, cb) { ); } +async function getPlayersByAccountIdHistogramsByField(req, res, cb) { + const { field } = req.params; + req.queryObj.project = req.queryObj.project + .concat("radiant_win", "player_slot") + .concat([field].filter((f) => subkeys[f])); + queries.getPlayerMatches( + req.params.account_id, + req.queryObj, + (err, cache) => { + if (err) { + return cb(err); + } + const buckets = 40; + // Find the maximum value to determine how large each bucket should be + const max = Math.max(...cache.map((m) => m[field])); + // Round the bucket size up to the nearest integer + const bucketSize = Math.ceil((max + 1) / buckets); + const bucketArray = Array.from( + { + length: buckets, + }, + (value, index) => ({ + x: bucketSize * index, + games: 0, + win: 0, + }) + ); + cache.forEach((m) => { + if (m[field] || m[field] === 0) { + const index = Math.floor(m[field] / bucketSize); + if (bucketArray[index]) { + bucketArray[index].games += 1; + bucketArray[index].win += + utility.isRadiant(m) === m.radiant_win ? 1 : 0; + } + } + }); + return res.json(bucketArray); + } + ); +} + module.exports = { getPlayersByRank, getPlayersByAccountId, diff --git a/routes/spec.js b/routes/spec.js index 6b34e9ce4..185151196 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -415,47 +415,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/players/:account_id/histograms/:field", - func: (req, res, cb) => { - const { field } = req.params; - req.queryObj.project = req.queryObj.project - .concat("radiant_win", "player_slot") - .concat([field].filter((f) => subkeys[f])); - queries.getPlayerMatches( - req.params.account_id, - req.queryObj, - (err, cache) => { - if (err) { - return cb(err); - } - const buckets = 40; - // Find the maximum value to determine how large each bucket should be - const max = Math.max(...cache.map((m) => m[field])); - // Round the bucket size up to the nearest integer - const bucketSize = Math.ceil((max + 1) / buckets); - const bucketArray = Array.from( - { - length: buckets, - }, - (value, index) => ({ - x: bucketSize * index, - games: 0, - win: 0, - }) - ); - cache.forEach((m) => { - if (m[field] || m[field] === 0) { - const index = Math.floor(m[field] / bucketSize); - if (bucketArray[index]) { - bucketArray[index].games += 1; - bucketArray[index].win += - utility.isRadiant(m) === m.radiant_win ? 1 : 0; - } - } - }); - return res.json(bucketArray); - } - ); - }, + func: playersHandler.getPlayersByAccountIdHistogramsByField, }, }, "/players/{account_id}/wardmap": { From 0ddf9375c0af5cad8546cede112e7f51b5a542de Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 20:26:22 +0300 Subject: [PATCH 13/58] :art: refactor: move getPlayersByAccountIdWardMap route handler to players module --- routes/handlers/players.js | 25 +++++++++++++++++++++++++ routes/spec.js | 25 +------------------------ 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/routes/handlers/players.js b/routes/handlers/players.js index dce20e0d8..c1927e7da 100644 --- a/routes/handlers/players.js +++ b/routes/handlers/players.js @@ -440,6 +440,31 @@ async function getPlayersByAccountIdHistogramsByField(req, res, cb) { ); } +async function getPlayersByAccountIdWardMap(req, res, cb) { + const result = { + obs: {}, + sen: {}, + }; + req.queryObj.project = req.queryObj.project.concat( + Object.keys(result) + ); + queries.getPlayerMatches( + req.params.account_id, + req.queryObj, + (err, cache) => { + if (err) { + return cb(err); + } + cache.forEach((m) => { + Object.keys(result).forEach((key) => { + utility.mergeObjects(result[key], m[key]); + }); + }); + return res.json(result); + } + ); +} + module.exports = { getPlayersByRank, getPlayersByAccountId, diff --git a/routes/spec.js b/routes/spec.js index 185151196..41a5ee77d 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -441,30 +441,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/players/:account_id/wardmap", - func: (req, res, cb) => { - const result = { - obs: {}, - sen: {}, - }; - req.queryObj.project = req.queryObj.project.concat( - Object.keys(result) - ); - queries.getPlayerMatches( - req.params.account_id, - req.queryObj, - (err, cache) => { - if (err) { - return cb(err); - } - cache.forEach((m) => { - Object.keys(result).forEach((key) => { - utility.mergeObjects(result[key], m[key]); - }); - }); - return res.json(result); - } - ); - }, + func: playersHandler.getPlayersByAccountIdWardMap, }, }, "/players/{account_id}/wordcloud": { From bcb3cd0cf1664ca5792d3a20ad2eeea2384b9f9b Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 20:26:36 +0300 Subject: [PATCH 14/58] :art: refactor: move getPlayersByAccountIdWordCloud route handler to players module --- routes/handlers/players.js | 25 +++++++++++++++++++++++++ routes/spec.js | 25 +------------------------ 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/routes/handlers/players.js b/routes/handlers/players.js index c1927e7da..7f7777046 100644 --- a/routes/handlers/players.js +++ b/routes/handlers/players.js @@ -465,6 +465,31 @@ async function getPlayersByAccountIdWardMap(req, res, cb) { ); } +async function getPlayersByAccountIdWordCloud(req, res, cb) { + const result = { + my_word_counts: {}, + all_word_counts: {}, + }; + req.queryObj.project = req.queryObj.project.concat( + Object.keys(result) + ); + queries.getPlayerMatches( + req.params.account_id, + req.queryObj, + (err, cache) => { + if (err) { + return cb(err); + } + cache.forEach((m) => { + Object.keys(result).forEach((key) => { + utility.mergeObjects(result[key], m[key]); + }); + }); + return res.json(result); + } + ); +} + module.exports = { getPlayersByRank, getPlayersByAccountId, diff --git a/routes/spec.js b/routes/spec.js index 41a5ee77d..2a0bf24f7 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -467,30 +467,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/players/:account_id/wordcloud", - func: (req, res, cb) => { - const result = { - my_word_counts: {}, - all_word_counts: {}, - }; - req.queryObj.project = req.queryObj.project.concat( - Object.keys(result) - ); - queries.getPlayerMatches( - req.params.account_id, - req.queryObj, - (err, cache) => { - if (err) { - return cb(err); - } - cache.forEach((m) => { - Object.keys(result).forEach((key) => { - utility.mergeObjects(result[key], m[key]); - }); - }); - return res.json(result); - } - ); - }, + func: playersHandler.getPlayersByAccountIdWordCloud, }, }, "/players/{account_id}/ratings": { From 32b9c89b09bd8286d18bb3e3ff85ec29c3c6bc6c Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 20:26:51 +0300 Subject: [PATCH 15/58] :art: refactor: move getPlayersByAccountIdRatings route handler to players module --- routes/handlers/players.js | 9 +++++++++ routes/spec.js | 9 +-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/routes/handlers/players.js b/routes/handlers/players.js index 7f7777046..a5604e72b 100644 --- a/routes/handlers/players.js +++ b/routes/handlers/players.js @@ -490,6 +490,15 @@ async function getPlayersByAccountIdWordCloud(req, res, cb) { ); } +async function getPlayersByAccountIdRatings(req, res, cb) { + queries.getPlayerRatings(db, req.params.account_id, (err, result) => { + if (err) { + return cb(err); + } + return res.json(result); + }); +} + module.exports = { getPlayersByRank, getPlayersByAccountId, diff --git a/routes/spec.js b/routes/spec.js index 2a0bf24f7..41c0a960a 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -496,14 +496,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/players/:account_id/ratings", - func: (req, res, cb) => { - queries.getPlayerRatings(db, req.params.account_id, (err, result) => { - if (err) { - return cb(err); - } - return res.json(result); - }); - }, + func: playersHandler.getPlayersByAccountIdRatings, }, }, "/players/{account_id}/rankings": { From 4dfd502271d3a389355f015058028cf8d056d35e Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 20:27:06 +0300 Subject: [PATCH 16/58] :art: refactor: move getPlayersByAccountIdRankings route handler to players module --- routes/handlers/players.js | 12 ++++++++++++ routes/spec.js | 12 +----------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/routes/handlers/players.js b/routes/handlers/players.js index a5604e72b..bb60ab846 100644 --- a/routes/handlers/players.js +++ b/routes/handlers/players.js @@ -499,6 +499,18 @@ async function getPlayersByAccountIdRatings(req, res, cb) { }); } +async function getPlayersByAccountIdRankings(req, res, cb) { + queries.getPlayerHeroRankings( + req.params.account_id, + (err, result) => { + if (err) { + return cb(err); + } + return res.json(result); + } + ); +} + module.exports = { getPlayersByRank, getPlayersByAccountId, diff --git a/routes/spec.js b/routes/spec.js index 41c0a960a..e11d69203 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -525,17 +525,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/players/:account_id/rankings", - func: (req, res, cb) => { - queries.getPlayerHeroRankings( - req.params.account_id, - (err, result) => { - if (err) { - return cb(err); - } - return res.json(result); - } - ); - }, + func: playersHandler.getPlayersByAccountIdRankings, }, }, "/players/{account_id}/refresh": { From 26774c7a338e5e73b77a754d18ca9f2848120d86 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 20:27:17 +0300 Subject: [PATCH 17/58] :art: refactor: move getPlayersByAccountIdRefresh route handler to players module --- routes/handlers/players.js | 17 +++++++++++++++++ routes/spec.js | 17 +---------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/routes/handlers/players.js b/routes/handlers/players.js index bb60ab846..e097228d0 100644 --- a/routes/handlers/players.js +++ b/routes/handlers/players.js @@ -511,6 +511,23 @@ async function getPlayersByAccountIdRankings(req, res, cb) { ); } +async function getPlayersByAccountIdRefresh(req, res, cb) { + redis.rpush( + "fhQueue", + JSON.stringify({ + account_id: req.params.account_id || "1", + }), + (err, length) => { + if (err) { + return cb(err); + } + return res.json({ + length, + }); + } + ); +} + module.exports = { getPlayersByRank, getPlayersByAccountId, diff --git a/routes/spec.js b/routes/spec.js index e11d69203..fe4cfab36 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -548,22 +548,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/players/:account_id/refresh", - func: (req, res, cb) => { - redis.rpush( - "fhQueue", - JSON.stringify({ - account_id: req.params.account_id || "1", - }), - (err, length) => { - if (err) { - return cb(err); - } - return res.json({ - length, - }); - } - ); - }, + func: playersHandler.getPlayersByAccountIdRefresh, }, }, "/proPlayers": { From 97fbef66f39297f7b1660d2ce0b7026ac8d33e8f Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 20:27:53 +0300 Subject: [PATCH 18/58] :art: refactor: move getProPlayers route handler to players module --- routes/handlers/players.js | 17 +++++++++++++++++ routes/spec.js | 17 +---------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/routes/handlers/players.js b/routes/handlers/players.js index e097228d0..92f81e23f 100644 --- a/routes/handlers/players.js +++ b/routes/handlers/players.js @@ -528,6 +528,23 @@ async function getPlayersByAccountIdRefresh(req, res, cb) { ); } +async function getProPlayers(req, res, cb) { + db.select() + .from("players") + .rightJoin( + "notable_players", + "players.account_id", + "notable_players.account_id" + ) + .orderBy("notable_players.account_id", "asc") + .asCallback((err, result) => { + if (err) { + return cb(err); + } + return res.json(result); + }); +} + module.exports = { getPlayersByRank, getPlayersByAccountId, diff --git a/routes/spec.js b/routes/spec.js index fe4cfab36..1865f6d50 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -573,22 +573,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/proPlayers", - func: (req, res, cb) => { - db.select() - .from("players") - .rightJoin( - "notable_players", - "players.account_id", - "notable_players.account_id" - ) - .orderBy("notable_players.account_id", "asc") - .asCallback((err, result) => { - if (err) { - return cb(err); - } - return res.json(result); - }); - }, + func: playersHandler.getProPlayers, }, }, "/proMatches": { From 39d80503e16403d0d67db520e4eea92bdc4d2f02 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 20:28:08 +0300 Subject: [PATCH 19/58] exports --- routes/handlers/players.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/routes/handlers/players.js b/routes/handlers/players.js index 92f81e23f..269575006 100644 --- a/routes/handlers/players.js +++ b/routes/handlers/players.js @@ -549,4 +549,18 @@ module.exports = { getPlayersByRank, getPlayersByAccountId, getPlayersByAccountIdWl, + getPlayersByAccountIdRecentMatches, + getPlayersByAccountIdMatches, + getPlayersByAccountIdHeroes, + getPlayersByAccountIdPeers, + getPlayersByAccountIdPros, + getPlayersByAccountIdTotals, + getPlayersByAccountIdCounts, + getPlayersByAccountIdHistogramsByField, + getPlayersByAccountIdWardMap, + getPlayersByAccountIdWordCloud, + getPlayersByAccountIdRatings, + getPlayersByAccountIdRankings, + getPlayersByAccountIdRefresh, + getProPlayers, }; From cd75ab918cdbe66f083718b6bdf57eda830b390a Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 20:29:03 +0300 Subject: [PATCH 20/58] :art: imports --- routes/handlers/players.js | 82 ++++++++++++++++++++------------------ routes/spec.js | 5 +-- 2 files changed, 45 insertions(+), 42 deletions(-) diff --git a/routes/handlers/players.js b/routes/handlers/players.js index 269575006..dc9bece01 100644 --- a/routes/handlers/players.js +++ b/routes/handlers/players.js @@ -1,8 +1,14 @@ const async = require("async"); +const constants = require("dotaconstants"); const cacheFunctions = require("../../store/cacheFunctions"); const db = require("../../store/db"); const queries = require("../../store/queries"); +const redis = require("../../store/redis"); const utility = require("../../util/utility"); +const playerFields = require("../playerFields.json"); + +const { countPeers } = utility; +const { subkeys, countCats } = playerFields; async function getPlayersByRank(req, res, cb) { try { @@ -24,63 +30,63 @@ async function getPlayersByRank(req, res, cb) { } async function getPlayersByAccountId(req, res, cb) { - const accountId = Number(req.params.account_id); - async.parallel( + const accountId = Number(req.params.account_id); + async.parallel( { - profile(cb) { + profile(cb) { queries.getPlayer(db, accountId, (err, playerData) => { - if (playerData !== null && playerData !== undefined) { + if (playerData !== null && playerData !== undefined) { playerData.is_contributor = utility.isContributor(accountId); playerData.is_subscriber = Boolean(playerData?.status); - } - cb(err, playerData); + } + cb(err, playerData); }); - }, - solo_competitive_rank(cb) { + }, + solo_competitive_rank(cb) { db.first() - .from("solo_competitive_rank") - .where({ account_id: accountId }) - .asCallback((err, row) => { + .from("solo_competitive_rank") + .where({ account_id: accountId }) + .asCallback((err, row) => { cb(err, row ? row.rating : null); - }); - }, - competitive_rank(cb) { + }); + }, + competitive_rank(cb) { db.first() - .from("competitive_rank") - .where({ account_id: accountId }) - .asCallback((err, row) => { + .from("competitive_rank") + .where({ account_id: accountId }) + .asCallback((err, row) => { cb(err, row ? row.rating : null); - }); - }, - rank_tier(cb) { + }); + }, + rank_tier(cb) { db.first() - .from("rank_tier") - .where({ account_id: accountId }) - .asCallback((err, row) => { + .from("rank_tier") + .where({ account_id: accountId }) + .asCallback((err, row) => { cb(err, row ? row.rating : null); - }); - }, - leaderboard_rank(cb) { + }); + }, + leaderboard_rank(cb) { db.first() - .from("leaderboard_rank") - .where({ account_id: accountId }) - .asCallback((err, row) => { + .from("leaderboard_rank") + .where({ account_id: accountId }) + .asCallback((err, row) => { cb(err, row ? row.rating : null); - }); - }, - mmr_estimate(cb) { + }); + }, + mmr_estimate(cb) { queries.getMmrEstimate(accountId, (err, est) => - cb(err, est || {}) + cb(err, est || {}) ); - }, + }, }, (err, result) => { - if (err) { + if (err) { return cb(err); - } - return res.json(result); + } + return res.json(result); } - ); + ); } async function getPlayersByAccountIdWl(req, res, cb) { diff --git a/routes/spec.js b/routes/spec.js index 1865f6d50..3a768e5f8 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -9,21 +9,18 @@ const queries = require("../store/queries"); const search = require("../store/search"); const searchES = require("../store/searchES"); const buildStatus = require("../store/buildStatus"); -const playerFields = require("./playerFields.json"); // const getGcData = require("../util/getGcData"); const utility = require("../util/utility"); const db = require("../store/db"); const redis = require("../store/redis"); const packageJson = require("../package.json"); -const cacheFunctions = require("../store/cacheFunctions"); const params = require("./requests/importParams"); const responses = require("./responses/schemas/importResponseSchemas"); const generateOperationId = require("./generateOperationId"); const matchesHandler = require("./handlers/matches") const playersHandler = require("./handlers/players"); -const { redisCount, countPeers, matchupToString } = utility; -const { subkeys, countCats } = playerFields; +const { redisCount, matchupToString } = utility; const parameters = Object.values(params).reduce( (acc, category) => ({ ...acc, ...category }), From 5813fc02722cc36fe58f7d1ecea29e3b04c07322 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 20:47:46 +0300 Subject: [PATCH 21/58] :art: drop `async` from method definition, as await is not used --- routes/handlers/players.js | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/routes/handlers/players.js b/routes/handlers/players.js index dc9bece01..ff60b50eb 100644 --- a/routes/handlers/players.js +++ b/routes/handlers/players.js @@ -29,7 +29,7 @@ async function getPlayersByRank(req, res, cb) { } } -async function getPlayersByAccountId(req, res, cb) { +function getPlayersByAccountId(req, res, cb) { const accountId = Number(req.params.account_id); async.parallel( { @@ -89,7 +89,7 @@ async function getPlayersByAccountId(req, res, cb) { ); } -async function getPlayersByAccountIdWl(req, res, cb) { +function getPlayersByAccountIdWl(req, res, cb) { const result = { win: 0, lose: 0, @@ -117,7 +117,7 @@ async function getPlayersByAccountIdWl(req, res, cb) { ); } -async function getPlayersByAccountIdRecentMatches(req, res, cb) { +function getPlayersByAccountIdRecentMatches(req, res, cb) { queries.getPlayerMatches( req.params.account_id, { @@ -159,7 +159,7 @@ async function getPlayersByAccountIdRecentMatches(req, res, cb) { ); } -async function getPlayersByAccountIdMatches(req, res, cb) { +function getPlayersByAccountIdMatches(req, res, cb) { // Use passed fields as additional fields, if available const additionalFields = req.query.project || [ "hero_id", @@ -191,7 +191,7 @@ async function getPlayersByAccountIdMatches(req, res, cb) { ); } -async function getPlayersByAccountIdHeroes(req, res, cb) { +function getPlayersByAccountIdHeroes(req, res, cb) { const heroes = {}; // prefill heroes with every hero Object.keys(constants.heroes).forEach((heroId) => { @@ -267,7 +267,7 @@ async function getPlayersByAccountIdHeroes(req, res, cb) { ); } -async function getPlayersByAccountIdPeers(req, res, cb) { +function getPlayersByAccountIdPeers(req, res, cb) { req.queryObj.project = req.queryObj.project.concat( "heroes", "start_time", @@ -306,7 +306,7 @@ async function getPlayersByAccountIdPeers(req, res, cb) { ); } -async function getPlayersByAccountIdPros(req, res, cb) { +function getPlayersByAccountIdPros(req, res, cb) { req.queryObj.project = req.queryObj.project.concat( "heroes", "start_time", @@ -338,7 +338,7 @@ async function getPlayersByAccountIdPros(req, res, cb) { ); } -async function getPlayersByAccountIdTotals(req, res, cb) { +function getPlayersByAccountIdTotals(req, res, cb) { const result = {}; Object.keys(subkeys).forEach((key) => { result[key] = { @@ -370,7 +370,7 @@ async function getPlayersByAccountIdTotals(req, res, cb) { ); } -async function getPlayersByAccountIdCounts(req, res, cb) { +function getPlayersByAccountIdCounts(req, res, cb) { const result = {}; Object.keys(countCats).forEach((key) => { result[key] = {}; @@ -404,7 +404,7 @@ async function getPlayersByAccountIdCounts(req, res, cb) { ); } -async function getPlayersByAccountIdHistogramsByField(req, res, cb) { +function getPlayersByAccountIdHistogramsByField(req, res, cb) { const { field } = req.params; req.queryObj.project = req.queryObj.project .concat("radiant_win", "player_slot") @@ -446,7 +446,7 @@ async function getPlayersByAccountIdHistogramsByField(req, res, cb) { ); } -async function getPlayersByAccountIdWardMap(req, res, cb) { +function getPlayersByAccountIdWardMap(req, res, cb) { const result = { obs: {}, sen: {}, @@ -471,7 +471,7 @@ async function getPlayersByAccountIdWardMap(req, res, cb) { ); } -async function getPlayersByAccountIdWordCloud(req, res, cb) { +function getPlayersByAccountIdWordCloud(req, res, cb) { const result = { my_word_counts: {}, all_word_counts: {}, @@ -496,7 +496,7 @@ async function getPlayersByAccountIdWordCloud(req, res, cb) { ); } -async function getPlayersByAccountIdRatings(req, res, cb) { +function getPlayersByAccountIdRatings(req, res, cb) { queries.getPlayerRatings(db, req.params.account_id, (err, result) => { if (err) { return cb(err); @@ -505,7 +505,7 @@ async function getPlayersByAccountIdRatings(req, res, cb) { }); } -async function getPlayersByAccountIdRankings(req, res, cb) { +function getPlayersByAccountIdRankings(req, res, cb) { queries.getPlayerHeroRankings( req.params.account_id, (err, result) => { @@ -517,7 +517,7 @@ async function getPlayersByAccountIdRankings(req, res, cb) { ); } -async function getPlayersByAccountIdRefresh(req, res, cb) { +function getPlayersByAccountIdRefresh(req, res, cb) { redis.rpush( "fhQueue", JSON.stringify({ @@ -534,7 +534,7 @@ async function getPlayersByAccountIdRefresh(req, res, cb) { ); } -async function getProPlayers(req, res, cb) { +function getProPlayers(req, res, cb) { db.select() .from("players") .rightJoin( From 41b130a2be914489be505ccd4464f56d61d0af70 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 20:49:38 +0300 Subject: [PATCH 22/58] :art: refactor: move getProMatches route handler to matches module --- routes/handlers/matches.js | 31 +++++++++++++++++++++++++++++++ routes/spec.js | 29 +---------------------------- 2 files changed, 32 insertions(+), 28 deletions(-) diff --git a/routes/handlers/matches.js b/routes/handlers/matches.js index 3a8f6d164..9e00a936d 100644 --- a/routes/handlers/matches.js +++ b/routes/handlers/matches.js @@ -1,4 +1,5 @@ const buildMatch = require("../../store/buildMatch"); +const db = require("../../store/db"); async function getMatchById(req, res, cb) { try { @@ -12,6 +13,36 @@ async function getMatchById(req, res, cb) { } } +function getProMatches(req, res, cb) { + db.raw( + ` + SELECT match_id, duration, start_time, + radiant_team_id, radiant.name as radiant_name, + dire_team_id, dire.name as dire_name, + leagueid, leagues.name as league_name, + series_id, series_type, + radiant_score, dire_score, + radiant_win + FROM matches + LEFT JOIN teams radiant + ON radiant.team_id = matches.radiant_team_id + LEFT JOIN teams dire + ON dire.team_id = matches.dire_team_id + LEFT JOIN leagues USING(leagueid) + WHERE match_id < ? + ORDER BY match_id DESC + LIMIT 100 + `, + [req.query.less_than_match_id || Number.MAX_SAFE_INTEGER] + ).asCallback((err, result) => { + if (err) { + return cb(err); + } + return res.json(result.rows); + }); +} + module.exports = { getMatchById, + getProMatches, }; diff --git a/routes/spec.js b/routes/spec.js index 3a768e5f8..d4b4abc69 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -596,34 +596,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/proMatches", - func: (req, res, cb) => { - db.raw( - ` - SELECT match_id, duration, start_time, - radiant_team_id, radiant.name as radiant_name, - dire_team_id, dire.name as dire_name, - leagueid, leagues.name as league_name, - series_id, series_type, - radiant_score, dire_score, - radiant_win - FROM matches - LEFT JOIN teams radiant - ON radiant.team_id = matches.radiant_team_id - LEFT JOIN teams dire - ON dire.team_id = matches.dire_team_id - LEFT JOIN leagues USING(leagueid) - WHERE match_id < ? - ORDER BY match_id DESC - LIMIT 100 - `, - [req.query.less_than_match_id || Number.MAX_SAFE_INTEGER] - ).asCallback((err, result) => { - if (err) { - return cb(err); - } - return res.json(result.rows); - }); - }, + func: matchesHandler.getProMatches, }, }, "/publicMatches": { From f8b22e634d77fc196da0860704452eaffcc3caf5 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 20:51:31 +0300 Subject: [PATCH 23/58] :art: refactor: move getPublicMatches route handler to matches module --- routes/handlers/matches.js | 52 ++++++++++++++++++++++++++++++++++++++ routes/spec.js | 51 +------------------------------------ 2 files changed, 53 insertions(+), 50 deletions(-) diff --git a/routes/handlers/matches.js b/routes/handlers/matches.js index 9e00a936d..bded9a61b 100644 --- a/routes/handlers/matches.js +++ b/routes/handlers/matches.js @@ -42,7 +42,59 @@ function getProMatches(req, res, cb) { }); } +async function getPublicMatches(req, res, cb) { + const currMax = + (await db("public_matches").max("match_id").first()).max || 0; + const lessThan = Number(req.query.less_than_match_id) || currMax; + let moreThan = lessThan - 1000000; + let order = ""; + if (req.query.mmr_ascending) { + order = "ORDER BY avg_rank_tier ASC NULLS LAST"; + } else if (req.query.mmr_descending) { + order = "ORDER BY avg_rank_tier DESC NULLS LAST"; + } else { + order = "ORDER BY match_id DESC"; + moreThan = 0; + } + const minRank = req.query.min_rank + ? `AND avg_rank_tier >= ${req.query.min_rank}` + : ""; + const maxRank = req.query.max_rank + ? `AND avg_rank_tier <= ${req.query.max_rank}` + : ""; + + db.raw( + ` + WITH match_ids AS (SELECT match_id FROM public_matches + WHERE TRUE + AND match_id > ? + AND match_id < ? + ${minRank} + ${maxRank} + ${order} + LIMIT 100) + SELECT * FROM + (SELECT * FROM public_matches + WHERE match_id IN (SELECT match_id FROM match_ids)) matches + JOIN + (SELECT match_id, string_agg(hero_id::text, ',') radiant_team FROM public_player_matches WHERE match_id IN (SELECT match_id FROM match_ids) AND player_slot <= 127 GROUP BY match_id) radiant_team + USING(match_id) + JOIN + (SELECT match_id, string_agg(hero_id::text, ',') dire_team FROM public_player_matches WHERE match_id IN (SELECT match_id FROM match_ids) AND player_slot > 127 GROUP BY match_id) dire_team + USING(match_id) + ${order} + `, + [moreThan, lessThan] + ).asCallback((err, result) => { + if (err) { + return cb(err); + } + return res.json(result.rows); + }); +} + module.exports = { getMatchById, getProMatches, + getPublicMatches, }; diff --git a/routes/spec.js b/routes/spec.js index d4b4abc69..ac987964b 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -628,56 +628,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/publicMatches", - func: async (req, res, cb) => { - const currMax = - (await db("public_matches").max("match_id").first()).max || 0; - const lessThan = Number(req.query.less_than_match_id) || currMax; - let moreThan = lessThan - 1000000; - let order = ""; - if (req.query.mmr_ascending) { - order = "ORDER BY avg_rank_tier ASC NULLS LAST"; - } else if (req.query.mmr_descending) { - order = "ORDER BY avg_rank_tier DESC NULLS LAST"; - } else { - order = "ORDER BY match_id DESC"; - moreThan = 0; - } - let minRank = req.query.min_rank - ? `AND avg_rank_tier >= ${req.query.min_rank}` - : ""; - let maxRank = req.query.max_rank - ? `AND avg_rank_tier <= ${req.query.max_rank}` - : ""; - - db.raw( - ` - WITH match_ids AS (SELECT match_id FROM public_matches - WHERE TRUE - AND match_id > ? - AND match_id < ? - ${minRank} - ${maxRank} - ${order} - LIMIT 100) - SELECT * FROM - (SELECT * FROM public_matches - WHERE match_id IN (SELECT match_id FROM match_ids)) matches - JOIN - (SELECT match_id, string_agg(hero_id::text, ',') radiant_team FROM public_player_matches WHERE match_id IN (SELECT match_id FROM match_ids) AND player_slot <= 127 GROUP BY match_id) radiant_team - USING(match_id) - JOIN - (SELECT match_id, string_agg(hero_id::text, ',') dire_team FROM public_player_matches WHERE match_id IN (SELECT match_id FROM match_ids) AND player_slot > 127 GROUP BY match_id) dire_team - USING(match_id) - ${order} - `, - [moreThan, lessThan] - ).asCallback((err, result) => { - if (err) { - return cb(err); - } - return res.json(result.rows); - }); - }, + func: matchesHandler.getPublicMatches, }, }, "/parsedMatches": { From 0614143d15d35c926da24e48d5367c3e0cb65b9b Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 20:52:27 +0300 Subject: [PATCH 24/58] :art: refactor: move getParsedMatches route handler to matches module --- routes/handlers/matches.js | 21 +++++++++++++++++++++ routes/spec.js | 20 +------------------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/routes/handlers/matches.js b/routes/handlers/matches.js index bded9a61b..fe5180ad9 100644 --- a/routes/handlers/matches.js +++ b/routes/handlers/matches.js @@ -93,8 +93,29 @@ async function getPublicMatches(req, res, cb) { }); } +function getParsedMatches(req, res, cb) { + const lessThan = + req.query.less_than_match_id || Number.MAX_SAFE_INTEGER; + + db.raw( + ` + SELECT * FROM parsed_matches + WHERE match_id < ? + ORDER BY match_id DESC + LIMIT 100 + `, + [lessThan] + ).asCallback((err, result) => { + if (err) { + return cb(err); + } + return res.json(result.rows); + }); +} + module.exports = { getMatchById, getProMatches, getPublicMatches, + getParsedMatches, }; diff --git a/routes/spec.js b/routes/spec.js index ac987964b..c51c8c6d6 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -654,25 +654,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/parsedMatches", - func: (req, res, cb) => { - const lessThan = - req.query.less_than_match_id || Number.MAX_SAFE_INTEGER; - - db.raw( - ` - SELECT * FROM parsed_matches - WHERE match_id < ? - ORDER BY match_id DESC - LIMIT 100 - `, - [lessThan] - ).asCallback((err, result) => { - if (err) { - return cb(err); - } - return res.json(result.rows); - }); - }, + func: matchesHandler.getParsedMatches, }, }, "/explorer": { From f549ee78f2dcacded74fd0a8a4b47ab9d73e5859 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 21:15:02 +0300 Subject: [PATCH 25/58] :art: refactor: move getHeroRankings route handler to heroes module --- routes/handlers/heroes.js | 16 ++++++++++++++++ routes/spec.js | 16 ++-------------- 2 files changed, 18 insertions(+), 14 deletions(-) create mode 100644 routes/handlers/heroes.js diff --git a/routes/handlers/heroes.js b/routes/handlers/heroes.js new file mode 100644 index 000000000..44c087b5c --- /dev/null +++ b/routes/handlers/heroes.js @@ -0,0 +1,16 @@ +const db = require("../../store/db"); +const queries = require("../../store/queries"); +const redis = require("../../store/redis"); + +function getHeroRankings(req, res, cb) { + queries.getHeroRankings(db, redis, req.query.hero_id, {}, (err, result) => { + if (err) { + return cb(err); + } + return res.json(result); + }); +} + +module.exports = { + getHeroRankings, +}; diff --git a/routes/spec.js b/routes/spec.js index c51c8c6d6..7fa591ce3 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -19,6 +19,7 @@ const responses = require("./responses/schemas/importResponseSchemas"); const generateOperationId = require("./generateOperationId"); const matchesHandler = require("./handlers/matches") const playersHandler = require("./handlers/players"); +const heroesHandler = require("./handlers/heroes"); const { redisCount, matchupToString } = utility; @@ -857,20 +858,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/rankings", - func: (req, res, cb) => { - queries.getHeroRankings( - db, - redis, - req.query.hero_id, - {}, - (err, result) => { - if (err) { - return cb(err); - } - return res.json(result); - } - ); - }, + func: heroesHandler.getHeroRankings, }, }, "/benchmarks": { From ba82edf57bca89658a2f0ef4543ca9ce4ba083cc Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 21:15:40 +0300 Subject: [PATCH 26/58] :art: refactor: move searchPlayers route handler to players module --- routes/handlers/players.js | 22 ++++++++++++++++++++++ routes/spec.js | 24 +----------------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/routes/handlers/players.js b/routes/handlers/players.js index ff60b50eb..df327cde9 100644 --- a/routes/handlers/players.js +++ b/routes/handlers/players.js @@ -551,6 +551,27 @@ function getProPlayers(req, res, cb) { }); } +function searchPlayers(req, res, cb) { + if (!req.query.q) { + return res.status(400).json([]); + } + + if (req.query.es || utility.checkIfInExperiment(res.locals.ip, config.ES_SEARCH_PERCENT)) { + return searchES(req.query, (err, result) => { + if (err) { + return cb(err); + } + return res.json(result); + }); + } + return search(req.query, (err, result) => { + if (err) { + return cb(err); + } + return res.json(result); + }); +} + module.exports = { getPlayersByRank, getPlayersByAccountId, @@ -569,4 +590,5 @@ module.exports = { getPlayersByAccountIdRankings, getPlayersByAccountIdRefresh, getProPlayers, + searchPlayers, }; diff --git a/routes/spec.js b/routes/spec.js index 7fa591ce3..6208297ac 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -803,29 +803,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/search", - func: (req, res, cb) => { - if (!req.query.q) { - return res.status(400).json([]); - } - - if ( - req.query.es || - utility.checkIfInExperiment(res.locals.ip, config.ES_SEARCH_PERCENT) - ) { - return searchES(req.query, (err, result) => { - if (err) { - return cb(err); - } - return res.json(result); - }); - } - return search(req.query, (err, result) => { - if (err) { - return cb(err); - } - return res.json(result); - }); - }, + func: playersHandler.searchPlayers, }, }, "/rankings": { From a7a1523fb31bf83067dbeb93d0f0de278c5c411f Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 21:17:46 +0300 Subject: [PATCH 27/58] :art: refactor: move findMatches route handler to matches module --- routes/handlers/matches.js | 38 ++++++++++++++++++++++++++++++++++++++ routes/spec.js | 38 +------------------------------------- 2 files changed, 39 insertions(+), 37 deletions(-) diff --git a/routes/handlers/matches.js b/routes/handlers/matches.js index fe5180ad9..51f388856 100644 --- a/routes/handlers/matches.js +++ b/routes/handlers/matches.js @@ -1,5 +1,7 @@ const buildMatch = require("../../store/buildMatch"); const db = require("../../store/db"); +const redis = require("../../store/redis"); +const utility = require("../../util/utility"); async function getMatchById(req, res, cb) { try { @@ -113,9 +115,45 @@ function getParsedMatches(req, res, cb) { }); } +function findMatches(req, res, cb) { + // accept as input two arrays of up to 5 + const t0 = [].concat(req.query.teamA || []).slice(0, 5); + const t1 = [].concat(req.query.teamB || []).slice(0, 5); + + // Construct key for redis + const key = `combos:${utility.matchupToString(t0, t1, true)}`; + redis.get(key, (err, reply) => { + if (err) { + return cb(err); + } + if (reply) { + return res.end(reply); + } + // Determine which comes first + // const rcg = groupToString(t0); + // const dcg = groupToString(t1); + + // const inverted = rcg > dcg; + const inverted = false; + const teamA = inverted ? t1 : t0; + const teamB = inverted ? t0 : t1; + + return db + .raw("select * from hero_search where (teamA @> ? AND teamB @> ?) OR (teamA @> ? AND teamB @> ?) order by match_id desc limit 10", [teamA, teamB, teamB, teamA]) + .asCallback((err, result) => { + if (err) { + return cb(err); + } + redis.setex(key, 60, JSON.stringify(result.rows)); + return res.json(result.rows); + }); + }); +} + module.exports = { getMatchById, getProMatches, getPublicMatches, getParsedMatches, + findMatches, }; diff --git a/routes/spec.js b/routes/spec.js index 6208297ac..aa86c2c8e 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -1121,43 +1121,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/findMatches", - func: (req, res, cb) => { - // accept as input two arrays of up to 5 - const t0 = [].concat(req.query.teamA || []).slice(0, 5); - const t1 = [].concat(req.query.teamB || []).slice(0, 5); - - // Construct key for redis - const key = `combos:${matchupToString(t0, t1, true)}`; - redis.get(key, (err, reply) => { - if (err) { - return cb(err); - } - if (reply) { - return res.end(reply); - } - // Determine which comes first - // const rcg = groupToString(t0); - // const dcg = groupToString(t1); - - // const inverted = rcg > dcg; - const inverted = false; - const teamA = inverted ? t1 : t0; - const teamB = inverted ? t0 : t1; - - return db - .raw( - "select * from hero_search where (teamA @> ? AND teamB @> ?) OR (teamA @> ? AND teamB @> ?) order by match_id desc limit 10", - [teamA, teamB, teamB, teamA] - ) - .asCallback((err, result) => { - if (err) { - return cb(err); - } - redis.setex(key, 60, JSON.stringify(result.rows)); - return res.json(result.rows); - }); - }); - }, + func: matchesHandler.findMatches, }, }, "/heroes": { From b59f7307ba86cc460afcf9a96e20b207a450e552 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 21:18:56 +0300 Subject: [PATCH 28/58] :art: refactor: move getHeroBenchmarks route handler to heroes module --- routes/handlers/heroes.js | 17 +++++++++++++++++ routes/spec.js | 16 +--------------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/routes/handlers/heroes.js b/routes/handlers/heroes.js index 44c087b5c..f80ce35fd 100644 --- a/routes/handlers/heroes.js +++ b/routes/handlers/heroes.js @@ -2,6 +2,22 @@ const db = require("../../store/db"); const queries = require("../../store/queries"); const redis = require("../../store/redis"); +function getHeroBenchmarks(req, res, cb) { + queries.getHeroBenchmarks( + db, + redis, + { + hero_id: req.query.hero_id, + }, + (err, result) => { + if (err) { + return cb(err); + } + return res.json(result); + } + ); +} + function getHeroRankings(req, res, cb) { queries.getHeroRankings(db, redis, req.query.hero_id, {}, (err, result) => { if (err) { @@ -12,5 +28,6 @@ function getHeroRankings(req, res, cb) { } module.exports = { + getHeroBenchmarks, getHeroRankings, }; diff --git a/routes/spec.js b/routes/spec.js index aa86c2c8e..d13a9d8bf 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -869,21 +869,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/benchmarks", - func: (req, res, cb) => { - queries.getHeroBenchmarks( - db, - redis, - { - hero_id: req.query.hero_id, - }, - (err, result) => { - if (err) { - return cb(err); - } - return res.json(result); - } - ); - }, + func: heroesHandler.getHeroBenchmarks, }, }, "/status": { From 5b03eb47aa099c627d5b84ca91014bec4710b37d Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 21:19:50 +0300 Subject: [PATCH 29/58] :art: refactor: move getHeroData route handler to heroes module --- routes/handlers/heroes.js | 13 +++++++++++++ routes/spec.js | 12 +----------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/routes/handlers/heroes.js b/routes/handlers/heroes.js index f80ce35fd..725a59bb4 100644 --- a/routes/handlers/heroes.js +++ b/routes/handlers/heroes.js @@ -27,7 +27,20 @@ function getHeroRankings(req, res, cb) { }); } +function getHeroData(req, res, cb) { + db.select() + .from("heroes") + .orderBy("id", "asc") + .asCallback((err, result) => { + if (err) { + return cb(err); + } + return res.json(result); + }); +} + module.exports = { getHeroBenchmarks, getHeroRankings, + getHeroData, }; diff --git a/routes/spec.js b/routes/spec.js index d13a9d8bf..e8e419bab 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -1132,17 +1132,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/heroes", - func: (req, res, cb) => { - db.select() - .from("heroes") - .orderBy("id", "asc") - .asCallback((err, result) => { - if (err) { - return cb(err); - } - return res.json(result); - }); - }, + func: heroesHandler.getHeroData, }, }, "/heroStats": { From a2e3da3ef74b29f73f8381a606cc28f65478477b Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 21:28:42 +0300 Subject: [PATCH 30/58] :art: refactor: move getHeroStats route handler to heroes module --- routes/handlers/heroes.js | 11 +++++++++++ routes/spec.js | 10 +--------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/routes/handlers/heroes.js b/routes/handlers/heroes.js index 725a59bb4..b6c97a490 100644 --- a/routes/handlers/heroes.js +++ b/routes/handlers/heroes.js @@ -39,8 +39,19 @@ function getHeroData(req, res, cb) { }); } +function getHeroStats(req, res, cb) { + // fetch from cached redis value + redis.get("heroStats", (err, result) => { + if (err) { + return cb(err); + } + return res.json(JSON.parse(result)); + }); +} + module.exports = { getHeroBenchmarks, getHeroRankings, getHeroData, + getHeroStats, }; diff --git a/routes/spec.js b/routes/spec.js index e8e419bab..53283b7d9 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -1158,15 +1158,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/heroStats", - func: (req, res, cb) => { - // fetch from cached redis value - redis.get("heroStats", (err, result) => { - if (err) { - return cb(err); - } - return res.json(JSON.parse(result)); - }); - }, + func: heroesHandler.getHeroStats, }, }, "/heroes/{hero_id}/matches": { From 488348e08964c9c89fa3bbc6c00ad3fd3bfb80ae Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 21:28:56 +0300 Subject: [PATCH 31/58] :art: refactor: move getRecentMatchesByHeroId route handler to heroes module --- routes/handlers/heroes.js | 33 +++++++++++++++++++++++++++++++++ routes/spec.js | 32 +------------------------------- 2 files changed, 34 insertions(+), 31 deletions(-) diff --git a/routes/handlers/heroes.js b/routes/handlers/heroes.js index b6c97a490..20268ec7b 100644 --- a/routes/handlers/heroes.js +++ b/routes/handlers/heroes.js @@ -49,9 +49,42 @@ function getHeroStats(req, res, cb) { }); } +function getRecentMatchesByHeroId(req, res, cb) { + const heroId = req.params.hero_id; + db.raw( + `SELECT + matches.match_id, + matches.start_time, + matches.duration, + matches.radiant_win, + matches.leagueid, + leagues.name as league_name, + ((player_matches.player_slot < 128) = matches.radiant_win) radiant, + player_matches.player_slot, + player_matches.account_id, + player_matches.kills, + player_matches.deaths, + player_matches.assists + FROM matches + JOIN player_matches using(match_id) + JOIN leagues using(leagueid) + LEFT JOIN heroes on heroes.id = player_matches.hero_id + WHERE player_matches.hero_id = ? + ORDER BY matches.match_id DESC + LIMIT 100`, + [heroId] + ).asCallback((err, result) => { + if (err) { + return cb(err); + } + return res.json(result.rows); + }); +} + module.exports = { getHeroBenchmarks, getHeroRankings, getHeroData, getHeroStats, + getRecentMatchesByHeroId, }; diff --git a/routes/spec.js b/routes/spec.js index 53283b7d9..2dbaf2731 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -1184,37 +1184,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/heroes/:hero_id/matches", - func: (req, res, cb) => { - const heroId = req.params.hero_id; - db.raw( - `SELECT - matches.match_id, - matches.start_time, - matches.duration, - matches.radiant_win, - matches.leagueid, - leagues.name as league_name, - ((player_matches.player_slot < 128) = matches.radiant_win) radiant, - player_matches.player_slot, - player_matches.account_id, - player_matches.kills, - player_matches.deaths, - player_matches.assists - FROM matches - JOIN player_matches using(match_id) - JOIN leagues using(leagueid) - LEFT JOIN heroes on heroes.id = player_matches.hero_id - WHERE player_matches.hero_id = ? - ORDER BY matches.match_id DESC - LIMIT 100`, - [heroId] - ).asCallback((err, result) => { - if (err) { - return cb(err); - } - return res.json(result.rows); - }); - }, + func: heroesHandler.getRecentMatchesByHeroId }, }, "/heroes/{hero_id}/matchups": { From 62845750b197b5c2c6c791de105dc37dde7720e0 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 21:31:35 +0300 Subject: [PATCH 32/58] :art: refactor: move getMatchupsByHeroId route handler to heroes module --- routes/handlers/heroes.js | 25 +++++++++++++++++++++++++ routes/spec.js | 23 +---------------------- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/routes/handlers/heroes.js b/routes/handlers/heroes.js index 20268ec7b..5bddce784 100644 --- a/routes/handlers/heroes.js +++ b/routes/handlers/heroes.js @@ -1,3 +1,4 @@ +const moment = require("moment"); const db = require("../../store/db"); const queries = require("../../store/queries"); const redis = require("../../store/redis"); @@ -81,10 +82,34 @@ function getRecentMatchesByHeroId(req, res, cb) { }); } +function getMatchupsByHeroId(req, res, cb) { + const heroId = req.params.hero_id; + db.raw( + `SELECT + pm2.hero_id, + count(player_matches.match_id) games_played, + sum(case when (player_matches.player_slot < 128) = matches.radiant_win then 1 else 0 end) wins + FROM matches + JOIN player_matches using(match_id) + JOIN player_matches pm2 on player_matches.match_id = pm2.match_id AND (player_matches.player_slot < 128) != (pm2.player_slot < 128) + WHERE player_matches.hero_id = ? + AND matches.start_time > ? + GROUP BY pm2.hero_id + ORDER BY games_played DESC`, + [heroId, moment().subtract(1, "year").format("X")] + ).asCallback((err, result) => { + if (err) { + return cb(err); + } + return res.json(result.rows); + }); +} + module.exports = { getHeroBenchmarks, getHeroRankings, getHeroData, getHeroStats, getRecentMatchesByHeroId, + getMatchupsByHeroId, }; diff --git a/routes/spec.js b/routes/spec.js index 2dbaf2731..d81282bb9 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -1210,28 +1210,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/heroes/:hero_id/matchups", - func: (req, res, cb) => { - const heroId = req.params.hero_id; - db.raw( - `SELECT - pm2.hero_id, - count(player_matches.match_id) games_played, - sum(case when (player_matches.player_slot < 128) = matches.radiant_win then 1 else 0 end) wins - FROM matches - JOIN player_matches using(match_id) - JOIN player_matches pm2 on player_matches.match_id = pm2.match_id AND (player_matches.player_slot < 128) != (pm2.player_slot < 128) - WHERE player_matches.hero_id = ? - AND matches.start_time > ? - GROUP BY pm2.hero_id - ORDER BY games_played DESC`, - [heroId, moment().subtract(1, "year").format("X")] - ).asCallback((err, result) => { - if (err) { - return cb(err); - } - return res.json(result.rows); - }); - }, + func: heroesHandler.getMatchupsByHeroId, }, }, "/heroes/{hero_id}/durations": { From 910c3d43041a4563cbe0beefb6f55dae9d6cedc8 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 21:31:45 +0300 Subject: [PATCH 33/58] :art: refactor: move getMatchDurationsByHeroId route handler to heroes module --- routes/handlers/heroes.js | 21 +++++++++++++++++++++ routes/spec.js | 20 +------------------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/routes/handlers/heroes.js b/routes/handlers/heroes.js index 5bddce784..4153afe21 100644 --- a/routes/handlers/heroes.js +++ b/routes/handlers/heroes.js @@ -105,6 +105,26 @@ function getMatchupsByHeroId(req, res, cb) { }); } +function getMatchDurationsByHeroId(req, res, cb) { + const heroId = req.params.hero_id; + db.raw( + `SELECT + (matches.duration / 300 * 300) duration_bin, + count(match_id) games_played, + sum(case when (player_matches.player_slot < 128) = matches.radiant_win then 1 else 0 end) wins + FROM matches + JOIN player_matches using(match_id) + WHERE player_matches.hero_id = ? + GROUP BY (matches.duration / 300 * 300)`, + [heroId] + ).asCallback((err, result) => { + if (err) { + return cb(err); + } + return res.json(result.rows); + }); +} + module.exports = { getHeroBenchmarks, getHeroRankings, @@ -112,4 +132,5 @@ module.exports = { getHeroStats, getRecentMatchesByHeroId, getMatchupsByHeroId, + getMatchDurationsByHeroId, }; diff --git a/routes/spec.js b/routes/spec.js index d81282bb9..cfd796897 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -1236,25 +1236,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/heroes/:hero_id/durations", - func: (req, res, cb) => { - const heroId = req.params.hero_id; - db.raw( - `SELECT - (matches.duration / 300 * 300) duration_bin, - count(match_id) games_played, - sum(case when (player_matches.player_slot < 128) = matches.radiant_win then 1 else 0 end) wins - FROM matches - JOIN player_matches using(match_id) - WHERE player_matches.hero_id = ? - GROUP BY (matches.duration / 300 * 300)`, - [heroId] - ).asCallback((err, result) => { - if (err) { - return cb(err); - } - return res.json(result.rows); - }); - }, + func: heroesHandler.getMatchDurationsByHeroId, }, }, "/heroes/{hero_id}/players": { From 24ecf8b0d8bcb96814cf49e6f32aad3a3b289cef Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 21:32:03 +0300 Subject: [PATCH 34/58] :art: refactor: move getPlayersByHeroId route handler to heroes module --- routes/handlers/heroes.js | 22 ++++++++++++++++++++++ routes/spec.js | 21 +-------------------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/routes/handlers/heroes.js b/routes/handlers/heroes.js index 4153afe21..07663009e 100644 --- a/routes/handlers/heroes.js +++ b/routes/handlers/heroes.js @@ -125,6 +125,27 @@ function getMatchDurationsByHeroId(req, res, cb) { }); } +function getPlayersByHeroId(req, res, cb) { + const heroId = req.params.hero_id; + db.raw( + `SELECT + account_id, + count(match_id) games_played, + sum(case when (player_matches.player_slot < 128) = matches.radiant_win then 1 else 0 end) wins + FROM matches + JOIN player_matches using(match_id) + WHERE player_matches.hero_id = ? + GROUP BY account_id + ORDER BY games_played DESC`, + [heroId] + ).asCallback((err, result) => { + if (err) { + return cb(err); + } + return res.json(result.rows); + }); +} + module.exports = { getHeroBenchmarks, getHeroRankings, @@ -133,4 +154,5 @@ module.exports = { getRecentMatchesByHeroId, getMatchupsByHeroId, getMatchDurationsByHeroId, + getPlayersByHeroId, }; diff --git a/routes/spec.js b/routes/spec.js index cfd796897..90dfe8703 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -1265,26 +1265,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/heroes/:hero_id/players", - func: (req, res, cb) => { - const heroId = req.params.hero_id; - db.raw( - `SELECT - account_id, - count(match_id) games_played, - sum(case when (player_matches.player_slot < 128) = matches.radiant_win then 1 else 0 end) wins - FROM matches - JOIN player_matches using(match_id) - WHERE player_matches.hero_id = ? - GROUP BY account_id - ORDER BY games_played DESC`, - [heroId] - ).asCallback((err, result) => { - if (err) { - return cb(err); - } - return res.json(result.rows); - }); - }, + func: heroesHandler.getPlayersByHeroId, }, }, "/heroes/{hero_id}/itemPopularity": { From 0154323f20b3fb37b867af4d588847c19f8deaa1 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 21:32:57 +0300 Subject: [PATCH 35/58] :art: refactor: move getItemPopularityByHeroId route handler to heroes module --- routes/handlers/heroes.js | 11 +++++++++++ routes/spec.js | 17 +---------------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/routes/handlers/heroes.js b/routes/handlers/heroes.js index 07663009e..d0e4f6183 100644 --- a/routes/handlers/heroes.js +++ b/routes/handlers/heroes.js @@ -146,6 +146,16 @@ function getPlayersByHeroId(req, res, cb) { }); } +function getItemPopularityByHeroId(req, res, cb) { + const heroId = req.params.hero_id; + queries.getHeroItemPopularity(db, redis, heroId, {}, (err, result) => { + if (err) { + return cb(err); + } + return res.json(result); + }); +} + module.exports = { getHeroBenchmarks, getHeroRankings, @@ -155,4 +165,5 @@ module.exports = { getMatchupsByHeroId, getMatchDurationsByHeroId, getPlayersByHeroId, + getItemPopularityByHeroId, }; diff --git a/routes/spec.js b/routes/spec.js index 90dfe8703..865f47980 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -1,5 +1,4 @@ const constants = require("dotaconstants"); -const moment = require("moment"); const { Client } = require("pg"); const config = require("../config"); // const crypto = require("crypto"); @@ -1292,21 +1291,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/heroes/:hero_id/itemPopularity", - func: (req, res, cb) => { - const heroId = req.params.hero_id; - queries.getHeroItemPopularity( - db, - redis, - heroId, - {}, - (err, result) => { - if (err) { - return cb(err); - } - return res.json(result); - } - ); - }, + func: heroesHandler.getItemPopularityByHeroId, }, }, "/leagues": { From 0de8e7ecc92732c9fa94f9bd624d56d0d7d2a672 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 21:38:27 +0300 Subject: [PATCH 36/58] :art: --- routes/handlers/players.js | 462 +++++++++++++++---------------------- 1 file changed, 185 insertions(+), 277 deletions(-) diff --git a/routes/handlers/players.js b/routes/handlers/players.js index df327cde9..98c1532d8 100644 --- a/routes/handlers/players.js +++ b/routes/handlers/players.js @@ -1,11 +1,14 @@ const async = require("async"); const constants = require("dotaconstants"); const cacheFunctions = require("../../store/cacheFunctions"); +const config = require("../../config"); const db = require("../../store/db"); const queries = require("../../store/queries"); const redis = require("../../store/redis"); const utility = require("../../util/utility"); const playerFields = require("../playerFields.json"); +const search = require("../../store/search"); +const searchES = require("../../store/searchES"); const { countPeers } = utility; const { subkeys, countCats } = playerFields; @@ -75,9 +78,7 @@ function getPlayersByAccountId(req, res, cb) { }); }, mmr_estimate(cb) { - queries.getMmrEstimate(accountId, (err, est) => - cb(err, est || {}) - ); + queries.getMmrEstimate(accountId, (err, est) => cb(err, est || {})); }, }, (err, result) => { @@ -94,27 +95,20 @@ function getPlayersByAccountIdWl(req, res, cb) { win: 0, lose: 0, }; - req.queryObj.project = req.queryObj.project.concat( - "player_slot", - "radiant_win" - ); - queries.getPlayerMatches( - req.params.account_id, - req.queryObj, - (err, cache) => { - if (err) { - return cb(err); - } - cache.forEach((m) => { - if (utility.isRadiant(m) === m.radiant_win) { - result.win += 1; - } else { - result.lose += 1; - } - }); - return cacheFunctions.sendDataWithCache(req, res, result, "wl"); + req.queryObj.project = req.queryObj.project.concat("player_slot", "radiant_win"); + queries.getPlayerMatches(req.params.account_id, req.queryObj, (err, cache) => { + if (err) { + return cb(err); } - ); + cache.forEach((m) => { + if (utility.isRadiant(m) === m.radiant_win) { + result.win += 1; + } else { + result.lose += 1; + } + }); + return cacheFunctions.sendDataWithCache(req, res, result, "wl"); + }); } function getPlayersByAccountIdRecentMatches(req, res, cb) { @@ -179,16 +173,12 @@ function getPlayersByAccountIdMatches(req, res, cb) { "party_size", ]; req.queryObj.project = req.queryObj.project.concat(additionalFields); - queries.getPlayerMatches( - req.params.account_id, - req.queryObj, - (err, cache) => { - if (err) { - return cb(err); - } - return res.json(cache); + queries.getPlayerMatches(req.params.account_id, req.queryObj, (err, cache) => { + if (err) { + return cb(err); } - ); + return res.json(cache); + }); } function getPlayersByAccountIdHeroes(req, res, cb) { @@ -208,134 +198,90 @@ function getPlayersByAccountIdHeroes(req, res, cb) { }; heroes[hero_id_int] = hero; }); - req.queryObj.project = req.queryObj.project.concat( - "heroes", - "account_id", - "start_time", - "player_slot", - "radiant_win" - ); - queries.getPlayerMatches( - req.params.account_id, - req.queryObj, - (err, cache) => { - if (err) { - return cb(err); - } - cache.forEach((m) => { - const { isRadiant } = utility; - const playerWin = isRadiant(m) === m.radiant_win; - const group = m.heroes || {}; - Object.keys(group).forEach((key) => { - const tm = group[key]; - const tmHero = tm.hero_id; - // don't count invalid heroes - if (tmHero in heroes) { - if (isRadiant(tm) === isRadiant(m)) { - if (tm.account_id === m.account_id) { - heroes[tmHero].games += 1; - heroes[tmHero].win += playerWin ? 1 : 0; - if (m.start_time > heroes[tmHero].last_played) { - heroes[tmHero].last_played = m.start_time; - } - } else { - heroes[tmHero].with_games += 1; - heroes[tmHero].with_win += playerWin ? 1 : 0; + req.queryObj.project = req.queryObj.project.concat("heroes", "account_id", "start_time", "player_slot", "radiant_win"); + queries.getPlayerMatches(req.params.account_id, req.queryObj, (err, cache) => { + if (err) { + return cb(err); + } + cache.forEach((m) => { + const { isRadiant } = utility; + const playerWin = isRadiant(m) === m.radiant_win; + const group = m.heroes || {}; + Object.keys(group).forEach((key) => { + const tm = group[key]; + const tmHero = tm.hero_id; + // don't count invalid heroes + if (tmHero in heroes) { + if (isRadiant(tm) === isRadiant(m)) { + if (tm.account_id === m.account_id) { + heroes[tmHero].games += 1; + heroes[tmHero].win += playerWin ? 1 : 0; + if (m.start_time > heroes[tmHero].last_played) { + heroes[tmHero].last_played = m.start_time; } } else { - heroes[tmHero].against_games += 1; - heroes[tmHero].against_win += playerWin ? 1 : 0; + heroes[tmHero].with_games += 1; + heroes[tmHero].with_win += playerWin ? 1 : 0; } + } else { + heroes[tmHero].against_games += 1; + heroes[tmHero].against_win += playerWin ? 1 : 0; } - }); + } }); - const result = Object.keys(heroes) - .map((k) => heroes[k]) - .filter( - (hero) => - !req.queryObj.having || - hero.games >= Number(req.queryObj.having) - ) - .sort((a, b) => b.games - a.games); - return cacheFunctions.sendDataWithCache( - req, - res, - result, - "heroes" - ); - } - ); + }); + const result = Object.keys(heroes) + .map((k) => heroes[k]) + .filter((hero) => !req.queryObj.having || hero.games >= Number(req.queryObj.having)) + .sort((a, b) => b.games - a.games); + return cacheFunctions.sendDataWithCache(req, res, result, "heroes"); + }); } function getPlayersByAccountIdPeers(req, res, cb) { - req.queryObj.project = req.queryObj.project.concat( - "heroes", - "start_time", - "player_slot", - "radiant_win", - "gold_per_min", - "xp_per_min" - ); - queries.getPlayerMatches( - req.params.account_id, - req.queryObj, - (err, cache) => { - if (err) { - return cb(err); - } - const teammates = utility.countPeers(cache); - return queries.getPeers( - db, - teammates, - { - account_id: req.params.account_id, - }, - (err, result) => { - if (err) { - return cb(err); - } - return cacheFunctions.sendDataWithCache( - req, - res, - result, - "peers" - ); - } - ); + req.queryObj.project = req.queryObj.project.concat("heroes", "start_time", "player_slot", "radiant_win", "gold_per_min", "xp_per_min"); + queries.getPlayerMatches(req.params.account_id, req.queryObj, (err, cache) => { + if (err) { + return cb(err); } - ); + const teammates = utility.countPeers(cache); + return queries.getPeers( + db, + teammates, + { + account_id: req.params.account_id, + }, + (err, result) => { + if (err) { + return cb(err); + } + return cacheFunctions.sendDataWithCache(req, res, result, "peers"); + } + ); + }); } function getPlayersByAccountIdPros(req, res, cb) { - req.queryObj.project = req.queryObj.project.concat( - "heroes", - "start_time", - "player_slot", - "radiant_win" - ); - queries.getPlayerMatches( - req.params.account_id, - req.queryObj, - (err, cache) => { - if (err) { - return cb(err); - } - const teammates = countPeers(cache); - return queries.getProPeers( - db, - teammates, - { - account_id: req.params.account_id, - }, - (err, result) => { - if (err) { - return cb(err); - } - return res.json(result); - } - ); + req.queryObj.project = req.queryObj.project.concat("heroes", "start_time", "player_slot", "radiant_win"); + queries.getPlayerMatches(req.params.account_id, req.queryObj, (err, cache) => { + if (err) { + return cb(err); } - ); + const teammates = countPeers(cache); + return queries.getProPeers( + db, + teammates, + { + account_id: req.params.account_id, + }, + (err, result) => { + if (err) { + return cb(err); + } + return res.json(result); + } + ); + }); } function getPlayersByAccountIdTotals(req, res, cb) { @@ -347,27 +293,21 @@ function getPlayersByAccountIdTotals(req, res, cb) { sum: 0, }; }); - req.queryObj.project = req.queryObj.project.concat( - Object.keys(subkeys) - ); - queries.getPlayerMatches( - req.params.account_id, - req.queryObj, - (err, cache) => { - if (err) { - return cb(err); - } - cache.forEach((m) => { - Object.keys(subkeys).forEach((key) => { - if (m[key] !== null && m[key] !== undefined) { - result[key].n += 1; - result[key].sum += Number(m[key]); - } - }); - }); - return res.json(Object.keys(result).map((key) => result[key])); + req.queryObj.project = req.queryObj.project.concat(Object.keys(subkeys)); + queries.getPlayerMatches(req.params.account_id, req.queryObj, (err, cache) => { + if (err) { + return cb(err); } - ); + cache.forEach((m) => { + Object.keys(subkeys).forEach((key) => { + if (m[key] !== null && m[key] !== undefined) { + result[key].n += 1; + result[key].sum += Number(m[key]); + } + }); + }); + return res.json(Object.keys(result).map((key) => result[key])); + }); } function getPlayersByAccountIdCounts(req, res, cb) { @@ -375,75 +315,62 @@ function getPlayersByAccountIdCounts(req, res, cb) { Object.keys(countCats).forEach((key) => { result[key] = {}; }); - req.queryObj.project = req.queryObj.project.concat( - Object.keys(countCats) - ); - queries.getPlayerMatches( - req.params.account_id, - req.queryObj, - (err, cache) => { - if (err) { - return cb(err); - } - cache.forEach((m) => { - m.is_radiant = utility.isRadiant(m); - Object.keys(countCats).forEach((key) => { - if (!result[key][Math.floor(m[key])]) { - result[key][Math.floor(m[key])] = { - games: 0, - win: 0, - }; - } - result[key][Math.floor(m[key])].games += 1; - const won = Number(m.radiant_win === utility.isRadiant(m)); - result[key][Math.floor(m[key])].win += won; - }); - }); - return res.json(result); + req.queryObj.project = req.queryObj.project.concat(Object.keys(countCats)); + queries.getPlayerMatches(req.params.account_id, req.queryObj, (err, cache) => { + if (err) { + return cb(err); } - ); + cache.forEach((m) => { + m.is_radiant = utility.isRadiant(m); + Object.keys(countCats).forEach((key) => { + if (!result[key][Math.floor(m[key])]) { + result[key][Math.floor(m[key])] = { + games: 0, + win: 0, + }; + } + result[key][Math.floor(m[key])].games += 1; + const won = Number(m.radiant_win === utility.isRadiant(m)); + result[key][Math.floor(m[key])].win += won; + }); + }); + return res.json(result); + }); } function getPlayersByAccountIdHistogramsByField(req, res, cb) { const { field } = req.params; - req.queryObj.project = req.queryObj.project - .concat("radiant_win", "player_slot") - .concat([field].filter((f) => subkeys[f])); - queries.getPlayerMatches( - req.params.account_id, - req.queryObj, - (err, cache) => { - if (err) { - return cb(err); - } - const buckets = 40; - // Find the maximum value to determine how large each bucket should be - const max = Math.max(...cache.map((m) => m[field])); - // Round the bucket size up to the nearest integer - const bucketSize = Math.ceil((max + 1) / buckets); - const bucketArray = Array.from( - { - length: buckets, - }, - (value, index) => ({ - x: bucketSize * index, - games: 0, - win: 0, - }) - ); - cache.forEach((m) => { - if (m[field] || m[field] === 0) { - const index = Math.floor(m[field] / bucketSize); - if (bucketArray[index]) { - bucketArray[index].games += 1; - bucketArray[index].win += - utility.isRadiant(m) === m.radiant_win ? 1 : 0; - } - } - }); - return res.json(bucketArray); + req.queryObj.project = req.queryObj.project.concat("radiant_win", "player_slot").concat([field].filter((f) => subkeys[f])); + queries.getPlayerMatches(req.params.account_id, req.queryObj, (err, cache) => { + if (err) { + return cb(err); } - ); + const buckets = 40; + // Find the maximum value to determine how large each bucket should be + const max = Math.max(...cache.map((m) => m[field])); + // Round the bucket size up to the nearest integer + const bucketSize = Math.ceil((max + 1) / buckets); + const bucketArray = Array.from( + { + length: buckets, + }, + (value, index) => ({ + x: bucketSize * index, + games: 0, + win: 0, + }) + ); + cache.forEach((m) => { + if (m[field] || m[field] === 0) { + const index = Math.floor(m[field] / bucketSize); + if (bucketArray[index]) { + bucketArray[index].games += 1; + bucketArray[index].win += utility.isRadiant(m) === m.radiant_win ? 1 : 0; + } + } + }); + return res.json(bucketArray); + }); } function getPlayersByAccountIdWardMap(req, res, cb) { @@ -451,24 +378,18 @@ function getPlayersByAccountIdWardMap(req, res, cb) { obs: {}, sen: {}, }; - req.queryObj.project = req.queryObj.project.concat( - Object.keys(result) - ); - queries.getPlayerMatches( - req.params.account_id, - req.queryObj, - (err, cache) => { - if (err) { - return cb(err); - } - cache.forEach((m) => { - Object.keys(result).forEach((key) => { - utility.mergeObjects(result[key], m[key]); - }); - }); - return res.json(result); + req.queryObj.project = req.queryObj.project.concat(Object.keys(result)); + queries.getPlayerMatches(req.params.account_id, req.queryObj, (err, cache) => { + if (err) { + return cb(err); } - ); + cache.forEach((m) => { + Object.keys(result).forEach((key) => { + utility.mergeObjects(result[key], m[key]); + }); + }); + return res.json(result); + }); } function getPlayersByAccountIdWordCloud(req, res, cb) { @@ -476,24 +397,18 @@ function getPlayersByAccountIdWordCloud(req, res, cb) { my_word_counts: {}, all_word_counts: {}, }; - req.queryObj.project = req.queryObj.project.concat( - Object.keys(result) - ); - queries.getPlayerMatches( - req.params.account_id, - req.queryObj, - (err, cache) => { - if (err) { - return cb(err); - } - cache.forEach((m) => { - Object.keys(result).forEach((key) => { - utility.mergeObjects(result[key], m[key]); - }); - }); - return res.json(result); + req.queryObj.project = req.queryObj.project.concat(Object.keys(result)); + queries.getPlayerMatches(req.params.account_id, req.queryObj, (err, cache) => { + if (err) { + return cb(err); } - ); + cache.forEach((m) => { + Object.keys(result).forEach((key) => { + utility.mergeObjects(result[key], m[key]); + }); + }); + return res.json(result); + }); } function getPlayersByAccountIdRatings(req, res, cb) { @@ -506,15 +421,12 @@ function getPlayersByAccountIdRatings(req, res, cb) { } function getPlayersByAccountIdRankings(req, res, cb) { - queries.getPlayerHeroRankings( - req.params.account_id, - (err, result) => { - if (err) { - return cb(err); - } - return res.json(result); + queries.getPlayerHeroRankings(req.params.account_id, (err, result) => { + if (err) { + return cb(err); } - ); + return res.json(result); + }); } function getPlayersByAccountIdRefresh(req, res, cb) { @@ -537,11 +449,7 @@ function getPlayersByAccountIdRefresh(req, res, cb) { function getProPlayers(req, res, cb) { db.select() .from("players") - .rightJoin( - "notable_players", - "players.account_id", - "notable_players.account_id" - ) + .rightJoin("notable_players", "players.account_id", "notable_players.account_id") .orderBy("notable_players.account_id", "asc") .asCallback((err, result) => { if (err) { From 2b1ea9550960b8d89acd24c1eaa4e6c0b540b6db Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 21:39:10 +0300 Subject: [PATCH 37/58] :art: refactor: move getTeamsData route handler to teams module --- routes/handlers/teams.js | 22 ++++++++++++++++++++++ routes/spec.js | 20 ++------------------ 2 files changed, 24 insertions(+), 18 deletions(-) create mode 100644 routes/handlers/teams.js diff --git a/routes/handlers/teams.js b/routes/handlers/teams.js new file mode 100644 index 000000000..a9f122a98 --- /dev/null +++ b/routes/handlers/teams.js @@ -0,0 +1,22 @@ +const db = require("../../store/db"); + +function getTeamsData(req, res, cb) { + db.raw( + `SELECT team_rating.*, teams.* + FROM teams + LEFT JOIN team_rating using(team_id) + ORDER BY rating desc NULLS LAST + LIMIT 1000 + OFFSET ?`, + [(Number(req.query.page) || 0) * 1000] + ).asCallback((err, result) => { + if (err) { + return cb(err); + } + return res.json(result.rows); + }); +} + +module.exports = { + getTeamsData, +}; diff --git a/routes/spec.js b/routes/spec.js index 865f47980..66af48723 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -5,8 +5,6 @@ const config = require("../config"); // const uuidV4 = require("uuid/v4"); const queue = require("../store/queue"); const queries = require("../store/queries"); -const search = require("../store/search"); -const searchES = require("../store/searchES"); const buildStatus = require("../store/buildStatus"); // const getGcData = require("../util/getGcData"); const utility = require("../util/utility"); @@ -19,6 +17,7 @@ const generateOperationId = require("./generateOperationId"); const matchesHandler = require("./handlers/matches") const playersHandler = require("./handlers/players"); const heroesHandler = require("./handlers/heroes"); +const teamsHandler = require("./handlers/teams"); const { redisCount, matchupToString } = utility; @@ -1474,22 +1473,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/teams", - func: (req, res, cb) => { - db.raw( - `SELECT team_rating.*, teams.* - FROM teams - LEFT JOIN team_rating using(team_id) - ORDER BY rating desc NULLS LAST - LIMIT 1000 - OFFSET ?`, - [(Number(req.query.page) || 0) * 1000] - ).asCallback((err, result) => { - if (err) { - return cb(err); - } - return res.json(result.rows); - }); - }, + func: teamsHandler.getTeamsData, }, }, "/teams/{team_id}": { From 49a971a17fb030a3497ed09029316f7ed433f765 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 21:39:57 +0300 Subject: [PATCH 38/58] :art: refactor: move getTeamById route handler to teams module --- routes/handlers/teams.js | 16 ++++++++++++++++ routes/spec.js | 15 +-------------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/routes/handlers/teams.js b/routes/handlers/teams.js index a9f122a98..2d78a1d26 100644 --- a/routes/handlers/teams.js +++ b/routes/handlers/teams.js @@ -17,6 +17,22 @@ function getTeamsData(req, res, cb) { }); } +function getTeamById(req, res, cb) { + db.raw( + `SELECT team_rating.*, teams.* + FROM teams + LEFT JOIN team_rating using(team_id) + WHERE teams.team_id = ?`, + [req.params.team_id] + ).asCallback((err, result) => { + if (err) { + return cb(err); + } + return res.json(result.rows[0]); + }); +} + module.exports = { getTeamsData, + getTeamById, }; diff --git a/routes/spec.js b/routes/spec.js index 66af48723..4b349ec5d 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -1496,20 +1496,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/teams/:team_id", - func: (req, res, cb) => { - db.raw( - `SELECT team_rating.*, teams.* - FROM teams - LEFT JOIN team_rating using(team_id) - WHERE teams.team_id = ?`, - [req.params.team_id] - ).asCallback((err, result) => { - if (err) { - return cb(err); - } - return res.json(result.rows[0]); - }); - }, + func: teamsHandler.getTeamById, }, }, "/teams/{team_id}/matches": { From 746d9a99a910c165febde5d89a14d31064240a13 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 21:40:44 +0300 Subject: [PATCH 39/58] :art: refactor: move getMatchesByTeamId route handler to teams module --- routes/handlers/teams.js | 22 ++++++++++++++++++++++ routes/spec.js | 21 +-------------------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/routes/handlers/teams.js b/routes/handlers/teams.js index 2d78a1d26..e603a0a6b 100644 --- a/routes/handlers/teams.js +++ b/routes/handlers/teams.js @@ -32,7 +32,29 @@ function getTeamById(req, res, cb) { }); } +function getMatchesByTeamId(req, res, cb) { + db.raw( + ` + SELECT team_match.match_id, radiant_win, radiant_score, dire_score, team_match.radiant, duration, start_time, leagueid, leagues.name as league_name, cluster, tm2.team_id opposing_team_id, teams2.name opposing_team_name, teams2.logo_url opposing_team_logo + FROM team_match + JOIN matches USING(match_id) + JOIN leagues USING(leagueid) + JOIN team_match tm2 on team_match.match_id = tm2.match_id and team_match.team_id != tm2.team_id + JOIN teams teams2 on tm2.team_id = teams2.team_id + WHERE team_match.team_id = ? + ORDER BY match_id DESC + `, + [req.params.team_id] + ).asCallback((err, result) => { + if (err) { + return cb(err); + } + return res.json(result.rows); + }); +} + module.exports = { getTeamsData, getTeamById, + getMatchesByTeamId, }; diff --git a/routes/spec.js b/routes/spec.js index 4b349ec5d..5f6f324df 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -1519,26 +1519,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/teams/:team_id/matches", - func: (req, res, cb) => { - db.raw( - ` - SELECT team_match.match_id, radiant_win, radiant_score, dire_score, team_match.radiant, duration, start_time, leagueid, leagues.name as league_name, cluster, tm2.team_id opposing_team_id, teams2.name opposing_team_name, teams2.logo_url opposing_team_logo - FROM team_match - JOIN matches USING(match_id) - JOIN leagues USING(leagueid) - JOIN team_match tm2 on team_match.match_id = tm2.match_id and team_match.team_id != tm2.team_id - JOIN teams teams2 on tm2.team_id = teams2.team_id - WHERE team_match.team_id = ? - ORDER BY match_id DESC - `, - [req.params.team_id] - ).asCallback((err, result) => { - if (err) { - return cb(err); - } - return res.json(result.rows); - }); - }, + func: teamsHandler.getMatchesByTeamId, }, }, "/teams/{team_id}/players": { From 2f2a621e2011dfcb11fd77d340ac3f02bc0132ca Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 21:41:29 +0300 Subject: [PATCH 40/58] :art: refactor: move getPlayersByTeamId route handler to teams module --- routes/handlers/teams.js | 21 +++++++++++++++++++++ routes/spec.js | 20 +------------------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/routes/handlers/teams.js b/routes/handlers/teams.js index e603a0a6b..14e0f73b2 100644 --- a/routes/handlers/teams.js +++ b/routes/handlers/teams.js @@ -53,8 +53,29 @@ function getMatchesByTeamId(req, res, cb) { }); } +function getPlayersByTeamId(req, res, cb) { + db.raw( + `SELECT account_id, notable_players.name, count(matches.match_id) games_played, sum(case when (player_matches.player_slot < 128) = matches.radiant_win then 1 else 0 end) wins, notable_players.team_id = teams.team_id is_current_team_member + FROM matches + JOIN team_match USING(match_id) + JOIN player_matches ON player_matches.match_id = matches.match_id AND team_match.radiant = (player_matches.player_slot < 128) + JOIN teams USING (team_id) + LEFT JOIN notable_players USING(account_id) + WHERE teams.team_id = ? + GROUP BY account_id, notable_players.name, notable_players.team_id, teams.team_id + ORDER BY games_played DESC`, + [req.params.team_id] + ).asCallback((err, result) => { + if (err) { + return cb(err); + } + return res.json(result.rows); + }); +} + module.exports = { getTeamsData, getTeamById, getMatchesByTeamId, + getPlayersByTeamId, }; diff --git a/routes/spec.js b/routes/spec.js index 5f6f324df..d8b556109 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -1542,25 +1542,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/teams/:team_id/players", - func: (req, res, cb) => { - db.raw( - `SELECT account_id, notable_players.name, count(matches.match_id) games_played, sum(case when (player_matches.player_slot < 128) = matches.radiant_win then 1 else 0 end) wins, notable_players.team_id = teams.team_id is_current_team_member - FROM matches - JOIN team_match USING(match_id) - JOIN player_matches ON player_matches.match_id = matches.match_id AND team_match.radiant = (player_matches.player_slot < 128) - JOIN teams USING (team_id) - LEFT JOIN notable_players USING(account_id) - WHERE teams.team_id = ? - GROUP BY account_id, notable_players.name, notable_players.team_id, teams.team_id - ORDER BY games_played DESC`, - [req.params.team_id] - ).asCallback((err, result) => { - if (err) { - return cb(err); - } - return res.json(result.rows); - }); - }, + func: teamsHandler.getPlayersByTeamId, }, }, "/teams/{team_id}/heroes": { From e557db7d0aa899c66dcd5b6a09a14ff5d0796be0 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 21:42:10 +0300 Subject: [PATCH 41/58] :art: refactor: move getHeroesByTeamId route handler to teams module --- routes/handlers/teams.js | 21 +++++++++++++++++++++ routes/spec.js | 20 +------------------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/routes/handlers/teams.js b/routes/handlers/teams.js index 14e0f73b2..66c293632 100644 --- a/routes/handlers/teams.js +++ b/routes/handlers/teams.js @@ -73,9 +73,30 @@ function getPlayersByTeamId(req, res, cb) { }); } +function getHeroesByTeamId(req, res, cb) { + db.raw( + `SELECT hero_id, localized_name, count(matches.match_id) games_played, sum(case when (player_matches.player_slot < 128) = matches.radiant_win then 1 else 0 end) wins + FROM matches + JOIN team_match USING(match_id) + JOIN player_matches ON player_matches.match_id = matches.match_id AND team_match.radiant = (player_matches.player_slot < 128) + JOIN teams USING(team_id) + LEFT JOIN heroes ON player_matches.hero_id = heroes.id + WHERE teams.team_id = ? + GROUP BY hero_id, localized_name + ORDER BY games_played DESC`, + [req.params.team_id] + ).asCallback((err, result) => { + if (err) { + return cb(err); + } + return res.json(result.rows); + }); +} + module.exports = { getTeamsData, getTeamById, getMatchesByTeamId, getPlayersByTeamId, + getHeroesByTeamId, }; diff --git a/routes/spec.js b/routes/spec.js index d8b556109..bec64ecc4 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -1565,25 +1565,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/teams/:team_id/heroes", - func: (req, res, cb) => { - db.raw( - `SELECT hero_id, localized_name, count(matches.match_id) games_played, sum(case when (player_matches.player_slot < 128) = matches.radiant_win then 1 else 0 end) wins - FROM matches - JOIN team_match USING(match_id) - JOIN player_matches ON player_matches.match_id = matches.match_id AND team_match.radiant = (player_matches.player_slot < 128) - JOIN teams USING(team_id) - LEFT JOIN heroes ON player_matches.hero_id = heroes.id - WHERE teams.team_id = ? - GROUP BY hero_id, localized_name - ORDER BY games_played DESC`, - [req.params.team_id] - ).asCallback((err, result) => { - if (err) { - return cb(err); - } - return res.json(result.rows); - }); - }, + func: teamsHandler.getHeroesByTeamId, }, }, "/replays": { From 2aa319c35ea01dd68ee381230157b9cd2281511e Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 21:46:44 +0300 Subject: [PATCH 42/58] :art: refactor: move explorer route handler to database module --- routes/handlers/database.js | 26 ++++++++++++++++++++++++++ routes/spec.js | 25 ++----------------------- 2 files changed, 28 insertions(+), 23 deletions(-) create mode 100644 routes/handlers/database.js diff --git a/routes/handlers/database.js b/routes/handlers/database.js new file mode 100644 index 000000000..ad44a91da --- /dev/null +++ b/routes/handlers/database.js @@ -0,0 +1,26 @@ +const { Client } = require("pg"); +const config = require("../../config"); + +async function explorer(req, res) { + // TODO handle NQL (@nicholashh query language) + const input = req.query.sql; + const client = new Client({ + connectionString: config.READONLY_POSTGRES_URL, + statement_timeout: 10000, + }); + client.connect(); + let result = null; + let err = null; + try { + result = await client.query(input); + } catch (e) { + err = e; + } + client.end(); + const final = { ...result, err: err && err.toString() }; + return res.status(err ? 400 : 200).json(final); +} + +module.exports = { + explorer, +}; diff --git a/routes/spec.js b/routes/spec.js index bec64ecc4..e75364bdf 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -1,6 +1,4 @@ const constants = require("dotaconstants"); -const { Client } = require("pg"); -const config = require("../config"); // const crypto = require("crypto"); // const uuidV4 = require("uuid/v4"); const queue = require("../store/queue"); @@ -14,6 +12,7 @@ const packageJson = require("../package.json"); const params = require("./requests/importParams"); const responses = require("./responses/schemas/importResponseSchemas"); const generateOperationId = require("./generateOperationId"); +const databaseHandler = require("./handlers/database") const matchesHandler = require("./handlers/matches") const playersHandler = require("./handlers/players"); const heroesHandler = require("./handlers/heroes"); @@ -687,27 +686,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/explorer", - func: async (req, res) => { - // TODO handle NQL (@nicholashh query language) - const input = req.query.sql; - const client = new Client({ - connectionString: config.READONLY_POSTGRES_URL, - statement_timeout: 10000, - }); - client.connect(); - let result = null; - let err = null; - try { - result = await client.query(input); - } catch (e) { - err = e; - } - client.end(); - const final = Object.assign({}, result, { - err: err && err.toString(), - }); - return res.status(err ? 400 : 200).json(final); - }, + func: databaseHandler.explorer, }, }, "/metadata": { From d93be57daa4f66ddcacc20e75eb71abb911474cf Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 21:51:01 +0300 Subject: [PATCH 43/58] :art: refactor: move getLeagues route handler to leagues module --- routes/handlers/leagues.js | 16 ++++++++++++++++ routes/spec.js | 12 ++---------- 2 files changed, 18 insertions(+), 10 deletions(-) create mode 100644 routes/handlers/leagues.js diff --git a/routes/handlers/leagues.js b/routes/handlers/leagues.js new file mode 100644 index 000000000..35d98188d --- /dev/null +++ b/routes/handlers/leagues.js @@ -0,0 +1,16 @@ +const db = require("../../store/db"); + +function getLeagues(req, res, cb) { + db.select() + .from("leagues") + .asCallback((err, result) => { + if (err) { + return cb(err); + } + return res.json(result); + }); +} + +module.exports = { + getLeagues, +}; diff --git a/routes/spec.js b/routes/spec.js index e75364bdf..564207d0f 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -13,6 +13,7 @@ const params = require("./requests/importParams"); const responses = require("./responses/schemas/importResponseSchemas"); const generateOperationId = require("./generateOperationId"); const databaseHandler = require("./handlers/database") +const leaguesHandler = require("./handlers/leagues") const matchesHandler = require("./handlers/matches") const playersHandler = require("./handlers/players"); const heroesHandler = require("./handlers/heroes"); @@ -1294,16 +1295,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/leagues", - func: (req, res, cb) => { - db.select() - .from("leagues") - .asCallback((err, result) => { - if (err) { - return cb(err); - } - return res.json(result); - }); - }, + func: leaguesHandler.getLeagues, }, }, "/leagues/{league_id}": { From 75b338652686fd875f8ef03845b3fb9b2d5f4b69 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 21:52:48 +0300 Subject: [PATCH 44/58] :art: refactor: move getLeaguesById route handler to leagues module --- routes/handlers/leagues.js | 15 +++++++++++++++ routes/spec.js | 14 +------------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/routes/handlers/leagues.js b/routes/handlers/leagues.js index 35d98188d..f1d1b0270 100644 --- a/routes/handlers/leagues.js +++ b/routes/handlers/leagues.js @@ -11,6 +11,21 @@ function getLeagues(req, res, cb) { }); } +function getLeaguesById(req, res, cb) { + db.raw( + `SELECT leagues.* + FROM leagues + WHERE leagues.leagueid = ?`, + [req.params.league_id] + ).asCallback((err, result) => { + if (err) { + return cb(err); + } + return res.json(result.rows[0]); + }); +} + module.exports = { getLeagues, + getLeaguesById, }; diff --git a/routes/spec.js b/routes/spec.js index 564207d0f..850260bc2 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -1321,19 +1321,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/leagues/:league_id", - func: (req, res, cb) => { - db.raw( - `SELECT leagues.* - FROM leagues - WHERE leagues.leagueid = ?`, - [req.params.league_id] - ).asCallback((err, result) => { - if (err) { - return cb(err); - } - return res.json(result.rows[0]); - }); - }, + func: leaguesHandler.getLeaguesById, }, }, "/leagues/{league_id}/matches": { From c592f823074c6df343adff06a2e352bcc9c133cc Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 21:54:09 +0300 Subject: [PATCH 45/58] :art: refactor: move getMatchesByLeagueId route handler to leagues module --- routes/handlers/leagues.js | 15 +++++++++++++++ routes/spec.js | 14 +------------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/routes/handlers/leagues.js b/routes/handlers/leagues.js index f1d1b0270..479fb5364 100644 --- a/routes/handlers/leagues.js +++ b/routes/handlers/leagues.js @@ -25,7 +25,22 @@ function getLeaguesById(req, res, cb) { }); } +function getMatchesByLeagueId(req, res, cb) { + db.raw( + `SELECT matches.* + FROM matches + WHERE matches.leagueid = ?`, + [req.params.league_id] + ).asCallback((err, result) => { + if (err) { + return cb(err); + } + return res.json(result.rows); + }); +} + module.exports = { getLeagues, getLeaguesById, + getMatchesByLeagueId, }; diff --git a/routes/spec.js b/routes/spec.js index 850260bc2..a63f66267 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -1344,19 +1344,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/leagues/:league_id/matches", - func: (req, res, cb) => { - db.raw( - `SELECT matches.* - FROM matches - WHERE matches.leagueid = ?`, - [req.params.league_id] - ).asCallback((err, result) => { - if (err) { - return cb(err); - } - return res.json(result.rows); - }); - }, + func: leaguesHandler.getMatchesByLeagueId, }, }, "/leagues/{league_id}/teams": { From b805713afc371b697496a71c3c503b18ee6d98fa Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 21:55:07 +0300 Subject: [PATCH 46/58] :art: refactor: move getTeamsByLeagueId route handler to leagues module --- routes/handlers/leagues.js | 19 +++++++++++++++++++ routes/spec.js | 18 +----------------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/routes/handlers/leagues.js b/routes/handlers/leagues.js index 479fb5364..fdec76a69 100644 --- a/routes/handlers/leagues.js +++ b/routes/handlers/leagues.js @@ -39,8 +39,27 @@ function getMatchesByLeagueId(req, res, cb) { }); } +function getTeamsByLeagueId(req, res, cb) { + db.raw( + `SELECT team_rating.*, teams.* + FROM matches + LEFT JOIN team_match using(match_id) + LEFT JOIN teams using(team_id) + LEFT JOIN team_rating using(team_id) + WHERE matches.leagueid = ? + GROUP BY (teams.team_id, team_rating.team_id)`, + [req.params.league_id] + ).asCallback((err, result) => { + if (err) { + return cb(err); + } + return res.json(result.rows); + }); +} + module.exports = { getLeagues, getLeaguesById, getMatchesByLeagueId, + getTeamsByLeagueId, }; diff --git a/routes/spec.js b/routes/spec.js index a63f66267..7ed9763a4 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -1367,23 +1367,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/leagues/:league_id/teams", - func: (req, res, cb) => { - db.raw( - `SELECT team_rating.*, teams.* - FROM matches - LEFT JOIN team_match using(match_id) - LEFT JOIN teams using(team_id) - LEFT JOIN team_rating using(team_id) - WHERE matches.leagueid = ? - GROUP BY (teams.team_id, team_rating.team_id)`, - [req.params.league_id] - ).asCallback((err, result) => { - if (err) { - return cb(err); - } - return res.json(result.rows); - }); - }, + func: leaguesHandler.getTeamsByLeagueId, }, }, "/teams": { From dc34b175d88bc3f8674b3ffe02721e2c5621fa99 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 22:00:27 +0300 Subject: [PATCH 47/58] :art: refactor: move getSchema route handler to database module --- routes/handlers/database.js | 16 ++++++++++++++++ routes/spec.js | 14 +------------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/routes/handlers/database.js b/routes/handlers/database.js index ad44a91da..801ea1df3 100644 --- a/routes/handlers/database.js +++ b/routes/handlers/database.js @@ -1,5 +1,6 @@ const { Client } = require("pg"); const config = require("../../config"); +const db = require("../../store/db"); async function explorer(req, res) { // TODO handle NQL (@nicholashh query language) @@ -21,6 +22,21 @@ async function explorer(req, res) { return res.status(err ? 400 : 200).json(final); } +function getSchema(req, res, cb) { + db.select(["table_name", "column_name", "data_type"]) + .from("information_schema.columns") + .where({ + table_schema: "public", + }) + .asCallback((err, result) => { + if (err) { + return cb(err); + } + return res.json(result); + }); +} + module.exports = { explorer, + getSchema, }; diff --git a/routes/spec.js b/routes/spec.js index 7ed9763a4..6e34ebf32 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -1791,19 +1791,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/schema", - func: (req, res, cb) => { - db.select(["table_name", "column_name", "data_type"]) - .from("information_schema.columns") - .where({ - table_schema: "public", - }) - .asCallback((err, result) => { - if (err) { - return cb(err); - } - return res.json(result); - }); - }, + func: databaseHandler.getSchema, }, }, "/constants/{resource}": { From a9a6e6a72a3668e70df639601168df9ba4b64b1d Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 22:03:29 +0300 Subject: [PATCH 48/58] :art: refactor: move getMmrDistributions route handler to database module --- routes/handlers/database.js | 12 ++++++++++++ routes/spec.js | 9 +-------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/routes/handlers/database.js b/routes/handlers/database.js index 801ea1df3..cadf87d20 100644 --- a/routes/handlers/database.js +++ b/routes/handlers/database.js @@ -1,6 +1,8 @@ const { Client } = require("pg"); const config = require("../../config"); const db = require("../../store/db"); +const queries = require("../../store/queries"); +const redis = require("../../store/redis"); async function explorer(req, res) { // TODO handle NQL (@nicholashh query language) @@ -36,7 +38,17 @@ function getSchema(req, res, cb) { }); } +function getMmrDistributions(req, res, cb) { + queries.getDistributions(redis, (err, result) => { + if (err) { + return cb(err); + } + return res.json(result); + }); +} + module.exports = { explorer, getSchema, + getMmrDistributions, }; diff --git a/routes/spec.js b/routes/spec.js index 6e34ebf32..853966fb1 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -738,14 +738,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/distributions", - func: (req, res, cb) => { - queries.getDistributions(redis, (err, result) => { - if (err) { - return cb(err); - } - return res.json(result); - }); - }, + func: databaseHandler.getMmrDistributions, }, }, "/search": { From 2f0ea1a5b6f43906739e8c6f228ac80b73a7deca Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 22:05:10 +0300 Subject: [PATCH 49/58] :art: refactor: move getBuildStatus route handler to database module --- routes/handlers/database.js | 11 +++++++++++ routes/spec.js | 13 +------------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/routes/handlers/database.js b/routes/handlers/database.js index cadf87d20..d7e5fd60b 100644 --- a/routes/handlers/database.js +++ b/routes/handlers/database.js @@ -1,4 +1,5 @@ const { Client } = require("pg"); +const buildStatus = require("../../store/buildStatus"); const config = require("../../config"); const db = require("../../store/db"); const queries = require("../../store/queries"); @@ -47,8 +48,18 @@ function getMmrDistributions(req, res, cb) { }); } +function getBuildStatus(req, res, cb) { + buildStatus(db, redis, (err, status) => { + if (err) { + return cb(err); + } + return res.json(status); + }); +} + module.exports = { explorer, getSchema, getMmrDistributions, + getBuildStatus, }; diff --git a/routes/spec.js b/routes/spec.js index 853966fb1..9f288d6f1 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -1,10 +1,6 @@ const constants = require("dotaconstants"); -// const crypto = require("crypto"); -// const uuidV4 = require("uuid/v4"); const queue = require("../store/queue"); const queries = require("../store/queries"); -const buildStatus = require("../store/buildStatus"); -// const getGcData = require("../util/getGcData"); const utility = require("../util/utility"); const db = require("../store/db"); const redis = require("../store/redis"); @@ -863,14 +859,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/status", - func: (req, res, cb) => { - buildStatus(db, redis, (err, status) => { - if (err) { - return cb(err); - } - return res.json(status); - }); - }, + func: databaseHandler.getBuildStatus, }, }, "/health": { From 9b4e382fc26646487f48994266e32f163e313560 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 22:07:14 +0300 Subject: [PATCH 50/58] :art: refactor: move getLiveMatches route handler to matches module --- routes/handlers/matches.js | 19 +++++++++++++++++++ routes/spec.js | 18 +----------------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/routes/handlers/matches.js b/routes/handlers/matches.js index 51f388856..a0fc025a0 100644 --- a/routes/handlers/matches.js +++ b/routes/handlers/matches.js @@ -150,10 +150,29 @@ function findMatches(req, res, cb) { }); } +function getLiveMatches(req, res, cb) { + redis.zrangebyscore("liveGames", "-inf", "inf", (err, rows) => { + if (err) { + return cb(err); + } + if (!rows.length) { + return res.json(rows); + } + const keys = rows.map((r) => `liveGame:${r}`); + return redis.mget(keys, (err, rows) => { + if (err) { + return cb(err); + } + return res.json(rows.map((r) => JSON.parse(r))); + }); + }); +} + module.exports = { getMatchById, getProMatches, getPublicMatches, getParsedMatches, findMatches, + getLiveMatches, }; diff --git a/routes/spec.js b/routes/spec.js index 9f288d6f1..69fe4b760 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -1609,23 +1609,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/live", - func: (req, res, cb) => { - redis.zrangebyscore("liveGames", "-inf", "inf", (err, rows) => { - if (err) { - return cb(err); - } - if (!rows.length) { - return res.json(rows); - } - const keys = rows.map((r) => `liveGame:${r}`); - return redis.mget(keys, (err, rows) => { - if (err) { - return cb(err); - } - return res.json(rows.map((r) => JSON.parse(r))); - }); - }); - }, + func: matchesHandler.getLiveMatches, }, }, "/scenarios/itemTimings": { From 9e2f09e42de9d9ee983771278d04da3c67c7d997 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 22:09:33 +0300 Subject: [PATCH 51/58] :art: refactor: move getRecordsByField route handler to database module --- routes/handlers/database.js | 25 +++++++++++++++++++++++++ routes/spec.js | 30 +----------------------------- 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/routes/handlers/database.js b/routes/handlers/database.js index d7e5fd60b..b8da16a7d 100644 --- a/routes/handlers/database.js +++ b/routes/handlers/database.js @@ -57,9 +57,34 @@ function getBuildStatus(req, res, cb) { }); } +function getRecordsByField(req, res, cb) { + redis.zrevrange(`records:${req.params.field}`, 0, 99, "WITHSCORES", (err, rows) => { + if (err) { + return cb(err); + } + const entries = rows + .map((r, i) => { + const matchId = parseInt(r.split(":")[0], 10); + const startTime = parseInt(r.split(":")[1], 10); + const heroId = parseInt(r.split(":")[2], 10); + const score = parseInt(rows[i + 1], 10); + + return { + match_id: Number.isNaN(matchId) ? null : matchId, + start_time: Number.isNaN(startTime) ? null : startTime, + hero_id: Number.isNaN(heroId) ? null : heroId, + score: Number.isNaN(score) ? null : score, + }; + }) + .filter((r, i) => i % 2 === 0); + return res.json(entries); + }); +} + module.exports = { explorer, getSchema, getMmrDistributions, getBuildStatus, + getRecordsByField, }; diff --git a/routes/spec.js b/routes/spec.js index 69fe4b760..021a75c6e 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -1553,35 +1553,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/records/:field", - func: (req, res, cb) => { - redis.zrevrange( - `records:${req.params.field}`, - 0, - 99, - "WITHSCORES", - (err, rows) => { - if (err) { - return cb(err); - } - const entries = rows - .map((r, i) => { - const match_id = parseInt(r.split(":")[0]); - const start_time = parseInt(r.split(":")[1]); - const hero_id = parseInt(r.split(":")[2]); - const score = parseInt(rows[i + 1]); - - return { - match_id: Number.isNaN(match_id) ? null : match_id, - start_time: Number.isNaN(start_time) ? null : start_time, - hero_id: Number.isNaN(hero_id) ? null : hero_id, - score: Number.isNaN(score) ? null : score, - }; - }) - .filter((r, i) => i % 2 === 0); - return res.json(entries); - } - ); - }, + func: databaseHandler.getRecordsByField, }, }, "/live": { From 3446c3985a69fa3bb9760698e7ba44ff2a2e8ccb Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 22:10:43 +0300 Subject: [PATCH 52/58] :art: refactor: move getReplayData route handler to database module --- routes/handlers/database.js | 13 +++++++++++++ routes/spec.js | 15 +-------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/routes/handlers/database.js b/routes/handlers/database.js index b8da16a7d..448fd76ce 100644 --- a/routes/handlers/database.js +++ b/routes/handlers/database.js @@ -57,6 +57,18 @@ function getBuildStatus(req, res, cb) { }); } +function getReplayData(req, res, cb) { + db.select(["match_id", "cluster", "replay_salt"]) + .from("match_gcdata") + .whereIn("match_id", [].concat(req.query.match_id || []).slice(0, 5)) + .asCallback((err, result) => { + if (err) { + return cb(err); + } + return res.json(result); + }); +} + function getRecordsByField(req, res, cb) { redis.zrevrange(`records:${req.params.field}`, 0, 99, "WITHSCORES", (err, rows) => { if (err) { @@ -86,5 +98,6 @@ module.exports = { getSchema, getMmrDistributions, getBuildStatus, + getReplayData, getRecordsByField, }; diff --git a/routes/spec.js b/routes/spec.js index 021a75c6e..59c9ca0fd 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -1504,20 +1504,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/replays", - func: (req, res, cb) => { - db.select(["match_id", "cluster", "replay_salt"]) - .from("match_gcdata") - .whereIn( - "match_id", - [].concat(req.query.match_id || []).slice(0, 5) - ) - .asCallback((err, result) => { - if (err) { - return cb(err); - } - return res.json(result); - }); - }, + func: databaseHandler.getReplayData, }, }, "/records/{field}": { From 29ad7e0bff84a6c200bb483dd712796111c1feb8 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 22:17:48 +0300 Subject: [PATCH 53/58] :art: refactor: move requestParse and getRequestState route handler to database module --- routes/handlers/database.js | 57 ++++++++++++++++++++++++++++++++ routes/spec.js | 65 ++----------------------------------- 2 files changed, 59 insertions(+), 63 deletions(-) diff --git a/routes/handlers/database.js b/routes/handlers/database.js index 448fd76ce..0151d1702 100644 --- a/routes/handlers/database.js +++ b/routes/handlers/database.js @@ -3,7 +3,9 @@ const buildStatus = require("../../store/buildStatus"); const config = require("../../config"); const db = require("../../store/db"); const queries = require("../../store/queries"); +const queue = require("../../store/queue"); const redis = require("../../store/redis"); +const utility = require("../../util/utility"); async function explorer(req, res) { // TODO handle NQL (@nicholashh query language) @@ -93,6 +95,59 @@ function getRecordsByField(req, res, cb) { }); } +function getRequestState(req, res, cb) { + queue.getJob(req.params.jobId, (err, job) => { + if (err) { + return cb(err); + } + if (job) { + return res.json({ ...job, jobId: job.id }); + } + return res.json(null); + }); +} + +function requestParse(req, res) { + const matchId = req.params.match_id; + const match = { + match_id: Number(matchId), + }; + function exitWithJob(err, parseJob) { + if (err) { + console.error(err); + } + res.status(err ? 400 : 200).json({ + job: { + jobId: parseJob && parseJob.id, + }, + }); + } + if (match && match.match_id) { + // match id request, get data from API + return utility.getData(utility.generateJob("api_details", match).url, (err, body) => { + if (err) { + // couldn't get data from api, non-retryable + return exitWithJob(JSON.stringify(err)); + } + // Count this request + utility.redisCount(redis, "request"); + // match details response + const match = body.result; + return queries.insertMatch( + match, + { + type: "api", + attempts: 1, + priority: 1, + forceParse: true, + }, + exitWithJob + ); + }); + } + return exitWithJob("invalid input"); +} + module.exports = { explorer, getSchema, @@ -100,4 +155,6 @@ module.exports = { getBuildStatus, getReplayData, getRecordsByField, + getRequestState, + requestParse, }; diff --git a/routes/spec.js b/routes/spec.js index 59c9ca0fd..0278ca2db 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -1,8 +1,5 @@ const constants = require("dotaconstants"); -const queue = require("../store/queue"); const queries = require("../store/queries"); -const utility = require("../util/utility"); -const db = require("../store/db"); const redis = require("../store/redis"); const packageJson = require("../package.json"); const params = require("./requests/importParams"); @@ -15,8 +12,6 @@ const playersHandler = require("./handlers/players"); const heroesHandler = require("./handlers/heroes"); const teamsHandler = require("./handlers/teams"); -const { redisCount, matchupToString } = utility; - const parameters = Object.values(params).reduce( (acc, category) => ({ ...acc, ...category }), {} @@ -932,21 +927,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/request/:jobId", - func: (req, res, cb) => { - queue.getJob(req.params.jobId, (err, job) => { - if (err) { - return cb(err); - } - if (job) { - return res.json( - Object.assign({}, job, { - jobId: job.id, - }) - ); - } - return res.json(null); - }); - }, + func: databaseHandler.getRequestState, }, }, "/request/{match_id}": { @@ -969,49 +950,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/request/:match_id", - func: (req, res) => { - const matchId = req.params.match_id; - const match = { - match_id: Number(matchId), - }; - function exitWithJob(err, parseJob) { - if (err) { - console.error(err); - } - res.status(err ? 400 : 200).json({ - job: { - jobId: parseJob && parseJob.id, - }, - }); - } - if (match && match.match_id) { - // match id request, get data from API - return utility.getData( - utility.generateJob("api_details", match).url, - (err, body) => { - if (err) { - // couldn't get data from api, non-retryable - return exitWithJob(JSON.stringify(err)); - } - // Count this request - redisCount(redis, "request"); - // match details response - const match = body.result; - return queries.insertMatch( - match, - { - type: "api", - attempts: 1, - priority: 1, - forceParse: true, - }, - exitWithJob - ); - } - ); - } - return exitWithJob("invalid input"); - }, + func: databaseHandler.requestParse, }, }, "/findMatches": { From ab2fee5fb60979b556837257b2cabbe4ca229da7 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 22:25:28 +0300 Subject: [PATCH 54/58] :art: refactor: move getMetadata route handler to database module --- routes/handlers/database.js | 10 ++++++++++ routes/spec.js | 9 +-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/routes/handlers/database.js b/routes/handlers/database.js index 0151d1702..6a3f5b72f 100644 --- a/routes/handlers/database.js +++ b/routes/handlers/database.js @@ -148,11 +148,21 @@ function requestParse(req, res) { return exitWithJob("invalid input"); } +function getMetadata(req, res, cb) { + queries.getMetadata(req, (err, result) => { + if (err) { + return cb(err); + } + return res.json(result); + }); +} + module.exports = { explorer, getSchema, getMmrDistributions, getBuildStatus, + getMetadata, getReplayData, getRecordsByField, getRequestState, diff --git a/routes/spec.js b/routes/spec.js index 0278ca2db..5da39113a 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -700,14 +700,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/metadata", - func: (req, res, cb) => { - queries.getMetadata(req, (err, result) => { - if (err) { - return cb(err); - } - return res.json(result); - }); - }, + func: databaseHandler.getMetadata, }, }, "/distributions": { From 9db977292147379a3d267d124976cf3066f44271 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 22:26:50 +0300 Subject: [PATCH 55/58] :art: refactor: move getHealth route handler to database module --- routes/handlers/database.js | 19 +++++++++++++++++++ routes/spec.js | 19 +------------------ 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/routes/handlers/database.js b/routes/handlers/database.js index 6a3f5b72f..3a86aa219 100644 --- a/routes/handlers/database.js +++ b/routes/handlers/database.js @@ -157,9 +157,28 @@ function getMetadata(req, res, cb) { }); } +function getHealth(req, res, cb) { + redis.hgetall("health", (err, result) => { + if (err) { + return cb(err); + } + const response = result || {}; + Object.keys(response).forEach((key) => { + response[key] = JSON.parse(response[key]); + }); + if (!req.params.metric) { + return res.json(response); + } + const single = response[req.params.metric]; + const healthy = single.metric < single.threshold; + return res.status(healthy ? 200 : 500).json(single); + }); +} + module.exports = { explorer, getSchema, + getHealth, getMmrDistributions, getBuildStatus, getMetadata, diff --git a/routes/spec.js b/routes/spec.js index 5da39113a..7183ef044 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -1,5 +1,4 @@ const constants = require("dotaconstants"); -const queries = require("../store/queries"); const redis = require("../store/redis"); const packageJson = require("../package.json"); const params = require("./requests/importParams"); @@ -870,23 +869,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/health/:metric?", - func: (req, res, cb) => { - redis.hgetall("health", (err, result) => { - if (err) { - return cb(err); - } - const response = result || {}; - Object.keys(response).forEach((key) => { - response[key] = JSON.parse(response[key]); - }); - if (!req.params.metric) { - return res.json(response); - } - const single = response[req.params.metric]; - const healthy = single.metric < single.threshold; - return res.status(healthy ? 200 : 500).json(single); - }); - }, + func: databaseHandler.getHealth, }, }, "/request/{jobId}": { From 463a87a3980d5be056bc4a45dbe4220953e00bcb Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 22:32:11 +0300 Subject: [PATCH 56/58] :art: refactor: move getConstants and getConstantsByResource route handler to database module --- routes/handlers/database.js | 14 ++++++++++++++ routes/spec.js | 12 ++---------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/routes/handlers/database.js b/routes/handlers/database.js index 3a86aa219..c43918a09 100644 --- a/routes/handlers/database.js +++ b/routes/handlers/database.js @@ -71,6 +71,18 @@ function getReplayData(req, res, cb) { }); } +function getConstants(req, res) { + return res.json(Object.keys(constants)); +} + +function getConstantsByResource(req, res, cb) { + const { resource } = req.params; + if (resource in constants) { + return res.json(constants[resource]); + } + return cb(); +} + function getRecordsByField(req, res, cb) { redis.zrevrange(`records:${req.params.field}`, 0, 99, "WITHSCORES", (err, rows) => { if (err) { @@ -177,6 +189,8 @@ function getHealth(req, res, cb) { module.exports = { explorer, + getConstants, + getConstantsByResource, getSchema, getHealth, getMmrDistributions, diff --git a/routes/spec.js b/routes/spec.js index 7183ef044..8a3cf49f1 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -1690,13 +1690,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/constants/:resource?", - func: (req, res, cb) => { - const { resource } = req.params; - if (resource in constants) { - return res.json(constants[resource]); - } - return cb(); - }, + func: databaseHandler.getConstantsByResource, }, }, "/constants": { @@ -1723,9 +1717,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/constants", - func: (req, res) => { - return res.json(Object.keys(constants)); - }, + func: databaseHandler.getConstants, }, }, }, From 6c709db09268b3ad230b9e7afd8e051dcbc1b7c1 Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 22:33:05 +0300 Subject: [PATCH 57/58] :art: refactor: move scenario route handlers to database module --- routes/handlers/database.js | 31 +++++++++++++++++++++++++++++++ routes/spec.js | 27 +++------------------------ 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/routes/handlers/database.js b/routes/handlers/database.js index c43918a09..28c1260e1 100644 --- a/routes/handlers/database.js +++ b/routes/handlers/database.js @@ -1,3 +1,4 @@ +const constants = require("dotaconstants"); const { Client } = require("pg"); const buildStatus = require("../../store/buildStatus"); const config = require("../../config"); @@ -187,6 +188,33 @@ function getHealth(req, res, cb) { }); } +function getItemTimings(req, res, cb) { + queries.getItemTimings(req, (err, result) => { + if (err) { + return cb(err); + } + return res.json(result.rows); + }); +} + +function getLaneRoles(req, res, cb) { + queries.getLaneRoles(req, (err, result) => { + if (err) { + return cb(err); + } + return res.json(result.rows); + }); +} + +function getTeamScenarios(req, res, cb) { + queries.getTeamScenarios(req, (err, result) => { + if (err) { + return cb(err); + } + return res.json(result.rows); + }); +} + module.exports = { explorer, getConstants, @@ -199,5 +227,8 @@ module.exports = { getReplayData, getRecordsByField, getRequestState, + getItemTimings, + getLaneRoles, + getTeamScenarios, requestParse, }; diff --git a/routes/spec.js b/routes/spec.js index 8a3cf49f1..23675d558 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -1521,14 +1521,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/scenarios/itemTimings", - func: (req, res, cb) => { - queries.getItemTimings(req, (err, result) => { - if (err) { - return cb(err); - } - return res.json(result.rows); - }); - }, + func: databaseHandler.getItemTimings, }, }, "/scenarios/laneRoles": { @@ -1565,14 +1558,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/scenarios/laneRoles", - func: (req, res, cb) => { - queries.getLaneRoles(req, (err, result) => { - if (err) { - return cb(err); - } - return res.json(result.rows); - }); - }, + func: databaseHandler.getLaneRoles, }, }, "/scenarios/misc": { @@ -1598,14 +1584,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, route: () => "/scenarios/misc", - func: (req, res, cb) => { - queries.getTeamScenarios(req, (err, result) => { - if (err) { - return cb(err); - } - return res.json(result.rows); - }); - }, + func: databaseHandler.getTeamScenarios, }, }, "/schema": { From 8f169357ce80cce718c7aab80602f3b77191c20a Mon Sep 17 00:00:00 2001 From: ff137 Date: Fri, 15 Sep 2023 22:39:59 +0300 Subject: [PATCH 58/58] :art: --- routes/handlers/database.js | 190 ++++++++++++++++++------------------ routes/handlers/heroes.js | 96 +++++++++--------- routes/handlers/matches.js | 14 +-- routes/handlers/players.js | 6 +- routes/handlers/teams.js | 74 +++++++------- routes/spec.js | 4 +- 6 files changed, 188 insertions(+), 196 deletions(-) diff --git a/routes/handlers/database.js b/routes/handlers/database.js index 28c1260e1..e30c35f6d 100644 --- a/routes/handlers/database.js +++ b/routes/handlers/database.js @@ -28,29 +28,6 @@ async function explorer(req, res) { return res.status(err ? 400 : 200).json(final); } -function getSchema(req, res, cb) { - db.select(["table_name", "column_name", "data_type"]) - .from("information_schema.columns") - .where({ - table_schema: "public", - }) - .asCallback((err, result) => { - if (err) { - return cb(err); - } - return res.json(result); - }); -} - -function getMmrDistributions(req, res, cb) { - queries.getDistributions(redis, (err, result) => { - if (err) { - return cb(err); - } - return res.json(result); - }); -} - function getBuildStatus(req, res, cb) { buildStatus(db, redis, (err, status) => { if (err) { @@ -60,18 +37,6 @@ function getBuildStatus(req, res, cb) { }); } -function getReplayData(req, res, cb) { - db.select(["match_id", "cluster", "replay_salt"]) - .from("match_gcdata") - .whereIn("match_id", [].concat(req.query.match_id || []).slice(0, 5)) - .asCallback((err, result) => { - if (err) { - return cb(err); - } - return res.json(result); - }); -} - function getConstants(req, res) { return res.json(Object.keys(constants)); } @@ -84,6 +49,60 @@ function getConstantsByResource(req, res, cb) { return cb(); } +function getHealth(req, res, cb) { + redis.hgetall("health", (err, result) => { + if (err) { + return cb(err); + } + const response = result || {}; + Object.keys(response).forEach((key) => { + response[key] = JSON.parse(response[key]); + }); + if (!req.params.metric) { + return res.json(response); + } + const single = response[req.params.metric]; + const healthy = single.metric < single.threshold; + return res.status(healthy ? 200 : 500).json(single); + }); +} + +function getItemTimings(req, res, cb) { + queries.getItemTimings(req, (err, result) => { + if (err) { + return cb(err); + } + return res.json(result.rows); + }); +} + +function getLaneRoles(req, res, cb) { + queries.getLaneRoles(req, (err, result) => { + if (err) { + return cb(err); + } + return res.json(result.rows); + }); +} + +function getMetadata(req, res, cb) { + queries.getMetadata(req, (err, result) => { + if (err) { + return cb(err); + } + return res.json(result); + }); +} + +function getMmrDistributions(req, res, cb) { + queries.getDistributions(redis, (err, result) => { + if (err) { + return cb(err); + } + return res.json(result); + }); +} + function getRecordsByField(req, res, cb) { redis.zrevrange(`records:${req.params.field}`, 0, 99, "WITHSCORES", (err, rows) => { if (err) { @@ -108,6 +127,18 @@ function getRecordsByField(req, res, cb) { }); } +function getReplayData(req, res, cb) { + db.select(["match_id", "cluster", "replay_salt"]) + .from("match_gcdata") + .whereIn("match_id", [].concat(req.query.match_id || []).slice(0, 5)) + .asCallback((err, result) => { + if (err) { + return cb(err); + } + return res.json(result); + }); +} + function getRequestState(req, res, cb) { queue.getJob(req.params.jobId, (err, job) => { if (err) { @@ -120,6 +151,29 @@ function getRequestState(req, res, cb) { }); } +function getSchema(req, res, cb) { + db.select(["table_name", "column_name", "data_type"]) + .from("information_schema.columns") + .where({ + table_schema: "public", + }) + .asCallback((err, result) => { + if (err) { + return cb(err); + } + return res.json(result); + }); +} + +function getTeamScenarios(req, res, cb) { + queries.getTeamScenarios(req, (err, result) => { + if (err) { + return cb(err); + } + return res.json(result.rows); + }); +} + function requestParse(req, res) { const matchId = req.params.match_id; const match = { @@ -161,74 +215,20 @@ function requestParse(req, res) { return exitWithJob("invalid input"); } -function getMetadata(req, res, cb) { - queries.getMetadata(req, (err, result) => { - if (err) { - return cb(err); - } - return res.json(result); - }); -} - -function getHealth(req, res, cb) { - redis.hgetall("health", (err, result) => { - if (err) { - return cb(err); - } - const response = result || {}; - Object.keys(response).forEach((key) => { - response[key] = JSON.parse(response[key]); - }); - if (!req.params.metric) { - return res.json(response); - } - const single = response[req.params.metric]; - const healthy = single.metric < single.threshold; - return res.status(healthy ? 200 : 500).json(single); - }); -} - -function getItemTimings(req, res, cb) { - queries.getItemTimings(req, (err, result) => { - if (err) { - return cb(err); - } - return res.json(result.rows); - }); -} - -function getLaneRoles(req, res, cb) { - queries.getLaneRoles(req, (err, result) => { - if (err) { - return cb(err); - } - return res.json(result.rows); - }); -} - -function getTeamScenarios(req, res, cb) { - queries.getTeamScenarios(req, (err, result) => { - if (err) { - return cb(err); - } - return res.json(result.rows); - }); -} - module.exports = { explorer, + getBuildStatus, getConstants, getConstantsByResource, - getSchema, getHealth, - getMmrDistributions, - getBuildStatus, + getItemTimings, + getLaneRoles, getMetadata, - getReplayData, + getMmrDistributions, getRecordsByField, + getReplayData, getRequestState, - getItemTimings, - getLaneRoles, + getSchema, getTeamScenarios, requestParse, }; diff --git a/routes/handlers/heroes.js b/routes/handlers/heroes.js index d0e4f6183..38e2c9901 100644 --- a/routes/handlers/heroes.js +++ b/routes/handlers/heroes.js @@ -19,15 +19,6 @@ function getHeroBenchmarks(req, res, cb) { ); } -function getHeroRankings(req, res, cb) { - queries.getHeroRankings(db, redis, req.query.hero_id, {}, (err, result) => { - if (err) { - return cb(err); - } - return res.json(result); - }); -} - function getHeroData(req, res, cb) { db.select() .from("heroes") @@ -40,6 +31,15 @@ function getHeroData(req, res, cb) { }); } +function getHeroRankings(req, res, cb) { + queries.getHeroRankings(db, redis, req.query.hero_id, {}, (err, result) => { + if (err) { + return cb(err); + } + return res.json(result); + }); +} + function getHeroStats(req, res, cb) { // fetch from cached redis value redis.get("heroStats", (err, result) => { @@ -50,29 +50,27 @@ function getHeroStats(req, res, cb) { }); } -function getRecentMatchesByHeroId(req, res, cb) { +function getItemPopularityByHeroId(req, res, cb) { + const heroId = req.params.hero_id; + queries.getHeroItemPopularity(db, redis, heroId, {}, (err, result) => { + if (err) { + return cb(err); + } + return res.json(result); + }); +} + +function getMatchDurationsByHeroId(req, res, cb) { const heroId = req.params.hero_id; db.raw( `SELECT - matches.match_id, - matches.start_time, - matches.duration, - matches.radiant_win, - matches.leagueid, - leagues.name as league_name, - ((player_matches.player_slot < 128) = matches.radiant_win) radiant, - player_matches.player_slot, - player_matches.account_id, - player_matches.kills, - player_matches.deaths, - player_matches.assists + (matches.duration / 300 * 300) duration_bin, + count(match_id) games_played, + sum(case when (player_matches.player_slot < 128) = matches.radiant_win then 1 else 0 end) wins FROM matches JOIN player_matches using(match_id) - JOIN leagues using(leagueid) - LEFT JOIN heroes on heroes.id = player_matches.hero_id WHERE player_matches.hero_id = ? - ORDER BY matches.match_id DESC - LIMIT 100`, + GROUP BY (matches.duration / 300 * 300)`, [heroId] ).asCallback((err, result) => { if (err) { @@ -105,17 +103,18 @@ function getMatchupsByHeroId(req, res, cb) { }); } -function getMatchDurationsByHeroId(req, res, cb) { +function getPlayersByHeroId(req, res, cb) { const heroId = req.params.hero_id; db.raw( `SELECT - (matches.duration / 300 * 300) duration_bin, + account_id, count(match_id) games_played, sum(case when (player_matches.player_slot < 128) = matches.radiant_win then 1 else 0 end) wins FROM matches JOIN player_matches using(match_id) WHERE player_matches.hero_id = ? - GROUP BY (matches.duration / 300 * 300)`, + GROUP BY account_id + ORDER BY games_played DESC`, [heroId] ).asCallback((err, result) => { if (err) { @@ -125,18 +124,29 @@ function getMatchDurationsByHeroId(req, res, cb) { }); } -function getPlayersByHeroId(req, res, cb) { +function getRecentMatchesByHeroId(req, res, cb) { const heroId = req.params.hero_id; db.raw( `SELECT - account_id, - count(match_id) games_played, - sum(case when (player_matches.player_slot < 128) = matches.radiant_win then 1 else 0 end) wins + matches.match_id, + matches.start_time, + matches.duration, + matches.radiant_win, + matches.leagueid, + leagues.name as league_name, + ((player_matches.player_slot < 128) = matches.radiant_win) radiant, + player_matches.player_slot, + player_matches.account_id, + player_matches.kills, + player_matches.deaths, + player_matches.assists FROM matches JOIN player_matches using(match_id) + JOIN leagues using(leagueid) + LEFT JOIN heroes on heroes.id = player_matches.hero_id WHERE player_matches.hero_id = ? - GROUP BY account_id - ORDER BY games_played DESC`, + ORDER BY matches.match_id DESC + LIMIT 100`, [heroId] ).asCallback((err, result) => { if (err) { @@ -146,24 +156,14 @@ function getPlayersByHeroId(req, res, cb) { }); } -function getItemPopularityByHeroId(req, res, cb) { - const heroId = req.params.hero_id; - queries.getHeroItemPopularity(db, redis, heroId, {}, (err, result) => { - if (err) { - return cb(err); - } - return res.json(result); - }); -} - module.exports = { getHeroBenchmarks, getHeroRankings, getHeroData, getHeroStats, - getRecentMatchesByHeroId, - getMatchupsByHeroId, + getItemPopularityByHeroId, getMatchDurationsByHeroId, + getMatchupsByHeroId, getPlayersByHeroId, - getItemPopularityByHeroId, + getRecentMatchesByHeroId, }; diff --git a/routes/handlers/matches.js b/routes/handlers/matches.js index a0fc025a0..30d7746d5 100644 --- a/routes/handlers/matches.js +++ b/routes/handlers/matches.js @@ -45,8 +45,7 @@ function getProMatches(req, res, cb) { } async function getPublicMatches(req, res, cb) { - const currMax = - (await db("public_matches").max("match_id").first()).max || 0; + const currMax = (await db("public_matches").max("match_id").first()).max || 0; const lessThan = Number(req.query.less_than_match_id) || currMax; let moreThan = lessThan - 1000000; let order = ""; @@ -58,12 +57,8 @@ async function getPublicMatches(req, res, cb) { order = "ORDER BY match_id DESC"; moreThan = 0; } - const minRank = req.query.min_rank - ? `AND avg_rank_tier >= ${req.query.min_rank}` - : ""; - const maxRank = req.query.max_rank - ? `AND avg_rank_tier <= ${req.query.max_rank}` - : ""; + const minRank = req.query.min_rank ? `AND avg_rank_tier >= ${req.query.min_rank}` : ""; + const maxRank = req.query.max_rank ? `AND avg_rank_tier <= ${req.query.max_rank}` : ""; db.raw( ` @@ -96,8 +91,7 @@ async function getPublicMatches(req, res, cb) { } function getParsedMatches(req, res, cb) { - const lessThan = - req.query.less_than_match_id || Number.MAX_SAFE_INTEGER; + const lessThan = req.query.less_than_match_id || Number.MAX_SAFE_INTEGER; db.raw( ` diff --git a/routes/handlers/players.js b/routes/handlers/players.js index 98c1532d8..c17931de6 100644 --- a/routes/handlers/players.js +++ b/routes/handlers/players.js @@ -185,9 +185,9 @@ function getPlayersByAccountIdHeroes(req, res, cb) { const heroes = {}; // prefill heroes with every hero Object.keys(constants.heroes).forEach((heroId) => { - hero_id_int = parseInt(heroId); + const heroIdInt = parseInt(heroId, 10); const hero = { - hero_id: hero_id_int, + hero_id: heroIdInt, last_played: 0, games: 0, win: 0, @@ -196,7 +196,7 @@ function getPlayersByAccountIdHeroes(req, res, cb) { against_games: 0, against_win: 0, }; - heroes[hero_id_int] = hero; + heroes[heroIdInt] = hero; }); req.queryObj.project = req.queryObj.project.concat("heroes", "account_id", "start_time", "player_slot", "radiant_win"); queries.getPlayerMatches(req.params.account_id, req.queryObj, (err, cache) => { diff --git a/routes/handlers/teams.js b/routes/handlers/teams.js index 66c293632..509ba7c89 100644 --- a/routes/handlers/teams.js +++ b/routes/handlers/teams.js @@ -1,34 +1,22 @@ const db = require("../../store/db"); -function getTeamsData(req, res, cb) { - db.raw( - `SELECT team_rating.*, teams.* - FROM teams - LEFT JOIN team_rating using(team_id) - ORDER BY rating desc NULLS LAST - LIMIT 1000 - OFFSET ?`, - [(Number(req.query.page) || 0) * 1000] - ).asCallback((err, result) => { - if (err) { - return cb(err); - } - return res.json(result.rows); - }); -} - -function getTeamById(req, res, cb) { +function getHeroesByTeamId(req, res, cb) { db.raw( - `SELECT team_rating.*, teams.* - FROM teams - LEFT JOIN team_rating using(team_id) - WHERE teams.team_id = ?`, + `SELECT hero_id, localized_name, count(matches.match_id) games_played, sum(case when (player_matches.player_slot < 128) = matches.radiant_win then 1 else 0 end) wins + FROM matches + JOIN team_match USING(match_id) + JOIN player_matches ON player_matches.match_id = matches.match_id AND team_match.radiant = (player_matches.player_slot < 128) + JOIN teams USING(team_id) + LEFT JOIN heroes ON player_matches.hero_id = heroes.id + WHERE teams.team_id = ? + GROUP BY hero_id, localized_name + ORDER BY games_played DESC`, [req.params.team_id] ).asCallback((err, result) => { if (err) { return cb(err); } - return res.json(result.rows[0]); + return res.json(result.rows); }); } @@ -73,18 +61,15 @@ function getPlayersByTeamId(req, res, cb) { }); } -function getHeroesByTeamId(req, res, cb) { +function getTeamsData(req, res, cb) { db.raw( - `SELECT hero_id, localized_name, count(matches.match_id) games_played, sum(case when (player_matches.player_slot < 128) = matches.radiant_win then 1 else 0 end) wins - FROM matches - JOIN team_match USING(match_id) - JOIN player_matches ON player_matches.match_id = matches.match_id AND team_match.radiant = (player_matches.player_slot < 128) - JOIN teams USING(team_id) - LEFT JOIN heroes ON player_matches.hero_id = heroes.id - WHERE teams.team_id = ? - GROUP BY hero_id, localized_name - ORDER BY games_played DESC`, - [req.params.team_id] + `SELECT team_rating.*, teams.* + FROM teams + LEFT JOIN team_rating using(team_id) + ORDER BY rating desc NULLS LAST + LIMIT 1000 + OFFSET ?`, + [(Number(req.query.page) || 0) * 1000] ).asCallback((err, result) => { if (err) { return cb(err); @@ -93,10 +78,25 @@ function getHeroesByTeamId(req, res, cb) { }); } +function getTeamById(req, res, cb) { + db.raw( + `SELECT team_rating.*, teams.* + FROM teams + LEFT JOIN team_rating using(team_id) + WHERE teams.team_id = ?`, + [req.params.team_id] + ).asCallback((err, result) => { + if (err) { + return cb(err); + } + return res.json(result.rows[0]); + }); +} + module.exports = { - getTeamsData, - getTeamById, + getHeroesByTeamId, getMatchesByTeamId, getPlayersByTeamId, - getHeroesByTeamId, + getTeamsData, + getTeamById, }; diff --git a/routes/spec.js b/routes/spec.js index 23675d558..a9fa265b9 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -1,14 +1,12 @@ -const constants = require("dotaconstants"); -const redis = require("../store/redis"); const packageJson = require("../package.json"); const params = require("./requests/importParams"); const responses = require("./responses/schemas/importResponseSchemas"); const generateOperationId = require("./generateOperationId"); const databaseHandler = require("./handlers/database") +const heroesHandler = require("./handlers/heroes"); const leaguesHandler = require("./handlers/leagues") const matchesHandler = require("./handlers/matches") const playersHandler = require("./handlers/players"); -const heroesHandler = require("./handlers/heroes"); const teamsHandler = require("./handlers/teams"); const parameters = Object.values(params).reduce(