Update the document (Update)
Basic Methods for Updating Documents
Mongoose provides multiple ways to update documents, with the most basic being the updateOne()
and updateMany()
methods. updateOne()
updates the first matching document, while updateMany()
updates all matching documents.
// Update a single document
await User.updateOne(
{ name: 'Zhang San' },
{ $set: { age: 30 } }
);
// Update multiple documents
await User.updateMany(
{ status: 'active' },
{ $set: { status: 'inactive' } }
);
The update operator $set
is used to specify the fields to update. Other commonly used operators include:
$inc
: Increment a field's value$push
: Add an element to an array$pull
: Remove an element from an array
Using findByIdAndUpdate
findByIdAndUpdate()
is a convenient method for updating documents. It finds and updates a document by ID and, by default, returns the document before the update. You can set the new: true
option to return the updated document.
const updatedUser = await User.findByIdAndUpdate(
'5f8d0d55b54764421b7156da',
{ $set: { name: 'Li Si' } },
{ new: true }
);
This method is particularly useful for scenarios where you need to retrieve the updated document, such as returning the updated data in a response.
Atomic Update Operations
Mongoose supports various atomic update operators to ensure data consistency in concurrent environments:
// Increment a value
await Product.updateOne(
{ _id: productId },
{ $inc: { stock: -1 } }
);
// Add an element to an array
await BlogPost.updateOne(
{ _id: postId },
{ $push: { comments: newComment } }
);
// Remove an element from an array
await BlogPost.updateOne(
{ _id: postId },
{ $pull: { comments: { _id: commentId } } }
);
Bulk Update Operations
For scenarios requiring updates to large numbers of documents, bulk write operations can improve performance:
const bulkOps = [
{
updateOne: {
filter: { status: 'pending' },
update: { $set: { status: 'processed' } }
}
},
{
updateMany: {
filter: { createdAt: { $lt: new Date('2023-01-01') } },
update: { $set: { archived: true } }
}
}
];
await User.bulkWrite(bulkOps);
Update Validation
By default, Mongoose skips validation during updates. To enable validation, set the runValidators: true
option:
await User.updateOne(
{ _id: userId },
{ $set: { email: 'invalid-email' } },
{ runValidators: true }
);
This will cause the operation to fail because the email format is invalid. You can also use the context
option to allow validators to access the original document:
await User.updateOne(
{ _id: userId },
{ $set: { age: 17 } },
{
runValidators: true,
context: 'query'
}
);
Middleware Handling
Mongoose provides middleware hooks for update operations, allowing custom logic to be executed before or after updates:
schema.pre('updateOne', function(next) {
console.log('About to update document');
this.set({ updatedAt: new Date() });
next();
});
schema.post('updateOne', function(doc, next) {
console.log('Document updated');
next();
});
Optimistic Concurrency Control
Use version numbers to implement optimistic locking and prevent concurrent update conflicts:
const user = await User.findById(userId);
user.name = 'Wang Wu';
await user.save();
If the document is modified by another operation during this time, the save()
operation will fail and throw a VersionError
. You can catch this error and handle the conflict:
try {
await user.save();
} catch (err) {
if (err.name === 'VersionError') {
// Handle version conflict
}
}
Performance Optimization for Update Operations
For update operations on large collections, consider the following optimization strategies:
- Use projection to return only necessary fields
await User.updateMany(
{ status: 'active' },
{ $set: { lastActive: new Date() } },
{ select: '_id status' }
);
- Add appropriate indexes
userSchema.index({ status: 1 });
- Process large updates in batches
const batchSize = 100;
let skip = 0;
let hasMore = true;
while (hasMore) {
const users = await User.find({})
.skip(skip)
.limit(batchSize);
if (users.length === 0) {
hasMore = false;
} else {
await User.updateMany(
{ _id: { $in: users.map(u => u._id) } },
{ $set: { processed: true } }
);
skip += batchSize;
}
}
Update Operations in Transactions
For scenarios requiring atomicity across multiple update operations, use MongoDB transactions:
const session = await mongoose.startSession();
session.startTransaction();
try {
await User.updateOne(
{ _id: userId },
{ $inc: { balance: -100 } },
{ session }
);
await Payment.create(
[{ userId, amount: 100 }],
{ session }
);
await session.commitTransaction();
} catch (error) {
await session.abortTransaction();
throw error;
} finally {
session.endSession();
}
Best Practices for Update Operations
- Always use operators for updates instead of replacing entire documents
// Not recommended
await User.updateOne({ _id }, userData);
// Recommended
await User.updateOne(
{ _id },
{ $set: userData }
);
- Use appropriate indexes for frequently updated fields
- Consider using
lean()
to improve performance for read-only operations
const user = await User.findById(userId).lean();
- Monitor slow queries
mongoose.set('debug', function(collectionName, method, query, doc) {
logger.debug(`${collectionName}.${method}`, JSON.stringify(query));
});
- Handle cases where updates may not match any documents
const result = await User.updateOne(
{ _id: nonExistentId },
{ $set: { name: 'test' } }
);
if (result.matchedCount === 0) {
// Handle case where no document was found
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:查询文档(Read)
下一篇:删除文档(Delete)