Search code examples
mongodbmongodb-queryaggregation-frameworklaravel-mongodb

update specific element from nested document array mongodb where has two matches


I need to update or create if not exist, specific obj,set score.b1 =50 and total=100 where object match curse=5 block=2

{ "_id":"sad445"
  "year":2020,
  "grade":4,
  "seccion":"A",
    "id": 100,
  "name": "pedro",
  "notes":[{"curse":5, 
          "block":1, 
          "score":{ "a1": 5,"a2": 10, "a3": 15},
          "total" : 50
          },{
          "curse":5, 
          "block":2, 
          "score":{ "b1": 10,"b2": 20, "b3": 30},
          "total" : 20
          }
   ]
}

I can update all obj but I need to update or create specific elem from the score and not all. and/or create objs "notes":[{curse, block and score}] if notes is empty notes:[]

notas.UpdateMany(
{"$and":[{"_id":"sad445"},{"notes":{"$elemMatch":{"curse":5,"block":3}}}]},

{"$set":{"updated_at":{"$date":{"$numberLong":"1620322881360"}},

"notes.$.score":{"vvkzo":15,"i2z4i":2,"i2z4i|pm":5},
"notes.$.total":100}},

{"multiple":false})

Solution

  • Demo - https://mongoplayground.net/p/VaE28ujeOPx

    Use $ (update)

    The positional $ operator identifies an element in an array to update without explicitly specifying the position of the element in the array.

    the positional $ operator acts as a placeholder for the first element that matches the query document, and

    the array field must appear as part of the query document.

    db.collection.update({
      "notes": {
        "$elemMatch": { "block": 2, "curse": 5 }
      }
    },
    {
      $set: { "notes.$.score.b4": 40 }
    })
    

    Read upsert: true

    Optional. When true, update() either:

    Creates a new document if no documents match the query. For more details see upsert behavior. Updates a single document that matches the query. If both upsert and multi are true and no documents match the query, the update operation inserts only a single document.

    To avoid multiple upserts, ensure that the query field(s) are uniquely indexed. See Upsert with Unique Index for an example.

    Defaults to false, which does not insert a new document when no match is found.


    Update

    Demo - https://mongoplayground.net/p/iQQDyjG2a_B

    Use $function

    db.collection.update(
        { "_id": "sad445" },
        [
          {
            $set: {
              notes: {
                $function: {
                  body: function(notes) {
                            var record = { curse:5, block:2, score:{ b4:40 } };
                            if(!notes || !notes.length) { return [record]; } // create new record and return in case of no notes
                            var updated = false;
                            for (var i=0; i < notes.length; i++) {
                                if (notes[i].block == 2 && notes[i].curse == 5) { // check condition for update
                                    updated = true;
                                    notes[i].score.b4=40; break; // update here
                                }
                            }
                            if (!updated) notes.push(record); // if no update push the record in notes array
                            return notes;
                        },
                  args: [
                    "$notes"
                  ],
                  lang: "js"
                }
              }
            }
          }
        ]
    )