10
Documents.update(
  {_id: Session.get("current_document_id")}, 
  {$push: {schema: {type: "text", size: size, name: name, label: label}}}
);

The above query is a Meteor collection, and 'Documents.update' maps to 'db.documents.update' in MongoDB documentation (http://docs.mongodb.org/manual/applications/update/). With that query I can add a schema document inside the main document. Subdocuments are stored in an array:

Document:
  schema:
    array:
      {type: "text", size: 6, name: "first_name", label: "First name"},
      {type: "text", size: 6, name: "last_name", label: "Last name"}

I want to modify the name and size attributes of the subdocuments with this query:

Documents.update(
  {_id: Session.get("current_document_id"), 'schema' : "first_name"}, 
  {$push: {schema: {type: "text", size: 7, name: name, label: "First Name2"}}}
);

But that operation append a new object directly under schema and deletes the array:

Document:
  schema:
      {type: "text", size: 7, name: "first_name", label: "First Name2"}

How can I modify the query to change the attributes avoiding this issue? After the query I would like to have this document:

Document:
  schema:
    array:
      {type: "text", size: 7, name: "first_name", label: "First name2"},
      {type: "text", size: 6, name: "last_name", label: "Last name"}
JohnnyHK
  • 305,182
  • 66
  • 621
  • 471
rtacconi
  • 14,317
  • 20
  • 66
  • 84

2 Answers2

22

You can update an existing array element using a $set operation that uses the $ positional operator to identify the array element matched in the selector like this:

Documents.update(
  {_id: Session.get("current_document_id"), 'schema.name': "first_name"}, 
  {$set: {'schema.$': {type: "text", size: 7, name: name, label: "First Name2"}}}
);

This will replace the matched schema element with the one included in the $set object.

If you only want to update individual fields of the targeted schema element, you can use dot notation. For example, to only update the size and name fields:

Documents.update(
  {_id: Session.get("current_document_id"), 'schema.name': "first_name"}, 
  {$set: {'schema.$.size': 7, 'schema.$.name': name}}
);
JohnnyHK
  • 305,182
  • 66
  • 621
  • 471
  • Thanks it works but I am getting this error: https://gist.github.com/4243896. I will have a look later to find the cause. The error is not very explanatory. – rtacconi Dec 09 '12 at 08:40
  • 1
    @rtacconi Looks like the `$` positional operator isn't supported in meteor's minimongo: https://github.com/meteor/meteor/issues/153 – JohnnyHK Dec 09 '12 at 15:00
  • I also got the same problem . But I am trying to update the array using php code. In my code, I dont want to replace the array. But, I want to add a new row in the array – Gireesh Doddipalli Jan 09 '14 at 10:57
  • When I run this piece of code I'm getting an error: "Uncaught Error: Not permitted. Untrusted code may only update documents by ID. [403]". Can't figure out this one. I don't have id in the objects inside an array, what to do? – aGit Aug 16 '15 at 13:26
  • 2
    @aGit For Meteor, you need to put this in a server-defined method. See http://stackoverflow.com/questions/31796164/updating-item-in-array-with-unique-id – JohnnyHK Aug 16 '15 at 13:59
  • 2
    @JohnnyHK thx a lot. I've got it solved. But the solution here described i'm not updating only a field of the object in the array but the whole object. If someone wants to update only one field of the object the modifier should go something like this: var modifier = {$set: {'schema.$.name': content}}; Again, thanks a lot for your answer! – aGit Aug 17 '15 at 20:48
  • @aGit @JohnnyHK : how can you do the update on a certain element when you are inside an update hook and have the modifer: `modifier.$set.myArray.index.value = x` when index is the position and value is what i want to set? I tried `modifier.$set.myArray[index].value` but didn't work... – Gobliins Sep 04 '18 at 10:20
  • @Gobliins Would be best to post that as a separate question. – JohnnyHK Sep 04 '18 at 14:40
  • @JohnnyHK Done: https://stackoverflow.com/questions/52163399/meteor-collection-hooks-updating-element-on-a-position-in-array/52164412#52164412 And i found also a solution – Gobliins Sep 05 '18 at 10:14
2

You can use arrayFilters with positional $[] operator

The example below from official mongodb documentation uses "elem" as the positional identifier


Consider a collection students2 with the following documents:

{
   "_id" : 1,
   "grades" : [
      { "grade" : 80, "mean" : 75, "std" : 6 },
      { "grade" : 85, "mean" : 90, "std" : 4 },
      { "grade" : 85, "mean" : 85, "std" : 6 }
   ]
}
{
   "_id" : 2,
   "grades" : [
      { "grade" : 90, "mean" : 75, "std" : 6 },
      { "grade" : 87, "mean" : 90, "std" : 3 },
      { "grade" : 85, "mean" : 85, "std" : 4 }
   ]
}

To modify the value of the mean field for all elements in the grades array where the grade is greater than or equal to 85, use the positional $[] operator and arrayFilters:

db.students2.update(
   { },
   { $set: { "grades.$[elem].mean" : 100 } },
   {
     multi: true,
     arrayFilters: [ { "elem.grade": { $gte: 85 } } ]
   }
)
Sri
  • 39
  • 1
  • 1