我建立了一个简单的MERN应用程序,用户可以在其中对电话号码进行评分。用户只需填写电话号码,选择评分(1-5星评分),他们的城市和短文字。该应用程序具有带有过滤器和排序选项的搜索功能。这一切都足以在ATM上正常运行,但是我认为当多个并发用户使用该网站时,它可能会中断,因为我在提交了评级(messageSchema)后使用Mongoose中间件(后钩)更新了电话号码模型(mobileSchema)。
例如,我需要计算电话号码的等级数(messagesCount)。为此,我使用Message.countDocuments({ mobile: mobile._id })
。但是,我还需要更新电话号码的其他属性(mobileSchema-lastMessageDate,globalRating,averageRating),以便操作花费一些时间。我认为,当2个用户同时提交评级时,评级的数量可能不正确-它将使评级(messagesCount)的数量增加1,而不是2。
有没有更好的方法?前一个挂钩已经完成后,可以解雇一个挂钩吗?
示例代码:
const mobileSchema = new Schema({
number: { type: String, required: true },
plan: { type: String, required: true },
date: { type: Date, default: Date.now, required: true, index: 1 },
messagesCount: { type: Number, default: 0, index: 1 },
lastMessageDate: { type: Date, index: 1 },
// normal mean
globalRating: { type: Number, default: 0, index: 1 },
// weighted mean
averageRating: { type: Number, default: 0, index: 1 }
});
const messageSchema = new Schema({
comment: { type: String, required: true },
city: { type: Schema.Types.ObjectId, ref: 'City', required: true, index: 1 },
rating: { type: Number, required: true, index: 1 },
date: { type: Date, default: Date.now, required: true, index: 1 },
mobile: { type: Schema.Types.ObjectId, ref: 'Mobile', required: true },
user: { type: Schema.Types.ObjectId, ref: 'User', required: true }
});
messageSchema.post('save', function (message, next) {
const messageModel = this.constructor;
return updateMobile(messageModel, message, next, 1);
});
const updateMobile = (messageModel, message, next, addMessage) => {
const { _id } = message.mobile;
const cityId = message.city._id;
const lastMessageDate = message.date;
let mobile;
hooks.get(Mobile, { _id })
.then(mobileRes => {
mobile = mobileRes;
return Message.countDocuments({ mobile: mobile._id })
})
.then(messagesCount => {
if (messagesCount <= 0) {
const deleteMobile = Mobile.findOneAndDelete({ _id: mobile._id })
const deleteSeen = SeenMobile.findOneAndDelete({ mobile: mobile._id, user: message.user._id })
const cityMobile = updateCityMobile(messageModel, mobile, cityId)
Promise.all([deleteMobile, deleteSeen, cityMobile])
.then(() => {
return next();
})
.catch((err) => {
console.log(err);
return next();
})
}
else {
if (addMessage === -1) lastMessageDate = mobile.lastMessageDate;
const ratings = hooks.updateGlobalRating(mobile, messageModel)
.then(() => hooks.updateAverageRating(mobile, messageModel))
.then(() => {
return new Promise((resolve, reject) => {
mobile.set({
messagesCount,
lastMessageDate
});
mobile.save((err, mobile) => {
if (err) return reject(err);
resolve();
});
})
})
const cityMobile = updateCityMobile(messageModel, mobile, cityId)
Promise.all([ratings, cityMobile])
.then(([ratings, cityMobile]) => {
return next();
})
.catch(err => console.log(err))
}
})
.catch(err => {
console.log(err);
})
}
答案 0 :(得分:3)
我认为您总是会遇到与您的方法有关的异步问题。我不相信您可以“同步”这些钩子;似乎有悖于MongoDB的一切。但是,从较高的层次上讲,您可能会在运行时获取总计/摘要方面取得更大的成功,而不是尝试使它们始终保持同步。例如,如果您需要给定移动设备的消息总数,为什么不这样做:
Messages.find({mobile: mobile._id})
然后计算结果?这样可以节省您存储摘要并保持更新的时间。但是,我也认为您当前的方法可行,但是您可能需要废弃“ countDocuments”。一些异步友好的东西,例如:
Mobile.aggregation([
{ $match: { _id: mobile._id } },
{ $add: [ "$mobile.messagesCount", 1 ] }
]);
最终,我认为如果将Messages作为数组存储在Mobile内,那么您的设计将会得到加强,因此您只需将消息推送到其中即可。但是要直接回答这个问题,聚合应该使一切保持整洁。
答案 1 :(得分:0)
我找到了这个答案:Locking a document in MongoDB
我将计算后挂接中所需的所有值(messagesCount,globalRating等),然后在最终findOneAndUpdate操作期间检查移动文档是否具有相同的__v(版本)值(因为此操作将文档锁定并可以增加__v)。如果它的__v不同,那么我将再次调用post钩子,以确保它将计算正确的值。
答案 2 :(得分:0)
首先,我们需要在此处修复一些数据库结构
移动架构
const mobileSchema = new Schema({
number: { type: String, required: true },
plan: { type: String, required: true },
date: { type: Date, default: Date.now, required: true, index: 1 },
//messagesCount: { type: Number, default: 0, index: 1 },
//lastMessageDate: { type: Date, index: 1 },
// normal mean
//globalRating: { type: Number, default: 0, index: 1 },
// weighted mean
//averageRating: { type: Number, default: 0, index: 1 }
});
消息架构
const messageSchema = new Schema({
comment: { type: String, required: true },
city: { type: Schema.Types.ObjectId, ref: 'City', required: true, index: 1 },
//rating: { type: Number, required: true, index: 1 },
date: { type: Date, default: Date.now, required: true, index: 1 },
mobile: { type: Schema.Types.ObjectId, ref: 'Mobile', required: true },
user: { type: Schema.Types.ObjectId, ref: 'User', required: true }
});
评分系统(接受所有评分或将其设为一组) (分子和分母达到100个评级后,很难读取每个数字)也可以检查手机
const ratingSchema = new Schema({
mobile: { type: String, required: true },
commmentId:{type:String, required: true, index: 1}
rate: { type: Number required: true, },
//rating: { type: Number, required: true, index: 1 },
timestamp: { type: Date, default: Date.now, required: true, index: 1 }
denominator:{ type: Number},
numerator:{type:Number}
});
谢谢