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

Eliminar el campo encontrado en cualquier matriz mongodb

Así que hice una pregunta en los comentarios, pero parece que te alejaste, así que supongo que solo respondo los tres casos posibles que veo.

Para empezar, no estoy seguro de si los elementos que se muestran dentro de las matrices anidadas son solo elementos dentro de la matriz o, de hecho, si arrayToDelete es el único campo presente en esos elementos. Así que básicamente necesito resumir un poco e incluir ese caso:

{
    field: 'value',
    field2: 'value',
    scan: [
        [
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
            {   somethingToKeep: 1 },
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
        ],
        [
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
            {   somethingToKeep: 1 },
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
        ],
        [
            {   somethingToKeep: 1 },
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
        ],
    ]
}

Caso 1:elimine los elementos de la matriz interna donde está presente el campo

Esto usaría el $pull operador ya que eso es lo que elimina los elementos de la matriz enteramente. Haces esto en MongoDB moderno con una declaración como esta:

db.collection.updateMany(
  { "scan": {
    "$elemMatch": {
      "$elemMatch": {
        "arrayToDelete": { "$exists": true }
      }
    }
  } },
  {
    "$pull": {
      "scan.$[a]": { "arrayToDelete": { "$exists": true } }
    }
  },
  { "arrayFilters": [
      {  "a": { "$elemMatch": { "arrayToDelete": { "$exists": true } } } }
    ]
  }
)

Eso altera todos los documentos coincidentes como este:

{
        "_id" : ObjectId("5ca1c36d9e31550a618011e2"),
        "field" : "value",
        "field2" : "value",
        "scan" : [
                [
                        {
                                "somethingToKeep" : 1
                        }
                ],
                [
                        {
                                "somethingToKeep" : 1
                        }
                ],
                [
                        {
                                "somethingToKeep" : 1
                        }
                ]
        ]
}

Así que todos los elementos que contenían ese campo ahora se eliminan.

Caso 2:simplemente elimine el campo coincidente de los elementos internos

Aquí es donde usas $unset . Es solo un poco diferente al "hard indexed" versión que estabas haciendo:

db.collection.updateMany(
  { "scan": {
    "$elemMatch": {
      "$elemMatch": {
        "arrayToDelete": { "$exists": true }
      }
    }
  } },
  { "$unset": { "scan.$[].$[].arrayToDelete": ""  } }
)

Lo que altera todos los documentos coincidentes para que sean:

{
        "_id" : ObjectId("5ca1c4c49e31550a618011e3"),
        "field" : "value",
        "field2" : "value",
        "scan" : [
                [
                        {
                                "anotherField" : "a"
                        },
                        {
                                "somethingToKeep" : 1
                        },
                        {
                                "anotherField" : "a"
                        },
                        {
                                "anotherField" : "a"
                        },
                        {
                                "anotherField" : "a"
                        }
                ],
                [
                        {
                                "anotherField" : "a"
                        },
                        {
                                "anotherField" : "a"
                        },
                        {
                                "anotherField" : "a"
                        },
                        {
                                "somethingToKeep" : 1
                        },
                        {
                                "anotherField" : "a"
                        }
                ],
                [
                        {
                                "somethingToKeep" : 1
                        },
                        {
                                "anotherField" : "a"
                        },
                        {
                                "anotherField" : "a"
                        },
                        {
                                "anotherField" : "a"
                        },
                        {
                                "anotherField" : "a"
                        }
                ]
        ]
}

Así que todo sigue ahí, pero solo los campos identificados se han eliminado de cada documento de matriz interna.

Caso 3:en realidad deseaba eliminar "Todo" de la matriz.

Que en realidad es solo un caso simple de usar $set y limpiando todo lo que había antes:

db.collection.updateMany(
  { "scan": {
    "$elemMatch": {
      "$elemMatch": {
        "arrayToDelete": { "$exists": true }
      }
    }
  } },
  { "$set": { "scan": []  } }
)

Donde los resultados deberían esperarse:

{
        "_id" : ObjectId("5ca1c5c59e31550a618011e4"),
        "field" : "value",
        "field2" : "value",
        "scan" : [ ]
}

Entonces, ¿qué están haciendo todos estos?

Lo primero que debería ver es el predicado de consulta . En general, esta es una buena idea para asegurarse de que no está emparejando e incluso "intentando" para que se cumplan las condiciones de actualización en documentos que ni siquiera contienen datos con el patrón que pretende actualizar. Las matrices anidadas son difíciles en el mejor de los casos, y cuando sea práctico, debería evitarlos, ya que a menudo "realmente quiere decir" en realidad está representado en una matriz singular con atributos adicionales que representan lo que "piensas" el anidamiento está haciendo por ti.

Pero solo porque son duros no significa imposible . Es solo que necesitas entender $elemMatch :

db.colelction.find(
  { "scan": {
    "$elemMatch": {
      "$elemMatch": {
        "arrayToDelete": { "$exists": true }
      }
    }
  }}
)

Ese es el find() básico ejemplo, que coincide según el $elemMatch condición para el exterior matriz usa otro $elemMatch para que coincida con otra condición en el interno formación. Aunque esto "aparece" ser un predicado singular. Algo como:

"scan.arrayToDelete": { "$exists": true }

Simplemente no funcionará. Tampoco:

"scan..arrayToDelete": { "$exists": true }

Con el "doble punto" .. porque eso básicamente no es válido.

Ese es el predicado de consulta para hacer coincidir los "documentos" que deben procesarse, pero el resto se aplica para determinar realmente *qué partes actualizar".

En el Caso 1 para $pull desde el interior matriz, primero tenemos que ser capaces de identificar qué elementos del externo array contiene los datos a actualizar. Eso es lo que "scan.$[a]" lo que está haciendo usando el filtrado posicional $[<identifier>] operador.

Ese operador básicamente transpone los índices coincidentes ( tantos muchos de ellos) en la matriz a otro predicado que se define en la tercera sección de la update Comandos de estilo con arrayFilters sección. Esta sección básicamente define las condiciones que deben cumplirse desde la perspectiva del identificador nombrado.

En este caso, el "identificador" se llama a , y ese es el prefijo usado en arrayFilters entrada:

  { "arrayFilters": [
      {  "a": { "$elemMatch": { "arrayToDelete": { "$exists": true } } } }
    ]
  }

Tomado en contexto con la actual declaración de actualización parte:

  {
    "$pull": {
      "scan.$[a]": { "arrayToDelete": { "$exists": true } }
    }
  },

Entonces desde la perspectiva del "a" siendo el identificador para el externo primer elemento de matriz hacia adentro desde "scan" , se aplican las mismas condiciones que para el predicado de consulta original pero desde "dentro" el primero $elemMatch declaración. Entonces, básicamente puede pensar en esto como una "consulta dentro de una consulta" desde la perspectiva de ya "mirar dentro" el contenido de cada exterior elemento.

Del mismo modo, el $pull actúa como una "consulta dentro de una consulta" en que sus propios argumentos también se aplican desde la perspectiva del elemento de la matriz. Por lo tanto, solo arrayToDelete campo existente en lugar de:

  // This would be wrong! and do nothing :(
  {
    "$pull": {
      "scan.$[a]": { "$elemMatch": { "arrayToDelete": { "$exists": true } } }
    }
  }

Pero todo eso es específico de $pull , y otras cosas tienen casos diferentes:

El Caso 2 mira dónde quieres simplemente $unset el campo nombrado. Parece bastante fácil ya que solo nombra el campo, ¿verdad? Bueno, no exactamente, ya que lo siguiente claramente no es correcto por lo que sabemos antes:

  { "$unset": { "scan.arrayToDelete": ""  } } // Not right :(

Y, por supuesto, anotar los índices de matriz para todo es solo una molestia:

  { "$unset": { 
    "scan.0.0.arrayToDelete": "",
    "scan.0.1.arrayToDelete": "",
    "scan.0.2.arrayToDelete": "",
    "scan.0.3.arrayToDelete": "",  // My fingers are tired :-<
  } }

Esta es la razón de la posición todo $[] operador. Este es un poco más "fuerza bruta" que el filtro posicional $[<identifier>] en que en lugar de coincidir con otro predicado proporcionado dentro de arrayFilters , esto simplemente se aplica a todo dentro del contenido de la matriz en ese "índice". Básicamente es una forma de decir "todos los índices" sin escribir cada uno como el horrible caso que se muestra arriba.

Entonces no es para todos los casos , pero ciertamente se adapta bien a un $unset ya que tiene un nombre de ruta muy específico lo cual, por supuesto, no importa si esa ruta no coincide con todos los elementos de la matriz.

podrías todavía usa un arrayFilters y un filtro posicional $[<identifier>] , pero aquí sería excesivo. Además, no está de más demostrar el otro enfoque.

Pero, por supuesto, probablemente valga la pena entender cómo exactamente esa declaración se vería, entonces:

db.collection.updateMany(
  { "scan": {
    "$elemMatch": {
      "$elemMatch": {
        "arrayToDelete": { "$exists": true }
      }
    }
  } },
  { "$unset": { "scan.$[a].$[b].arrayToDelete": ""  } },
  {
    "arrayFilters": [
      { "a": { "$elemMatch": { "arrayToDelete": { "$exists": true } } } },
      { "b.arrayToDelete": { "$exists": true } },
    ]
  }
)

Observando allí que el "b.arrayToDelete" puede que no sea lo que esperaba al principio, pero dado el posicionamiento en "scan.$[a].$[b] realmente debería tener sentido a partir de b el nombre del elemento se alcanzaría a través de la "notación de puntos" tal como se muestra. Y de hecho en ambos casos. Una vez más, un $unset de todos modos, solo se aplicaría al campo con nombre, por lo que los criterios de selección realmente no son necesarios.

Y Caso 3 . Bueno, es bastante simple si no necesitas para mantener cualquier otra cosa en la matriz después de eliminar este contenido (es decir, un $pull donde los campos que coincidían con esto eran los únicos cosas allí, o un $unset para el caso), entonces simplemente no te metas con nada más y simplemente borra la matriz .

Esta es una distinción importante si considera que según el punto para aclarar si los documentos con el campo nombrado donde solo elementos dentro de las matrices anidadas y, de hecho, que la clave nombrada era la única cosa presente en los documentos.

Con el razonamiento de que usar $pull como se muestra aquí y bajo esas condiciones obtendrías:

{
        "_id" : ObjectId("5ca321909e31550a618011e6"),
        "field" : "value",
        "field2" : "value",
        "scan" : [
            [ ],
            [ ],
            [ ]
        ]
}

O con el $unset :

{
        "_id" : ObjectId("5ca322bc9e31550a618011e7"),
        "field" : "value",
        "field2" : "value",
        "scan" : [
            [{ }, { }, { }, { }],
            [{ }, { }, { }, { }],
            [{ }, { }, { }, { }]
        ]
}

Ambos claramente no son deseables. Por lo tanto, es razonable que arrayToDelete el campo era el único contenido que estaba allí en absoluto, entonces la forma más lógica de eliminar todo es simplemente reemplazar la matriz con una vacía. O de hecho $unset toda la propiedad del documento.

Tenga en cuenta, sin embargo, que todas estas "cosas extravagantes" (con la excepción del $set por supuesto) requiere que debe tener MongoDB 3.6 al menos disponibles para usar esta funcionalidad.

En el caso de que todavía esté ejecutando una versión anterior de MongoDB que esa (y a la fecha de redacción, realmente no debería estarlo ya que su soporte oficial se agota en solo 5 meses a partir de esta fecha), luego otras respuestas existentes sobre Cómo actualizar múltiples Los elementos de matriz en mongodb son realmente para ti.