sql >> Database teknologi >  >> NoSQL >> MongoDB

Opdatering af et indlejret array med MongoDB

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.



  1. Overvej at gense indgangene ovenfor eller definere en bean af typen 'org.springframework.data.redis.core.RedisTemplate' i din konfiguration

  2. Kan jeg indstille global TTL i redis?

  3. Indstilling af dynamisk sti i redis.conf ved hjælp af miljøvariablen

  4. Hvordan bruger du $set i MongoDB til at opdatere en indlejret værdi/indlejret dokument?