Skip to content

Advanced Aggregation

$facet

Run several pipelines on the same input: totals, buckets and samples in one round trip.

AdvancedAbout 7 minutes

$facet runs several pipelines on the same input documents and returns one document whose fields are those results (each an array). One round trip for a dashboard: a total, a breakdown, a sample. Facets cannot see each other. They cannot $lookup each other's output.

db.products.aggregate([
  { $match: { inStock: true } },
  {
    $facet: {
      total: [{ $count: "n" }],
      byCategory: [
        { $group: { _id: "$category", n: { $sum: 1 } } },
        { $sort: { n: -1 } }
      ],
      sample: [
        { $limit: 3 },
        { $project: { _id: 0, name: 1, price: 1 } }
      ]
    }
  }
])
In-stock catalogue: how many, by category, three names
{
  total: [{ n: 40 }],
  byCategory: [{ _id: "electronics", n: 12 }, { _id: "books", n: 8 }, ...],
  sample: [{ name: "Mechanical Keyboard", price: 4500 }, ...]
}

$match before $facet so every branch is cheaper. A facet pipeline starts from that filtered set, not from the collection. Empty facet → { total: [], ... }. $count inside a facet still returns a one-element array. You cannot $unwind a sibling from inside another branch. Use $facet when the branches are independent; use one pipeline when the second stage needs the first's $group.

Interview question

What is $facet for, and what can a facet pipeline not do?

Think about it first.