如何创建满足多条件的MongoDB批量更新操作?
解决MongoDB Books集合的重复文档标记问题
Hey there, let's figure out how to mark those older duplicate books as inactive exactly as you described. The goal is to set active: false for documents that share the same title and author, are currently active, have different code values, and were updated earlier than the most recent entry in their group.
Step 1: Identify Documents to Update
First, we'll use an aggregation pipeline to pinpoint which documents need their active status changed. This pipeline filters and groups documents to isolate the older duplicates:
db.books.aggregate([ // Only consider books that are currently active { $match: { active: true } }, // Group by title + author to find the most recent update time per group { $group: { _id: { title: "$title", author: "$author" }, latestLastUpdated: { $max: "$lastUpdated" }, // Collect all docs in the group with their ID, update time and code groupDocs: { $push: { docId: "$_id", updatedAt: "$lastUpdated", code: "$code" } } } }, // Expand the grouped document list to process each entry individually { $unwind: "$groupDocs" }, // Filter out the most recent document in each group (keep only older ones) { $match: { $expr: { $lt: ["$groupDocs.updatedAt", "$latestLastUpdated"] } } }, // Only keep the ID of documents that need updating { $project: { _id: "$groupDocs.docId" } } ])
Step 2: Batch Update the Target Documents
Once we have the list of document IDs to update, we can use updateMany to bulk-set their active field to false. Here's how to tie it all together:
// Fetch all IDs of documents that need to be marked inactive const targetDocIds = db.books.aggregate([ { $match: { active: true } }, { $group: { _id: { title: "$title", author: "$author" }, latestLastUpdated: { $max: "$lastUpdated" }, groupDocs: { $push: { docId: "$_id", updatedAt: "$lastUpdated" } } } }, { $unwind: "$groupDocs" }, { $match: { $expr: { $lt: ["$groupDocs.updatedAt", "$latestLastUpdated"] } } }, { $project: { _id: "$groupDocs.docId" } } ]).toArray().map(entry => entry._id); // Perform the bulk update if there are documents to modify if (targetDocIds.length > 0) { const updateResult = db.books.updateMany( { _id: { $in: targetDocIds } }, { $set: { active: false } } ); print(`Updated ${updateResult.modifiedCount} documents to active: false`); } else { print("No documents found that match the update criteria."); }
Key Notes
- This approach ensures we only touch documents that meet all your criteria: same title/author, active=true, different code (since we're grouping by title/author and keeping the latest, any other entries in the group must have a different code per your example), and older
lastUpdatedtime. - If a title/author group has only one document, it won't be included in the update list—perfect, since there's no duplicate to mark inactive.
- If multiple documents in a group have the same
lastUpdatedtime, none of them will be updated (since$ltwon't match). If you need to handle this edge case (e.g., keep one active and mark others as inactive), you could adjust the aggregation to pick a secondary criteria like the highest/lowestcodevalue.
内容的提问来源于stack exchange,提问作者Igor
相关产品推荐
相关产品推荐

