Поток данных Mongoose - PullRequest
       4

Поток данных Mongoose

0 голосов
/ 28 ноября 2018

Я создал простое приложение MERN, где пользователи могут оценивать телефонные номера.Пользователи просто вводят номер телефона, выбирают рейтинг (1 - 5 звезд), свой город и короткий текст.Приложение имеет функцию поиска с параметрами фильтра и сортировки.Все это работает достаточно хорошо, но я думаю, что он может сломаться, когда несколько одновременных пользователей используют веб-сайт, потому что я обновляю модель телефонных номеров (mobileSchema) после того, как рейтинг (messageSchema) был отправлен - используя промежуточное ПО Mongoose (post hooks).

Например, мне нужно рассчитать количество оценок (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 голосов
/ 02 декабря 2018

Я нашел этот ответ: Блокировка документа в MongoDB

Я вычислю все нужные мне значения (messagesCount, globalRating и т. Д.) В ловушке post, а затем проверю,Мобильный документ имеет то же значение __v (версия) во время последней операции findOneAndUpdate (поскольку эта операция блокирует документ и может увеличивать __v).Если у него другое __v, я снова вызову ловушку post, чтобы убедиться, что она вычислит правильные значения.

0 голосов
/ 07 декабря 2018

Сначала нам нужно исправить некоторую структуру базы данных здесь

Мобильная схема

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

Спасибо

0 голосов
/ 01 декабря 2018

Я думаю, что вы всегда будете сталкиваться с асинхронными проблемами с вашим подходом.Я не верю, что вы можете "синхронизировать" хуки;кажется, идет вразрез со всем, что правда о MongoDB.Однако на высоком уровне вы могли бы добиться большего успеха при сборе итогов / итогов во время выполнения, чем при попытке их синхронизации всегда.Например, если вам нужно общее количество сообщений для данного мобильного устройства, почему бы не:

Messages.find({mobile: mobile._id})

, а затем подсчитать результаты?Это избавит вас от необходимости хранить резюме и обновлять их.Тем не менее, я также думаю, что ваш текущий подход может сработать, но вам, вероятно, нужно отказаться от "countDocuments".Что-то более дружественное к асинхронности, например:

Mobile.aggregation([
    { $match: { _id: mobile._id } },
    { $add: [ "$mobile.messagesCount", 1 ] }
]);

В конечном счете, я думаю, что ваш дизайн будет усилен, если вы сохраните сообщения в виде массива внутри Mobile, так что вы можете просто отправить сообщение на него.Но чтобы ответить на вопрос напрямую, агрегация должна поддерживать порядок.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...