MongoDB更新递归嵌套数组中所有changed字段时路径不存在报错咨询
Hey there! Let's tackle this problem step by step.
Why You're Seeing That Error
The error pops up because when you use hardcoded paths like level_2.$[].level_3.$[].changed in your $set operation, MongoDB expects every intermediate path (like level_3) to exist in all elements of the parent array. In your example, the first object in level_2 doesn't have a level_3 field, so MongoDB can't apply the update to that non-existent path and throws the error.
The Solution: Use an Aggregation Pipeline for Recursive Updates
Instead of manually listing every possible nested path (which gets messy and breaks when paths are missing), we can use MongoDB's aggregation pipeline (available in version 4.2+) to recursively traverse the entire document and update every changed field—regardless of how deep it is or whether intermediate levels exist.
Here's a working update command using a custom JavaScript function (via $function, available in MongoDB 4.4+) to handle the recursion:
// Replace 'your-collection-name' with your actual collection name db.getCollection('your-collection-name').update( { "id": 1 }, // Match the document you want to update [ { $set: { root: { $function: { body: function(doc) { // Recursive function to update all "changed" fields to true const updateChanged = (obj) => { if (Array.isArray(obj)) { // If it's an array, iterate over each element and recurse return obj.map(item => updateChanged(item)); } else if (typeof obj === 'object' && obj !== null) { // If it's an object, set "changed" to true, then recurse over all properties const updatedObj = { ...obj, changed: true }; Object.keys(updatedObj).forEach(key => { updatedObj[key] = updateChanged(updatedObj[key]); }); return updatedObj; } // For non-object/non-array values, just return as-is return obj; }; return updateChanged(doc); }, args: ["$$ROOT"], // Pass the entire document to the function lang: "js" } } } }, // Replace the original document with our updated root object { $replaceRoot: { newRoot: "$root" } } ] )
How This Works
- The
$functionlets us write a custom JavaScript function that recursively walks through every part of your document:- For arrays, it maps over each element and applies the same update logic.
- For objects, it sets the
changedfield totrue, then recursively checks all other properties in the object. - For primitive values (like numbers, strings), it leaves them untouched.
- We then use
$replaceRootto replace the original document with our fully updated version.
Alternative: Pure Aggregation (No JavaScript)
If you prefer to avoid JavaScript, you can use a recursive aggregation with $map and $mergeObjects, but it's more verbose and requires handling each potential array type explicitly. The $function approach is far more flexible for arbitrary nested structures.
Key Takeaway
Hardcoding nested paths in $set works only if every intermediate level exists in all elements. For dynamic or partially nested structures, using an aggregation pipeline with recursion is the reliable way to update all changed fields across every level.
内容的提问来源于stack exchange,提问作者Hector Landete

