【问题标题】:How to update embedded document in array in mongoose where operation should be atomic, should update on the latest one and return updated document?如何更新猫鼬数组中的嵌入文档,操作应该是原子的,应该更新最新的并返回更新的文档?
【发布时间】:2020-12-19 05:51:31
【问题描述】:

目前正在使用 mongoose 开发 node.js 和 MongoDB。

//Document in mongoDB
{
  name: 'first',
  _id: '1000'
  phases: [{
      _id: 1,
      phaseName: 'phase1',
      stories: [{
          _id: s1,
          sname: 'sfirst'
        },
        {
          _id: s2,
          sname: 'ssecond'
        },
        {
          _id: s3,
          sname: 'sthird'
        }
      ],

    },
    {
      _id: 2,
      phaseName: 'phase2',
      stories: [{
          _id: s1,
          sname: 'sfirst'
        },
        {
          _id: s2,
          sname: 'ssecond'
        },
        {
          _id: s3,
          sname: 'sthird'
        }
      ],

    }
  ],
}

我只有

  1. id=1000(在 mongodb 中查找此文档)
  2. phaseId=1 和 remstoryId=s2(即从 id 为 1 的阶段的故事中删除 s2)
  3. phaseId=2,storyId={ _id: s4, sname: Fourth},位置:1(在第 2 阶段的索引 1 处添加故事中的故事)

主要要求是这个操作应该是

  1. 原子
  2. 其他用户在执行上述操作时不能对同一文档执行操作。我的意思是锁定此文档。

我们可以使用 findOne() 和 save() 来执行此操作

Model.findOne({
  id
}, (err, plan) => {
  if (!err) {

    //REMOVE THE STORY FROM PHASE
    const phase = plan.phases.find((phase) => phase._id === delPhaseID);
    phase.stories = phase.stories.filter((story) => story._id !== delStoryId);

    //ADD STORY IN STORIES
    const addPhase = plan.phases.find((phase) => phase._id === addPhaseID);
    addPhase.stories.splice(pos, 0, story);

    //SAVE PLAN
    plan.save((err, doc) => {
       if(!err)
         console.log(doc) // UPDATED DOCUMENT
       });
  }
})

但是这个操作不是原子的,如下图取自masteringjs。我们该如何解决这个问题

【问题讨论】:

    标签: javascript node.js mongodb mongoose


    【解决方案1】:

    你可以试试findOneAndUpdate()arrayFilters

    • $pull 删除p1 id 的故事是s1
    • $pushp2 的特定位置使用 $position
    • new: true 返回更新后的文档
    Model.findOneAndUpdate(
        { _id: "1000" },
        {
            $pull: {
                "phases.$[p1].stories": {
                    _id: "s1"
                }
            },
            $push: {
                "phases.$[p2].stories": {
                    $each: [{
                        _id: "s4",
                        sname: "fourth"
                    }],
                    $position: 1
                }
            }
        },
        {
            arrayFilters: [
                { "p1._id": 1 },
                { "p2._id": 2 }
            ],
            new: true
        }
    )
    

    【讨论】:

    • 正在处理它。只要它在我的项目中起作用,我就会接受你的回答。顺便说一句,您的回答学到了很多东西,尤其是使用带有 $push 和 $pull 的 findOneAndUpdate。非常感谢。
    • 谢谢,它成功了。我还想知道你给我的解决方案中有什么方法,如果拉操作以某种方式失败,那么整个操作就会失败并且 MongoDB 会抛出错误。如果没有,那么有什么办法可以做到这一点。
    • 1) 你可以用 2 个查询来完成,第一次拉,然后第二次推送,2) 可能你可以用 update with aggregation pipeline
    猜你喜欢
    • 2012-02-15
    • 2015-04-21
    • 2012-04-16
    • 2011-10-25
    • 2012-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多