iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Aggregation Overview

The aggregation pipeline runs documents through a series of stages — filter, transform, group, join, sort, lookup. It’s Mongo’s SQL-equivalent for analytics queries.

Real pipelines you will write

EXAMPLE
// 1) Top users by post count in the last 7 days
await db.posts.aggregate([
    { $match: { createdAt: { $gte: weekAgo() } } },
    { $group: { _id: '$userId', n: { $sum: 1 } } },
    { $sort:  { n: -1 } },
    { $limit: 20 },
    { $lookup: {
        from:         'users',
        localField:   '_id',
        foreignField: '_id',
        as:           'user',
    } },
    { $unwind: '$user' },
    { $project: { _id: 0, email: '$user.email', n: 1 } },
]).toArray();

// 2) Revenue per category — group + add fields
await db.orders.aggregate([
    { $match: { status: 'paid', createdAt: { $gte: monthStart() } } },
    { $unwind: '$items' },
    { $group: {
        _id:      '$items.category',
        revenue:  { $sum: { $multiply: ['$items.price', '$items.qty'] } },
        units:    { $sum: '$items.qty' },
        orders:   { $addToSet: '$_id' },
    } },
    { $addFields: { orderCount: { $size: '$orders' } } },
    { $project:   { orders: 0 } },
    { $sort:      { revenue: -1 } },
]).toArray();

// 3) Time-series rollups — group by day
await db.events.aggregate([
    { $match: { ts: { $gte: weekAgo() } } },
    { $group: {
        _id: { $dateTrunc: { date: '$ts', unit: 'day' } },
        n:   { $sum: 1 },
    } },
    { $sort: { _id: 1 } },
]).toArray();

// 4) Faceted query — multiple aggregations in one round trip
await db.products.aggregate([
    { $facet: {
        byCategory: [{ $group: { _id: '$category', n: { $sum: 1 } } }],
        byPriceBucket: [
            { $bucket: {
                groupBy:    '$price',
                boundaries: [0, 10, 50, 100, 500, 10000],
                default:    'over',
                output:     { n: { $sum: 1 } },
            } },
        ],
        topRated: [
            { $sort:  { rating: -1 } },
            { $limit: 5 },
            { $project: { name: 1, rating: 1 } },
        ],
    } },
]).toArray();

// 5) Use indexes!
// db.posts.createIndex({ userId: 1, createdAt: -1 })
// runs $match + $group above as index scans, not collection scans.

Why it matters

Always put $match first and use indexes that cover its fields. The optimiser pushes filters as early as possible, but you can’t skip the index design step.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
db.orders.aggregate([
    { $match: { status: 'paid' } },
    { $group: { _id: '$customer_id', total: { $sum: '$total' } } },
    { $sort: { total: -1 } },
]);
Try it Yourself »

Exercise

Aggregation operator to filter rows early.

{ : { status: 'paid' } }

Test yourself

Q1. The aggregation operator that bucket-sums by a key is…
Q2. SQL-style join in aggregation is…
Q3. Filter early in a pipeline with…

Discussion

Loading…