Det du försöker göra här är att lägga till ett nytt objekt i en array endast där objektet inte finns och även skapa ett nytt dokument där det inte finns. Du väljer $addToSet
eftersom du vill att föremålen ska vara unika, men i själva verket vill du verkligen att de ska vara unika endast med "a".
Så $addToset
kommer inte att göra det, och du måste snarare "testa" elementet som finns. Men det verkliga problemet här är att det inte går att både göra det och "uppröra" samtidigt. Logiken kan inte fungera eftersom ett nytt dokument kommer att skapas när arrayelementet inte hittades, istället för att lägga till arrayelementet som du vill.
Den nuvarande operationen är designad som $addToSet
kan inte användas för att "skapa" en array, utan bara för att "lägga till" medlemmar i en befintlig array. Men som redan nämnts har du andra problem med att uppnå logiken.
Vad du behöver här är en sekvens av uppdateringsoperationer som var och en "försöker" utföra sin förväntade åtgärd. Detta kan endast göras med flera påståenden:
// 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 } }
)
Vid en första iteration kommer den första satsen att "upphäva" dokumentet och skapa arrayen med objekt. Den andra satsen matchar inte dokumentet eftersom "a"-elementet har det angivna värdet. Den tredje satsen kommer att matcha dokumentet men den kommer inte att ändra den i en skrivoperation eftersom värdena inte har ändrats.
Om du nu ändrar inmatningen till "b": 3
du får olika svar men önskat 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 matchar den första satsen ett dokument med "name": "abc"
men gör ingenting eftersom de enda giltiga operationerna är på "insert". Den andra satsen matchar inte eftersom "a" matchar villkoret. Det tredje påståendet matchar värdet på "a" och ändrar "b" i det matchade elementet till det önskade värdet.
Om du därefter ändrar "a" till ett annat värde som inte finns i arrayen kan både 1 och 3 inte göra något annat än att den andra satsen lägger till ytterligare en medlem i arrayen och håller innehållet unikt med sina "a"-nycklar.
Att även skicka in ett uttalande utan ändringar från befintliga data kommer naturligtvis att resultera i ett svar som säger att ingenting har ändrats på alla konton.
Det är så du gör dina operationer. Du kan göra detta med "beställd" Bulk operationer så att det bara finns en enda begäran och ett svar från servern med det giltiga svaret på modifierad eller skapad.