2

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' ?

SuleymanSah
18k6 gold badges38 silver badges60 bronze badges
asked Dec 29, 2019 at 18:30
0

3 Answers 3

3

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 Dec 29, 2019 at 19:39
Sign up to request clarification or add additional context in comments.

9 Comments

Thanks! Awesome approach! Is there a way I could not just push to the scores array, but can specify the index where to put my new score in that array?
@user2557097 yes it seems possible, please have a look at the docs
thanks, it works! Like this: $push: { "takenBy.$[inner].scores": { $each: [score], $position: scorePosition }, },
@user2557097 great:) You did it by yourself. Please reconsider to accept this answer, this is the way how we update in mongoose.
I accepted your answer. Thanks again! Another problem I can't figure out: The premise of my question was that the takenBy array has objects with ids in it. When the user hasn't submitted a score, it does not add a score. Is there a way to create the object in the taken by array with the corresponding score array and TotalScore in one query? I could find the field and search for the Object with the id, if it exists, execute your query, if not create a new field. Is there a way to do this in one query?
|
1

You can retrieve the object first:

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

Of course you can use destructuring if you prefer!

answered Dec 29, 2019 at 23:01

Comments

0

...how to update / add elements to the scores Array.

I would first retrieve that array... Push the new value, then update.

Something like:

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)
 }
 )
 }
});
answered Dec 29, 2019 at 18:48

12 Comments

Ho, I missed the fact the score array is in the takenBy array... I will update my answer. But the concept is the same. It's get, push, set. ;)
hahem... Maybe you have to target the right takenBy element... Do you have the second id for it? Hold on a bit more... I'm editing
Try that... ;) We should be close.
It finally works! The problem in the end was that the updateOne function only changes the database document, when it has a callback function, like you did in the first line. I found this link. So everything works now, thanks!
Mind to accept the answer then? I'm happy for you. Enjoy coding!
|

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.