MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

Comment puis-je $addToSet un objet à un tableau et $sort aussi en utilisant MongoDB ?

Vous avez fait partie du chemin en identifiant correctement les opérations que vous devez faire. Mais bien sûr $sort n'est pas un modificateur valide pour $addToSet puisque le mantra de MongoDB est "les ensembles ne sont pas considérés comme étant ordonnés" :

L'autre problème ici, comme indiqué par l'erreur, est que vous ne pouvez pas utiliser plusieurs opérateurs de mise à jour (tels que $addToSet et $push ) sur le même chemin vers une propriété au même moment. Il n'y a en fait "aucun ordre" dans l'exécution des différents opérateurs de mise à jour, il n'y a donc aucune garantie que le $addToSet se produit avant le $push . En fait, ils agissent probablement en parallèle, c'est pourquoi l'erreur et que cela n'est pas autorisé.

La réponse est bien sûr "deux" déclarations de mise à jour. Un pour le $addToSet et un pour appliquer le $sort en "poussant" un tableau vide via $each ,

Mais comme nous ne voulons vraiment pas "attendre" la fin de chaque mise à jour, c'est à cela que sert l'API des opérations "Bulk". Ainsi, vous pouvez envoyer les deux instructions au serveur en une envoyez et obtenez un réponse :

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();

Il ne s'agit donc toujours que d'une seule demande au serveur et d'une seule réponse. Il s'agit toujours de "deux" opérations, mais la surcharge et la possibilité qu'un thread saisisse l'état intermédiaire de la mise à jour sont négligeables.

Il existe une alternative à cette approche qui consiste à déplacer la logique "set detection" dans le .find() partie de la déclaration de mise à jour, puis appliquez simplement $push où le ou les membres à ajouter à "l'ensemble" n'existent pas déjà :

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();

Bien sûr, la complication est que si vous ajoutez "plusieurs" éléments de tableau ici, vous devrez envelopper ces $not et $elemMacth teste dans un $and condition, puis si "un seul" de ces éléments était valide, il ne pouvait pas être ajouté seul.

Vous pouvez "essayer" ce genre d'opération avec "plusieurs" éléments "d'abord", mais ensuite vous devriez avoir une exécution "de repli" de chaque élément de tableau individuel avec la même logique que ci-dessus pour "tester" la possibilité de "pousser" pour chacun.

Donc $addToSet rend cette deuxième partie facile avec plusieurs entrées de tableau. Pour une entrée, il suffit de "requête" et de $push , pour plus d'un, c'est probablement le chemin le plus court pour utiliser le "premier" modèle avec $addToSet et $push un tableau vide pour "trier" le résultat puisque l'application du deuxième modèle signifie de toute façon plusieurs tests de mise à jour.