The $accumulator operator lets you write custom JavaScript inside $group when built-in accumulators like $sum or $avg are not enough—ideal for conditional totals, weighted math, or bespoke rollups.
01
Custom logic
Your own JS functions.
02
init + accumulate
Start state, update per doc.
03
merge
Combine parallel partial states.
04
finalize
Optional output transform.
05
vs $sum
Custom vs built-in speed.
06
lang: "js"
JavaScript required today.
Fundamentals
Definition and Usage
$accumulator is a MongoDB accumulator used in the $group stage. Instead of a fixed operation like addition or averaging, you supply JavaScript functions that define how state is initialized, updated for each document, merged across workers, and optionally finalized.
💡
Beginner tip
Think of $accumulator as a DIY reducer. init sets the starting value (like 0 for a sum). accumulate runs once per document and returns the new state. When MongoDB splits work across shards, merge combines two partial states into one.
Reach for built-in accumulators first—$sum, $avg, $push. Use $accumulator when your business rule needs JavaScript expressiveness that those operators cannot provide.
Foundation
📝 Syntax
$accumulator takes an object with JavaScript function definitions:
State holds intermediate values; finalize produces the user-facing average. Built-in $avg does this natively—this pattern shows how state objects work.
Conditional logic inside accumulate is a common reason to choose $accumulator. You could also use $sum with a $cond expression—compare both approaches for readability.
Compare
📋 $accumulator vs related options
Approach
Flexibility
Performance
Best for
$accumulator
Full JavaScript logic
Slower (JS per doc)
Custom rules, complex state
$sum / $avg
Fixed numeric ops
Fast native code
Totals, means, counts
$push / $addToSet
Array collection
Fast native code
Lists of values or unique sets
$sum + $cond
Conditional math
Usually faster than JS
Filtered totals without custom functions
$function stage
JS on whole docs
Per-document map
Row transforms, not group rollups
🧠 How $accumulator Works
1
init runs once per group
MongoDB calls init() when a new _id bucket starts and stores the returned state.
Init
2
accumulate per document
For each document, evaluate accumulateArgs, then call accumulate(state, ...args).
Loop
3
merge partial states
If work is split across processes, merge folds partial states into one combined state.
Merge
=
⚙
finalize & output
Optional finalize(state) shapes the final value written to the group result document.
Important
📝 Notes
$accumulator runs JavaScript—expect higher overhead than native accumulators like $sum.
merge is always required even on a standalone server; it defines how partial states combine.
lang: "js" is mandatory; functions must be valid JavaScript.
State can be any BSON-serializable value—numbers, objects, or arrays.
Prefer expression-based accumulators ($sum with $cond) when they cover your rule without custom code.
$accumulator is MongoDB’s escape hatch for custom group-level logic: you own initialization, per-document updates, merging, and optional final formatting through JavaScript.
Start with built-in accumulators whenever possible. When you truly need bespoke rollups, wire up init, accumulate, and merge carefully—then continue with $addToSet for unique value collection.
Try built-in accumulators and expression operators before writing custom JavaScript
Keep accumulate pure—return new state, avoid mutating external variables
Design merge to match how accumulate combines values
Use finalize only for output shaping, not heavy per-document work
Test on a small dataset first—debugging JS inside aggregation is harder than plain mongosh scripts
❌ Don’t
Replace simple $sum or $avg with $accumulator without a clear benefit
Forget merge—pipelines fail or return wrong results on sharded clusters without it
Store non-serializable values in state (functions, closures to outer scope)
Perform slow I/O or network calls inside accumulator functions
Assume finalize is required—it is optional
Summary
Key Takeaways
Knowledge Unlocked
Five things to remember about $accumulator
Use these points when built-in accumulators are not enough.
5
Core concepts
⚙01
Custom JS
Your group logic.
Basics
🚀02
init
Starting state.
Lifecycle
🔄03
accumulate
Per-document update.
Core
🔗04
merge
Combine partial states.
Sharding
⚡05
Prefer built-ins
$sum when possible.
Performance
❓ Frequently Asked Questions
$accumulator lets you define custom JavaScript functions inside $group to compute a value across documents. You control initialization (init), per-document updates (accumulate), combining partial results (merge), and optional final formatting (finalize).
Use it when built-in accumulators like $sum, $avg, or $push cannot express your logic—weighted averages, conditional totals, or multi-field custom rollups. Prefer built-ins when they fit; they are faster and simpler.
Inside the $group stage as an accumulator: { customTotal: { $accumulator: { init: function() { … }, accumulate: function(state, arg) { … }, accumulateArgs: ["$field"], merge: function(a, b) { … }, lang: "js" } } }.
merge combines accumulator state from parallel workers during sharded aggregation. Even on a single node you must provide merge—it defines how two partial states become one.
No. finalize is optional. Use it when the raw accumulated state needs a last transformation before output—for example converting an internal object into a single number.
$sum adds numeric field values with native code. $accumulator runs JavaScript you write, so it can implement any logic—but with higher overhead. Use $sum for simple totals.
Did you know?
The same custom-sum logic in $accumulator can often be written as { $sum: "$amount" } or { $sum: { $cond: [condition, "$amount", 0] } } without JavaScript—native operators compile to faster server code. Reserve $accumulator for state shapes and rules that expressions cannot express cleanly. See the official $accumulator docs.