Escopo geral e explicação
Há algumas coisas erradas com o que você está fazendo aqui. Em primeiro lugar, suas condições de consulta. Você está se referindo a vários
_id
valores onde você não deveria precisar, e pelo menos um dos quais não está no nível superior. Para entrar em um valor "aninhado" e também presumir que
_id
value é único e não apareceria em nenhum outro documento, seu formulário de consulta deve ser assim:Model.update(
{ "array1.array2._id": "123" },
{ "$push": { "array1.0.array2.$.answeredBy": "success" } },
function(err,numAffected) {
// something with the result in here
}
);
Agora, isso realmente funcionaria, mas na verdade é apenas um acaso que funciona, pois há boas razões pelas quais não deveria funcionar para você.
A leitura importante está na documentação oficial do
$
posicional operador sob o assunto "Aninhado Arrays". O que isso diz é:
O operador posicional $ não pode ser usado para consultas que percorrem mais de uma matriz, como consultas que percorrem matrizes aninhadas em outras matrizes, porque a substituição do espaço reservado $ é um valor único
Especificamente, o que isso significa é que o elemento que será correspondido e retornado no espaço reservado posicional é o valor do índice do primeiro matriz correspondente. Isso significa que, no seu caso, o índice correspondente na matriz de nível "superior".
Portanto, se você observar a notação de consulta conforme mostrado, "codificamos" o primeiro ( ou 0 index ) na matriz de nível superior, e acontece que o elemento correspondente dentro de "array2" também é a entrada de índice zero.
Para demonstrar isso, você pode alterar o
_id
correspondente valor para "124" e o resultado será $push
uma nova entrada no elemento com _id
"123", pois ambos estão na entrada de índice zero de "array1" e esse é o valor retornado ao espaço reservado. Então esse é o problema geral com matrizes aninhadas. Você pode remover um dos níveis e ainda poderá
$push
para o elemento correto em sua matriz "top", mas ainda haveria vários níveis. Tente evitar o aninhamento de arrays, pois você terá problemas de atualização, conforme mostrado.
O caso geral é "achatar" as coisas que você "acha" que são "níveis" e realmente fazer esses "atributos" nos detalhes finais. Por exemplo, a forma "achatada" da estrutura na pergunta deve ser algo como:
{
"answers": [
{ "by": "success", "type2": "123", "type1": "12" }
]
}
Ou mesmo quando aceitar o array interno é
$push
apenas, e nunca atualizado: {
"array": [
{ "type1": "12", "type2": "123", "answeredBy": ["success"] },
{ "type1": "12", "type2": "124", "answeredBy": [] }
]
}
Ambos se prestam a atualizações atômicas dentro do escopo do
$
posicional operador MongoDB 3.6 e superior
A partir do MongoDB 3.6, há novos recursos disponíveis para trabalhar com arrays aninhados. Isso usa o
$[<identifier>]
filtrado posicional sintaxe para corresponder aos elementos específicos e aplicar condições diferentes por meio de arrayFilters
na declaração de atualização: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" }]
}
)
Os
"arrayFilters"
como passado para as opções para .update()
ou mesmo.updateOne()
, .updateMany()
, .findOneAndUpdate()
ou .bulkWrite()
O método especifica as condições para corresponder ao identificador fornecido na instrução de atualização. Quaisquer elementos que correspondam à condição fornecida serão atualizados. Como a estrutura é "aninhada", na verdade usamos "vários filtros", conforme especificado com uma "matriz" de definições de filtro, conforme mostrado. O "identificador" marcado é usado na correspondência com o
$[<identifier>]
filtrado posicional sintaxe realmente usada no bloco de atualização da instrução. Neste caso inner
e outer
são os identificadores usados para cada condição conforme especificado com a cadeia aninhada. Essa nova expansão possibilita a atualização do conteúdo do array aninhado, mas não ajuda muito na praticidade de "consultar" esses dados, portanto, aplicam-se as mesmas advertências explicadas anteriormente.
Você normalmente realmente "pretende" expressar como "atributos", mesmo que seu cérebro inicialmente pense em "aninhamento", geralmente é apenas uma reação à forma como você acredita que as "partes relacionais anteriores" se juntam. Na realidade, você realmente precisa de mais desnormalização.
Consulte também Como atualizar vários elementos de matriz no mongodb, pois esses novos operadores de atualização realmente correspondem e atualizam "vários elementos de matriz" em vez de apenas o primeiro , que foi a ação anterior de atualizações posicionais.
OBSERVAÇÃO Um tanto ironicamente, já que isso é especificado 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.
No entanto, isso não é verdade para omongo
shell, já que a forma como o método é implementado lá ("ironicamente para compatibilidade com versões anteriores") oarrayFilters
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 nomongo
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.
Veja também
positional all $[]
que também atualiza "vários elementos de matriz", mas sem se aplicar a condições especificadas e se aplica a todos elementos na matriz onde essa é a ação desejada.