Node.js操作MongoDB:生成起止时间间隔序列遇阻
Hey there! I see you're trying to generate a series of time points between start and end in your Schedule collection, incrementing by a specified duration. The issue with your current approach is that $add only performs a single addition—so you're just getting the first incremented time instead of the full sequence. Let's fix this with two solid approaches: one using MongoDB's aggregation pipeline, and another handling the logic on the Node.js side.
Option 1: MongoDB Aggregation Pipeline (Server-Side)
We can use MongoDB's aggregation operators like $range, $map, $subtract, and $multiply to generate the full sequence directly in the database. Here's how it works:
- Match the target Schedule document using its
_id. - Calculate the total number of intervals by dividing the time difference between
endandstartby your duration (plus 1 to include the end time). - Use
$rangeto create an array of integers representing each interval index. - Map each index to a time point by adding the index multiplied by the duration to the
starttime, and assign an incrementingid. - Unwind the array of intervals and replace the root to get each interval as a separate document.
Here's the updated code:
const { idSchedule } = req.params; const { duration } = req.body; const durationMs = duration * 60 * 1000; const objId = new ObjectID(idSchedule); Schedule.aggregate([ { "$match": { "_id": objId } }, { "$project": { "_id": 0, "intervals": { "$map": { "input": { "$range": [ 0, { "$add": [ { "$divide": [ { "$subtract": [ "$end", "$start" ] }, durationMs ] }, 1 ] } ] }, "as": "i", "in": { "id": { "$add": [ "$$i", 1 ] }, "time": { "$add": [ "$start", { "$multiply": [ "$$i", durationMs ] } ] } } } } } }, { "$unwind": "$intervals" }, { "$replaceRoot": { "newRoot": "$intervals" } } ]).then((response) => { res.send(response) }) .catch(err => res.status(500).send(err));
Option 2: Node.js Side Processing (Client-Side)
If you prefer more straightforward logic or are working with an older MongoDB version that doesn't support some aggregation operators, you can fetch the document first and generate the intervals in your Node.js code:
const { idSchedule } = req.params; const { duration } = req.body; const durationMs = duration * 60 * 1000; const objId = new ObjectID(idSchedule); Schedule.findById(objId) .then(doc => { const intervals = []; let currentTime = new Date(doc.start); const endTime = new Date(doc.end); let id = 1; // Loop until we pass the end time while (currentTime <= endTime) { intervals.push({ id: id++, time: currentTime.toISOString() }); // Increment the current time by the duration currentTime = new Date(currentTime.getTime() + durationMs); } res.send(intervals); }) .catch(err => res.status(500).send(err));
Key Notes:
- Both approaches ensure you include the
endtime as the final interval (as long as the duration divides evenly into the total time difference, or even if it doesn't—though you might want to adjust logic if partial intervals aren't desired). - Make sure your
startandendfields are stored as MongoDBDatetypes (not strings) for accurate date arithmetic. If they're strings, you'll need to convert them using$toDatein the aggregation pipeline, ornew Date()in Node.js.
内容的提问来源于stack exchange,提问作者zyka

