<p>mongoose 4.0.1 : middleware avant la "mise à jour", cet objet ne renvoie pas l'objet modèle</p>

Créé le 30 mars 2015  ·  40Commentaires  ·  Source: Automattic/mongoose

Bonjour, j'ai un problème avec la dernière version de Mongoose. Je crée une API avec Express et Mongoose 4.0.1, et je ne sais pas si je fais quelque chose de mal, mais le fait est que chaque fois que j'essaie d'utiliser le nouveau middleware de mise à jour pre la même manière J'utilise pre save middleware, l'objet this ne renvoie pas l'objet en cours de mise à jour, mais renvoie l'objet Query .

Exemple de ce que j'essaye d'expliquer :

ExerciseSchema.pre('save', function (next, done) {
        var self = this;
        console.log('exercise:', self); // returns exercise object
        // some validations here
       next();
});
ExerciseSchema.pre('update', function (next, done) {
        var self = this;
        console.log('exercise:', self); // returns Query object instead of exercise object
        // some validations here
       next();
});

C'est ce que j'obtiens dans l'objet de référence this dans le middleware, et je ne sais pas en quoi cela m'est utile.

{ _mongooseOptions: {},
  mongooseCollection: 
   { collection: { s: [Object] },
     opts: { bufferCommands: true, capped: false },
     name: 'exercises',
     conn: 
     ... },
   ...
}

En regardant le code source, certaines de ses propriétés sont définies dans la fonction Query définie dans ./node_modules/mongoose/lib/query.js :

Est-ce quelque chose d'inattendu ou est-ce que je fais quelque chose de mal ? Il serait intéressant d'avoir une solution car je n'aime pas l'idée de valider au sein d'un middleware sur la sauvegarde d'objet et d'être obligé d'exécuter des validations directement sur le contrôleur lors de la mise à jour.

La même chose se produit avec findOneAndUpdate pre middleware, mais je ne m'attends pas à ce qu'il renvoie l'exercice avant de le trouver. En fait et à mon avis honnête, je pense qu'il serait intéressant que findAndUpdate et findOneAndUpdate pré middlewares déclenchent find(One) et mettent à jour les middlewares, dans cet ordre, au lieu d'avoir son propre middleware.

Merci d'avance pour votre aide.

won't fix

Commentaire le plus utile

Ah ok je vois que vous utilisez findOneAndUpdate() plutôt que update() . Ce sont deux fonctions distinctes avec des crochets distincts, donc si vous voulez ajouter des opérations de mise à jour à findOneAndUpdate dans un pré-crochet, vous devriez faire

schema.pre('findOneAndUpdate', function() {
  this.findOneAndUpdate({}, { $set: { key: 'value' } });
});

Tous les 40 commentaires

De par sa conception, le document mis à jour peut même ne pas se trouver dans la mémoire du serveur. Pour ce faire, la mangouste devrait faire un findOne() pour charger le document avant de faire la mise à jour(), ce qui n'est pas acceptable.

La conception est de vous permettre de manipuler l'objet de requête en ajoutant ou en supprimant des filtres, en mettant à jour des paramètres, des options, etc. Par exemple, en appelant automatiquement .populate() avec find() et findOne() , en définissant le multi: true option par défaut sur certains modèles, contrôle d'accès, et autres possibilités .

findOneAndUpdate() est un peu impropre, il utilise la commande sous-jacente mongodb findAndModify , ce n'est pas la même chose que findOne() + update() . En tant qu'opération distincte, il devrait avoir son propre middleware.

Comment suggéreriez-vous alors de mettre à jour un champ de document lors de la mise à jour "pré" du middleware ?

this.update({ field: val }); ajoutera { $set: { field: val } } à l'opération de mise à jour avant qu'elle ne se produise.

Merci pour la réponse, sur quelle version est-ce censé fonctionner ? parce que j'utilise 4.0.2 et ce n'est pas le cas. Dois-je appeler "exec" sur la requête ?

si je lance this.update, n'appellerais-je pas cette méthode http://mongoosejs.com/docs/api.html#query_Query -update ?

J'ai fini par faire this.findOneAndUpdate({matcher: "myvalue"}); espérons que c'est la bonne approche.

Hmm 4.0.2 devrait fonctionner. À quoi ressemble votre code ?

Je viens de faire quelque chose comme ça :

var schema = mongoose.Schema({
    name: String,
    description: String,
    matcher: String,

});

var generateMatcherUpdate= function(next) {
     var matcher = "generate matcher function"
        this.update({matcher: matcher});

    next();
};

 schema.pre('update', generateMatcherUpdate);

Ah ok je vois que vous utilisez findOneAndUpdate() plutôt que update() . Ce sont deux fonctions distinctes avec des crochets distincts, donc si vous voulez ajouter des opérations de mise à jour à findOneAndUpdate dans un pré-crochet, vous devriez faire

schema.pre('findOneAndUpdate', function() {
  this.findOneAndUpdate({}, { $set: { key: 'value' } });
});

la documentation man mangouste est horriblement mauvaise. Quelqu'un peut-il s'il vous plaît mettre cela là-dedans?

@askdesigners a mis quoi exactement là-dedans ?

Désolé j'ai écrit ça dans un moment de stress :)

Juste la fragilité de la compatibilité des versions vraiment. Ce n'est pas un problème évident et laisse beaucoup de gens se gratter la tête.

Pas de soucis. Quel genre de problèmes de compatibilité de version rencontrez-vous ?

Et que se passe-t-il si je veux désinfecter une valeur sur pre.update... Je n'arrive pas à trouver un moyen de le faire.
Merci!

schema.pre('update', function() {
  var v = this.getUpdate().valueToSanitize;
  this.update({}, { $set: { valueToSanitize: sanitize(v) } });
});

Super, je vais essayer ça !
Merci!!

@vkarpov15 , j'ai rencontré quelque chose que je dirais être une sorte de comportement inattendu, ou du moins il devrait être documenté.

Si je fais quelque chose comme

MyModel.findOneAndUpdate({a: 1}, {v: '__3'}, function (err, model) {
    console.log(model);
});

Et sur mon modèle j'implémente votre suggestion

mySchema.pre('findOneAndUpdate', function() {
  var v = this.getUpdate().v;
  this.findOneAndUpdate({}, { $set: { v: sanitize(v) } });
});

Le journal affichera '__3' puisque la requête de mise à jour d'origine sera exécutée après le hook. Je peux modifier d'autres propriétés de schéma, mais tous les champs mis à jour sur la requête d'origine resteront les mêmes même s'ils sont modifiés dans le pré-hook.

@CMatias c'est un cas où vous devez faire attention à utiliser $set ou pas de manière cohérente. Mongoose ne sait pas nécessairement s'il faut envelopper ou non $set , donc dans votre cas, vous devriez faire this.findOneAndUpdate({}, { v: sanitize(v) });

@ vkarpov15 merci, cela a fonctionné. Pourriez-vous expliquer brièvement quelle est la différence entre utiliser $set et ne pas l'utiliser ? Je ne trouve pas beaucoup d'informations à ce sujet.

Donc, si vous utilisez directement le pilote mongodb natif et faites coll.findOneAndUpdate({ a: 1 }, { __v: 3 }) , mongodb prendra le premier document avec a = 1 et le remplacera par le document { __v: 3 } modulo _id . En d'autres termes, il écrasera le document existant. Afin de définir simplement la clé '__v', vous devez faire coll.findOneAndUpdate({ a: 1 }, { $set: { __v: 3 } }) .

Ce comportement a toujours été controversé et sujet aux erreurs, donc par défaut, la mangouste vous empêche d'écraser le document. En d'autres termes, MyModel.findOneAndUpdate({ a: 1 }, { __v: 3 }) devient MyModel.collection.findOneAndUpdate({ a: 1 }, { $set: { __v: 3 } }) _à moins que vous ne définissiez l'option overwrite: true . Cependant, vous pouvez faire des choses étranges comme MyModel.update({}, { $set: { b: 2 } }).findOneAndUpdate({ a: 1 }, { __v: 3 }, { overwrite: true }) qui rendent assez déroutant l'état actuel de getUpdate() , nous laissons donc le statut de update tel quel pour le middleware.

J'essaie de comprendre tout cela, mais j'ai du mal à déterminer si je peux modifier le document qui sera renvoyé à partir d'une recherche à l'aide de ces méthodes middleware. Plus précisément, j'aimerais "décorer" la réponse en ajoutant des champs supplémentaires.

Par exemple, si j'ai un schéma :

const Thing = new mongoose.Schema({
  name: {type: String}
});

Thing.post('find', function(doc, next){
  doc.newfield = "example text";
  next();
}

Est-ce possible?

La raison pour laquelle newfield n'est pas défini dans le schéma est qu'en fonction des valeurs de Thing , différents noms de champ sont nécessaires.

Merci!

j'ai besoin d'un plugin spécifique pour utiliser schema.pre('update') ?

Non @jrogatis

@vkarpov15

En regardant l'exemple que vous avez donné ci-dessus ...:

schema.pre('update', function() {
  var v = this.getUpdate().valueToSanitize;
  this.update({}, { $set: { valueToSanitize: sanitize(v) } });
});

Cela ne fonctionne pas pour moi. Le this.getUpdate().value ne contient pas ma propriété value comme le suggère l'exemple. Au lieu de cela, il est disponible sous this.getUpdate().$set.property .

C'est censé être comme ça ? Est-ce que je comprends mal quelque chose ? L'API a-t-elle changé ? Existe-t-il une documentation à ce sujet ?

@qqilihq pouvez-vous ouvrir un problème séparé avec un script repro ?

@qqilihq nvm, je viens de voir que tu l'as fait :P

@varunjayaraman En fait, le problème séparé que j'ai ouvert est différent :) Mais j'en ouvrirai volontiers un nouveau pour le commentaire ci-dessus, une fois que j'aurai quelques minutes de rechange pour compiler un exemple.

@qqilihq ah ok, merci !

Exactement la même chose pour moi comme mentionné par @qqilihq

image

Ainsi, le comportement souhaité pourrait être atteint avec ce code :

userSchema.pre('update', function (next) {
  const newEmail = this.getUpdate().$set.email
  if (newEmail) {
    this.update({}, {$set: {email: normalizeEmail(newEmail)}})
  }
  next()
})

Mais il semble que j'essaie d'utiliser des champs qui ne sont pas destinés à cela.

La méthode pre ne devrait pas fonctionner avec this.update , car elle est "pré"-enregistrée et il n'y a encore rien à mettre à jour, tandis que this.newProp = "value" fonctionne parfaitement, merci les gars.

Mon cas est :

UserSchema.pre('save', function(next) {
    this.created = Date.now()
    next()
})

Je comprends le raisonnement derrière avoir la requête comme "ceci" sur le crochet de mise à jour "pré" mais pourquoi le crochet de mise à jour "post" se comporte-t-il de la même manière? La publication de la mise à jour ne devrait-elle pas avoir le modèle réel comme "ceci" ?

Alors, post find s'exécuterait plusieurs fois ou aurait this comme tableau de plusieurs résultats? Et s'il n'y a pas de résultats, la recherche de publication ne devrait-elle pas être exécutée ?

Tout cela est très étrange. À mon humble avis, les pré-hooks devraient tous se comporter de la même manière, avec un this qui est l'objet modifié et de belles méthodes comme isModified pour vérifier les choses.

En fait, je pense que je comprends maintenant. Je devrais juste utiliser la méthode pre 'save' puis la méthode this.isNew. Merci!

@ajbraus c'est un peu étrange, mais l'alternative est une surcharge de performances massive. Supposons que vous ayez un updateMany qui met à jour des millions de documents - mongoose devrait tous les charger en mémoire.

Comment est-on censé vérifier les champs du document avant les mises à jour ? Disons que je dois vérifier si certains champs sont remplis pour définir le document comme "valide", comment puis-je accéder à l'ensemble du document, et pas seulement aux champs en cours de mise à jour ? this fait référence à la requête ici

@ PDS42 , vous devrez exécuter une requête distincte pour charger le document à partir de mongodb

Y a-t-il un lien particulier dans la documentation de la mangouste où tout cela est expliqué plus en profondeur ? J'aimerais lire à ce sujet et comprendre cela plus complètement :).

ÉDITER:

crochets de pré-mise à jour : https://github.com/Automattic/mongoose/issues/2812
middleware : https://mongoosejs.com/docs/middleware.html
objet de requête : https://mongoosejs.com/docs/api.html#Query
requêtes : https://mongoosejs.com/docs/queries.html

J'ai du mal à trouver de la documentation sur la façon exacte dont on doit utiliser les requêtes référencées par 'this' dans le middleware de mise à jour. Par exemple, que se passe-t-il lorsque :
this.update({field: "value"}, {$set: {updatedAt: new Date()}}); Est-ce que cela ajoute {field: "value"} au filtre ? Ou ça n'a aucun effet ? Y a-t-il de la documentation que je ne vois pas ?

@ronakvora il y a une fonction Query#getConditions() .

schema.pre('update', function() {
  console.log(this.getConditions()); // Will include `{ field: 'value' }`
});

Gotcha, ça m'aidera à jouer avec les choses :). Merci!

Ps c'est Ronak d'en haut. Apparemment, j'ai deux comptes connectés sur des appareils différents.

Cette page vous a été utile?
0 / 5 - 0 notes