MongoDB CRUD Operations: Insert, Read, Update, and Delete
Master MongoDB CRUD operations from foundational commands to advanced querying and modification techniques. Learn insertOne, insertMany, find, findOne, projections, comparison and logical operators, array and nested-document filters, updateOne, updateMany, replacement operations, upserts, update operators, delete operations, return documents, write results, atomicity, concurrency considerations, and practical data manipulation patterns.
Understanding CRUD and MongoDB Documents
CRUD stands for Create, Read, Update, and Delete. These four operations form the foundation of database interaction.
MongoDB implements CRUD operations against collections of BSON documents. Each operation has its own behavior, options, return values, and failure scenarios.
1. Create
Create operations insert new documents into a collection.
Common methods:
insertOne()inserts one document.insertMany()inserts multiple documents.
Example:
db.students.insertOne({
name: "Arun",
age: 22
});
2. Read
Read operations retrieve documents that match a filter.
Common methods:
find()returns a cursor for matching documents.findOne()returns one matching document or null.
Example:
db.students.find({ age: { $gte: 18 } });
3. Update
Update operations modify existing documents.
Common methods:
updateOne()modifies at most one matching document.updateMany()modifies all matching documents.replaceOne()replaces the contents of one matching document, apart from the immutable_idbehavior and other operation constraints.
Example:
db.students.updateOne(
{ name: "Arun" },
{ $set: { age: 23 } }
);
4. Delete
Delete operations remove documents that match a filter.
Common methods:
deleteOne()deletes at most one matching document.deleteMany()deletes all matching documents.
Example:
db.students.deleteOne({ name: "Arun" });
CRUD operations are not interchangeable
A read operation does not modify stored data. An update operation can modify selected fields without replacing the entire document. A replacement operation replaces the document's non-_id contents. A delete operation removes matching documents.
Understanding these differences prevents common mistakes, particularly when updating production data.
Atomicity
MongoDB guarantees atomicity at the individual-document level. An update that modifies several fields within one document is atomic as a single-document write. Multi-document operations such as updateMany() are not atomic as a whole unless they are executed within a transaction.
This distinction matters when an application modifies related data across multiple documents.
insertOne, insertManyfind, findOneupdateOne, updateMany, replaceOnedeleteOne, deleteMany- CRUD stands for Create, Read, Update, and Delete.
- MongoDB CRUD methods operate on collections of documents.
- Update operations can modify selected fields without replacing the whole document.
- Replacement operations have different semantics from modifier updates.
- MongoDB guarantees atomicity for individual-document writes, not automatically for an entire multi-document operation.
Which statement about MongoDB updateMany() is correct?
Inserting a Single Document with insertOne()
The insertOne() method inserts a single document into a collection.
Basic syntax
db.collection.insertOne(document, options)
The options argument is optional. Supported options depend on the operation and MongoDB version.
Example 1: Insert a student
db.students.insertOne({
name: "Arun",
age: 22,
course: "MongoDB",
active: true
});
If _id is omitted, MongoDB's driver or shell ordinarily generates an ObjectId for the inserted document.
The insert result includes the inserted identifier.
Example 2: Insert nested documents
db.students.insertOne({
name: "Meera",
age: 24,
address: {
city: "Chennai",
country: "India"
},
skills: ["JavaScript", "Node.js", "MongoDB"]
});
MongoDB stores address as an embedded document and skills as an array.
Example 3: Supply a custom _id
db.students.insertOne({
_id: "student-1001",
name: "Ravi",
age: 25
});
The _id field does not have to be an ObjectId. It must be unique within the collection.
If another document already has the same _id, the insert fails with a duplicate-key error.
Example 4: Inspect the insert result
const result = db.students.insertOne({
name: "Kavitha",
age: 21
});
printjson(result);
The result typically contains acknowledged: true and insertedId.
An acknowledged write indicates that the server acknowledged the operation according to the configured write concern. It should not be confused with a guarantee that every possible downstream system has processed the data.
Example 5: Insert an explicitly typed value
db.products.insertOne({
name: "Keyboard",
price: NumberDecimal("2499.99"),
createdAt: new Date()
});
This example stores the price as Decimal128 and the creation time as BSON Date.
Practical considerations
- Validate required fields before inserting data.
- Avoid hardcoding production identifiers unless the application requires deterministic IDs.
- Use a consistent field naming convention.
- Store values using appropriate BSON types.
- Handle duplicate-key and validation errors in the application.
- Never assume an insert succeeded solely because the application sent the request.
When a new document is inserted, use the returned insertedId to retrieve or reference that document rather than relying on a non-unique field such as a person's name.
const result = db.students.insertOne({
name: "Arun",
age: NumberInt(22),
course: "MongoDB",
skills: ["JavaScript", "MongoDB"],
createdAt: new Date()
});
print("Acknowledged:", result.acknowledged);
print("Inserted ID:", result.insertedId);
db.students.findOne({ _id: result.insertedId });- insertOne() inserts one document.
- MongoDB ordinarily generates an ObjectId when _id is omitted.
- Custom _id values are supported when unique within the collection.
- Duplicate identifiers cause duplicate-key errors.
- The insert result exposes the inserted identifier and write acknowledgment status.
What happens if insertOne() attempts to insert a document with an _id already present in the collection?
Inserting Multiple Documents with insertMany()
The insertMany() method inserts multiple documents in one operation.
Basic syntax
db.collection.insertMany(
[document1, document2, document3],
options
)
The first argument must be an array of documents.
Example: Insert a course catalog
db.courses.insertMany([
{
title: "MongoDB Basics",
level: "beginner",
durationHours: 8
},
{
title: "MongoDB Aggregation",
level: "intermediate",
durationHours: 12
},
{
title: "MongoDB Performance",
level: "advanced",
durationHours: 10
}
]);
MongoDB generates identifiers for documents that do not specify _id.
Ordered inserts
By default, insertMany() uses ordered insertion behavior. If an insert fails, the operation stops attempting later documents after encountering the error, although documents successfully inserted before the error remain inserted.
For example, suppose an array contains three documents and the second document violates a unique constraint. The first may already be inserted, the second fails, and the third may not be attempted under ordered execution.
Ordered execution does not make the entire batch transactional.
Unordered inserts
You can request unordered insertion:
db.courses.insertMany(
[
{ _id: "course-1", title: "MongoDB Basics" },
{ _id: "course-1", title: "Duplicate ID" },
{ _id: "course-2", title: "Aggregation" }
],
{ ordered: false }
);
The duplicate _id causes that document's insertion to fail. MongoDB can continue attempting other documents when ordered is false. The operation still reports errors for failed inserts.
The exact result details depend on the driver or shell and the errors encountered.
Batch size and memory
For large imports, avoid constructing an enormous array in application memory without considering memory limits and operational requirements.
Consider batching, streaming input, retry behavior, and the maximum BSON size of individual documents.
insertMany() is not a replacement for bulkWrite()
insertMany() is designed for inserting documents. bulkWrite() supports a mixture of insert, update, replacement, and delete operations in one bulk request.
Use the appropriate method for the workload and understand its error-handling behavior.
Successful inserts can remain even when another document in the same insertMany() operation fails. Use a transaction when an actual multi-document all-or-nothing requirement calls for one and the deployment supports it.
db.insertManyDemo.insertMany([
{ _id: 1, name: "Arun" },
{ _id: 2, name: "Meera" },
{ _id: 3, name: "Ravi" }
]);
// Unordered insertion attempts independent documents even if one fails.
// Run this example in a disposable practice collection.
db.insertManyDemo.insertMany(
[
{ _id: 4, name: "Kavitha" },
{ _id: 4, name: "Duplicate ID" },
{ _id: 5, name: "Divya" }
],
{ ordered: false }
);
// Inspect which documents exist after the attempted batch.
db.insertManyDemo.find().sort({ _id: 1 });- insertMany() inserts an array of documents.
- Ordered insertion stops attempting later documents after an insertion error.
- Unordered insertion can continue attempting documents after individual failures.
- A failed batch does not imply that no documents were inserted.
- Use bulkWrite() when you need a mixture of CRUD operations in a batch.
What is a key difference between ordered and unordered insertMany() operations?
Reading Documents with find() and findOne()
MongoDB provides find() and findOne() for retrieving documents.
find()
db.users.find({ active: true });
The find() method returns a cursor representing the matching documents. The shell displays results according to its cursor-display behavior.
In application code, a cursor can be iterated or consumed using the APIs provided by the MongoDB driver.
findOne()
db.users.findOne({ email: "arun@example.com" });
findOne() returns one matching document or null if no document matches.
If several documents match, do not assume that the returned document is the newest, oldest, or otherwise preferred document unless you explicitly define a sort order.
Empty filter
db.users.find({});
An empty filter matches all documents in the collection.
This is useful for exploration, but production queries should generally include appropriate filters and pagination or limits where necessary.
Querying nested fields
db.users.find({ "address.city": "Chennai" });
Dot notation allows you to query fields inside embedded documents.
Querying arrays
db.users.find({ skills: "MongoDB" });
For an array field, this matches documents whose array contains the specified value.
Sorting results
db.users.find({ active: true }).sort({ createdAt: -1 });
The sort order -1 means descending and 1 means ascending.
For deterministic pagination, use a sort order that includes a unique tie-breaker, such as _id, especially when multiple documents may have the same primary sort value.
Limiting results
db.users.find({ active: true })
.sort({ createdAt: -1, _id: -1 })
.limit(20);
This returns at most 20 matching documents, ordered by creation time and identifier.
Cursors and large datasets
A cursor allows results to be processed incrementally rather than materializing every document at once. This is important when working with large collections.
In the Node.js driver, use cursor iteration or an appropriate cursor API when handling large result sets. Avoid calling toArray() on an unbounded result set if the result may exceed available application memory.
db.readDemo.insertMany([
{ name: "Arun", age: 22, active: true },
{ name: "Meera", age: 24, active: true },
{ name: "Ravi", age: 25, active: false }
]);
// Find all active users.
db.readDemo.find({ active: true });
// Retrieve one matching document.
db.readDemo.findOne({ name: "Arun" });
// Sort and limit the result set.
db.readDemo.find({ active: true })
.sort({ age: -1, _id: -1 })
.limit(10);- find() returns a cursor for matching documents.
- findOne() returns one document or null.
- An empty filter matches all documents.
- Sorting is required when a particular result order matters.
- Cursors help process large result sets without loading every document into memory.
What does findOne() return when no document matches its filter?
Query Filters and Comparison Operators
MongoDB query filters determine which documents are selected by a read, update, or delete operation.
A filter is a document describing conditions that documents must satisfy.
Equality
db.products.find({ category: "electronics" });
This matches documents where the category field equals the specified value.
Comparison operators
MongoDB provides comparison operators such as:
$eq: equal to.$ne: not equal to.$gt: greater than.$gte: greater than or equal to.$lt: less than.$lte: less than or equal to.$in: matches any specified value.$nin: matches none of the specified values.
Greater than and less than
db.products.find({ price: { $gt: 1000 } });
This selects products with a numeric price greater than 1000, subject to MongoDB's comparison semantics and the stored field types.
Range query
db.products.find({
price: {
$gte: 500,
$lte: 2000
}
});
The query requires the price to be at least 500 and at most 2000.
$in
db.products.find({
category: { $in: ["electronics", "books"] }
});
The query matches either category. $in is also useful for matching array fields against any of the supplied values.
$nin
db.products.find({
category: { $nin: ["electronics", "books"] }
});
$nin matches documents where the field value is not in the supplied list. It can also match documents where the field is missing, so consider whether that behavior is appropriate for your requirement.
$ne
db.products.find({ price: { $ne: 1000 } });
$ne is not equivalent to checking that a field exists and has a different value. Depending on the query, documents with missing fields can match $ne filters.
Type awareness
If a price field contains the string "1500", do not assume that it behaves like the number 1500 in a numeric range filter. Use consistent types and inspect legacy data when query results seem unexpected.
Query selectivity
A filter that matches a small fraction of a collection may be more selective than one that matches nearly every document. Indexes and data distribution affect query performance, which will be covered in the indexing chapters.
EqualNot equalGreater thanLess thanMembershipdb.filterDemo.insertMany([
{ name: "Keyboard", price: 1500, category: "electronics" },
{ name: "Book", price: 500, category: "books" },
{ name: "Monitor", price: 8000, category: "electronics" },
{ name: "Chair", price: 2500, category: "furniture" }
]);
// Price greater than 1000.
db.filterDemo.find({ price: { $gt: 1000 } });
// Inclusive price range.
db.filterDemo.find({
price: { $gte: 500, $lte: 2500 }
});
// Match one of several categories.
db.filterDemo.find({
category: { $in: ["electronics", "books"] }
});- Query filters define which documents an operation affects.
- Comparison operators support equality, inequalities, and membership tests.
- $in matches any specified value.
- $ne and $nin may match documents where a field is missing.
- Consistent BSON types are important for predictable filtering.
What does `{ price: { $gte: 500, $lte: 2000 } }` mean?
Logical Operators: $and, $or, $nor, and $not
MongoDB supports logical operators for combining or negating query conditions.
$and
$and matches documents that satisfy every condition in the supplied array.
db.products.find({
$and: [
{ price: { $gte: 1000 } },
{ stock: { $gt: 0 } }
]
});
This selects products priced at least 1000 that also have positive stock.
MongoDB often allows implicit AND when multiple conditions are expressed as separate fields in the same filter:
db.products.find({
price: { $gte: 1000 },
stock: { $gt: 0 }
});
The explicit form is useful when necessary to express multiple conditions involving the same field or more complicated logical structures.
$or
$or matches documents satisfying at least one condition.
db.products.find({
$or: [
{ category: "books" },
{ price: { $lt: 500 } }
]
});
A product matches if it is a book, costs less than 500, or satisfies both conditions.
$nor
$nor matches documents that fail all listed query expressions.
db.products.find({
$nor: [
{ category: "electronics" },
{ stock: { $lt: 1 } }
]
});
This excludes documents matching either listed condition. Missing fields can affect how individual expressions evaluate, so inspect the intended behavior for optional fields.
$not
$not is a field-level operator used to negate the result of another operator or regular expression.
db.products.find({
price: { $not: { $gt: 1000 } }
});
This matches documents for which the price does not satisfy the greater-than condition, including some documents where the field is missing. It is not always interchangeable with $lte.
Combine logical and comparison operators
db.products.find({
$or: [
{
category: "electronics",
price: { $lt: 5000 }
},
{
category: "books",
stock: { $gt: 0 }
}
]
});
Each object inside $or can contain multiple conditions. Those conditions are combined with AND within that object, while the outer $or requires at least one object to match.
Practical query design
Before using nested logical operators, write the requirement in plain language. Then translate each condition into a filter and verify it against a small dataset.
db.logicalDemo.insertMany([
{ name: "Keyboard", category: "electronics", price: 1500, stock: 10 },
{ name: "Book", category: "books", price: 400, stock: 5 },
{ name: "Monitor", category: "electronics", price: 8000, stock: 0 },
{ name: "Notebook", category: "stationery", price: 100, stock: 20 }
]);
// AND: both conditions must match.
db.logicalDemo.find({
price: { $gte: 500 },
stock: { $gt: 0 }
});
// OR: either condition can match.
db.logicalDemo.find({
$or: [
{ category: "books" },
{ price: { $lt: 500 } }
]
});
// NOR: exclude documents matching either expression.
db.logicalDemo.find({
$nor: [
{ category: "electronics" },
{ stock: { $lt: 1 } }
]
});- $and requires every listed condition to match.
- $or requires at least one listed condition to match.
- $nor excludes documents matching any listed condition.
- $not negates a field-level query expression and is not always equivalent to a simpler comparison operator.
- Nested conditions within one $or branch are combined with AND.
In an $or array, what happens when one condition object contains both `category: "books"` and `stock: { $gt: 0 }`?
Projection: Selecting Fields in Query Results
Projection controls which fields MongoDB returns from matching documents. It can reduce response size and prevent unnecessary fields from being exposed to application code.
Include specific fields
db.users.find(
{ active: true },
{ name: 1, email: 1 }
);
The query returns matching documents with name, email, and _id by default.
Exclude the identifier
db.users.find(
{ active: true },
{ name: 1, email: 1, _id: 0 }
);
The _id field is included by default unless explicitly excluded.
Exclude a field
db.users.find(
{},
{ passwordHash: 0 }
);
This excludes passwordHash while retaining other fields.
Inclusion and exclusion rules
Ordinarily, you cannot mix inclusion and exclusion projections in the same projection, apart from excluding _id in an inclusion projection.
For example, this is a valid inclusion projection:
{ name: 1, email: 1, _id: 0 }
This is generally invalid as an ordinary find projection:
{ name: 1, passwordHash: 0 }
Nested-field projection
db.users.find(
{},
{ name: 1, "address.city": 1, _id: 0 }
);
This includes the name and the nested city field.
Array projection operators
MongoDB offers specialized projection mechanisms for arrays, including positional projection and $slice, with distinct semantics.
For example, $slice can return only a portion of an array in the result:
db.posts.find(
{},
{ title: 1, comments: { $slice: 5 } }
);
This returns up to five array elements in the projected comments field. It does not limit how many documents the query returns.
Projection is not authorization
Projection can help reduce data returned by a query, but sensitive fields should also be protected through authorization and careful API response design. Do not assume that selecting fields at the database layer alone implements every application security requirement.
db.projectionDemo.insertOne({
name: "Arun",
email: "arun@example.com",
passwordHash: "example-hash",
address: {
city: "Chennai",
country: "India"
},
skills: ["Node.js", "MongoDB", "React"]
});
// Include selected fields and exclude _id.
db.projectionDemo.find(
{},
{ name: 1, email: 1, _id: 0 }
);
// Return all fields except passwordHash.
db.projectionDemo.find(
{},
{ passwordHash: 0 }
);
// Include a nested field and a subset of an array.
db.projectionDemo.find(
{},
{ name: 1, "address.city": 1, skills: { $slice: 2 }, _id: 0 }
);- Projection selects which fields are returned.
- The _id field is included by default unless excluded.
- Inclusion and exclusion projections generally cannot be mixed, apart from excluding _id.
- Nested fields can be projected using dot notation.
- Projection reduces returned data but is not a complete authorization strategy.
What does `{ name: 1, email: 1, _id: 0 }` do in a find projection?
Updating Documents with updateOne() and Update Operators
MongoDB update operations can modify selected fields without replacing an entire document. Modifier operators define how the selected document changes.
Basic update syntax
db.collection.updateOne(
filter,
updateDocument,
options
)
The filter identifies the target document. The update document describes the modifications.
$set
$set creates a field if it does not exist or changes its value if it does.
db.users.updateOne(
{ name: "Arun" },
{ $set: { age: 23 } }
);
Other fields remain unchanged.
$unset
$unset removes a field from the document.
db.users.updateOne(
{ name: "Arun" },
{ $unset: { temporaryField: "" } }
);
The value supplied to $unset does not determine the resulting field value; the field is removed.
$inc
$inc increases or decreases a numeric field.
db.products.updateOne(
{ sku: "KEYBOARD-01" },
{ $inc: { stock: 5 } }
);
A negative increment decreases the field. If the field does not exist, $inc creates it with the specified value. If the field exists with a nonnumeric value, the update fails.
$mul
$mul multiplies a numeric field by a value.
db.products.updateOne(
{ sku: "MOUSE-01" },
{ $mul: { price: 1.1 } }
);
This operation changes the numeric value, but floating-point precision and the chosen numeric BSON type remain important.
$rename
$rename changes a field's name.
db.users.updateOne(
{ _id: ObjectId("65a000000000000000000001") },
{ $rename: { fullname: "name" } }
);
Renaming a field affects schema expectations, queries, and application code. Plan migrations carefully if multiple application versions are running.
$min and $max
$min updates a field only if the specified value is less than the current value under the applicable comparison rules. $max updates it only if the specified value is greater.
These operators can be useful for maintaining high-water marks or lowest observed values, but their behavior depends on type and comparison semantics.
$currentDate
$currentDate sets a field to the current date or a BSON Timestamp, depending on the requested type.
db.users.updateOne(
{ name: "Arun" },
{ $currentDate: { updatedAt: true } }
);
For application audit fields, BSON Date is usually the intended choice.
Multiple operators in one update
db.products.updateOne(
{ sku: "KEYBOARD-01" },
{
$set: { active: true },
$inc: { stock: -1 },
$currentDate: { updatedAt: true }
}
);
The update applies all specified modifications atomically to the matched document. Ensure the filter identifies the correct target and that the resulting state satisfies your business rules.
db.updateDemo.insertOne({
sku: "KEYBOARD-01",
name: "Keyboard",
stock: 10,
active: false,
obsoleteField: "remove-me"
});
db.updateDemo.updateOne(
{ sku: "KEYBOARD-01" },
{
$set: { active: true },
$inc: { stock: 5 },
$unset: { obsoleteField: "" },
$currentDate: { updatedAt: true }
}
);
db.updateDemo.findOne({ sku: "KEYBOARD-01" });- $set creates or modifies selected fields.
- $unset removes fields.
- $inc and $mul modify numeric values.
- $rename changes field names and can affect application compatibility.
- Multiple update operators can be applied atomically to one document.
What happens when `$inc` is applied to a field that does not exist?
Updating Multiple Documents with updateMany()
The updateMany() method applies an update to every document matching the supplied filter.
Basic example
db.products.updateMany(
{ category: "electronics" },
{ $set: { taxable: true } }
);
Every matching document receives the specified update.
Update all documents deliberately
An empty filter matches all documents:
db.products.updateMany(
{},
{ $set: { reviewed: true } }
);
This is a valid operation, but it can modify the entire collection. Always verify that this is the intended behavior before running a broad update.
Update nested fields
db.users.updateMany(
{ "address.country": "India" },
{ $set: { "address.region": "South Asia" } }
);
Dot notation updates the nested region field without replacing the entire address document.
Increment matching documents
db.products.updateMany(
{ category: "books" },
{ $inc: { stock: 10 } }
);
Every matching document receives the increment.
Understand the result
An acknowledged update result typically provides:
matchedCount: the number of documents matching the filter.modifiedCount: the number of documents actually modified.upsertedCount: the number of documents inserted through upsert behavior, if enabled.upsertedId: the identifier of an upserted document when applicable.
A matched document does not necessarily count as modified. If the update produces no change, matchedCount may exceed modifiedCount.
Multi-document atomicity
updateMany() is not a single atomic operation across every matching document. Other operations may observe partial progress, and an error can occur after some documents have been modified.
If the business requirement demands that several document changes succeed or fail together, evaluate a transaction supported by your deployment and workload. Transactions introduce additional costs and should not be used without understanding the consistency requirement.
Safe operational workflow
- Run a find query with the intended filter.
- Inspect representative matching documents.
- Estimate how many documents will be affected.
- Run the update in a suitable environment.
- Inspect matchedCount and modifiedCount.
- Verify the resulting documents.
Before running updateMany({}, ...), verify that modifying every document is intentional. Test broad migrations in a non-production environment and use an appropriate recovery plan.
db.updateManyDemo.insertMany([
{ name: "Keyboard", category: "electronics", stock: 10 },
{ name: "Monitor", category: "electronics", stock: 5 },
{ name: "Book", category: "books", stock: 20 }
]);
// Inspect targets before updating.
db.updateManyDemo.find({ category: "electronics" });
const result = db.updateManyDemo.updateMany(
{ category: "electronics" },
{ $set: { taxable: true } }
);
printjson({
matchedCount: result.matchedCount,
modifiedCount: result.modifiedCount
});
db.updateManyDemo.find({ category: "electronics" });- updateMany() modifies all documents matching the filter.
- An empty filter matches all documents in the collection.
- matchedCount and modifiedCount measure different things.
- A multi-document update is not atomic as a whole.
- Inspect and validate the target set before broad updates.
Why can matchedCount be greater than modifiedCount?
Updating Array Fields and Array Elements
MongoDB provides update operators for adding, removing, and modifying array elements.
$push
$push appends a value to an array.
db.users.updateOne(
{ name: "Arun" },
{ $push: { skills: "MongoDB" } }
);
If the array does not exist, $push creates it. If the field exists but is not an array, the update fails.
$addToSet
$addToSet adds a value only if an equal value is not already present.
db.users.updateOne(
{ name: "Arun" },
{ $addToSet: { skills: "MongoDB" } }
);
For arrays of embedded documents, equality is based on document value comparison, including field order. $addToSet does not enforce uniqueness by a selected nested key such as skillId.
$each
$each adds multiple values with operators such as $push and $addToSet.
db.users.updateOne(
{ name: "Arun" },
{
$addToSet: {
skills: {
$each: ["React", "Node.js", "MongoDB"]
}
}
}
);
$pull
$pull removes all array elements that match the supplied condition.
db.users.updateOne(
{ name: "Arun" },
{ $pull: { skills: "LegacySkill" } }
);
For an array of embedded documents, you can match by nested field values.
$pop
$pop removes one element from either end of an array.
// Remove the last element.
db.users.updateOne(
{ name: "Arun" },
{ $pop: { skills: 1 } }
);
// Remove the first element.
db.users.updateOne(
{ name: "Arun" },
{ $pop: { skills: -1 } }
);
$pullAll
$pullAll removes all array elements equal to any of the specified values.
db.users.updateOne(
{ name: "Arun" },
{ $pullAll: { skills: ["LegacySkill", "DeprecatedSkill"] } }
);
Positional update operator
The positional $ operator updates the first array element matched by the query condition in supported update patterns.
For example, when the query identifies a student and a matching score element, the positional operator can target that element.
db.students.updateOne(
{ _id: 1, "scores.subject": "Math" },
{ $set: { "scores.$.score": 95 } }
);
The positional operator has restrictions and should be used only when the query correctly identifies the intended array element.
Array filters
Array filters allow updates to selected elements using identifiers in the update path.
db.students.updateOne(
{ _id: 1 },
{ $set: { "scores.$[item].passed": true } },
{ arrayFilters: [{ "item.score": { $gte: 40 } }] }
);
This updates every matching array element in the selected document. The update path identifier and array filter identifier must correspond.
Positional all operator
The $[] operator targets all elements of an array.
db.students.updateOne(
{ _id: 1 },
{ $inc: { "scores.$[].attempts": 1 } }
);
This increments attempts for every element in the selected document's scores array, assuming the path and element structures are valid.
db.arrayUpdateDemo.insertOne({
name: "Arun",
skills: ["JavaScript", "React"],
scores: [
{ subject: "Math", score: 35, passed: false },
{ subject: "Science", score: 55, passed: true }
]
});
// Add a unique skill.
db.arrayUpdateDemo.updateOne(
{ name: "Arun" },
{ $addToSet: { skills: "MongoDB" } }
);
// Add multiple unique skills.
db.arrayUpdateDemo.updateOne(
{ name: "Arun" },
{
$addToSet: {
skills: { $each: ["Node.js", "MongoDB"] }
}
}
);
// Update the Math score.
db.arrayUpdateDemo.updateOne(
{ name: "Arun", "scores.subject": "Math" },
{ $set: { "scores.$.score": 45, "scores.$.passed": true } }
);
// Inspect the result.
db.arrayUpdateDemo.findOne({ name: "Arun" });- $push appends values, while $addToSet avoids adding an equal existing value.
- $pull removes matching array elements.
- $pop removes one element from an end of an array.
- The positional $ operator targets a matched array element.
- Array filters can target selected elements using explicit identifiers.
Which update operator is designed to add a value only if an equal value is not already present in an array?