crakzor
crakzor

Reputation: 25

make nested mongodb queries faster

This function triggers on getinitialprops which waits for it to finish before render. Is there any way to gain speed versus waiting for this long query to finish?

exports.getAppStats = (req, res, next) => {
  const startToday = moment(req.query.today).startOf('day');
  const endToday = moment(req.query.today).endOf('day');
  const startsevenDays = moment(req.query.today).subtract(7, 'd').startOf('day');
  const startThirtyDays = moment(req.query.today).subtract(30, 'd').startOf('day');
  const startYear = moment(req.query.today).subtract(365, 'd').startOf('day');

  const stats = {}
  Application.countDocuments({ dateTime: { $gte: startToday, $lte: endToday } })
    .then((result) => {
      stats.today = result
      Application.countDocuments({ dateTime: { $gte: startsevenDays, $lte: endToday } })
      .then((result) => {
        stats.sevenDays = result
        Application.countDocuments({ dateTime: { $gte: startThirtyDays, $lte: endToday } })
        .then((result) => {
          stats.thirtyDays = result
          Application.countDocuments({ dateTime: { $gte: startYear, $lte: endToday } })
          .then((result) => {
            stats.year = result
            return res.json(stats);
          }).catch(err => console.log(err))
        }).catch(err => console.log(err))
      }).catch(err => console.log(err))
    }).catch(err => console.log(err))
}

Upvotes: 2

Views: 57

Answers (1)

mhodges
mhodges

Reputation: 11116

This is where you can leverage the Promise.all() function to run all of your queries in parallel and then wait until all of them are finished, like so:

exports.getAppStats = (req, res, next) => {
  const startToday = moment(req.query.today).startOf('day');
  const endToday = moment(req.query.today).endOf('day');
  const startsevenDays = moment(req.query.today).subtract(7, 'd').startOf('day');
  const startThirtyDays = moment(req.query.today).subtract(30, 'd').startOf('day');
  const startYear = moment(req.query.today).subtract(365, 'd').startOf('day');

  const stats = {}
  // get promises for each query
  let today = Application.countDocuments({ dateTime: { $gte: startToday, $lte: endToday } });
  let sevenDays = Application.countDocuments({ dateTime: { $gte: startsevenDays, $lte: endToday } });
  let thirtyDays = Application.countDocuments({ dateTime: { $gte: startThirtyDays, $lte: endToday } });
  let year = Application.countDocuments({ dateTime: { $gte: startYear, $lte: endToday } });
  // wait until all promises have been resolved, then set stats & send response
  Promise.all([today, sevenDays, thirtyDays, year]).then(([todayData, sevenDaysData, thirtyDaysData, yearData]) => {
    stats = {
      today: todayData,
      sevenDays: sevenDaysData,
      thirtyDays: thirtyDaysData,
      year: yearData
    }
    res.status(200).json({data: stats});
  }, (err) => {
    console.log(err);
    // next(err);
    // res.status(500).json({ message: "Something went wrong..." });
  });
}

Alternatively, you can use the async/await keywords to avoid callbacks, altogether. Although it will require a try/catch to handle errors. Honestly it makes no difference, it's purely preference.

// note this is now an async function
exports.getAppStats = async (req, res, next) => {
  const startToday = moment(req.query.today).startOf('day');
  const endToday = moment(req.query.today).endOf('day');
  const startsevenDays = moment(req.query.today).subtract(7, 'd').startOf('day');
  const startThirtyDays = moment(req.query.today).subtract(30, 'd').startOf('day');
  const startYear = moment(req.query.today).subtract(365, 'd').startOf('day');

  // get promises for each query
  let today = Application.countDocuments({ dateTime: { $gte: startToday, $lte: endToday } });
  let sevenDays = Application.countDocuments({ dateTime: { $gte: startsevenDays, $lte: endToday } });
  let thirtyDays = Application.countDocuments({ dateTime: { $gte: startThirtyDays, $lte: endToday } });
  let year = Application.countDocuments({ dateTime: { $gte: startYear, $lte: endToday } });
  try {
    // wait until all promises have been resolved, then set stats & send response
    // await Promise.all instead of using callbacks
    let [todayData, sevenDaysData, thirtyDaysData, yearData] = await Promise.all(
      [today, sevenDays, thirtyDays, year]
    );
    let stats = {
      today: todayData,
      sevenDays: sevenDaysData,
      thirtyDays: thirtyDaysData,
      year: yearData
    }
    res.status(200).json({data: stats});
  } catch(err) {
    console.log(err);
    // next(err);
    // res.status(500).json({ message: "Something went wrong..." });
  }
}

Upvotes: 4

Related Questions