MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

Remover objeto da matriz aninhada por vários critérios


Você pode $pull a "primeira correspondência" do "array externo" com a remoção de "todos os elementos internos" simplesmente fazendo:
db.Events.updateMany(
  {
    "Distributions.DistributionData": {
      "$elemMatch": {
        "Key": null,
        "Value": null,
        "Children": null
      }
    }
  },
  {
    "$pull": {
      "Distributions.$.DistributionData": { 
        "Key": null,
        "Value": null,
        "Children": null
      }
    }
  }
)

Tudo bem se você tiver apenas uma entrada no "Distributions" array ou pelo menos apenas uma dessas entradas tem entradas de array filho que corresponderiam à condição. É assim que o $ posicional O operador funciona com todas as versões do MongoDB.

Se os dados tiverem "múltiplas" correspondências no "externo" "Distributions" array, se você tiver o MongoDB 3.6, poderá aplicar o $[<identifier>] filtrado posicional operador para modificar todas as entradas correspondentes:
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
        }
      }}
    ]
  }
)

Nesse caso, os arrayFilters opção define uma condição pela qual combinamos entradas no array "externo" para que isso possa de fato se aplicar a tudo o que for correspondido.

Ou mesmo desde $pull essencialmente tem essas condições em si, então você pode alternadamente usar o posicional all $[] operador neste caso:
db.Event.updateMany(
  {
    "Distributions.DistributionData": {
      "$elemMatch": {
        "Key": null,
        "Value": null,
        "Children": null
      }
    }
  },
  {
    "$pull": {
      "Distributions.$[].DistributionData": { 
        "Key": null,
        "Value": null,
        "Children": null
      }
    }
  }
)

Ambos os casos alteram o documento na pergunta removendo o item interno com todos os null chaves:
{
        "_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 as condições de "consulta" usam $elemMatch para seleção de documentos. Isso é realmente necessário para o $ posicional operador para obter o "índice de posição" usado para a "primeira correspondência". Embora isso não seja realmente um "requisito" para o $[<identifier>] filtrado posicional ou o posicional todo $[] operador, ainda é útil para que você nem considere documentos para atualização que não correspondam às condições de atualização posteriores do $pull ou os arrayFilters opções.

Quanto ao $pull em si, as condições aqui realmente se aplicam a "cada" elemento da matriz, portanto, não há necessidade do $elemMatch nessa operação, pois já estamos olhando para o nível "elemento".

O terceiro exemplo mostra que todos os $[] posicionais operador pode simplesmente usar aqueles $pull condições em consideração a cada elemento "interno" da matriz e se aplicará apenas a TODOS os elementos "externos" da matriz. Portanto, o ponto real do $[<identifier>] filtrado posicional expression é "apenas" processar os elementos "externos" da matriz que realmente correspondem à condição "interna". Por isso, usamos $elemMatch na consideração para corresponder a cada elemento "interno" da matriz.

Se você realmente não tiver o MongoDB 3.6, pelo menos, estará usando o primeiro formulário e provavelmente repetindo isso até que as atualizações finalmente não retornem mais documentos modificados, indicando que não há mais elementos restantes que correspondam à condição.

Há uma escrita muito mais detalhada sobre as "alternativas" como abordagens em Como atualizar vários elementos de matriz no mongodb, mas desde que seus dados sejam adequados ao caso inicial ou você realmente tenha o MongoDB 3.6 disponível, então este é o correto abordagem aqui.

Se você quiser ver o efeito completo da nova sintaxe do MongoDB 3.6. esta é a alteração do documento na pergunta que usei para verificar as declarações de atualização aqui:
{
    "_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")
                    ]
            }
    ]
}

O que basicamente duplica algumas entradas "externas" e "internas" para mostrar como a instrução remove todos os null valores.

OBSERVAÇÃO arrayFilters são especificados no argumento "options" para .update() e como métodos, a sintaxe é geralmente compatível com todas as versões de driver de lançamento recentes e mesmo aquelas anteriores ao lançamento do MongoDB 3.6.

No entanto, isso não é verdade para o mongo shell, já que a forma como o método é implementado lá ("ironicamente para compatibilidade com versões anteriores") o arrayFilters argumento não é reconhecido e removido por um método interno que analisa as opções para fornecer "compatibilidade com versões anteriores" com versões anteriores do servidor MongoDB e um .update() "legado" Sintaxe de chamada da API.

Então, se você quiser usar o comando no mongo shell ou outros produtos "baseados em shell" (principalmente Robo 3T), você precisa de uma versão mais recente da ramificação de desenvolvimento ou da versão de produção a partir de 3.6 ou superior.

O Robo 3T, notavelmente aqui, ainda está vinculado a ser baseado em um shell MongoDB 3.4. Portanto, mesmo ao se conectar a uma instância do MongoDB 3.6 capaz, essas opções não serão passadas para o servidor a partir deste programa. É aconselhável ficar apenas com o shell e os produtos suportados, embora existam algumas outras ofertas que não têm a mesma limitação.