تخطَّ إلى المحتوى

التحديث والحذف في MongoDB

الدرس 5 من 8 · MongoDB

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

db.books.updateOne(
{ title: 'البرمجة للمبتدئين' },
{ $set: { available: true, price: 110 } }
)
الناتج
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}

updateOne تأخذ:

  1. الفلتر: أي مستند تعدّل، بنفس صيغة find.
  2. التعديل: ماذا تغيّر، عبر معاملات التحديث.

$set تضع قيمة جديدة للحقول المذكورة فقط، وتترك باقي المستند كما هو. وإذا كان الحقل غير موجود، تضيفه.

قراءة الرد:

  • matchedCount: كم مستنداً طابق الفلتر.
  • modifiedCount: كم مستنداً تغيّر فعلاً. قد يكون أقل من المطابق إذا كانت القيم الجديدة مساوية للقديمة.

$inc: زيادة رقم

db.books.updateOne({ title: 'رحلة إلى القمر' }, { $inc: { pages: 20 } })

الصفحات كانت 180، فأصبحت 200. و $inc بقيمة سالبة تُنقص، مثل { $inc: { stock: -1 } } عند بيع نسخة. ميزتها أنها آمنة عندما يعدّل عدة مستخدمين نفس الرقم في نفس اللحظة.

$push: إضافة عنصر لمصفوفة

db.books.updateOne({ title: 'رحلة إلى القمر' }, { $push: { genres: 'أطفال' } })
db.books.findOne({ title: 'رحلة إلى القمر' }, { genres: 1, _id: 0 })
الناتج
{ genres: [ 'خيال علمي', 'مغامرات', 'أطفال' ] }

ولحذف عنصر من مصفوفة: $pull. ولإضافة عنصر فقط إن لم يكن موجوداً: $addToSet، مفيدة لمنع تكرار التصنيفات.

$unset: حذف حقل

db.books.updateOne({ title: 'رحلة إلى القمر' }, { $unset: { notes: '' } })

الحقل notes اختفى من المستند تماماً. (القيمة '' لا تهم، فالمهم اسم الحقل.)

المعامل الوظيفة
$set تعيين قيمة حقل
$unset حذف حقل
$inc زيادة رقم أو إنقاصه
$push و $pull إضافة عنصر لمصفوفة أو حذفه منها
$addToSet إضافة لمصفوفة دون تكرار

لتعديل كل المستندات المطابقة. مثلاً، زيادة 10 جنيهات على كل كتب «دار المعرفة»:

db.books.updateMany({ 'publisher.name': 'دار المعرفة' }, { $inc: { price: 10 } })
الناتج
{
acknowledged: true,
insertedId: null,
matchedCount: 2,
modifiedCount: 2,
upsertedCount: 0
}

كتابان طابقا وتغيّرا: «أسرار البحار» أصبح 100، و«عالم الروبوتات» أصبح 160.

الخطأ الشائع: نسيان المعامل

Section titled “الخطأ الشائع: نسيان المعامل”

ماذا لو كتبت التعديل دون $set؟

db.books.updateOne({ title: 'حكايات جدتي' }, { price: 50 })
الناتج
Update document requires atomic operators

رفض MongoDB العملية، لأن updateOne تحتاج معاملات مثل $set. وهذه حماية مفيدة: في أنظمة قديمة، كان هذا الخطأ يستبدل المستند كله بـ { price: 50 } فتضيع كل باقي الحقول! وإذا أردت فعلاً استبدال المستند كاملاً، فهناك أمر صريح لذلك: replaceOne.

ماذا لو لم يطابق الفلتر أي مستند؟

db.books.updateOne({ title: 'كتاب غير موجود' }, { $set: { price: 1 } })

الرد فيه matchedCount: 0، ولم يحدث شيء. لكن أحياناً تريد: «حدّث المستند إن وُجد، وإلا أنشئه». هذا هو upsert:

db.books.updateOne(
{ title: 'علوم الفضاء' },
{ $set: { author: 'كريم نبيل', year: 2024, available: true } },
{ upsert: true }
)
الناتج
{
acknowledged: true,
insertedId: ObjectId('6af3b81b51809dda14a5123b'),
matchedCount: 0,
modifiedCount: 0,
upsertedCount: 1
}

لم يجد كتاباً بهذا العنوان، فأنشأ مستنداً جديداً يجمع الفلتر والتعديل معاً. مفيد جداً لعدادات الزيارات وإعدادات المستخدمين.

db.books.deleteOne({ title: 'علوم الفضاء' })
الناتج
{ acknowledged: true, deletedCount: 1 }

ولحذف كل الكتب الأقدم من 2019:

db.books.deleteMany({ year: { $lt: 2019 } })
الناتج
{ acknowledged: true, deletedCount: 2 }

حُذف «أسرار البحار» (2018) و«حكايات جدتي» (2015)، وبقيت 3 كتب.

على مجموعة الكتب:

  1. اجعل كل الكتب غير المتاحة متاحة بـ updateMany و $set.
  2. أضف حقل ratings لكتاب، ثم أضف إليه تقييمين بـ $push.
  3. أنقص سعر كتاب 15 جنيهاً بـ $inc.
  4. احذف حقلاً من كتاب بـ $unset.
  5. استخدم upsert لكتاب جديد، ثم نفّذ نفس الأمر مرة ثانية، وقارن الردين.
  6. احذف الكتب التي عدد صفحاتها أقل من 100، بعد التأكد بـ countDocuments.
  • نسيان $set: خطأ Update document requires atomic operators.
  • updateOne حين تقصد الكل: تعدّل أول مستند مطابق فقط. استخدم updateMany.
  • عدم قراءة الرد: matchedCount: 0 يعني أن الفلتر لم يطابق شيئاً، غالباً بسبب خطأ إملائي في القيمة أو اسم الحقل.

سؤال

أي تعديل يزيد عدد النسخ stock بمقدار 5؟

سؤال

ماذا يفعل الخيار upsert: true إذا لم يطابق الفلتر أي مستند؟

  • updateOne و updateMany تأخذان فلتراً وتعديلاً بمعاملات مثل $set و $inc.
  • $push و $pull و $addToSet للمصفوفات، و $unset لحذف حقل.
  • matchedCount و modifiedCount تخبرانك بما حدث فعلاً.
  • upsert: true يحدّث أو ينشئ، و replaceOne لاستبدال المستند كاملاً.
  • deleteOne و deleteMany للحذف، و deleteMany({}) تحذف الكل.