如何在MongoDB双层嵌套数组中添加或移除指定元素?
嵌套数组点赞切换功能实现方案
你原有代码存在两个核心问题:
- 两层嵌套数组的场景下,普通
$占位符只能匹配第一层符合条件的数组元素,搭配$[]全量匹配会命中所有评论,无法精确定位到目标回复的点赞数组 - 仅用
$push操作只能实现添加逻辑,无法实现存在即删除的切换效果
以下是两种可落地的实现方案:
方案1:数组过滤定位法(MongoDB 3.6+ 支持,语法简洁)
用arrayFilters精确定位到目标回复,先判断用户是否已点赞,再执行对应更新操作:
// 第一步:查询用户是否已经给目标回复点赞 const targetPost = await collection('post').findOne( { postId: postId, 'commentsOnPost.commentId': commentId, 'commentsOnPost.repliesOnPost.replyId': replyId }, { projection: { 'commentsOnPost.repliesOnPost.likesOnReply': 1 } } ) // 定位到目标回复的点赞列表 const targetLikes = targetPost.commentsOnPost .find(comment => comment.commentId === commentId) .repliesOnPost.find(reply => reply.replyId === replyId) .likesOnReply const hasLiked = targetLikes.includes(userid) // 第二步:执行对应更新 const updateAction = hasLiked // 已点赞则移除 ? { $pull: { 'commentsOnPost.$[cmt].repliesOnPost.$[rep].likesOnReply': userid } } // 未点赞则添加 : { $addToSet: { 'commentsOnPost.$[cmt].repliesOnPost.$[rep].likesOnReply': userid } } await collection('post').findOneAndUpdate( { postId: postId }, updateAction, { arrayFilters: [ { 'cmt.commentId': commentId }, { 'rep.replyId': replyId } ] } )
方案2:原子聚合更新法(MongoDB 4.2+ 支持,高并发场景适用)
直接用更新聚合管道实现单语句原子操作,避免并发导致的数据不一致:
await collection('post').findOneAndUpdate( { postId: postId, 'commentsOnPost.commentId': commentId, 'commentsOnPost.repliesOnPost.replyId': replyId }, [ { $set: { commentsOnPost: { $map: { input: '$commentsOnPost', as: 'cmt', in: { $cond: [ { $ne: ['$$cmt.commentId', commentId] }, '$$cmt', { $mergeObjects: [ '$$cmt', { repliesOnPost: { $map: { input: '$$cmt.repliesOnPost', as: 'rep', in: { $cond: [ { $ne: ['$$rep.replyId', replyId] }, '$$rep', { $mergeObjects: [ '$$rep', { likesOnReply: { $cond: [ { $in: [userid, '$$rep.likesOnReply'] }, // 已点赞则过滤掉对应用户 { $filter: { input: '$$rep.likesOnReply', cond: { $ne: ['$$this', userid] } } }, // 未点赞则拼接用户id { $concatArrays: ['$$rep.likesOnReply', [userid]] } ] } } ] } ] } } } } ] } ] } } } } } ] )
内容的提问来源于stack exchange,提问作者Deepak
相关产品推荐
相关产品推荐

