阿里云主机折上折
  • 微信号
Current Site:Index > Update the document (Update)

Update the document (Update)

Author:Chuan Chen 阅读数:4660人阅读 分类: MongoDB

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:

  1. Use projection to return only necessary fields
await User.updateMany(
  { status: 'active' },
  { $set: { lastActive: new Date() } },
  { select: '_id status' }
);
  1. Add appropriate indexes
userSchema.index({ status: 1 });
  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

  1. Always use operators for updates instead of replacing entire documents
// Not recommended
await User.updateOne({ _id }, userData);

// Recommended
await User.updateOne(
  { _id },
  { $set: userData }
);
  1. Use appropriate indexes for frequently updated fields
  2. Consider using lean() to improve performance for read-only operations
const user = await User.findById(userId).lean();
  1. Monitor slow queries
mongoose.set('debug', function(collectionName, method, query, doc) {
  logger.debug(`${collectionName}.${method}`, JSON.stringify(query));
});
  1. 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

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.