בדף הזה מוסבר הפלט של שאילתה שהופעלה באמצעות 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: $eq($user_1, $user_id) AND $eq($date_placed_1, "2025-08-10")
| records returned: 1
| records scanned: 1
הבא
מבצעת הצטרפות חוזרת (back-join) של המזהה של השורה שסופקה לתוכן השורה בפועל מהאחסון הראשי. צריך להשתמש ב-Fetch אם צומת הורה (או התוצאה הסופית של השאילתה) דורש קבוצת משנה של שדות מהמסמכים.
• Fetch
| order: PRESERVE_INPUT_ORDER
| peak memory usage: 4.00 KiB (4,096 B)
| properties: *
| records returned: 1
TableScan
סריקה מלאה ולא מסודרת של אוסף. השגיאה הזו מופיעה כשמריצים שאילתה בלי אינדקס משויך.
הסדר יכול להיות STABLE או UNDEFINED, כאשר STABLE מציין סדר דטרמיניסטי.
• TableScan
| order: STABLE
| properties: *
| records returned: 1
| records scanned: 1
| source: (default)#/**/collection
עדכון
מבצעת איחוד בין שתי קבוצות של נתונים (input ו-map) על ידי איטרציה בכל שורה של input, ובכל שורה, סריקה והחזרת תוצאות מהצד של map.
הערך join_type מציין את סוג ההצטרפות. לדוגמה, LEFT_OUTER פירושו שכל השורות מ-input נכללות לפחות פעם אחת בפלט.
אם שורה ב-input לא מוצאת תוצאות בצד של map, היא עדיין תיכלל, עם ערכי null בעמודות מהצד של map.
• Apply
| join_type: LEFT_OUTER
|
└── • input tree
| ...
└── • map tree
...
HashAggregate
הטמעה של פעולות צבירה שמגובות בגיבוב (hash). כדי להשתמש בה, צריך להמיר את כל הקבוצה בזיכרון לפני החזרת התוצאה, והיא לא יכולה לחרוג ממגבלת הזיכרון של השאילתה.
• 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
מבצעת פעולת מיון על קבוצה קבועה של מאפיינים. כל הרשומות נשמרות בזיכרון בבת אחת, והערכים הממוינים מוחזרים לפי הסדר. גודל קבוצת המיון מוגבל על ידי מגבלת הזיכרון של השאילתה.
כשמציינים מגבלה נוספת, נעשה שימוש באלגוריתם מיון מסוג top-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
| expression: $eq(foo, "bar")
| records returned: 1
RecordCount
סופר את מספר השורות שנוצרו על ידי צומת הצאצא ופולט את הספירה הנוכחית למשתנה שצוין במאפיין count.
• RecordCount
| count: $row_number_1
| records returned: 1
ערכים
יוצרת רצף של ערכים מילוליים לעבודה. משמש בעיקר כשמספקים רשימה מוגדרת של מסמכים כקלט לשאילתה.
• Values
| expression: [{__key__=/col/1}, {__key__=/col/2}]
Unnest
מבטל את ההטמעה של הערך שנוצר על ידי צומת הצאצא.
• Unnest
| expression: foo AS unnested_foo
מגבלה
מגבילה את מספר השורות שמוחזרות לצומת ההורה.
• Limit
| limit: 10
| records returned: 1
הזזה
דילוג על מספר מוגדר של שורות שנוצרו על ידי צומת הצאצא.
• Offset
| offset: 10
| records returned: 1