MongoDB: مقدمة إلى مسارات التجميع
آخر تحديث: 2026-08-26
تُعد مسارات البيانات المجمعة أقوى أداة لتحليل البيانات في MongoDB — وإتقان استخدامها يتيح لك الاستعانة بـ MongoDB لاستبدال 90% من سيناريوهات التحليل التي تعتمد على SQL.
تقدم هذه الدورة مقدمة لأساسيات مسارات التجميع، ومفاهيم المراحل، وترتيب التنفيذ.
1. ما ستتعلمه
- القواعد الأساسية لطريقة
aggregate() - مفهوم مراحل خط الأنابيب وترتيب تنفيذها
- المرحلة الأساسية: $match، $group، $project، $sort، $limit، $skip، $count
- التجميع البسيط في الممارسة العملية
2. ما هو مسار التجميع؟
شرح المفهوم: «مسار التجميع» (Aggregation Pipeline) هو أقوى إطار عمل لتحليل البيانات في MongoDB. فهو يقسم معالجة البيانات إلى مراحل متعددة يتم تنفيذها بالتتابع؛ حيث تأخذ كل مرحلة الناتج من المرحلة السابقة كمدخل لها، وتقوم بعمليات التصفية أو التحويل أو التجميع أو الحسابات الإحصائية لتصل في النهاية إلى النتيجة النهائية. ويستند نموذج «المسار» هذا إلى مفهوم مسار UNIX، مما يجعل تحليل البيانات المعقدة قابلاً للتركيب وسهل التصحيح.
كيفية العمل: يتم تنفيذ مسار التجميع مرحلةً تلو الأخرى وفقًا لترتيب المصفوفة. تقوم المرحلة الأولى بجمع جميع المستندات في عملية التجميع، بينما تقوم كل مرحلة لاحقة بتحويل دفق المستندات (التصفية، والإسقاط، والتجميع، وما إلى ذلك)، ثم تمرير الناتج إلى المرحلة التالية. يحاول مُحسِّن استعلامات MongoDB دفع $match و$sort إلى مقدمة مسار التجميع للاستفادة من الفهارس. يبلغ الحد الأقصى للذاكرة لكل مرحلة 100 ميغابايت؛ ويتطلب تجاوز هذا الحد استخدام allowDiskUse: true.
إعادة الترتيب التلقائي بواسطة مُحسِّن مسار المعالجة: يقوم مُحسِّن استعلامات MongoDB بإعادة ترتيب مراحل معينة تلقائيًا لتحسين الأداء— 1. تقديم $match إلى الأمام: إذا ظهرت $match بعد $project/$group، فسيحاول المُحسِّن تقديمها إلى الأمام (لأن حجم البيانات بعد $match أصغر)؛ 2. دمج $sort + $match: قد يتم دمج عمليات $sort + $match المتتالية في عملية $sort + $match واحدة تستخدم فهرسًا؛ 3. نقل $project للأمام: إذا كان $project يؤثر فقط على الحقول التي لا تحتاجها عمليات $match/$sort اللاحقة، فقد يقوم مُحسِّن الاستعلام بنقل $project للأمام لتقليل عدد الحقول. ومع ذلك، لا يغير المُحسّن الدلالات المُعرَّفة من قِبل المستخدم — فالنتائج تظل دائمًا هي نفسها كما في الترتيب الأصلي.
التأثير العملي لحد الذاكرة البالغ 100 ميغابايت: لكل مرحلة تجميع حد ذاكرة يبلغ 100 ميغابايت — ويؤدي تجاوز هذا الحد إلى ظهور خطأ «تجاوز حد الذاكرة». ويُعد هذا الحد إجراءً وقائيًّا على مستوى التصميم (لمنع استنفاد ذاكرة الخادم بواسطة استعلام تجميع واحد). الحلول البديلة: 1. قم بتعيين allowDiskUse: true (يتم كتابة الفائض إلى ملف مؤقت؛ ينخفض الأداء ولكن لا يتم إصدار أي خطأ)؛ 2. استخدم $match قبل $group لتقليل حجم المدخلات؛ 3. تجنب استخدام $push لجمع المصفوفات الكبيرة في $group (استخدم $sum للعد بدلاً من ذلك)؛ 4. قسّم عمليات التجميع الكبيرة إلى عدة عمليات أصغر (معالجتها على دفعات). يجب أن يكون allowDiskUse الملاذ الأخير — يجب عليك أولاً تحسين بنية مسار البيانات لتقليل متطلبات الذاكرة.
مقارنة بين أنابيب التجميع وتجميعات SQL: تعتبر أنابيب التجميع في MongoDB مكافئة من الناحية النظرية لعبارة SELECT...GROUP BY...HAVING في SQL، لكنها تختلف في طريقة التنفيذ— 1. تجمع SQL جميع العمليات في عبارة واحدة، بينما ترتبها MongoDB في تسلسل متسلسل من المراحل؛ 2. WHERE في SQL تقابل $match، وGROUP BY تقابل $group، وHAVING تقابل $match (بعد $group)، وSELECT يقابل $project، وORDER BY يقابل $sort؛ 3. الاستعلامات الفرعية في SQL تقابل خطوط الأنابيب المتداخلة أو $lookup؛ 4. دوال النوافذ في SQL تقابل $setWindowFields. الاستراتيجية الرئيسية للانتقال من SQL إلى مسارات التجميع: تقسيم جمل SQL إلى مراحل مستقلة وترتيبها وفقًا لتسلسل تدفق البيانات.
خريطة طريق تعلم مسار التجميع: ينقسم مسار التجميع إلى ثلاث مراحل، من المبتدئ إلى الخبير — 1. المبتدئ (هذه الدورة): إتقان المراحل السبع الأساسية ($match/$group/$project/$sort/$limit/$skip/$count) ونموذج تنفيذ مسار التجميع؛ 2. المستوى المتوسط (الدورة التالية): إتقان نظام التعبيرات ($cond/$switch/$dateOperators/$typeOperators/$stringOperators/$arrayOperators) والتحويلات المعقدة؛ 3. المتقدم (الدورات اللاحقة): إتقان عمليات ربط المجموعات المتعددة ($lookup/$unwind)، والبحث حسب الأوجه ($facet/$bucket)، والبحث النصي/الجغرافي ($text/$geoNear). يُنصح بالدراسة بالترتيب، مع إكمال 3–5 تمارين عملية في كل مرحلة لتعزيز تعلمك.
graph LR
A[Gathering<br/>1000 Documents] --> B[$match<br/>Filter]
B --> C[$group<br/>Group Aggregation]
C --> D[$sort<br/>Sort]
D --> E[$limit<br/>Restrictions]
E --> F[Results]
style B fill:#fff3cd
style C fill:#d4edda
sequenceDiagram
participant DB as MongoDB
participant S1 as $match
participant S2 as $group
participant S3 as $sort
participant S4 as $limit
DB->>S1: 1000 Document
Note over S1: Filter: category=Electronics
S1->>S2: 250 Document
Note over S2: Group by brand<br/>$sum, $avg
S2->>S3: 15 groups
Note over S3: Sort by total descending
S3->>S4: 15 groups
Note over S4: Take the previous 10
S4-->>DB: 10 Group Results
على غرار الأنابيب في نظام UNIX:
# Find products with score > 4, sort by price descending, top 10
cat products.json | jq 'select(.rating > 4)' | jq 'sort_by(-.price)' | head -10
# MongoDB Aggregation
db.products.aggregate([
{ $match: { rating: { $gt: 4 } } },
{ $sort: { price: -1 } },
{ $limit: 10 }
]);
3. القواعد الأساسية لـ aggregate()
وصف المفهوم: db.collection.aggregate(pipeline, options) هي طريقة الإدخال لتنفيذ مسار التجميع. pipeline عبارة عن مصفوفة من كائنات المراحل، بينما يتحكم options في سلوك التنفيذ (حدود الذاكرة، مهلات الانتظار، تلميحات الفهرس). تُرجع دالة التجميع مؤشرًا يدعم toArray() لاسترداد جميع النتائج دفعة واحدة أو forEach() للتكرار بينها واحدة تلو الأخرى.
كيفية العمل: عندما يتلقى MongoDB أمر تجميع، يقوم مُحسِّن الاستعلام أولاً بتحليل بنية مسار المعالجة ويحاول دفع المرحلة $match إلى الأمام (بدمجها مع $sort للاستفادة من الفهرس)، ثم ينفذ كل مرحلة بالتتابع وفقًا للترتيب المُحسَّن. تحتفظ كل مرحلة بدفق مستندات في الذاكرة؛ وإذا تم تجاوز الحد الأقصى البالغ 100 ميجابايت، يتم إصدار خطأ (باستثناء allowDiskUse: true).
تكوين بيئة الإنتاج لخيارات التجميع: يجب تكوين المعلمة options الخاصة بـ aggregate بشكل مناسب في بيئة الإنتاج — 1. allowDiskUse: true: يجب تمكينه للتجميعات الكبيرة (لمنع الأخطاء الناتجة عن حد الذاكرة البالغ 100 ميجابايت)، مع إعطاء الأولوية لتحسين مسار المعالجة لتقليل متطلبات الذاكرة (إدخال/إخراج القرص أبطأ 100 مرة من الذاكرة)؛ 2. maxTimeMS: 30000: تعيين مهلة (30 ثانية) لمنع التجميعات البطيئة من تعطيل المثيل بأكمله؛ 3. hint: {category: 1}: فرض استخدام فهرس محدد (لتصحيح المحسّن يدويًّا إذا اختار الفهرس الخاطئ)؛ 4. batchSize: 100: يتحكم في عدد المستندات التي يتم إرجاعها لكل دفعة (تقلل الدفعات الأصغر من وقت الاستجابة الأولى، بينما تقلل الدفعات الأكبر من عدد رحلات الشبكة ذهابًا وإيابًا). يجب أن تتضمن عمليات التجميع في بيئات الإنتاج maxTimeMS — فعمليات التجميع التي تفتقر إلى حماية المهلة الزمنية هي بمثابة قنابل موقوتة.
سلوك مُحسِّن مسار التجميع: يقوم مُحسِّن التجميع في MongoDB تلقائيًّا بتنفيذ بعض عمليات التحسين — 1. دفع $match إلى مرحلة سابقة: إذا ظهر $match بعد $group/$project، فسيحاول مُحسّن التجميع دفعه إلى مرحلة سابقة (على الرغم من أن النجاح غير مضمون)؛ 2. تحسين $sort + $limit: يتم تحسين عمليات $sort + $limit المتتالية إلى وضع Top N (مع الحفاظ على كومة من N عنصرًا فقط بدلاً من الفرز الكامل)؛ 3. عمليات التحسين التي لا يتم إجراؤها: لن يقوم مُحسِّن التجميع بإعادة ترتيب تسلسل المراحل المحددة من قبل المستخدم (على سبيل المثال، نقل $match من الموضع الثالث إلى الأول)، ولن يقوم بنقل $match إلى الأمام بعد $group. إن فهم حدود سلوك مُحسِّن التجميع يساعدك على كتابة مسارات أكثر كفاءة يدويًّا — «لا تعتمد على مُحسِّن التجميع؛ ضع $match في البداية بنفسك».
طريقتان لاستخدام مؤشرات التجميع: تُرجع aggregate مؤشرًا وليس مصفوفة — طريقتان للاستخدام — 1. cursor.toArray(): يسترد جميع النتائج إلى الذاكرة دفعة واحدة (مناسب لمجموعات النتائج الصغيرة، < 1,000 صف)؛ الكود موجز لكنه يستهلك قدرًا كبيرًا من الذاكرة؛ 2. cursor.forEach() / for await...of: يعالج النتائج واحدة تلو الأخرى (مناسب لمجموعات النتائج الكبيرة أو المعالجة المتدفقة)؛ استهلاك منخفض للذاكرة لكن الكود أكثر تعقيدًا قليلاً. يُنصح باستخدام for await...of في بيئات الإنتاج — فحتى لو كانت مجموعة النتائج الحالية صغيرة، فلن تتسبب في حدوث خطأ OutOfMemoryError عندما يزداد حجم البيانات في المستقبل. أما الكود الذي يستخدم toArray() فسيواجه فجأة خطأ OutOfMemoryError عندما يزداد حجم البيانات من 100 سجل إلى 100,000 سجل.
| المعلمة | النوع | الوصف |
|---|---|---|
pipeline |
مصفوفة | مصفوفة من كائنات المراحل؛ يتم تنفيذها بالترتيب |
options.allowDiskUse |
منطقية | تسمح بالكتابة المؤقتة على القرص (الافتراضي: false) |
options.maxTimeMS |
الرقم | مهلة الانتظار (بالميلي ثانية) |
options.batchSize |
الرقم | عدد المستندات التي يتم إرجاعها في كل دفعة |
options.hint |
سلسلة/كائن | فرض استخدام الفهرس المحدد |
// === Basic Usage ===
db.products.aggregate([
{ $match: { category: 'Electronics' } },
{ $group: { _id: '$brand', total: { $sum: 1 } } }
]);
// === Return to Cursor ===
const cursor = db.products.aggregate([...]);
const results = await cursor.toArray();
// === Aggregation Options ===
db.products.aggregate(
[{ $match: {} }],
{
allowDiskUse: true, // Allow disk usage(Processing Large Datasets)
maxTimeMS: 30000, // 30 Timeout in seconds
batchSize: 100, // Batch Size
hint: 'category_1' // Force Index Usage
}
);
4. شرح مفصل للمرحلة التأسيسية
نظرة عامة على المفهوم: يوفر مسار التجميع أكثر من 30 عامل مرحلة؛ ويركز هذا القسم على العوامل السبعة الأساسية التالية: $match (التصفية)، $group (التجميع حسب المجموعات)، $project (الإسقاط/الحساب)، $sort (الفرز)، $limit (تحديد العدد)، $skip (التخطي)، و$count (العد). وعند استخدامها مجتمعة، تغطي هذه العوامل 80% من سيناريوهات التحليل اليومية.
كيفية العمل: لكل مرحلة قواعد تحويل محددة من المدخلات إلى المخرجات. لا يغير $match بنية المستند؛ بل يقتصر دوره على التصفية فقط. أما $group فيدمج مستندات متعددة في مجموعات، ويُنتج مستندًا واحدًا لكل مجموعة. تقوم $project باختيار أو حساب حقول المخرجات. تتحكم $sort/$limit/$skip في ترتيب المستندات وعددها. مبدأ التحسين الرئيسي: $match قم بتطبيقها في أقرب وقت ممكن لتقليل عبء المعالجة في المراحل اللاحقة.
graph TB
A[Foundation Stage] --> B[$match<br/>Filter Documents<br/>Similar to find filter]
A --> C[$group<br/>Group Aggregation<br/>$sum/$avg/$min/$max]
A --> D[$project<br/>Field Projection<br/>Select/Calculated Fields]
A --> E[$sort<br/>Sort<br/>1 ascending -1 descending]
A --> F[$limit/$skip<br/>Pagination<br/>Restrictions/Skip]
A --> G[$count<br/>Count<br/>Number of output documents]
B --> H["✅ Put it at the very front<br/>Reducing Data Volume Using Indexes"]
style B fill:#fff3cd
style C fill:#d4edda
style H fill:#d4edda
(1) $match: تصفية المستندات
مبادئ التحسين المسبق لـ $match: تعد $match هي المرحلة الوحيدة في مسار التجميع التي يمكنها الاستفادة من الفهارس، لذا فإن وضعها في بداية المسار مباشرةً هو أهم مبدأ من مبادئ التحسين. الأسباب: 1. يتم تنفيذ $match قبل $group، مستخدمةً الفهارس مباشرةً للتصفية لتقليل حجم البيانات التي تتم معالجتها في المراحل اللاحقة؛ 2. يحاول مُحسِّن الاستعلامات في MongoDB دفع $match إلى الأسفل لتنفيذها قبل $lookup، لكنه لا يعيد ترتيب تسلسل المراحل المحددة من قبل المستخدم؛ 3. تنفيذ $match بعد $group يعني تجميع جميع البيانات أولاً ثم تصفية تلك البيانات، مما يؤدي إلى إهدار قدر كبير من الموارد الحاسوبية.
دليل الاختيار بين «التجميع» و«البحث»: متى تستخدم aggregate بدلاً من find؟ المعايير: 1. إذا كنت بحاجة إلى إحصائيات مجمعة ($group) → يجب استخدام aggregate؛ 2. إذا كنت بحاجة إلى حساب الحقول أو إعادة تسميتها ($project لحساب الحقول) → استخدم aggregate؛ 3. إذا كانت هناك حاجة إلى تحويلات متعددة الخطوات (على سبيل المثال، التصفية، ثم التجميع، ثم الفرز) → استخدم aggregate؛ 4. بالنسبة لاستعلامات CRUD البسيطة → يكون find أكثر كفاءة. تتسبب aggregate في عبء أكبر من find (بسبب تهيئة خط الأنابيب ونقل البيانات بين المراحل)، لذا تجنب الإفراط في استخدامها للاستعلامات البسيطة.
الاختلافات في الأداء بين $match و find: على الرغم من أن صيغة $match مماثلة لصيغة find، إلا أن سياقات تنفيذهما تختلف — فـ find عبارة عن استعلام مستقل يقوم المُحسِّن بتحسينه بالكامل، بينما يُعد $match جزءًا من مسار معالجة، وتكون قدرات تحسينه مقيدة بهيكل المسار. الاختلافات الرئيسية: 1. يمكن لـ find استخدام استعلام مغطى (استعلام مفهرس لا يقرأ المستندات)، بينما يقرأ $match دائمًا المستندات داخل مسار المعالجة؛ 2. $hint يسري مفعوله مباشرةً في find، في حين أن التلميحات في $match تُمرر عبر خيارات التجميع؛ 3. يدعم المؤشر الذي يُرجعه find التحكم في batchSize، في حين أن مؤشر التجميع يتصرف بشكل مشابه ولكنه يتطلب عبء تهيئة أكبر.
أفضل الممارسات لاستخدام فهارس $match: شروط استخدام $match للفهارس — 1. يجب أن تكون $match هي المرحلة الأولى من مسار المعالجة (أو المرحلة الأولى من مسار فرعي داخل $lookup)؛ وإلا فلن تتمكن من استخدام الفهرس؛ 2. يجب أن تكون الحقول الموجودة في شروط $match مفهرسة (سواء كفهارس أحادية الحقل أو كبادئات في فهارس مركبة)؛ 3. يمكن لمجموعة $match + $sort استخدام فهرس مركب لتلبية متطلبات كل من التصفية والفرز؛ 4. تحقق باستخدام explain(): db.orders.aggregate([{$match: {status: 'paid'}}]).explain() للتأكد من حدوث IXSCAN. إذا رأيت COLLSCAN، فهذا يعني أن $match لا تستخدم الفهرس — عليك إنشاء فهرس أو تعديل ترتيب مسار المعالجة. في بيئات الإنتاج، يجب عليك التحقق بانتظام من سجل الاستعلامات البطيئة للتأكد من أن $match في مسار التجميع يستخدم فهرسًا.
// === $match Similar to find filter ===
db.products.aggregate([
{ $match: { category: 'Electronics', price: { $gte: 100 } } }
]);
| مزايا $match | الوصف |
|---|---|
| التصفية في المراحل المبكرة من مسار المعالجة | تقلل من حجم البيانات التي تتم معالجتها في المراحل اللاحقة |
| يمكن استخدام الفهارس | أداء عالٍ (مشابه لـ find) |
(2) التجميع الجماعي $group
$group مبدأ التجميع: يُعد $group المرحلة الأكثر أهمية في مسار التجميع — فهو يقوم بتجميع تدفق المستندات حسب حقل _id، ويُجري حسابات التجميع بشكل مستقل لكل مجموعة، ويُخرج نتيجة تجميع واحدة. تحدد قيمة _id دقة التجميع: حيث تقوم الإشارة إلى الحقل ('_id: $category') بالتجميع حسب حقل واحد، بينما يقوم التعبير الكائني ('_id: {year, month}') بالتجميع حسب مجموعة من الحقول، وتشير القيمة الفارغة إلى عدم وجود تجميع — حيث تُحسب الإحصائيات للمجموعة بأكملها. إن فهم التحويل «واحد إلى عدة → واحد إلى واحد» لـ $group هو المفتاح لإتقان مسار التجميع.
شرح تفصيلي لمُجمِّع $group: يوفر $group عدة مُجمِّعات — $sum (المجموع/العدد)، و$avg (المتوسط)، و$min/$max (الحد الأدنى/الحد الأقصى)، و$first/$last (القيمة الأولى/القيمة الأخيرة)، و$push/$addToSet (جمع القيم في مصفوفة/إنشاء مصفوفة فريدة). الاختلافات الرئيسية: $sum: 1 يقوم بالعد (يزيد بمقدار 1 لكل مستند)، و$sum: '$field' يقوم بالجمع (تجميع قيم الحقول)؛ ويحتفظ $push بالقيم المكررة، بينما يقوم $addToSet بإزالة التكرارات تلقائيًا؛ وتعتمد قيم $first/$last على ترتيب الفرز للمدخلات (لا يمكن التنبؤ بها عند عدم استخدام $sort). ينخفض عدد المستندات بشكل حاد بعد $group (N مستندًا → M مجموعات)، مما يؤدي إلى انخفاض عبء المعالجة في المراحل اللاحقة.
نمط تصميم _id الخاص بـ $group: يحدد _id الخاص بـ $group دقة التجميع، وهو القرار التصميمي الأكثر أهمية بالنسبة لنتائج التجميع — 1. التجميع حسب حقل واحد (_id: '$category'): يتم التجميع حسب الفئة، مع إخراج صف واحد لكل فئة؛ 2. التجميع بالجمع بين حقول متعددة (_id: {category: '$category', status: '$status'}): جدولة تقاطعية حسب الفئة والحالة، مع إخراج صف واحد لكل تركيبة؛ 3. التجميع حسب التاريخ (_id: {year: {$year: '$createdAt'}, month: {$month: '$createdAt'}}): تحليل الاتجاهات حسب السنة والشهر؛ 4. إحصائيات شاملة (_id: null): بدون تجميع؛ يتم تجميع المجموعة بأكملها. كلما كان _id أكثر تفصيلاً، زاد عدد المجموعات؛ وتكون النتائج أكثر تفصيلاً لكن تأثير التجميع أضعف. وكلما كان _id أقل تفصيلاً، قل عدد المجموعات؛ ويكون تأثير التجميع أقوى لكن يتم فقدان المزيد من المعلومات.
دمج المجمعات: يمكن استخدام عدة مجمعات في $group في آن واحد لتوليد نتائج إحصائية شاملة— 1. حساب العدد والمتوسط والحد الأدنى والحد الأقصى في آن واحد (الإحصاءات الأساسية الأربعة التي تغطي احتياجات الإحصاء الوصفي)؛ 2. استخدم $push لجمع جميع القيم داخل المجموعة (على سبيل المثال، جمع جميع أسماء المنتجات في كل فئة)، ولكن لاحظ أن $push قد ينتج مصفوفات كبيرة (استخدم $slice لاستخراج مجموعة فرعية أو $size لإجراء العد بدلاً من ذلك)؛ 3. استخدم $addToSet لجمع القيم الفريدة (على سبيل المثال، حساب عدد العلامات التجارية المختلفة في كل فئة)، لكن $addToSet أبطأ من المجمعات الأخرى (لأنه يتطلب فحص التكرارات)؛ 4. تُستخدم $sum + $cond لإجراء العد المشروط (على سبيل المثال، حساب عدد المنتجات في كل فئة التي يكون سعرها > 1000: $sum: {$cond: [{$gt: ['$price', 1000]}, 1, 0]}).
المشاكل الشائعة وحل المشكلات المتعلقة بـ $group: هناك ثلاث مشاكل شائعة تتعلق بـ $group—1. لا يمكن حذف _id: يجب أن تحدد $group _id (حتى لو كانت قيمتها null)؛ وإلا فسيحدث خطأ؛ 2. تُفقد الحقول بعد $group: تحتفظ $group فقط بحقول _id والمُجمِّع؛ وتُفقد جميع الحقول الأصلية (على سبيل المثال، للوصول إلى حقل name بعد $group، يجب استخدام $push: '$name' في المُجمِّع، أو استخدام $project قبل $group للحفاظ على الحقول المطلوبة ثم استخدام $lookup بعد $group لاستردادها)؛ 3. $group يغير بنية المستند: المستندات بعد $group هي نتائج تجميع جديدة تمامًا ذات بنية مختلفة تمامًا عن المستندات الأصلية — ولا يمكن للمراحل اللاحقة إلا الإشارة إلى _id والحقول التي أنشأها أداة التجميع. نصيحة لاستكشاف الأخطاء وإصلاحها: قم بتشغيل عملية التجميع مرحلةً تلو الأخرى (مع تخطي المراحل اللاحقة) وراقب بنية المخرجات في كل خطوة.
// === Group by Field ===
db.products.aggregate([
{ $group: {
_id: '$category', // Grouping Field
count: { $sum: 1 }, // Count
avgPrice: { $avg: '$price' },
maxPrice: { $max: '$price' },
minPrice: { $min: '$price' }
}}
]);
// { _id: 'Electronics', count: 250, avgPrice: 599, maxPrice: 1999, minPrice: 99 },
// { _id: 'Books', count: 200, avgPrice: 29, maxPrice: 79, minPrice: 9 },
// ...
// ]
// === Multi-Field Grouping ===
db.orders.aggregate([
{ $group: {
_id: { year: { $year: '$createdAt' }, month: { $month: '$createdAt' } },
total: { $sum: '$total' },
count: { $sum: 1 }
}}
]);
// === Grouping the Entire Set ===
db.products.aggregate([
{ $group: {
_id: null, // No groups,Statistics for the Entire Set
totalProducts: { $sum: 1 },
avgPrice: { $avg: '$price' }
}}
]);
(3) عرض حقل $project
استخدامان لـ $project: لـ $project غرضان — 1. اختيار الحقول (على غرار الأمر SELECT في لغة SQL)، حيث يحدد الرقم 1 أو 0 ما إذا كان سيتم إخراج الحقل أم لا؛ 2. حساب الحقول (على غرار AS في لغة SQL)، باستخدام التعبيرات لإنشاء حقول جديدة. ملاحظة: في قاعدة 1/0 الخاصة بـ $project، يتم عرض _id افتراضيًا (يجب تعيينه صراحةً على 0 لإخفائه). بالنسبة للحقول الأخرى، إذا تم تعيين أي منها على 1، فإن الباقي يُعيَّن افتراضيًّا على 0 (وضع القائمة البيضاء)؛ وإذا تم تعيين أي منها على 0، فإن الباقي يُعيَّن افتراضيًّا على 1 (وضع القائمة السوداء). قد يؤدي خلط هذين الوضعين إلى سلوك غير متوقع.
// === Select Output Fields ===
db.products.aggregate([
{ $project: {
sku: 1,
title: 1,
price: 1,
discountedPrice: { $multiply: ['$price', 0.9] } // Calculate a New Field
}}
]);
// === Rename Field ===
db.products.aggregate([
{ $project: {
productName: '$title', // Rename title → productName
price: 1,
category: 1
}}
]);
(4) $sort: الفرز
حدود الذاكرة والتحسينات الخاصة بوظيفة $sort: تقوم وظيفة $sort بالفرز في الذاكرة، ويبلغ الحد الافتراضي للذاكرة 100 ميغابايت — وسيؤدي تجاوز هذا الحد إلى ظهور خطأ (ما لم يتم تعيين allowDiskUse: true). استراتيجيات التحسين: 1. استخدم $match قبل $sort لتقليل حجم البيانات (ففرز 1,000 سجل أسرع بكثير من فرز 100,000 سجل)؛ 2. أنشئ فهرسًا على حقل الفرز (نظرًا لأن الفهرس مرتب بالفعل، يمكن لـ MongoDB إرجاع النتائج مباشرةً بترتيب الفهرس دون الحاجة إلى الفرز في الذاكرة)؛ 3. عند الجمع بين $sort و$limit، يحتفظ MongoDB فقط بمكدس Top N (بدلاً من فرز مجموعة البيانات بأكملها)، مما يقلل من استخدام الذاكرة من O(N) إلى O(limit)؛ 4. يمكن للفهرس المركب {category: 1, price: -1} تلبية متطلبات كل من $match و$sort، مما يتيح إجراء «استعلام شامل + فرز».
// === 1 ascending, -1 descending ===
db.products.aggregate([
{ $sort: { price: -1 } }
]);
// === Sorting by Multiple Fields ===
db.products.aggregate([
{ $sort: { category: 1, price: -1 } }
]);
(5) ترقيم الصفحات باستخدام $limit و$skip
مشاكل الأداء المتعلقة بـ $skip: كلما زادت قيمة $skip، انخفض الأداء — حيث يتطلب $skip(10000) من MongoDB مسح أول 10,000 مستند وتجاهلها؛ ورغم أن هذه المستندات لا تُعاد إلى العميل، إلا أن العبء الإضافي الناجم عن المسح والفرز لا يزال قائمًا. تحسين الترقيم المتقدم: 1. الترقيم القائم على المؤشر (استخدم _id: {$gt: lastId} بدلاً من skip؛ يبقى الأداء ثابتًا)؛ 2. تحديد الحد الأقصى لعدد الصفحات (على سبيل المثال، السماح بالتنقل حتى الصفحة 50 فقط؛ وفي حالة تجاوزه، مطالبة المستخدم بتضييق نطاق البحث)؛ 3. وضع «Top N» باستخدام $sort + $limit (بدون تخطي؛ استرجاع أول N سجلات فقط). لا يُقبل الترقيم الإزاحي إلا عندما يكون حجم البيانات صغيرًا (< 1,000 صفحة).
تفاصيل تنفيذ الترقيم القائم على المؤشر: يستخدم الترقيم القائم على المؤشر قيمة الفرز الخاصة بالسجل الأخير في الصفحة السابقة باعتبارها «مؤشرًا»— 1. الصفحة الأولى: استعلام عادي $sort + $limit(N)؛ 2. الصفحات التالية: $match({createdAt: {$lt: lastCursor}, _id: {$lt: lastId}}) + $sort + $limit(N). يضمن استخدام حقلين (حقل الفرز + _id) تفرد المؤشر (قد يحتوي حقل الفرز على تكرارات، لكن _id لن يحتوي عليها)؛ 3. النقل إلى الواجهة الأمامية: تُستخدم قيمتي createdAt و_id للسجل الأخير في الصفحة السابقة كمعلمات المؤشر للصفحة التالية؛ 4. المزايا: بغض النظر عن رقم الصفحة، تظل تعقيد عملية الاستعلام ثابتًا عند O(N) (يتم مسح N سجلًا فقط؛ دون تخطي أي سجلات)؛ 5. القيود: لا يمكن الانتقال إلى أرقام صفحات عشوائية (يتم دعم «الصفحة التالية» فقط)؛ غير مناسب للسيناريوهات التي تتطلب التنقل بين أرقام الصفحات. تستخدم قوائم منتجات التجارة الإلكترونية الفرز القائم على المؤشر، بينما تستخدم الخلفيات الإدارية الفرز القائم على الإزاحة.
// === $limit Limit the number of results returned ===
db.products.aggregate([
{ $sort: { price: -1 } },
{ $limit: 10 }
]);
// === $skip Skip ===
db.products.aggregate([
{ $sort: { price: -1 } },
{ $skip: 20 },
{ $limit: 10 } // Items 21-30
]);
(6) $count العدد
حالات استخدام $count: تُعد $count أبسط مرحلة تجميع — فهي تأخذ N مستندًا كمدخلات وتُخرج مستندًا واحدًا يحتوي على قيمة العدد. وهي تعادل $group({ _id: null, total: { $sum: 1 } })، ولكن بصيغة أكثر إيجازًا. الاستخدامات الشائعة: 1. حساب عدد المستندات التي تمت تصفيتها ($match + $count)؛ 2. كخط أنابيب فرعي في $facet للحصول على العدد الإجمالي (لترقيم الصفحات)؛ 3. بالاقتران مع $match لإجراء عملية عد مشروطة (على سبيل المثال، «كم عدد منتجات الإلكترونيات الموجودة؟»).
// === Simple Counting ===
db.products.aggregate([
{ $match: { category: 'Electronics' } },
{ $count: 'totalElectronics' }
]);
// [ { totalElectronics: 250 } ]
// === equivalent to countDocuments ===
db.products.countDocuments({ category: 'Electronics' });
5. المركم
شرح المفهوم: المجمِّعات هي دوال تجميع تُستخدم في مرحلة $group، وتقوم بإجراء حسابات على كل مجموعة من المستندات وتُنتج نتيجة واحدة. $sum المجموع، $avg المتوسط، $min/$max القيم المتطرفة، $first/$last القيمة الأولى والأخيرة، $push/$addToSet تراكم المصفوفة. تعد المجمعات أدوات أساسية للتحليل الإحصائي.
كيفية العمل: تقوم مرحلة $group بتجميع المستندات بناءً على الحقل _id، وتجري حسابات التراكم بشكل مستقل لكل مجموعة. يقوم $sum: 1 بالعد، و$sum: '$field' بالجمع، و$avg: '$field' بحساب المتوسط، و$push: '$field' بجمع جميع القيم في مصفوفة، و$addToSet: '$field' بجمع القيم بعد إزالة التكرارات. يشير _id: null إلى عدم وجود تجميع؛ حيث تُحسب الإحصائيات للمجموعة بأكملها.
graph LR
A[Grouping Field _id] --> B[Group 1]
A --> C[Group 2]
A --> D[Group 3]
B --> E["$sum: {$sum:1}<br/>$avg: {$avg:'$price'}<br/>$push: {$push:'$name'}"]
C --> F["$sum: {$sum:1}<br/>$avg: {$avg:'$price'}<br/>$push: {$push:'$name'}"]
D --> G["$sum: {$sum:1}<br/>$avg: {$avg:'$price'}<br/>$push: {$push:'$name'}"]
E --> H[Output one line per group<br/>Aggregated Results]
F --> H
G --> H
style H fill:#d4edda
(1) القائمة الكاملة للمراكم
| المصطلح | المعنى | مثال |
|---|---|---|
$sum |
المجموع | { $sum: '$price' } |
$avg |
المتوسط | { $avg: '$price' } |
$min |
القيمة الدنيا | { $min: '$price' } |
$max |
القيمة القصوى | { $max: '$price' } |
$first |
الأول | { $first: '$name' } |
$last |
الأخير | { $last: '$name' } |
$push |
تراكم المصفوفات | { $push: '$name' } |
$addToSet |
إزالة التكرارات وتجميع القيم في مصفوفة | { $addToSet: '$name' } |
$count |
العدد (للمستوى الأعلى) | { $count: 'total' } |
▶ المثال 1: المجمع قيد التشغيل
الاستخدام المشترك للمجمعات: في سيناريوهات الأعمال الواقعية، عادةً ما يتم دمج عدة مجمعات ضمن مجموعة واحدة $group — على سبيل المثال، تتطلب إحصائيات فئات التجارة الإلكترونية الحساب المتزامن لعدد الوحدات ($sum: 1)، وإجمالي الإيرادات ($sum: '$price')، ومتوسط السعر ($avg: '$price')، والسعر الأقصى ($max: '$price'). يتم حساب المقاييس الأربعة جميعها في عملية $group واحدة، مما يلغي الحاجة إلى استعلامات متعددة. تعد مرحلة $group هي مرحلة «ضغط البيانات» — حيث تأخذ N مستندًا كمدخلات وتنتج M مجموعات (M << N)، مما يقلل بشكل كبير من عبء المعالجة في المراحل اللاحقة.
الاختيار بين $push و$addToSet: تحتفظ $push بالقيم المكررة (على سبيل المثال، ['Electronics', 'Electronics', 'Books'])، بينما تقوم $addToSet بإزالة التكرارات تلقائيًا (على سبيل المثال، ['Electronics', 'Books']). معايير الاختيار: 1. الحاجة إلى قائمة كاملة بالعناصر (بما في ذلك التكرارات) → $push (على سبيل المثال، "قائمة العناصر من جميع الطلبات")؛ 2. الحاجة إلى العناصر الفريدة فقط → $addToSet (على سبيل المثال، "فئات العناصر المشتراة"). ملاحظة: قد تؤدي وظيفة $push إلى إنشاء مصفوفات كبيرة جدًّا (على سبيل المثال، الفئات الشائعة التي تحتوي على 10,000 اسم منتج)؛ استخدم $slice لتحديد طول المصفوفة أو $unwind + $group لإعادة هيكلة المصفوفة.
// === Count the number of items in each category and summarize the prices ===
db.products.aggregate([
{
$group: {
_id: '$category',
count: { $sum: 1 },
totalStock: { $sum: '$stock' },
avgPrice: { $avg: '$price' },
maxPrice: { $max: '$price' },
minPrice: { $min: '$price' },
topProducts: { $push: '$title' } // All Product Names
}
}
]);
// === $addToSet Cumulative deduplication ===
db.users.aggregate([
{
$group: {
_id: '$city',
uniqueRoles: { $addToSet: '$role' } // Set of Roles for Each City
}
}
]);
الإخراج:
TEXT 📖 للعرض فقط[ { _id: 'Electronics', count: 250, totalStock: 1500, avgPrice: 599.99, maxPrice: 1999.99, minPrice: 99.99, topProducts: ['Smartphone X', 'Laptop Pro', ...] }, { _id: 'Books', count: 200, totalStock: 800, avgPrice: 29.99, maxPrice: 79.99, minPrice: 9.99, topProducts: ['MongoDB Guide', 'Node.js Handbook', ...] }, ... ]
6. تحسين ترتيب التنفيذ
شرح المفهوم: يعتمد أداء مسارات التجميع بشكل كبير على ترتيب المراحل. مبادئ التحسين الأساسية: يجب وضع $match في أقرب مرحلة ممكنة؛ ويجب أن تعمل $project على تقليل عدد الحقول بعد $group؛ ويجب أن تكون $sort و$match متجاورتين للاستفادة من الفهارس. يقوم مُحسِّن استعلامات MongoDB تلقائيًا بتنفيذ بعض عمليات التحسين (مثل دفع $match قبل $lookup)، لكنه لا يغير ترتيب المراحل المحدد من قبل المستخدم.
كيفية العمل: يحاول المُحسِّن تنفيذ نوعين من التحسين: (1) تحريك $match إلى الأمام ودمجها مع $sort في عملية مسح الفهرس؛ (2) دفع $match إلى أسفل في مسار $lookup. ومع ذلك، لا يقوم مُحسِّن الأداء بإعادة ترتيب تسلسل المراحل المُعرَّفة من قِبل المستخدم — فإذا جاءت $match بعد $group، فلا يمكن نقلها تلقائيًّا إلى الأمام.
graph LR
subgraph "✅ Optimization Order"
A1[$match<br/>Filter first] --> B1[$sort<br/>Sort] --> C1[$limit<br/>Restrictions]
end
subgraph "⚠️ Anti-pattern"
A2[$sort<br/>Full Sort] --> B2[$limit<br/>Restrictions] --> C2[$match<br/>Post-filtration]
end
style A1 fill:#d4edda
style C2 fill:#f8d7da
| استراتيجية التحسين | التأثير | الشروط المطبقة |
|---|---|---|
$match وضعها في البداية |
تقليل حجم البيانات اللاحقة | تصفية الحقول باستخدام الفهارس |
$match + $sort متجاوران |
تم دمجهما في عملية مسح الفهرس | حقل الفرز له فهرس مركب |
$project تبسيط الحقول |
تقليل استخدام الذاكرة | الاستخدام بعد $group |
hint() الفهرس القسري |
تجنب عمليات المسح الكامل للجدول | عندما يختار المُحسِّن الفهرس الخاطئ |
// ✅ Optimization:$match Put it at the very front
db.products.aggregate([
{ $match: { category: 'Electronics' } }, // Filter first(Using an Index)
{ $sort: { price: -1 } },
{ $limit: 10 }
]);
// ⚠️ Counterexample:$match Put it in the back
db.products.aggregate([
{ $sort: { price: -1 } },
{ $limit: 10 },
{ $match: { category: 'Electronics' } } // Filter only after processing,Waste of resources
]);
7. التدريب العملي الشامل
مبادئ تنفيذ مسار التجميع وإدارة الذاكرة: تحتفظ كل مرحلة من مراحل مسار التجميع بتيار من المستندات في الذاكرة، مع حد أقصى للذاكرة يبلغ 100 ميغابايت لكل مرحلة (يتم الإبلاغ عن خطأ في حالة تجاوز هذا الحد، ما لم يتم تعيين allowDiskUse: true). وهذا يعني: 1. يجب ألا يكون عدد مفاتيح المجموعات في مرحلة $group مفرطًا (فوجود ملايين مفاتيح المجموعات سيؤدي إلى تجاوز سعة الذاكرة)؛ 2. يقوم المجمع $push بجمع جميع القيم في مصفوفة، مما قد يؤدي بسهولة إلى تجاوز الحد الأقصى عند التعامل مع أحجام كبيرة من البيانات؛ 3. يقوم $sort بإجراء الفرز في الذاكرة؛ وبالنسبة لمجموعات البيانات الكبيرة، يلزم وجود فهرس لمنع تجاوز سعة الذاكرة. استراتيجيات التحسين: قم بمعالجة البيانات مسبقًا قبل استخدام $match لتقليل الحجم، واستخدم $project لتبسيط الحقول، واستبدل الفرز الكامل بـ $sort + $limit.
(1) إحصاءات مبيعات التجارة الإلكترونية
// === Monthly Sales Statistics ===
db.orders.aggregate([
{ $match: { status: 'paid', createdAt: { $gte: new Date('2026-01-01') } } },
{
$group: {
_id: {
year: { $year: '$createdAt' },
month: { $month: '$createdAt' }
},
totalSales: { $sum: '$total' },
orderCount: { $sum: 1 },
avgOrderValue: { $avg: '$total' }
}
},
{ $sort: { '_id.year': 1, '_id.month': 1 } }
]);
(2) تحليل فئات المنتجات
// === Product Categories + Price Range Analysis ===
db.products.aggregate([
{
$bucket: {
groupBy: '$price',
boundaries: [0, 100, 500, 1000, 5000, 10000],
default: 'Other',
output: {
count: { $sum: 1 },
products: { $push: '$title' }
}
}
}
]);
▶ المثال 2: مسار تحليل فئات التجارة الإلكترونية في ShopHub
// Scene:ShopHub The operations team needs to analyze product data from multiple perspectives.
// Preparing the Data
db.products.insertMany([
{ sku: 'PHONE-001', title: 'Smartphone X', category: 'Electronics', brand: 'TechCorp', price: 599, stock: 120, rating: 4.5 },
{ sku: 'PHONE-002', title: 'Smartphone Y', category: 'Electronics', brand: 'DataFlow', price: 399, stock: 80, rating: 4.0 },
{ sku: 'LAPTOP-001', title: 'Laptop Pro', category: 'Electronics', brand: 'TechCorp', price: 1299, stock: 50, rating: 4.8 },
{ sku: 'BOOK-001', title: 'MongoDB Guide', category: 'Books', brand: 'AppVenture', price: 29, stock: 500, rating: 4.2 },
{ sku: 'BOOK-002', title: 'Node.js Mastery', category: 'Books', brand: 'AppVenture', price: 39, stock: 300, rating: 4.6 }
]);
// 1. Statistics by Category:Number of Products、Average Price、Highest Price、Brand List
db.products.aggregate([
{
$group: {
_id: '$category',
count: { $sum: 1 },
avgPrice: { $avg: '$price' },
maxPrice: { $max: '$price' },
minPrice: { $min: '$price' },
totalStock: { $sum: '$stock' },
brands: { $addToSet: '$brand' }
}
},
{ $sort: { count: -1 } }
]);
// 2. Price Range Distribution($bucket)
db.products.aggregate([
{
$bucket: {
groupBy: '$price',
boundaries: [0, 100, 500, 1000, 5000],
default: '5000+',
output: { count: { $sum: 1 }, titles: { $push: '$title' } }
}
}
]);
// 3. Highly Rated Products(rating >= 4.5)
db.products.aggregate([
{ $match: { rating: { $gte: 4.5 } } },
{ $project: { sku: 1, title: 1, price: 1, rating: 1, _id: 0 } },
{ $sort: { rating: -1, price: 1 } }
]);
النتائج: 1) إحصائيات مصنفة (الأجهزة الإلكترونية: 3 عناصر بمتوسط سعر 765.67؛ الكتب: 2 عنصر بمتوسط سعر 34)؛ 2) فئات الأسعار (0–100: 2؛ 500–1,000: 1؛ 1,000–5,000: 1)؛ 3) قائمة بالمنتجات ذات التقييمات العالية.
نمط تصميم _id الخاص بـ $group: يحدد _id الخاص بـ $group دقة التجميع — 1. التجميع أحادي الحقل: _id: '$category' (مجمَّع حسب الفئة)؛ 2. التجميع متعدد الحقول: _id: {category: '$category', brand: '$brand'} (مجمَّع بشكل متقاطع حسب الفئة والعلامة التجارية، مما ينتج عنه مفتاح مركب)؛ 3. التجميع القائم على التاريخ: _id: {year: {$year: '$createdAt'}, month: {$month: '$createdAt'}} (مجمّع حسب السنة والشهر؛ يُستخدم عادةً للتقارير الشهرية)؛ 4. التجميع باستخدام التعبيرات: _id: {$cond: [{$gte: ['$price', 100]}, 'premium', 'budget']} (التجميع حسب الشروط، التصنيف الديناميكي)؛ 5. التجميع بقيمة null: _id: null (لا يوجد تجميع؛ يُستخدم لحساب الإحصائيات الشاملة، مثل إجمالي عدد الطلبات وإجمالي المبيعات). يحدد تصميم _id البعد الإحصائي — حيث ينتج عن _id متعدد الحقول منتج ديكارت (صف واحد لكل تركيبة)، وكلما زاد عدد الحقول، زاد عدد صفوف المخرجات.
دمج المجمعات: يمكن دمج المجمعات الموجودة في $group—1. $sum + $avg: حساب عدد العناصر ومتوسط السعر لكل فئة (count: {$sum: 1}, avgPrice: {$avg: '$price'}); 2. $min + $max: حساب نطاق السعر (minPrice: {$min: '$price'}, maxPrice: {$max: '$price'}); 3. $push + $addToSet: جمع القيم داخل المجموعة (products: {$push: '$title'} للاحتفاظ بالتكرارات، brands: {$addToSet: '$brand'} لإزالة التكرارات)؛ 4. $first + $last: استرداد القيم الأولى والأخيرة داخل المجموعة (تُستخدم بالاقتران مع $sort لاسترداد أقدم السجلات وأحدثها). ملاحظة: قد ينتج عن استخدام $push و$addToSet مصفوفات كبيرة (تجمع جميع القيم داخل كل مجموعة)؛ بالنسبة لمجموعات البيانات الكبيرة، استخدمهما بالاقتران مع $limit أو $slice.
▶ المثال 3:تحليل مبيعات التجارة الإلكترونية الشامل(الصعوبة ⭐⭐)
// Scene:ShopHub Multi-dimensional sales analysis dashboard
db.orders.insertMany([
{ orderId: 'ORD-001', customerId: 'C001', product: 'Smartphone X', category: 'Electronics', quantity: 2, price: 599, status: 'paid', createdAt: new Date('2026-06-01') },
{ orderId: 'ORD-002', customerId: 'C002', product: 'Laptop Pro', category: 'Electronics', quantity: 1, price: 1299, status: 'paid', createdAt: new Date('2026-06-15') },
{ orderId: 'ORD-003', customerId: 'C001', product: 'Headphones', category: 'Electronics', quantity: 3, price: 99, status: 'paid', createdAt: new Date('2026-07-01') },
{ orderId: 'ORD-004', customerId: 'C003', product: 'MongoDB Book', category: 'Books', quantity: 2, price: 39, status: 'paid', createdAt: new Date('2026-07-10') },
{ orderId: 'ORD-005', customerId: 'C002', product: 'Tablet Pro', category: 'Electronics', quantity: 1, price: 899, status: 'paid', createdAt: new Date('2026-07-15') }
]);
// تحليل متعدد الأبعاد: إحصائيات شهرية + تصنيف حسب الفئة + أفضل العملاء
db.orders.aggregate([
{ $match: { status: 'paid' } },
{
$facet: {
// 1. الإحصائيات الشهرية
monthlyStats: [
{
$group: {
_id: {
year: { $year: '$createdAt' },
month: { $month: '$createdAt' }
},
revenue: { $sum: { $multiply: ['$quantity', '$price'] } },
orderCount: { $sum: 1 },
avgOrderValue: { $avg: { $multiply: ['$quantity', '$price'] } }
}
},
{ $sort: { '_id.year': 1, '_id.month': 1 } }
],
// 2. إحصائيات الفئات
categoryStats: [
{
$group: {
_id: '$category',
totalRevenue: { $sum: { $multiply: ['$quantity', '$price'] } },
itemCount: { $sum: '$quantity' }
}
},
{ $sort: { totalRevenue: -1 } }
],
// 3. أفضل 5 عملاء
topCustomers: [
{
$group: {
_id: '$customerId',
totalSpent: { $sum: { $multiply: ['$quantity', '$price'] } },
orderCount: { $sum: 1 }
}
},
{ $sort: { totalSpent: -1 } },
{ $limit: 5 }
],
// 4. المنتجات الأكثر مبيعًا
topProducts: [
{
$group: {
_id: '$product',
totalSold: { $sum: '$quantity' },
revenue: { $sum: { $multiply: ['$quantity', '$price'] } }
}
},
{ $sort: { totalSold: -1 } },
{ $limit: 5 }
]
}
}
]);
الإخراج:
TEXT 📖 للعرض فقط{ monthlyStats: [{_id: {year: 2026, month: 6}, revenue: 2497, orderCount: 2, avgOrderValue: 1248.5}, ...], categoryStats: [{_id: 'Electronics', totalRevenue: 3894, itemCount: 7}, {_id: 'Books', totalRevenue: 78, itemCount: 2}], topCustomers: [{_id: 'C001', totalSpent: 1495, orderCount: 2}, ...], topProducts: [{_id: 'Headphones', totalSold: 3, revenue: 297}, ...] }
❓ أسئلة شائعة
find؟find للاستعلامات البسيطة وaggregate للتحليلات المعقدة.hint() استخدام فهرس معين.allowDiskUse: true.aggregate؟cursor.hasNext() وcursor.next()، أو قم بالتكرار مباشرةً في mongosh.📖 ملخص
- مسار التجميع = معالجة البيانات عبر عدة مراحل متتالية
- المراحل الأساسية: $match، $group، $project، $sort، $limit، $skip، $count
- المتغيرات التراكمية: $sum، $avg، $min، $max، $first، $last، $push، $addToSet
- ضع
$matchفي البداية لتقليل عبء المعالجة - الحد الأقصى للذاكرة: 100 ميغابايت؛ يمكن تجاوزه باستخدام
allowDiskUse - يمكن أن تستخدم مسارات التجميع الفهارس
📝 تمارين
- المشكلة الأساسية (⭐): استخدم $group لحساب عدد العناصر في كل فئة وحساب متوسط السعر.
- السؤال الأساسي (⭐): استخدم $match + $sort + $limit للبحث عن أفضل 10 منتجات إلكترونية حسب السعر.
- مشكلة متقدمة (⭐⭐): احسب إجمالي عدد الطلبات وإجمالي المبيعات حسب الشهر ($year/$month + $group).
- مشكلة متقدمة (⭐⭐): استخدم المتغير $project لحساب discountedPrice (السعر × 0.9).
- التحدي (⭐⭐⭐): إنجاز لوحة معلومات المبيعات (تحليل المبيعات حسب الشهر والفئة والعميل).