MongoDB中如何使用$addToSet添加唯一对象至数组?
解决MongoDB $addToSet因自动生成_id导致重复添加数组对象的问题
方案一:禁用子文档自动生成_id
MongoDB默认会给数组里的子文档自动生成_id字段,哪怕其他字段完全一致,$addToSet也会因为_id不同把它们当成不同对象重复添加。你可以在Schema里直接禁用子文档的_id生成:
修改你的userSchema:
const userSchema = new mongoose.Schema( { bankDetails: [ { _id: false, // 关键:关掉自动生成_id cardHolder: String, cardNumber: String, expiry: String, cvc: String, cardBrand: String, }, ] } )
改完之后,$addToSet会对比子对象的所有字段值,只有当所有字段完全匹配时才会跳过,否则就把新对象加入数组。
方案二:基于卡号判断是否添加(更贴合业务场景)
银行卡号cardNumber是天然的唯一标识,实际业务里通常只要卡号存在,就不该重复添加,不管其他信息有没有变化。可以用下面两种原子操作实现:
方式1:先判断再用$push添加
exports.createPayment = async (req, res) => { const { user, cardHolder, cardNumber, expiry, cvc, cardBrand } = req.body // 先匹配目标用户,同时确保数组里没有该卡号 const saveDetails = await User.findByIdAndUpdate( { _id: user._id, 'bankDetails.cardNumber': { $ne: cardNumber } }, { $push: { bankDetails: { cardHolder, cardNumber, expiry, cvc, cardBrand } } }, { new: true } ).exec(); // 如果返回null,说明该卡号已经存在 res.json(saveDetails || { message: "该银行卡信息已存在" }); }
方式2:用聚合管道自定义判断逻辑(MongoDB 4.2及以上支持)
通过聚合管道可以更灵活地控制逻辑,比如检查卡号是否存在,再决定是否添加:
exports.createPayment = async (req, res) => { const { user, cardHolder, cardNumber, expiry, cvc, cardBrand } = req.body const saveDetails = await User.findByIdAndUpdate( { _id: user._id }, [ { $set: { bankDetails: { $cond: [ // 判断数组里有没有目标卡号 { $in: [cardNumber, "$bankDetails.cardNumber"] }, "$bankDetails", // 已存在,保持原数组不变 // 不存在,把新对象追加到数组里 { $concatArrays: ["$bankDetails", [{ cardHolder, cardNumber, expiry, cvc, cardBrand }]] } ] } } } ], { new: true } ).exec(); res.json(saveDetails); }
方案三:给子文档字段加全局唯一索引(不推荐)
如果要强制整个集合里所有用户的银行卡号都不重复,可以给bankDetails.cardNumber创建唯一索引,但这种限制不符合大多数业务场景,所以不建议用。
内容的提问来源于stack exchange,提问作者sgt_pepper85
相关产品推荐
相关产品推荐

