Dot Notation for Nested Fields and Arrays
MongoDB lets you address nested fields and array elements using dot notation, both in queries and in update operators: address.city reaches into an embedded sub-document, and tags.0 reaches the first element of an array. For arrays of sub-documents, a query like { 'items.sku': 'ABC123' } matches any document where at least one array element has that sku, without needing to specify which index — this is the array's implicit $elemMatch-like behavior for a single-field condition. When you need multiple conditions to match on the *same* array element (not just each condition matching some element independently), you must use $elemMatch explicitly, otherwise MongoDB may match a document where condition A is satisfied by one element and condition B by a different element.
Cricket analogy: A scorecard query for 'any bowler with figures 5-for' matches the innings if any single bowler achieved it, but asking for 'a bowler with 5 wickets AND an economy under 3' needs both conditions checked on the same bowler's row, exactly like $elemMatch versus separate top-level conditions.
Updating Array Elements with Positional Operators
Updating a specific array element in place uses the positional $ operator, which stands in for the index of the first array element matched by the query filter — so db.orders.updateOne({ 'items.sku': 'ABC123' }, { $set: { 'items.$.qty': 5 } }) updates only the matched item's quantity. When you need to update *every* matching element rather than just the first, $[] updates all array elements unconditionally, and the filtered positional operator $[<identifier>] combined with arrayFilters updates every element that satisfies a specified sub-condition — for example, marking every item with qty below a threshold as lowStock: true in one call, across potentially many matching elements, without a client-side loop.
Cricket analogy: Updating just one fielder's position on a lineup card when the captain calls a specific change is like the $ positional operator, touching only the matched element, while resetting every fielder's position at a drinks break is like $[], touching all elements at once.
Array Update Operators: $push, $pull, $addToSet
Beyond modifying existing elements, MongoDB provides dedicated operators for adding and removing array elements atomically: $push appends one or more values (optionally with $each, $sort, and $slice modifiers to insert several items, keep the array sorted, and cap its size in a single operation), $pull removes all elements matching a condition, and $addToSet appends a value only if it isn't already present, giving you set-like uniqueness without a separate read-check-write round trip. Combining $push with $slice: -N is a common way to implement a bounded 'recent items' array directly at the database level — every push automatically trims the array back to the last N elements, which is a lightweight, race-condition-free alternative to application-level array trimming.
Cricket analogy: A team's 'recent form' tracker uses $push with $slice to append the latest match result and automatically drop anything beyond the last five games, keeping a bounded, always-current form guide without manual trimming.
// $elemMatch: both conditions must match the SAME array element
db.orders.find({
items: { $elemMatch: { sku: "ABC123", qty: { $gte: 5 } } }
});
// Positional $ operator: update the first matched element in place
db.orders.updateOne(
{ _id: orderId, "items.sku": "ABC123" },
{ $set: { "items.$.qty": 5 } }
);
// arrayFilters: update every element matching a sub-condition
db.orders.updateOne(
{ _id: orderId },
{ $set: { "items.$[lowItem].lowStock": true } },
{ arrayFilters: [{ "lowItem.qty": { $lt: 3 } }] }
);
// $push + $slice: bounded 'recent activity' array capped at 10 entries
db.users.updateOne(
{ _id: userId },
{
$push: {
recentActivity: {
$each: [{ action: "login", ts: new Date() }],
$slice: -10
}
}
}
);
// $addToSet: append only if not already present
db.users.updateOne({ _id: userId }, { $addToSet: { interests: "mongodb" } });$slice accepts a negative number to keep the last N elements or a positive number to keep the first N, applied after $each inserts the new elements and after $sort (if present) reorders the array — the order of evaluation is $each, then $sort, then $slice.
A common bug: querying with two separate top-level conditions on an array field (e.g., { 'items.sku': 'A', 'items.qty': { $gte: 5 } }) can match a document where sku 'A' has qty 2 and a different item has qty 10, because each condition is independently satisfied by some element. Use $elemMatch when the conditions must hold on the same element.
- Dot notation ('a.b.c') addresses nested sub-document fields and array indices in queries and updates.
- A single-field condition on an array field matches if any element satisfies it; multi-field conditions need $elemMatch to require the same element.
- The positional $ operator updates the first array element matched by the query filter.
- $[] updates every array element unconditionally; $[<id>] with arrayFilters updates every element matching a specified sub-condition.
- $push (with $each, $sort, $slice) appends and can atomically bound array size; $pull removes matching elements; $addToSet enforces uniqueness on append.
- $push combined with $slice is a lightweight, race-condition-free way to implement a bounded 'recent items' array server-side.
- Array update operators are atomic at the single-document level, avoiding read-modify-write races in application code.
Practice what you learned
1. Why might a query with two independent top-level conditions on an array field match unintended documents?
2. What does the positional $ operator update?
3. Which combination lets you update every array element that satisfies a specific sub-condition?
4. What does $addToSet guarantee that plain $push does not?
5. In what order are $each, $sort, and $slice applied within a $push operation?
Was this page helpful?
You May Also Like
Schema Design in MongoDB
Learn how to design MongoDB document schemas around your application's access patterns rather than around abstract normalized tables.
Embedding vs Referencing
Understand the core MongoDB modeling trade-off between embedding related data inside a document and referencing it via a separate collection.
Data Modeling Patterns
A tour of named MongoDB schema design patterns — attribute, bucket, computed, subset, and extended reference — and the problems each one solves.