【问题标题】:MongoDB (Mongoose) update element in array of arrayMongoDB(Mongoose)更新数组数组中的元素
【发布时间】: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”)?

【问题讨论】:

    标签: arrays node.js mongodb express mongoose


    【解决方案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。所以现在一切正常,谢谢!
      • 介意接受答案吗?我为你感到高兴。享受编码!
      猜你喜欢
      • 2013-09-07
      • 1970-01-01
      • 1970-01-01
      • 2022-01-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多