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