sql >> Base de Datos >  >> NoSQL >> MongoDB

Actualización de un arreglo anidado con MongoDB

Alcance general y explicación

Hay algunas cosas mal con lo que estás haciendo aquí. En primer lugar, sus condiciones de consulta. Te refieres a varios _id valores donde no debería necesitarlos, y al menos uno de los cuales no está en el nivel superior.

Para entrar en un valor "anidado" y también suponiendo que _id el valor es único y no aparecería en ningún otro documento, su formulario de consulta debería ser así:

Model.update(
    { "array1.array2._id": "123" },
    { "$push": { "array1.0.array2.$.answeredBy": "success" } },
    function(err,numAffected) {
       // something with the result in here
    }
);

Eso realmente funcionaría, pero en realidad es solo una casualidad que lo haga, ya que hay muy buenas razones por las que no debería funcionar para usted.

La lectura importante está en la documentación oficial para el posicional $ operador bajo el tema "Matrices anidadas". Lo que dice es:

El operador posicional $ no se puede usar para consultas que atraviesan más de una matriz, como consultas que atraviesan matrices anidadas dentro de otras matrices, porque el reemplazo del marcador de posición $ es un valor único

Específicamente, lo que eso significa es que el elemento que coincidirá y devolverá en el marcador de posición posicional es el valor del índice del primero matriz coincidente. Esto significa, en su caso, el índice coincidente en la matriz de nivel "superior".

Entonces, si observa la notación de consulta como se muestra, hemos "codificado" el primero (o índice 0) en la matriz de nivel superior, y sucede que el elemento coincidente dentro de "matriz2" también es la entrada de índice cero.

Para demostrar esto, puede cambiar el _id coincidente valor a "124" y el resultado será $push una nueva entrada en el elemento con _id "123" ya que ambos están en la entrada de índice cero de "matriz1" y ese es el valor devuelto al marcador de posición.

Entonces ese es el problema general con las matrices anidadas. Podría eliminar uno de los niveles y aún podría $push al elemento correcto en su matriz "superior", pero aún habría múltiples niveles.

Trate de evitar el anidamiento de matrices, ya que se encontrará con problemas de actualización, como se muestra.

El caso general es "aplanar" las cosas que "piensas" que son "niveles" y convertirlas en "atributos" en los elementos de detalles finales. Por ejemplo, la forma "aplanada" de la estructura en la pregunta debería ser algo como:

 {
   "answers": [
     { "by": "success", "type2": "123", "type1": "12" }
   ]
 }

O incluso cuando aceptar la matriz interna es $push solo, y nunca actualizado:

 {
   "array": [
     { "type1": "12", "type2": "123", "answeredBy": ["success"] },
     { "type1": "12", "type2": "124", "answeredBy": [] }
   ]
 }

Ambos se prestan a actualizaciones atómicas dentro del alcance del $ posicional operador

MongoDB 3.6 y superior

Desde MongoDB 3.6 hay nuevas funciones disponibles para trabajar con arreglos anidados. Esto usa el filtro posicional $[<identifier>] sintaxis para hacer coincidir los elementos específicos y aplicar diferentes condiciones a través de arrayFilters en la declaración de actualización:

Model.update(
  {
    "_id": 1,
    "array1": {
      "$elemMatch": {
        "_id": "12","array2._id": "123"
      }
    }
  },
  {
    "$push": { "array1.$[outer].array2.$[inner].answeredBy": "success" }
  },
  {
    "arrayFilters": [{ "outer._id": "12" },{ "inner._id": "123" }] 
  }
)

Los "arrayFilters" como se pasa a las opciones para .update() o incluso .updateOne() , .updateMany() , .findOneAndUpdate() o .bulkWrite() El método especifica las condiciones que deben coincidir con el identificador dado en la declaración de actualización. Cualquier elemento que coincida con la condición dada será actualizado.

Debido a que la estructura está "anidada", en realidad usamos "múltiples filtros" como se especifica con una "matriz" de definiciones de filtro como se muestra. El "identificador" marcado se utiliza en la comparación con el $[<identifier>] filtrado por posición sintaxis realmente utilizada en el bloque de actualización de la declaración. En este caso inner y outer son los identificadores utilizados para cada condición según lo especificado con la cadena anidada.

Esta nueva expansión hace posible la actualización del contenido de matrices anidadas, pero en realidad no ayuda con la practicidad de "consultar" dichos datos, por lo que se aplican las mismas advertencias que se explicaron anteriormente.

Por lo general, realmente "quieres decir" expresar como "atributos", incluso si tu cerebro inicialmente piensa "anidar", generalmente es solo una reacción a cómo crees que las "partes relacionales anteriores" se unen. En realidad, realmente necesitas más desnormalización.

Consulte también Cómo actualizar varios elementos de matriz en mongodb, ya que estos nuevos operadores de actualización en realidad coinciden y actualizan "múltiples elementos de matriz" en lugar de solo el primero , que ha sido la acción anterior de actualizaciones posicionales.

NOTA Irónicamente, ya que esto se especifica en el argumento "opciones" para .update() y métodos similares, la sintaxis es generalmente compatible con todas las versiones recientes del controlador.

Sin embargo, esto no es cierto para el mongo shell, ya que la forma en que se implementa el método allí ("irónicamente para compatibilidad con versiones anteriores") los arrayFilters El argumento no es reconocido y eliminado por un método interno que analiza las opciones para ofrecer "compatibilidad con versiones anteriores" con versiones anteriores del servidor MongoDB y un .update() "heredado". Sintaxis de llamada de API.

Entonces, si desea usar el comando en mongo shell u otros productos "basados ​​en shell" (especialmente Robo 3T), necesita una versión más reciente de la rama de desarrollo o la versión de producción a partir de 3.6 o superior.

Véase también positional all $[] que también actualiza "múltiples elementos de la matriz" pero sin aplicar las condiciones especificadas y se aplica a todos elementos en la matriz donde esa es la acción deseada.