MongoDBقواعد البيانات

MongoDB: كيفية حساب القيمة المتوسطة لحقل

دليل شامل وأكاديمي يوضح كيفية حساب القيمة المتوسطة لحقل في MongoDB باستخدام إطار عمل التجميع ومُعامل $avg مع أمثلة عملية وتحليلات للأداء.

تاريخ النشر

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

تكمن قوة إطار عمل التجميع في MongoDB (Aggregation Framework) في قدرته على نقل الأعباء الحسابية الثقيلة من طبقة التطبيق (Application Layer) إلى طبقة قاعدة البيانات مباشرة، مما يقلل من استهلاك النطاق الترددي للشبكة ويستفيد من البنية الداخلية لمحرك التخزين في تحسين سرعة المعالجة واستغلال الذاكرة المؤقتة. إن حساب القيمة المتوسطة لحقل معين في MongoDB ليس مجرد استدعاء لدالة برمجية بسيطة، بل هو عملية متكاملة تخضع لتدفق خطي عبر مراحل معالجة متعددة، وتتأثر بعوامل تقنية دقيقة مثل أنواع البيانات المخزنة بتنسيق BSON، ومعالجة القيم المفقودة والمنعدمة (Nulls)، والتعامل مع التراكيب المعقدة كالمستندات المضمنة والمصفوفات الرقمية، فضلاً عن اعتبارات الكفاءة الحاسوبية وحدود استهلاك الذاكرة في البيئات الإنتاجية ذات الأحمال العالية.

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

1. مقدمة نظرية حول تجميع البيانات وحساب المتوسطات في MongoDB

1.1 أهمية العمليات الحسابية والتجميعية في قواعد البيانات غير العلائقية

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

يعد المتوسط الحسابي أحد أهم المقاييس الإحصائية في علم البيانات وهندسة البرمجيات، إذ يوفر قيمة تلخيصية مركزية تمثل نقطة التوازن لمجموعة من البيانات العددية. إن الاعتماد على إجراء العمليات التجميعية وحساب المتوسطات على مستوى التطبيق (Application-level Processing) يفرض أعباءً معمارية قاسية تشمل استنزاف الذاكرة العشوائية للخوادم الوسيطة، وزيادة زمن التأخير (Latency) نتيجة نقل كتل ضخمة من البيانات عبر الشبكة، فضلاً عن تعقيد معالجة التوازي والتزامن. في المقابل، توفر المعالجة على مستوى قاعدة البيانات (Database-level Processing) تحسيناً استثنائياً؛ حيث يستفيد المحرك من قرب المعالجة الحسابية من وسائط التخزين الفعلية والذاكرة المخبأة، مما يمكنه من تقليص مصفوفات المستندات الضخمة وتمرير النتيجة الرقمية النهائية فقط إلى التطبيق العميل.

1.2 نظرة عامة على محرك الاستعلامات في MongoDB

تعتمد معمارية MongoDB على تخزين المستندات بصيغة BSON (Binary JSON)، وهي صياغة ثنائية متقدمة تدعم نطاقاً واسعاً من الأنواع العددية الصريحة مقارنة بنصوص JSON التقليدية، مثل الأعداد الصحيحة بترميز 32-بت و64-بت، والأرقام العشرية ذات الفاصلة العائمة المزدوجة (Double)، والأرقام العشرية فائقة الدقة (Decimal128). توفر هذه البنية الثنائية إمكانية مسح الحقول وفك ترميز القيم الرقمية بسرعة فائقة دون الحاجة إلى معالجة النصوص البرمجية وتحليلها، مما يمنح محرك التخزين WiredTiger قدرة عالية على تنفيذ الحسابات الرياضية المباشرة في الذاكرة الحية.

تطورت قدرات MongoDB التحليلية بشكل كبير عبر مسارها التاريخي؛ فبعد أن كانت تعتمد على نموذج البرمجة الموزعة Map-Reduce الذي كان يستهلك موارد حوسبية هائلة ويتسم ببطء التنفيذ بسبب اعتماده على محرك جافا سكريبت، قامت MongoDB بإطلاق “إطار عمل التجميع” (Aggregation Framework) المبني بلغة C++ عالية الأداء. يعمل هذا المحرك بتناغم تام مع محرك التخزين الأساسي، حيث ينفذ العمليات الحسابية المتزامنة، ويستغل بنية التعليمات البرمجية المتعددة للعتاد (CPU Vectorization) لتسريع عمليات الجمع والقسمة التراكمية، مما يجعله قادراً على معالجة ملايين المستندات في أجزاء من الثانية وبأعلى درجات الموثوقية.

2. المفاهيم الأساسية لإطار عمل التجميع (Aggregation Framework)

2.1 مفهوم خطوط أنابيب التجميع (Aggregation Pipelines)

يعتمد إطار عمل التجميع في MongoDB على نموذج معماري مستوحى من مفهوم خطوط الأنابيب (Pipelines) التقليدي في أنظمة يونكس (Unix Pipes)، حيث تتدفق البيانات عبر سلسلة متتابعة من مراحل المعالجة المنطقية. يدخل المستند في بداية خط الأنابيب، ويخضع لتحويلات هيكلية، أو عمليات تصفية، أو حسابات رياضية داخل كل مرحلة، ثم يتم تمرير مخرجات المرحلة الحالية لتصبح مدخلات صريحة للمرحلة التي تليها مباشرة دون الحاجة إلى إنشاء هياكل تخزين وسيطة دائمة على القرص.

تتميز هذه المعمارية بكفاءة حسابية استثنائية وإدارة متطورة للذاكرة المؤقتة، حيث يعالج المحرك تدفق مستندات BSON في شكل كتل بيانات (Data Batches). يتيح هذا التصميم للمحرك تحسين خطة التنفيذ (Execution Plan Optimization) عبر إعادة ترتيب المراحل تلقائياً كلما أمكن ذلك، كدفع مراحل التصفية إلى البداية لتقليل عدد المستندات التي ستخضع للعمليات الحسابية اللاحقة. إن فهم خط الأنابيب بوصفه تحويلاً متسلسلاً للبيانات يعد الأساس الجوهري لكتابة استعلامات حساب المتوسط المعقدة التي تتطلب إعداد البيانات وتنقيتها قبل إخضاعها للمشغلات الإحصائية.

2.2 دور مرحلة التجميع الإحصائي $group

تعد المرحلة $group الركيزة المركزية في خطوط أنابيب التجميع عندما يتعلق الأمر بالتحليل الإحصائي وإجراء العمليات الحسابية التراكمية. تتمثل الوظيفة الجوهرية لهذه المرحلة في استقبال سيل المستندات المتدفقة وفصلها أو دمجها وفقاً لـ “معرف المجموعة” الذي يتم تعريفه في الحقل الإلزامي _id. تقوم هذه المرحلة بإنشاء مستند ناتج جديد لكل مجموعة مميزة، بحيث يحتوي المستند على المعرف التجميعي إلى جانب حقول محسوبة تمثل مخرجات العمليات التراكمية المنفذة على المستندات المنتمية لتلك المجموعة.

توفر مرحلة $group مجموعة واسعة من “المشغلات التراكمية” (Accumulators)، مثل $sum لحساب المجاميع، و$min و$max لتحديد القيم الطرفية، و$avg لحساب المتوسطات الحسابية. تعمل هذه المشغلات التراكمية في بيئة متوازية داخل الذاكرة، حيث تحتفظ بمتغيرات حالة داخلية (مثل مجموع القيم وعددها) وتحدثها تزامناً مع مرور كل مستند BSON يطابق معيار المجموعة، مما يمكنها من استخلاص النتائج الإحصائية بكفاءة متناهية وبأقل قدر ممكن من العمليات الحسابية المكررة.

2.3 الفروق التقنية بين الاستعلام العادي find() والتجميع aggregate()

يقتصر الاستعلام القياسي find() في MongoDB على استرجاع المستندات، وإجراء عمليات التصفية البسيطة، وإسقاط الحقول (Projection)، وترتيب النتائج، دون امتلاك القدرة على إجراء عمليات تحويل رياضي أو تجميع إحصائي مباشر داخل محرك قاعدة البيانات. إذا رغب المطور في حساب متوسط حقل ما باستخدام find()، فإنه يضطر إلى جلب جميع المستندات المطابقة عبر الشبكة إلى خادم التطبيق، ثم تشغيل حلقة تكرارية (Loop) لحساب المجموع وقسمته على العدد، وهو نمط تصميمي كارثي يؤدي إلى اختناق الشبكة واستنزاف ذاكرة الخادم في مجموعات البيانات الكبيرة.

في المقابل، صُمم التابع aggregate() ليوفر بيئة حوسبية متكاملة داخل المحرك، قادرة على تحويل البيانات وهيكلتها وإجراء العمليات الإحصائية المتقدمة قبل إرسال النتائج. يوضح الجدول التالي مقارنة معمارية دقيقة بين الاستعلام العادي والتجميع التراكمي:

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

3. المشغل الإحصائي $avg: البنية والخصائص التشغيلية

3.1 البنية النحوية (Syntax) لمشغل $avg

يعد المشغل $avg من المشغلات التراكمية التعبيرية الأساسية في MongoDB، ويستخدم لحساب المتوسط الحسابي لمجموعة من القيم الرقمية. تتبع الصيغة النحوية للمشغل قالباً قياسياً سواء تم استخدامه داخل مرحلة $group بوصفه مشغلاً تراكمياً، أو داخل مرحلة $project ومشتقاتها بوصفه مشغلاً تعبيرياً على المصفوفات الرقمية. يتم التعبير عنه كزوج من المفتاح والقيمة داخل كائن التجميع على النحو التالي:

{ $avg: <expression> }

حيث يمثل <expression> المسار المؤدي إلى الحقل الرقمي في المستند. وتعتمد MongoDB قاعدة صارمة في التمييز بين أسماء الحقول ومساراتها؛ إذ يتم استخدام علامة الدولار المفردة متبوعة باسم الحقل، مثل "$price" أو "$metrics.score"، للإشارة إلى أن المحرك يجب أن يستبدل هذا النص بالقيمة الفعلية المخزنة داخل الحقل لكل مستند قيد المعالجة. كما يدعم $avg تمرير تعبيرات حسابية مركبة كمدخلات، مثل ضرب حقلين ثم حساب متوسط الناتج، أو تمرير مصفوفة صريحة من الأرقام والتعبيرات.

3.2 السلوك الرياضي للمشغل $avg مع أنواع البيانات المختلفة

يتميز المشغل $avg بذكاء حسابي مدمج عند التعامل مع أنواع البيانات غير المتجانسة المخزنة في المستندات. عندما يواجه المحرك قيماً تنتمي إلى الأنواع الرقمية المعيارية (32-bit Integer, 64-bit Integer, Double)، فإنه يقوم بإجراء ترقية تلقائية للنوع (Type Promotion) لضمان دقة العمليات الحسابية وتجنب تجاوز سعة الأعداد الصحيحة (Integer Overflow)، مع تحويل الناتج النهائي عادة إلى عدد عشري مزدوج (Double) أو الاحتفاظ به كـ Decimal128 إذا كان أحد المدخلات ينتمي لهذا النوع عالي الدقة.

أما فيما يتعلق بالقيم غير الرقمية، مثل السلاسل النصية (Strings)، والقيم المنطقية (Booleans)، والمصفوفات، والكائنات، فإن المشغل $avg يتجاهلها تماماً أثناء الحساب؛ أي أنه لا يقوم بجمعها ولا يدرجها في مقام الكسر الحسابي (Denominator) الذي يمثل عدد العناصر. وينطبق هذا السلوك أيضاً على الحقول التي تحمل القيمة null أو الحقول غير الموجودة أصلاً في المستند، حيث يتم استبعادها بصمت دون التسبب في إيقاف الاستعلام أو إرجاع خطأ برمجي. وفي حال تم تطبيق $avg على مجموعة مستندات لا تحتوي على أي قيمة رقمية صالحة، فإن الناتج النهائي للمشغل يكون null تعبيراً عن عدم إمكانية حساب قيمة متوسطة لمجال فارغ.

4. الطريقة الأولى: حساب المتوسط الحسابي العام لكافة المستندات

4.1 التحليل النظري لتعيين _id إلى null

عند الرغبة في حساب المتوسط الحسابي الإجمالي لقيمة حقل ما عبر جميع مستندات المجموعة دون تقسيمها إلى فئات أو قطاعات، يتم استخدام أسلوب التجميع الشامل عن طريق تعيين الحقل المعرف _id في مرحلة $group إلى القيمة null أو قيمة ثابتة موحدة. يحمل هذا التعيين دلالة منطقية ومعمارية عميقة لمحرك الاستعلامات؛ فهو يوجه المحرك إلى إلغاء أي تجزئة للبيانات ودمج تدفق كافة المستندات الواردة في مسار تجميعي واحد متكامل.

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

4.2 كتابة الاستعلام القياسي للتجميع العام

تتم صياغة الاستعلام القياسي لحساب المتوسط الإجمالي عبر تمرير مصفوفة مراحل إلى الدالة aggregate() تحتوي على مرحلة $group واحدة. يتم تحديد حقل المخرجات باسم وصفي ذي دلالة واضحة (مثل avg_val أو averageScore) وربطه بالمشغل $avg الذي يشير بدوره إلى حقل القيمة المستهدف مسبوقاً بعلامة الدولار. يوضح المثال التالي التركيب العام للاستعلام:

db.collection.aggregate([ { $group: { _id: null, avg_val: {$avg: "$valueField" } } } ])

يقوم محرك قاعدة البيانات عند تنفيذ هذا الاستعلام بمسح المستندات في المجموعة collection، واستخلاص القيمة المخزنة في valueField لكل مستند. وإذا كان الحقل يحتوي على قيمة عددية، يتم إضافتها إلى المجموع الداخلي وزيادة عداد العناصر بمقدار 1. وبمجرد اكتمال قراءة كافة المستندات، يُنفذ المحرك عملية القسمة الإحصائية المعيارية:

المتوسط الحسابي = مجموع القيم الرقمية المستخلصة ÷ إجمالي عدد القيم الرقمية الصالحة

ويتم إرجاع النتيجة في هيكل وثيقة BSON وحيدة تمثل الحل الدقيق للمعادلة الرياضية.

5. الطريقة الثانية: حساب المتوسط المقسم حسب الفئات والمجموعات

5.1 التجميع الشرطي المعتمد على حقل الفئة (Grouping by Category)

في معظم التطبيقات التحليلية والعملية، لا يكون الهدف حساب المتوسط الشامل فحسب، بل استخراج المتوسطات المقسمة فئوياً وفقاً لقطاعات محددة (مثل حساب متوسط المبيعات لكل منطقة جغرافية، أو متوسط درجات الطلاب لكل مادة دراسية). يتم تحقيق هذا الهدف في MongoDB عبر إسناد مسار حقل التصنيف إلى المعرف _id في مرحلة $group، مثل _id: "$categoryField".

عند تمرير حقل تصنيف، يقوم محرك التجميع بإنشاء جدول تجزئة داخلي (Internal Hash Table) في الذاكرة، حيث يمثل كل مفتاح تصنيف فريد مجموعة تجميعية مستقلة تمتلك مجمعها التراكمي الخاص للمتوسط. بالإضافة إلى ذلك، يتيح إطار العمل استخدام “المفاتيح المركبة” (Compound Keys) عن طريق تعيين كائن يحتوي على حقول متعددة لحقل _id، مثل _id: { team: "$team", season: "$season" }، مما يمكن مهندسي البيانات من استخراج إحصائيات متقدمة متعددة الأبعاد في استعلام تجميعي فائق السرعة.

5.2 صياغة الاستعلام الفئوي وتفسير مخرجاته

تتم كتابة الاستعلام الفئوي بإسناد حقل التوزيع إلى _id، وتحديد العملية التراكمية للمتوسط عبر المشغل $avg على النحو التالي:

db.collection.aggregate([ { $group: { _id: "$groupField", avg_val: { $avg: "$valueField" } } } ])

عند تنفيذ هذا الاستعلام، يقوم المحرك بفرز المستندات ديناميكياً وتوجيه قيم valueField إلى المجموعة المطابقة لقيمة groupField. إذا تكررت الفئة في آلاف المستندات، تدمج قيمها في حساب المجمع الخاص بها فقط دون التداخل مع الفئات الأخرى. ينتج عن هذا الاستعلام مصفوفة من المستندات، يمثل كل مستند منها فئة مميزة تحتوي على معرف الفئة في _id والمتوسط الحسابي الخاص بها في avg_val، مما يلغي الحاجة إلى كتابة استعلامات منفصلة لكل فئة ويوفر كفاءة حوسبية قصوى.

6. دراسة تطبيقية مفصلة: إنشاء مجموعة البيانات وتطبيق الاستعلامات خطوة بخطوة

6.1 بناء مجموعة بيانات الفرق الرياضية (teams)

لتجسيد الجوانب النظرية في سياق عملي ملموس، سنقوم بإنشاء مجموعة بيانات تجريبية باسم teams تحاكي إحصائيات مباريات كرة السلة لفرق رياضية، مع تتبع حقول النقاط المسجلة (points) والمتابعات (rebounds). سنقوم بإدخال خمس وثائق منفصلة لتمثيل سيناريوهات التجميع المختلفة لفريقي ‘Mavs’ و ‘Spurs’:

يتم تنفيذ أوامر الإدخال في بيئة MongoDB على النحو التالي:

db.teams.insertOne({ team: 'Mavs', points: 30, rebounds: 8 })
db.teams.insertOne({ team: 'Mavs', points: 30, rebounds: 12 })
db.teams.insertOne({ team: 'Spurs', points: 20, rebounds: 7 })
db.teams.insertOne({ team: 'Spurs', points: 25, rebounds: 5 })
db.teams.insertOne({ team: 'Spurs', points: 25, rebounds: 9 })

تتضمن هذه المجموعة الآن مستندين لفريق ‘Mavs’ بإجمالي نقاط (30، 30) وثلاثة مستندات لفريق ‘Spurs’ بنقاط (20، 25، 25)، وهي عينة متوازنة لاختبار صحة العمليات الحسابية الشاملة والفئوية.

6.2 تطبيق الطريقة الأولى لحساب متوسط النقاط الإجمالي

لحساب متوسط النقاط المسجلة لجميع المباريات عبر كافة الفرق دون تمييز، ننفذ استعلام التجميع الشامل مع تعيين _id إلى null:

db.teams.aggregate([ { $group: { _id: null, avg_val: {$avg: "$points" } } } ])

عند تشغيل هذا الأمر، يقوم محرك MongoDB بمسح المستندات الخمسة واسترجاع النتيجة التالية مباشرة:

{ "_id" : null, "avg_val" : 26 }

وللتحقق من الدقة الحسابية للمحرك، يمكننا تطبيق المعادلة الرياضية يدوياً:

المجموع الكلي للنقاط = 30 + 30 + 20 + 25 + 25 = 130
العدد الكلي للمباريات (المستندات الصالحة) = 5
المتوسط الإجمالي = 130 ÷ 5 = 26

تطابق النتيجة المستخرجة من محرك MongoDB المعادلة الرياضية بدقة متناهية، مما يوضح كفاءة المشغل $avg في التلخيص الشامل للمستندات.

6.3 تطبيق الطريقة الثانية لحساب متوسط النقاط حسب الفريق

لحساب القوة الهجومية لكل فريق على حدة من خلال استخراج متوسط النقاط المخصصة لكل اسم فريق، نقوم بتنفيذ استعلام التجميع الفئوي بجعل _id مساوياً للمسار "$team":

db.teams.aggregate([ { $group: { _id: "$team", avg_val: { $avg: "$points" } } } ])

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

{ "_id" : "Mavs", "avg_val" : 30 }
{ "_id" : "Spurs", "avg_val" : 23.333333333333332 }

التحليل الحسابي للمخرجات الفئوية:

  • فريق Mavs: مجموع النقاط = 30 + 30 = 60، عدد المباريات = 2. المتوسط الحسابي = 60 ÷ 2 = 30.
  • فريق Spurs: مجموع النقاط = 20 + 25 + 25 = 70، عدد المباريات = 3. المتوسط الحسابي = 70 ÷ 3 = 23.333333333333332.

يعكس الناتج الرقمي لفريق ‘Spurs’ التمثيل الدقيق للأعداد الكسرية الدورية في معيار الفاصلة العائمة المزدوجة IEEE 754 المعمول به في محركات المعالجة الحاسوبية الحديثة.

7. معالجة البيانات المفقودة والقيم غير الرقمية وأنواع الحقول الاستثنائية

7.1 معالجة الحقول غير الموجودة أو التي تحمل قيمة null

في بيئات الإنتاج الواقعية، تتسم مجموعات البيانات في قواعد بيانات NoSQL بدرجة عالية من التباين في المخطط (Schema Polymorphism)، حيث قد تحتوي بعض المستندات على حقول رقمية مفقودة تماماً، أو حقول تم إسناد القيمة null إليها صراحة نتيجة خطأ في الإدخال أو عدم توفر البيان أثناء التسجيل. يقدم مشغل $avg حلاً هندسياً مدمجاً لهذه المعضلة من خلال تجاهل هذه المستندات بالكامل عند حساب كل من البسط والمقام، مما يحول دون تشويه المتوسط الحسابي بالقيم الصفرية غير المقصودة.

ومع ذلك، تفرض بعض متطلبات الأعمال اعتبار الحقول المفقودة أو التي تحمل القيمة null بمثابة قيمة صفرية صريحة (مثل احتساب غياب الطالب كدرجة صفر في حساب متوسط الفصل). في مثل هذه الحالات، يجب التدخل لإعادة هيكلة البيانات قبل تمريرها لمشغل $avg باستخدام المشغل الشرطي $ifNull داخل مرحلة $project أو ضمن تعبير $avg مباشرة على النحو التالي:

{ $avg: {$ifNull: [ "$score", 0 ] } }

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

7.2 التعامل مع الحقول ذات الأنواع المختلطة والتحويل القسري

تنشأ مشاكل حسابية جسيمة عندما يتم تخزين القيم الرقمية في هيئة سلاسل نصية (مثل تخزين الرقم "25" بدلاً من 25)، وهو خطأ شائع ينتج عن استقبال البيانات من واجهات برمجة التطبيقات (APIs) دون تدقيق الأنواع. وبما أن المشغل $avg يتجاهل السلاسل النصية تلقائياً، فإن هذه القيم ستسقط تماماً من الحساب مما يؤدي إلى نتائج إحصائية مضللة وغير دقيقة.

لمعالجة هذه الظاهرة، يوفر إطار عمل التجميع مشغلات التحويل النوعي الصريح مثل $toDouble، $toInt، و $toDecimal، بالإضافة إلى المشغل الشامل $convert الذي يتيح معالجة استثناءات فشل التحويل عبر تحديد قيم افتراضية عند مواجهة نصوص غير صالحة. يوضح الاستعلام التالي كيفية إجبار المحرك على تحويل السلاسل النصية إلى أرقام عشرية قبل حساب المتوسط:

db.collection.aggregate([ { $project: { convertedValue: {$toDouble: "$stringField" } } }, {$group: { _id: null, avg_val: { $avg: "$convertedValue" } } } ])

وكحل وقائي جذري، يُنصح دائماً بتفعيل قواعد التحقق من صحة المخطط (Schema Validation) على مستوى المجموعة في MongoDB باستخدام معايير JSON Schema لمنع إدخال أي مستند يحتوي على أنواع بيانات غير مطابقة للمواصفات الحسابية.

8. بناء خطوط أنابيب تجميع متقدمة: التصفية، الفرز، وإعادة التشكيل

8.1 التصفية المسبقة للبيانات باستخدام $match

تعتبر مرحلة $match بمثابة البوابة الأساسية لتحسين أداء خطوط أنابيب التجميع. تتمثل أفضل الممارسات الهندسية في وضع مرحلة $match في أقصى نقطة ممكنة في بداية خط الأنابيب، وذلك لتصفية المستندات غير الضرورية وتقليص حجم تدفق البيانات قبل وصولها إلى العمليات الحسابية المستهلكة للذاكرة في مرحلة $group.

يتيح دمج التصفية المسبقة حساب متوسطات شرطية دقيقة، مثل حساب متوسط نقاط اللاعبين الذين شاركوا لأكثر من 20 دقيقة، أو حساب متوسط المبيعات التي تمت خلال الربع الأخير من العام الحالي فقط. يوضح الاستعلام التالي تطبيق التصفية المسبقة لحساب متوسط النقاط للمباريات التي تجاوزت فيها نقاط الفريق 22 نقطة:

db.teams.aggregate([ { $match: { points: {$gt: 22 } } }, { $group: { _id: "$team", avg_val: { $avg: "$points" } } } ])

يقوم المحرك في هذا السيناريو باستبعاد المستندات التي لا تطابق الشرط مبكراً، مما يتيح له استخدام الفهارس المتاحة، ويسرع زمن تنفيذ الاستعلام بشكل كبير عبر تقليل عدد السجلات المعالجة في الذاكرة.

8.2 فرز وتحديد النتائج باستخدام $sort و$limit

في العديد من السيناريوهات التحليلية، لا تقتصر الحاجة على حساب المتوسطات فحسب، بل يمتد الهدف لاستخراج الفئات ذات الأداء الأعلى أو الأدنى، مثل تحديد “أفضل 5 فرق من حيث المعدل التهديفي” أو “الفروع الأقل كفاءة في استهلاك الطاقة”. لتحقيق ذلك، يتم تمرير مخرجات مرحلة $group إلى مرحلة الفرز $sort متبوعة بمرحلة تحديد العدد $limit.

يوضح الاستعلام التالي كيفية ترتيب نتائج المتوسطات تنازلياً واقتناص الفئة صاحبة المتوسط الأعلى:

db.teams.aggregate([ { $group: { _id: "$team", avg_points: { $avg: "$points" } } }, { $sort: { avg_points: -1 } }, {$limit: 1 } ])

يقوم المحرك بترتيب المستندات الملخصة الناتجة عن التجميع بناءً على الحقل المحسوب avg_points من الأعلى إلى الأدنى بفضل القيمة -1، ثم تقتطع مرحلة $limit: 1 المستند الأول فقط لتقديمه كنتيجة نهائية، مما يوفر استجابة برمجية موجزة ومباشرة للتطبيقات التحليلية.

8.3 إعادة هيكلة المخرجات وتدوير الأرقام باستخدام $project

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

يوفر المشغل الرياضي $round إمكانية تقريب المتوسطات العشرية الناتجة إلى عدد محدد من المنازل العشرية (Decimal Places) لتفادي ظهور متتاليات كسرية طويلة. يوضح المثال التالي كيفية تقريب المتوسط لمنزلتين عشريتين وإعادة هيكلة الحقول:

db.teams.aggregate([ { $group: { _id: "$team", raw_avg: { $avg: "$points" } } }, { $project: { _id: 0, team_name: "$_id", average_score: { $round: [ "$raw_avg", 2 ] } } } ])

يقوم هذا الاستعلام بإلغاء إظهار _id، وتسمية المعرف بـ team_name، وإرجاع القيمة المقربة في الحقل average_score، مما يرفع من جودة البيانات المنسقة وجاهزيتها للاستهلاك المباشر.

9. حساب المتوسطات في المصفوفات والمستندات المتداخلة (Embedded Documents & Arrays)

9.1 استخراج المتوسط لحقول المستندات الفرعية المتداخلة

تدعم قاعدة بيانات MongoDB تضمين كائنات كاملة ومستندات فرعية داخل المستند الرئيسي (Embedded/Nested Documents) للتعبير عن العلاقات الوثيقة بين الكيانات. وعند التعامل مع هذه البنى الشجرية، يتيح محرك التجميع استخدام “الترميز النقطي” (Dot Notation) للوصول المباشر إلى الحقول الرقمية المتداخلة على أي عمق داخل المستند وحساب قيمها المتوسطة بسلاسة تامة.

إذا افترضنا وجود مستندات تحتوي على الحقل الفرعي metrics.performance.rating، فإن حساب المتوسط العام لهذا الحقل المتداخل لا يتطلب تفكيك المستند، بل يتم عبر تمرير المسار النقطي مسبوقاً بعلامة الدولار إلى المشغل $avg مباشرة:

db.evaluations.aggregate([ { $group: { _id: "$department", avg_rating: { $avg: "$metrics.performance.rating" } } } ])

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

9.2 حساب المتوسط لعناصر المصفوفات الرقمية داخل كل مستند

يتكرر في تصميم قواعد البيانات الموجهة للمستندات تخزين قوائم من الأرقام داخل مصفوفة في المستند الواحد (مثل تخزين درجات الاختبارات السنوية لطالب في مصفوفة scores: [85, 90, 78, 92]). لحساب متوسط هذه المصفوفة، توجد طريقتان رئيستان تختلفان جذرياً في الأداء والسلوك المعماري:

الطريقة الأولى: الاستخدام المباشر لـ $avg داخل $project
يمكن لمشغل $avg العمل مباشرة كمشغل تعبيري (Expression Operator) يأخذ مصفوفة كمدخل داخل نفس المستند، ويقوم بحساب متوسط عناصرها على مستوى السجل الفردي دون الحاجة إلى مرحلة $group:

db.students.aggregate([ { $project: { name: 1, average_grade: {$avg: "$scores" } } } ])

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

الطريقة الثانية: استخدام تفكيك المصفوفات عبر $unwind
عند الرغبة في حساب المتوسط الإجمالي لعناصر المصفوفات عبر مجموعة واسعة من المستندات المنفصلة وتجميعها فئوياً وفق حقول أخرى، يتم اللجوء لمرحلة $unwind التي تقوم بتفكيك المصفوفة وتوليد مستند مستقل لكل عنصر فيها، ثم تمرير المستندات الناتجة إلى مرحلة $group:

db.students.aggregate([ { $unwind: "$scores" }, { $group: { _id: "$grade_level", overall_avg: { $avg: "$scores" } } } ])

يوضح الجدول التالي المقارنة التقنية بين النهجين لاختيار الأداة المثلى وفق متطلبات الاستعلام:

وجه المقارنة مشغل $avg المباشر على المصفوفة مرحلة $unwind متبوعة بـ $group
نطاق المعالجة داخل المستند الواحد (Single Document Scope). عبر مستندات متعددة (Cross-Document Aggregation).
استهلاك الذاكرة منخفض جداً ولا يولد مستندات مؤقتة. مرتفع نتيجة مضاعفة عدد المستندات في خط الأنابيب.
سرعة التنفيذ فائقة السرعة وقريبة من الأداء الخطي. أبطأ نسبياً وتتطلب موارد حوسبية إضافية.
حالة الاستخدام المثلى حساب متوسط درجات أو مؤشرات تخص السجل الفردي. حساب إحصائيات عامة تشمل عناصر مصفوفات متفرقة بين سجلات مختلفة.

10. تحليل الأداء واستراتيجيات الفهرسة لتحسين استعلامات $avg

10.1 دور الفهارس (Indexes) في تسريع عمليات التجميع

تلعب الفهارس دوراً حاسماً في تعزيز كفاءة استعلامات التجميع في MongoDB، على الرغم من أن مرحلة $group بحد ذاتها لا تستخدم الفهارس لحساب العمليات التراكمية بشكل مباشر إذا تطلبت مسح حقول المستندات. تظهر القوة الحقيقية للفهارس عند دمجها مع مراحل التصفية الأولية $match والفرز المسبق $sort، حيث يمكن للفهرس المركب (Compound Index) تصفية ملايين المستندات وتقليصها إلى بضعة آلاف قبل وصولها إلى مرحلة الحساب الرياضي.

في حالات متقدمة، يمكن تحقيق ما يُعرف بـ “الاستعلام المغطى” (Covered Query) عندما يحتوي الفهرس المركب على كل من حقل التصفية، وحقل التصنيف، وحقل القيمة الرقمية المطلوبة لحساب المتوسط (مثل فهرس على { status: 1, team: 1, points: 1 }). في هذا السيناريو، يستخرج محرك التجميع كافة البيانات العددية اللازمة للمتوسط مباشرة من شجرة الفهرس (B-Tree Index) المخزنة في الذاكرة الحية (RAM) دون الحاجة إلى قراءة المستندات الأصلية من وسائط التخزين، مما يؤدي إلى قفزة هائلة في سرعة الاستجابة.

ولتقييم خطة تنفيذ خط الأنابيب واكتشاف الاختناقات الأدائية، يوفر محرك MongoDB أداة التحليل التشخيصية explain(). يمكن تشغيلها بتمرير المعامل executionStats لمراقبة عدد المستندات المفحوصة مقارنة بالمستندات المرتجعة، والتأكد من استخدام الفهارس بالشكل الأمثل:

db.teams.explain("executionStats").aggregate([ { $match: { team: "Spurs" } }, {$group: { _id: "$team", avg_pts: {$avg: "$points" } } } ])

10.2 إدارة استهلاك الذاكرة وتخطي حد 100 ميغابايت

يفرض محرك MongoDB حداً صارماً على استهلاك الذاكرة العشوائية لكل مرحلة من مراحل خط أنابيب التجميع، حيث يُسمح للمرحلة الواحدة (مثل $group أو $sort) باستهلاك ما لا يزيد عن 100 ميغابايت (100MB RAM) من الذاكرة بشكل افتراضي. إذا تجاوز جدول التجزئة الداخلي للمجموعات هذا السقف أثناء معالجة ملايين السجلات ذات الفئات المتشعبة، يتوقف تنفيذ الاستعلام فوراً وتصدر قاعدة البيانات خطأ فشل المعالجة لتجاوز سعة الذاكرة المسموحة.

لتجاوز هذا القيد في استعلامات معالجة البيانات الضخمة (Big Data Pipelines)، تتيح MongoDB تفعيل الخيار allowDiskUse: true. يمنح هذا الخيار محرك التجميع الإذن لتفريغ البيانات الوسيطة الزائدة على وسائط التخزين الصلبة (Spilling to Disk) في مساحات تخزين مؤقتة، مما يسمح بإتمام العمليات التحليلية مهما بلغ حجم البيانات:

db.large_dataset.aggregate([ { $group: { _id: "$customer_id", avg_spending: { $avg: "$amount" } } } ], { allowDiskUse: true })

وعلى الرغم من أن هذا الخيار يضمن نجاح الاستعلام وعدم انهياره، إلا أنه يترتب عليه انخفاض ملحوظ في سرعة المعالجة مقارنة بالحوسبة المعتمدة كلياً على الذاكرة الحية نتيجة عمليات الإدخال والإخراج الإضافية على القرص (Disk I/O). لذا، يظل تحسين الاستعلامات والتصفية المسبقة بالفهارس هو الخيار الهندسي الأمثل.

11. الأخطاء الشائعة واستكشاف المشاكل البرمجية وإصلاحها

11.1 الأخطاء التركيبية الشائعة عند استخدام أسماء الحقول

يقع العديد من المطورين، وخاصة المبتدئين في التعامل مع MongoDB، في خطأ تركيبي شهير يتعلق بإسقاط علامة الدولار ($) عند الإشارة إلى الحقول داخل المشغلات التراكمية. إذا تمت كتابة الاستعلام على النحو التالي:

{ $group: { _id: "$team", avg_val: { $avg: "points" } } }

فإن المحرك يتعامل مع الكلمة "points" كسلسلة نصية ثابتة (Literal String) وليس كمسار حقل مستند. وبما أن $avg يتجاهل النصوص تلقائياً، فإن الاستعلام سينفذ بنجاح ولكن ستكون نتيجة المتوسط دائماً null لجميع المجموعات، وهو خطأ منطقي صامت يصعب أحياناً اكتشافه دون فحص دقيق للبنية النحوية.

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

11.2 مشاكل دقة الفاصلة العائمة (Floating-Point Precision)

عند إجراء العمليات الحسابية للمتوسطات على حقول رقمية عشرية قياسية (Double)، قد تظهر أحياناً نتائج غير متوقعة تتضمن ذيولاً كسرية طويلة وشاذة، مثل ظهور الناتج 23.333333333333332 بدلاً من 23.33، أو حدوث أخطاء طفيفة في الكسور العشرية الدقيقة (مثل 0.1 + 0.2 = 0.30000000000000004). تعود هذه الظاهرة إلى قيود معيار الفاصلة العائمة الثنائية IEEE 754 المستخدم في لغات البرمجة ومحركات قواعد البيانات، والذي يعجز عن تمثيل بعض الكسور العشرية بدقة ثنائية مطلقة.

في التطبيقات المالية، والأنظمة المحاسبية، ومنصات التداول المصرفي، تعتبر هذه الفروق الدقيقة غير مقبولة هندسياً. ولمعالجة هذا التحدي بشكل جذري، وفرت MongoDB نوع البيانات عالي الدقة Decimal128 (المعتمد على المعيار IEEE 754-2008). يضمن استخدام Decimal128 دقة حسابية تصل إلى 34 رقماً معنوياً، مما يقضي تماماً على أخطاء التقريب الحسابي أثناء حساب المتوسطات والمجاميع المالية الحساسة، مع إمكانية استخدام دالة $round في نهاية خط الأنابيب لتنسيق المخرجات.

12. مقارنة معمارية: حساب المتوسط في MongoDB مقابل قواعد البيانات العلائقية (SQL)

12.1 المقارنة البنائية بين AVG() في SQL ومرحلة $avg في MongoDB

تاريخياً، ارتبطت العمليات الإحصائية التجميعية بقواعد البيانات العلائقية من خلال لغة الاستعلامات البنيوية (SQL) واستخدام الدالة القياسية AVG() المقترنة بعبارة GROUP BY. وعلى الرغم من التشابه المنطقي في الهدف الحسابي النهائي، إلا أن هناك اختلافات جوهرية في البنية المعمارية وطريقة معالجة البيانات بين النموذجين.

يوضح الاستعلام التالي المقابلة التركيبية بين استعلام SQL القياسي وخط أنابيب التجميع في MongoDB لحساب متوسط النقاط لكل فريق للمباريات التي تجاوزت 20 نقطة:

استعلام SQL التقليدي:

SELECT team, AVG(points) AS avg_points FROM matches WHERE points > 20 GROUP BY team ORDER BY avg_points DESC;

استعلام خط أنابيب MongoDB المقابل:

db.matches.aggregate([ { $match: { points: {$gt: 20 } } }, { $group: { _id: "$team", avg_points: { $avg: "$points" } } }, { $sort: { avg_points: -1 } } ])

تتفوق MongoDB في قدرتها على تطبيق العمليات الحسابية على بنى البيانات الهرمية المعقدة (المستندات المتداخلة والمصفوفات) في نفس الاستعلام، وهو ما يتطلب في بيئات SQL إجراء عمليات ربط معقدة (Complex JOINs) تؤدي غالباً إلى تدهور حاد في أداء الاستعلامات الضخمة.

12.2 المعايير المثلى لاختيار استراتيجية التجميع في المشاريع الإنتاجية

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

  • العروض المجمعة عند الطلب (On-Demand Materialized Views): عند التعامل مع مجموعات بيانات تحتوي على عشرات الملايين من المستندات، يُفضل جدولة خطوط أنابيب تجميعية دورية تقوم بحساب المتوسطات وتفريغ النتائج في مجموعات بيانات مخصصة باستخدام المرحلة $merge أو $out، مما يحول زمن استرجاع المتوسط من دقائق إلى ميلي ثانية.
  • مجموعات السلاسل الزمنية (Time Series Collections): في تطبيقات إنترنت الأشياء (IoT) ومراقبة الأنظمة، توفر مجموعات السلاسل الزمنية في MongoDB تحسيناً ثورياً في سرعة حساب المتوسطات الزمنية بفضل ضغط البيانات الداخلي الموجه زمنياً.
  • التجميع في الذاكرة مقابل الأقراص: يجب الحرص دائماً على تصميم الفهارس وتصفية البيانات بالمرحلة $match لضمان بقاء خط الأنابيب ضمن حدود الذاكرة السريعة، وتجنب اللجوء إلى خيار allowDiskUse إلا في المهام التحليلية الخلفية غير الحساسة لزمن الاستجابة.

الخاتمة والخلاصة التنفيذية

يمثل حساب القيمة المتوسطة لحقل ما في MongoDB أحد أهم الركائز التحليلية في قواعد البيانات الحديثة. ومن خلال استعراضنا الشامل لإطار عمل التجميع (Aggregation Framework)، يتضح أن المشغل $avg ومرحلة $group يوفران معمارية حوسبية متطورة تتجاوز مجرد إجراء العمليات الحسابية التقليدية، لتشمل معالجة البيانات غير المتجانسة، والتنقل المرن داخل التراكيب الشجرية للمستندات والمصفوفات، وإدارة تدفق البيانات الضخمة بكفاءة وموثوقية عالية.

إن نجاح مهندسي البيانات ومطوري البرمجيات في استغلال هذه الإمكانيات يعتمد بشكل وثيق على فهم السلوك الرياضي للمشغلات، والالتزام بالقواعد البنيوية لأسماء ومسارات الحقول، وتطبيق أفضل الممارسات الهندسية التي تشمل التصفية المسبقة بالمرحلة $match، وبناء الفهارس المغطاة، وإدارة استهلاك الذاكرة عبر النوع Decimal128 والأداة explain(). وباتباع هذه المعايير المعمارية، يمكن بناء حلول برمجية وتحليلية فائقة السرعة قادرة على استخراج المؤشرات الإحصائية الحرجة وتوفير رؤى الأعمال القيمة بأعلى درجات الدقة والكفاءة.

References

اقتباس هذا المقال

looti, M. (2026, أغسطس 31). MongoDB: كيفية حساب القيمة المتوسطة لحقل. عرب سايكلوجي. https://arabpsychology.com/statistics/mongodb-how-to-calculate-average-value-of-field/
looti, Mohammed. “MongoDB: كيفية حساب القيمة المتوسطة لحقل.” عرب سايكلوجي, 31 أغسطس 2026, https://arabpsychology.com/statistics/mongodb-how-to-calculate-average-value-of-field/.
looti, Mohammed. “MongoDB: كيفية حساب القيمة المتوسطة لحقل.” عرب سايكلوجي. أغسطس 31, 2026. https://arabpsychology.com/statistics/mongodb-how-to-calculate-average-value-of-field/.