J'ai un problème avec findOneAndUpdate en mangouste. Le cas est que je mets à jour un document en le trouvant. La requête est la suivante:
UserModel.findOneAndUpdate({ individualId: 'some id' }, { $push: { supporterOf: 'some string' } })
Le 'supporterOf' est la référence de UserModel et son type est 'ObjectId'.
Le problème auquel je suis confronté ici est que "une chaîne" est poussée deux fois sous "supporterOf" dans le document.
Quelqu'un peut-il me dire comment pousser un élément de tableau à l'intérieur du document?
8 Réponses :
J'ai récemment rencontré le même problème. Cependant, j'ai réussi à surmonter ce problème par d'autres logiques (détails donnés ci-dessous) mais je n'ai pas pu comprendre la raison pour laquelle findOneAndUpdate insérant des entrées dupliquées dans mongodb.
Vous pouvez surmonter ce problème en suivant la logique.
Utilisez findOne ou findById au lieu de findOneAndUpdate pour rechercher le document dans votre collection, puis mettez à jour manuellement votre document et exécutez save()
Vous pouvez avoir une meilleure idée avec cet extrait de code
return new Promise(function (resolve, reject) { Model.findOne({ someCondition... }, function (err, item) { if (err) { reject(err); } else { item.someArray.push({ someKeyValue... }); item.save().then((result) => { resolve(result) }).catch((err) => { reject(err) }); } }).catch((err) => { reject(err) }); });
Cela n'insérera pas d'élément en double. Cependant, si vous apprenez à connaître le raisonnement derrière la duplication, vous devez mettre à jour ce fil.
@Faizy Vous savez que la mangouste renvoie aussi directement une promesse (à moins que vous n'utilisiez une version assez ancienne), vous n'avez donc pas à l'envelopper dans une autre promesse une fois de plus?
@BenSower Fondamentalement, Asad Ullah m'a dit la solution de contournement des entrées en double. Je veux annuler les entrées en double de la base de données, et pour l'instant findOneAndUpdate causant le problème des entrées en double.
Oui, mais cela ne change rien au fait que cela pourrait améliorer votre style de code en utilisant des fonctions plus faciles à lire ;-)
J'avais le même problème, la solution est un peu bizarre.
J'attendais comme ci-dessous.
**await** schema.findOneAndUpdate(queryParms, { "$push": { "array1": arrayDetails, "array2": array2Details } }, { "upsert": true, "new": true }, function (error, updateResponse) { if (error) { throw new Error (error); } else { // do something with updateResponse; } });
Le simple fait de supprimer await m'a aidé à résoudre ce problème. Besoin de trouver la cause profonde. tout pointeur de référence est le bienvenu.
J'avais le même problème. Mon code était:
const doc = await model.findOneAndUpdate( {filter}, {update}, {new: true}, (err, item) => if(err) console.log(err) } ) res.locals.doc = doc next();
Le problème est que, pour une raison quelconque, ce rappel après la "nouvelle" option créait une double entrée. J'ai supprimé le rappel et cela a fonctionné.
J'ai eu le même problème. J'ai trouvé une solution pour moi:
J'ai utilisé simultanément un rappel et une promesse (donc en utilisant le mot-clé "await").
L'utilisation simultanée d'un rappel et d'une promesse entraînera l'exécution de la requête deux fois. Vous devriez utiliser l'un ou l'autre, mais pas les deux.
options = { upsert: true // creates the object if it doesn't exist. defaults to false. }; await Company.findByIdAndUpdate(company._id, { $push: { employees: savedEmployees } }, options ).exec();
to
options = { upsert: true // creates the object if it doesn't exist. defaults to false. }; await Company.findByIdAndUpdate(company._id, { $push: { employees: savedEmployees } }, options, (err) => { if (err) { debug(err); } } ).exec();
UserModel.findOneAndUpdate( { _id: id }, { object } ) Even if you use _id as a parameter don't forget to make the filter explicit by id
Le problème avec la réponse acceptée est qu'elle ne résout le problème qu'en l'enveloppant dans une promesse supplémentaire inutile, lorsque la méthode findOneAndUpdate () renvoie déjà une promesse. De plus, il utilise à la fois des promesses ET des rappels, ce que vous ne devriez presque jamais faire.
Au lieu de cela, j'adopterais l'approche suivante:
J'aime généralement garder ma logique de requête de mise à jour séparée des autres préoccupations à la fois pour la lisibilité et la réutilisation. donc je créerais une fonction wrapper un peu comme:
const makePush = async () => { try { const result = await update('someObjectId', {$push: {someField: value}}); // do whatever you want to do with the updated document catch (e) { handleError(e) } }
Cette fonction pourrait ensuite être réutilisée dans toute mon application, m'évitant d'avoir à réécrire la configuration d'options répétitives ou exécuter des appels. / p>
Ensuite, j'aurais une autre fonction chargée d'appeler ma requête, de lui transmettre des valeurs et de gérer ce qui en revient.
Quelque chose comme:
const update = (id, updateObj) => { const options = { new: true, upsert: true } return model.findOneAndUpdate({_id: id}, {...updateObj}, options).exec() }
Pas besoin de créer des promesses inutiles, pas d'enfer de rappel, pas de demandes en double et une adhésion plus forte aux principes de responsabilité unique.
Le problème semble provenir de la combinaison d'une attente et d'un rappel. J'ai eu le même problème jusqu'à ce que je réalise que j'utilisais un (err, resp) callback et un .catch (...).
models[auxType].findOneAndUpdate( filter, updateObject, options, (err, resp)=>{ if (err) { console.log("Update failed:",err) res.json(err) } else if (resp) { console.log("Update succeeded:",resp) res.json(resp) } else { console.log("No error or response returned by server") } }) .catch((e)=>{console.log("Error saving Aux Edit:",e)}); // << THE PROBLEM WAS HERE!!
Le problème a été résolu comme dès que j'ai supprimé la ligne .catch (...).
De la documentation mangouste:
Dans mon cas, la modification du rappel async a résolu le problème.
en modifiant ceci:
await schema.findOneAndUpdate( { queryData }, { updateData }, { upsert: true }, (err) => { if (err) console.log(err); } ); if (success) await asyncFunction();
En ceci:
await schema.findOneAndUpdate( { queryData }, { updateData }, { upsert: true }, (err) => { if (err) console.log(err); else await asyncFunction(); } );
Votre requête semble correcte, je pense que votre problème se situe ailleurs dans votre code. Pouvez-vous s'il vous plaît publier le reste de votre point de terminaison? Avez-vous essayé de récupérer manuellement le document, de pousser la chaîne dans le tableau et de l'enregistrer à l'aide de .save ()?
Non, je n'ai pas essayé la récupération manuelle, puis la mise à jour .save () car je veux la transaction en une seule fois. Je veux trouver et mettre à jour le résultat, sa mise à jour mais en insérant l'élément en double. J'ai également débogué le code, je reçois une valeur unique du point final. Pouvez-vous me dire que l'heure de la transaction sera la même lorsque je (récupère et pousse manuellement) et lorsque je (utilise findOneAndUpdate)?
Ce sera plus lent, mais à moins que vous ne prévoyiez de mettre à jour de nombreux documents (par exemple plus de 1000) en même temps, ou que vous ayez des restrictions matérielles très faibles, vous devriez pouvoir négliger ces différences. Néanmoins, je vous suggère d'essayer de déboguer davantage ce problème, car je pense qu'il pourrait y avoir un problème sous-jacent qui pourrait causer d'autres problèmes plus tard. Pour cela, pouvez-vous s'il vous plaît poster une preuve de concept de votre problème?
Oui @BenSower vous avez raison, ce sera plus lent, mais croyez-moi, il se passe en ce moment que findOneAndUpdate insère une entrée en double dans les éléments Pushed. Pour que le problème se reproduise, veuillez créer un schéma dans lequel vous avez un tableau vide. Ensuite, exécutez findOneAndUpdate et poussez les éléments comme dans l'article ci-dessus, vous obtiendrez le même problème d'entrées en double.
J'ai créé ce gist gist.github.com/BenSower/9800a21c2ae4202d81a46fc64bc55bate qui appelle deux fois pousse une corde chacun. Quelle version mangouste utilisez-vous?