$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)
Use
Prefer
Simple total or average
$sum / $avg (faster, clearer)
Custom / multi-field logic
$accumulator
Weighted or conditional rollups
$accumulator
Approach
How to Use $accumulator in MongoDB
Four functions (plus lang) drive the lifecycle of each group.
Put $accumulator inside $group on an output field.
Write init to return the starting state.
Write accumulate to fold each document into that state; pass fields with accumulateArgs.
Write merge so two partial states combine correctly; add finalize only if you need a last transform.
Key
Required?
Role
init
Yes
Zero-arg function → initial state
accumulate
Yes
(state, ...args) → newState
accumulateArgs
Yes
Expressions passed after state (e.g. ["$amount"])
merge
Yes
(state1, state2) → combined
finalize
No
(state) → output value
lang
Yes
Must 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 }
Reference
Cheat Sheet
Keep these facts handy while writing a custom accumulator.
Topic
Detail
Stage
$group only (as an accumulator)
Minimal keys
init, accumulate, accumulateArgs, merge, lang
Sum pattern
init: 0, accumulate: state + amount, merge: a + b
Avg pattern
State { sum, count }; divide in finalize
vs $sum
Use $sum for plain totals — skip JS when possible
Sample check
food amounts 100 + 150 + 50 → 300
Try it
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).
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).
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
Insert once. The sample command adds 4 documents to transactions.
MongoDB stores fields as written. Each document keeps the same field names and values you inserted.
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"
}
}
}
}
]);
Output
[ { _id: null, totalAmount: 500 } ]
How It Works
Start at zero.init returns 0 before any document is seen.
Read each amount.accumulateArgs: ["$amount"] means “pass the amount field into accumulate.”
Add one by one. First document: 0 + 100 = 100. Next: 100 + 150 = 250, and so on, until 500.
Merge is a safety net. If MongoDB processes documents in parallel, merge adds those partial totals so the final answer is still correct.
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.
Category
Amounts
Sum
Count
Average
food
100, 150, 50
300
3
100
travel
200
200
1
200
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 } }
]);
Split into buckets._id: "$category" makes separate groups for food and travel.
Track two values.init returns { sum: 0, count: 0 } so we can remember the total and how many documents we saw.
Update both each time. For food: after three receipts, state is { sum: 300, count: 3 }.
Merge keeps both fields. If two workers each saw some food receipts, we add their sums and their counts.
Finalize does the division.300 / 3 = 100 for food; 200 / 1 = 200 for travel. That number is what appears as avgAmount.
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.
Analysis
Performance Notes
Approach
Cost
Best for
$sum / $avg
Native, lower overhead
Simple numeric rollups
$accumulator
JS 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.
Remember
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.