Skip to content

Interview Revision

Revision: Aggregation and $lookup

Pipeline order, $group, $unwind, $lookup — the questions that come up most.

IntermediateAbout 7 minutes
  1. 01Documents
  2. 02$match
  3. 03$group
  4. 04$sort
  5. 05$limit
  6. 06Result
  • find = stored docs. aggregate = computed answer. Do not reduce in Node.
  • Order is meaning. $match on status before $group. $match on totalSpent after.
  • `$group`. _id = key (null = one bucket). Accumulators only — no leftover status. $sum: 1 counts.
  • `$unwind`. One array element → one doc. Empty array drops the parent unless preserveNullAndEmptyArrays. Do not $sum amount after unwind (repeats the order total).
  • `$lookup`. Left join, always an array. No match = []. Index the foreign field. $match/$limit first. $unwind without preserve = inner join. $size: 0 = anti-join.
  • `$project` = new shape. `$set` = patch. Expressions: operands in arrays. $cond vs $match. $map/$filter in place; unwind to group across orders.
  • Dates. $dateToString %Y-%m in _id, timezone if IST. Filter status before grouping.
db.orders.aggregate([
  { $match: { status: { $ne: "cancelled" } } },
  { $group: { _id: "$userId", total: { $sum: "$amount" } } },
  { $sort: { total: -1 } },
  { $limit: 3 },
  { $lookup: { from: "users", localField: "_id", foreignField: "_id", as: "user" } },
  { $unwind: "$user" }
])
The shapes they ask you to write

Interview question

Why put $match as early as possible?

Think about it first.

Interview question

What does $lookup return, and what should you watch for?

Think about it first.

Practice