NahdaAIنهضة
NoSQL Databases • المستوى الثالث • الدرس 4

إسقاط وتشكيل الحقول ($project & $unwind)

العودة لفهرس المستوى

أهداف الدرس

  • تفكيك عناصر المصفوفات المضمنة بـ $unwind لمعالجتها بشكل مستقل.
  • إعادة تشكيل مخرجات التجميع بـ $project.
  • استخدام تعبيرات المسار ("$items.name") لإعادة التسمية.
  • بناء تقارير متقدمة بدمج $unwind مع $lookup و $group.

1. $unwind — تفكيك المصفوفات

$unwind يحوّل كل عنصر في المصفوفة إلى مستند مستقل — فيتضاعف عدد المستندات بعدد العناصر.

javascript
// قبل $unwind
{
  orderNumber: "ORD-001",
  items: [
    { name: "لابتوب", quantity: 1, price: 1500 },
    { name: "ماوس",  quantity: 3, price: 50 }
  ]
}

// بعد $unwind: "$items"
{ orderNumber: "ORD-001", items: { name: "لابتوب", quantity: 1, price: 1500 } }
{ orderNumber: "ORD-001", items: { name: "ماوس",  quantity: 3, price: 50 } }

// ملاحظة: orderNumber مكرر لأن العنصرين من نفس الطلب!

2. $project — إعادة التشكيل مع التعبيرات

يمكنك استخدام تعبيرات المسار لاستخراج قيم متداخلة وإعادة تسميتها.

javascript
db.orders.aggregate([
  { $unwind: "$items" },
  {
    $project: {
      _id: 0,
      orderNumber: 1,                          // <- نسخ مباشر
      productName: "$items.name",              // <- استخراج وإعادة تسمية
      itemPrice: "$items.price",
      quantity: "$items.quantity"
    }
  }
]);

// النتيجة:
{ orderNumber: "ORD-001", productName: "لابتوب", itemPrice: 1500, quantity: 1 }
{ orderNumber: "ORD-001", productName: "ماوس",  itemPrice: 50,   quantity: 3 }

3. الحقول المحسوبة — $multiply & $add

في $project يمكن حساب قيم جديدة من الحقول الموجودة.

$multiply — ضرب
subtotal: {
  $multiply: [
    "$items.quantity",
    "$items.price"
  ]
}
$add — جمع
total: {
  $add: [
    "$price",
    "$tax"
  ]
}
javascript
db.orders.aggregate([
  { $unwind: "$items" },
  {
    $project: {
      _id: 0,
      productName: "$items.name",
      quantity: "$items.quantity",
      unitPrice: "$items.price",
      subtotal: {
        $multiply: ["$items.quantity", "$items.price"]
      }
    }
  }
]);

// النتيجة:
{ productName: "لابتوب", quantity: 1, unitPrice: 1500, subtotal: 1500 }
{ productName: "ماوس",  quantity: 3, unitPrice: 50,   subtotal: 150 }

4. دمج المراحل — $unwind مع $lookup و $group

القوة الحقيقية تظهر عند دمج $unwind مع بقية المراحل لبناء تقارير واقعية.

javascript
// تقرير: أعلى المنتجات مبيعاً في السعودية ومصر
db.orders.aggregate([
  // 1. ربط الطلبات بالمستخدمين
  { $lookup: {
      from: "users",
      localField: "user_id",
      foreignField: "_id",
      as: "userDetails"
  } },
  { $unwind: "$userDetails" },

  // 2. فلترة حسب الدولة
  { $match: { "userDetails.country": { $in: ["السعودية", "مصر"] } } },

  // 3. تفكيك المنتجات
  { $unwind: "$items" },

  // 4. تجميع حسب اسم المنتج
  { $group: {
      _id: "$items.name",
      totalSold: { $sum: "$items.quantity" }
  } },

  // 5. ترتيب تنازلي
  { $sort: { totalSold: -1 } },

  // 6. تشكيل المخرجات النهائية
  { $project: {
      _id: 0,
      productName: "$_id",
      totalSold: 1
  } }
]);
التطبيق العملي المباشر

تحدي الدرس الرابع: $unwind & $project

قم بالتعديل على الكود بداخل المحرر أدناه لتطبيق المتطلبات التالية:

  1. استخدم $unwind لتفكيك مصفوفة items.
  2. استخدم $project لعرض orderNumber و productName و quantity.
  3. أضف حقل subtotal محسوب بـ $multiply.
  4. أضف $group بعد $unwind لحساب إجمالي كل منتج.
  5. أضف $sort لترتيب النتائج حسب الإجمالي تنازلياً.
⏳ جارٍ تحميل المحرر التفاعلي...