MongoDB $accumulator Operator

Intermediate
6 min read
Updated: Oct 2026
3 examples
Live preview

What Is $accumulator?

$accumulator is a MongoDB accumulator for the $group stage. Instead of a fixed operation like $sum or $avg, you supply JavaScript functions that define how group state starts, updates, merges, and optionally finishes.

Remember
$accumulator = custom reducer inside $group

init       → starting state for each group
accumulate → update state once per document
merge      → combine partial states (shards / workers)
finalize   → optional last transform before output
lang       → "js" (required)
UsePrefer
Simple total or average$sum / $avg (faster, clearer)
Custom / multi-field logic$accumulator
Weighted or conditional rollups$accumulator

How to Use $accumulator in MongoDB

Four functions (plus lang) drive the lifecycle of each group.

  1. Put $accumulator inside $group on an output field.
  2. Write init to return the starting state.
  3. Write accumulate to fold each document into that state; pass fields with accumulateArgs.
  4. Write merge so two partial states combine correctly; add finalize only if you need a last transform.
KeyRequired?Role
initYesZero-arg function → initial state
accumulateYes(state, ...args) → newState
accumulateArgsYesExpressions passed after state (e.g. ["$amount"])
mergeYes(state1, state2) → combined
finalizeNo(state) → output value
langYesMust be "js"

Pseudocode

Pseudocode
for each group:
    state = init()
    for each document in group:
        state = accumulate(state, fields...)
    // if work was split:
    state = merge(partialA, partialB)
    output = finalize(state)   // optional
    emit { _id: groupKey, field: output }

Cheat Sheet

Keep these facts handy while writing a custom accumulator.

TopicDetail
Stage$group only (as an accumulator)
Minimal keysinit, accumulate, accumulateArgs, merge, lang
Sum patterninit: 0, accumulate: state + amount, merge: a + b
Avg patternState { sum, count }; divide in finalize
vs $sumUse $sum for plain totals — skip JS when possible
Sample checkfood amounts 100 + 150 + 50 → 300

Live Preview

Watch a custom sum update document by document. Sample collection: food 100, food 150, travel 200, food 50.

Simulates init → accumulate → finalize for a custom sum (same idea as the mongosh examples below).

Result
init → state = 0
+ 100 (food) → 100
+ 150 (food) → 250
+ 200 (travel) → 450
+ 50 (food) → 500
finalize → totalAmount = 500

{ _id: null, totalAmount: 500 }

Worked Walkthrough — Custom Sum

Same four receipts as the examples below. Watch the running total grow from 0 to 500.

Calculation
Documents:
  { amount: 100, category: "food" }
  { amount: 150, category: "food" }
  { amount: 200, category: "travel" }
  { amount:  50, category: "food" }

init():
  state = 0

accumulate(state, amount):
  0 + 100 → 100
  100 + 150 → 250
  250 + 200 → 450
  450 + 50 → 500

merge(a, b):
  if work split as [100,150] and [200,50]:
  merge(250, 250) → 500

Output:
  { _id: null, totalAmount: 500 }

MongoDB Examples

We use a tiny shopping list of four purchases. First we add up every amount. Then we average amounts per category. Run these in mongosh after inserting the sample data once.

Example 1 — Sample Data

Imagine four receipts in a transactions collection. Each document has an amount (how much you spent) and a category (what it was for).

mongosh
db.transactions.insertMany([
  { amount: 100, category: "food" },
  { amount: 150, category: "food" },
  { amount: 200, category: "travel" },
  { amount: 50, category: "food" }
]);

How the Data Is Stored in MongoDB

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

mongosh
db.transactions.find()

Read each document like this:

Field in MongoDBMeaningExample
amountMoney spent on this purchase100
categoryWhat the purchase was for"food"
Picture
Four purchases in one list:

  food   100
  food   150
  travel 200
  food    50

Next: add them with a custom $accumulator
  100 + 150 + 200 + 50 = 500

How It Works

  1. Insert once. The sample command adds 4 documents to transactions.
  2. MongoDB stores fields as written. Each document keeps the same field names and values you inserted.
  3. find() shows the same shape. What you inserted is what you get back—ready for $accumulator in the next examples.

Quick math you will see in the next examples: all amounts 100 + 150 + 200 + 50 = 500; food only 100 + 150 + 50 = 300 (average 100).

Example 2 — Add Every Amount Together

Goal: one number for the whole collection — total money spent. _id: null means “put every document in one big group.” Think of state as a running total that starts at 0 and grows.

mongosh
db.transactions.aggregate([
  {
    $group: {
      // null = one group for the whole collection
      _id: null,
      totalAmount: {
        $accumulator: {
          // 1) Start the running total at 0
          init: function () {
            return 0;
          },

          // 2) For each document: old total + this amount
          accumulate: function (state, amount) {
            return state + amount;
          },

          // 3) Tell MongoDB which field to pass as "amount"
          accumulateArgs: ["$amount"],

          // 4) If MongoDB splits work, add the partial totals
          merge: function (a, b) {
            return a + b;
          },

          // 5) Functions are written in JavaScript
          lang: "js"
        }
      }
    }
  }
]);

How It Works

  1. Start at zero. init returns 0 before any document is seen.
  2. Read each amount. accumulateArgs: ["$amount"] means “pass the amount field into accumulate.”
  3. Add one by one. First document: 0 + 100 = 100. Next: 100 + 150 = 250, and so on, until 500.
  4. Merge is a safety net. If MongoDB processes documents in parallel, merge adds those partial totals so the final answer is still correct.
  5. Same job, simpler tool. For a plain total you can write { $sum: "$amount" } instead. We use $accumulator here so you can see every step.

Example 3 — Average Amount per Category

Goal: average spend for food and for travel. An average needs two numbers (sum and count), so init returns a small object instead of a single number. At the end, finalize does sum / count.

CategoryAmountsSumCountAverage
food100, 150, 503003100
travel2002001200
mongosh
db.transactions.aggregate([
  {
    $group: {
      // One group per category value ("food", "travel", ...)
      _id: "$category",
      avgAmount: {
        $accumulator: {
          // Start with sum = 0 and count = 0
          init: function () {
            return { sum: 0, count: 0 };
          },

          // Add this document's amount; increase count by 1
          accumulate: function (state, amount) {
            return {
              sum: state.sum + amount,
              count: state.count + 1
            };
          },

          accumulateArgs: ["$amount"],

          // Combine two { sum, count } objects from parallel work
          merge: function (a, b) {
            return {
              sum: a.sum + b.sum,
              count: a.count + b.count
            };
          },

          // Last step: turn { sum, count } into one average number
          finalize: function (state) {
            if (state.count === 0) {
              return null;
            }
            return state.sum / state.count;
          },

          lang: "js"
        }
      }
    }
  },
  { $sort: { _id: 1 } }
]);

How It Works

  1. Split into buckets. _id: "$category" makes separate groups for food and travel.
  2. Track two values. init returns { sum: 0, count: 0 } so we can remember the total and how many documents we saw.
  3. Update both each time. For food: after three receipts, state is { sum: 300, count: 3 }.
  4. Merge keeps both fields. If two workers each saw some food receipts, we add their sums and their counts.
  5. Finalize does the division. 300 / 3 = 100 for food; 200 / 1 = 200 for travel. That number is what appears as avgAmount.
  6. Everyday shortcut. For a normal average, use { $avg: "$amount" }. This example shows why $accumulator is useful when you need custom multi-step state.

Edge Cases & Pitfalls

Check these before shipping a custom accumulator.

merge

Always implement merge

MongoDB requires it. Partial states from different workers must combine with the same math as accumulate.

Built-ins

Prefer $sum / $avg first

JavaScript accumulators are flexible but slower. Use them only when built-ins cannot express the rule.

null

Guard missing values

If amount can be null or missing, decide in accumulate whether to skip or treat as 0.

lang

Set lang: "js"

Omitting lang fails. Currently only JavaScript is supported.

Performance Notes

ApproachCostBest for
$sum / $avgNative, lower overheadSimple numeric rollups
$accumulatorJS per document (+ merge)Custom state or business rules

Both scan the grouped documents once. The difference is execution cost inside each update — keep custom JS short and push filtering into an earlier $match when you can.

Key Takeaways

  • Role: custom JavaScript reducer inside $group.
  • Lifecycle: init → accumulate → merge → finalize?
  • Args: pass fields with accumulateArgs; set lang: "js".
  • Default: use $sum / $avg unless you truly need custom state.

One line: $accumulator is your DIY $group reducer — write init, accumulate, and merge in JS.

Frequently Asked Questions

$accumulator lets you define custom JavaScript inside $group. You control init (start state), accumulate (update per document), merge (combine partial states), and optional finalize (shape the output).
Use it when built-ins like $sum, $avg, or $push cannot express your rule—weighted totals, multi-field state, or other custom rollups. Prefer built-ins when they fit; they are faster.
Yes. merge combines accumulator states from parallel workers (important on sharded clusters). Even on a single node you must provide it.
No. Use finalize only when the internal state needs a last transform—for example turning { sum, count } into an average.
Set lang to "js". Functions are written in JavaScript.
$sum is a native numeric total. $accumulator runs your JavaScript, so it can implement any logic but with more overhead. Use $sum for simple addition.

Next: $addToSet

Collect unique values into an array for each group.

$addToSet 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