fix: pre-compute stats to make the API endpoint fast
This commit is contained in:
@@ -1,14 +1,29 @@
|
||||
import type { MongoClient } from 'mongodb'
|
||||
import { connectToDatabase, fetchLatestPatch, getAvailablePlatforms } from '../utils/mongo'
|
||||
|
||||
interface StatsDocument {
|
||||
patch: string
|
||||
total: number
|
||||
platforms: Record<string, number>
|
||||
updatedAt: Date
|
||||
}
|
||||
|
||||
async function fetchGameCount(client: MongoClient, patch: string) {
|
||||
const database = client.db('matches')
|
||||
const statsCollection = database.collection<StatsDocument>('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<string, number> = {}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
152
match_collector/src/stats.ts
Normal file
152
match_collector/src/stats.ts
Normal file
@@ -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<string, number>
|
||||
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<void> {
|
||||
const database = client.db(STATS_DATABASE)
|
||||
const collection = database.collection<StatsDocument>(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<void> {
|
||||
const database = client.db(STATS_DATABASE)
|
||||
const collection = database.collection<StatsDocument>(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<StatsDocument | null> {
|
||||
const database = client.db(STATS_DATABASE)
|
||||
const collection = database.collection<StatsDocument>(STATS_COLLECTION)
|
||||
|
||||
return await collection.findOne({ patch })
|
||||
}
|
||||
|
||||
/**
|
||||
* Get stats for all patches, sorted from latest to oldest.
|
||||
*/
|
||||
async function getAllStats(client: MongoClient): Promise<StatsDocument[]> {
|
||||
const database = client.db(STATS_DATABASE)
|
||||
const collection = database.collection<StatsDocument>(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<void> {
|
||||
if (patches.length === 0) return
|
||||
|
||||
const database = client.db(STATS_DATABASE)
|
||||
const collection = database.collection<StatsDocument>(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<void> {
|
||||
const database = client.db(STATS_DATABASE)
|
||||
|
||||
let total = 0
|
||||
const platformCounts: Record<string, number> = {}
|
||||
|
||||
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<StatsDocument>(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 }
|
||||
Reference in New Issue
Block a user