Mongoose: لا يتم تنفيذ البرامج الوسيطة السابقة واللاحقة على findByIdAndUpdate

تم إنشاؤها على ١٥ يونيو ٢٠١٢  ·  102تعليقات  ·  مصدر: Automattic/mongoose

لأنه يتم تقديم طريقة findAndUpdate لقطع الكود التالي:

 Model.findById(_id, function (err, doc) {
      if (doc) {
          doc.field = 'value';
          doc.save(function (err) {
                 // do something;
          });
      }
 });

الى هذا:

   .findByIdAndUpdate(_id, {$set: {field: 'value'}}, function (err, doc) {
        // do something 
    });

نحتاج إلى استخدام البرامج الوسيطة السابقة واللاحقة بنفس الطريقة تمامًا. في الوقت الحالي ، لا يتم تنفيذ البرامج الوسيطة اللاحقة عندما أجعل findByIdAndUpdate.

التعليق الأكثر فائدة

مرحبا،

أعلم أن المشكلة قد تم إغلاقها ، لكني لست راضيًا تمامًا عن الإجابة. ألا يجب أن يكون هناك على الأقل برمجية وسيطة بعد الحفظ أو التحديث اللاحق لـ findOneAndUpdate والعمليات الأخرى المماثلة؟ يبدو على ما يرام منذ إرجاع المستند. غير ممكن للأدوات الوسيطة المسبقة أو Model.update ، أوافق.

من شأنه أن يحسن بشكل كبير من قدرات المكونات الإضافية مثل mongoosastic الذي يتجاهل حاليًا بعض العمليات التي يجب أن يكون قادرًا على دعمها.

إذا لم يكن هناك برنامج وسيط ، فهل لدى شخص ما فكرة عن كيفية إدارة بعض عمليات التحديث اللاحقة في مكون إضافي؟

شكرا

ال 102 كومينتر

من تصمبم. هناك مستندات متضمنة لاستدعاء الخطافات.

التصحيح ، لا توجد مستندات لاستدعاء الخطافات.

لذا؟ إذا كنت أرغب في الاتصال بالبرامج الوسيطة السابقة واللاحقة ، فأنا بحاجة إلى استخدام النهج الأول؟

نعم هذا صحيح. Model.update ، findByIdAndUpdate ، findOneAndUpdate ، findOneAndRemove ، findByIdAndRemove كلها أوامر منفذة مباشرة في قاعدة البيانات.

يجب توضيح ذلك بالتأكيد في الدليل ، خاصةً إذا كنت تتحدث عن التحقق في نفس الصفحة وتصف findByIdAndUpdate بأنه "أفضل"

إذا نقرت على الرابط "أفضل" ، فسيأخذك إلى التوثيق الكامل لـ
الطريقة التي تشرح فيها التحقق وما إلى ذلك.

لا تتردد في إرسال طلب سحب لإضافة شيء تشعر أنه أفضل. انها
من السهل جدًا القيام بذلك:
https://github.com/LearnBoost/mongoose/blob/master/CONTRIBUTING.md

يوم الأربعاء ، 31 تشرين الأول (أكتوبر) 2012 الساعة 6:03 مساءً ، كتب جيسي فولتون إخطارات github.com:

يجب بالتأكيد توضيح ذلك في الدليل http://mongoosejs.com/docs/documents.html ،
خاصة إذا كنت تتحدث عن التحقق في نفس الصفحة و
وصف findByIdAndUpdate بأنه "أفضل"

-
يمكنك الرد على هذه الرسالة الإلكترونية مباشرةً أو عرضها على Gi tHubhttps: //github.com/LearnBoost/mongoose/issues/964#issuecomment -9967865.

هارون
aaronheckmann https://twitter.com/#!/aaronheckmann

تمت إضافة ملاحظات إلى مستند البرنامج الوسيط

طلب السحب: https://github.com/LearnBoost/mongoose/pull/1750

مرحبا،

أعلم أن المشكلة قد تم إغلاقها ، لكني لست راضيًا تمامًا عن الإجابة. ألا يجب أن يكون هناك على الأقل برمجية وسيطة بعد الحفظ أو التحديث اللاحق لـ findOneAndUpdate والعمليات الأخرى المماثلة؟ يبدو على ما يرام منذ إرجاع المستند. غير ممكن للأدوات الوسيطة المسبقة أو Model.update ، أوافق.

من شأنه أن يحسن بشكل كبير من قدرات المكونات الإضافية مثل mongoosastic الذي يتجاهل حاليًا بعض العمليات التي يجب أن يكون قادرًا على دعمها.

إذا لم يكن هناك برنامج وسيط ، فهل لدى شخص ما فكرة عن كيفية إدارة بعض عمليات التحديث اللاحقة في مكون إضافي؟

شكرا

albanm تتخطى طرق معينة النمس تمامًا ، لذا لن تحصل على أدوات وسيطة. AFAIK ، الطريقة الوحيدة لتنفيذ الخطافات هي استخدام مكالمات منفصلة find() و save() كما هو مذكور أعلاه.

أنا أفهم ذلك وهو أمر منطقي. البرامج الوسيطة المسبقة غير واردة حيث لا يوجد جلب قبل طرق معينة. ولكن مع ذلك ، يجب أن يكون النمس قادرًا على التفاف عمليات التحديث التي تقوم أيضًا بإرجاع المستندات المحدثة وتشغيل بعض رسائل البريد.

أعتقد أن albanm صحيح ، فقد يرغب الأشخاص في الحصول على الوظيفة نفسها عندما يستخدمون نفس الوظيفة. ماذا عن التفاف طرق "التحديث المباشر" هذه مع بعض الاعتراض للتحقق من وجود أي خطافات؟ في حالة وجود الخطاف ، استخدمه ، أو اتصل بطرق التحديث الأصلية بخلاف ذلك.

+1

+1

+1

+1

: +1:

+1

: -1:
لقد تلقيت طلب الميزة ، ولكن كلما طالت فترة عملي مع البرامج الوسيطة ، أجد نفسي بحاجة إلى تجاوز البرامج الوسيطة غالبًا عند استدعاء نصوص تحديث db وعناصر أخرى ليست روتينية لعملياتي العادية. باستخدام طرق ثابتة ، يصبح من السهل جدًا إنشاء غلاف مخصص يقوم بالفعل بتنفيذ طلبات الميزات المذكورة أعلاه. نظرًا لأن النمس يفتح الآن على أفكار للإصدار 4 ، فمن غير المرجح أن يحدث أي تغيير في واجهة برمجة التطبيقات بهذا الحجم في الإصدار 3. أطلب أن يتم نقل هذا إلى مناقشة v4.

ومع ذلك ، أود أن: +1: إذا كنت قادرًا على تعطيل البرامج الوسيطة في مكالمة حفظ. غالبًا ما أجد نفسي مع كائن نمس تم تمريره من وظيفة أخرى وأريد ببساطة تنفيذ حفظ - في هذه الحالة ، يُفضل إجراء حفظ على كتابة استعلام جديد. إذا كان هذا ممكنا ، يرجى الإشارة إليه

في كلتا الحالتين ، مكتبة مذهلة. مجد للمشرفين الرائعين. لست متأكدا كيف سأعمل بدونها.

+1

طريقة بسيطة لإضافة الخطافات لهذه الطريقة هي الكتابة فوق وظائف التضمين:

_update = UserModel.update
UserModel.update = (conditions, update) ->
  update.updatedAt or= new Date
  _update.apply this, arguments

بعد ذلك ، ستعمل كل مكالمة تحديث من النمس على إصلاح مفتاح البيانات المحدث.

يمكنك تجربة هذا النموذج من النسيان . وهو عبارة عن غلاف بسيط لنموذج النمس ، يدعم ربط ثابت / طريقة / الكتابة فوق جميع المخططات ، واستدعاء طرق rpc للاستعلام عن mongodb.

+1 ، أحتاج هذه الميزة ...

بينما أفهم نوعًا ما المنطق ، فإن عدم وجود روابط على التحديثات الذرية IMHO يجعل النمس إلى حد ما عديم الجدوى في الممارسة. عندما أستخدم التحديثات الذرية ، لا يتم تنفيذ أي عمليات تحقق من الصحة أو الإعدادات الافتراضية أو ما إلى ذلك ، وبالتالي يتم إهمال الغرض الكامل من استخدام ODM. سيؤدي استخدام البحث / الحفظ إلى تنفيذ المهمة ، ولكن هل هناك أي ضمان لاستخدامه دائمًا؟

علاوة على ذلك ، عادةً ما أحاول تجنب البحث / الحفظ لأنها ليست عملية ذرية. وتعوض MongoDB افتقارها إلى دعم المعاملات من خلال توفير ميزات الاستعلام والتحديث الذري القوية. لذلك سأستخدم هذه العمليات الذرية ولكن بدون دعم البرامج الوسيطة Mongoose لن يقدم قيمة كبيرة على الأصل MongoClient .

حتى الأمثلة في http://aaronheckmann.tumblr.com/post/48943525537/mongoose-v3-part-1-versioning ستستخدم update ، وبالتالي تجاوز البرمجيات الوسيطة. يمكنني استخدام الإصدار بشكل صحيح أو البرامج الوسيطة ولكن لا يمكنني الجمع بين الاثنين؟ حقًا ، أين الهدف من الحصول عليه؟

لا أفهم تمامًا الأسباب الفنية: إذا كان update & co. التفاف حول عمليات قاعدة البيانات ، لماذا لا يمكننا اعتراض المكالمة وتمرير كائنات الاستعلام حتى نتمكن من إجراء بعض التحقق من الصحة / التخصيص قبل أن نقوم بالتحديث بالفعل؟

joerx +1 سيكون كافيا .. :) لكن تفكيرك لا تشوبه شائبة.

يحتوي الفرع 3.9.x على دعم للخطافات السابقة واللاحقة لـ find و findOne - يجب أن يكون من السهل إضافة دعم لـ findOneAndUpdate و update .

هل تم دمج هذه الميزة؟

لذا فإن الخطافات pre('findOneAndUpdate') و post('findOneAndUpdate') في وضع رئيسي ، لا يوجد رابط تحديث حتى الآن. لا يوجد إصدار يحتوي على أيٍّ منهما حتى الآن.

فهل يتم تشغيل الحفظ المسبق بعد التحديث () الآن؟

لا ، هناك ربط منفصل update() لـ Query.update() . تختلف أدوات الحفظ عن خطاطيف الاستعلام.

@ vkarpov15 هل يمكنك ربط المستندات الداعمة لخطاف التحديث؟ أي كلمة عند pre('findOneAndUpdate') و post('findOneAndUpdate') إلى إصدار؟

@ Karlstanton استخدم 4.0.0-rc2 ، npm install mongoose@unstable :)

'use strict';

var Promise  = require('bluebird');
var mongoose = Promise.promisifyAll(require('mongoose'));

var counterSchema = new mongoose.Schema({
    total: {
        type:    Number,
        default: 0
    }
});

counterSchema.post('findOneAndUpdate', function (doc) {
    console.log(doc.total);
});

var Counter = mongoose.model('Counter', counterSchema);

Promise.coroutine(function *() {
    yield mongoose.connectAsync(process.env.MONGODB_URI);
    console.log('Connected');
    let counter = yield Counter.createAsync({});
    console.log(`${counter.total}`);
    for (let i = 0; i < 10; i++) {
        yield Counter.findOneAndUpdateAsync({ _id: counter.id }, { $inc: { total: 1} });
    }
})();
0
0
1
2
3
4
5
6
7
8
9

يبدو أنها خطوة إلى الخلف. ماذا ينقصني؟

آه ، التغيير الفاصل الافتراضي لعلامة new . بينما هناك ما يمكن قوله حول الاتساق مع المحرك الأساسي ، يجب أن أقول إنه أمر غير بديهي حقًا ، خاصة عند التفكير في هذا الخطاف الجديد.

MustafaHosny اللهم امين :)

نعم neverfox التقاط جيد. تم توثيق هذا التغيير في ملاحظات الإصدار ويمكنك أن ترى المزيد من النقاش حول سبب تغيير ذلك في # 2262.

والسبب هو أن new خطأ افتراضيًا في برنامج تشغيل عقدة mongodb ، و mongodb shell ، وخادم mongodb الفعلي ، وما إلى ذلك ، كما أن إعداد طبقات الغلاف الافتراضي غير القياسي يجعل الحياة صعبة على المطورين. المثال الأساسي الخاص بي للوحدة التي تفهم هذا الخطأ الفادح هو gulp-uglify ، والذي يتجاوز مجموعة من الإعدادات الافتراضية لـ uglify-js.

أرى أن المشكلة قد تم إغلاقها ، ولكن هل الوظيفة موجودة في الإصدار 4.0.2 ، هل ما زالت موجودة في الإصدار غير المستقر؟ لا يبدو أنه يتم تنفيذه في findOneAndUpdate مع Scema.pre ('تحديث') أو Schema.pre ('findOneAndUpdate') مع الإصدار 4.0.2. هل أفتقد شيئًا يجب عليّ تمريره إلى الوظيفة؟

كيف يتم التصريح عن الخطاف المسبق CaptainStaplerz ؟

هل أداة findOneAndUpdate الوسيطة متوفرة في 4.0.2؟ لقد قمت بالترقية من 3.8 إلى الإصدار 4.0.2 الأحدث من mongoose لاستخدام هذا البرنامج الوسيط Document.schema.post ('findOneAndUpdate' ، الوظيفة (doc) لا يتم تشغيلها مثل save () أو إزالة ()

honitus أرني الرمز الخاص بك

@ vkarpov15 - شكرًا للاستجابة السريعة ، تفضل

blog.controller ، js

// Updates an existing Blog in the DB, adds comment
exports.update = function(req, res) {

Blog.findOneAndUpdate(
     {"_id": req.body._id,},
      {"$push": {"comments": buildComment}},
     {safe: true, upsert: true}, function (err, workspace) {
      if (err) {
         return handleError(res, err);
       }
       return res.send(200);
      }
   );

}

blog.socket.js

/**
 * Broadcast updates to client when the model changes
 */

'use strict';

var Blog= require('./blog.model');

exports.register = function(socket) {
//SAVE WORKS
  Blog.schema.post('save', function (doc) {
    onSave(socket, doc);
  });

// IS NOT TRIGGERED :(
 Blog.schema.post('findOneAndUpdate', function (doc) {
    onComment(socket, doc);
  });

  Blog.schema.post('remove', function (doc) {
    onRemove(socket, doc);
  });
}

//SAVE WORKS when a new blog is created
function onSave(socket, doc, cb) {
  socket.emit('blog:save', doc);
}

// IS NOT TRIGGERED :(
function onComment(socket, doc, cb) {
  socket.emit('blog:findOneAndUpdate', doc);
}

function onRemove(socket, doc, cb) {
  socket.emit('blog:remove', doc);
}

@ vkarpov15 شكرا لإعادة الافتتاح. هل هذا يعني أن ربط findOneAndUpdate ليس موجودًا في 4.0.2 وأنت تخطط لتضمينه في 4.0.3.

@ vkarpov15 هذا هو الكود الذي أعلن فيه الخطافات:

...
var TodoSchema = new mongoose.Schema({
  name: {type: String, required: true},
  note: String,
  completed: {type: Boolean, default: false},
  updatedAt: {type: Date, default: Date.now},
  user: {
    type: mongoose.Schema.ObjectId,
    ref: 'Users'
  }
});
...
// Not executed
TodoSchema.pre('update', function() {
  console.log('------------->>>>>> update updatedAt')
  this.updatedAt = Date.now();
});
// Not executed
TodoSchema.pre('findOneAndUpdate', function() {
  console.log('------------->>>>>> update updatedAt')
  this.updatedAt = Date.now();
});

وهنا حيث أتصل بالتحديث:

...
router.route('/:id')
.put(function(req, res, next) {
  TodoModel.findOneAndUpdate({_id: req.params.id, user: req.user.id}, req.body, {new: true}, function(err, post) {
    if(err) return next(err);
    if(post) {
      res.status(200).json(post);
    }
    else {
      next(newSystemError(errorCodes.TODO_NOT_FOUND, undefined, req.params.id));
    }
  });
});

بقدر ما أعرف ، يجب أن يكون الخطاف findOneAndUpdate() هناك ، إذا لم يكن يعمل فهذا خطأ

CaptainStaplerz حاول:

TodoSchema.pre('update', function(next) {
  console.log('------------->>>>>> update updatedAt')
  this.updatedAt = Date.now();
  next();
});

أيضًا ، هل يتم تنفيذ عبارة console.log أم أن جزء Date.now () فقط هو الذي يعطي نتائج غير متوقعة؟

هههههههههههههه
لقد قمت بتغيير كود المصدر الخاص بي ، بإضافة التغييرات التي أجريتها في إضافة التنفيذ # 964 https://github.com/Automattic/mongoose/commit/e98ef98e857965c4b2ae3339fdd7eefd2a5a9913

يعمل مثل السحر الآن. لذلك أعتقد أن الإصلاح لم يتم تحديده في main

honitus هل أنت متأكد من أنك تستخدم mongoose 4.0.2؟ هذا التغيير في الواقع ملتزم بـ 4.0.0 وما فوق.

honitus $ npm عرض الإصدار النمس
4.0.2

honitus ما تفعله بشكل غير صحيح هو أنك تضيف خطافات إلى المخطط بعد تجميع النموذج الخاص بك. من غير المتوقع أن يعمل Model.schema.pre('remove'); ، راجع "تجميع نموذجك الأول" في مستندات النموذج . قم بإرفاق الخطافات بالمخطط أولاً ثم يجب أن تعمل الأشياء - هذا هو الاختلاف الوحيد الذي أراه بين الكود الخاص بك واختباراتنا.

CaptainStaplerz الطريقة الوحيدة التي يمكنني إيجادها لإعادة إصدار التعليمات البرمجية الخاصة بك هي تحديث فارغ. يعمل الكود أدناه

var mongoose = require('mongoose');
mongoose.set('debug', true);
var util = require('util');

mongoose.connect('mongodb://localhost:27017/gh964');

var TodoSchema = new mongoose.Schema({
  name: {type: String, required: true},
  note: String,
  completed: {type: Boolean, default: false},
  updatedAt: {type: Date, default: Date.now},
  user: {
    type: mongoose.Schema.ObjectId,
    ref: 'Users'
  }
});
TodoSchema.pre('update', function() {
  console.log('------------->>>>>> update updatedAt')
  this.updatedAt = Date.now();
});
TodoSchema.pre('findOneAndUpdate', function() {
  console.log('------------->>>>>> update updatedAt')
  this.updatedAt = Date.now();
});

var Todo = mongoose.model('Todo', TodoSchema);

Todo.update({}, { note: "1" }, function(err) {
  if (err) {
    console.log(err);
  }
  console.log('Done');
  process.exit(0);
});

ولكن بمجرد أن تجعل التحديث فارغًا:

var mongoose = require('mongoose');
mongoose.set('debug', true);
var util = require('util');

mongoose.connect('mongodb://localhost:27017/gh964');

var TodoSchema = new mongoose.Schema({
  name: {type: String, required: true},
  note: String,
  completed: {type: Boolean, default: false},
  updatedAt: {type: Date, default: Date.now},
  user: {
    type: mongoose.Schema.ObjectId,
    ref: 'Users'
  }
});
TodoSchema.pre('update', function() {
  console.log('------------->>>>>> update updatedAt')
  this.updatedAt = Date.now();
});
TodoSchema.pre('findOneAndUpdate', function() {
  console.log('------------->>>>>> update updatedAt')
  this.updatedAt = Date.now();
});

var Todo = mongoose.model('Todo', TodoSchema);

Todo.update({}, { }, function(err) {
  if (err) {
    console.log(err);
  }
  console.log('Done');
  process.exit(0);
});

لم يعد يتم تنفيذ ربط التحديث المسبق. هل هذا يتفق مع ما تراه؟

هههههههههههههه
Model.schema.pre ("إزالة") ؛ هو أحد الأجزاء الأساسية التي يتم إنشاؤها تلقائيًا بواسطة المكدس الكامل الزاوي
أضفت التنفيذ # 964 e98ef98
كل ما فعلته هو إضافة سطر واحد أدناه
this._schema.s_.hooks.wrap ('findOneAndUpdate'، Query.base.findOneAndUpdate،
انظر السطر 1526 في 4.0.2 - this.model.hooks.wrap ('findOneAndUpdate'، Query.base.findOneAndUpdate،

استبدل السطر 1526 بـ- this.schema.s.hooks.wrap ('findOneAndUpdate'، Query.base.findOneAndUpdate،
ويعمل.

هذا حسب التصميم. ليس من المفترض أن يسمح Mongoose بتعديل خطافات المخطط بعد تجميع النموذج (أي ، استدعاء mongoose.model() ) ، وهذا هو السبب في أن ذلك لم يعد يعمل.

@ vkarpov15 تمكنت من تشغيل الخطاف الآن مع ما يلي ، لا بد أنه كان خطأ مطبعيًا غبيًا أو أي شيء آخر:

TodoSchema.pre('findOneAndUpdate', function(next) {
  console.log('------------->>>>>> update updatedAt: ', this.updatedAt);
  this.updatedAt = Date.now();
  next();
});

ومع ذلك ، لا يبدو أن كلمة "هذا" تشير إلى النموذج المحدث (لكنها تفعل ذلك في خطاف "الحفظ"؟) ، وبالتالي فإن هذا.

كيف يمكنني تحديث الحقل "updatedAt" في الخطاف "findOneAndUpdate"؟

يجب أن أضيف المزيد من المستندات لتوضيح ذلك - قد لا يكون المستند الذي يتم تحديثه موجودًا في الذاكرة عند استدعاء findOneAndUpdate ، لذلك يشير الكائن this إلى الاستعلام بدلاً من المستند في البرنامج الوسيط للاستعلام . جرب this.update({ $set: { updatedAt: Date.now() } });

@ vkarpov15 لم أتمكن من الحصول على this.update({ $set: { updatedAt: Date.now() } }); للعمل ، لكنني تمكنت من تحديث المستند بنجاح على الخطاف findOneAndUpdate بهذا: this._update['$setOnInsert'].updatedAt=Date.now(); .

أوصي بشدة بعدم الاعتماد على التغيير والتبديل في الحالة الداخلية من هذا القبيل. أنا مندهش جدًا من أنه لا يمكنك الحصول على this.update() للعمل - هل يمكنك أن تريني كيف يبدو خطافك؟

بالتأكيد! (لاحظ أنني أستخدم 4.0.2)

tagSchema.pre('findOneAndUpdate',function(next){
  var self = this;

  //NOTE THAT 'this' in the findOneAndUpdate hook refers to the query, not the document
  //https://github.com/Automattic/mongoose/issues/964

  geoData.country.findOne({'_id':self._update['$setOnInsert'].countryCode}).select('_id name cca2 cca3 ccn3').lean().exec(function(err,country){
    if (err){throw err;}
    if (!country){throw 'no coutnry';}
    self._update['$setOnInsert'].country=country;
    next();
  });
});

من الواضح أنه يمكنني التعامل مع هذا عندما أقوم بتهيئة المستند في مكان آخر في تطبيقي ، ولكن من الجيد أن يكون كل شيء موجودًا هناك في Mongoose. نرحب بأي أفكار!

نعم لقد كنت في حيرة من أمري واعتقدت أنك تستخدم update . النص أدناه

var mongoose = require('mongoose');
mongoose.set('debug', true);
var util = require('util');

mongoose.connect('mongodb://localhost:27017/gh964');

var TodoSchema = new mongoose.Schema({
  name: {type: String, required: true},
  note: String,
  completed: {type: Boolean, default: false},
  updatedAt: {type: Date, default: Date.now},
  user: {
    type: mongoose.Schema.ObjectId,
    ref: 'Users'
  }
});
TodoSchema.pre('findOneAndUpdate', function() {
  this.findOneAndUpdate({}, { updatedAt: Date.now() });
});

var Todo = mongoose.model('Todo', TodoSchema);

Todo.findOneAndUpdate({}, { note: "1" }, function(err) {
  if (err) {
    console.log(err);
  }
  console.log('Done');
  process.exit(0);
});

يعمل بشكل صحيح وينفذ الاستعلام المطلوب:

Mongoose: todos.findAndModify({}) [] { '$set': { note: '1', updatedAt: new Date("Thu, 07 May 2015 20:36:39 GMT") } } { new: false, upsert: false }
Done

لذا يرجى استخدام

TodoSchema.pre('findOneAndUpdate', function() {
  this.findOneAndUpdate({}, { updatedAt: Date.now() });
});

بدلاً من التلاعب يدويًا بالحالة الداخلية لـ mquery - عادة ما تكون هذه فكرة سيئة إلا إذا كنت تعرف حقًا ما تفعله.

هل هذا يعمل أم لا؟ الخطاف الوحيد الذي يناسبني هو "حفظ" ، ويتم تجاهل الباقي تمامًا. أنا أعمل على 4.0.6. شكرا

agjs تقديم مثال على رمز من فضلك.

UserController.prototype.updateAvatar = function (req, res) {
    return new Promise(function (resolve, reject) {
        CompanyDetails.update({
            _author: req.user._id
        }, {
            avatarPath: req.files.file
        }, function (error, updated) {
            if (error) {
                reject(error);
            } else {
                resolve(updated);
            }
        });
    }).then(function (resolved) {
        res.sendStatus(204).send(updated);
    }).catch(function (error) {
        next(error);
    })

};

CompanyAvatarSchema.pre('update', function (next) {
    console.log('pre save');
    let VirtualModel = this,
        parent = this.ownerDocument(),
        PATH = path.normalize('./public/images/uploads/avatars/' + parent._id);

    mkdirp(PATH, function (error) {
        if (error) {
            next(error);
        } else {
            fs.rename(VirtualModel.path, path.join(PATH, VirtualModel.name), function (error2) {
                if (error2) {
                    next(error2);
                } else {
                    next();
                }
            });
        }
    });

});

لدي خطاف مسبق آخر على طراز آخر مع model.create وحفظه مسبقًا وهو يعمل بشكل طبيعي.

مع التحديث على الإطلاق ، فإنه ببساطة لا يرى الخطاف ، ولا حتى وحدة التحكم. حاولت العثور على OneAndUpdate أيضًا وما إلى ذلك ، لا يعمل حقًا ... شيء مضحك لقد مررت بهذا الموضوع بالكامل وكما أفعل عادةً ، فحصت الوثائق الرسمية وتدعي يا رفاق أنها تعمل.

ما العلاقة بين CompanyAvatarSchema و CompanyDetails في الكود أعلاه؟

تفاصيل الشركة لديها CompanyAvatar.schema كمستند ثانوي

avatarPath: {
        type: [CompanyAvatar.schema],
        required: true
    }

أيضًا ، لا يقتصر الأمر على الخطاف المسبق فحسب ، بل يتم تجاهل التحقق تمامًا أيضًا. يتم نشر هذا المستند الثانوي ولكنه يتجاهل كلاً من التحقق من الصحة والربط المسبق. لقد بحثت في كل شيء على Google ، وجربت هذا أيضًا ولكن لا شيء يعمل. عندما أقوم فقط للاختبار بتغيير استعلامي لإنشاء نموذج واستدعاءه باستخدام جديد ويعرف أيضًا باسم var parent = new Parent () ، فإنه يعمل.

أنت تطلب CompanyDetails.update() لكن الخطاف المسبق محدد في مخطط منفصل. البرمجيات الوسيطة للاستعلام لا تطلق المخطط المتداخل pre('update') atm.

يُرجى أيضًا تقديم مثال أكثر شمولاً للرمز الخاص بحالة "يتم تجاهل عملية التحقق تمامًا" أيضًا.

إليك مخطط الصورة الرمزية لشركتي المصمم للتحقق من الصحة والخطافات المسبقة للمستخدمين الذين يقومون بتحديث ملفهم الشخصي (الصورة الرمزية):

'use strict';

let mongoose = require('mongoose'),
    mkdirp = require('mkdirp'),
    fs = require('fs'),
    path = require('path'),
    Schema = mongoose.Schema;

let CompanyAvatarSchema = new Schema({
    name: String,
    width: Number,
    height: Number,
    size: Number,
    type: String
});


CompanyAvatarSchema.path('type').validate(function (type) {
    return /^image\//.test(type);
}, 'Image type not allowed!');

CompanyAvatarSchema.path('size').validate(function (size) {
    return size < 5;
}, 'Image too big!');



CompanyAvatarSchema.virtual('path').set(function (path) {
    return this._path = path;
}).get(function () {
    return this._path;
});


CompanyAvatarSchema.virtual('public_path').get(function () {
    var parent = this.ownerDocument();
    var PATH = path.normalize('images/uploads/avatars/' + parent._id);
    if (this.name) {
        return path.join(PATH, this.name);
    }
});

CompanyAvatarSchema.set('toJSON', {
    getters: true
});

CompanyAvatarSchema.pre('findOneAndUpdate', function (next) {
    console.log('pre save');
    let VirtualModel = this,
        parent = this.ownerDocument(),
        PATH = path.normalize('./public/images/uploads/avatars/' + parent._id);

    mkdirp(PATH, function (error) {
        if (error) {
            next(error);
        } else {
            fs.rename(VirtualModel.path, path.join(PATH, VirtualModel.name), function (error2) {
                if (error2) {
                    next(error2);
                } else {
                    next();
                }
            });
        }
    });

});


let runValidatorsPlugin = function (schema, options) {
    schema.pre('findOneAndUpdate', function (next) {
        this.options.runValidators = true;
        next();
    });
};

CompanyAvatarSchema.plugin(runValidatorsPlugin);

let CompanyAvatar = mongoose.model('CompanyAvatar', CompanyAvatarSchema);
module.exports = CompanyAvatar;

إليك مخطط company_details حيث تعد company_avatar مستندًا ثانويًا:

let CompanyDetailsSchema = new mongoose.Schema({
    _author: [{
        type: Schema.Types.ObjectId,
        ref: 'CompanyAccount'
    }],
    company_name: {
        type: String,
        es_indexed: true,
        es_boost: 2.0
    },
    contact_email: {
        type: String,
        es_indexed: true
    },
    website: {
        type: String,
        es_indexed: true
    },
    country: {
        type: String,
        es_indexed: true
    },
    industry: {
        type: String,
        es_indexed: true
    },
    address: {
        type: String,
        es_indexed: true
    },
    about: {
        type: String,
        es_indexed: true
    },
    avatarPath: {
        type: [CompanyAvatar.schema],

    }
});

وإليك تحديث وحدة التحكم في ملف التعريف و avatarPath الذي يجب التحقق من صحته / ربطه قبل تنفيذ هذا التحديث:

UserController.prototype.updateAvatar = function (req, res, next) {
    let updates = {
        $set: {
            avatarPath: req.files.file
        }
    };
    return new Promise(function (resolve, reject) {
        CompanyDetails.findOneAndUpdate({
            _author: req.user._id
        }, updates, function (error) {
            if (error) {
                reject(error);
            } else {
                resolve('done');
            }
        });
    }).then(function () {
        res.sendStatus(204);
    }).catch(function (error) {
        next(error);
    });

};

في الأساس ، يتم ملء mongodb الخاص بي بالحقول من req.files.file ولكن بخلاف ذلك ، يتم تجاهل التحقق من الصحة ولا تعمل أي خطافات.

تكمن المشكلة في أن البرامج الوسيطة السابقة ("findOneAndUpdate") مُعرَّفة في مخطط قاعدة بيانات متداخل. في الوقت الحالي ، يقوم mongoose بإطلاق البرامج الوسيطة للاستعلام عن مخططات المستوى الأعلى فقط ، لذلك سيتم إطلاق البرامج الوسيطة المحددة على CompanyDetailsSchema لإعادة: # 3125

+1

@ vkarpov15 فهمت ذلك

TodoSchema.pre('findOneAndUpdate', function() {
  this.findOneAndUpdate({}, { updatedAt: Date.now() });
});

يعمل على تعيين خاصية إلى قيمة صلبة ، ولكن كيف يمكنني قراءة وتغيير خاصية ، مثل تجزئة كلمة المرور؟

TodoSchema.pre('findOneAndUpdate', function() {
  this.findOneAndUpdate({}, { password: hashPassword(.....?....) });
});

أي أفكار؟ هذه حالة استخدام شائعة جدًا ، أليس كذلك؟ أو هل يجد الأشخاص عادةً () ثم يفصلون عن الحفظ ()؟

TodoSchema.pre('findOneAndUpdate', function() {
  this.findOneAndUpdate({}, { password: hashPassword(this.getUpdate().$set.password) });
});

يجب أن يعمل هذا willemmulder.

@ vkarpov15 رائع ، شكرًا! سأحاول ذلك الليلة. يجب أن يعمل ذلك أيضًا مقابل pre('update') أليس كذلك؟

@ vkarpov15 لذا جربتها للتو

schema.pre('update', function(next) {
this.update({}, { $set : { password: bcrypt.hashSync(this.getUpdate().$set.password) } });
next();
});

وإذا كنت أحصل على console.log this

_update: { '$set': { password: '$2a$10$CjLYwXFtx0I94Ij0SImk0O32cyQwsShKnWh1248BpYsJLIHh7jb66', postalAddress: [Object], permissions: [Object], firstName: 'Willem', lastName: 'Mulder', email: '...@...', _id: 55ed4e8b6de4ff183c1f98e8 } },

وهو ما يبدو جيدًا (وقد حاولت تعيين هذه الخاصية مباشرة من قبل) ولكن في النهاية ، لا تكتب القيمة المجزأة في قاعدة البيانات ، ولكنها تكتب القيمة "الأولية" ببساطة. هل هناك أي شيء يمكنني تجربته؟

هذا غريب. يمكنك محاولة تمكين وضع تصحيح أخطاء النمس باستخدام require('mongoose').set('debug', true); ومعرفة ما هو الاستعلام الذي يتم إرساله إلى db ، والذي قد يلقي بعض الضوء.

شكرا على اقتراحك. فقط فعلت ذلك:

أنا أشغل هذا:

schema.pre('update', function(next) {
    this.update({}, { password: bcrypt.hashSync(this.getUpdate().$set.password) } );
    console.log(this.getUpdate());
    next();
});

الذي يعيد هذا لـ console.log

{ '$set':
   { password: '$2a$10$I1oXet30Cl5RUcVMxm3GEOeTFOLFmPWaQvXbr6Z5368zbfpA8nFEK',
     postalAddress: { street: '', houseNumber: '', zipCode: '', city: '', country: '' },
     permissions: [ '' ],
     __v: 0,
     lastName: '',
     firstName: '',
     email: '[email protected]',
     _id: 563b0410bd07ce2030eda26d } }

ثم هذا من أجل تصحيح النمس

Mongoose: users.update({ _id: ObjectId("563b0410bd07ce2030eda26d") }) { '$set': { password: 'test', postalAddress: { street: '', houseNumber: '', zipCode: '', city: '', country: '' }, permissions: [ '\u001b[32m\'\'\u001b[39m' ], __v: 0, lastName: '', firstName: '', email: '[email protected]', _id: ObjectId("563b0410bd07ce2030eda26d") } } { overwrite: false, strict: true, multi: false, upsert: false, safe: true }
Mongoose: users.findOne({ _id: ObjectId("563b0410bd07ce2030eda26d") }) { fields: { password: 0 } }

أي فكرة؟

لست متأكدًا ، فتحت مشكلة جديدة لتتبعها.

@ vkarpov15 شكرا ، سوف تتبع القضية الأخرى.

@ vkarpov15 أعتقد أن الطريقة الصحيحة لتعيين خيارات الاستعلام المستمر في الخطاف المسبق ستكون شيئًا مثل:

  finishSchema.pre('findOneAndUpdate', function (next) {
    this.setOptions({
      new: true,
      runValidators: true
    });
    this.update({}, {
      lastEdited: Date.now()
    });
    next();
  });

لكن الوثائق ، http://mongoosejs.com/docs/api.html#query_Query -setOptions لا تذكر أيًا من هذه الخيارات. إذا كان هذا يعتبر حلول اختراق ، فما هو الأنسب؟

هذه مشكلة في المستندات ، ويبدو أن الرمز الذي تصفه يجب أن يعمل للوهلة الأولى

هل يمكنك فتح قضية منفصلة لذلك؟

@ vkarpov15 نعم ، إنه يعمل. أعتقد أنني لم أكن واضحًا بما فيه الكفاية.

setOptions يطبق new و runValidators بشكل صحيح ، كنت فقط أسأل ما إذا كان يجب تفضيل هذه الخيارات من خلال setOptions على this.options .

يُفضل setOptions() IMO ، لكن يجب أن يعمل كلاهما. أو يمكنك أن تفعل فقط

this.update({}, { lastEdited: Date.now() }, { new: true, runValidators: true });
schema.pre('update', function(next) {
this.update({}, { $set : { password: bcrypt.hashSync(this.getUpdate().$set.password) } });
next();
});

سيؤدي هذا إلى تحديث كلمة المرور في كل مكالمة تحديث (). لذلك إذا قمت بتغيير قيمة الخصائص الأخرى ، مثل الاسم أو العمر ، فسيتم تحديث كلمة المرور أيضًا وهي غير صحيحة !؟

nlonguit أعتقد أنه سيكون. ولكن يمكنك الوصول من خلال this الحقول التي سيتم تحديثها ويمكنك القيام بشيء مثل:

if (this._fields.password) { // <- I'm sure about this one, check in debugger the properties of this 
    this.update({}, { $set : { password: bcrypt.hashSync(this.getUpdate().$set.password) } });
}

if (this._update.$set.password) { this.update({}, { $set: { password: bcrypt.hashSync(this.getUpdate().$set.password)} }); }

هذا الرمز يعمل بشكل جيد بالنسبة لي. شكرا akoskm

أتساءل عما إذا كان من الممكن إضافة خطاف مسبق لـ findByIdAndUpdate أيضًا. سيكون من الجيد أن يتوفر كلا الخطافين.

لقد فعلت ذلك بهذه الطريقة وهي تعمل: ما عليك سوى findById ثم الحفظ دون تحديث أي حقول ثم استخدم طريقة findByIdAndUpdate:

dbModel.findById(barId, function (err, bar) {
        if (bar) {

            bar.save(function (err) {
                if (err) throw err;
            });
        }
    });
    dbModel.findByIdAndUpdate(barId, {$set:req.body}, function (err, bar) {
        if (err) throw err;
        res.send('Updated');
    });`

أحاول تعيين خاصية بطول المصفوفة.

schema.post('findOneAndUpdate', function(result) {
    console.log(result.comments.length);
    this.findOneAndUpdate({}, { totalNumberOfComments: result.comments.length });
});

يتم تسجيل الطول الصحيح ، على الرغم من أن الاستعلام لا يقوم بتعيين totalNumberOfComments أبدًا ، ويظل الحقل عند 0 (نظرًا لأن مخطط قاعدة البيانات يشير افتراضيًا: 0).

عندما أضع console.log(this) في نهاية الخطاف ، أستطيع أن أرى أن query يحتوي على ما يلي:

_update: { '$push': { comments: [Object] }, totalNumberOfComments: 27 }

على الرغم من أنني عندما أقوم بتشغيل وضع التصحيح ، لا يتم تسجيل الاستعلام من قبل Mongoose.

هل هناك شيء ما أفعله خطأ ، أم أن هذا خطأ؟

zilions this.findOneAndUpdate({}, { totalNumberOfComments: result.comments.length }).exec(); بحاجة إلى تنفيذ الاستعلام فعليًا :) فقط كن حذرًا ، ستحصل على عودية لا نهائية هناك لأن خطاف حفظ المنشور الخاص بك سيؤدي إلى تنشيط وظيفة أخرى

@ vkarpov15 اههههه الحق! ثم يمكنني فقط استخدام this.update({} {....}).exec() بدلاً من ذلك :)
على الرغم من ذلك ، عند استخدام هذا ، فإنه يعين الحقل totalNumberOfComments بشكل مثالي ، على الرغم من إجراء التحديث الأصلي لـ findOneAndUpdate أيضًا.

فمثلا:

Post.findOneAndUpdate({_id: fj394hri3hfj}, {$push: {comments: myNewComment}})

سيؤدي إلى تشغيل الخطاف التالي:

schema.post('findOneAndUpdate', function(result) {
    this.update({}, {
        totalNumberOfComments: result.comments.length
    }).exec();
}));

على الرغم من أن الخطاف سيرتفع من $push إلى comments و myNewComment مرة أخرى ، مما يجعل الإدخال مكررًا.

لأنك تنفذ نفس الاستعلام تقنيًا -

schema.post('findOneAndUpdate', function(result) {
    this.update({}, {
        totalNumberOfComments: result.comments.length
    }).exec();
}));

هو في الأساس نفس

var query = Post.findOneAndUpdate({_id: fj394hri3hfj}, {$push: {comments: myNewComment}});
query.update({}, {
        totalNumberOfComments: result.comments.length
    }).exec();
query.findOneAndUpdate().exec();

إذا كنت تريد إنشاء استعلام جديد من البداية ، فما عليك سوى القيام بذلك

schema.post('findOneAndUpdate', function(result) {
    this.model.update({}, { // <--- `this.model` gives you access to the `Post` model
        totalNumberOfComments: result.comments.length
    }).exec();
}));

فقط لا تلمس خطاف الحفظ المسبق ،

router.put('/:id', jsonParser, function(req, res, next) {

  currentCollection.findByIdAndUpdate(req.params.id, req.body, function (err, item) {
    if (err) {
        res.status(404);
        return res.json({'error': 'Server Error', 'trace': err});
    }
    item.save(); // <=== this is were you save your data again which triggers the pre hook :)
    res.status(200); 
    return res.json({'message': 'Saved successfully'});
  });
});

لقد اكتشفت أن الترتيب الذي من خلاله تحدد نموذجًا وتحدد خطافًا pre مهم. اسمح لي أن أوضح:

لا يعمل:

// Create Model
let model = Database.Connection.model(`UserModel`, this._schema, `users`);

// Attach Pre Hook
this._schema.pre(`findOneAndUpdate`, function(next) {
    console.log('pre update');
    return next();
});

تعمل:

// Attach Pre Hook
this._schema.pre(`findOneAndUpdate`, function(next) {
    console.log('pre update');
    return next();
});

// Create Model
let model = Database.Connection.model(`UserModel`, this._schema, `users`);

أتمنى أن يساعد هذا أي شخص!

لقد اكتشفت للتو نفس الشيء مثل @ nicky-lenaers.

إنه يعمل بشكل جيد مع 'safe' . 'delete' . إلخ إذا قمت بتعريف الخطافات بعد تعريف النموذج.

هل هناك حل بديل هل تحدد الخطاف 'findOneAndUpdate' بعد تعريف النموذج؟

@ albert-92 لا ليس حاليا

لأي شخص يحاول الحصول على شيء اعتاد أن يكون

SCHEMA.pre('validate', function(done) {
    // and here use something like 
    this.yourNestedElement 
    // to change a value or maybe create a hashed character    
    done();
});

يجب أن يعمل هذا

SCHEMA.pre('findOneAndUpdate', function(done){
    this._update.yourNestedElement
    done();
});

لا يمكنني الحصول على خطافات النشر لتحديث المستند في المجموعة.

`module.exports = function (mongoose) {
var mySchema = mongoose.Schema ({
المعرّف: {type: Number، index: {unique: true}}،
الحقل 1: {type: String}،
الحقل 2: {type: String}
} ، {
المجموعة: "mySchema"،
versionKey: خطأ
}) ؛

mySchema.post('findOneAndUpdate', function (result) {
    this.model.update({}, {
        field2: 'New Value'
    }).exec();
});
return mySchema;

} `

mySchema.findOneAndUpdate({id: 1}, {field1: 'test'}, {new: true});

يعين الحقل في المجموعة إلى {id: 1، field1: 'test') ولكن يجب أن يكون {id: 1، field1: 'test'، field2: 'New Value'}
لست متأكدا مما أفعله خطأ

يمكنني تغيير نتيجة findOneAndUpdate من خلال القيام بذلك
mySchema.post('findOneAndUpdate', function (result) { result.field2 = 'something' });

أعتقد أنك ربما تحاول تحديث النموذج بعنصر موجود بالفعل في النموذج. أو من المحتمل أنك قمت بتحديده بشكل خاطئ. حاول طباعة "هذا" في mySchema.post الخاص بك. كما يبدو أنك لم تقم بعمل () أو التالي () في مشاركتك. لست على دراية كبيرة بالموضوع ولكني أعلم أن طباعة هذا على الأقل سيعطيك فكرة عما تتعامل معه.

أليس الهدف من التحديث هو تغيير مستند موجود في نموذجك؟

هذا هو كائن الاستعلام

لا تحتاج إلى القيام به أو بعد ذلك في خطافات النشر بقدر ما أفهم.

حسنًا ، لديك this.model.update وهو المخطط وليس نموذج الكائن. أعتقد .. مما يعني أنه سيتعين عليك استخدام

mySchema.post('findOneAndUpdate', function (result) {
    this.model.update({}, {
        $set: { field2: 'New Value'}
    }).exec();
});
return mySchema;

يبدو هذا عكسيًا قليلاً لاستدعاء وظيفة نموذجية داخل النموذج. نظرًا لأنه يمكنك فقط استخدام أجزاء من كائن "هذا" المعطى لك. قد يكون من الأفضل لك استخدام findOneAndUpdate بدلاً من استدعائه ثم استدعاء دالة نموذج أخرى فوقه.
في المدير

var data = yourNewData;
self.findOneAndUpdate({id: something._id}, data, {safe: false, new: true})
    .exec()
    .then(resolve)
    .catch(reject);

في المثال أعلاه ، استخدمت this ._update لأن هذا كان كائن التحديث الذي يجب استخدامه للخروج من هذا.

لقد حاولت استخدام $ set. ما زال لا يغير المستند الخاص بي في المجموعة.

أين يمكنني أن أجد جميع الخطافات المسبقة واللاحقة المتوفرة؟

مستندات البرامج الوسيطة http://mongoosejs.com/docs/middleware.html

+1

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات