Что вы пропустили, так это то, что промежуточное ПО post
имеет первый аргумент в качестве "документа", а не обработчик next
:
user.js
const { Schema } = mongoose = require('mongoose');
const userSchema = new Schema({
firstName: String,
lastName: String,
posts: [{ type: Schema.Types.ObjectId, ref: 'Post' }]
});
post.js
const { Schema } = mongoose = require('mongoose');
const User = require('./user');
const postSchema = new Schema({
user: { type: Schema.Types.ObjectId, ref: 'User' },
title: String,
body: String
});
// note that first argument is the "document" as in "post" once it was created
postSchema.post('save', async function(doc, next) {
await User.update({ _id: doc.user._id },{ $push: { posts: doc._id } });
next();
});
index.js
const { Schema } = mongoose = require('mongoose');
const User = require('./user');
const Post = require('./post');
const uri = 'mongodb://localhost/posttest';
mongoose.set('debug', true);
mongoose.Promise = global.Promise;
const log = data => console.log(JSON.stringify(data, undefined, 2));
(async function() {
try {
const conn = await mongoose.connect(uri);
await Promise.all(Object.entries(conn.models).map(([k,m]) => m.remove()));
let user = await User.create({ firstName: 'Ted', lastName: 'Logan' });
let post = new Post({ user: user._id, title: 'Hi', body: 'Whoa!' });
post = await post.save();
mongoose.disconnect();
} catch(e) {
console.error(e)
} finally {
process.exit()
}
})()
Возвращает:
Mongoose: users.remove({}, {})
Mongoose: posts.remove({}, {})
Mongoose: users.insertOne({ posts: [], _id: ObjectId("5b0217001b5a55208150cc9b"), firstName: 'Ted', lastName: 'Logan', __v: 0 })
Mongoose: posts.insertOne({ _id: ObjectId("5b0217001b5a55208150cc9c"), user: ObjectId("5b0217001b5a55208150cc9b"), title: 'Hi', body: 'Whoa!', __v: 0 })
Mongoose: users.update({ _id: ObjectId("5b0217001b5a55208150cc9b") }, { '$push': { posts: ObjectId("5b0217001b5a55208150cc9c") } }, {})
Показывает, что обновление запускается с правильной детализацией.
В хорошемдизайн, вы действительно должны избегать этого и просто удалить массив posts
из модели User
.Вы всегда можете использовать virtual вместо:
userSchema.virtual('posts', {
ref: 'Post',
localField: '_id',
foreignField: 'user'
})
или просто получить данные через $lookup
:
User.aggregate([
{ "$match": { "_id": userId } }
{ "$lookup": {
"from": Post.collection.name,
"localField": "_id",
"foreignField": "user",
"as": "posts"
}}
])
Хранениеи поддержание массивов связанных ObjectId
значений «на родительском» является своего рода «анти-паттерном» и приводит к ненужным накладным расходам, таким как запись в двух местах, где вам нужен только «один».
Также вВ общем, вы должны выбрать встраивание «first» и рассматривать «обращение» только в том случае, если и когда шаблон использования приложения действительно этого требует.Простое копирование тех же шаблонов СУБД с ядром базы данных, которое не было разработано для этого, не лучший способ использовать его.