Participant Context & Hydration
Overview
Participants can be returned with contextual information that is not part of the core TODS document node. The process of adding this contextual information is called "hydration" or adding "context".
By default, a participant in the tournament record contains only essential data. Through hydration, the Competition Factory can enrich participant objects with:
- Events and draws they're entered in
- MatchUps they've played or will play
- Statistics (wins, losses, sets won)
- Opponent information
- Ratings and rankings from timeItems
- Individual participant details (for PAIR/TEAM/GROUP)
- Custom extensions as accessible attributes
Core vs. Hydrated Participants
Core Participant (In Tournament Record)
{
participantId: 'player-123',
participantType: 'INDIVIDUAL',
participantRole: 'COMPETITOR',
person: {
personId: 'person-456',
standardFamilyName: 'Nadal',
standardGivenName: 'Rafael'
}
}
Fully Hydrated Participant
{
// Core attributes
participantId: 'player-123',
participantType: 'INDIVIDUAL',
participantRole: 'COMPETITOR',
person: {
personId: 'person-456',
standardFamilyName: 'Nadal',
standardGivenName: 'Rafael',
nationalityCode: 'ESP'
},
// Added by hydration
events: [ // Added by withEvents
{ eventId: 'event-singles', eventName: 'Men\'s Singles' }
],
draws: [ // Added by withDraws
{ drawId: 'draw-123', drawName: 'Main Draw' }
],
matchUps: [ // Added by withMatchUps
{
matchUpId: 'match-1',
matchUpStatus: 'COMPLETED',
winningSide: 1,
score: { ... }
}
],
statistics: { // Added by withStatistics
matchUpsPlayed: 5,
matchUpsWon: 4,
matchUpsLost: 1,
setsWon: 9,
setsLost: 3,
gamesWon: 72,
gamesLost: 48
},
opponents: [ // Added by withOpponents
{ participantId: 'player-456', participantName: 'Djokovic' }
],
rankings: { // Added by withScaleValues
SINGLES: { rating: 1250, ranking: 2 }
}
}
Hydration Options
withMatchUps
Add all matchUps where the participant has competed:
const { participants } = tournamentEngine.getParticipants({
withMatchUps: true,
});
participants.forEach((p) => {
console.log(`${p.person.standardFamilyName}: ${p.matchUps.length} matches`);
p.matchUps.forEach((matchUp) => {
console.log(` - ${matchUp.matchUpStatus} on ${matchUp.schedule?.scheduledDate}`);
});
});
Result includes:
- Completed matchUps with scores
- Scheduled matchUps
- MatchUp details (opponents, dates, venues)
- Match outcome (won/lost)
withStatistics
Add calculated win/loss statistics:
const { participants } = tournamentEngine.getParticipants({
withStatistics: true,
});
participants.forEach((p) => {
const stats = p.statistics;
const winPct = ((stats.matchUpsWon / stats.matchUpsPlayed) * 100).toFixed(1);
console.log(`${p.person.standardFamilyName}: ${winPct}% (${stats.matchUpsWon}-${stats.matchUpsLost})`);
});
Statistics included:
matchUpsPlayed,matchUpsWon,matchUpsLostsetsWon,setsLostgamesWon,gamesLostpointsWon,pointsLost(if available)
withOpponents
Include information about all opponents faced:
const { participants } = tournamentEngine.getParticipants({
withOpponents: true,
withMatchUps: true, // Often used together
});
participants.forEach((p) => {
console.log(`${p.person.standardFamilyName} opponents:`);
p.opponents.forEach((opponent) => {
console.log(` - ${opponent.participantName || opponent.person.standardFamilyName}`);
});
});
withIndividualParticipants
For PAIR, TEAM, or GROUP participants, expand to include full individual participant details:
const { participants } = tournamentEngine.getParticipants({
participantFilters: { participantTypes: ['PAIR'] },
withIndividualParticipants: true
});
// Before hydration:
{
participantId: 'pair-123',
participantType: 'PAIR',
individualParticipantIds: ['player-1', 'player-2']
}
// After hydration:
{
participantId: 'pair-123',
participantType: 'PAIR',
participantName: 'Bryan/Bryan',
individualParticipantIds: ['player-1', 'player-2'],
individualParticipants: [ // ← Added
{
participantId: 'player-1',
person: { standardFamilyName: 'Bryan', standardGivenName: 'Bob' }
},
{
participantId: 'player-2',
person: { standardFamilyName: 'Bryan', standardGivenName: 'Mike' }
}
]
}
Critical for:
- Displaying doubles pair names
- Team roster information
- Understanding group composition
withScaleValues
Convert timeItems (rankings/ratings) into accessible scale values:
const { participants } = tournamentEngine.getParticipants({
withScaleValues: true,
});
participants.forEach((p) => {
if (p.rankings?.SINGLES) {
console.log(`${p.person.standardFamilyName}: Rank ${p.rankings.SINGLES.ranking}`);
}
if (p.ratings?.SINGLES) {
console.log(` Rating: ${p.ratings.SINGLES.rating}`);
}
});
Converts timeItems like:
// From:
timeItems: [{
itemType: 'RANKING.SINGLES',
itemValue: '45',
itemDate: '2024-01-15'
}]
// To:
rankings: {
SINGLES: {
ranking: 45,
rankingDate: '2024-01-15'
}
}
convertExtensions
Convert extensions to underscore-prefixed attributes for easy access:
const { participants } = tournamentEngine.getParticipants({
convertExtensions: true
});
// Extension in tournament record:
{
extensions: [{
name: 'membershipLevel',
value: 'GOLD'
}]
}
// After conversion:
{
extensions: [...],
_membershipLevel: 'GOLD' // ← Easily accessible
}
// Usage:
if (participant._membershipLevel === 'GOLD') {
// Provide benefits
}
withEvents and withDraws
Add event and draw information:
const { participants } = tournamentEngine.getParticipants({
withEvents: true,
withDraws: true,
});
participants.forEach((p) => {
console.log(`${p.person.standardFamilyName}:`);
p.events?.forEach((event) => {
console.log(` Event: ${event.eventName}`);
});
p.draws?.forEach((draw) => {
console.log(` Draw: ${draw.drawName}`);
});
});
Schedule Analysis
Detect participants with scheduling conflicts:
const { participants, participantIdsWithConflicts } = tournamentEngine.getParticipants({
withMatchUps: true,
scheduleAnalysis: {
scheduledMinutesDifference: 60, // Flag matches within 60 minutes
},
});
if (participantIdsWithConflicts.length > 0) {
console.log('Participants with scheduling conflicts:');
participantIdsWithConflicts.forEach((participantId) => {
const participant = participants.find((p) => p.participantId === participantId);
console.log(` - ${participant.person.standardFamilyName}`);
// Conflicts available in participant.scheduleConflicts
participant.scheduleConflicts.forEach((conflict) => {
console.log(` Conflict: ${conflict.matchUpId1} vs ${conflict.matchUpId2}`);
});
});
}
See: Scheduling Conflicts for detailed conflict management.
Combined Hydration Example
Retrieve fully enriched participant data for display:
const { participants } = tournamentEngine.getParticipants({
participantFilters: {
participantTypes: ['INDIVIDUAL'],
participantRoles: ['COMPETITOR'],
eventIds: ['singles-main'],
},
// Hydration options
withMatchUps: true,
withStatistics: true,
withOpponents: true,
withScaleValues: true,
withEvents: true,
withDraws: true,
convertExtensions: true,
// Schedule analysis
scheduleAnalysis: {
scheduledMinutesDifference: 90,
},
// Privacy
policyDefinitions: { participant: participantPolicy },
});
// Result: Fully hydrated participants ready for display
participants.forEach((participant) => {
renderPlayerCard({
name: `${participant.person.standardGivenName} ${participant.person.standardFamilyName}`,
nationality: participant.person.nationalityCode,
ranking: participant.rankings?.SINGLES?.ranking,
rating: participant.ratings?.SINGLES?.rating,
stats: participant.statistics,
upcomingMatches: participant.matchUps.filter((m) => m.matchUpStatus === 'TO_BE_PLAYED'),
completedMatches: participant.matchUps.filter((m) => m.matchUpStatus === 'COMPLETED'),
membershipLevel: participant._membershipLevel,
hasConflicts: participant.scheduleConflicts?.length > 0,
});
});
Performance Considerations
What to Hydrate
Always hydrate:
- Data needed for immediate display
- Required for business logic
Avoid hydrating:
- Unnecessary data that slows retrieval
- Data available through other means
Selective Hydration
// Bad: Hydrate everything
const { participants } = tournamentEngine.getParticipants({
withMatchUps: true,
withStatistics: true,
withOpponents: true,
withIndividualParticipants: true,
withScaleValues: true,
// ... participant list takes 2 seconds to load
});
// Good: Only what's needed
const { participants } = tournamentEngine.getParticipants({
withStatistics: true, // Only need stats for leaderboard
withScaleValues: true, // And rankings
// ... participant list loads in 200ms
});
Participant Map
For fast lookups without full hydration:
const {
participantMap, // Object: { [participantId]: participant }
} = tournamentEngine.getParticipants({
// participantMap is NOT fully hydrated (performance optimization)
});
// Fast lookup
const participant = participantMap['player-123'];
Note: participantMap participants are NOT fully hydrated. Use for quick ID→participant lookups only.
Custom Context
Pass additional context to be added to all participants:
const { participants } = tournamentEngine.getParticipants({
context: {
tournamentName: 'US Open 2024',
surfaceType: 'HARD',
customAttribute: 'value',
},
});
// All participants now have context attributes:
participants.forEach((p) => {
console.log(`${p.person.standardFamilyName} at ${p.context.tournamentName}`);
});
Hydration for Different Participant Types
INDIVIDUAL Hydration
const { participants } = tournamentEngine.getParticipants({
participantFilters: { participantTypes: ['INDIVIDUAL'] },
withMatchUps: true,
withStatistics: true,
});
// Individuals get their direct match history and stats
PAIR Hydration
const { participants } = tournamentEngine.getParticipants({
participantFilters: { participantTypes: ['PAIR'] },
withIndividualParticipants: true, // Critical for pairs
withMatchUps: true,
withStatistics: true,
});
// Each pair now shows:
// - Combined pair statistics
// - Full details of both individual partners
// - Pair's match history
TEAM Hydration
const { participants } = tournamentEngine.getParticipants({
participantFilters: { participantTypes: ['TEAM'] },
withIndividualParticipants: true, // Shows full roster
withMatchUps: true,
withStatistics: true,
});
// Each team now shows:
// - Complete roster with individual details
// - Team match history
// - Team statistics
Related Documentation
- Participants Overview - Participant types and management
- Scheduling Conflicts - Understanding schedule analysis
- Participant Policy - Privacy and data filtering
- Query Governor - Complete API reference
- Extensions - Understanding TODS extensions system