MongoDB $push Accumulator

Beginner
⏱️ 9 min read
📚 Updated: Jul 2026
🎯 5 Examples
Aggregation

What You’ll Learn

The $push accumulator builds an array of every value while MongoDB groups documents—keeping duplicates and preserving processing order. It is ideal for event logs, score histories, and full lists per category.

01

Append all values

Duplicates included.

02

$group syntax

Accumulator with field or expression.

03

vs $addToSet

Full list vs distinct set.

04

Order matters

$sort before $group.

05

Sub-documents

Push object shapes per row.

06

$unwind pattern

Scalars from nested arrays.

Definition and Usage

$push is a MongoDB accumulator used inside the $group stage. For each group, it evaluates an expression on every input document and appends the result to an output array—without removing duplicates.

💡
Beginner tip

Think of $push as “collect everything I saw, in order” and $addToSet as “collect each different value once.” Both return arrays; only $push keeps repeats.

Typical uses include building order histories per customer, listing every score in a class, or gathering audit events per user. Pair with $sort before $group when chronological order matters, and with $size afterward to count entries.

📝 Syntax

$push appears as a field accumulator in $group:

mongosh
{
  $group: {
    _id: <expression>,
    <outputField>: { $push: <expression> }
  }
}

Syntax Rules

  • Stage — used only as an accumulator in $group (not in $match filters).
  • Expression — usually "$fieldName", a computed value, or an object literal for sub-documents.
  • Output — an array per group containing every appended value (empty array if no documents match).
  • Duplicates — same value may appear multiple times—unlike $addToSet.
  • Array fields — without $unwind, the whole source array is pushed as one element.
mongosh
db.orders.aggregate([
  {
    $group: {
      _id: "$customerId",
      productIds: { $push: "$productId" }
    }
  }
]);

⚡ Quick Reference

QuestionAnswer
Stage$group accumulator
Syntax{ $push: <expression> }
DuplicatesKept (every occurrence appended)
vs $addToSet$addToSet deduplicates; $push does not
OrderFollows document processing order—use $sort first
Common pairing$sort$group$project
Scalar field
{ $push: "$city" }

All cities per group

Sub-document
{ $push: {
  score: "$score",
  name: "$name"
} }

Array of objects

After unwind
{ $unwind: "$tags" }
{ $group: {
  _id: "$userId",
  tags: { $push: "$tags" }
} }

Every tag occurrence

Count entries
{ $size: "$items" }

In $project after push

🧰 Parameters

$push accepts one expression argument inside $group:

expression Required

Value appended to the array for each document in the group. Can be a field path, computed expression, or object literal for embedded documents.

{ $push: "$amount" }
{ $push: { id: "$id", ts: "$date" } }
_id Group key

The _id expression defines buckets. Each bucket gets its own growing array as documents are processed.

_id: "$department"
output field Array

Name your result field (e.g. allOrders). The value is always an array—even a single push produces a one-element array.

allOrders: { $push: … }
missing / null Important

If the expression resolves to null or a missing field, MongoDB still appends null. Filter with $match when nulls should be excluded.

$match: { city: { $ne: null } }

Examples Gallery

Sample sales records grouped by department—collect every city visit, compare with $addToSet, and build ordered sub-document arrays.

📚 Getting Started

Load sample documents and run a basic $push grouping.

Example 1 — Sample collection

Three sales records with repeating cities inside the same department:

mongosh
db.sales.insertMany([
  { department: "Engineering", city: "Austin",  productId: "P1", amount: 120 },
  { department: "Engineering", city: "Seattle", productId: "P2", amount: 85 },
  { department: "Engineering", city: "Austin",  productId: "P3", amount: 200 },
  { department: "Marketing",   city: "Boston",  productId: "P1", amount: 50 },
  { department: "Marketing",   city: "Boston",  productId: "P4", amount: 75 }
]);

How It Works

Engineering visited Austin twice—$push will list Austin twice in the output array, unlike $addToSet.

Example 2 — All cities per department

Group by department and push every city value:

mongosh
db.sales.aggregate([
  {
    $group: {
      _id: "$department",
      allCities: { $push: "$city" }
    }
  },
  { $sort: { _id: 1 } }
]);

/* Result:
[
  { _id: "Engineering", allCities: ["Austin", "Seattle", "Austin"] },
  { _id: "Marketing",   allCities: ["Boston", "Boston"] }
]
*/

How It Works

Every document contributes one array element. Duplicates reflect real repeat visits or orders—not collapsed into a set.

📈 Practical Patterns

Control order, compare accumulators, and push structured objects.

Example 3 — Ordered push with $sort

Sort by amount ascending before grouping so the pushed array follows lowest-to-highest order:

mongosh
db.sales.aggregate([
  { $sort: { department: 1, amount: 1 } },
  {
    $group: {
      _id: "$department",
      amounts: { $push: "$amount" }
    }
  }
]);

/* Engineering amounts (sorted asc):
   [85, 120, 200] */
*/

How It Works

$push does not sort on its own—it appends in pipeline order. Add $sort before $group when sequence matters.

Example 4 — $push vs $addToSet side by side

mongosh
db.sales.aggregate([
  {
    $group: {
      _id: "$department",
      allCities:    { $push: "$city" },
      uniqueCities: { $addToSet: "$city" }
    }
  },
  { $match: { _id: "Engineering" } }
]);

/* Engineering result:
{
  _id: "Engineering",
  allCities: ["Austin", "Seattle", "Austin"],
  uniqueCities: ["Austin", "Seattle"]
}
*/

How It Works

Use $push when every occurrence counts (transactions, events). Use $addToSet when you only need the distinct set (unique cities visited at least once).

Example 5 — Push sub-documents per order

Build an array of line-item objects for each customer:

mongosh
db.orders.insertMany([
  { customerId: "C1", productId: "P1", qty: 2, price: 10 },
  { customerId: "C1", productId: "P2", qty: 1, price: 25 },
  { customerId: "C2", productId: "P1", qty: 5, price: 10 }
]);

db.orders.aggregate([
  { $sort: { customerId: 1, productId: 1 } },
  {
    $group: {
      _id: "$customerId",
      lineItems: {
        $push: {
          productId: "$productId",
          qty: "$qty",
          lineTotal: { $multiply: ["$qty", "$price"] }
        }
      }
    }
  },
  {
    $project: {
      _id: 0,
      customerId: "$_id",
      lineItems: 1,
      orderCount: { $size: "$lineItems" }
    }
  }
]);

How It Works

Object expressions inside $push create an array of embedded documents—ideal for reconstructing order details per customer without a second query.

🧠 How $push Works

1

Documents enter $group

MongoDB buckets documents by the _id expression (e.g. department name).

Group
2

Evaluate expression

For each document in the bucket, compute the $push expression (field, formula, or object).

Eval
3

Append to array

The value is appended to the group’s array—duplicates are not filtered.

Append
=

Full array per group

Each group document contains one array with every collected value in processing order.

📝 Notes

  • $push is an accumulator—it belongs inside $group, not in $match.
  • For array fields, $unwind first unless you intentionally want whole-array elements pushed.
  • Large groups can produce very large arrays—consider $match, limits, or $firstN / $lastN when appropriate.
  • null values are appended if the expression resolves to null—filter early with $match when needed.
  • Do not confuse with the update operator $push on updateOne—same name, different context (single-document updates vs aggregation).
  • Previous topic: $minN. Next: $stdDevPop.

Conclusion

$push is the standard MongoDB accumulator when you need every value per group: full city visit lists, order line items, score histories, or audit trails. Sort before grouping when order matters, and use $size to count entries.

When duplicates should be removed, switch to $addToSet. For custom fold logic, see $accumulator.

💡 Best Practices

✅ Do

  • Use $push when every occurrence or sequence matters
  • Add $sort before $group for chronological or ranked arrays
  • Push object literals to capture multiple fields per document in one array entry
  • Combine with $size for entry counts per group
  • $unwind nested arrays when you need individual elements pushed

❌ Don’t

  • Use $push when you only need distinct values ($addToSet instead)
  • Assume output arrays are deduplicated automatically
  • Push unbounded arrays on huge groups without testing memory and document size limits
  • Mix up aggregation $push with update $push modifier syntax
  • Forget that pipeline order before $group defines append order

Key Takeaways

Knowledge Unlocked

Five things to remember about $push

Use these points whenever you build full-value aggregation arrays.

5
Core concepts
📄 02

$group only

Accumulator context.

Syntax
🔄 03

vs $addToSet

All vs distinct.

Compare
📅 04

$sort first

Control order.

Pattern
📝 05

Sub-docs

Object per push.

Advanced

❓ Frequently Asked Questions

$push appends the result of an expression to an array for each document in a $group bucket. Every value is kept—including duplicates—and the array grows as MongoDB processes documents in the group.
Inside the $group stage as an accumulator: { allCities: { $push: "$city" } }. It is not a query filter operator—it runs while grouping documents into buckets.
$push keeps every value including repeats. $addToSet only adds a value if it is not already in the array. Use $push for full histories and ordered lists; use $addToSet for distinct sets.
Yes. Values are appended in the order MongoDB processes documents within the group. Use $sort before $group when you need a specific ordering (e.g. chronological events).
$push adds the entire array as one element unless you $unwind the field first. To collect individual tags from nested arrays, $unwind before $group, then $push each scalar.
Yes. Use an object expression: { $push: { score: "$score", name: "$name" } } to build an array of embedded documents per group—useful for activity feeds and audit trails.
Did you know?

MongoDB also has an update operator named $push (with modifiers like $each and $slice) for adding items to an array on a single document. The aggregation accumulator you learned here runs inside $group across many documents. Same name, different pipeline stage. See the official $push aggregation docs.

Continue the Accumulators Series

Collect every value with $push, then learn $stdDevPop for statistical spread per group.

Next: $stdDevPop →

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.

5 people found this page helpful