猫鼬数据流

时间:2018-11-27 21:57:13

标签: javascript mongodb express mongoose

我建立了一个简单的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);
        })
}

3 个答案:

答案 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}
});

谢谢