Как использовать findOneAndUpdate() в Mongoose
Функция findOneAndUpdate() в Mongoose имеет широкий спектр применений. Вы должны использовать save() для обновления документов, где это возможно, для лучшей валидации и поддержки средств промежуточного ПО. Однако есть случаи, когда вам необходимо использовать findOneAndUpdate(). В этом руководстве вы узнаете, как использовать findOneAndUpdate(), и поймёте, когда это необходимо.
Начало работы
Как следует из названия, findOneAndUpdate() находит первый документ, соответствующий заданному filter, применяет update, и возвращает документ. По умолчанию findOneAndUpdate() возвращает документ в том виде, в котором он был до применения update.
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
Вы должны установить опцию new в true, чтобы вернуть документ после применения update.
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
findOneAndUpdate() Mongoose немного отличается от findOneAndUpdate() драйвера MongoDB Node.js, потому что он возвращает сам документ, а не объект результата.
В качестве альтернативы опции new, вы также можете использовать опцию returnOriginal. returnOriginal: false эквивалентна new: true. Опция returnOriginal существует для согласованности с findOneAndUpdate() драйвера MongoDB Node.js, которая имеет ту же опцию.
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
Атомарные обновления
За исключением неиндексированного upsert, findOneAndUpdate() атомарны. Это означает, что вы можете предположить, что документ не изменился между моментом, когда MongoDB находит документ, и моментом, когда он обновляет документ, если только вы не выполняете upsert.
Например, если вы используете save() для обновления документа, документ может измениться в MongoDB между моментом загрузки документа с помощью findOne() и моментом сохранения документа с помощью save(), как показано ниже. Для многих случаев использования проблема гонки save() не актуальна. Но вы можете обойти её с помощью 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
Используя опцию upsert, вы можете использовать findOneAndUpdate() как операцию поиска и upsert. Upsert ведёт себя как обычная операция findOneAndUpdate() , если находит документ, соответствующий filter. Однако, если ни один документ не соответствует filter, MongoDB вставит новый документ, объединив filter и update, как показано ниже.
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
Опция `rawResult`
Mongoose по умолчанию преобразует результат findOneAndUpdate(): он возвращает обновлённый документ. Это затрудняет проверку того, был ли документ обновлён или добавлен. Чтобы получить обновлённый документ и проверить, был ли новый документ добавлен 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
Вот как выглядит объект res из приведённого выше примера:
{ lastErrorObject:
{ n: 1,
updatedExisting: false,
upserted: 5e6a9e5ec6e44398ae2ac16a },
value:
{ _id: 5e6a9e5ec6e44398ae2ac16a,
name: 'Will Riker',
__v: 0,
age: 29 },
ok: 1 }
© 2010 LearnBoost
Licensed under the MIT License.
https://mongoosejs.com/docs/tutorials/findoneandupdate.html