1

I am making a voting system in which an object will have an array called scores, every time it is qualified a record will be entered to this array as long as the same id does not exist.

Sample Doc :

{
 name:"jose luis",
 scores:[]
}

The user push :

{id:1,stars:5}

Updated Doc :

{
 name:"jose luis",
 scores:[{id:1,stars:5}]
}

The user push 2nd time :

{id:1,stars:4}

Updated Doc :

{
 name:"jose luis",
 scores:[{id:1,stars:4}]
}  //this should update the value of the stars under the array element with the existing id.

I have tried this :

   Politico.update(
        { name: "jose luis" },
        {
            $setOnInsert: { "$scores.id": body.req.id},
            "$addToSet": {
                scores: {
                    "id": body.req.id,
                    "starts": body.req.stars
                }
            }


        },
        { upsert: true }

I have tried this but it doesn't work, how can I fix it? Thanks a lot.

2
  • Are you using MongoDB version 4.2 or above ? Commented Apr 3, 2020 at 23:27
  • 4.2.3 is my current version Commented Apr 3, 2020 at 23:54

1 Answer 1

1

On MongoDB 4.2 or above as you can use aggregation-pipeline in updates, try below query :

Politico.updateOne(
  { name: "jose luis" },
  [{
    $addFields: {
      scores: {
        $reduce: {
          input: "$scores",
          initialValue: [{ id: 1, stars: 5 }], // Input
          in: {
            $cond: [
              { $in: ["$$this.id", "$$value.id"] }, /** Check id exists in 'scores' array */
              "$$value", /** If YES, return input */
              { $concatArrays: ["$$value", ["$$this"]] }/** If NO, concat value(holding array) with current object as array */
            ]
          }
        }
      }
    }
  }]);

You might not need { upsert: true } as you're not writing a new document with name : "jose Luis" & respective scores array. But if you wanted to do that :

Politico.updateOne(
  { name: "jose luis" },
  [{
    $addFields: {
      scores: {
        $reduce: {
          input: "$scores",
          initialValue: [{ id: 1, stars: 5 }], // Input
          in: {
            $cond: [
              { $in: ["$$this.id", "$$value.id"] }, /** Check id exists in 'scores' array */
              "$$value", /** If YES, return input */
              { $concatArrays: ["$$value", ["$$this"]] }/** If NO, concat value(holding array) with current object as array */
            ]
          }
        }
      }
    }
  },
  {$addFields : {scores : { $ifNull: [ "$scores", {id : 13, stars: 3} ] }}} /** This additional 'addFields' will add 'scores' array with input object to newly created doc */
],{upsert : true});

Test : MongoDB-Playground

Sign up to request clarification or add additional context in comments.

13 Comments

Thanks for your valuable help, something strange happens. If I add another item with a different id from the one already found, it doesn't add it, it only updates it. If I enter an element with an id that is not present in the array, I should add it, otherwise update it. I thought the solution would be simpler XD
@yavg : Can elaborate you issues, I couldn't understand what's happening, Please provide some examples..
@yavg : That playground only for finds/aggregation, You need to test it in DB with update() !! It should work as expected..
excuse my ignorance, there are things that are not clear to me. for example, when you use the $ symbol twice, and what is the $reduce for in this case? you are the best!
Thank you very much, I am sorry to ask you again for help and bother you.
|

Your Answer

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.