Exercise 4 — Expressions Deep Dive

Topics: Arithmetic, comparison, boolean, control flow ($cond, $switch), and array operators ($map, $filter, $reduce)

Reading before you start: 08 - Expressions Overview, 09 - Arithmetic Operators, 10 - Comparison Operators, 11 - Boolean and Control Flow Operators, 12 - Array Operators

Database: aggregation_lab — see Setup - Database


Part A — Arithmetic and Comparison Expressions

Task A1

For each project, compute a field budgetUtilization as (actualCost / estimatedCost) * 100. Also compute budgetRemaining as estimatedCost - actualCost. Project title, budgetUtilization, and budgetRemaining.

Hint: Use $divide and $multiply nested together for the percentage.

Task A2

For each employee, compute yearsOfExperience as (2025 - year_of_hire). Since we can’t easily extract the year from a date, approximate by computing salary / 5000 and call it salaryTier instead. Also compute doubledSalary as salary * 2.

Task A3

For each order, unwind items and compute lineTotal as price * quantity. Then compute discountedTotal as lineTotal * 0.9 (10% discount). Project product, lineTotal, and discountedTotal.

Task A4

For each task, add a field isHeavy using $gt to check if hours > 15. Also add cmpTo10 using $cmp to compare hours with 10.

Project title, hours, isHeavy, and cmpTo10.


Part B — Boolean and Control Flow

Task B1

For each employee, use $cond to add a field salaryCategory:

  • If salary >= 60000"SENIOR"
  • Otherwise → "STANDARD"

Project name, salary, and salaryCategory.

Task B2

For each project, use $cond to add a field overBudget:

  • If actualCost > estimatedCosttrue
  • Otherwise → false

Task B3

For each employee, use $switch to add a field experienceLevel:

  • If role is "INTERN""JUNIOR"
  • If role is "DEVELOPER""MID"
  • If role is "LEAD" or "MANAGER""SENIOR"
  • Default → "OTHER"

Hint: For the LEAD/MANAGER case, you can use $in inside $switch to check if the role is in a list, or you can have two separate case branches.

Task B4

For each task, use $cond with $and to add a field urgent:

  • If the task is "TODO" and hours >= 15true
  • Otherwise → false

Task B5

For each order, use $switch to add a field statusLabel:

  • "DELIVERED""Completed"
  • "SHIPPED""On the way"
  • "PROCESSING""Being prepared"
  • "CANCELLED""Cancelled"
  • Default → "Unknown"

Part C — Array Operators

Task C1

Use $filter on the employees collection to add a field highRatings that contains only ratings greater than or equal to 4 from the ratings array.

Project name and highRatings.

Task C2

Use $map on the employees collection to add a field ratingsPlusOne where each rating in the ratings array is increased by 1.

Project name, ratings (original), and ratingsPlusOne.

Task C3

Use $filter on the projects collection to add a field completedMilestones that only includes milestones where completed is true.

Then add a field completedCount using $size on completedMilestones.

Project title, completedCount, and completedMilestones.

Task C4

Use $reduce on the employees collection to add a field skillString that concatenates all skills into a single comma-separated string.

Hint:

$reduce: {
  input: "$skills",
  initialValue: "",
  in: {
    $cond: [
      { $eq: ["$$value", ""] },
      "$$this",
      { $concat: ["$$value", ", ", "$$this"] }
    ]
  }
}

Project name and skillString.

Task C5

For each order, use $map to transform the items array so that each item gets a new field lineTotal (price * quantity):

$map: {
  input: "$items",
  as: "item",
  in: {
    product: "$$item.product",
    quantity: "$$item.quantity",
    price: "$$item.price",
    lineTotal: { $multiply: ["$$item.price", "$$item.quantity"] }
  }
}

Project _id, date, and the transformed items.


Part D — Combining Expressions with Stages

Task D1

Find all employees where the average of their ratings is above 4.0.

  1. Add a field avgRating using $avg on ratings
  2. Match where avgRating > 4 (you’ll need $expr with $gt — or just use $match with a regular comparison on the computed field)
  3. Project name, avgRating
  4. Sort by avgRating descending

Task D2

For each project:

  1. Add teamSize, milestoneCount, completedMilestones (filtered array), completedCount
  2. Add completionRate computed as (completedCount / milestoneCount) * 100
  3. Use $cond to add onTrack: true if completionRate >= 50, else false
  4. Project title, status, teamSize, completionRate, onTrack
  5. Sort by completionRate descending

Task D3

For each order:

  1. Use $map to add lineTotal to each item in the items array
  2. Use $addFields to add orderTotal as the $sum of all items.lineTotal values after the map — or use $reduce to sum them
  3. Use $cond to add orderSize: "LARGE" if orderTotal > 500, else "SMALL"
  4. Project date, status, orderTotal, orderSize
  5. Sort by orderTotal descending

Part E — Creative Challenge

Task E1

Employee scoring system: Design a pipeline that computes a “performance score” for each employee using any combination of expressions. Your score formula might consider: average rating, number of skills, salary relative to department average, etc. Be creative and explain your formula in a comment.

Task E2

Smart log analysis: Using the log_entries collection, write a pipeline that:

  • Adds a field severity using $switch based on level (ERROR=3, WARNING=2, INFO=1)
  • Filters to only entries with severity >= 2
  • Groups by service, computing totalSeverity and entryCount
  • Sorts by totalSeverity descending

This mimics a real monitoring dashboard that highlights the most problematic services.

Task E3

Order intelligence: Create a pipeline on orders that transforms each order into a “receipt” format:

  • Each item should have its lineTotal computed (use $map)
  • The order should have orderTotal (sum of line totals)
  • Add itemCount (number of items)
  • Use $cond to add freeShipping: true if orderTotal > 1000
  • Project everything cleanly and save to a collection order_receipts

Reflection:

  • How does $map differ from $unwind? When would you prefer one over the other?
  • Why is $filter more efficient than $unwind + $match for filtering array elements?
  • What are $$value and $$this in $reduce, and how do they change as the reduction progresses?