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

Come posso $ addToSet un oggetto su un array e $sort anche usando MongoDB?

Hai fatto parte del percorso individuando correttamente le operazioni che devi fare. Ma ovviamente $sort non è un modificatore valido per $addToSet poiché il mantra di MongoDB è "i set non sono considerati ordinati" :

L'altro problema qui indicato dall'errore è che non è possibile utilizzare più operatori di aggiornamento (come $addToSet e $push ) sullo stesso percorso verso una proprietà contemporaneamente. Non c'è infatti "nessun ordine" per l'esecuzione di diversi operatori di aggiornamento, quindi non vi è alcuna garanzia che il $addToSet si verifica prima di $push . In effetti probabilmente stanno agendo in parallelo, motivo per cui l'errore e che questo non è consentito.

La risposta ovviamente sono "due" dichiarazioni di aggiornamento. Uno per il $addToSet e uno per applicare il $sort "spingendo" un array vuoto tramite $each ,

Ma poiché non vogliamo davvero "aspettare" il completamento di ogni aggiornamento, ecco a cosa serve l'API delle operazioni "in blocco". Quindi puoi inviare entrambe le istruzioni al server in una invia e ricevi uno risposta:

var bulk = db.perros.initializeOrderedBulkOp();
bulk.find({ "name": "Risas" }).update({ 
   "$addToSet": { 
       "propiedades": { "name": "cola", "cantidad": 1 }
   }
});
bulk.find({ "name": "Risas" }).update({ 
   "$push": { 
       "propiedades": { 
           "$each": [ ], "$sort": { "cantidad": -1 } 
        }
   }
});
bulk.execute();

Quindi questa è davvero ancora solo una richiesta al server e una risposta. Sono ancora "due" operazioni, ma l'overhead e la possibilità che alcuni thread afferrino lo stato provvisorio dell'aggiornamento sono trascurabili.

C'è un'alternativa a questo approccio che consiste nello spostare la logica "set detection" nel .find() parte della dichiarazione di aggiornamento e quindi applica semplicemente $push dove il/i membro/i da aggiungere al "set" non esistono già:

var bulk = db.perros.initializeOrderedBulkOp();
bulk.find({ 
    "name": "Risas", 
    "propiedades": { 
        "$not": { "$elemMatch": { "name": "cola", "cantidad": 1 } } 
    } 
}).update({ 
   "$push": { 
       "propiedades": { 
           "$each": [{ "name": "cola", "cantidad": 1 }], "$sort": { "cantidad": -1 } 
        }
   }
});
bulk.execute();

Ovviamente la complicazione è che se stai aggiungendo elementi di array "multipli" qui dovresti avvolgere quei $not e $elemMacth test in un $and condizione, e quindi se "solo uno" di quegli elementi era valido, non poteva essere aggiunto da solo.

Puoi "provare" quel tipo di operazione con "più" elementi "prima", ma poi dovresti avere un'esecuzione "di fallback" di ogni singolo elemento dell'array con la stessa logica di cui sopra per "testare" la possibilità di "push" per ciascuno.

Quindi $addToSet semplifica la seconda parte con più voci di array. Per una voce è abbastanza semplice "interrogare" e $push , per più di uno è probabilmente il percorso più breve per utilizzare il "primo" modello con $addToSet e $push un array vuoto per "ordinare" il risultato poiché l'applicazione del secondo modello significa comunque più test di aggiornamento.