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

Fjern felt fundet i ethvert mongodb-array

Så jeg stillede et spørgsmål i kommentarerne, men du ser ud til at være gået væk, så jeg svarer vist bare på de tre mulige tilfælde, jeg ser.

Til at begynde med er jeg ikke sikker på, om de elementer, der vises i de indlejrede arrays, er de eneste elementer i arrayet eller faktisk hvis arrayToDelete er den eneste felt til stede i disse elementer. Så dybest set er jeg nødt til at abstrahere lidt og medtag den sag:

{ 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" }, ], [ { noget ToKeep: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" }, ], ]}

Tilfælde 1 - Fjern de indre array-elementer, hvor feltet er til stede

Dette ville bruge $pull operator, da det er det, der fjerner array-elementer helt. Du gør dette i moderne MongoDB med et udsagn som dette:

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

Det ændrer alle matchende dokumenter som dette:

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

Så hvert element, der indeholdt det felt, er nu fjernet.

Case 2 - Fjern bare det matchede felt fra de indre elementer

Det er her du bruger $unset . Det er bare lidt anderledes end "hårdt indekseret" version du lavede:

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

Hvilket ændrer alle matchede dokumenter til at være:

{ "_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" } ] } 

Så alt er der stadig, men kun de identificerede felter er blevet fjernet fra hvert indre array-dokument.

Case 3 - Du ville faktisk fjerne "Alt" i arrayet.

Hvilket egentlig bare er et simpelt tilfælde af at bruge $set og tørrer alt, hvad der var der før:

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

Hvor resultaterne ret godt kunne forventes:

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

Så hvad laver de alle sammen?

Det allerførste, du bør se, er forespørgselsprædikatet . Dette er generelt en god idé for at sikre, at du ikke matcher og endda "forsøger" at få opfyldt opdateringsbetingelser på dokumenter, som ikke engang indeholder data med det mønster, du har til hensigt at opdatere. Indlejrede arrays er vanskelige i bedste fald, og hvor det er praktisk, bør du virkelig undgå dem, som det du ofte "virkelig mener" er faktisk repræsenteret i et enkelt array med yderligere attributter, der repræsenterer, hvad du "tror" indlejringen faktisk gør for dig.

Men bare fordi de er hårde betyder ikke umuligt . Det er bare, at du skal forstå $elemMatch :

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

Det er den grundlæggende find() eksempel, som matcher baseret på $elemMatch betingelse for den ydre array bruger en anden $elemMatch for at matche en anden betingelse i den indre array. Selvom dette "vises" at være et entalsprædikat. Noget som:

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

Vil bare ikke virke. Det ville heller ikke:

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

Med den "dobbelte prik" .. fordi det i bund og grund bare ikke er gyldigt.

Det er forespørgselsprædikatet at matche "dokumenter", der skal behandles, men resten gælder for faktisk at bestemme *hvilke dele der skal opdateres".

I tilfælde 1 for at $pull fra det indre array, skal vi først være i stand til at identificere hvilke elementer i det ydre array indeholder de data, der skal opdateres. Det er hvad "scan.$[a]" ting gør ved at bruge den positionsfiltrerede $[] operatør.

Denne operatør transponerer dybest set de matchede indekser (så mange af dem ) i arrayet til et andet prædikat som er defineret i den tredje sektion af opdateringen stilkommandoer med arrayFilters afsnit. Dette afsnit definerer grundlæggende de betingelser, der skal opfyldes ud fra den navngivne identifikator.

I dette tilfælde hedder ud "identifikator" a , og det er præfikset, der bruges i arrayFilters indgang:

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

Taget i sammenhæng med den faktiske opdateringserklæring del:

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

Derefter fra perspektivet "a" er identifikatoren for den ydre array-element først indad fra "scan" , så gælder de samme betingelser som for det originale forespørgselsprædikat men fra "indenfor" den første $elemMatch udmelding. Så du kan dybest set tænke på dette som en "forespørgsel i en forespørgsel" fra perspektivet af allerede "kigger indenfor" indholdet af hver ydre element.

På samme måde er $pull fungerer meget som en "forespørgsel i en forespørgsel" i, at dets egne argumenter også anvendes fra perspektivet af elementet i arrayet. Derfor kun arrayToDelete eksisterende felt i stedet for:

 // Dette ville være forkert! og gør ingenting :( { "$pull":{ "scan.$[a]":{ "$elemMatch":{ "arrayToDelete":{ "$exists":true } } } } 

Men det er alt sammen specifikt for $pull , og andre ting har forskellige tilfælde:

Case 2 ser på, hvor du bare vil $unset det navngivne felt. Det virker ret nemt, da du bare navngiver feltet, ikke? Nå, ikke ligefrem, da følgende tydeligvis ikke er rigtigt ud fra, hvad vi ved tidligere:

 { "$unset":{ "scan.arrayToDelete":"" } } // Ikke rigtigt :( 

Og selvfølgelig er det bare en smerte at notere array-indekser for alting:

 { "$unset":{ "scan.0.0.arrayToDelete":"", "scan.0.1.arrayToDelete":"", "scan.0.2.arrayToDelete":"", "scan.0.3 .arrayToDelete":"", // Mine fingre er trætte :-<} ​​} 

Dette er årsagen til den positionelle alle $[] operatør. Denne er lidt mere "brute force" end den positionsfiltrerede $[] i det i stedet for at matche et andet prædikat leveres i arrayFilters , hvad dette blot gør, er at gælde alt inden for array-indholdet ved det "indeks". Det er dybest set en måde at sige "alle indekser" på uden at skrive hver enkelt ud som den forfærdelige tilfælde vist ovenfor.

Så det er ikke for alle tilfælde , men det er bestemt velegnet til en $unset da det har en meget specifik stinavngivning hvilket ikke er ligegyldigt, hvis den sti ikke matcher hvert enkelt element i arrayet.

Du kunne brug stadig en arrayFilters og en positionsfiltreret $[] , men her ville det være overdrevent. Derudover skader det ikke at demonstrere den anden tilgang.

Men selvfølgelig er det nok værd at forstå hvordan præcis det udsagn ville se ud, så:

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

Bemærk der, at "b.arrayToDelete" er måske ikke, hvad du forventer i starten, men givet placeringen i "scan.$[a].$[b] det burde virkelig give mening fra b elementnavnet nås via "punktnotation" ligesom vist. Og faktisk i begge tilfælde. Endnu en gang en $unset ville kun gælde for det navngivne felt alligevel, så udvælgelseskriterierne er virkelig ikke påkrævet.

Og Case 3 . Nå, det er ret simpelt, hvis du ikke bruger for at beholde noget andet i arrayet efter at have fjernet dette indhold (dvs. en $pull hvor felter, der matcher dette, var de eneste ting derinde, eller en $unset for den sags skyld), så lad være med at rode rundt med andet, og tør arrayet .

Dette er en vigtig sondring, hvis du overvejer, at i punktet for at afklare, om dokumenterne med det navngivne felt er kun elementer i de indlejrede arrays, og faktisk at den navngivne nøgle var den eneste ting, der findes i dokumenterne.

Med begrundelsen at bruge $pull som vist her og under disse betingelser ville du få:

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

Eller med $unset :

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

Begge dele er tydeligvis ikke ønskværdige. Så det er grunden til, at arrayToDelete felt var det eneste indhold, der overhovedet var derinde, så den mest logiske måde at fjerne alt er simpelthen at erstatte arrayet med et tomt. Eller faktisk $unset hele dokumentegenskaben.

Bemærk dog, at alle disse "fancy ting" (med undtagelse af $set selvfølgelig ) kræver, at du skal have MongoDB 3.6 mindst tilgængelig for at bruge denne funktionalitet.

I tilfælde af at du stadig kører en ældre version af MongoDB end det (og på skrivedatoen burde du virkelig ikke være det, da din officielle support løber ud om kun 5 måneder fra denne dato), så andre eksisterende svar om Sådan opdaterer du flere Array-elementer i mongodb er faktisk for dig.



  1. rmongodb:bruger $or i forespørgsel

  2. MongoDB Multikey indekser &indeks skæringsgrænser

  3. Få dokumenter med tags på listen, sorteret efter det samlede antal matches

  4. Forespørger MongoDB GridFS?