MatchUp Context & Hydration
Overview
Context (or hydration) is the process of enriching matchUp objects with additional information from their containing structures (draws, events, tournaments). By default, all matchUp retrieval methods return hydrated matchUps with contextual data that makes them easier to work with in applications.
Key Concepts
Hydration: Adding contextual information not stored directly on the matchUp
inContext: Parameter controlling whether context is added (default: true)
Deep Copies: All returned matchUps are deep copies, safe to modify
Extensions: Custom data automatically converted to underscore-prefixed attributes
Participant Hydration: Participants attached to matchUp sides
Default Behavior
By default, all matchUp retrieval methods return hydrated matchUps:
// These are equivalent:
const { matchUps: m1 } = tournamentEngine.allTournamentMatchUps();
const { matchUps: m2 } = tournamentEngine.allTournamentMatchUps({ inContext: true });
// Matchups include context
console.log(m1[0].tournamentName); // ✓ Available
console.log(m1[0].eventName); // ✓ Available
console.log(m1[0].roundName); // ✓ Available
// To get raw matchUps without context:
const { matchUps: raw } = tournamentEngine.allTournamentMatchUps({
inContext: false,
});
console.log(raw[0].tournamentName); // ✗ undefined
Context Attributes Added
Tournament/Event/Draw Context
Every hydrated matchUp includes identifiers and names from its hierarchy:
{
matchUpId: 'match-123',
// Tournament context
tournamentId: 'tournament-456',
tournamentName: 'Summer Championships',
// Event context
eventId: 'event-789',
eventName: 'Men\'s Singles',
eventType: 'SINGLES',
// Draw context
drawId: 'draw-012',
drawName: 'Main Draw',
// Structure context
structureId: 'structure-345',
structureName: 'Qualifying',
structureOrder: 1,
structureAbbreviation: 'Q'
}
Round Information
Round details help understand match position in draw:
{
roundNumber: 2,
roundName: 'Round of 16',
roundPosition: 3,
// For team events
roundOrder: 1,
// Playoff rounds (consolation/playoff structures)
finishingPositionRange: {
winner: [9, 16],
loser: [17, 32]
}
}
Scheduling Context
When scheduled, matchUps include venue and court information:
{
schedule: {
scheduledDate: '2024-06-15',
scheduledTime: '14:00',
courtId: 'court-5',
courtName: 'Court 5',
venueId: 'venue-123',
startTime: '2024-06-15T14:05:00Z',
endTime: '2024-06-15T15:45:00Z'
},
// Additional venue context
venueName: 'Stadium',
venueAbbreviation: 'STD',
// Order of play
courtOrder: 2, // Second match on this court
scheduleTime: '14:00'
}
Participant Hydration
Participants are automatically attached to matchUp sides:
{
sides: [
{
sideNumber: 1,
participantId: 'player-1',
// Hydrated participant
participant: {
participantId: 'player-1',
participantType: 'INDIVIDUAL',
participantRole: 'COMPETITOR',
person: {
personId: 'person-1',
standardFamilyName: 'Federer',
standardGivenName: 'Roger',
nationalityCode: 'SUI',
},
},
},
{
sideNumber: 2,
participantId: 'player-2',
participant: {
participantId: 'player-2',
participantType: 'INDIVIDUAL',
person: {
standardFamilyName: 'Nadal',
standardGivenName: 'Rafael',
nationalityCode: 'ESP',
},
},
},
];
}
See: Participant Context for participant hydration options.
Next MatchUps (Progression)
The nextMatchUps parameter adds winner/loser progression information:
const { matchUps } = tournamentEngine.allDrawMatchUps({
drawId: 'draw-123',
nextMatchUps: true,
});
matchUps.forEach((matchUp) => {
// Winner progression
if (matchUp.winnerTo) {
console.log('Winner advances to:');
console.log(` MatchUp: ${matchUp.winnerTo.matchUpId}`);
console.log(` Round: ${matchUp.winnerTo.roundNumber}`);
console.log(` Position: ${matchUp.winnerTo.roundPosition}`);
}
// Loser progression (feed-in/consolation)
if (matchUp.loserTo) {
console.log('Loser feeds to:');
console.log(` MatchUp: ${matchUp.loserTo.matchUpId}`);
console.log(` Round: ${matchUp.loserTo.roundNumber}`);
}
});
Example: Final Match Progression
{
matchUpId: 'semifinal-1',
roundName: 'Semifinals',
winnerTo: {
matchUpId: 'final',
roundNumber: 4,
roundName: 'Final',
roundPosition: 1
},
loserTo: {
matchUpId: 'bronze-medal',
roundNumber: 4,
roundName: 'Bronze Medal Match',
roundPosition: 1
}
}
Extensions Handling
Extensions (custom data) are automatically converted to underscore-prefixed attributes:
// Original matchUp has extension
{
matchUpId: 'match-123',
extensions: [
{
name: 'courtPreference',
value: 'Stadium Court'
},
{
name: 'broadcastMatch',
value: true
}
]
}
// When hydrated, extensions become attributes
const { matchUps } = tournamentEngine.allTournamentMatchUps();
console.log(matchUps[0]._courtPreference); // 'Stadium Court'
console.log(matchUps[0]._broadcastMatch); // true
// Original extensions array still present
console.log(matchUps[0].extensions); // Array of extension objects
Extension Name Conversion
Extension names are converted to camelCase with underscore prefix:
// Extension name → Attribute name
'courtPreference' → _courtPreference
'broadcast_match' → _broadcastMatch
'estimated-duration' → _estimatedDuration
'PRIORITY_LEVEL' → _priorityLevel
See: Extensions for detailed extension documentation.
Context Filters
Use contextFilters to retrieve matchUps based on contextual attributes:
const { matchUps } = tournamentEngine.allTournamentMatchUps({
contextFilters: {
// Schedule-based filters
scheduledDate: '2024-06-15',
scheduledDates: ['2024-06-15', '2024-06-16'],
venueIds: ['venue-123'],
courtIds: ['court-1', 'court-2'],
// Structure filters
stages: ['QUALIFYING', 'MAIN'],
structureIds: ['structure-1'],
// Round filters
roundNumbers: [1, 2],
// Event filters
eventIds: ['event-123'],
// Draw filters
drawIds: ['draw-456'],
},
});
See: MatchUp Filtering for comprehensive filtering options.
Performance Considerations
When to Disable Context
Disable context when:
- Processing large numbers of matchUps for internal calculations
- Only need core matchUp data (IDs, scores, status)
- Implementing high-frequency operations
// Fast: minimal data
const { matchUps } = tournamentEngine.allTournamentMatchUps({
inContext: false,
});
// Full featured: more memory/processing
const { matchUps } = tournamentEngine.allTournamentMatchUps({
inContext: true,
});
Deep Copy Overhead
All hydrated matchUps are deep copies, ensuring data integrity but using more memory:
// Get hydrated matchUps
const { matchUps } = tournamentEngine.allTournamentMatchUps();
// Safe to modify without affecting tournament state
matchUps[0].customProperty = 'value'; // ✓ Safe
delete matchUps[0].roundName; // ✓ Safe
// Tournament state unchanged
const { matchUps: fresh } = tournamentEngine.allTournamentMatchUps();
console.log(fresh[0].customProperty); // undefined
console.log(fresh[0].roundName); // Still present
Participant Context Control
Control how participants are hydrated within matchUps:
const { matchUps } = tournamentEngine.allTournamentMatchUps({
// Include full participant details (default)
inContext: true,
// Exclude specific PII
policyDefinitions: {
participant: {
excludePersonDetails: ['addresses', 'contacts'],
},
},
});
// Participants included but privacy respected
matchUps[0].sides.forEach((side) => {
console.log(side.participant.person.standardFamilyName); // ✓ Available
console.log(side.participant.person.addresses); // ✗ undefined
});
See: Participant Context for complete hydration options.
Additional Context Parameters
Custom Context Data
Pass additional contextual data to retrieval methods:
const { matchUps } = tournamentEngine.allTournamentMatchUps({
context: {
myCustomField: 'value',
applicationData: { key: 'value' },
},
});
// Custom context available on all matchUps
matchUps.forEach((matchUp) => {
console.log(matchUp.myCustomField); // 'value'
console.log(matchUp.applicationData); // { key: 'value' }
});
Tournament Record Context
When working with tournament records directly, context can be passed:
import { getMatchUps } from 'tods-competition-factory';
const matchUps = getMatchUps({
tournamentRecord,
inContext: true,
context: {
additionalData: 'value',
},
});
Practical Examples
Display Match Information
const { matchUps } = tournamentEngine.allTournamentMatchUps({
matchUpFilters: {
matchUpStatuses: ['TO_BE_PLAYED'],
},
contextFilters: {
scheduledDate: '2024-06-15',
},
});
matchUps.forEach((matchUp) => {
console.log(`${matchUp.tournamentName}`);
console.log(` ${matchUp.eventName} - ${matchUp.roundName}`);
console.log(` ${matchUp.courtName} at ${matchUp.scheduleTime}`);
matchUp.sides.forEach((side) => {
const name = side.participant?.person?.standardFamilyName || 'TBD';
console.log(` ${name}`);
});
});
Results with Full Context
const { matchUps } = tournamentEngine.allTournamentMatchUps({
matchUpFilters: {
matchUpStatuses: ['COMPLETED'],
},
});
matchUps.forEach((matchUp) => {
const winner = matchUp.sides.find((s) => s.sideNumber === matchUp.winningSide);
const loser = matchUp.sides.find((s) => s.sideNumber !== matchUp.winningSide);
console.log(`${matchUp.eventName} ${matchUp.roundName}`);
console.log(` ${winner.participant.person.standardFamilyName} d.`);
console.log(` ${loser.participant.person.standardFamilyName}`);
console.log(` ${matchUp.score.scoreStringSide1}`);
// Custom extension data
if (matchUp._broadcastMatch) {
console.log(' [BROADCAST MATCH]');
}
});
Build Draw Bracket
const { matchUps } = tournamentEngine.allDrawMatchUps({
drawId: 'draw-123',
nextMatchUps: true,
});
// Group by round
const rounds = matchUps.reduce((acc, matchUp) => {
const round = matchUp.roundNumber;
if (!acc[round]) acc[round] = [];
acc[round].push(matchUp);
return acc;
}, {});
// Display bracket structure
Object.entries(rounds)
.sort(([a], [b]) => a - b)
.forEach(([roundNum, matches]) => {
console.log(`\n${matches[0].roundName}:`);
matches.forEach((matchUp) => {
console.log(` Match ${matchUp.roundPosition}`);
matchUp.sides.forEach((side) => {
const name = side.participant?.person?.standardFamilyName || 'BYE';
console.log(` ${name}`);
});
if (matchUp.winnerTo) {
console.log(` → Winner to Round ${matchUp.winnerTo.roundNumber}`);
}
});
});
Related Documentation
- MatchUp Overview - Core matchUp concepts and types
- MatchUp Filtering - Comprehensive filtering options
- Participant Context - Participant hydration details
- Extensions - Custom data and extensions
- Query Governor - Complete API reference