Update the property of an object in an nested array

I’m new to mongo and have already searched through most of this forum to find the solution to my current problem, but I haven’t been able to, so I apologize in advance if my question appears repetitive or stupid.

I have provided one real-world example based on the mongo db documents and examples:

db.students.insertOne(
   { "_id" : 1,
       "key":"first",
      "grades" : [
        { type: "quiz", questions: [{name:"1",value:"1"}, {name:"2",value:"2"}],images:["1","2"] },
        { type: "quiz" },
        { type: "hw", questions: [ {name:"5",value:"5"}, {name:"6"} ] },
        { type: "exam", questions: [ {name:"7",value:"7"}, {name:"8",value:"8"},{name:"1",value:"1"} ],images:["1","2"] },
      ]
   }
)

In my search, I am looking for all objects containing {“name”:“1”} and updating just the name to "10
Here is the query:

db.getCollection("students").updateMany({"grades.questions.name":"1"},{$set:{"grades.$[].questions.$[x].name":"10"}},
{ arrayFilters: [  {"x.name": "1" } ] }
,{upsert:false})

But it complains that ‘grades.1.questions’ must exist in the document in order to apply array updates. This is why I included the first condition “grades.questions.name”:“1”.

any help will be appreciated!

your query works, but you have this incorrect data. it has no “questions.name” thus the problem happens.

1 Like

That’s true, but I did it intentionally as this is the current state of our database; there are some records that don’t even have the second array!
Do you think it is possible to tailor the query to skip such situations without complaining?

you may need to change your schema if it proves difficult to write a query. for example, splitting each question into their own field and embedding grade type in them instead of embedding under questions under grades. if then, you would loop only on questions but add an extra type check. pros and cons.

another thing to do might be (pre)processing all documents and insert an empty array if there is none, because if something does not exists you cannot tell if it is array or not, but you can tell “oh, this array is empty”. this kind of operations are required when the shape of documents, the schema, is important in queries.

you can do this per query, depending on the type of query, or patch the whole database to reflect this. pros/cons: query time versus disk size.

Thank for your help!
This is how I checked the existence, and it works well

db.getCollection("students").updateMany({"grades.questions.name":"1"}
,{$set:{"grades.$[y].questions.$[x].name":"10"}},
{ arrayFilters: [  {"y.questions":{$exists:true}},{"x.name": "1" } ] }
)

As you can see, the existence check was placed on the array filter, but if I place it on the first part, it does not work!

db.getCollection("students").updateMany({"grades": { $exists: true, $ne: [],$elemMatch: { "questions": {$exists: true } 
}}},{$set:{"grades.$[].questions.$[x].name":"10"}},{ arrayFilters: [  {"x.name": "1" } ] })

As I understand it, the first part selects the right record and then applies an update or any other operator based on _id or any other condition, but this example does not work. am I right?

2 Likes

it is nice you have a working solution.

for the second one, the first part of the query is about selecting a matching document, and I am guessing when you check for the existence, this document will match and be used because the condition is satisfied by other non-empty elements in the array. but when it comes to processing array elements in this document, you will hit the empty-field element.

it is like requesting a bunch of numbers from a user, denying characters other than numbers, but your function does division and hits the divide-by-zero problem.

You might be already aware of this page, but check it out if not:

Thank you so much for your quick response.
I totally understand now what you said!

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.