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

Eliminar objeto de matriz anidada por múltiples criterios

Puedes $pull la "primera coincidencia" de la "matriz externa" con la eliminación de "todos los elementos internos" simplemente haciendo:

db.Events.updateMany(
  {
    "Distributions.DistributionData": {
      "$elemMatch": {
        "Key": null,
        "Value": null,
        "Children": null
      }
    }
  },
  {
    "$pull": {
      "Distributions.$.DistributionData": { 
        "Key": null,
        "Value": null,
        "Children": null
      }
    }
  }
)

Eso está bien si solo tiene una entrada en "Distributions" matriz o al menos solo una de esas entradas tiene entradas de matriz secundaria que coincidirían con la condición. Así funciona el $ posicional operador funciona con todas las versiones de MongoDB.

Si los datos tuvieran coincidencias "múltiples" en las "Distributions" "externas" matriz, entonces, si tiene MongoDB 3.6, puede aplicar el filtrado posicional $[<identifier>] operador para modificar todas las entradas coincidentes:

db.Events.updateMany(
  {
    "Distributions.DistributionData": {
      "$elemMatch": {
        "Key": null,
        "Value": null,
        "Children": null
      }
    }
  },
  {
    "$pull": {
      "Distributions.$[element].DistributionData": { 
        "Key": null,
        "Value": null,
        "Children": null
      }
    }
  },
  {
    "arrayFilters": [
      { "element.DistributionData": {
        "$elemMatch": {
          "Key": null,
          "Value": null,
          "Children": null
        }
      }}
    ]
  }
)

En ese caso, los arrayFilters La opción define una condición por la cual hacemos coincidir las entradas en la matriz "externa" para que esto pueda aplicarse a todo lo que coincida.

O de hecho desde $pull esencialmente tiene esas condiciones en sí, entonces puede alternativamente usar el posicional all $[] operador en este caso:

db.Event.updateMany(
  {
    "Distributions.DistributionData": {
      "$elemMatch": {
        "Key": null,
        "Value": null,
        "Children": null
      }
    }
  },
  {
    "$pull": {
      "Distributions.$[].DistributionData": { 
        "Key": null,
        "Value": null,
        "Children": null
      }
    }
  }
)

Cualquiera de los casos cambia el documento en la pregunta eliminando el elemento interno con todo null teclas:

{
        "_id" : UUID("cf397865-c000-4f51-8959-1aae84769706"),
        "CreationDateTime" : ISODate("2016-05-06T05:09:14.589Z"),
        "WKT" : "",
        "Distributions" : [
                {
                        "_id" : UUID("bb95bedb-4baa-4ada-90b1-0d763e70ebfe"),
                        "DeliveryType" : 1,
                        "DistributionData" : [
                                {
                                        "Key" : "Topic",
                                        "Value" : "Topics",
                                        "Children" : null
                                },
                                {
                                        "Key" : "Message",
                                        "Value" : "test",
                                        "Children" : null
                                }
                        ],
                        "Schedules" : [
                                ISODate("2016-05-06T05:09:56.988Z")
                        ]
                }
        ]
}

Todas las condiciones de "consulta" usan $elemMatch para la selección de documentos. Esto es realmente necesario para el $ posicional operador para obtener el "índice de posición" utilizado para el "primer partido". Si bien esto no es realmente un "requisito" para el filtro posicional $[<identifier>] o el posicional todo $[] operador, sigue siendo útil para que ni siquiera considere documentos para actualizar que no coincidan con las condiciones de actualización posteriores de cualquiera de los $pull o los arrayFilters opciones.

En cuanto al $pull en sí mismo, las condiciones aquí en realidad se aplican a "cada" elemento de matriz, por lo que no hay necesidad de $elemMatch en esa operación ya que ya estamos mirando el nivel de "elemento".

El tercer ejemplo muestra que el posicional todo $[] el operador simplemente puede usar esos $pull condiciones en consideración de cada elemento de matriz "interno" y solo se aplicará a TODOS los elementos de matriz "externos". Entonces, el punto real del filtrado posicional $[<identifier>] La expresión es "solo" procesar aquellos elementos de matriz "externos" que realmente coinciden con la condición "interna". Por eso usamos $elemMatch en la consideración de hacer coincidir cada elemento de matriz "interno".

Si en realidad no tiene MongoDB 3.6 al menos, entonces está usando el primer formulario y probablemente lo repita hasta que las actualizaciones finalmente no devuelvan más documentos modificados que indiquen que no quedan más elementos que coincidan con la condición.

Hay una redacción mucho más detallada sobre las "alternativas" como enfoques en Cómo actualizar varios elementos de matriz en mongodb, pero siempre que sus datos se adapten al caso inicial o realmente tenga MongoDB 3.6 disponible, entonces este es el correcto acercarse aquí.

Si desea ver el efecto completo de la nueva sintaxis para MongoDB 3.6. esta es la alteración del documento en la pregunta que usé para verificar las declaraciones de actualización aquí:

{
    "_id" : UUID("cf397865-c000-4f51-8959-1aae84769706"),
    "CreationDateTime" : ISODate("2016-05-06T05:09:14.589Z"),
    "WKT" : "",
    "Distributions" : [
            {
                    "_id" : UUID("bb95bedb-4baa-4ada-90b1-0d763e70ebfe"),
                    "DeliveryType" : 1,
                    "DistributionData" : [
                            {
                                    "Key" : "Topic",
                                    "Value" : "Topics",
                                    "Children" : null
                            },
                            {
                                    "Key" : null,
                                    "Value" : null,
                                    "Children" : null
                            },
                            {
                                    "Key" : "Message",
                                    "Value" : "test",
                                    "Children" : null
                            },
                            {
                                    "Key" : null,
                                    "Value" : null,
                                    "Children" : null
                            }
                    ],
                    "Schedules" : [
                            ISODate("2016-05-06T05:09:56.988Z")
                    ]
            },
            {
                    "_id" : UUID("bb95bedb-4baa-4ada-90b1-0d763e70ebfe"),
                    "DeliveryType" : 1,
                    "DistributionData" : [
                            {
                                    "Key" : "Topic",
                                    "Value" : "Topics",
                                    "Children" : null
                            },
                            {
                                    "Key" : null,
                                    "Value" : null,
                                    "Children" : null
                            },
                            {
                                    "Key" : "Message",
                                    "Value" : "test",
                                    "Children" : null
                            },
                            {
                                    "Key" : null,
                                    "Value" : null,
                                    "Children" : null
                            }
                    ],
                    "Schedules" : [
                            ISODate("2016-05-06T05:09:56.988Z")
                    ]
            }
    ]
}

Lo que básicamente duplica algunas entradas tanto "externas" como "internas" para mostrar cómo la declaración elimina todos los null valores.

NOTA arrayFilters se especifican en el argumento "opciones" para .update() y métodos similares, la sintaxis es generalmente compatible con todas las versiones recientes del controlador e incluso con aquellas anteriores al lanzamiento de MongoDB 3.6.

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.

Robo 3T, en particular, aquí todavía está vinculado a estar basado en un shell MongoDB 3.4. Entonces, incluso cuando se conecta a una instancia MongoDB 3.6 capaz, estas opciones no se pasarán al servidor desde este programa. Se recomienda quedarse solo con el shell y los productos compatibles, aunque hay otras ofertas que no tienen la misma limitación.