How to Use findOneAndUpdate() in Mongoose如何在Mongoose中使用findOneAndUpdate()

The findOneAndUpdate() function in Mongoose has a wide variety of use cases. Mongoose中的findOneAndUpdate()函数有各种各样的用例。You should use save() to update documents where possible, for better validation and middleware support. 您应该尽可能使用save()来更新文档,以获得更好的验证中间件支持。However, there are some cases where you need to use findOneAndUpdate(). 然而,在某些情况下,您需要使用findOneAndUpdate()In this tutorial, you'll see how to use findOneAndUpdate(), and learn when you need to use it.在本教程中,您将了解如何使用findOneAndUpdate(),并了解何时需要使用它。

Getting Started入门

As the name implies, findOneAndUpdate() finds the first document that matches a given filter, applies an update, and returns the document. By default, findOneAndUpdate() returns the document as it was before update was applied.顾名思义,findOneAndUpdate()查找与给定filter匹配的第一个文档,应用更新并返回该文档。默认情况下,findOneAndUpdate()会返回应用更新之前的文档。

const Character = mongoose.model('Character', new mongoose.Schema({
name: String,
age: Number
}));

await Character.create({ name: 'Jean-Luc Picard' });

const filter = { name: 'Jean-Luc Picard' };
const update = { age: 59 };

// `doc` is the document _before_ `update` was applied
let doc = await Character.findOneAndUpdate(filter, update);
doc.name; // 'Jean-Luc Picard'
doc.age; // undefined

doc = await Character.findOne(filter);
doc.age; // 59

You should set the new option to true to return the document after update was applied.您应该将new选项设置为true,以便在应用更新后返回文档。

const filter = { name: 'Jean-Luc Picard' };
const update = { age: 59 };

// `doc` is the document _after_ `update` was applied because of
// `new: true`
const doc = await Character.findOneAndUpdate(filter, update, {
new: true
});
doc.name; // 'Jean-Luc Picard'
doc.age; // 59

Mongoose's findOneAndUpdate() is slightly different from the MongoDB Node.js driver's findOneAndUpdate() because it returns the document itself, not a result object.Mongoose的findOneAndUpdate()MongoDB Node.js驱动程序的findOneAndUpdate()略有不同,因为它返回的是文档本身,而不是结果对象。

As an alternative to the new option, you can also use the returnOriginal option. 作为new选项的替代方案,您也可以使用returnOriginal选项。returnOriginal: false is equivalent to new: true. 相当于new: trueThe returnOriginal option exists for consistency with the the MongoDB Node.js driver's findOneAndUpdate(), which has the same option.returnOriginal选项的存在是为了和MongoDB Node.js驱动程序的findOneAndUpdate()保持一致,后者具有相同的选项。

const filter = { name: 'Jean-Luc Picard' };
const update = { age: 59 };

// `doc` is the document _after_ `update` was applied because of
// `returnOriginal: false`
const doc = await Character.findOneAndUpdate(filter, update, {
returnOriginal: false
});
doc.name; // 'Jean-Luc Picard'
doc.age; // 59

Atomic Updates原子更新

With the exception of an unindexed upsert, findOneAndUpdate() is atomic. 除了未编制索引的upstart之外,findOneAndUpdate()是原子的That means you can assume the document doesn't change between when MongoDB finds the document and when it updates the document, unless you're doing an upsert.这意味着您可以假设文档在MongoDB找到文档和更新文档之间不会发生变化,除非您正在进行upsert

For example, if you're using save() to update a document, the document can change in MongoDB in between when you load the document using findOne() and when you save the document using save() as show below. 例如,如果您使用save()更新文档,则在使用findOne()加载文档和使用save()保存文档之间,文档可能会在MongoDB中发生更改,如下所示。For many use cases, the save() race condition is a non-issue. But you can work around it with findOneAndUpdate() (or transactions) if you need to.对于许多用例来说,save()race条件不是问题。但是,如果需要的话,您可以使用findOneAndUpdate()(或事务)来解决这个问题。

const filter = { name: 'Jean-Luc Picard' };
const update = { age: 59 };

let doc = await Character.findOne({ name: 'Jean-Luc Picard' });

// Document changed in MongoDB, but not in Mongoose
await Character.updateOne(filter, { name: 'Will Riker' });

// This will update `doc` age to `59`, even though the doc changed.
doc.age = update.age;
await doc.save();

doc = await Character.findOne();
doc.name; // Will Riker
doc.age; // 59

Upsert

Using the upsert option, you can use findOneAndUpdate() as a find-and- upsert operation. 使用upstart选项,您可以使用findOneAndUpdate()作为查找和upsert操作。An upsert behaves like a normal findOneAndUpdate() if it finds a document that matches filter. 如果upstart找到与筛选器匹配的文档,那么它的行为与普通的findOneAndUpdate()类似。But, if no document matches filter, MongoDB will insert one by combining filter and update as shown below.但是,如果没有文档匹配filter,MongoDB将通过组合filterupdate来插入一个,如下所示。

const filter = { name: 'Will Riker' };
const update = { age: 29 };

await Character.countDocuments(filter); // 0

const doc = await Character.findOneAndUpdate(filter, update, {
new: true,
upsert: true // Make this update into an upsert
});
doc.name; // Will Riker
doc.age; // 29

The `rawResult` OptionrawResult选项

Mongoose transforms the result of findOneAndUpdate() by default: it returns the updated document. Mongoose默认转换findOneAndUpdate()的结果:它返回更新后的文档。That makes it difficult to check whether a document was upserted or not. 这使得很难检查文档是否被打乱了。In order to get the updated document and check whether MongoDB upserted a new document in the same operation, you can set the rawResult flag to make Mongoose return the raw result from MongoDB.为了获得更新后的文档并检查MongoDB是否在同一操作中打乱了新文档,您可以设置rawResult标志,使Mongoose返回MongoDB的原始结果。

const filter = { name: 'Will Riker' };
const update = { age: 29 };

await Character.countDocuments(filter); // 0

const res = await Character.findOneAndUpdate(filter, update, {
new: true,
upsert: true,
rawResult: true // Return the raw result from the MongoDB driver
});

res.value instanceof Character; // true
// The below property will be `false` if MongoDB upserted a new
// document, and `true` if MongoDB updated an existing object.
res.lastErrorObject.updatedExisting; // false

Here's what the res object from the above example looks like:上面例子中的res对象是这样的:

{ lastErrorObject:
{ n: 1,
updatedExisting: false,
upserted: 5e6a9e5ec6e44398ae2ac16a },
value:
{ _id: 5e6a9e5ec6e44398ae2ac16a,
name: 'Will Riker',
__v: 0,
age: 29 },
ok: 1 }