MongoDB (Mongoose) update element in array of array

Issue

I’m currently building a REST-Api with node.js express and can’t figure out how to update / add elements to the scores Array.

Here is one document from my MongoDB collection (how it should look):

field

My mongoose model:

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
    }
})

Basically I receive an id, which I can use to do Challenges.findById({challenge._id}) .
I figured out how to add an object to the takenBy Array, like so:

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

How can I add an element (score, like “20”) to the scores array in the array ‘takenBy’ ?

Solution

You can push score and calculate the new TotalScore in one go using filtered positional operator $ like this.

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");
  }
});

Test:

Let’s have this existing document:

{
    "_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)
}

If we want to add a score of 50 to then id1, we send a PUT request (http://…./challenges/5e08fe4c0bc1b932e8726a0f/id1) with this body:

{
    "score": 50
}

The result will be like this:

{
    "_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)
}

As you see the score is added to the related item array, and it’s TotalScore is also incremented by 50 giving the TotalScore 150

Answered By – SuleymanSah

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published