title: Mongoose data flow
body: <p>I have built a simple MERN app where users can rate phone numbers. Users just fill in the phone number, choose rating (1 - 5 star rating), their city &amp; short text. The app has search function with filter &amp; sorting options. It all works good enough ATM but I think it might break when multiple concurrent users use the website because I update the phone number model (mobileSchema) after a rating (messageSchema) has been submitted - using Mongoose middlewares (post hooks).</p>
  
  <p>For example, I need to calculate number of ratings (messagesCount) for phone number. I use <code>Message.countDocuments({ mobile: mobile._id })</code> for that. However, I also need to update other properties of phone number (mobileSchema - lastMessageDate, globalRating, averageRating) so that operation takes some time. I believe the number of ratings might not be right when 2 users submit rating at the same time - it will increment the number of ratings (messagesCount) by 1 instead of 2.</p>
  
  <p>Is there a better approach? Can a post hook be fired after the previous post hook already finished?</p>
  
  <p>Sample code: </p>
  
  <pre><code>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) =&gt; {
      const { _id } = message.mobile;
      const cityId = message.city._id;
      const lastMessageDate = message.date;
      let mobile;
      hooks.get(Mobile, { _id })
          .then(mobileRes =&gt; {
              mobile = mobileRes;
              return Message.countDocuments({ mobile: mobile._id })
          })
          .then(messagesCount =&gt; {
              if (messagesCount &lt;= 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(() =&gt; {
                          return next();
                      })
                      .catch((err) =&gt; {
                          console.log(err);
                          return next();
                      })
              }
              else {
                  if (addMessage === -1) lastMessageDate = mobile.lastMessageDate;
                  const ratings = hooks.updateGlobalRating(mobile, messageModel)
                      .then(() =&gt; hooks.updateAverageRating(mobile, messageModel))
                      .then(() =&gt; {
                          return new Promise((resolve, reject) =&gt; {
                              mobile.set({
                                  messagesCount,
                                  lastMessageDate
                              });
                              mobile.save((err, mobile) =&gt; {
                                  if (err) return reject(err);
                                  resolve();
                              });
                          })
                      })
                  const cityMobile = updateCityMobile(messageModel, mobile, cityId)
                  Promise.all([ratings, cityMobile])
                      .then(([ratings, cityMobile]) =&gt; {
                          return next();
                      })
                      .catch(err =&gt; console.log(err))
              }
          })
          .catch(err =&gt; {
              console.log(err);
          })
  }
  </code></pre>