מה זה?

Find (מהשיעור הקודם) מסנן, בדיוק כמו Where ב-LINQ — אבל מה עם “כמה הזמנות יש לכל לקוח” (קיבוץ) או “הבא לי הזמנה עם פרטי הלקוח המלאים” (JOIN בין Collections)? Aggregation Pipeline פותר את זה: שרשרת שלבים (בדיוק כמו Middleware Pipeline מיחידת ASP.NET Core!) שכל אחד מעבד את הנתונים ומעביר הלאה — $match מסנן, $group מקבץ, $lookup מבצע JOIN.

מילות מפתח שחשוב לזכור

• Aggregation Pipeline — שרשרת שלבים שכל אחד מעבד נתונים ומעביר לשלב הבא, בדיוק כמו Middleware Pipeline

• $match — שלב סינון, מקביל ל-Where/WHERE

• $group — שלב קיבוץ, מקביל ל-GroupBy/GROUP BY

• $lookup — שלב JOIN בין Collections שונות — הדרך של MongoDB לשלב נתונים מכמה Collections

• TTL Index (Time To Live) — Index מיוחד שגורם למסמכים “לפוג” ולהימחק אוטומטית אחרי זמן מוגדר

// Aggregation Pipeline — a chain of stages, just like Middleware
var pipeline = new BsonDocument[]
{
    new BsonDocument("$match", new BsonDocument("Status", "completed")),   // filter — only completed orders
    new BsonDocument("$group", new BsonDocument
    {
        { "_id", "$CustomerId" },
        { "TotalOrders", new BsonDocument("$sum", 1) },
        { "TotalSpent", new BsonDocument("$sum", "$Amount") }
    })
};

var results = await orders.Aggregate<BsonDocument>(pipeline).ToListAsync();
// For each customer: how many orders they completed, and how much they spent in total
// $lookup — JOIN between Collections
var pipelineWithJoin = new BsonDocument[]
{
    new BsonDocument("$lookup", new BsonDocument
    {
        { "from", "Customers" },        // the Collection being joined
        { "localField", "CustomerId" }, // field on Orders
        { "foreignField", "_id" },      // matching field on Customers
        { "as", "CustomerDetails" }     // name of the new field that will hold the result
    })
};
// Each order now contains a CustomerDetails array with the full customer details
// TTL Index — automatic deletion of old session tokens after an hour
var indexKeys = Builders<Session>.IndexKeys.Ascending(s => s.CreatedAt);
var indexOptions = new CreateIndexOptions { ExpireAfter = TimeSpan.FromHours(1) };
await sessions.Indexes.CreateOneAsync(new CreateIndexModel<Session>(indexKeys, indexOptions));
// Every Session document is deleted automatically an hour after its CreatedAt — no manual cleanup code

הסבר עיקרי

Pipeline הוא בדיוק אותו רעיון כמו Middleware — כל שלב ($match, $group, $lookup) מקבל את פלט השלב הקודם, מעבד, ומעביר הלאה. סדר השלבים משנה תוצאה וביצועים — $match לפני $group מסנן את הנתונים לפני שמבזבזים משאבים על קיבוץ מסמכים שיידחו ממילא, בדיוק כמו שסדר Middleware משנה מה קורה.

$lookup הוא ה"JOIN" של MongoDB, אך לא זהה ל-SQL JOIN — הוא מוסיף שדה חדש (CustomerDetails בדוגמה) שמכיל מערך של תוצאות תואמות, לא “משטח” את הנתונים לשורה אחת כמו JOIN רגיל ב-SQL. זה מתאים לטבע ה-Document — תוצאה מקוננת, לא שטוחה.

TTL Index פותר בעיה אמיתית — session tokens, קאשים זמניים, לוגים ישנים — כל אלה נתונים שרוצים שיימחקו אוטומטית אחרי זמן מוגדר, בלי לכתוב ולתזמן job נפרד לניקוי (נושא שיופיע גם ביחידת Python/ETL עם Scheduling). MongoDB עושה את זה ברמת ה-DB עצמו, פשוט על בסיס timestamp בשדה.

יתרונות

Aggregation Pipeline מבטא שאילתות אנליטיות מורכבות (סינון+קיבוץ+JOIN) בשרשרת שלבים קריאה; $lookup מאפשר לשלב נתונים מ-Collections שונות בלי לבנות אותם מראש; TTL Index מבטל צורך בקוד ניקוי ידני לנתונים זמניים.

חסרונות

Aggregation Pipeline עם הרבה שלבים מורכבים קשה יותר לקריאה ולדיבוג מ-SQL רגיל; $lookup פחות יעיל מ-JOIN אמיתי ב-SQL על נתונים גדולים מאוד; TTL Index מוחק בפועל רק בבדיקה תקופתית (לא מיידית ברגע התפוגה), כך שיש פער זמן קטן.

נקודות חשובות

• Aggregation Pipeline = שרשרת שלבים, כל אחד מעבד ומעביר הלאה — בדומה ל-Middleware

• $match≈WHERE, $group≈GROUP BY, $lookup≈JOIN (אך עם תוצאה מקוננת)

• סדר שלבים משפיע על תוצאה וביצועים — $match מוקדם ככל האפשר מומלץ

• TTL Index מוחק מסמכים אוטומטית לפי timestamp — שימושי ל-sessions/קאש זמני

טעויות נפוצות

• לשים $match בסוף הפייפליין במקום בהתחלה — מעבד נתונים מיותרים לפני הסינון

• לצפות ש-$lookup “ישטח” תוצאה כמו JOIN ב-SQL — היא מגיעה כמערך מקונן, דורש טיפול נוסף

• לשכוח שה-TTL לא מיידי — לא לסמוך עליו לתזמון מדויק לשנייה

סיכום

Aggregation Pipeline מעבד Documents דרך שרשרת שלבים — $match לסינון, $group לקיבוץ, $lookup ל-JOIN בין Collections — בדיוק כמו Middleware Pipeline שכבר מוכר. TTL Index מוחק מסמכים אוטומטית אחרי זמן מוגדר, שימושי לנתונים זמניים כמו sessions או קאש. יחד, אלה הכלים שהופכים MongoDB ממאגר-מסמכים פשוט למנוע שאילתות אנליטי מלא.

דוקומנטציה רשמית

MongoDB Manual — Aggregation Pipeline

MongoDB Manual — TTL Indexes


תרגילים

תרגיל 1 — match+group

המשימה: על Collection Orders עם שדות Status+Amount, כתבו Pipeline שמסנן רק Status="completed" ומקבץ לפי CustomerId עם סכום כולל.

בדיקה: התוצאה מציגה רק לקוחות עם הזמנות שהושלמו, כל אחד עם הסכום הכולל הנכון.

תרגיל 2 — TTL Index

המשימה: צרו Collection Sessions עם שדה CreatedAt, והגדירו עליה TTL Index שמוחק מסמכים אחרי דקה אחת (לצורך בדיקה מהירה).

בדיקה: מסמך שנוצר נעלם מה-Collection בערך דקה אחרי היצירה, בלי קוד מחיקה ידני.


פרויקט מסכם

המשימה: בנו דוח “לקוחות מובילים” עם Aggregation Pipeline מלא הכולל $lookup.

דרישות:

  1. Pipeline על Orders: $match להזמנות מושלמות, $group לפי CustomerId עם סכום כולל
  2. $lookup שמצרף את פרטי הלקוח המלאים מ-Collection Customers
  3. מיון התוצאה הסופית לפי סכום כולל יורד (אפשר עם שלב $sort נוסף בפייפליין, או מיון ב-C# אחרי השליפה)

בדיקה: הדוח מציג את הלקוחות המובילים עם שמם המלא (מ-$lookup) והסכום הכולל הנכון, ממוינים נכון.

מה בפרק הבא

בפרק הבא — Redis: מטמון (Cache) מהיר בזיכרון, ותבנית Cache-Aside שמאיצה שאילתות חוזרות בלי לוותר על עדכניות הנתונים.