Mongoose: ميزة جديدة: عدم التزامن الافتراضي

تم إنشاؤها على ٢٧ أكتوبر ٢٠١٧  ·  42تعليقات  ·  مصدر: Automattic/mongoose

ميزة جديدة virtual async ، plz support!

const User = new Schema(
  {
    username: {
      type: String,
      index: true,
      unique: true
    },
    encryptedPassword: {
      type: String,
      required: true,
      minlength: 64,
      maxlength: 64
    },
    passwordSalt: {
      type: String,
      required: true,
      minlength: 32,
      maxlength: 32
    }
})

User.virtual('password').set(async function generate(v) {
  this.passwordSalt = await encryptor.salt()
  this.encryptedPassword = await encryptor.hash(v, this.passwordSalt)
})
  const admin = new User({
    username: 'admin',
    password: 'admin'
  })
  admin.save()

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

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

ال 42 كومينتر

حاليًا ، أستخدم طريقة قذرة:

User.virtual('password').set(function(v) {
  this.encryptedPassword = v
})

User.pre('validate', function preValidate(next) {
  return this.encryptPassword().then(next)
})

User.method('encryptPassword', async function encryptPassword() {
  this.passwordSalt = await encryptor.salt()
  this.encryptedPassword = await encryptor.hash(
    this.encryptedPassword,
    this.passwordSalt
  )
})

+1

+1

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

المشكلة .. كيف تبدو صيغة الاستخدام؟

await (user.password = 'some-secure-password');

هذا لا يعمل.

وفقًا لـ ECMA262 12.15.4 ، يجب أن تكون قيمة الإرجاع user.password = 'some-secure-password' _rval_ ، وهي في هذه الحالة 'some-secure-password' .

أنت تقترح أن تكون قيمة الإرجاع someVar = object Promise ، ووفقًا لهذا الخيط ، والمواصفات ES262 المرتبطة أعلاه ، فإن ذلك يعد "انتهاكًا عميقًا لدلالات ES."

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

لماذا لا تفعل فقط:

const hashPassword = require('./lib/hashPassword');

const password = await hashPassword('some-secure-password');
User.password = password; // This is completely normal.

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

يمكنك أيضًا القيام بذلك:

User.methods.setPassword = async function (password) {
  const hashedPassword = await hashPassword(password);
  this.password = hashedPassword;
  await this.save();
  return this;
};
const myUser = new User();
await myUser.setPassword('mypassword...');

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

أتفق معheisian. هذا يبدو وكأنه ميزة / API سخام بالنسبة لي IMHO. لا أرى كيف أن بديل استخدام طريقة المثيل غير مريح هنا. لكن إضافة دعم نحوي كبير جدًا لهذا الأمر يبدو بالتأكيد وكأنه منتفخ.

يجب أن تكون لدينا ميزة بسيطة جدًا مثل هذه:

User.virtual('password').set((value, done) => {
  encryptValueWithAsyncFunction
    .then(response => done(null, value))
    .catch(reason => done(reason))
  ;
})

gcanu أنت تتجاهل تمامًا ما نشرته ، وما

ما الخطأ في مجرد القيام بما يلي:

await User.setPassword('password');

؟؟؟

في حالة عدم رؤيتك من قبل ، لن يعمل هذا :

await (User.password = 'password');

@ vkarpov15 هذه ليست مشكلة خاصة

الكود أدناه فكرة سيئة للغاية! لماذا يتضمن تعيين كلمة المرور العملية save ؟

User.methods.setPassword = async function (password) {
  const hashedPassword = await hashPassword(password);
  this.password = hashedPassword;
  await this.save();
  return this;
};

const myUser = new User();
await myUser.setPassword('mypassword...');

يحتاج النمس إلى مزيد من الحداثة والأناقة.

heisian حسنًا أداة الإعداد ...

heisian Plz راجع https://github.com/Automattic/mongoose/blob/master/lib/virtualtype.js.

حاليًا ، في Mongoose IMPL أو getter أو setter فقط قم بتسجيل وظيفة ثم الاتصال ، إنها ليست https://tc39.github.io/ecma262/#sec -assignment-clients-runtime-semantics -التقييم و https://github.com/tc39/ecmascript-asyncawait/issues/82. هذا مختلف.

لذا يرجى فتح هذا الطلب.

fundon ، أخبرني هذا: كيف async فيجب أن يتم التعامل معها بوعد. لا يُظهر المثال الأصلي الخاص بك await في أي مكان في استدعاء المُعيِّن / التعيين.

رمز المثال الخاص بي هو مجرد مثال ... يمكنك أيضًا القيام بذلك بسهولة:

User.methods.setPassword = async function (password) {
  const hashedPassword = await hashPassword(password);
  this.password = hashedPassword;
  return this;
};

const myUser = new User();
await myUser.setPassword('mypassword...');
await myUser.save();

بوضوح..

مثالك ليس طريقة جيدة بالنسبة لي.

أريد

await new User({ password }).save()

تجزئة كلمة المرور في الوضع الأكثر بساطة ، وأكثر أناقة.

لماذا ا؟ حتى تتمكن من حفظ بضعة أسطر من التعليمات البرمجية؟ السبب لا يكفي لتبرير كل العمل الإضافي وربما كسر التغييرات في قاعدة البيانات.

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

أنا لا أتفق معheisian. النمس لديه الكثير من الأشياء القديمة. النمس يحتاج إلى معمل!
النمس يحتاج الحديث.

إذا تم إغلاق هذه القضية. سأفرق النمس ، أعيد بنائه! وداعا!

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

لا يوجد قلق حقًا بشأن الحاجة إلى await (User.password = 'password'); . الجانب السلبي الحقيقي الوحيد هو أن user.password = 'password'; سيعني بعد ذلك أن هناك عملية غير متزامنة تحدث ، لذلك لن يتم تعيين user.passwordSalt . كيف يرتبط ذلك بالخطافات هو أيضًا سؤال مثير للاهتمام: ماذا يحدث إذا كان لديك خطاف pre('validate') أو pre('save') ، فهل يجب أن ينتظر هؤلاء حتى يتم الانتهاء من العملية غير المتزامنة user.password ؟

أنا لا أميل إلى صرف النظر عن هذه المسألة. هناك الكثير من القيمة التي يجب الحصول عليها في دمج السلوك غير المتزامن وراء .save() ، .find() ، وما إلى ذلك فقط تحتاج إلى التأكد من أنها تتناسب تمامًا مع بقية واجهة برمجة التطبيقات.

اليوم ، يعد الحاصلون وغير المتزامنون مهمون جدًا بالنسبة لي. أحتاج إلى إرسال طلبات HTTP من المحسِنين والمُحدِدين لفك تشفير / تشفير الحقول باستخدام طرق تشفير خاصة ، وحاليًا ، لا توجد طريقة للقيام بذلك. هل لديك فكرة عن كيفية تحقيق ذلك؟

gcanu كنت سأقوم فقط بتنفيذ هذه الأساليب

للأسباب التي ذكرتها وحقيقة أن هناك طرقًا للتعامل بسهولة مع أي عمليات غير متزامنة تحتاجها ، لا أرى أي أداة مساعدة وراء دمج سلوك async .. مرة أخرى ، await (User.password = 'password') يكسر اتفاقية ECMAScript وأنا أضمن أنه سيكون صعبًا ولن يستحق التنفيذ بأمان ...

أنت على حق ، هذا النمط ليس شيئًا سنفعله. فكرة انتظار حل غير متزامن الظاهري قبل الحفظ مثيرة للاهتمام.

أرغب في تطبيق toJSON({virtuals: true}) . بعض الحقول الافتراضية التي أحصل عليها عن طريق تشغيل استعلامات أخرى على db ، والتي أريد تشغيلها بمجرد إجراء التسلسل.

gabzim سيكون ذلك فوضويًا جدًا لأن JSON.stringify لا يدعم الوعود. لذا لن يتمكن () res.json من التعامل مع الظاهرية غير المتزامنة إلا إذا أضفت مساعدين إضافيين للتعبير.

آه نعم ، هذا منطقي ، شكرًا @ vkarpov15

هل سيكون إجراء استعلام داخل رد الاتصال get ممارسة جيدة؟
أعتقد أن هذا سيكون مفيدًا في بعض الحالات.

لنفترض أنني أريد الحصول على المسار الكامل لصفحة الويب (أو المستند) ، حيث يمكن دمج المستندات ، مثل مسارات Github URL.

const Doc = require('./Doc.js');
//...
subDocSchema.virtual('fullpath').get(async function(){
    const doc = await Doc.findById(this.doc); //doc is a Doc ref of type _id
    return `/${ doc.path }/${ this.path }`
})

هنا يتعين علينا استخدام غير متزامن / انتظار لأن عمليات الاستعلام غير متزامنة.

JulianSoto في هذه الحالة ، طريقة وليس افتراضية. السبب الرئيسي لاستخدام الظاهرية هو أنه يمكنك جعل Mongoose يتضمن ظاهريًا في toJSON() و toObject() الناتج . لكن toJSON() و toObject() متزامنان ، لذلك لن يتعاملوا مع الظاهري غير المتزامن.

واجهت حالة استخدام لهذا أيضًا ، وأحاول التفكير في حل جيد ، ومنفتح جدًا على الأفكار ، وأوافق على عدم كسر دلالات الواضع.

لقد كتبت أداة مساعدة لتطبيق مجموعات JSON Patch تلقائيًا على نماذج النمس. وهو يدعم سكان السيارات بمسارات عميقة: https://github.com/claytongulick/mongoose-json-patch

الفكرة هي أنه بالاقتران مع بعض القواعد: https://github.com/claytongulick/json-patch-rules يمكنك الاقتراب من وجود واجهة برمجة تطبيقات "تلقائية" مع تصحيح JSON.

كانت خطتي هي استخدام الظاهرية في الحالات التي لا ينجح فيها التعيين البسيط. عندما يتم تطبيق التصحيح ، سوف يلتقط الظاهري أي شيء تريده - وهذا من شأنه أن يسمح لكائن الواجهة أن يكون مختلفًا عن نموذج النمس الفعلي / كائن db.

على سبيل المثال ، لدي كائن مستخدم يدعم عملية "إضافة" على "طرق الدفع". لا تعد إضافة طريقة دفع إضافة مباشرة إلى المصفوفة - إنها استدعاء للمعالج برمز دفع ، واستعادة رمز مميز لطريقة الدفع ، وتخزين ذلك بطريقة مختلفة في النموذج ، وما إلى ذلك ...

لكني أرغب في أن يكون نموذج الواجهة ، النموذج المفاهيمي ، قادرًا على التصحيح باستخدام تصحيح JSON 'add' op.

بدون المستوطنين غير المتزامنين ، لن ينجح هذا. أعتقد أن الخيار الوحيد هو قبول mongoose-json-patch كخيار نوع من التعيين بين المسارات والعمليات وأساليب النمس ، ما لم تكن هناك أفكار أفضل؟

claytongulick لماذا تحتاج إلى أداة ضبط غير متزامنة بدلاً من await في عملية غير متزامنة ثم تعيينها بشكل متزامن؟

@ vkarpov15 ماذا عن مجرد جعل toObject() و toJSON() متزامن افتراضيًا وتقديم وظائف toObjectSync() و toJSONSync() ؟ Sync يجب أن المتغيرات ببساطة تخطي async virtuals. (أتذكر أن هذا النمط يستخدم في النمس في مكان ما ، لذلك لن يكون غريبًا جدًا.)

حالة الاستخدام الخاصة بي هي شيء من هذا القبيل: لدي مخطط به مخطط افتراضي يقوم بـ find() على نموذج آخر (أكثر تعقيدًا قليلاً من مجرد ملء معرّف). بالطبع يمكنني إلغاء تنسيق العناصر التي أريدها في نموذجي الرئيسي باستخدام خطافات الحفظ / الحذف ولكن هذا يأتي مع الكثير من تكاليف الصيانة (وأنا لا أحتاج حقًا إلى مزايا الأداء في هذه الحالة بالذات). لذلك من الطبيعي أن يكون لديك برنامج افتراضي للقيام بذلك من أجلي.

لا يدعم JSON.stringify() المتزامن toJSON() ، لذا للأسف لن تعمل فكرة toJSONSync() .

أعلم أنك قلت إن find() الخاص بك معقد جدًا ، ولكن قد ترغب في إلقاء نظرة على الملء الظاهري فقط في حالة. يمكنك أيضًا تجربة البرامج الوسيطة للاستعلام.

أيضًا ، هل يشتمل الافتراضي غير المتزامن الخاص بك على محدد أم فقط isamert ؟

حل لمن لديهم هذه المشكلة:

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

لاستخدام مثال أساسي مستوحى من السؤال الأول:

const User = new Schema(
  {
    username: {
      type: String,
      index: true,
      unique: true
    },
    encryptedPassword: {
      type: String,
      required: true,
      minlength: 64,
      maxlength: 64
    }
})

User.virtual('password').set(function generate(inputWithCb, virtual, doc) {
  let cb = inputWithCb.cb;
  let password = inputWithCb.password;
  encryptor.hash(password)
  .then((hash) => {
    doc.set("encryptedPassword", hash);
    cb && cb();
  });
})
// create the document
const admin = new User({
  username: 'admin'
});
// setup the promise for setting the async virtuals
const pwdProm = new Promise((resolve) => {
  admin.set("password", {cb: resolve, password: "admin"});
})

//run the promise and save only when the virtual setters have finished executing
pwdProm
.then(() => {
  admin.save();
});

قد يكون لهذا عواقب غير مرغوب فيها لذا استخدمها على مسؤوليتك الخاصة.

silto لماذا لا تستخدم فقط طريقة مخطط ترجع الوعد؟

@ vkarpov15 عادةً ما أفعل ذلك ، لكن في المشروع الذي قمت فيه بهذا ، لدي مخططات ، افتراضية ونقاط نهاية في GraphQL يتم إنشاؤها تلقائيًا من "خطة" json ، لذلك أفضل وجود واجهة افتراضية موحدة بدلاً من طريقة لحالة معينة.

silto هل يمكنك تقديم أي عينات من التعليمات البرمجية؟ أود أن أرى كيف يبدو هذا

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

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

أو يمكنك ببساطة استخدام دالة exec مثل التي لديك بالفعل (execPopulate).

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

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

في كلتا الحالتين ، سيكون من الجيد أن يكون لديك نوع من الغلاف لحل جميع البيانات بالفعل ولا يتعين عليك استخدام "ثم" في كل جهاز افتراضي وحاصل تم التعهد به ، بعد تحديد واضع موعود.

أريد أن أساعدك في هذا النهج.

تحياتي الحارة.

ملاحظة: مثال نموذجي لاستخدام الظاهرية الموعودة ، في حالتي ، أستخدم مسارين لمعرفة ما إذا كان يمكن حذف المستندات الخاصة بي أو تحديثها وفقًا للبيانات الخارجية ، لذلك عادةً ما أحتاج إلى الاستعلام عن نماذج أخرى لمعرفة ما إذا كان يمكن حذفها أو تعديلها . كما قلت من قبل ، فإن الوعد الصريح يحل هذه المشكلة بالنسبة لي ، ولكن إذا كنت أرغب في التحقق داخليًا مما إذا كان من الممكن القيام بهذه المهام ، فسيتعين علي الوفاء بهذه الوعود من قبل.

chumager هل يمكنك تقديم بعض عينات التعليمات البرمجية؟

مرحبًا ، على سبيل المثال ، وفقًا لتعليقي أدناه ، أستخدم 2 Virtuals _update و _delete ، ومكوِّنًا إضافيًا يعرّف تلك الظاهرية في حالة عدم تعريفها في المخطط ، وإرجاعها بشكل صحيح.

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

يتم حل _update الافتراضي في المحاكاة من خلال العثور على مشروع بالمحاكاة المشار إليها والحالة "En Financiamiento" ، إذا كان هذا الاستعلام صحيحًا ، فيمكن عندئذٍ أن يتم تحديث المحاكاة ... من الواضح أن "البحث" هو وعد ، لذا فإن الافتراضي هو أيضًا ...

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

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

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

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

يعتبر...

ملاحظة: إذا كنت حقًا بحاجة إلى مثال كودو ، فيرجى إبلاغي بذلك.

@ chumager جدار كبير من النثر! == عينة رمز. أنا أفضل حقا عينة رمز.

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