Aggregation Operators and Expressions

Intermediate
13 min

Aggregation Operators and Expressions

Pipeline stages decide what happens to documents; expressions decide what values are computed along the way. The same expression language works inside $project, $set, $group, $match (through $expr) and even pipeline-style updates. After this lesson you will be able to compute derived fields with arithmetic, string, date and conditional operators, transform arrays without unwinding them, summarise groups with accumulators, and compare two fields of the same document in a query.

Expression Basics

An expression is a field path ("$price"), a literal (5, "paid"), a system variable ("$$NOW", "$$ROOT") or an operator object whose arguments are themselves expressions. Operators that take several arguments use an array:

javascript
{ $multiply: ["$price", "$qty"] } { $concat: ["$first", " ", "$last"] } { $gt: ["$stock", "$reorderLevel"] } // comparison between two fields

Arithmetic, Strings, Conversion and Conditionals

| Family | Operators | |---|---| | Arithmetic | $add, $subtract, $multiply, $divide, $mod, $round, $trunc, $abs, $ceil, $floor | | Strings | $concat, $toUpper, $toLower, $trim, $split, $substrCP, $strLenCP, $replaceAll, $regexMatch | | Comparison | $eq, $ne, $gt, $gte, $lt, $lte, $cmp | | Conversion | $toInt, $toDouble, $toString, $toDate, $toObjectId, $convert |

javascript
db.products.aggregate([ { $set: { slug: { $toLower: { $replaceAll: { input: "$name", find: " ", replacement: "-" } } }, priceInt: { $convert: { input: "$priceText", to: "int", onError: null, onNull: 0 } } } } ])

$convert with onError and onNull is the safe way to clean up inconsistently typed fields; the short forms such as $toInt throw on bad input.

Conditionals belong to the same toolbox:

javascript
{ $cond: { if: { $gte: ["$stock", 1] }, then: "in stock", else: "sold out" } } { $cond: [ { $eq: ["$status", "paid"] }, "$total", 0 ] } // array form { $ifNull: ["$nickname", "$name", "anonymous"] } // first non-null value { $switch: { branches: [ { case: ..., then: ... } ], default: "other" } }

$cond inside $sum is the standard way to count or total a subset within a group, for example paidTotal: { $sum: { $cond: [{ $eq: ["$status", "paid"] }, "$total", 0] } }.

Dates

javascript
{ $dateToString: { format: "%Y-%m-%d", date: "$placedAt", timezone: "Asia/Kolkata" } } { $dateTrunc: { date: "$placedAt", unit: "week", timezone: "Asia/Kolkata" } } // bucket by week { $dateDiff: { startDate: "$placedAt", endDate: "$$NOW", unit: "day" } } { $dateAdd: { startDate: "$placedAt", unit: "day", amount: 30 } } { $year: "$placedAt" }, { $month: "$placedAt" }, { $dayOfWeek: "$placedAt" }

Dates are stored in UTC; pass timezone whenever a report must follow local calendar days.

Arrays and Accumulators

javascript
db.orders.aggregate([ { $set: { itemCount: { $size: "$items" }, firstItem: { $first: "$items" }, bigLines: { $filter: { input: "$items", as: "i", cond: { $gte: ["$$i.qty", 5] } } }, lineTotals: { $map: { input: "$items", as: "i", in: { $multiply: ["$$i.price", "$$i.qty"] } } }, orderTotal: { $sum: { $map: { input: "$items", in: { $multiply: ["$$this.price", "$$this.qty"] } } } } } } ])

$filter, $map and $reduce operate on an array in place, which is usually cheaper than $unwind followed by $group. $in, $arrayElemAt, $slice, $concatArrays and $sortArray round out the family.

Inside $group the operators are accumulators: $sum, $avg, $min, $max, $count, $first, $last, $push, $addToSet, $mergeObjects, $stdDevPop, and the ranked $top, $bottom, $topN, $bottomN. $first and $last are only meaningful after a $sort.

Using Expressions in find() with $expr

Query operators compare a field with a constant; $expr lets a query use aggregation expressions, including comparisons between two fields:

javascript
db.products.find({ $expr: { $lt: ["$stock", "$reorderLevel"] } }) db.orders.find({ $expr: { $gte: [{ $size: "$items" }, 3] } })

Field-to-field comparisons cannot use an index; a $expr comparing a field with a constant can.

Common Mistakes

  • Forgetting the $ prefix, so "price" is a string literal and $multiply fails.
  • Summing strings. $sum ignores non-numeric values and returns 0 without warning; convert first.
  • Reporting in UTC by accident, shifting late-evening orders to the next day.
  • Using $first in $group without a preceding $sort, which returns an arbitrary document.
Quick Quiz
Question 1 of 3

What does `{ $concat: ["price", ": ", "$price"] }` produce for a document with `price: 10`?

Key Takeaways

  • Expressions combine field paths ("$field"), literals, variables ("$$NOW") and operator objects; multi-argument operators take arrays.
  • Arithmetic, string, comparison and $convert operators compute derived fields in $set and $project.
  • $cond, $ifNull and $switch add branching; $cond inside $sum totals a subset of a group.
  • Date operators accept a timezone; $dateTrunc is the idiomatic way to bucket by day, week or month.
  • $filter, $map and $reduce transform arrays in place, and $expr brings the same expressions into find().

Next lesson: Joins with $lookup and Flattening with $unwind — combine data from several collections and turn array elements into documents.

Aggregation Operators and Expressions - MongoDB | CodeYourCraft | CodeYourCraft