I det korteste svar er det både "ja" og "nej".
Der er faktisk en måde at matche individuelle matrixelementer og opdatere dem med separate værdier i en enkelt sætning, da du faktisk kan levere "flere" arrayFilters
betingelser og brug disse identifikatorer i din opdateringserklæring.
Problemet med din specifikke prøve her er, at en af posterne i dit "ændringssæt" (den sidste) faktisk ikke matcher noget array-medlem, der er til stede i øjeblikket. Den "formodede" handling her ville være at $push
det nye ikke-matchede medlem ind i arrayet, hvor det ikke blev fundet. Men den særlige handling kan ikke gøres i en "enkelt operation" , men du kan bruge bulkWrite()
at udstede "flere" erklæringer for at dække den sag.
Matching af forskellige array-betingelser
Forklar det i punkter, overvej de to første elementer i dit "skiftesæt". Du kan anvende en "single" opdater sætning med flere arrayFilters
sådan her:
db.avail_rates_copy.updateOne( { "_id":12345 }, { "$set":{ "rates.$[one]":{ "productId" :NumberInt(1234), "rate" :400.0, "rateCardId":NumberInt(1), "month" :NumberInt(201801) }, "rates.$[two]":{ "productId" :NumberInt(1234), "rate" :500.0, "rateCardId":NumberInt(1), "month" :NumberInt(201802) } } }, { "arrayFilters":[ { "one.productId":NumberInt(1234), "one.rateCardId":NumberInt(1), "one.month ":NumberInt(201801) }, { "two.productId":NumberInt(1234), "two.rateCardId":NumberInt(1), "two.month":NumberInt(201802) } ] })
Hvis du kørte, ville du se det ændrede dokument bliver:
{ "_id" :12345, "_class" :"com.example.ProductRates", "rates" :[ {// Matchede og ændrede dette med ét "productId" :1234, "rate" :400 , "rateCardId" :1, "month" :201801 }, { // Og dette som to "productId" :1234, "rate" :500, "rateCardId" :1, "month" :201802 }, { "productId" :1234, "rate" :400, "rateCardId" :2, "month" :201803 }, { "productId" :1235, "rate" :500, "rateCardId" :1, "month" :201801 } { "productId" :1235, "rate" :234, "rateCardId" :2, "month" :201803 } ]}
Bemærk her, at du angiver hver "identfier" på listen over arrayFilters
med flere betingelser for at matche elementet som sådan:
{ "one.productId":NumberInt(1234), "one.rateCardId":NumberInt(1), "one.month":NumberInt(201801) },
Så hver "tilstand" kortlægges effektivt som:
.
Så det ved at se på "priserne"
array efter sætningen i opdateringsblokken af $[]
:
"rates.$[one]"
Og ser på hvert element i "rater"
at matche betingelserne. Så "en"
identifikator ville matche betingelserne foran med "one"
og på samme måde for det andet sæt betingelser præfikset med "to"
, derfor gælder den faktiske opdateringssætning kun for dem, der matcher de betingelser, der er tildelt identifikatoren.
Hvis du bare ville have "priserne"
egenskab i modsætning til hele objektet, så noterer du bare som:
{ "$set":{ "rates.$[one].rate":400, "rates.$[two].rate":500 } }
Tilføjelse af ikke-matchede objekter
Så den første del er forholdsvis enkel at forstå, men som nævnt laver man en
code>$push
for "elementet som ikke er der" er en anden sag, da vi grundlæggende har brug for en forespørgselsbetingelse på "dokument"-niveauet for at bestemme, at et array-element "mangler".
Hvad dette i bund og grund betyder er, at du skal udstede en opdatering med
Det er her bulkWrite()
kommer i spil, og du bruger det ved at tilføje en ekstra opdatering til vores første operation ovenfor for hvert element i "ændringssættet":
db.avail_rates_copy.bulkWrite( [ { "updateOne":{ "filter":{ "_id":12345 }, "update":{ "$set":{ "rates.$[one]" :{ "productId" :NumberInt(1234), "rate" :400.0, "rateCardId":NumberInt(1), "month" :NumberInt(201801) }, "rates.$[two]":{ "productId" :NumberInt(1234), "rate" :500.0, "rateCardId":NumberInt(1), "month" :NumberInt(201802) }, "rates.$[three]":{ "productId" :NumberInt(1235), " rate" :700.0, "rateCardId":NumberInt(1), "month" :NumberInt(201802) } } }, "arrayFilters":[ { "one.productId":NumberInt(1234), "one.rateCardId":NumberInt (1), "one.month":NumberInt(201801) }, { "two.productId":NumberInt(1234), "two.rateCardId":NumberInt(1), "two.month":Nummer rInt(201802) }, { "three.productId":NumberInt(1235), "three.rateCardId":NumberInt(1), "three.month":NumberInt(201802) } ] }}, { "updateOne":{ "filter":{ "_id":12345, "rates":{ "$not":{ "$elemMatch":{ "productId" :NumberInt(1234), "rateCardId":NumberInt(1), "month" :NumberInt(201801) } } } }, "update":{ "$push":{ "rates":{ "productId" :NumberInt(1234), "rate" :400.0, "rateCardId":NumberInt(1), " month" :NumberInt(201801) } } } }}, { "updateOne":{ "filter":{ "_id":12345, "rates":{ "$not":{ "$elemMatch":{ "productId" :NumberInt(1234), "rateCardId":NumberInt(1), "month" :NumberInt(201802) } } } }, "update":{ "$push":{ "rates":{ "productId" :NumberInt(1234), "rate" :500.0, "rateCardId":NumberInt(1), "month" :NumberInt(201802) } } } }}, { "updateOne":{ "filter":{ "_id":12345, "rates":{ "$not":{ "$elemMatch":{ "productId" :NumberInt(1235), " rateCardId":NumberInt(1), "month" :NumberInt(201802) } } } }, "update":{ "$push":{ "rates":{ "productId" :NumberInt(1235), "rate" :700.0, "rateCardId":NumberInt(1), "month" :NumberInt(201802) } } } }} ], { "ordered":true })
Bemærk $elemMatch
inden for forespørgselsfilteret, da dette er et krav for at matche et array-element med "flere betingelser". Det havde vi ikke brug for på arrayFilters
poster, fordi de kun se på hvert array-element, de allerede er anvendt på, men som en "forespørgsel" kræver betingelserne $elemMatch
da simpel "punktnotation" ville returnere forkerte matches.
Se også $not
operator bruges her til at "nægte" $elemMatch
, da vores sande betingelser kun er at matche et dokument, der "ikke har matchende matrixelement" til de angivne betingelser, og det er det, der berettiger valg til at tilføje et nyt element.
Og den enkelte erklæring, der sendes til serveren, forsøger i det væsentlige fire opdateringshandlinger som én for at forsøge at opdatere matchede matrixelementer og en anden for hver af de tre "ændre sæt" forsøger at $push
hvor dokumentet viste sig ikke at matche betingelserne for array-elementet i "ændringssættet".
Resultatet er derfor som forventet:
{ "_id" :12345, "_class" :"com.example.ProductRates", "rates" :[ {// matchede og opdaterede "productId" :1234, "rate" :400, "rateCardId " :1, "month" :201801 }, { // matchede og opdaterede "productId" :1234, "rate" :500, "rateCardId" :1, "month" :201802 }, { "productId" :1234, " rate" :400, "rateCardId" :2, "month" :201803 }, { "productId" :1235, "rate" :500, "rateCardId" :1, "month" :201801 }, { "productId" :1235, "rate" :234, "rateCardId" :2, "month" :201803 }, { // Dette blev tilføjet "productId" :1235, "rate" :700, "rateCardId" :1, "måned" :201802 } ]}
Afhængigt af hvor mange elementer der faktisk ikke matchede bulkWrite()
svar vil rapportere om, hvor mange af disse udsagn, der faktisk matchede og påvirkede et dokument. I dette tilfælde er det 2
matchet og modificeret, da den "første" opdateringsoperation matcher eksisterende array-indgange, og den "sidste" ændringsopdatering matcher, at dokumentet ikke indeholder array-indgangen og udfører $push
at ændre.
Konklusion
Så der har du den kombinerede tilgang, hvor:
-
Den første del af "opdatering" i dit spørgsmål er meget let og kan gøres i en enkelt erklæring , som det er demonstreret i det første afsnit.
-
Den anden del, hvor der er et array-element, som "ikke findes i øjeblikket" inden for det aktuelle dokumentarray kræver dette faktisk, at du bruger
bulkWrite()
for at udstede "flere" operationer i en enkelt anmodning.
Derfor opdater , er "JA" til en enkelt handling. Men tilføje forskel betyder flere operationer. Men du kan kombinere de to tilgange, ligesom det er vist her.
Der er mange "fancy" måder, hvorpå du kan konstruere disse udsagn baseret på "change set"-arrayindholdet med kode, så du ikke behøver at "hardcode" hvert medlem.
Som et grundlæggende tilfælde for JavaScript og kompatibel med den nuværende udgivelse af mongo-skallen (som noget irriterende nok ikke understøtter objektspredningsoperatorer):
db.getCollection('avail_rates_copy').drop();db.getCollection('avail_rates_copy').insert( { "_id" :12345, "_class" :"com.example.ProductRates", " rates" :[ { "productId" :1234, "rate" :100, "rateCardId" :1, "month" :201801 }, { "productId" :1234, "rate" :200, "rateCardId" :1, " month" :201802 }, { "productId" :1234, "rate" :400, "rateCardId" :2, "month" :201803 }, { "productId" :1235, "rate" :500, "rateCardId" , "month" :201801 }, { "productId" :1235, "rate" :234, "rateCardId" :2, "month" :201803 } } });var changeSet =[ { "productId" :1234, "rate " :400.0, "rateCardId":1, "month" :201801 }, { "productId" :1234, "rate" :500.0, "rateCardId":1, "month" :201802 }, { "productId" , "r ate" :700.0, "rateCardId":1, "month" :201802 }];var arrayFilters =changeSet.map((obj,i) => Object.keys(obj).filter(k => k !='rate ' ) .reduce((o,k) => Object.assign(o, { [`u${i}.${k}`]:obj[k] }) ,{}));var $set =changeSet.reduce((o,r,i) => Object.assign(o, { [`rates.$[u${i}].rate`]:r.rate }), {});var updates =[ { "updateOne":{ "filter":{ "_id":12345 }, "update":{ $set }, arrayFilters }}, ...changeSet.map(obj => ( { "updateOne":{ " filter":{ "_id":12345, "rates":{ "$not":{ "$elemMatch":Object.keys(obj).filter(k => k !='rate') .reduce((o) ,k) => Object.assign(o, { [k]:obj[k] }),{}) } } }, "update":{ "$push":{ "rates":obj } } }} ))];db.getCollection('avail_rates_copy').bulkWrite(opdateringer,{ ordnet:true });
Dette vil dynamisk konstruere en liste over "Bulk" opdateringsoperationer, som ville se sådan ud:
[ { "updateOne":{ "filter":{ "_id":12345 }, "update":{ "$set":{ "rates.$[u0].rate":400, " rates.$[u1].rate":500, "rates.$[u2].rate":700 } }, "arrayFilters":[ { "u0.productId":1234, "u0.rateCardId":1, " u0.month":201801 }, { "u1.productId":1234, "u1.rateCardId":1, "u1.month":201802 }, { "u2.productId":1235, "u2.rateCardId":1 , "u2.month":201802 } ] } }, { "updateOne":{ "filter":{ "_id":12345, "rates":{ "$not":{ "$elemMatch":{ "productId" :1234, "rateCardId":1, "month":201801 } } } }, "update":{ "$push":{ "rates":{ "productId":1234, "rate":400, "rateCardId" :1 måned" :201801 } } } }, { "updateOne":{ "filter":{ "_id":12345, "rates":{ "$not":{ "$elemMatch":{ "productId":1234, "rateCardId ":1, "month":201802 } } } }, "update":{ "$push":{ "rates":{ "productId":1234, "rate":500, "rateCardId":1, "month ":201802 } } } }, { "updateOne":{ "filter":{ "_id":12345, "rates":{ "$not":{ "$elemMatch":{ "productId":1235, " rateCardId":1, "month":201802 } } } }, "update":{ "$push":{ "rates":{ "productId":1235, "rate":700, "rateCardId":1, " måned":201802 } } } } }]
Ligesom det blev beskrevet i den "lange form" af det generelle svar, men bruger selvfølgelig blot input-"array"-indholdet for at konstruere alle disse udsagn.
Du kan lave en sådan dynamisk objektkonstruktion på et hvilket som helst sprog, og alle MongoDB-drivere accepterer input af en eller anden type struktur, du har lov til at "manipulere", som derefter transformeres til BSON, før den rent faktisk sendes til serveren til udførelse.