
在单次 findByIdAndUpdate 中,若为多个数组字段分别声明 $addToSet,因 JavaScript 对象键名重复导致后写覆盖前写,仅最后一个生效;正确做法是将所有 $addToSet 字段合并至一个对象内。
在单次 `findbyidandupdate` 中,若为多个数组字段分别声明 `$addtoset`,因 javascript 对象键名重复导致后写覆盖前写,仅最后一个生效;正确做法是将所有 `$addtoset` 字段合并至一个对象内。
MongoDB 的更新操作符(如 $addToSet)要求在同一更新文档中每个操作符只能出现一次。当你在 JavaScript 对象字面量中多次声明 $addToSet,例如:
$addToSet: { androidIds: deviceId },
$addToSet: { advertisingIds: advertisingId },
$addToSet: { firebaseInstallationIds: firebaseInstallationId }由于 JavaScript 对象不允许重复键名,解析器会保留最后一个 $addToSet 声明(即 { firebaseInstallationIds: ... }),其余被静默丢弃——这正是只有 firebaseInstallationIds 被更新的根本原因。
✅ 正确写法是将所有需去重追加的字段统一归入单个 $addToSet 对象:
let user = {
_id: userId,
$set: { [`firebaseTokens.${deviceId}`]: token },
$addToSet: {
androidIds: deviceId,
advertisingIds: advertisingId,
firebaseInstallationIds: firebaseInstallationId
}
};
let options = { new: true, setDefaultsOnInsert: true };
let userDoc = await UserModel.findByIdAndUpdate(user._id, user, options);⚠️ 注意事项:
$addToSet仅对数组类型字段有效,确保androidIds、advertisingIds等 Schema 字段定义为[{ type: String }](而非String或Map);- 若需同时执行
$set、$addToSet、$push等多个操作符,各自仍须保持唯一顶层键名(如$set、$addToSet、$push并存是合法的);- 对于嵌套数组或复杂条件去重,可结合
$each(如$addToSet: { tags: { $each: ['a', 'b'] } })扩展能力。
该模式不仅修复了覆盖问题,也符合 Mongoose 和 MongoDB 原生驱动的更新协议规范,是批量维护多数组字段的推荐实践。


















