J'ai actuellement un problème avec la mise à jour des données dans MongoDB via mangouste. J'ai un document imbriqué de la structure suivante
const updateById = async (Id: string, NewDoc: DocClass) => { let doc: DocClass | null = await DocumentModel.findOneAndUpdate( { _id: Id }, { $set: NewDoc }, { new: true, runValidators: true }); if (!doc) { throw createError.documentNotFound( { msg: `The Document you tried to update (Id: ${Id}) does not exist` } ); } return doc; }
Maintenant, mon problème est que je mets à jour ceci avec findOneAndUpdateById. J'ai précédemment défini la position sur des valeurs autres que la valeur par défaut. Je veux mettre à jour en laissant la position telle quelle en faisant ma demande sans la position car mon frontend ne devrait jamais la mettre à jour (une autre application le fait).
Cependant l'appel suivant
const someSchema:Schema = new mongoose.Schema({ Title: String, Subdocuments: [{ SomeValue: String Position: { X: {type: Number, default: 0}, Y: {type: Number, default: 0}, Z: {type: Number, default: 0} } }] });
3 Réponses :
Pourquoi ne trouvez-vous pas le document par identifiant, mettez-vous à jour les nouvelles valeurs, puis enregistrez-le?
const updateById = async (Id: string, NewDoc: Training) => { const doc: Training | null = await TrainingModel.findById({ _id: Id }); if (!doc) { throw createError.documentNotFound( { msg: `The Document you tried to update (Id: ${Id}) does not exist` } ); } doc.title = NewDoc.title; doc.subdocument.someValue = NewDoc.subdocument.someValue await doc.save(); return doc; }
consultez le lien sur la façon de mettre à jour un document avec Mongoose https://mongoosejs.com/docs/documents.html#updating p >
Je pourrais mais avec un document imbriqué, je devrais alors utiliser des opérateurs ternaires pour vérifier si le NewDoc le contient et le définir si c'est vrai. Cela signifierait essentiellement écrire à nouveau tout le schéma, ce qui le rendrait laid à maintenir. Cela pourrait être la solution de secours si rien d'autre ne fonctionne. Je trouve aussi étrange que le comportement de mongoose / mongodb diffère entre le document racine et le sous-document
Je ne sais pas comment ce sera moche. Je suis sûr que la solution que vous avez n'est pas meilleure que celle-ci.
Après avoir réfléchi à cela ce week-end, je suis arrivé à la conclusion que le comportement de mongodb était correct.
Pourquoi?
Je passe un document et une requête à la base de données. MongoDb recherche ensuite les documents avec cette requête. Il mettra à jour tous les champs pour lesquels une valeur a été fournie. Si pour le titre je mets une nouvelle chaîne, le titre sera remplacé par celui-là, un nombre par celui-là et ainsi de suite. Maintenant, pour mon sous-document, je passe un tableau. Et comme il n'y a pas de requête, le comportement correct est que ce champ sera défini sur le tableau. Les sous-documents ne sont donc pas mis à jour mais bien initialisés. Ce qui provoquera correctement la définition des valeurs par défaut. Si je veux juste mettre à jour les sous-documents, ce n’est pas la bonne façon de procéder
Comment le faire correctement
Pour moi, le moyen idéal est de séparer la logique et créez un point de terminaison séparé pour mettre à jour les sous-documents avec leur propre requête. Donc, pour mettre à jour tous les sous-documents donnés, la fonction ressemblerait à quelque chose comme ça
const updateSubdocumentsById= async ({ Id, Subdocuments}: { Id: string; Subdocuments: Subdocument[]; }): Promise<Subdocument[]> => { let updatedSubdocuments:Subdocument[] = []; for (let doc of Subdocuments){ // Create the setter let set = {}; for (let key of Object.keys(doc)){ set[`Subdocument.$.${key}`] = doc[key]; } // Update the subdocument let updatedDocument: Document| null = await DocumentModel.findOneAndUpdate( {"_id": Id, "Subdocuments._id": doc._id}, { "$set" : set }, { new : true} ); // Aggregate and return the updated Subdocuments if(updatedDocument){ let updatedSubdocument:Subdocument = updatedTraining.Subdocuments.filter((a: Subdocument) => a._id.toString() === doc._id)[0]; if(updatedSubdocument) updatedSubdocuments.push(updatedSubdocument); } } return updatedSubdocuments; }
J'ai eu des difficultés avec ça toute la soirée. Je viens de trouver une solution très simple qui, pour autant que je sache, fonctionne parfaitement.
const venue = await Venue.findById(_id) venue.name = name venue.venueContact = venueContact venue.address.line1 = line1 || venue.address.line1 venue.address.line2 = line2 || venue.address.line2 venue.address.city = city || venue.address.city venue.address.county = county || venue.address.county venue.address.postCode = postCode || venue.address.postCode venue.address.country = country || venue.address.country venue.save() res.send(venue)
Le résultat est que toutes les clés qui ne reçoivent pas de nouvelle valeur seront simplement remplacées par l'original valeurs.
pas d'idées? Ce que j'imagine maintenant, c'est qu'en envoyant les sous-documents avec l'id mongoose, ce n'est pas réellement mettre à jour les sous-documents car en initialisant de nouveaux mais en définissant l'id sur l'ancienne valeur. ce qui est bien pour les requêtes get du frontend mais finit par exécuter la logique de création et définir ainsi toutes les valeurs par défaut pour les valeurs qui ne sont pas définies