From 491643b71226fc08d1cf305ef12d0d17d8aca20f Mon Sep 17 00:00:00 2001 From: Cody Date: Tue, 12 May 2026 06:10:45 -0600 Subject: [PATCH 1/2] perf(api): batch leaderboard stat lookups --- .../leaderboards/guild-leaderboard.service.ts | 27 +++++++++---------- .../player-leaderboard.service.ts | 27 +++++++++---------- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/apps/api/src/guild/leaderboards/guild-leaderboard.service.ts b/apps/api/src/guild/leaderboards/guild-leaderboard.service.ts index 430f00294..827c3b675 100644 --- a/apps/api/src/guild/leaderboards/guild-leaderboard.service.ts +++ b/apps/api/src/guild/leaderboards/guild-leaderboard.service.ts @@ -55,23 +55,22 @@ export class GuildLeaderboardService extends LeaderboardService { return acc; }, {} as Record); + selector.id = true; selector.nameFormatted = true; - return await Promise.all( - ids.map(async (id) => { - const guild = await this.guildModel - .findOne() - .where("id") - .equals(id) - .select(selector) - .lean() - .exec(); + const guilds = await this.guildModel + .find({ id: { $in: ids } }) + .select(selector) + .lean() + .exec(); - const additionalStats = flatten(guild) as LeaderboardAdditionalStats; - additionalStats.name = additionalStats.nameFormatted; + const guildsById = new Map(guilds.map((guild) => [guild.id, guild])); - return additionalStats; - }) - ); + return ids.map((id) => { + const additionalStats = flatten(guildsById.get(id)) as LeaderboardAdditionalStats; + additionalStats.name = additionalStats.nameFormatted; + + return additionalStats; + }); } } diff --git a/apps/api/src/player/leaderboards/player-leaderboard.service.ts b/apps/api/src/player/leaderboards/player-leaderboard.service.ts index 5877de527..476a6be32 100644 --- a/apps/api/src/player/leaderboards/player-leaderboard.service.ts +++ b/apps/api/src/player/leaderboards/player-leaderboard.service.ts @@ -55,22 +55,21 @@ export class PlayerLeaderboardService extends LeaderboardService { }, {} as Record); selector.displayName = true; + selector.uuid = true; - return await Promise.all( - ids.map(async (id) => { - const player = await this.playerModel - .findOne() - .where("uuid") - .equals(id) - .select(selector) - .lean() - .exec(); + const players = await this.playerModel + .find({ uuid: { $in: ids } }) + .select(selector) + .lean() + .exec(); - const additionalStats = flatten(player) as LeaderboardAdditionalStats; - additionalStats.name = additionalStats.displayName; + const playersById = new Map(players.map((player) => [player.uuid, player])); - return additionalStats; - }) - ); + return ids.map((id) => { + const additionalStats = flatten(playersById.get(id)) as LeaderboardAdditionalStats; + additionalStats.name = additionalStats.displayName; + + return additionalStats; + }); } } From ba946d11dac8712ebf022a1f82bd870a065c0fa0 Mon Sep 17 00:00:00 2001 From: Cody Date: Thu, 28 May 2026 14:05:35 -0600 Subject: [PATCH 2/2] perf(leaderboard): optimize leaderboard queries and handle unknown entries * deduplicate ids before querying the database * return a default entry for unknown players and guilds --- .../leaderboards/guild-leaderboard.service.ts | 12 ++++++++++-- apps/api/src/leaderboards/leaderboard.service.ts | 16 +++++++++------- .../leaderboards/player-leaderboard.service.ts | 12 ++++++++++-- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/apps/api/src/guild/leaderboards/guild-leaderboard.service.ts b/apps/api/src/guild/leaderboards/guild-leaderboard.service.ts index 827c3b675..61f44fb88 100644 --- a/apps/api/src/guild/leaderboards/guild-leaderboard.service.ts +++ b/apps/api/src/guild/leaderboards/guild-leaderboard.service.ts @@ -58,8 +58,10 @@ export class GuildLeaderboardService extends LeaderboardService { selector.id = true; selector.nameFormatted = true; + const uniqueIds = [...new Set(ids)]; + const guilds = await this.guildModel - .find({ id: { $in: ids } }) + .find({ id: { $in: uniqueIds } }) .select(selector) .lean() .exec(); @@ -67,7 +69,13 @@ export class GuildLeaderboardService extends LeaderboardService { const guildsById = new Map(guilds.map((guild) => [guild.id, guild])); return ids.map((id) => { - const additionalStats = flatten(guildsById.get(id)) as LeaderboardAdditionalStats; + const guild = guildsById.get(id); + + if (!guild) { + return { name: "Unknown" } as LeaderboardAdditionalStats; + } + + const additionalStats = flatten(guild) as LeaderboardAdditionalStats; additionalStats.name = additionalStats.nameFormatted; return additionalStats; diff --git a/apps/api/src/leaderboards/leaderboard.service.ts b/apps/api/src/leaderboards/leaderboard.service.ts index fd054864b..c512b79d7 100644 --- a/apps/api/src/leaderboards/leaderboard.service.ts +++ b/apps/api/src/leaderboards/leaderboard.service.ts @@ -135,13 +135,15 @@ export abstract class LeaderboardService { LeaderboardScanner.getLeaderboardField(constructor, extraDisplay, false) : undefined; - const additionalStats = await this.getAdditionalStats( - leaderboard.map(({ id }) => id), - [ - ...additionalFields.filter((k) => k !== field), - ...(extraDisplay ? [extraDisplay] : []), - ] - ); + const additionalStats = leaderboard.length ? + await this.getAdditionalStats( + leaderboard.map(({ id }) => id), + [ + ...additionalFields.filter((k) => k !== field), + ...(extraDisplay ? [extraDisplay] : []), + ] + ) : + []; const data = leaderboard.map((doc, index) => { const stats = additionalStats[index]; diff --git a/apps/api/src/player/leaderboards/player-leaderboard.service.ts b/apps/api/src/player/leaderboards/player-leaderboard.service.ts index 476a6be32..330885040 100644 --- a/apps/api/src/player/leaderboards/player-leaderboard.service.ts +++ b/apps/api/src/player/leaderboards/player-leaderboard.service.ts @@ -57,8 +57,10 @@ export class PlayerLeaderboardService extends LeaderboardService { selector.displayName = true; selector.uuid = true; + const uniqueIds = [...new Set(ids)]; + const players = await this.playerModel - .find({ uuid: { $in: ids } }) + .find({ uuid: { $in: uniqueIds } }) .select(selector) .lean() .exec(); @@ -66,7 +68,13 @@ export class PlayerLeaderboardService extends LeaderboardService { const playersById = new Map(players.map((player) => [player.uuid, player])); return ids.map((id) => { - const additionalStats = flatten(playersById.get(id)) as LeaderboardAdditionalStats; + const player = playersById.get(id); + + if (!player) { + return { name: "Unknown" } as LeaderboardAdditionalStats; + } + + const additionalStats = flatten(player) as LeaderboardAdditionalStats; additionalStats.name = additionalStats.displayName; return additionalStats;