diff --git a/frontend/server/api/stats.ts b/frontend/server/api/stats.ts index 371bdd6..b895c3e 100644 --- a/frontend/server/api/stats.ts +++ b/frontend/server/api/stats.ts @@ -1,14 +1,29 @@ import type { MongoClient } from 'mongodb' import { connectToDatabase, fetchLatestPatch, getAvailablePlatforms } from '../utils/mongo' +interface StatsDocument { + patch: string + total: number + platforms: Record + updatedAt: Date +} + async function fetchGameCount(client: MongoClient, patch: string) { const database = client.db('matches') + const statsCollection = database.collection('stats') - // Check for platform-specific collections + // Try to get stats from the pre-computed stats collection + const stats = await statsCollection.findOne({ patch }) + + if (stats) { + return { total: stats.total, platforms: stats.platforms } + } + + // Fallback: compute stats from collections if stats document doesn't exist + // This handles the migration case where stats weren't pre-computed const platforms = await getAvailablePlatforms(client, patch) if (platforms.length > 0) { - // Sum counts from all platform-specific collections let totalCount = 0 const platformCounts: Record = {} diff --git a/match_collector/src/index.ts b/match_collector/src/index.ts index a4e2d4d..c15856b 100644 --- a/match_collector/src/index.ts +++ b/match_collector/src/index.ts @@ -4,6 +4,7 @@ const sleep_minutes = 12 import { MongoClient } from 'mongodb' import champion_stat from './champion_stat' +import stats from './stats' import { Match } from './api' import { PLATFORMS, getPlatformBaseUrl, getRegionalBaseUrl, getRegionForPlatform } from './platform' import { downloadCDragonAssets } from './cdragon_cache' @@ -98,6 +99,9 @@ async function cleanupOldPatches(client: MongoClient, keepPatches: number): Prom } } + // Delete stats for removed patches + await stats.deleteStats(client, patchesToRemove) + console.log(`Cleanup: Dropped ${droppedCount} collection(s).`) } @@ -300,6 +304,9 @@ async function saveMatch(client: MongoClient, match: Match, patch: string, platf const collectionName = `${patch}_${platform}` const matches = database.collection(collectionName) await matches.insertOne(match) + + // Increment stats counter for this patch/platform + await stats.incrementMatchCount(client, patch, platform) } /** @@ -348,6 +355,11 @@ async function runWithPreloadedData() { .filter(name => name.startsWith(`${latestPatch}_`)) .map(name => name.replace(`${latestPatch}_`, '')) + // Recalculate match count stats from existing collections + if (platforms.length > 0) { + await stats.recalculateStats(client, latestPatch, platforms) + } + // Generate stats for each platform if (platforms.length > 0) { await champion_stat.makeChampionsStats(client, latestPatch, platforms) diff --git a/match_collector/src/stats.ts b/match_collector/src/stats.ts new file mode 100644 index 0000000..44613c2 --- /dev/null +++ b/match_collector/src/stats.ts @@ -0,0 +1,152 @@ +import { MongoClient } from 'mongodb' + +/** + * Stats document structure stored in the 'stats' collection. + * One document per patch, containing total and per-platform match counts. + */ +interface StatsDocument { + patch: string + total: number + platforms: Record + updatedAt: Date +} + +const STATS_COLLECTION = 'stats' +const STATS_DATABASE = 'matches' + +/** + * Initialize stats document for a patch if it doesn't exist. + * This should be called before processing matches for a new patch. + */ +async function initStats(client: MongoClient, patch: string): Promise { + const database = client.db(STATS_DATABASE) + const collection = database.collection(STATS_COLLECTION) + + await collection.updateOne( + { patch }, + { + $setOnInsert: { + patch, + total: 0, + platforms: {}, + updatedAt: new Date() + } + }, + { upsert: true } + ) +} + +/** + * Increment match count for a specific patch and platform. + * This should be called each time a new match is saved. + */ +async function incrementMatchCount( + client: MongoClient, + patch: string, + platform: string +): Promise { + const database = client.db(STATS_DATABASE) + const collection = database.collection(STATS_COLLECTION) + + await collection.updateOne( + { patch }, + { + $inc: { total: 1, [`platforms.${platform}`]: 1 }, + $set: { updatedAt: new Date() } + }, + { upsert: true } + ) +} + +/** + * Get stats for a specific patch. + * Returns null if no stats exist for the patch. + */ +async function getStats(client: MongoClient, patch: string): Promise { + const database = client.db(STATS_DATABASE) + const collection = database.collection(STATS_COLLECTION) + + return await collection.findOne({ patch }) +} + +/** + * Get stats for all patches, sorted from latest to oldest. + */ +async function getAllStats(client: MongoClient): Promise { + const database = client.db(STATS_DATABASE) + const collection = database.collection(STATS_COLLECTION) + + const stats = await collection.find({}).toArray() + + // Sort patches from latest to oldest + return stats.sort((a, b) => { + const [aMajor, aMinor] = a.patch.split('.').map(Number) + const [bMajor, bMinor] = b.patch.split('.').map(Number) + if (aMajor !== bMajor) return bMajor - aMajor + return bMinor - aMinor + }) +} + +/** + * Delete stats for patches that are being cleaned up. + * This should be called when old patch collections are dropped. + */ +async function deleteStats(client: MongoClient, patches: string[]): Promise { + if (patches.length === 0) return + + const database = client.db(STATS_DATABASE) + const collection = database.collection(STATS_COLLECTION) + + await collection.deleteMany({ patch: { $in: patches } }) + console.log(`Stats: Deleted stats for patches: ${patches.join(', ')}`) +} + +/** + * Recalculate stats from existing match collections. + * This is useful for migration or fixing inconsistent stats. + */ +async function recalculateStats( + client: MongoClient, + patch: string, + platforms: string[] +): Promise { + const database = client.db(STATS_DATABASE) + + let total = 0 + const platformCounts: Record = {} + + for (const platform of platforms) { + const collectionName = `${patch}_${platform}` + const collection = database.collection(collectionName) + const count = await collection.countDocuments() + platformCounts[platform] = count + total += count + } + + const statsCollection = database.collection(STATS_COLLECTION) + await statsCollection.updateOne( + { patch }, + { + $set: { + patch, + total, + platforms: platformCounts, + updatedAt: new Date() + } + }, + { upsert: true } + ) + + console.log(`Stats: Recalculated stats for patch ${patch}: ${total} total matches`) +} + +export default { + initStats, + incrementMatchCount, + getStats, + getAllStats, + deleteStats, + recalculateStats +} + +export type { StatsDocument }