如何通过Mongoose根据关联ObjectId的游戏文档状态更新MongoDB赛事文档状态?
Alright, let's break this down step by step. When a game's status changes, we need to find all competitions linked to that game, evaluate the statuses of all their associated games, and update the competition's status following your defined rules. Here's how to implement this with Mongoose:
Step 1: Core Logic Implementation
First, we'll create a function that handles the full workflow: finding relevant competitions, fetching their linked game statuses, calculating the new competition status, and updating the database.
const mongoose = require('mongoose'); const Competition = require('./models/Competition'); // Update to your model path const Game = require('./models/Game'); // Update to your model path async function updateCompetitionStatusOnGameChange(gameId) { try { // Convert string ID to Mongoose ObjectId (if needed) const targetGameObjectId = mongoose.Types.ObjectId(gameId); // 1. Find all competitions that include the updated game const relatedCompetitions = await Competition.find({ games: targetGameObjectId }); for (const competition of relatedCompetitions) { // 2. Fetch all game statuses linked to this competition const linkedGames = await Game.find( { _id: { $in: competition.games } }, 'gameStatus' // Only retrieve the status field for efficiency ); const gameStatuses = linkedGames.map(game => game.gameStatus); // 3. Calculate new competition status based on rules let newCompetitionStatus; const hasPlaying = gameStatuses.includes('playing'); const hasNotStarted = gameStatuses.includes('notStarted'); const hasEnded = gameStatuses.includes('ended'); if (gameStatuses.every(status => status === 'ended')) { newCompetitionStatus = 'ended'; } else if (hasPlaying || (hasNotStarted && hasEnded)) { newCompetitionStatus = 'playing'; } else { newCompetitionStatus = 'notStarted'; } // 4. Update the competition's status await Competition.findByIdAndUpdate( competition._id, { competitionStatus: newCompetitionStatus }, { new: true } // Optional: returns the updated document ); console.log(`Updated competition ${competition.compeititionName} to status: ${newCompetitionStatus}`); } } catch (error) { console.error('Failed to update competition status:', error); throw error; } } // Call this function after updating a game's status, e.g.: // updateCompetitionStatusOnGameChange('617...b18');
Step 2: Optimized Version with Aggregation
For better performance (especially with large datasets), use MongoDB's aggregation pipeline to fetch competitions and their linked game statuses in a single database call, reducing round trips:
async function updateCompetitionStatusOnGameChangeOptimized(gameId) { try { const targetGameObjectId = mongoose.Types.ObjectId(gameId); // Use aggregation to join competitions with their games and extract statuses const competitionsWithStatusData = await Competition.aggregate([ { $match: { games: targetGameObjectId } }, { $lookup: { from: 'games', // Use your actual games collection name (not model name) localField: 'games', foreignField: '_id', as: 'linkedGames' } }, { $project: { compeititionName: 1, gameStatuses: { $map: { input: '$linkedGames', as: 'g', in: '$$g.gameStatus' } } } } ]); for (const comp of competitionsWithStatusData) { // Reuse the same status calculation logic as before let newCompetitionStatus; const hasPlaying = comp.gameStatuses.includes('playing'); const hasNotStarted = comp.gameStatuses.includes('notStarted'); const hasEnded = comp.gameStatuses.includes('ended'); if (comp.gameStatuses.every(status => status === 'ended')) { newCompetitionStatus = 'ended'; } else if (hasPlaying || (hasNotStarted && hasEnded)) { newCompetitionStatus = 'playing'; } else { newCompetitionStatus = 'notStarted'; } await Competition.findByIdAndUpdate(comp._id, { competitionStatus: newCompetitionStatus }); } } catch (error) { console.error('Failed to update competition status:', error); throw error; } }
Key Notes
- Model Setup: Ensure your
Competitionmodel defines thegamesfield as an array of ObjectId references:const competitionSchema = new mongoose.Schema({ compeititionName: String, games: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Game' }], competitionStatus: String }); - Error Handling: The examples include basic error handling, but you can extend this to fit your application's needs (e.g., logging to a service, alerting).
- Performance: The aggregation version is preferred for large datasets since it minimizes database requests.
内容的提问来源于stack exchange,提问作者YulePale

