Actualización de matrices anidadas en mongodb

Tengo un documento en mongodb con una matriz anidada profunda de 2 niveles que necesito actualizar, algo como esto:

{ id: 1, items: [ { id: 2, blocks: [ { id: 3 txt: 'hello' } ] } ] } 

Si solo hubiera una matriz profunda de nivel, podría usar el operador posicional para actualizar objetos en ella, pero para el segundo nivel, la única opción que he encontrado es usar el operador posicional con el índice de objetos nesteds, como este:

 db.objects.update({'items.id': 2}, {'$set': {'items.$.blocks.0.txt': 'hi'}}) 

Este enfoque funciona pero me parece peligroso ya que estoy construyendo un servicio web y el número de índice debe provenir del cliente, que puede enviar decir 100000 como índice y esto obligará a mongodb a crear una matriz con 100000 índices con valor nulo.

¿Hay alguna otra forma de actualizar dichos objetos nesteds en los que pueda hacer referencia al ID del objeto en lugar de a su posición o quizás formas de verificar si el índice suministrado está fuera de límites antes de usarlo en la consulta?

Esta es la gran pregunta: ¿necesita aprovechar las operaciones de “agregar y configurar” de Mongo? Si realmente planea modificar solo elementos individuales en la matriz, entonces probablemente debería construir estas matrices como objetos.

Así es como estructuraría esto:

 { id: 1, items: { "2" : { "blocks" : { "3" : { txt : 'hello' } } }, "5" : { "blocks" : { "1" : { txt : 'foo'}, "2" : { txt : 'bar'} } } } } 

Esto básicamente transforma todo en objetos JSON en lugar de matrices. $addToSet la habilidad de usar $push y $addToSet pero creo que esto hace que todo sea más fácil. Por ejemplo, su consulta se vería así:

db.objects.update({'items.2': {$exists:true} }, {'$set': {'items.2.blocks.0.txt': 'hi'}})

También notarás que he descargado los “ID”. Cuando anida cosas como esta generalmente puede reemplazar “ID” simplemente usando ese número como índice. El concepto de “ID” ahora está implícito.

Esta característica se ha agregado en 3.6 con actualizaciones expresivas .

db.objects.update( {id: 1 }, { $set: { 'items.$[itm].blocks.$[blk].txt': "hi", } }, { multi: false, arrayFilters: [ { 'itm.id': 2 }, { 'blk.id': 3} ] } )

Los identificadores que estás usando son números lineales y tienen que venir de algún lugar como un campo adicional como ‘max_idx’ o algo similar. Esto significa una búsqueda para la identificación y luego actualizar. UUID / ObjectId se puede usar para identificadores que garantizarán que también pueda usar Distributed CRUD.