Честная точка. Вместо выполнения findById()
тогда, вероятно, лучше использовать вместо него findByIdAndUpdate()
, а также выполнить встроенное создание и вместо этого «связать» Обещание:
SongSchema.statics.addLyrics = function(songId, lyrics) {
const Lyric = mongoose.model('lyric');
return Lyric.insertMany(lyrics).then( lyrics =>
this.findByIdAndUpdate(
songId,
{ "$push": { "lyrics": { "$each": lyrics } } },
{ "new": true }
);
};
Использует $each
в качестве модификатора для $push
, который принимает и массивирует и выполняет «атомарную» операцию для обновления документа. Это намного эффективнее и безопаснее, чем извлекать документ «затем», изменяя его перед обновлением.
Также, конечно, insertMany()
делает ваш «массив» из lyrics
как одну запись в отличие от «многих».
Альтернативный подход заключается в создании экземпляров на основе Array.map()
и save()
параллельно.
SongSchema.statics.addLyrics = function(songId, lyrics) {
const Lyric = mongoose.model('lyric');
lyrics = lyrics.map(lyric => new Lyric(lyric));
return Promise.all([
this.findByIdAndUpdate(
songId,
{ "$push": { "lyrics": { "$each": lyrics } } },
{ "new": true }
),
...lyrics.map(lyric => lyric.save())
]).then(([song, ...lyrics]) => song);
};
Но первый подход действительно имеет меньше накладных расходов, и Promise.all()
не будет отвечать, пока "все" обещания не будут решены в любом случае. Таким образом, вы действительно ничего не получите, не выполняя последовательных операций.
Альтернативный случай, конечно, состоит в том, что вместо сохранения «массива» связанных ObjectId
значений в Song
вы просто записали бы songId
в записи Lyric
.
Таким образом, схема стала бы чем-то вроде:
const lyricSchema = new Schema({
title: String,
content: String,
songId: { type: Schema.Types.ObjectId, ref: 'Song' }
})
Тогда вставка просто
lyricSchema.statics.addLyrics = function(songId, lyrics) {
return this.insertMany(lyrics.map(lyric => ({ ...lyric, songId })))
}
И в схеме Song
вместо хранения массива, подобного этому:
songs: [{ type: Schema.Types.ObjectId, ref: 'Lyric' }]
Удалите это и замените на virtual ,
SongSchema.virtual.('songs', {
ref: 'Lyric',
localField: '_id',
foreignField: 'songId'
});
А это значит, что вообще не нужно прикасаться к модели Song
, так как вы можете просто вставить соответствующие данные без необходимости обновления массива.
Современные версии MongoDB действительно должны использовать $lookup
для «запроса» этой информации в любом случае, и поддержание «массивов» в родительском элементе является чем-то вроде «анти-паттерна», который обычно должен следует избегать.
«Виртуальный», следовательно, «необязательный», и просто способ включить populate()
для удобства.