8

How can I update a record in a document with multiple levels of array nesting?

My document structure is the following:

{
  "_id": "5bfa09f0a0441f38d45dcc9c",
  "nombre": "PROYECTO MAIN",
  "area": "Sistemas",
  "fecha": "27/01/2018",
  "reuniones": [
    {
      "_id": "5bfa09f0a0441f38d45dcc99",
      "objetivo": "Objetivo MODIFICADO",
      "fecha": "25/10/2018",
      "participantes": [
        {
          "nomina": 1,
          "nombre": "MODIFICADO",
          "rol": "rol",
          "area": "area",
          "firma": "url/imagen.jpg"
        },
        {
          "nomina": 2,
          "nombre": "nombre 2",
          "rol": "rol",
          "area": "area",
          "firma": "url/imagen.jpg"
        }
      ]
    }
  ],
  "_class": "proyecto"
}

Using the following query, returns me the document mentioned above.

 db.proyectos.find({
    _id:ObjectId("5bfa09f0a0441f38d45dcc9c"),
    "reuniones._id":ObjectId("5bfa09f0a0441f38d45dcc99"), 
    "reuniones.participantes.nomina":2 
 })

I want to update the firma field of participant with nomina 2.

nem035
  • 34,790
  • 6
  • 87
  • 99
Gibrán
  • 251
  • 3
  • 13

1 Answers1

19

Since Mongo 3.6, you can update multi-nested arrays by combining the following operators:

  • $set (to update a specific field)
  • $[] (to match any item in an array)
  • $[<identifier>] (to match specific items in an array)

Example

Here's how you can update a specific proyectos document that has a reuniones array that has a participantes array that has an object with the field nomina equal to 2:

// update a specific proyectos document
// that has a field "reuniones" which is an array
// in which each item is an object with a field "participantes" that is an array
// in which each item is an object that has a field "nomina" equal to 2
db.proyectos.update({
  _id: ObjectId("5bfa09f0a0441f38d45dcc9c"),
}, {
  $set: {
    "reuniones.$[].participantes.$[j].firma": <your update>
  },
}, { 
  arrayFilters: [
    {
      "j.nomina": 2
    }
  ]
})

If you wanted to limit your query to a specific reunion, you can do:

db.proyectos.update({
  _id: ObjectId("5bfa09f0a0441f38d45dcc9c"),
}, {
  $set: {
    "reuniones.$[i].participantes.$[j].firma": <your update>
  },
}, { 
  arrayFilters: [
    {
      "i._id": ObjectId("5bfa09f0a0441f38d45dcc99")
    },
    {
      "j.nomina": 2
    }
  ]
})

To update all proyectos satisfying the above condition, just omit the _id query:

// update all proyectos
// that have a field "reuniones" which is an array
// in which each item is an object with a field "participantes" that is an array
// in which each item is an object that has a field "nomina" equal to 2    
db.proyectos.update({}, {
  $set: {
    "reuniones.$[].participantes.$[j].firma": <your update>
  },
}, { 
  arrayFilters: [
    {
       "j.nomina": 2
    }
  ]
})
nem035
  • 34,790
  • 6
  • 87
  • 99
  • But how can I limit the update to the reunion that has `_id ": ObjectId (" 5bfa09f0a0441f38d45dcc99 ")`. This is because the proyecto document has many reuniones, each with a different id. The query that you suggest modifies the firma field of all participantes with nomina 2 that are in the reuniones array, right? – Gibrán Jan 05 '19 at 20:47
  • @Gibrán I added an example for a specific reuniones document – nem035 Jan 05 '19 at 20:58
  • Sorry @nem045, Is there no other alternative to achieve the same result but without `arrayFilters`? My problem is that I want to replicate the query in spring boot but the current driver does'nt allow me to run an update with `arrayFilters`. I appreciate any suggestion. Even generate a [question](https://stackoverflow.com/questions/54058055/how-to-replicate-mongo-update-query-in-spring-boot-and-kotlin) but it seems that went unnoticed. – Gibrán Jan 06 '19 at 23:23
  • 1
    Not an easy direct query no, you'd have to do some manual in-memory iteration (via a cursor) once the data is obtained afaik – nem035 Jan 06 '19 at 23:38
  • If you are using underscore in query, mongo database does not support underscore. https://stackoverflow.com/questions/58219745/update-with-array-filter-fails – redbull330 Feb 25 '22 at 21:14