<p>Mungo 4.0.1: Vor der „Update“-Middleware gibt dieses Objekt kein Modellobjekt zurück</p>

Erstellt am 30. März 2015  ·  40Kommentare  ·  Quelle: Automattic/mongoose

Hallo, ich habe ein Problem mit der neusten Version von Mongoose. Ich erstelle eine API mit Express und Mongoose 4.0.1 und bin mir nicht sicher, ob ich etwas falsch mache, aber Tatsache ist, dass jedes Mal, wenn ich versuche, die neue pre Update-Middleware auf die gleiche Weise zu verwenden Ich verwende pre save Middleware, das this Objekt gibt nicht das zu aktualisierende Objekt zurück, sondern das Query Objekt.

Beispiel was ich versuche zu erklären:

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

Dies ist, was ich in this Referenzobjekt innerhalb der Middleware bekomme, und ich weiß nicht, wie es für mich nützlich ist.

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

Wenn Sie sich den Quellcode ansehen, sind einige seiner Eigenschaften in der Funktion Query definiert, die in ./node_modules/mongoose/lib/query.js definiert ist:

Ist das etwas Unerwartetes oder mache ich etwas falsch? Es wäre interessant, eine Lösung zu haben, da ich die Idee nicht mag, innerhalb einer Middleware beim Speichern von Objekten zu validieren und beim Aktualisieren Validierungen direkt auf dem Controller ausführen zu müssen.

Dasselbe passiert mit findOneAndUpdate pre Middleware, aber ich erwarte nicht, dass sie die Übung zurückgibt, bevor sie gefunden wurde. Tatsächlich und meiner ehrlichen Meinung nach wäre es interessant, dass findAndUpdate und findOneAndUpdate pre-Middlewares find(One)- und update-Middlewares in dieser Reihenfolge auslösten, anstatt eine eigene Middleware zu haben.

Vielen Dank im Voraus für Ihre Hilfe.

won't fix

Hilfreichster Kommentar

Ah ok, ich sehe, Sie verwenden findOneAndUpdate() statt update() . Dies sind zwei unterschiedliche Funktionen mit unterschiedlichen Hooks. Wenn Sie also Aktualisierungsoperationen zu findOneAndUpdate in einem Pre-Hook hinzufügen möchten, sollten Sie dies tun

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

Alle 40 Kommentare

Absichtslos - das zu aktualisierende Dokument befindet sich möglicherweise nicht einmal im Arbeitsspeicher des Servers. Dazu müsste Mungo ein findOne() ausführen, um das Dokument zu laden, bevor update() ausgeführt wird, was nicht akzeptabel ist.

Das Design soll es Ihnen ermöglichen, das Abfrageobjekt durch Hinzufügen oder Entfernen von Filtern, Aktualisieren von Parametern, Optionen usw. zu manipulieren. Zum Beispiel automatisches Aufrufen von .populate() mit find() und findOne() , Setzen von multi: true Option standardmäßig bei bestimmten Modellen, Zugangskontrolle und andere Möglichkeiten .

findOneAndUpdate() ist ein bisschen irreführend, es verwendet den zugrunde liegenden mongodb findAndModify Befehl, es ist nicht dasselbe wie findOne() + update() . Als separate Operation sollte es über eine eigene Middleware verfügen.

Wie würden Sie dann vorschlagen, ein Dokumentfeld während der Middleware vor dem Update zu aktualisieren?

this.update({ field: val }); fügt { $set: { field: val } } zum Update-Vorgang hinzu, bevor er stattfindet.

Danke für die Antwort, auf welcher Version soll das funktionieren? weil ich 4.0.2 verwende und es nicht tut. Muss ich bei der Abfrage "exec" aufrufen?

Wenn ich this.update ausführe, würde ich diese Methode nicht aufrufen http://mongoosejs.com/docs/api.html#query_Query -update?

Am Ende habe ich dies getan.findOneAndUpdate({matcher: "myvalue"}); hoffe das ist der richtige ansatz.

Hmm 4.0.2 sollte funktionieren. Wie sieht dein Code aus?

Ich habe gerade so etwas gemacht:

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, ich sehe, Sie verwenden findOneAndUpdate() statt update() . Dies sind zwei unterschiedliche Funktionen mit unterschiedlichen Hooks. Wenn Sie also Aktualisierungsoperationen zu findOneAndUpdate in einem Pre-Hook hinzufügen möchten, sollten Sie dies tun

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

Man Mungo Dokumentation ist hasserfüllt schlecht. Kann das bitte jemand dort reinstellen?

@askdesigners was genau da rein?

Entschuldigung, dass ich das in einem Moment des Stresses geschrieben habe :)

Nur die Sprödigkeit der Versionskompatibilität wirklich. Es ist kein offensichtliches Problem und lässt viele Leute am Kopf kratzen.

Keine Bange. Welche Art von Versionskompatibilitätsproblemen haben Sie?

Und was ist, wenn ich einen Wert auf pre.update bereinigen möchte ... Ich kann anscheinend keinen Weg finden, dies zu tun.
Danke!

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

Schön, das werde ich ausprobieren!
Danke!!

Hey @vkarpov15 , ich bin auf etwas gestoßen, von dem ich sagen würde, dass es ein unerwartetes Verhalten ist, oder zumindest sollte es dokumentiert werden.

Wenn ich sowas mache

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

Und an meinem Modell setze ich deinen Vorschlag um

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

Das Protokoll gibt „__3“ aus, da die ursprüngliche Aktualisierungsabfrage nach dem Hook ausgeführt wird. Ich kann andere Schemaeigenschaften ändern, aber alle Felder, die in der ursprünglichen Abfrage aktualisiert werden, bleiben gleich, auch wenn sie im Pre-Hook geändert werden.

@CMatias Dies ist ein Fall, in dem Sie vorsichtig sein müssen, wenn Sie $set oder nicht konsequent verwenden. Mongoose weiß nicht unbedingt, ob er $set einschließen soll oder nicht, also sollten Sie in Ihrem Fall this.findOneAndUpdate({}, { v: sanitize(v) }); tun

@ vkarpov15 danke, das hat funktioniert. Können Sie kurz erklären, was der Unterschied zwischen der Verwendung $set und der Nichtverwendung ist? Kann nicht viele Informationen darüber finden.

Wenn Sie also direkt den nativen Mongodb-Treiber verwenden und coll.findOneAndUpdate({ a: 1 }, { __v: 3 }) ausführen, nimmt Mongodb das erste Dokument mit a = 1 und ersetzt es durch das Dokument { __v: 3 } modulo _id . Mit anderen Worten, es wird das vorhandene Dokument überschreiben. Um nur den Schlüssel '__v' zu setzen, müssen Sie coll.findOneAndUpdate({ a: 1 }, { $set: { __v: 3 } }) tun.

Dieses Verhalten war schon immer umstritten und fehleranfällig, daher verhindert Mongoose standardmäßig, dass Sie das Dokument überschreiben. Mit anderen Worten, aus MyModel.findOneAndUpdate({ a: 1 }, { __v: 3 }) wird MyModel.collection.findOneAndUpdate({ a: 1 }, { $set: { __v: 3 } }) , _es sei denn, Sie haben die Option overwrite: true gesetzt. Sie können jedoch seltsame Dinge wie MyModel.update({}, { $set: { b: 2 } }).findOneAndUpdate({ a: 1 }, { __v: 3 }, { overwrite: true }) tun, die den aktuellen Status von getUpdate() ziemlich verwirrend machen, also lassen wir den Status von update einfach so wie er ist für Middleware.

Ich versuche, all dies zu groken, aber es fällt mir schwer zu bestimmen, ob ich das Dokument ändern kann, das von einem Fund mit diesen Middleware-Methoden zurückgegeben wird. Insbesondere möchte ich die Antwort "dekorieren", indem ich zusätzliche Felder hinzufüge.

Wenn ich zum Beispiel ein Schema habe:

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

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

Ist das möglich?

Der Grund, warum newfield nicht im Schema definiert ist, liegt darin, dass abhängig von den Werten von Thing unterschiedliche Feldnamen benötigt werden.

Danke!

Ich brauche ein bestimmtes Plugin, um schema.pre('update') zu verwenden?

Nein @jrogatis

@vkarpov15

Wenn Sie sich das Beispiel ansehen, das Sie oben gegeben haben ...:

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

Das funktioniert bei mir nicht . Das this.getUpdate().value enthält nicht meine Eigenschaft value , wie das Beispiel vermuten lässt. Stattdessen ist es unter this.getUpdate().$set.property erhältlich.

Soll das so sein? Verstehe ich etwas falsch? Hat sich die API geändert? Gibt es dazu Unterlagen?

@qqilihq können Sie ein separates Problem mit einem Repro-Skript öffnen?

@qqilihq nvm, habe gerade gesehen, dass du es getan hast: P

@varunjayaraman Eigentlich ist die separate Ausgabe, die ich geöffnet habe, eine andere :) Aber ich werde gerne eine neue für den obigen Kommentar öffnen, sobald ich ein paar freie Minuten habe, um ein Beispiel zusammenzustellen.

@qqilihq ah ok, danke!

Bei mir genau das gleiche wie von @qqilihq erwähnt

image

Das gewünschte Verhalten könnte also mit diesem Code erreicht werden:

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

Aber es sieht so aus, als würde ich versuchen, einige Felder zu verwenden, die nicht dafür vorgesehen sind.

pre Methode sollte nicht mit this.update funktionieren, weil es "pre"-save ist und es noch nichts zu aktualisieren gibt, während this.newProp = "value" perfekt funktioniert, danke Leute.

Mein Fall ist:

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

Ich verstehe die Gründe dafür, dass die Abfrage „this“ auf dem „pre“-Update-Hook ist, aber warum verhält sich der „post“-Update-Hook auf ähnliche Weise? Sollte das Post-Update das tatsächliche Modell nicht als "this" haben?

Dann würde post find also mehrmals ausgeführt werden oder this als Array mit mehreren Ergebnissen haben? Und wenn es keine Ergebnisse gibt, sollte post find nicht ausgeführt werden?

Das ist alles sehr merkwürdig. IMHO sollten sich die Pre-Hooks alle gleich verhalten, mit einem this , das das Objekt ist, das geändert wird, und netten Methoden wie isModified, um Dinge zu überprüfen.

Eigentlich glaube ich, ich habe es jetzt verstanden. Ich sollte nur die Pre-'save'-Methode und dann die this.isNew-Methode verwenden. Danke!

@ajbraus es ist ein wenig seltsam, aber die Alternative ist ein massiver Leistungsaufwand. Angenommen, Sie haben ein UpdateMany, das Millionen von Dokumenten aktualisiert - Mungo müsste diese alle in den Speicher laden.

Wie soll man vor Updates nach Dokumentfeldern suchen? Angenommen, ich muss überprüfen, ob bestimmte Felder ausgefüllt sind, um das Dokument als "gültig" festzulegen. Wie kann ich auf das gesamte Dokument zugreifen und nicht nur auf Felder, die aktualisiert werden? this bezieht sich hier auf die Abfrage

@PDS42 Sie müssen eine separate Abfrage ausführen, um das Dokument aus Mongodb zu laden

Gibt es einen bestimmten Link in der Mongoose-Dokumentation, wo all dies ausführlicher erklärt wird? Ich würde es gerne nachlesen und das besser verstehen :).

BEARBEITEN:

Pre-Update-Hooks: https://github.com/Automattic/mongoose/issues/2812
Middleware: https://mongoosejs.com/docs/middleware.html
Abfrageobjekt: https://mongoosejs.com/docs/api.html#Query
Abfragen: https://mongoosejs.com/docs/queries.html

Ich habe Probleme, Dokumentation darüber zu finden, wie genau man die Abfragen verwenden sollte, auf die von „this“ in der Update-Middleware verwiesen wird. Was passiert zum Beispiel, wenn:
this.update({field: "value"}, {$set: {updatedAt: new Date()}}); Fügt dies dem Filter {field: "value"} hinzu? Oder hat es keine Wirkung? Gibt es eine Dokumentation, die ich nicht sehe?

@ronakvora es gibt eine Query#getConditions() Funktion .

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

Gotcha, das wird mir helfen, mit Dingen zu spielen :). Danke!

Ps das ist Ronak von oben. Anscheinend habe ich zwei Konten, die auf verschiedenen Geräten angemeldet sind.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen