Mongoose中findOneAndUpdate结合$unshift操作异常问题咨询
你的问题源于MongoDB数组更新操作符的使用方式错误,具体是$unshift的位置放错了,直接导致了这两个异常问题:
1. 为什么url和current被忽略,只生成_id和date?
在你的update_option里,你把$unshift嵌套在了profileImageURLs字段内部:
const update_option = { profileImageURLs: { $unshift: { url: `https://resources/profile_image`, current: true, }, }, };
这是完全错误的写法。MongoDB的数组操作符(比如$unshift、$push)需要作为顶层键存在,字段名要放在操作符的值里。你当前的写法会让MongoDB误以为你要把profileImageURLs字段设置为一个包含$unshift键的对象,但因为Schema定义profileImageURLs是数组类型,MongoDB会自动把这个对象包装成数组的一个元素。而$unshift不是你Schema里定义的字段,会被直接忽略,最终只根据Schema的默认值生成date和自动的_id,所以数组元素里没有url和current。
2. 为什么第二次更新会生成二维数组?
第一次错误更新后,profileImageURLs已经变成了包含单个错误对象的数组。当你第二次执行同样错误的更新时,MongoDB会尝试将新的{ $unshift: {...} }对象赋值给profileImageURLs,但因为当前字段是数组类型,它会把这个新对象也包装成一个数组,最终导致profileImageURLs变成二维数组(数组里的每个元素又是一个数组)。
修正后的代码
把$unshift提到顶层,正确的更新选项写法如下:
const updateProfileImageUrl = async (user_id) => { const search_option = { user: user_id }; // 修正update_option的结构,将$unshift作为顶层操作符 const update_option = { $unshift: { profileImageURLs: { url: `https://resources/profile_image`, current: true, }, }, }; const should_return_updated_profile = { new: true }; const updated_profile = await Profile.findOneAndUpdate( search_option, update_option, should_return_updated_profile ); console.log("🚀 ~ file: profileServices.js ~ line 1558 ~ updateProfileImageUrl ~ updated_profile", updated_profile); };
这样MongoDB就能正确识别$unshift操作,在profileImageURLs数组的开头添加包含url、current和date的新元素。
修复已有错误数据
如果你已经有了错误的二维数组数据,可以通过以下代码修正:
// 将错误的二维数组扁平化 await Profile.updateMany( { profileImageURLs: { $type: "array", $elemMatch: { $type: "array" } } }, { $set: { profileImageURLs: { $reduce: { input: "$profileImageURLs", initialValue: [], in: { $concatArrays: ["$$value", "$$this"] } } } } } );
这个操作会把二维数组扁平化变成一维数组。如果需要补全缺失的url和current,可以根据实际业务逻辑添加额外的更新规则。
内容的提问来源于stack exchange,提问作者AG_HIHI

