مرجع تنفيذ طلب البحث

توضّح هذه الصفحة ناتج طلب بحث تم تنفيذه باستخدام أداة Query Explain. لمعرفة كيفية تنفيذ طلب بحث باستخدام أداة Query Explain، يُرجى الاطّلاع على مقالة تحليل تنفيذ طلب البحث باستخدام أداة Query Explain.

المفاهيم الشائعة

تُستخدَم المفاهيم والمصطلحات الشائعة التالية في شجرة التنفيذ.

الصفوف والسجلّات

يُستخدَم المصطلحان صف وسجلّ للإشارة بشكل عام إلى مستند أو إدخال فهرس.

المتغيرات

$ تشير إلى متغيّر يتم إنشاؤه أو الإشارة إليه في شجرة التنفيذ. على سبيل المثال: $foo_1. تُستخدَم هذه المتغيّرات عادةً للإشارة إلى محتويات مستند أو قيمة تعبير تم تقييمه أثناء تنفيذ طلب بحث.

يمكن أن تظهر المتغيّرات الداخلية التالية في عُقد التنفيذ:

  • $__key__ - المعرّف هو معرّف داخلي لمستند. وهو معرّف مطلق وفريد يتضمّن المشروع وقاعدة البيانات والمسار الكامل للمستند.
  • $__id__ - المعرّف هو معرّف فريد لمستند ضمن مجموعته. وهو فريد ضمن مجموعة واحدة.
  • $rid - معرّف الصف هو معرّف داخلي لمستند في وحدة التخزين. وهو فريد ضمن مجموعة واحدة.

لنأخذ مثالاً يتم فيه استخدام عُقدة Compute لحساب __id__ من المستند __key__:

Compute
    |  $__id__1: _id($__key__)
    |  records returned: 1

القيود والنطاقات

تستخدم بعض عُقد الفحص سمات constraints وranges لوصف نطاق القيم التي يتم فحصها. تستخدم هذه السمات تنسيق شجرة النطاق الذي يحتوي على قائمة بالقيم. تتطابق هذه القيم مع القائمة المرتّبة للمفاتيح التي تظهر في تعريف الفهرس. على سبيل المثال، يتطابق النطاق الأول الذي يظهر في الشجرة، هنا (1..5]، مع القيود المفروضة على المفتاح الأول، هنا a، في القائمة المرتّبة للمفاتيح:

| index: type=CollectionGroupIndex, id=CICAgOjXh#EK, keys=[a ASC, b ASC, __key__ ASC]
| constraints: /
               |----(1..5]
                    |----[1L]

يشير كل مستوى من المسافة البادئة إلى القيد الذي يتم تطبيقه على المفتاح التالي في القائمة. تمثّل الأقواس المربعة نطاقًا شاملاً، بينما تمثّل الأقواس المستديرة نطاقًا غير شامل. في هذه الحالة، تتم ترجمة القيد إلى 1 < "a" <= 5 و "b" = 1.

في المثال التالي الذي يتضمّن فروعًا متعددة لـ a، يتطابق الشرط مع 1 < a <= 5 OR a = 10:

| constraints: /
               |----(1L, 5L]
               |----[10L]

متغيّرات المفاتيح

في بعض عُقد الفحص (مثل SequentialScan)، هناك قائمة بالمفاتيح كجزء من سمة index وسمة keys منفصلة في عُقدة Scan. تشير سمة keys في عُقدة Scan إلى اسم المتغيّر لكل مفتاح في تعريف الفهرس، بالترتيب. يمكن استخدام المتغيّرات للإشارة إلى قيم وقت التشغيل للحقل الذي تم فحصه في أعلى شجرة التنفيذ.

في المثال التالي، يتم ربط قيمة حقل user للمستند الحالي بالمتغيّر $user_1 وقيمة date_placed بالمتغيّر $date_placed_1.

index: type=CollectionGroupIndex, id=CICAgOjXh4EK, keys=[user ASC, date_placed ASC, __key__ ASC]
keys: [user ASC, date_placed ASC, __key__ ASC]

عُقد التنفيذ

يمكن أن تحتوي شجرة تنفيذ طلب البحث على العُقد التالية.

SeekingScan

تمثّل عملية فحص ديناميكية قد لا تكون فيها الصفوف المعروضة ضمن نطاق تسلسلي واحد من الفهرس، ويجب إجراء عمليات فحص متعددة ومختلفة لتلبية طلب البحث.

على سبيل المثال، يحتاج طلب البحث الذي يكون فيه a موجودًا وb يساوي 1 والذي يتم تنفيذه على فهرس ["a" ASC, "b" ASC] إلى فحص وعرض نطاق منفصل وغير تسلسلي محتملاً لكل قيمة مختلفة من a. ويكون هذا الإجراء أكثر كفاءة من عملية TableScan كاملة، ولكن أقل كفاءة من عملية SequentialScan واحدة على فهرس مركّب ["b" ASC, "a" ASC].

• SeekingScan
| constraints: /
               |----(-∞..+∞)
                    |----[1L]
| index: type=CollectionGroupIndex, id=CAE, keys=[user ASC, quantity ASC, __key__ ASC]
| keys: [user ASC, quantity ASC, __key__ ASC]
| properties: Selection { user }
| records returned: 1
| records scanned: 1

SequentialScan

تمثّل عملية فحص لنطاق ثابت وتسلسلي من الصفوف في وحدة التخزين يمكن إجراؤها في عملية قراءة واحدة.

يشير key ordering length إلى عدد المفاتيح التي يجب الاحتفاظ بها وعرضها بترتيب المفاتيح الأصلي. بالنسبة إلى مخطط [k1, k2, k3]، يعني طول ترتيب المفاتيح 0 أنّه يمكن للفحص عرض النتائج بأي ترتيب، ويعني 1 أنّه يتم الترتيب حسب k1، ولكن يمكن أن تظهر الصفوف التي لها القيمة k1 نفسها بأي ترتيب، ويعرض 3 المستندات بترتيب مرتّب تمامًا.

• SequentialScan
| index: type=CollectionGroupIndex, id=CAE, keys=[user ASC, date_placed ASC, __key__ ASC]
| key ordering length: 3
| keys: [user ASC, date_placed ASC, __key__ ASC]
| limit: 10
| properties: Selection { a }
| ranges: /
| records returned: 1
| records scanned: 1

UniqueScan

تمثّل عملية فحص لنطاق ثابت وتسلسلي من الصفوف في وحدة التخزين مع إزالة الصفوف المكرّرة في الذاكرة.

• UniqueScan
| index: type=CollectionGroupIndex, id=CAE, keys=[user ASC, date_placed ASC, __key__ ASC]
| keys: [user ASC, date_placed ASC, __key__ ASC]
| properties: Selection { a }
| ranges: /
          |----(-∞..+∞)
| records returned: 1
| records scanned: 1

IndexSeek

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

على سبيل المثال، يستخدم طلب البحث الذي يكون فيه user يساوي $user_id وdate_placed يساوي "2025-08-10" والذي يتم تنفيذه على فهرس ["user" ASC, "date_placed" ASC]، قيمة المتغيّر $user_id في وقت التشغيل والقيد "2025-08-10" على date_placed لتقييد نطاقات الفحص.

• IndexSeek
| index: type=CollectionGroupIndex, id=CAE, keys=[user ASC, date_placed ASC, __key__ ASC]
| fields: [$user_1 ASC, $date_placed_1 ASC, $rid ASC]
| key: $key_1
| filter: equal($user_1, $user_id) AND equal($date_placed_1, "2025-08-10")
| records returned: 1
| records scanned: 1

Fetch

تُعيد ربط معرّف الصف المقدَّم بمحتويات الصف الفعلية من وحدة التخزين الأساسية. يكون Fetch مطلوبًا إذا كانت عُقدة رئيسية (أو نتيجة طلب البحث النهائية) تتطلّب مجموعة فرعية من الحقول من المستندات.

• Fetch
|  order: PRESERVE_INPUT_ORDER
|  peak memory usage: 4.00 KiB (4,096 B)
|  properties: *
|  records returned: 1

LookupById

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

• LookupById
|  local_field: $localField_1
|  foreign_datasource: (default)#/**/foreign
|  output: $output_1

TableScan

تمثّل عملية فحص كاملة وغير مرتّبة لمجموعة. تُستخدَم عند تنفيذ طلب بحث بدون فهرس مرتبط.

يمكن أن يكون الترتيب STABLE أو UNDEFINED، ويشير STABLE إلى ترتيب محدّد.

• TableScan
|  order: STABLE
|  properties: *
|  records returned: 1
|  records scanned: 1
|  source: (default)#/**/collection

NestedLoopJoin

تُجري عملية ربط بين مجموعتَين من البيانات (يسار ويمين) من خلال تكرار كل صف من الإدخال الأيسر، وبالنسبة إلى كل صف أيسر، يتم فحص الإدخال الأيمن بحثًا عن صفوف مطابقة استنادًا إلى join_condition.

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

• NestedLoopJoin
|  join_type: LEFT_OUTER
|  join_condition: equal($left, $right)
|
└── • left tree
|     ...
└── • right tree
      ...

HashAggregate

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

• HashAggregate
|  aggregations: [sum($b_1) AS total]
|  groups: [$a_1]
|  peak memory usage: 4.00 KiB (4,096 B)
|  records returned: 0

StreamAggregate

تمثّل عُقدة تجميع متخصّصة تحتفظ بالحالة لمجموعة واحدة فقط في كل مرة، ما يقلّل من الحد الأقصى لاستخدام الذاكرة. تُستخدَم عندما تعرض العُقدة الفرعية الأساسية المجموعات بالتسلسل. على سبيل المثال، عند التجميع حسب القيم المميّزة لحقل أثناء استخدام فهرس في هذا الحقل.

• StreamAggregate
|  keys: [foo ASC, bar ASC]
|  properties: Selection { baz }
|  aggregations: [$sum($foo_1) AS baz]

MajorSort

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

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

• MajorSort
|  fields: [a ASC, b DESC]
|  limit: 10
|  peak memory usage: 4.00 KiB (4,096 B)
|  records returned: 1

Concat

تُسلسِل نتائج عُقد فرعية متعددة وتعرض النتيجة على العُقدة الرئيسية. لا تزيل هذه العُقدة النتائج المكرّرة التي تظهر في عُقد فرعية متعددة، ويكون ترتيب النتائج المعروضة غير محدّد.

• Concat
├── • Fetch
...
├── • Fetch

Compute

تُقيِّم مجموعة من التعبيرات، وتُعيِّن النتائج لمجموعة من المتغيّرات.

• Compute
|  $user_1: user
|  $full_name_1: str_concat($first_name_1, " ", $last_name_1)
|  $address_1: UNSET
|  records returned: 1

Filter

تعرض الصفوف بشكل انتقائي فقط إذا كانت تتطابق مع التعبير المقدَّم.

• Filter
|  expression: $eq(foo, "bar")
|  records returned: 1

RecordCount

تحسب عدد الصفوف التي تنتجها العُقدة الفرعية وتعرض العدد الحالي على المتغيّر المحدّد في سمة count.

• RecordCount
|  count: $row_number_1
|  records returned: 1

Values

تُنشئ تسلسلاً من القيم الحرفية للعمل عليها. تُستخدَم بشكل أساسي عند تقديم قائمة محدّدة من المستندات كإدخال لطلب بحث.

• Values
| expression: [{__key__=/col/1}, {__key__=/col/2}]

Unnest

تُزيل التداخل في القيمة التي تنتجها العُقدة الفرعية.

• Unnest
|  expression: foo AS unnested_foo

Limit

تحدّ من عدد الصفوف المعروضة على العُقدة الرئيسية.

• Limit
|  limit: 10
|  records returned: 1

Offset

تتخطّى عددًا محدّدًا من الصفوف التي تنتجها العُقدة الفرعية.

• Offset
|  offset: 10
|  records returned: 1