Array Operators
Arrays are first-class citizens in MongoDB. Array operators let you query arrays by element, match conditions across array elements, and manipulate array contents during updates.
Query Array Operators Overview
Operator | Category | Purpose |
|---|---|---|
$all | query | Array contains ALL specified values |
$elemMatch | query | At least one element matches ALL conditions |
$size | query | Array has exactly N elements |
$push | update | Append an element to an array |
$pull | update | Remove elements matching a condition |
$addToSet | update | Add element only if not already present |
$pop | update | Remove the first or last element |
$pullAll | update | Remove all instances of listed values |
$each | update modifier | Apply $push or $addToSet to multiple values |
$slice | update modifier / projection | Trim or cap array length |
$sort | update modifier | Sort array elements after $push |
$all — Array Contains All Values
Matches documents where the array field contains ALL of the specified elements.
$all — match all elements
// Find posts tagged with BOTH 'mongodb' AND 'nosql'
db.posts.find({
tags: { $all: ['mongodb', 'nosql'] }
})
// Equivalent: the array must contain every listed value (order doesn't matter)
// { tags: ['nosql', 'mongodb', 'database'] } ← matches
// { tags: ['mongodb'] } ← does NOT match (missing 'nosql')
// { tags: ['nosql'] } ← does NOT match (missing 'mongodb')$all does not require the array to match exactly — it just requires those elements to be present.$elemMatch — Element Matching Conditions
Matches documents where at least one array element satisfies ALL conditions in the $elemMatch expression. Essential for arrays of objects.
$elemMatch — object array conditions
// Find orders where a SINGLE item has quantity > 5 AND price < 20
db.orders.find({
items: {
$elemMatch: {
qty: { $gt: 5 },
price: { $lt: 20 }
}
}
})
// Matches: { items: [{ qty: 8, price: 15 }, { qty: 2, price: 50 }] }
// Because the first item satisfies BOTH conditionsWithout $elemMatch — the pitfall
// WARNING: This does NOT guarantee the conditions apply to the SAME element
db.orders.find({
'items.qty': { $gt: 5 },
'items.price': { $lt: 20 }
})
// This matches a document where:
// - ANY item has qty > 5 (could be item A)
// - ANY item has price < 20 (could be item B — a different item!)
//
// Example that incorrectly matches:
// { items: [{ qty: 8, price: 100 }, { qty: 1, price: 10 }] }
// item[0] satisfies qty > 5, item[1] satisfies price < 20 — but they're different items!
// Use $elemMatch to enforce conditions on the SAME element.$size — Array Length
$size — match by array length
// Find users with exactly 3 tags
db.users.find({ tags: { $size: 3 } })
// Find documents where the array is empty
db.users.find({ tags: { $size: 0 } })
// Find documents where hobbies has exactly 1 element
db.users.find({ hobbies: { $size: 1 } })$size only works with exact equality. For greater-than/less-than array length, use $where or create a computed tagCount field and index it.Querying by Array Index
Positional queries
// Query by exact position using dot notation
// Find documents where the FIRST score is greater than 90
db.results.find({ 'scores.0': { $gt: 90 } })
// Find documents where the first address is in 'New York'
db.users.find({ 'addresses.0.city': 'New York' })
// Find documents where the second tag is 'mongodb'
db.posts.find({ 'tags.1': 'mongodb' })$push — Add to Array
$push variants
// Push a single item onto the array
db.posts.updateOne(
{ _id: postId },
{ $push: { comments: { user: 'alice', text: 'Great post!' } } }
)
// Push MULTIPLE items using $each
db.users.updateOne(
{ _id: userId },
{ $push: { tags: { $each: ['mongodb', 'nosql', 'database'] } } }
)
// Push, then SORT the array (sort by 'score' descending)
db.students.updateOne(
{ _id: studentId },
{
$push: {
scores: {
$each: [{ subject: 'Math', score: 95 }],
$sort: { score: -1 }
}
}
}
)
// Push and CAP array length to last 5 elements (ring buffer pattern)
db.events.updateOne(
{ _id: deviceId },
{
$push: {
recentEvents: {
$each: [{ type: 'click', ts: new Date() }],
$slice: -5 // keep only the last 5 elements
}
}
}
)$pull — Remove from Array
$pull — remove matching elements
// Pull by exact value — remove the tag 'deprecated'
db.posts.updateOne(
{ _id: postId },
{ $pull: { tags: 'deprecated' } }
)
// Pull by condition — remove all scores below 60
db.students.updateOne(
{ _id: studentId },
{ $pull: { scores: { $lt: 60 } } }
)
// Pull objects matching a condition — remove items with qty 0
db.orders.updateOne(
{ _id: orderId },
{ $pull: { items: { qty: 0 } } }
)
// Pull from ALL documents (updateMany)
db.posts.updateMany(
{},
{ $pull: { tags: 'spam' } }
)$addToSet — Unique Push
$addToSet — no duplicates
// Add tag only if it doesn't already exist in the array
db.posts.updateOne(
{ _id: postId },
{ $addToSet: { tags: 'mongodb' } }
)
// If 'mongodb' is already in tags, this is a no-op
// $addToSet with $each — add multiple unique values at once
db.users.updateOne(
{ _id: userId },
{
$addToSet: {
roles: { $each: ['editor', 'viewer', 'editor'] }
// 'editor' appears twice — only ONE is added if not present
}
}
)$pop — Remove First or Last
$pop — remove from ends
// Remove the LAST element from the queue array
db.tasks.updateOne(
{ _id: taskId },
{ $pop: { queue: 1 } }
)
// Remove the FIRST element from the queue array
db.tasks.updateOne(
{ _id: taskId },
{ $pop: { queue: -1 } }
)
// $pop: 1 → removes last (like Array.pop())
// $pop: -1 → removes first (like Array.shift())The Positional $ Operator
The positional $ operator updates the first array element that matches the query condition.
Positional $ update
// Update the score of a specific student in the 'grades' array
// The $ refers to the first matching element from the query
db.students.updateOne(
{ _id: studentId, 'grades.subject': 'Math' },
{ $set: { 'grades.$.score': 98 } }
)
// Mark a specific comment as edited
db.posts.updateOne(
{ _id: postId, 'comments.user': 'alice' },
{ $set: { 'comments.$.edited': true } }
)The $[] and $[identifier] Operators
All-positional $[] update
// $[] — update ALL elements in the array
// Add 5 bonus points to every score
db.students.updateOne(
{ _id: studentId },
{ $inc: { 'scores.$[]': 5 } }
)
// $[identifier] — update elements matching arrayFilters
// Boost all scores below 50 to 50 (curved grading)
db.students.updateMany(
{},
{ $set: { 'scores.$[elem]': 50 } },
{ arrayFilters: [{ elem: { $lt: 50 } }] }
)
// Update nested array objects matching a condition
db.orders.updateMany(
{},
{ $set: { 'items.$[item].discounted': true } },
{ arrayFilters: [{ 'item.price': { $gt: 100 } }] }
)$elemMatch anytime you're querying an array of objects with multiple conditions — it's the only way to ensure conditions apply to the SAME element.$push without $slice on a frequently-updated array will cause it to grow indefinitely, potentially approaching the 16 MB document limit.