From 80534a5ca39e87ecfd850c1755f2cfa31ff1b1f1 Mon Sep 17 00:00:00 2001 From: Valentin Kaelin Date: Sat, 8 Jan 2022 00:16:49 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20add=20filter=20by=20season=20back=20?= =?UTF-8?q?=E2=9C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/Http/SummonersController.ts | 34 +++-- server/app/Repositories/MatchRepository.ts | 136 ++++++++++++------ server/app/Services/StatsService.ts | 17 ++- 3 files changed, 125 insertions(+), 62 deletions(-) diff --git a/server/app/Controllers/Http/SummonersController.ts b/server/app/Controllers/Http/SummonersController.ts index 3f6bf78..fd585d9 100644 --- a/server/app/Controllers/Http/SummonersController.ts +++ b/server/app/Controllers/Http/SummonersController.ts @@ -15,6 +15,19 @@ import SummonerOverviewValidator from 'App/Validators/SummonerOverviewValidator' import SummonerRecordValidator from 'App/Validators/SummonerRecordValidator' export default class SummonersController { + /** + * Get all played seasons for a summoner + * @param puuid of the summoner + */ + private async getSeasons(puuid: string): Promise { + const seasons = await MatchRepository.seasons(puuid) + return seasons.length ? seasons.map((s) => s.season) : [getCurrentSeason()] + } + + /** + * POST: get basic summoner data + * @param ctx + */ public async basic({ request, response }: HttpContextContract) { console.time('BASIC_REQUEST') const { summoner, region } = await request.validate(SummonerBasicValidator) @@ -39,7 +52,7 @@ export default class SummonersController { // All seasons the summoner has played // TODO: check if there is a way to do that with V5... - finalJSON.seasons = [getCurrentSeason()] + finalJSON.seasons = await this.getSeasons(account.puuid) // CURRENT GAME console.time('playing') @@ -65,6 +78,10 @@ export default class SummonersController { return response.json(finalJSON) } + /** + * POST: get overview view summoner data + * @param ctx + */ public async overview({ request, response }: HttpContextContract) { console.time('OVERVIEW_REQUEST') const { puuid, region, season } = await request.validate(SummonerOverviewValidator) @@ -74,12 +91,13 @@ export default class SummonersController { const summonerDB = await Summoner.firstOrCreate({ puuid: puuid }) // MATCHES BASIC - const matchlist = await summonerDB - .related('matchList') - .query() - .select('matchId') - .orderBy('matchId', 'desc') - .limit(10) + const matchListQuery = summonerDB.related('matchList').query().select('matchId') + if (season) { + matchListQuery + .join('matches', 'summoner_matchlist.match_id', 'matches.id') + .where('matches.season', season) + } + const matchlist = await matchListQuery.orderBy('matchId', 'desc').limit(10) const matchIds = matchlist.map((m) => m.matchId) finalJSON.matchesDetails = await MatchService.getMatches(region, matchIds, puuid) @@ -117,7 +135,7 @@ export default class SummonersController { public async records({ request, response }: HttpContextContract) { console.time('recordsRequest') const { puuid, season } = await request.validate(SummonerRecordValidator) - const records = await MatchRepository.records(puuid) + const records = await MatchRepository.records(puuid, season) const recordsSerialized = records.map((record) => { return { ...record, diff --git a/server/app/Repositories/MatchRepository.ts b/server/app/Repositories/MatchRepository.ts index caaece7..2995c2e 100644 --- a/server/app/Repositories/MatchRepository.ts +++ b/server/app/Repositories/MatchRepository.ts @@ -1,16 +1,44 @@ import Database from '@ioc:Adonis/Lucid/Database' +export interface SelectFilters { + puuid: string + season?: number + limit?: number + queue?: number +} + class MatchRepository { private readonly JOIN_MATCHES = 'INNER JOIN matches ON matches.id = match_players.match_id' private readonly JOIN_TEAMS = 'INNER JOIN match_teams ON match_players.match_id = match_teams.match_id AND match_players.team = match_teams.color' private readonly JOIN_ALL = `${this.JOIN_MATCHES} ${this.JOIN_TEAMS}` - private readonly GLOBAL_FILTERS = ` + private globalFilters(filters: SelectFilters) { + let query = ` match_players.summoner_puuid = :puuid AND match_players.remake = 0 AND matches.gamemode NOT IN (800, 810, 820, 830, 840, 850, 2000, 2010, 2020) - ` + ` + + if (filters.season) query += ' AND matches.season = :season ' + if (filters.queue) query += ' AND matches.gamemode = :queue ' + + return query + } + + public async seasons(puuid: string) { + const query = ` + SELECT DISTINCT + matches.season + FROM + match_players + ${this.JOIN_MATCHES} + WHERE + match_players.summoner_puuid = :puuid + ` + const { rows } = await Database.rawQuery(query, { puuid }) + return rows + } public async recentActivity(puuid: string) { const query = ` @@ -32,7 +60,7 @@ class MatchRepository { return rows } - public async globalStats(puuid: string) { + public async globalStats(filters: SelectFilters) { const query = ` SELECT SUM(match_players.kills) as kills, @@ -49,15 +77,16 @@ class MatchRepository { match_players ${this.JOIN_MATCHES} WHERE - ${this.GLOBAL_FILTERS} + ${this.globalFilters(filters)} LIMIT 1 ` - const { rows } = await Database.rawQuery(query, { puuid }) + + const { rows } = await Database.rawQuery(query, filters as any) return rows[0] } - public async gamemodeStats(puuid: string) { + public async gamemodeStats(filters: SelectFilters) { const query = ` SELECT matches.gamemode as id, @@ -68,17 +97,18 @@ class MatchRepository { match_players ${this.JOIN_MATCHES} WHERE - ${this.GLOBAL_FILTERS} + ${this.globalFilters(filters)} GROUP BY matches.gamemode ORDER BY count DESC ` - const { rows } = await Database.rawQuery(query, { puuid }) + + const { rows } = await Database.rawQuery(query, filters as any) return rows } - public async roleStats(puuid: string) { + public async roleStats(filters: SelectFilters) { const query = ` SELECT match_players.team_position as role, @@ -89,16 +119,17 @@ class MatchRepository { match_players ${this.JOIN_MATCHES} WHERE - ${this.GLOBAL_FILTERS} + ${this.globalFilters(filters)} AND match_players.team_position != 0 GROUP BY role ` - const { rows } = await Database.rawQuery(query, { puuid }) + + const { rows } = await Database.rawQuery(query, filters as any) return rows } - public async championStats(puuid: string, limit: number) { + public async championStats(filters: SelectFilters) { const query = ` SELECT match_players.champion_id as id, @@ -112,7 +143,7 @@ class MatchRepository { match_players ${this.JOIN_MATCHES} WHERE - ${this.GLOBAL_FILTERS} + ${this.globalFilters(filters)} GROUP BY match_players.champion_id ORDER BY @@ -120,11 +151,12 @@ class MatchRepository { LIMIT :limit ` - const { rows } = await Database.rawQuery(query, { puuid, limit }) + + const { rows } = await Database.rawQuery(query, filters as any) return rows } - public async championClassStats(puuid: string) { + public async championClassStats(filters: SelectFilters) { const query = ` SELECT match_players.champion_role as id, @@ -135,17 +167,49 @@ class MatchRepository { match_players ${this.JOIN_MATCHES} WHERE - ${this.GLOBAL_FILTERS} + ${this.globalFilters(filters)} GROUP BY match_players.champion_role ORDER BY count DESC ` - const { rows } = await Database.rawQuery(query, { puuid }) + + const { rows } = await Database.rawQuery(query, filters as any) return rows } + public async mates(filters: SelectFilters) { + const query = ` + SELECT + (array_agg(mates.summoner_name ORDER BY mates.match_id DESC))[1] as name, + COUNT(match_players.id) as count, + SUM(match_players.win) as wins, + SUM(match_players.loss) as losses + FROM + match_players + ${this.JOIN_ALL} + INNER JOIN match_players as mates ON match_players.match_id = mates.match_id AND match_players.team = mates.team + WHERE + ${this.globalFilters(filters)} + GROUP BY + mates.summoner_puuid + ORDER BY + count DESC, wins DESC + LIMIT + 15 + ` + + const { rows } = await Database.rawQuery(query, filters as any) + + // Remove the Summoner himself + unique game mates + return rows.splice(1).filter((row) => row.count > 1) + } + public async championCompleteStats(puuid: string, queue?: number, season?: number) { + const filters: SelectFilters = { puuid } + if (queue) filters.queue = queue + if (season) filters.season = season + const query = ` SELECT match_players.champion_id as id, @@ -166,43 +230,21 @@ class MatchRepository { match_players ${this.JOIN_MATCHES} WHERE - ${this.GLOBAL_FILTERS} + ${this.globalFilters(filters)} GROUP BY match_players.champion_id ORDER BY count DESC, match_players.champion_id ` - const { rows } = await Database.rawQuery(query, { puuid }) + + const { rows } = await Database.rawQuery(query, filters as any) return rows } - public async mates(puuid: string) { - const query = ` - SELECT - (array_agg(mates.summoner_name ORDER BY mates.match_id DESC))[1] as name, - COUNT(match_players.id) as count, - SUM(match_players.win) as wins, - SUM(match_players.loss) as losses - FROM - match_players - ${this.JOIN_ALL} - INNER JOIN match_players as mates ON match_players.match_id = mates.match_id AND match_players.team = mates.team - WHERE - ${this.GLOBAL_FILTERS} - GROUP BY - mates.summoner_puuid - ORDER BY - count DESC, wins DESC - LIMIT - 15 - ` - const { rows } = await Database.rawQuery(query, { puuid }) + public async records(puuid: string, season?: number) { + const filters: SelectFilters = { puuid } + if (season) filters.season = season - // Remove the Summoner himself + unique game mates - return rows.splice(1).filter((row) => row.count > 1) - } - - public async records(puuid: string) { const fields = [ 'match_players.kills', 'match_players.deaths', @@ -242,7 +284,7 @@ class MatchRepository { match_players ${this.JOIN_MATCHES} WHERE - ${this.GLOBAL_FILTERS} + ${this.globalFilters(filters)} ORDER BY ${field} DESC, matches.id LIMIT @@ -251,7 +293,7 @@ class MatchRepository { }) .join('UNION ALL ') - const { rows } = await Database.rawQuery(query, { puuid }) + const { rows } = await Database.rawQuery(query, filters as any) return rows } } diff --git a/server/app/Services/StatsService.ts b/server/app/Services/StatsService.ts index 43b271f..53b4bf9 100644 --- a/server/app/Services/StatsService.ts +++ b/server/app/Services/StatsService.ts @@ -1,6 +1,6 @@ import { sortTeamByRole } from 'App/helpers' import { ChampionRoles, TeamPosition } from 'App/Parsers/ParsedType' -import MatchRepository from 'App/Repositories/MatchRepository' +import MatchRepository, { SelectFilters } from 'App/Repositories/MatchRepository' import BasicMatchSerializer from 'App/Serializers/BasicMatchSerializer' class StatsService { @@ -11,14 +11,17 @@ class StatsService { return recentActivity } public async getSummonerStats(puuid: string, season?: number) { + const filters: SelectFilters = { puuid } + if (season) filters.season = season + console.time('GLOBAL') - const globalStats = await MatchRepository.globalStats(puuid) + const globalStats = await MatchRepository.globalStats(filters) console.timeEnd('GLOBAL') console.time('GAMEMODE') - const gamemodeStats = await MatchRepository.gamemodeStats(puuid) + const gamemodeStats = await MatchRepository.gamemodeStats(filters) console.timeEnd('GAMEMODE') console.time('ROLE') - const roleStats = await MatchRepository.roleStats(puuid) + const roleStats = await MatchRepository.roleStats(filters) // Check if all roles are in the array const roles = ['TOP', 'JUNGLE', 'MIDDLE', 'BOTTOM', 'UTILITY'] for (const role of roles) { @@ -36,19 +39,19 @@ class StatsService { } console.timeEnd('ROLE') console.time('CHAMPION') - const championStats = await MatchRepository.championStats(puuid, 5) + const championStats = await MatchRepository.championStats({ ...filters, limit: 5 }) for (const champ of championStats) { champ.champion = BasicMatchSerializer.getChampion(champ.id) } console.timeEnd('CHAMPION') console.time('CHAMPION-CLASS') - const championClassStats = await MatchRepository.championClassStats(puuid) + const championClassStats = await MatchRepository.championClassStats(filters) for (const champ of championClassStats) { champ.id = ChampionRoles[champ.id] } console.timeEnd('CHAMPION-CLASS') console.time('MATES') - const mates = await MatchRepository.mates(puuid) + const mates = await MatchRepository.mates(filters) console.timeEnd('MATES') console.time('RECENT_ACTIVITY')