Det, du prøver at gøre her, er kun at tilføje et nyt element til et array, hvor elementet ikke findes, og også oprette et nyt dokument, hvor det ikke findes. Du vælger $addToSet
fordi du ønsker, at genstandene skal være unikke, men i virkeligheden ønsker du, at de kun skal være unikke med "a".
Så $addToset
vil ikke gøre det, og du skal hellere "teste" det element, der er til stede. Men det egentlige problem her er, at det ikke er muligt både at gøre det og "upsert" på samme tid. Logikken kan ikke fungere, da et nyt dokument vil blive oprettet, når array-elementet ikke blev fundet, i stedet for at føje til array-elementet, som du ønsker.
Den aktuelle operation er designet som $addToSet
kan ikke bruges til at "skabe" et array, men kun til at "føje" medlemmer til et eksisterende array. Men som allerede nævnt, har du andre problemer med at opnå logikken.
Hvad du har brug for her er en sekvens af opdateringshandlinger, som hver "forsøger" at udføre deres forventede handling. Dette kan kun gøres med flere udsagn:
// attempt "upsert" where document does not exist
// do not alter the document if this is an update
db.test.update(
{ "name": "abc" },
{ "$setOnInsert": { "config": [{ "a": 1, "b": 2 }] }},
{ "upsert": true }
)
// $push the element where "a": 1 does not exist
db.test.update(
{ "name": "abc", "config.a": { "$ne": 1 } },
{ "$push": { "config": { "a": 1, "b": 2 } }}
)
// $set the element where "a": 1 does exist
db.test.update(
{ "name": "abc", "config.a": 1 },
{ "$set": { "config.$.b": 2 } }
)
Ved en første iteration vil den første sætning "ophæve" dokumentet og skabe arrayet med elementer. Den anden sætning vil ikke matche dokumentet, fordi "a"-elementet har den værdi, der blev angivet. Den tredje sætning vil matche dokumentet, men den vil ikke ændre den i en skriveoperation, fordi værdierne ikke er ændret.
Hvis du nu ændrer input til "b": 3
du får forskellige svar, men det ønskede resultat:
db.test.update(
{ "name": "abc" },
{ "$setOnInsert": { "config": [{ "a": 1, "b": 3 }] }},
{ "upsert": true }
)
db.test.update(
{ "name": "abc", "config.a": { "$ne": 1 } },
{ "$push": { "config": { "a": 1, "b": 3 } }}
)
db.test.update(
{ "name": "abc", "config.a": 1 },
{ "$set": { "config.$.b": 3 } }
)
Så nu matcher den første sætning et dokument med "name": "abc"
men gør ikke noget, da de eneste gyldige operationer er på "indsæt". Den anden sætning stemmer ikke overens, fordi "a" matcher betingelsen. Det tredje udsagn matcher værdien af "a" og ændrer "b" i det matchede element til den ønskede værdi.
Efterfølgende ændring af "a" til en anden værdi, der ikke eksisterer i arrayet, tillader både 1 og 3 ikke at gøre andet end den anden sætning tilføjer endnu et medlem til arrayet og holder indholdet unikt med deres "a"-nøgler.
Også indsendelse af en erklæring uden ændringer fra eksisterende data vil naturligvis resultere i et svar, der siger, at intet er ændret på alle konti.
Det er sådan, du gør dine operationer. Du kan gøre dette med "ordered" Bulk operationer, så der kun er en enkelt anmodning og svar fra serveren med det gyldige svar på ændret eller oprettet.