diff --git a/routes/recordFields.js b/routes/recordFields.js new file mode 100644 index 000000000..e79ea6500 --- /dev/null +++ b/routes/recordFields.js @@ -0,0 +1,32 @@ +module.exports = [ + 'kills', + 'actions_per_min', + 'assists', + 'comeback', + 'courier_kills', + 'deaths', + 'denies', + 'duration', + 'lane_efficiency_pct', + 'purchase_gem', + 'gold_per_min', + 'hero_damage', + 'hero_healing', + 'kda', + 'last_hits', + 'level', + 'loss', + 'pings', + 'neutral_kills', + 'purchase_ward_observer', + 'purchase_rapier', + 'purchase_ward_sentry', + 'stomp', + 'stuns', + 'throw', + 'tower_damage', + 'tower_kills', + 'purchase_tpscroll', + 'win_rate', + 'xp_per_min', +]; diff --git a/routes/spec.js b/routes/spec.js index 6c7747ba6..aecb398d1 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -1551,6 +1551,14 @@ The OpenDota API provides Dota 2 related data including advanced match data extr description: 'personaname', type: 'string', }, + name: { + description: 'name', + type: 'string', + }, + is_contributor: { + description: 'is_contributor', + type: 'boolean', + }, last_login: { description: 'last_login', type: 'string', @@ -2121,6 +2129,137 @@ The OpenDota API provides Dota 2 related data including advanced match data extr }, }, }, + '/players/{account_id}/records': { + get: { + summary: 'GET /players/{account_id}/records', + description: 'Player records', + tags: [ + 'players', + ], + parameters: [params.accountIdParam], + responses: { + 200: { + description: 'Success', + schema: { + type: 'object', + properties: { + hero_healing: { + type: 'object', + properties: { + hero_healing: { + description: 'hero_healing', + type: 'integer', + }, + hero_id: { + description: 'hero_id', + type: 'integer', + }, + start_time: { + description: 'start_time', + type: 'integer', + }, + }, + }, + gold_per_min: { + type: 'object', + properties: { + gold_per_min: { + description: 'gold_per_min', + type: 'integer', + }, + hero_id: { + description: 'hero_id', + type: 'integer', + }, + start_time: { + description: 'start_time', + type: 'integer', + }, + }, + }, + kda: { + type: 'object', + properties: { + kda: { + description: 'kda', + type: 'integer', + }, + hero_id: { + description: 'hero_id', + type: 'integer', + }, + start_time: { + description: 'start_time', + type: 'integer', + }, + }, + }, + last_hits: { + type: 'object', + properties: { + last_hits: { + description: 'last_hits', + type: 'integer', + }, + hero_id: { + description: 'hero_id', + type: 'integer', + }, + start_time: { + description: 'start_time', + type: 'integer', + }, + }, + }, + neutral_kills: { + type: 'object', + properties: { + neutral_kills: { + description: 'neutral_kills', + type: 'integer', + }, + hero_id: { + description: 'hero_id', + type: 'integer', + }, + start_time: { + description: 'start_time', + type: 'integer', + }, + }, + }, + pings: { + type: 'object', + properties: { + pings: { + description: 'pings', + type: 'integer', + }, + hero_id: { + description: 'hero_id', + type: 'integer', + }, + start_time: { + description: 'start_time', + type: 'integer', + }, + }, + }, + }, + }, + }, + }, + route: () => '/players/:account_id/records', + func: (req, res, cb) => { + queries.getPlayerRecords(req.params.account_id, (err, records) => { + if (err) { + return cb(err); + } + return res.json(records); + }); + }, + }, + }, '/players/{account_id}/refresh': { post: { summary: 'POST /players/{account_id}/refresh', diff --git a/store/queries.js b/store/queries.js index 61fa86618..e57ceb2ef 100644 --- a/store/queries.js +++ b/store/queries.js @@ -16,9 +16,10 @@ const { es, INDEX } = require('../store/elasticsearch'); const cassandra = require('../store/cassandra'); const cacheFunctions = require('./cacheFunctions'); const benchmarksUtil = require('../util/benchmarksUtil'); +const recordFields = require('../routes/recordFields'); const { - redisCount, convert64to32, serialize, deserialize, isRadiant, + redisCount, convert64to32, serialize, deserialize, isRadiant, isContributor, } = utility; const { computeMatchData } = compute; const columnInfo = {}; @@ -376,18 +377,24 @@ function getPeers(db, input, player, cb) { // limit to 200 max players teammatesArr = teammatesArr.slice(0, 200); return async.each(teammatesArr, (t, cb) => { - db.first().from('players').where({ - account_id: t.account_id, - }).asCallback((err, row) => { - if (err || !row) { + db.first('players.account_id', 'personaname', 'name', 'avatar', 'avatarfull') + .from('players') + .leftJoin('notable_players', 'players.account_id', 'notable_players.account_id') + .where({ + 'players.account_id': t.account_id, + }) + .asCallback((err, row) => { + if (err || !row) { + return cb(err); + } + t.personaname = row.personaname; + t.name = row.name; + t.is_contributor = isContributor(t.account_id); + t.last_login = row.last_login; + t.avatar = row.avatar; + t.avatarfull = row.avatarfull; return cb(err); - } - t.personaname = row.personaname; - t.last_login = row.last_login; - t.avatar = row.avatar; - t.avatarfull = row.avatarfull; - return cb(err); - }); + }); }, (err) => { cb(err, teammatesArr); }); @@ -451,6 +458,29 @@ function getMatchRankTier(match, cb) { }); } +function getPlayerRecords(player, cb) { + const obj = {}; + async.each(recordFields, (field, done) => { + const queryObj = { + sort: field, + limit: 1, + project: [field, 'hero_id', 'start_time'], + }; + getPlayerMatches(player, queryObj, (err, cache) => { + if (err) { + return cb(err); + } + [obj[field]] = cache; + return done(); + }); + }, (err) => { + if (err) { + return cb(err); + } + return cb(null, obj); + }); +} + function upsert(db, table, row, conflict, cb) { cleanRowPostgres(db, table, row, (err, row) => { if (err) { @@ -1175,6 +1205,7 @@ module.exports = { getPlayerMatches, getPlayerRatings, getPlayerHeroRankings, + getPlayerRecords, getPlayer, getMmrEstimate, getPeers,