← back

Array Update
Operators

FILE  13_array_update_operators
TOPIC  $push · $pull · $pop · $addToSet · $pullAll · $each · $slice · $sort · Positional
LEVEL  Foundation
01
$push
Append one or more elements to an array
append

$push appends a value to an array field. If the field does not exist it creates a new array. Unlike $addToSet, $push allows duplicates.

Basic Usage

// Append a single tag
db.articles.updateOne(
  { title: "MongoDB Tips" },
  { $push: { tags: "database" } }
)

// Append an embedded document
db.users.updateOne(
  { _id: 1 },
  { $push: { addresses: { city: "Mumbai", pin: "400001" } } }
)

Push Multiple Values with $each

// Correct — $each pushes all three as separate elements
db.articles.updateOne(
  { title: "MongoDB Tips" },
  { $push: { tags: { $each: ["nosql", "backend", "cloud"] } } }
)

// WRONG — pushes the entire array as ONE nested element
db.articles.updateOne(
  { title: "MongoDB Tips" },
  { $push: { tags: ["nosql", "backend"] } }
)
// Before: tags: ["mongodb"]
// After:  tags: ["mongodb", ["nosql", "backend"]]  ← nested array!
WARN
Passing an array directly to $push inserts it as a single nested element — not individual items. Always use $each when pushing multiple values as separate elements.

Field Creation on First Push

// "tags" doesn't exist → $push creates it as a new array
db.posts.updateOne({ _id: 1 }, { $push: { tags: "first" } })
// Before: { _id: 1, title: "Post" }
// After:  { _id: 1, title: "Post", tags: ["first"] }
02
$push Modifiers
$each · $slice · $sort · $position
modifiers

When combined with $each, $push supports additional modifiers that control insertion position, array trimming, and post-push sorting. All modifiers require $each.

$slice — Maintain a Fixed-Size Array (Sliding Window)

// Keep only the last 5 notifications
db.notifications.updateOne(
  { userId: "u123" },
  {
    $push: {
      recent: {
        $each:  [{ msg: "New login", at: new Date() }],
        $slice: -5    // negative → keep from END (most recent)
      }
    }
  }
)
// Positive $slice → keep from the START
// Zero $slice    → empties the array after push

$sort — Sort After Pushing

// Push a score and keep array sorted descending
db.game.updateOne(
  { _id: "global" },
  {
    $push: {
      topScores: {
        $each: [{ user: "Karan", score: 9500 }],
        $sort: { score: -1 }
      }
    }
  }
)

// Sort a flat (primitive) array after push
db.col.updateOne(
  { _id: 1 },
  { $push: { nums: { $each: [5, 3, 8], $sort: 1 } } }
)
// $sort re-sorts the ENTIRE array, not just newly added elements
NOTE
$sort and $slice require $each. $sort sorts the entire array (including elements that were already there before the push), not just the newly inserted ones.

$position — Insert at a Specific Index

// Insert at the beginning (index 0)
db.col.updateOne(
  { _id: 1 },
  { $push: { arr: { $each: ["first"], $position: 0 } } }
)

// Insert before the last element (negative index)
db.col.updateOne(
  { _id: 1 },
  { $push: { arr: { $each: ["second-to-last"], $position: -1 } } }
)
// Without $position, elements are appended to the end by default

Combining Modifiers — Sorted Top-5 Leaderboard

// Atomically maintain a sorted top-5 list
db.game.updateOne(
  { _id: "global" },
  {
    $push: {
      topScores: {
        $each:  [{ user: "Alice", score: 12500 }],
        $sort:  { score: -1 },   // highest first
        $slice: 5                // keep only top 5
      }
    }
  }
)
// Execution order: push → sort → slice
// $slice positive keeps from the start, so top 5 after sort = highest 5

Modifier Summary

ModifierPurposeRequires $eachValue
$each Push multiple values as individual elements N/A — is the base Array of values
$slice Trim to N elements after push yes Integer (neg=from end, pos=from start, 0=clear)
$sort Sort entire array after push yes { field: 1|-1 } or 1|-1 for flat arrays
$position Insert at specific index yes Integer (0-indexed, negative from end)
03
$addToSet
Push only when value doesn't already exist
unique

$addToSet treats the array as a set — it only adds a value if it is not already present, preventing duplicates.

Basic Usage

// Add "React" only if not already in skills
db.users.updateOne(
  { name: "Priya" },
  { $addToSet: { skills: "React" } }
)
// If "React" already present → no change (modifiedCount: 0)
// If "React" absent → appended (modifiedCount: 1)

With $each — Multiple Unique Values

// Each value added only if not already present
db.users.updateOne(
  { name: "Priya" },
  { $addToSet: { skills: { $each: ["Node.js", "MongoDB", "React"] } } }
)
// "React" already in skills → skipped
// "Node.js" not in skills → added
// "MongoDB" not in skills → added

$push vs $addToSet

$push$addToSet
Allows duplicatesyesno
Supports $slice/$sortyesno
Supports $eachyesyes
Best forLogs, history, ordered listsTags, roles, permissions, skills
TIP
Use $addToSet when uniqueness is a business invariant. It eliminates the application-side read-check-write race condition and makes the uniqueness guarantee atomic.
04
$pull & $pullAll
Remove elements by value or condition
removal

$pull — Remove by Value or Condition

$pull removes all array elements that match the specified value or condition. The condition acts as a query against each element.

// Remove exact value (all occurrences)
db.articles.updateOne(
  { title: "MongoDB Tips" },
  { $pull: { tags: "cloud" } }
)

// Remove by condition — all scores below 50
db.students.updateOne(
  { name: "Rahul" },
  { $pull: { scores: { $lt: 50 } } }
)

// Remove embedded documents matching a field value
db.orders.updateOne(
  { _id: 1 },
  { $pull: { items: { sku: "ABC-001" } } }
)
// Removes ALL item objects where sku equals "ABC-001"
// Field order within the match condition is irrelevant

// Remove embedded docs using operators
db.orders.updateOne(
  { _id: 1 },
  { $pull: { items: { qty: { $lte: 0 } } } }
)
NOTE
When $pull targets an array of embedded documents, the condition works exactly like a MongoDB query — it supports all query operators ($gt, $in, $elemMatch, etc.). Field order in the match object is ignored.

$pullAll — Remove Specific Exact Values

$pullAll removes all occurrences of each value in a given list. Unlike $pull, it only does exact value matching — no operators.

// Remove multiple exact values atomically
db.articles.updateOne(
  { title: "MongoDB Tips" },
  { $pullAll: { tags: ["nosql", "backend"] } }
)
// Equivalent to two separate $pull operations but in one atomic write

$pull vs $pullAll vs $pop Comparison

$pull$pullAll$pop
Remove by conditionyesnono
Remove exact valueyesyes (multiple)no
Remove by positionnonofirst/last only
Removes all matches?yesyesN/A
05
$pop
Remove first or last element by position
positional

$pop removes exactly one element from an array — the first (-1) or the last (1). No other values are accepted.

// Remove the LAST element
db.logs.updateOne(
  { service: "api" },
  { $pop: { history: 1 } }
)

// Remove the FIRST element
db.queue.updateOne(
  { _id: 1 },
  { $pop: { tasks: -1 } }
)
// Useful for FIFO queue consumers: $push to end, $pop -1 from front
WARN
$pop strictly accepts only 1 or -1. Any other value throws an error. Applying $pop to an empty array is a no-op (no error). Applying it to a non-array field throws: "Cannot apply $pop to a non-array field".

FIFO Queue Pattern

// Enqueue: push to the end
db.queue.updateOne(
  { _id: "main" },
  { $push: { tasks: { job: "send-email", createdAt: new Date() } } }
)

// Dequeue: atomically get + remove the oldest task
const result = db.queue.findOneAndUpdate(
  { _id: "main", "tasks.0": { $exists: true } },
  { $pop: { tasks: -1 } },
  { returnDocument: "before" }   // document BEFORE the pop
)
const task = result?.tasks[0]   // first element = the one just removed
06
Positional Operators
$ · $[] · $[identifier] with arrayFilters
targeting

Positional operators let you update specific elements within arrays without knowing their exact index at query time.

$ — Update the First Matching Element

The $ operator is a placeholder for the index of the first array element matched by the filter query.

// Increment the score of the first exam where subject is "Math"
db.students.updateOne(
  { "exams.subject": "Math" },       // filter establishes which element
  { $inc: { "exams.$.score": 5 } }   // $ refers to that element's index
)
// Only the FIRST matching element is updated

// Set status on first matching item in an order
db.orders.updateOne(
  { _id: orderId, "items.sku": "SKU-10" },
  { $set: { "items.$.status": "shipped" } }
)
WARN
$ updates only the first matching element. The filter query must include a condition on the array field — MongoDB needs it to determine which element $ refers to. For updating all matches, use $[identifier] with arrayFilters.

$[] — Update ALL Elements

$[] targets every element in the array unconditionally.

// Add 10 marks to all exam scores for a student
db.students.updateOne(
  { name: "Priya" },
  { $inc: { "exams.$[].score": 10 } }
)
// Every element in exams gets score incremented by 10

// Set a flag on all items across many orders
db.orders.updateMany(
  { status: "draft" },
  { $set: { "items.$[].discount": 0 } }
)

$[identifier] — Filtered Positional Update

The most flexible positional operator. Updates only elements matching the arrayFilters condition, not just the first.

// Add 5 marks to exam scores below 40 (across all students)
db.students.updateMany(
  {},
  { $inc: { "exams.$[elem].score": 5 } },
  { arrayFilters: [{ "elem.score": { $lt: 40 } }] }
)
// "elem" is the identifier — matches elements where score < 40
// All matching elements updated, not just the first

// Update all "pending" items in a specific order
db.orders.updateOne(
  { _id: orderId },
  { $set: { "items.$[item].status": "processing" } },
  { arrayFilters: [{ "item.status": "pending" }] }
)

// Multiple arrayFilters for multiple array fields in one operation
db.col.updateOne(
  { _id: 1 },
  {
    $set: {
      "arr1.$[a].active": true,
      "arr2.$[b].active": false
    }
  },
  {
    arrayFilters: [
      { "a.role": "admin" },
      { "b.role": "guest" }
    ]
  }
)
TIP
The identifier name (elem, item, a) is arbitrary. Whatever name you use in the update path ($[elem]) must match the prefix used in the arrayFilters condition ("elem.score"). Identifiers must start with a lowercase letter.

Positional Operator Comparison

OperatorUpdatesArray Field in Query?Conditions?
$ First matched element required Via query filter only
$[] All elements Not required none
$[identifier] All elements matching arrayFilters Not required yes — arrayFilters
07
Quick Reference
All array update operators at a glance
reference
OperatorPurposeKey Notes
$pushAppend element(s)Allows duplicates; use $each for multiple values
$addToSetAppend only if absentNo duplicates; supports $each
$pullRemove by value or conditionRemoves ALL matches; full query operator support
$pullAllRemove specific exact valuesExact match only; no operators; removes all occurrences
$popRemove first (-1) or last (1)Strictly 1 or -1; no-op on empty array
$eachMultiple values for $push/$addToSetModifier — required for $slice/$sort/$position
$sliceTrim array after pushRequires $each; neg=end, pos=start, 0=clear
$sortSort array after pushRequires $each; sorts entire array
$positionInsert at specific indexRequires $each; default = end
$Update first matched elementNeeds array field in query
$[]Update all elementsNo filtering; blanket update
$[id]Update filtered elementsWith arrayFilters; most flexible

Common Patterns

// Tag system — no duplicates, easy add/remove
db.posts.updateOne({ _id: id }, { $addToSet: { tags: { $each: ["mongodb", "nosql"] } } })
db.posts.updateOne({ _id: id }, { $pull: { tags: "nosql" } })

// Sliding window log — keep last 10 events
db.activity.updateOne({ userId: "u1" }, {
  $push: {
    logs: {
      $each:  [{ action: "login", at: new Date() }],
      $sort:  { at: -1 },
      $slice: 10
    }
  }
})

// Sorted top-N leaderboard
db.leaderboard.updateOne({ _id: "global" }, {
  $push: {
    topScores: {
      $each:  [{ user: "Alice", score: 9900 }],
      $sort:  { score: -1 },
      $slice: 5
    }
  }
})

// Conditional bulk element update with arrayFilters
db.students.updateMany({}, {
  $inc: { "exams.$[e].score": 5 }
}, { arrayFilters: [{ "e.score": { $lt: 40 } }] })