Generelt omfang og forklaring
Der er et par ting galt med det, du laver her. For det første dine forespørgselsbetingelser. Du henviser til flere _id
værdier, hvor du ikke burde have brug for det, og hvoraf mindst én ikke er på øverste niveau.
For at komme ind i en "indlejret" værdi og også forudsat at _id
værdien er unik og vil ikke blive vist i noget andet dokument, din forespørgselsformular skal være sådan her:
Model.update(
{ "array1.array2._id": "123" },
{ "$push": { "array1.0.array2.$.answeredBy": "success" } },
function(err,numAffected) {
// something with the result in here
}
);
Nu ville det faktisk virke, men det er i virkeligheden kun et lykketræf, at det gør, da der er meget gode grunde til, at det ikke burde virke for dig.
Den vigtige læsning er i den officielle dokumentation for den positionelle $
operatør under emnet "Indlejrede arrays". Hvad dette siger er:
Den positionelle $-operator kan ikke bruges til forespørgsler, der krydser mere end ét array, såsom forespørgsler, der krydser arrays indlejret i andre arrays, fordi erstatningen for $-pladsholderen er en enkelt værdi
Det betyder specifikt, at det element, der vil blive matchet og returneret i den positionelle pladsholder, er værdien af indekset fra den første matchende array. Dette betyder i dit tilfælde det matchende indeks på arrayet på "øverste" niveau.
Så hvis du ser på forespørgselsnotationen som vist, har vi "hardkodet" den første ( eller 0 indeks ) position i arrayet på øverste niveau, og det sker bare sådan, at det matchende element i "array2" også er nul indeksindgangen.
For at demonstrere dette kan du ændre det matchende _id
værdi til "124", og resultatet vil $push
en ny indgang til elementet med _id
"123", da de begge er i nulindeksindgangen for "array1", og det er den værdi, der returneres til pladsholderen.
Så det er det generelle problem med nesting-arrays. Du kan fjerne et af niveauerne, og du vil stadig være i stand til at $push
til det korrekte element i dit "top"-array, men der ville stadig være flere niveauer.
Prøv at undgå indlejrede arrays, da du vil støde på opdateringsproblemer som vist.
Den generelle sag er at "udjævne" de ting, du "tror" er "niveauer" og faktisk lave disse "attributter" på de sidste detalje. For eksempel bør den "fladede" form af strukturen i spørgsmålet være noget som:
{
"answers": [
{ "by": "success", "type2": "123", "type1": "12" }
]
}
Eller endda når accept af det indre array er $push
kun og aldrig opdateret:
{
"array": [
{ "type1": "12", "type2": "123", "answeredBy": ["success"] },
{ "type1": "12", "type2": "124", "answeredBy": [] }
]
}
Som begge egner sig til atomare opdateringer inden for rammerne af den positionelle $
operatør
MongoDB 3.6 og nyere
Fra MongoDB 3.6 er der nye funktioner tilgængelige til at arbejde med indlejrede arrays. Dette bruger den positionsfiltrerede $[<identifier>]
syntaks for at matche de specifikke elementer og anvende forskellige betingelser gennem arrayFilters
i opdateringserklæringen:
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" }]
}
)
"arrayFilters"
som videregivet til mulighederne for .update()
eller endda.updateOne()
, .updateMany()
, .findOneAndUpdate()
eller .bulkWrite()
metoden specificerer betingelserne for at matche på identifikatoren givet i opdateringssætningen. Alle elementer, der matcher den angivne betingelse, vil blive opdateret.
Fordi strukturen er "indlejret", bruger vi faktisk "flere filtre", som er angivet med en "matrix" af filterdefinitioner som vist. Den markerede "identifikator" bruges til at matche den positionsfiltrerede $[<identifier>]
syntaks, der faktisk bruges i opdateringsblokken af sætningen. I dette tilfælde inner
og outer
er de identifikatorer, der bruges for hver betingelse som angivet med den indlejrede kæde.
Denne nye udvidelse gør opdatering af indlejret array-indhold mulig, men det hjælper ikke rigtig med det praktiske ved at "forespørge" sådanne data, så de samme forbehold gælder som forklaret tidligere.
Du "mener" typisk virkelig at udtrykke som "attributter", selvom din hjerne først tænker "nesting", er det bare normalt en reaktion på, hvordan du tror, at de "tidligere relationelle dele" hænger sammen. I virkeligheden har du virkelig brug for mere denormalisering.
Se også Sådan opdaterer du flere array-elementer i mongodb, da disse nye opdateringsoperatører faktisk matcher og opdaterer "flere array-elementer" i stedet for kun de første , som har været den tidligere handling af positionsopdateringer.
BEMÆRK Lidt ironisk, da dette er specificeret i "options"-argumentet for .update()
og ligesom metoder er syntaksen generelt kompatibel med alle nyere udgivelsesdriverversioner.
Dette er dog ikke sandt for mongo
shell, da måden metoden er implementeret der ("ironisk nok for bagudkompatibilitet") arrayFilters
argument genkendes og fjernes ikke af en intern metode, der analyserer mulighederne for at levere "bagudkompatibilitet" med tidligere MongoDB-serverversioner og en "legacy" .update()
API-opkaldssyntaks.
Så hvis du vil bruge kommandoen i mongo
shell eller andre "skalbaserede" produkter (især Robo 3T), skal du bruge en seneste version fra enten udviklingsgrenen eller produktionsudgivelsen fra 3.6 eller nyere.
Se også positional all $[]
som også opdaterer "flere array-elementer", men uden at gælde for specificerede betingelser og gælder for alle elementer i arrayet, hvor det er den ønskede handling.