3.2 Array Update Operators & Positional Modifiers

Key Takeaways

  • $push appends elements to an array and can be combined with $each, $position, $slice, and $sort modifiers.
  • $addToSet adds values to an array only if they do not already exist, enforcing set uniqueness without altering existing duplicates.
  • $pop removes either the first element ({ $pop: { array: -1 } }) or the last element ({ $pop: { array: 1 } }), while $pull removes all elements matching a specified value or query condition.
  • The first-match positional operator ($) targets the first array element that matched the query filter.
  • The all-positional operator ($[]) updates every element in the array, while the filtered positional operator ($[<identifier>]) targets elements matching conditions defined in arrayFilters.
Last updated: September 2026

3.2 Array Update Operators & Positional Modifiers

Arrays are first-class data structures in MongoDB documents. Rather than retrieving an entire document, modifying an array in application memory, and writing it back, MongoDB provides expressive array update operators and positional syntax. These operators allow developers to append, insert, sort, slice, deduplicate, and conditionally update specific elements in-place with single-document atomicity.


1. Array Addition Operators: $push and $addToSet

1.1 The $push Operator

$push appends a specified value to an array. If the target array field does not exist, $push creates the array with the specified element.

// Appends a single string to the tags array
db.articles.updateOne(
  { _id: 501 },
  { $push: { tags: "database" } }
);

1.2 Advanced $push Modifiers

When combined with modifiers, $push becomes a powerful streaming tool capable of maintaining bounded, sorted arrays (such as top-N leaderboards or fixed-size audit logs). When using modifiers, the update payload must pass an object containing $each.

ModifierRole & UsageConstraint
$eachAppends multiple elements to the target array in a single operation.Required whenever any other modifier ($position, $slice, $sort) is used.
$positionSpecifies the exact 0-based index at which elements should be inserted.Must be used with $each. $position: 0 prepends elements to the front of the array.
$sliceLimits the total number of array elements after the push.Must be used with $each. Negative number (e.g., -5) retains the last N elements. Positive number (e.g., 5) retains the first N elements. 0 empties the array.
$sortSorts all elements in the array after insertion.Must be used with $each. Accepts 1 (ascending), -1 (descending), or a sort specification document for embedded documents (e.g., { score: -1 }).

The Fixed-Size Rolling Buffer Pattern

A common architectural pattern is maintaining a rolling window of the 5 most recent temperature readings, sorted by timestamp descending:

db.sensors.updateOne(
  { sensorId: "TEMP-ROOM-A" },
  {
    $push: {
      readings: {
        $each: [
          { temp: 22.4, recordedAt: new Date("2026-09-02T10:00:00Z") },
          { temp: 22.8, recordedAt: new Date("2026-09-02T10:05:00Z") }
        ],
        $sort: { recordedAt: -1 }, // Sort all elements by recordedAt descending
        $slice: 5                  // Keep only the top 5 elements
      }
    }
  }
);

Evaluation Order: Regardless of the order modifiers are written in the update document, MongoDB always evaluates $push modifiers in this strict internal order:

  1. $position (insert elements at the given index)
  2. $sort (sort the array)
  3. $slice (trim the array to the boundary length)

1.3 The $addToSet Operator

$addToSet adds an element to an array only if the value does not already exist in the array, treating the array like a mathematical set.

// Adds 'security' only if not present; ignores if already present
db.users.updateOne(
  { _id: 88 },
  { $addToSet: { roles: "security" } }
);

// Adding multiple unique elements using $each
db.users.updateOne(
  { _id: 88 },
  {
    $addToSet: {
      roles: { $each: ["auditor", "admin", "security"] }
    }
  }
);

Document Matching Semantics in $addToSet

When storing embedded documents in an array, $addToSet checks for equality based on exact BSON byte representation. Two embedded documents are considered equal only if they have identical field names, identical values, and identical field ordering.

  • { a: 1, b: 2 } and { a: 1, b: 2 } -> Match (duplicate rejected)
  • { a: 1, b: 2 } and { b: 2, a: 1 } -> No match (added as distinct element because BSON key order differs!)

2. Array Removal Operators: $pop, $pull, and $pullAll

2.1 The $pop Operator

$pop removes either the first or last element from an array. It does not accept index arguments.

  • { $pop: { arrayField: 1 } }: Removes the last element (tail) of the array.
  • { $pop: { arrayField: -1 } }: Removes the first element (head) of the array.
// Removes the oldest item from the front of the queue
db.jobQueue.updateOne(
  { _id: "primary-queue" },
  { $pop: { pendingTasks: -1 } }
);

2.2 The $pull Operator

$pull removes all instances of a value or all elements matching a specified query filter condition from an array.

// 1. Pull by exact literal match
db.posts.updateOne(
  { _id: 200 },
  { $pull: { tags: "draft" } }
);

// 2. Pull by comparison condition on scalar array
db.profiles.updateOne(
  { _id: 404 },
  { $pull: { scores: { $lt: 60 } } } // Removes all scores less than 60
);

// 3. Pull by query condition on embedded subdocuments
db.orders.updateOne(
  { orderId: "ORD-9912" },
  {
    $pull: {
      items: { sku: "OUT-OF-STOCK-ITEM", quantity: { $lte: 0 } }
    }
  }
);

2.3 The $pullAll Operator

$pullAll removes all instances of the specified list of literal values from an array. Unlike $pull, $pullAll accepts a literal array of target values and does not accept query operators (such as $gt or $regex).

db.survey.updateMany(
  {},
  {
    $pullAll: { flags: ["test", "demo", "deprecated"] }
  }
);

3. Positional Operators for Array Updates

MongoDB provides three distinct positional operators to target elements inside arrays without rewriting the entire array structure:

  1. $ (Positional Operator — First Match)
  2. $[] (All Positional Operator — Every Element)
  3. $[<identifier>] (Filtered Positional Operator — Specified Elements via arrayFilters)

3.1 The First-Match Positional Operator ($)

The positional $ operator identifies the first array element that satisfied the query filter in the update's filter document.

// Updates the score of the FIRST matching grade where gradeId is 'ENG101'
db.students.updateOne(
  {
    _id: 101,
    "grades.gradeId": "ENG101" // Query filter identifies the matching array element
  },
  {
    $set: { "grades.$.score": 95, "grades.$.verified": true }
  }
);

Crucial Constraints of the $ Operator:

  1. Filter Requirement: The array field must appear as an equality or query selector in the filter document. If the array is not in the filter, MongoDB throws an error (The positional operator did not find the match needed from the query).
  2. Single Element Limitation: The $ operator modifies only the first matching element in the array, even if multiple array elements satisfy the filter condition. If you need to update all matching array elements, you must use $[<identifier>].

3.2 The All-Positional Operator ($[])

The all-positional operator $[] indicates that the update operator should modify all elements in the specified array field.

// Increments all elements in the 'scores' array by 5 curve points
db.students.updateMany(
  { courseId: "CS-201" },
  {
    $inc: { "scores.$[]": 5 }
  }
);

// Updates all embedded subdocuments in the 'lineItems' array
db.invoices.updateMany(
  { status: "DRAFT" },
  {
    $set: { "lineItems.$[].taxRate": 0.08 }
  }
);

3.3 The Filtered Positional Operator ($[<identifier>]) & arrayFilters

The filtered positional operator $[<identifier>] provides granular control by allowing developers to modify all array elements that satisfy specific criteria defined in the arrayFilters option.

db.inventory.updateMany(
  { category: "electronics" }, // Query filter selecting documents
  {
    $set: { "warehouses.$[wh].clearance": true },
    $inc: { "warehouses.$[wh].stock": -10 }
  }, // Update document using identifier 'wh'
  {
    arrayFilters: [
      { "wh.quantity": { $gt: 100 }, "wh.location": { $in: ["US-EAST", "US-WEST"] } }
    ]
  } // Options document specifying conditions for 'wh'
);

Rules for arrayFilters Identifiers:

  • The identifier name inside $[<identifier>] must start with a lowercase letter and contain only alphanumeric characters.
  • The identifier defined in the update path (e.g., wh) must match the top-level property name in the arrayFilters condition object (e.g., "wh.quantity").
  • You can use multiple different identifiers in a single update to update nested multi-dimensional arrays (e.g., "grades.$[g].evaluations.$[e].score").

4. Array Operators Comparison Matrix

Operator / SyntaxTarget ScopeModifies Multiple Elements?Requires Query Match in Filter?Exam Identification Key
$pushArray End (or $position)Appends 1+NoUses $each, $slice, $sort for capped arrays.
$addToSetArray EndAppends uniqueNoPrevents duplicates; field-order sensitive on objects.
$pop: 1Array Last ElementRemoves 1No1 removes tail; -1 removes head.
$pullAll matching elementsRemoves allNoAccepts query selectors (e.g., { $lt: 50 }).
array.$First element matching filterModifies 1YesUses $ referencing filter match.
array.$[]Every element in arrayModifies allNoTargets all elements unconditionally.
array.$[elem]Elements matching arrayFiltersModifies all matchingNoUses arrayFilters: [{ "elem.x": ... }].
Loading diagram...
Comparison of Array Positional Targeting Operators
Test Your Knowledge

A collection has the document: { _id: 1, scores: [45, 80, 55, 92, 30] }. A developer wants to increase every score below 60 by 10 points. Which update command correctly accomplishes this?

A
B
C
D
Test Your Knowledge

What is the result of executing the following operation on a document with tags: ["react", "mongodb"]: db.posts.updateOne({ _id: 1 }, { $push: { tags: { $each: ["nodejs", "graphql"], $position: 0, $slice: 3 } } });?

A
B
C
D
Test Your Knowledge

A developer needs to remove all subdocuments from the items array where inStock is false or quantity is 0. Which operator should be used?

A
B
C
D
Test Your Knowledge

A developer attempts to update a document using: db.courses.updateOne({ _id: 50 }, { $set: { "modules.$.status": "COMPLETED" } }); What happens when this command runs?

A
B
C
D