MongoDB(Mongoose)更新数组数组中的元素

Posted

技术标签:

【中文标题】MongoDB(Mongoose)更新数组数组中的元素【英文标题】:MongoDB (Mongoose) update element in array of array 【发布时间】:2020-04-18 16:30:44 【问题描述】:

我目前正在使用 node.js express 构建一个 REST-Api,但不知道如何更新/添加元素到 scores 数组。

这是我的 MongoDB 集合中的一个文档(它的外观):

我的猫鼬模型:

const challengesSchema = new mongoose.Schema(
    createdBy:
        type:String,
        required: true
    ,
    mapType:
        type:String,
        required: true
    ,
    time:
        type:Number,
        required: true
    ,
    numberOfMaps:
        type:String,
        required: true
    ,
    maps:
        type:Array,
        required: true
    ,
    pin:
        type: String,
        required: true
    ,
    takenBy:
        type: Array,
        required: false
    
)

基本上我会收到一个 id,我可以用它来做 Challenges.findById(challenge._id) 。 我想出了如何将对象添加到takenBy Array,如下所示:

Challenges.findOneAndUpdate(
   _id: challenge._id ,
  
    $push: 
      takenBy: user
    
  
);

如何向数组“takenBy”中的分数数组添加一个元素(分数,例如“20”)?

【问题讨论】:

【参考方案1】:

您可以像这样使用filtered positional operator $ 一次性推送分数并计算新的 TotalScore。

router.put("/challenges/:id/:scoreId", async (req, res) => 
  let score = req.body.score;

  try 
    let result = await Challenges.findByIdAndUpdate(
      req.params.id,
      
        $push:  "takenBy.$[inner].scores": score ,
        $inc: 
          "takenBy.$[inner].TotalScore": score
        
      ,
      
        arrayFilters: [ "inner._id": req.params.scoreId ],
        new: true
      
    );

    if (!result) return res.status(404);

    res.send(result);
   catch (err) 
    console.log(err);
    res.status(500).send("Something went wrong");
  
);

测试

让我们拥有这个现有的文档:


    "_id" : ObjectId("5e08fe4c0bc1b932e8726a0f"),
    "maps" : [ ],
    "takenBy" : [
        
            "_id" : "id1",
            "TotalScore" : NumberInt(100),
            "scores" : [
                NumberInt(20),
                NumberInt(60),
                NumberInt(20)
            ]
        ,
        
            "_id" : "id2",
            "TotalScore" : NumberInt(30),
            "scores" : [
                NumberInt(10),
                NumberInt(20)
            ]
        
    ],
    "createdBy" : "5dfe0...",
    "mapType" : "World",
    "time" : NumberInt(2),
    "numberOfMaps" : "2",
    "pin" : "9558",
    "__v" : NumberInt(0)

如果我们想给 id1 加上 50 分,我们发送一个 PUT 请求 (http://..../challenges/5e08fe4c0bc1b932e8726a0f/id1) 并带有这个正文:


    "score": 50

结果会是这样的:


    "_id" : ObjectId("5e08fe4c0bc1b932e8726a0f"),
    "maps" : [ ],
    "takenBy" : [
        
            "_id" : "id1",
            "TotalScore" : NumberInt(150),
            "scores" : [
                NumberInt(20),
                NumberInt(60),
                NumberInt(20),
                NumberInt(50)
            ]
        ,
        
            "_id" : "id2",
            "TotalScore" : NumberInt(30),
            "scores" : [
                NumberInt(10),
                NumberInt(20)
            ]
        
    ],
    "createdBy" : "5dfe0...",
    "mapType" : "World",
    "time" : NumberInt(2),
    "numberOfMaps" : "2",
    "pin" : "9558",
    "__v" : NumberInt(0)

如您所见,分数已添加到相关项目数组中,并且它的 TotalScore 也增加了 50,得到 TotalScore 150

【讨论】:

谢谢!很棒的方法!有没有办法我不仅可以推送到分数数组,而且可以指定将我的新分数放入该数组的索引? @user2557097 是的,这似乎是可能的,请查看docs 谢谢,它有效!像这样:$push: "takenBy.$[inner].scores": $each: [score], $position: scorePosition , , @user2557097 太棒了:) 你自己做的。请重新考虑接受这个答案,这是我们在猫鼬中更新的方式。 我接受了你的回答。再次感谢!另一个我想不通的问题:我的问题的前提是 takeBy 数组中有带有 id 的对象。当用户没有提交分数时,它不会添加分数。有没有办法在一个查询中使用相应的分数数组和 TotalScore 在 take by 数组中创建对象?我可以找到该字段并搜索带有 id 的对象,如果它存在,则执行您的查询,如果不创建一个新字段。有没有办法在一个查询中做到这一点?【参考方案2】:

您可以先检索对象:

const updateChallenge = async (req,res) => 
    const challenge = await Challenges.findById(id);
    // Then you make duly changes to it with vanilla js:
    // Find which element in the array takenBy to update with regular js, methods 
    like filter work or hardcode it

    challenge.takenBy[1].push(newElement);

    await challenge.save();
    // DONE! :)

当然,如果您愿意,也可以使用解构!

【讨论】:

【参考方案3】:

...如何更新/添加元素到分数数组。

我将首先检索该数组...推送新值,然后更新。

类似:

Challenges.findById(challenge._id,(err, db_result) => 
  if(err)
    console.log(err)              // error management
    //...
  

  if(db_result)

    // Get the takenBy array
    let takenBy = db_result.takenBy

    // Who are we talking about... You need the second _id for that record
    let who_id = user._id

    let targetIndex = null
    let byWho = takenBy.filter((who, index) => 

      let gotYa = who._id === who_id
      if (gotYa)
        targetIndex = index
      
      return gotYa  // Boolean used as a return for .filter()
    )

    if(byWho.length>0)
      console.log("Got someone... Index: ", targetIndex)
    else
      console.log("No one found?")
      return
    


    // Push the new value using the index where to update.
    takenBy[targetIndex].scores.push(challenge.score)

    Challenges.updateOne(_id:challenge._id,
        $set: 
          takenBy: takenBy
        
      , (err, data) => 
        console.log(data)
      
    )

  
);

【讨论】:

嗬,我错过了 score 数组在 takenBy 数组中的事实......我会更新我的答案。但概念是一样的。是getpushset。 ;) hahem...也许您必须定位正确的takenBy 元素...您有第二个id 吗?再等等……我正在编辑 试试... ;) 我们应该很接近了。 终于成功了!最后的问题是 updateOne 函数只更改数据库文档,当它有一个回调函数时,就像你在第一行中所做的那样。我找到了这个link。所以现在一切正常,谢谢! 介意接受答案吗?我为你感到高兴。享受编码!

以上是关于MongoDB(Mongoose)更新数组数组中的元素的主要内容,如果未能解决你的问题,请参考以下文章

过滤和更新 mongodb mongoose 中的嵌入式数组

使用mongoose在mongoDB中更新数组的数组

使用 mongoose 在 mongoDb 中更新数组内的对象 [重复]

如何在 mongodb/mongoose 的嵌套数组中检查是不是存在并更新对象?

如何使用 Mongoose 正确更新数组中的元素

以角度(mongoose/mongodb)更新模型,特别是作为模型属性的对象数组