# MongoDB Aggregation Pipeline ו-TTL Index📖 שיעור

## מה זה?

`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 מיוחד שגורם למסמכים "לפוג" ולהימחק אוטומטית אחרי זמן מוגדר

```csharp
// 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
```

```csharp
// $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
```

```csharp
// 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](https://www.mongodb.com/docs/manual/core/aggregation-pipeline/)

[MongoDB Manual — TTL Indexes](https://www.mongodb.com/docs/manual/core/index-ttl/)

---

## תרגילים

### תרגיל 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 שמאיצה שאילתות חוזרות בלי לוותר על עדכניות הנתונים.
