diff --git a/convex/_model/tournamentResults/_helpers/aggregateTournamentData.ts b/convex/_model/tournamentResults/_helpers/aggregateTournamentData.ts index 03fab27d..a9919edb 100644 --- a/convex/_model/tournamentResults/_helpers/aggregateTournamentData.ts +++ b/convex/_model/tournamentResults/_helpers/aggregateTournamentData.ts @@ -116,6 +116,7 @@ export const aggregateTournamentData = async ( } // ---- 3. Convert stats to ranking factors for players and competitors ---- + const roundsPlayed = round + 1; // Add 1 to get length rather than index return { registrations: Object.entries(registrationStats).map(([key, { results }]) => { const id = key as Id<'tournamentRegistrations'>; @@ -123,7 +124,7 @@ export const aggregateTournamentData = async ( id, gamesPlayed: results.length, opponentIds: Array.from(new Set(results.map((s) => s.opponentId))).filter((id) => id !== null), - rankingFactors: computeRankingFactors(id, registrationStats, defaultBaseStats, round), + rankingFactors: computeRankingFactors(id, registrationStats, defaultBaseStats, roundsPlayed), rank: -1, }; }), @@ -139,7 +140,7 @@ export const aggregateTournamentData = async ( opponentIds: Array.from(new Set(results.map((s) => s.opponentId))).filter((id) => id !== null), playedTables: Array.from(playedTables), byeRounds: Array.from(byeRounds), - rankingFactors: computeRankingFactors(id, competitorStats, defaultBaseStats, round), + rankingFactors: computeRankingFactors(id, competitorStats, defaultBaseStats, roundsPlayed), rank: -1, }; }), diff --git a/convex/_model/tournaments/triggers.ts b/convex/_model/tournaments/triggers.ts index e1c9e7e3..ef92b529 100644 --- a/convex/_model/tournaments/triggers.ts +++ b/convex/_model/tournaments/triggers.ts @@ -1,6 +1,43 @@ +import isEqual from 'fast-deep-equal/es6'; + import { MutationCtx } from '../../_generated/server'; import { TriggerChange } from '../common/types'; import { refreshLeagueRankings as refreshLeagueRankingsHandler } from '../leagues'; +import { refreshTournamentResult } from '../tournamentResults'; + +/** + * Trigger refresh of tournament results when ranking factors change. + * + * Only refreshes when fields that affect ranking change: + * - rankingFactors + */ +export const refreshTournamentResults = async ( + ctx: MutationCtx, + change: TriggerChange<'tournaments'>, +): Promise => { + const { newDoc, oldDoc } = change; + + if (!newDoc || !oldDoc) { + return; + } + + if (newDoc.status === 'draft' || newDoc.status === 'published') { + return; + } + + // For updates, skip if no ranking-relevant fields changed: + if (isEqual(newDoc.rankingFactors, oldDoc.rankingFactors)) { + return; + } + + const tournamentResults = await ctx.db.query('tournamentResults') + .withIndex('by_tournament_round', (q) => q.eq('tournamentId', change.id)) + .collect(); + + for (const tournamentResult of tournamentResults) { + await refreshTournamentResult(ctx, tournamentResult); + } +}; /** * Trigger refresh of league rankings when a tournament is archived. diff --git a/convex/functions.ts b/convex/functions.ts index c0c0203f..5b69fc04 100644 --- a/convex/functions.ts +++ b/convex/functions.ts @@ -78,3 +78,4 @@ triggers.register('tournamentCompetitors', tournamentCompetitorTriggers.refreshT triggers.register('tournamentRegistrations', tournamentRegistrationTriggers.refreshTournamentResults); triggers.register('tournamentResults', tournamentResultTriggers.refreshLeagueRankings); triggers.register('tournaments', tournamentTriggers.refreshLeagueRankings); +triggers.register('tournaments', tournamentTriggers.refreshTournamentResults); diff --git a/convex/migrations.ts b/convex/migrations.ts index 0e19046a..48314e6f 100644 --- a/convex/migrations.ts +++ b/convex/migrations.ts @@ -50,17 +50,14 @@ export const fixMissingListData = migrations.define({ }, }); -export const migrateTournamentResults = migrations.define({ +export const refreshTournamentResults = migrations.define({ table: 'tournaments', migrateOne: async (ctx, doc) => { - if (doc.status === 'archived') { - const rounds = doc.lastRound !== undefined ? doc.lastRound + 1 : doc.roundCount; - for (let i = 0; i < rounds; i++) { - await refreshTournamentResult(ctx, { - tournamentId: doc._id, - round: i, - }); - } + const tournamentResults = await ctx.db.query('tournamentResults') + .withIndex('by_tournament_round', (q) => q.eq('tournamentId', doc._id)) + .collect(); + for (const result of tournamentResults) { + await refreshTournamentResult(ctx, result); } }, });