<p>mongoose 4.0.1:pre "update"ミドルウェアこのオブジェクトはモデルオブジェクトを返しません</p>

作成日 2015年03月30日  ·  40コメント  ·  ソース: Automattic/mongoose

こんにちは、マングースの最新バージョンに問題があります。 ExpressとMongoose4.0.1を使用してAPIを作成していますが、何か問題があるかどうかはわかりませんが、新しいpre更新ミドルウェアを同じように使用しようとすると必ず発生します。 preセーブミドルウェアを使用します。 thisオブジェクトは、更新中のオブジェクトを返しません。代わりに、 Queryオブジェクトを返します。

私が説明しようとしていることの例:

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

これは、ミドルウェア内のthis参照オブジェクトで取得するものであり、それがどのように役立つかわかりません。

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

ソースコードを見ると、そのプロパティのいくつかは、 ./node_modules/mongoose/lib/query.jsで定義されているQuery関数内で定義されています。

これは予期しないことですか、それとも私は何か間違ったことをしていますか? オブジェクトの保存についてミドルウェア内で検証し、更新時にコントローラーで直接検証を実行するように強制されるという考えは好きではないため、解決策があると興味深いでしょう。

findOneAndUpdate preミドルウェアでも同じことが起こりますが、それが見つかる前に演習が返されるとは思いません。 実際、私の正直な意見では、findAndUpdateとfindOneAndUpdate preミドルウェアが、独自のミドルウェアを使用する代わりに、find(One)をトリガーし、ミドルウェアをこの順序で更新することは興味深いと思います。

よろしくお願いします。

won't fix

最も参考になるコメント

ああ、 update() findOneAndUpdate()を使用しているようです。 これらは別個のフックを持つ2つの別個の関数であるため、事前フックのfindOneAndUpdateに更新操作を追加する場合は、次のようにする必要があります。

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

全てのコメント40件

設計上、更新されるドキュメントはサーバーのメモリにない場合もあります。 これを行うには、mongooseはupdate()を実行する前に、findOne()を実行してドキュメントをロードする必要がありますが、これは受け入れられません。

設計では、フィルターを追加または削除したり、パラメーターやオプションを更新したりして、クエリオブジェクトを操作できるようにします。たとえば、find()およびfindOne()を使用して.populate()を自動的に呼び出しmulti: trueを設定します。特定のモデル、アクセス制御、およびその他の可能性のデフォルトのオプション。

findOneAndUpdate()は少し誤称であり、基になるmongodb findAndModifyコマンドを使用します。これは、 findOne() + update()と同じではありません。 別の操作として、独自のミドルウェアが必要です。

その後、「pre」更新ミドルウェア中にドキュメントフィールドを更新することをどのように提案しますか?

this.update({ field: val });は、更新操作が発生する前に{ $set: { field: val } }を追加します。

返信ありがとうございます。どのバージョンで動作するはずですか? 4.0.2を使用していますが、使用していません。 クエリで「exec」を呼び出す必要がありますか?

this.updateを実行すると、このメソッドhttp://mongoosejs.com/docs/api.html#query_Query -updateを呼び出さないでしょうか?

最終的にthis.findOneAndUpdate({matcher: "myvalue"});を実行しました。 それが正しいアプローチであることを願っています。

うーん4.0.2が動作するはずです。 あなたのコードはどのように見えますか?

私はちょうどこのようなことをしました:

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

ああ、 update() findOneAndUpdate()を使用しているようです。 これらは別個のフックを持つ2つの別個の関数であるため、事前フックのfindOneAndUpdateに更新操作を追加する場合は、次のようにする必要があります。

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

男のマングースのドキュメントはひどく悪いです。 誰かがこれをそこに入れてもらえますか?

@askdesignersは正確に何をそこに入れますか?

申し訳ありませんが、ストレスの瞬間にそれを書きました:)

バージョン互換性の脆弱性だけです。 これは明らかな問題ではなく、多くの人が頭を悩ませています。

心配ない。 どのようなバージョンの互換性の問題がありますか?

そして、pre.updateで値をサニタイズしたい場合はどうなりますか...それを行う方法を見つけることができないようです。
ありがとう!

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

いいですね、やってみます!
ありがとう!!

ねえ@ vkarpov15 、私は私が言うだろう何かが予期しない振る舞いであることに出くわしました、あるいは少なくともそれは文書化されるべきです。

私が何かをするなら

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

そして私のモデルにあなたの提案を実装します

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

元の更新クエリはフックの後に実行されるため、ログには「__3」が出力されます。 他のスキーマプロパティを変更することはできますが、元のクエリで更新されたすべてのフィールドは、preフックで変更されても同じままです。

@CMatiasこれは、 $setの使用に注意する必要がある場合、または一貫して使用しない場合です。 マングースは必ずしも$setでラップするかどうかを知らないので、あなたの場合はthis.findOneAndUpdate({}, { v: sanitize(v) });を実行する必要があります

@ vkarpov15ありがとう、それはうまくいきました。 $setを使用する場合と使用しない場合の違いを簡単に説明してください。 それについて多くの情報を見つけることができません。

したがって、ネイティブのmongodbドライバーを直接使用してcoll.findOneAndUpdate({ a: 1 }, { __v: 3 })を実行すると、mongodbはa = 1の最初のドキュメントを取得し、それを{ __v: 3 }モジュロ_idのドキュメントに置き換えます。 つまり、既存のドキュメントを上書きします。 キー '__v'を設定するには、 coll.findOneAndUpdate({ a: 1 }, { $set: { __v: 3 } })を実行する必要があります。

この動作は常に物議を醸し、エラーが発生しやすいため、デフォルトではマングースはドキュメントの上書きを防ぎます。 つまり、$ overwrite: trueオプションを設定しない限り、 MyModel.findOneAndUpdate({ a: 1 }, { __v: 3 })MyModel.collection.findOneAndUpdate({ a: 1 }, { $set: { __v: 3 } })になります。 ただし、 MyModel.update({}, { $set: { b: 2 } }).findOneAndUpdate({ a: 1 }, { __v: 3 }, { overwrite: true })のような奇妙なことを行うと、 getUpdate()の現在の状態が非常に混乱する可能性があるため、 updateのステータスはそのままにしておきます。ミドルウェア用。

私はこれらすべてを理解しようとしていますが、これらのミドルウェアメソッドを使用して検索から返されるドキュメントを変更できるかどうかを判断するのに苦労しています。 具体的には、フィールドを追加して応答を「装飾」したいと思います。

たとえば、スキーマがある場合:

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

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

これは可能ですか?

newfieldがスキーマで定義されていない理由は、 Thingの値に応じて、異なるフィールド名が必要になるためです。

ありがとう!

schema.pre( 'update')を使用するには特定のプラグインが必要ですか?

いいえ、@ jrogatis

@ vkarpov15

あなたが上で与えた例を見て...:

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

これは私にはうまくいきませ。 例が示すように、 this.getUpdate().valueには私のプロパティvalueが含まれていません。 代わりに、 this.getUpdate().$set.propertyで利用できます。

そんなはずなの? 私は何か間違ったことを理解していますか? APIは変更されましたか? これに関するドキュメントはありますか?

@qqilihq再現スクリプトで別の問題を開くことができますか?

@qqilihq nvm、あなたがやったのを見た:P

@varunjayaraman実際、私が開いた別の問題は別の問題です:)しかし、例をコンパイルするための余裕ができたら、上記のコメントのために新しい問題を喜んで開きます。

@qqilihqああ、ありがとう!

@qqilihqが述べたのとまったく同じです

image

したがって、このコードで目的の動作を実現できます。

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

しかし、これを目的としていないフィールドを使用しようとしているようです。

preメソッドはthis.updateでは機能しないはずです。これは「事前」保存であり、まだ更新するものがないためですが、 this.newProp = "value"は完全に機能します。ありがとうございます。

私の場合は:

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

「pre」更新フックで「this」としてクエリを実行する理由は理解できますが、「post」更新フックが同じように動作するのはなぜですか。 更新後、実際のモデルを「これ」にするべきではありませんか?

では、post findは複数回実行されるのでしょうか、それとも複数の結果の配列としてthisを持つのでしょうか? また、結果がない場合は、post findを実行しないでください。

これはすべて非常に奇妙です。 IMHOプリフックはすべて同じように動作する必要がありますthisは変更されるオブジェクトであり、isModifiedのような優れたメソッドでチェックします。

実は今はわかったと思います。 pre'save 'メソッドを使用してから、this.isNewメソッドを使用する必要があります。 ありがとう!

@ajbraus少し奇妙ですが、代わりにパフォーマンスのオーバーヘッドが大きくなります。 何百万ものドキュメントを更新するupdateManyがあるとします-mongooseはそれらすべてをメモリにロードする必要があります。

更新前にドキュメントフィールドをどのようにチェックする必要がありますか? ドキュメントを「有効」に設定するために特定のフィールドが入力されているかどうかを確認する必要があるとしましょう。更新されているフィールドだけでなく、ドキュメント全体にアクセスするにはどうすればよいですか? thisはここでクエリを参照しています

@ PDS42 mongodbからドキュメントをロードするには、別のクエリを実行する必要があります

マングースのドキュメントに、これらすべてがより詳細に説明されている特定のリンクがありますか? 私はそれを読んで、これをもっと完全に理解したいと思います:)。

編集:

更新前のフック: https ://github.com/Automattic/mongoose/issues/2812
ミドルウェア: https ://mongoosejs.com/docs/middleware.html
クエリオブジェクト: https ://mongoosejs.com/docs/api.html#Query
クエリ: https ://mongoosejs.com/docs/queries.html

アップデートミドルウェアで「this」によって参照されるクエリを正確に使用する方法に関するドキュメントを見つけるのに問題があります。 たとえば、次の場合に何が起こりますか。
this.update({field: "value"}, {$set: {updatedAt: new Date()}});これにより、フィルターに{field: "value"}が追加されますか? それとも効果がありませんか? 表示されていないドキュメントはありますか?

@ronakvora Query#getConditions()関数があります。

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

お奨め、それは私が物事で遊ぶのに役立ちます:)。 ありがとう!

追伸これは上から見たロナックです。 どうやら私は異なるデバイスにログインしている2つのアカウントを持っています。

このページは役に立ちましたか?
0 / 5 - 0 評価