MongoDB $bucket Stage

Beginner
5 min read
Updated: Oct 2026
3 examples
Live preview

What Is $bucket?

$bucket is a MongoDB aggregation stage that groups documents into fixed numeric ranges (buckets) you define with boundaries. Use it for price tiers, score bands, and simple histograms.

Remember
$bucket = histogram with fixed edges

{
  $bucket: {
    groupBy: "$price",
    boundaries: [0, 50, 100, 500],
    default: "Other",
    output: { count: { $sum: 1 } }
  }
}

boundaries → [lower, upper) pairs
_id → inclusive lower bound
>= last boundary / null → default (or error)
$output → accumulators like $group
UsePrefer
Fixed ranges you choose$bucket
Auto-sized buckets from data$bucketAuto
Exact key groups$group

How to Use $bucket in MongoDB

Pick the field to band, list sorted boundaries, add a default catch-all, then choose output accumulators.

  1. Optionally filter first with $match.
  2. Set groupBy to the numeric field (e.g. "$price").
  3. Provide ascending boundaries (at least two values).
  4. Set default for outliers/nulls, and output for counts or averages.
PieceRequired?Role
groupByYesExpression that picks the value to band
boundariesYesSorted edges (≥ 2) defining ranges
defaultRecommendedCatch-all bucket id for out-of-range / null
outputNoAccumulators per bucket (default = count)

Pseudocode

Pseudocode
for each document:
    v = evaluate(groupBy)
    if v is null or v < firstBoundary or v >= lastBoundary:
        bucketId = default   // error if default missing
    else:
        find i where boundaries[i] <= v < boundaries[i+1]
        bucketId = boundaries[i]
    accumulate(bucketId, document)

emit one summary document per bucketId

Cheat Sheet

Keep these facts handy while building range histograms.

TopicDetail
TypeAggregation stage
Range shape[lower, upper) half-open intervals
Bucket _idInclusive lower bound (or default value)
Last boundaryExclusive cap; ≥ last → default
Boundaries ruleAscending; at least two values
Empty bucketsNot returned (only buckets with docs)
vs $bucketAutoYou pick edges vs MongoDB picks edges

Live Preview

Watch which bucket a price lands in for boundaries: [0, 50, 100, 500].

Simulates assignment for [0, 50), [50, 100), [100, 500) with default: "Other".

Result
price = 8
boundaries = [0, 50, 100, 500]

0 <= 8 < 50 → bucket _id: 0

{ _id: 0, range: 8 }

Worked Walkthrough — Price Bands

Boundaries [0, 50, 100, 500]. Place Headphones at price: 45 and Laptop at price: 899.

Calculation
boundaries: [0, 50, 100, 500]
ranges:
  [0, 50)    → _id: 0
  [50, 100)  → _id: 50
  [100, 500) → _id: 100
  >= 500 or null → default "Other"

Headphones price 45:
  0 <= 45 < 50 → _id: 0

Laptop price 899:
  899 >= 500 → default "Other"

(Not a bucket with _id: 500 — last edge is a cap.)

MongoDB Examples

We use a small products collection. First build a price histogram, then add richer per-bucket output. Run these in mongosh after inserting the sample data once.

Example 1 — Sample Data

Imagine six products with different prices, plus one gift card with null price for the default bucket.

mongosh
db.products.insertMany([
  { _id: 1, name: "Pen", price: 2, score: 78 },
  { _id: 2, name: "Notebook", price: 8, score: 85 },
  { _id: 3, name: "Headphones", price: 45, score: 91 },
  { _id: 4, name: "Keyboard", price: 75, score: 88 },
  { _id: 5, name: "Monitor", price: 220, score: 94 },
  { _id: 6, name: "Laptop", price: 899, score: 96 },
  { _id: 7, name: "Gift card", price: null, score: 70 }
]);

How the Data Is Stored in MongoDB

After the insert, MongoDB keeps seven documents in products. Run find() to see them:

mongosh
db.products.find()

Read the important fields like this:

FieldExamplePlain English
name"Pen"Product label
price2 … 899 / nullValue we will bucket
score78Review score (for custom output)

How It Works

  1. Insert once. insertMany adds seven product documents to products.
  2. Prices span several bands. Cheap items, mid-tier, expensive, and one null.
  3. find() shows the same shape. Ready for $bucket next.

Example 2 — Price Histogram

Goal: count products in [0, 50), [50, 100), and [100, 500), with outliers in Other.

mongosh
db.products.aggregate([
  {
    $bucket: {
      groupBy: "$price",
      boundaries: [0, 50, 100, 500],
      default: "Other",
      output: {
        count: { $sum: 1 }
      }
    }
  },
  { $sort: { _id: 1 } }
]);

How It Works

  1. Evaluate price. Each product gets a numeric (or null) value from Example 1.
  2. Match a half-open range. Pen/Notebook/Headphones → _id: 0; Keyboard → 50; Monitor → 100.
  3. Use default for the rest. Laptop (899 ≥ 500) and Gift card (null) → "Other".
  4. Count per bucket. $sum: 1 tallies documents in each band.

Example 3 — Custom Output

Same boundaries, but also compute average score and list product names in each bucket.

mongosh
db.products.aggregate([
  {
    $bucket: {
      groupBy: "$price",
      boundaries: [0, 50, 100, 500],
      default: "Other",
      output: {
        count: { $sum: 1 },
        avgScore: { $avg: "$score" },
        items: { $push: "$name" }
      }
    }
  },
  { $sort: { _id: 1 } }
]);

How It Works

  1. Same bucket assignment. Still using the ranges from Example 2.
  2. Accumulators per bucket. $avg and $push work like fields in $group.
  3. Dashboard-ready rows. Each band returns count, average score, and item names.
  4. Easy check. Bucket 0 should list Pen, Notebook, and Headphones.

Edge Cases & Pitfalls

Check these before using $bucket in production pipelines.

last

Last boundary is a cap

Values ≥ the last boundary are not a numbered bucket—they go to default (or error if omitted). Extend boundaries if you need a top band.

default

Always set default for real data

Nulls and outliers are common. Without default, $bucket can fail the whole pipeline.

empty

Empty buckets are omitted

If no document lands in a range, that bucket does not appear in the output. Fill gaps in your app if charts need every band.

sort

Boundaries must be ascending

Unsorted or duplicate edges cause errors. Keep numbers strictly increasing.

Performance Notes

ApproachCostBest for
$bucket aloneScan + assign per docFixed business ranges
$match then $bucketFewer docs to bandLarge collections
$bucketAutoExtra edge calculationExploratory histograms

Bucketing is usually cheaper than large sorts or joins. Filter early with $match when only a subset should enter the histogram.

Key Takeaways

  • Role: group docs into fixed numeric ranges.
  • Ranges: half-open [lower, upper); _id is lower.
  • default: catch outliers and nulls (recommended).
  • output: accumulators like $group fields.

One line: $bucket answers “how many documents fall into each numeric band I defined?”

Frequently Asked Questions

$bucket groups documents into fixed numeric ranges you define with boundaries. Each document’s groupBy value lands in one bucket, and each bucket returns one summary document.
groupBy (the expression to bucket on) and boundaries (an ascending array of at least two values). default is optional but recommended for out-of-range or null values. output is optional; if omitted, MongoDB counts documents with count: { $sum: 1 }.
Each adjacent pair forms a half-open range [lower, upper). The bucket _id is the inclusive lower bound. Values greater than or equal to the last boundary, or null/mismatched types, go to default when you set it—otherwise the stage errors.
$bucket uses boundaries you choose (business rules, age bands, price tiers). $bucketAuto picks edges from the data when you only know how many buckets you want.
$group buckets by exact key equality. $bucket buckets by numeric ranges. Use $group for categories; use $bucket for histograms and bands.
Yes. Put accumulator expressions in output—same idea as $group fields—such as $sum, $avg, and $push.

Next: $bucketAuto

Let MongoDB choose bucket edges from your data automatically.

$bucketAuto tutorial →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

9 people found this page helpful