How to Use Mongoose's findOneAndUpdate Function
Mongoose's findOneAndUpdate()
function finds the first document that matches a given filter
, applies an update
, and returns the document. Unlike updateOne()
, findOneAndUpdate()
returns the updated document.
Unlike save()
, findOneAndUpdate()
is atomic: the document can't change between when MongoDB finds the document and when MongoDB applies the update.
Getting Started
You need at least 2 parameters to call findOneAndUpdate()
: filter
and update
. MongoDB finds the first document that matches filter
and applies update
. By default, findOneAndUpdate()
returns the
document as it was before MongoDB applied update
.
const Character = mongoose.model('Character', Schema({
name: String,
rank: String
}));
await Character.create({ name: 'Luke Skywalker' });
// By default, `findOneAndUpdate()` returns the document as
// it was **before** MongoDB applied the update.
const filter = { name: 'Luke Skywalker' };
const update = { rank: 'Jedi Knight' };
let doc = await Character.findOneAndUpdate(filter, update);
doc.name; // 'Luke Skywalker'
doc.rank; // undefined
// But the document is updated in the database:
doc = await Character.findOne(filter);
doc.rank; // 'Jedi Knight'
To return the document as it was after MongoDB applied the given
update
, you need to set the new
option to true
:
// If you set the `new` option to `true`, Mongoose will
// return the document with the update applied.
const filter = { name: 'Luke Skywalker' };
const update = { rank: 'Jedi Knight' };
const opts = { new: true };
let doc = await Character.findOneAndUpdate(filter, update, opts);
doc.name; // 'Luke Skywalker'
doc.rank; // 'Jedi Knight'
Upserts
There are several other options for findOneAndUpdate()
. For example,
you can set the upsert
option to insert a new document if there isn't one that matches filter
.
await Character.deleteMany({});
const filter = { name: 'Luke Skywalker' };
const update = { rank: 'Jedi Knight' };
// If you set the `upsert` option, Mongoose will insert
// a new document if one isn't found.
const opts = { new: true, upsert: true };
let doc = await Character.findOneAndUpdate(filter, update, opts);
doc.name; // 'Luke Skywalker'
doc.rank; // 'Jedi Knight'
// If `new` is `false` and an upsert happened,
// `findOneAndUpdate()` will return `null`
await Character.deleteMany({});
opts.new = false;
doc = await Character.findOneAndUpdate(filter, update, opts);
doc; // null
Middleware
Mongoose has dedicated middleware for findOneAndUpdate()
. Calling findOneAndUpdate()
does not fire findOne
, updateOne
, or save
middleware. But it
does fire findOneAndUpdate
middleware.
const schema = Schema({
name: String,
rank: String
});
schema.pre('findOneAndUpdate', function middleware() {
this.getFilter(); // { name: 'Luke Skywalker' }
this.getUpdate(); // { rank: 'Jedi Knight' }
});
const Character = mongoose.model('Character', schema);
const filter = { name: 'Luke Skywalker' };
const update = { rank: 'Jedi Knight' };
// Mongoose calls the `middleware()` function above
await Character.findOneAndUpdate(filter, update, opts);