From 7cf840723b0083c6fbd07cbf0c2afe95b567eb2a Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Tue, 7 May 2024 15:18:07 +0100 Subject: [PATCH] Async scalars & less queries due to grouping (#14904) --- apps/web/pages/api/cron/monthlyDigestEmail.ts | 43 ++---- packages/features/insights/server/events.ts | 124 +++--------------- .../features/insights/server/trpc-router.ts | 115 +++++++--------- 3 files changed, 76 insertions(+), 206 deletions(-) diff --git a/apps/web/pages/api/cron/monthlyDigestEmail.ts b/apps/web/pages/api/cron/monthlyDigestEmail.ts index 2ff9314a36..199579c417 100644 --- a/apps/web/pages/api/cron/monthlyDigestEmail.ts +++ b/apps/web/pages/api/cron/monthlyDigestEmail.ts @@ -110,43 +110,18 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) teamId: null, }, ], + createdAt: { + gte: dayjs(firstDateOfMonth).toISOString(), + lte: dayjs(new Date()).toISOString(), + }, }; - const promisesResult = await Promise.all([ - EventsInsights.getCreatedEventsInTimeRange( - { - start: dayjs(firstDateOfMonth), - end: dayjs(new Date()), - }, - whereConditional - ), - EventsInsights.getCompletedEventsInTimeRange( - { - start: dayjs(firstDateOfMonth), - end: dayjs(new Date()), - }, - whereConditional - ), - EventsInsights.getRescheduledEventsInTimeRange( - { - start: dayjs(firstDateOfMonth), - end: dayjs(new Date()), - }, - whereConditional - ), - EventsInsights.getCancelledEventsInTimeRange( - { - start: dayjs(firstDateOfMonth), - end: dayjs(new Date()), - }, - whereConditional - ), - ]); + const countGroupedByStatus = await EventsInsights.countGroupedByStatus(whereConditional); - EventData["Created"] = promisesResult[0]; - EventData["Completed"] = promisesResult[1]; - EventData["Rescheduled"] = promisesResult[2]; - EventData["Cancelled"] = promisesResult[3]; + EventData["Created"] = countGroupedByStatus["_all"]; + EventData["Completed"] = countGroupedByStatus["completed"]; + EventData["Rescheduled"] = countGroupedByStatus["rescheduled"]; + EventData["Cancelled"] = countGroupedByStatus["cancelled"]; // Most Booked Event Type const bookingWhere: Prisma.BookingTimeStatusWhereInput = { diff --git a/packages/features/insights/server/events.ts b/packages/features/insights/server/events.ts index 5ec6593a10..ba7378b888 100644 --- a/packages/features/insights/server/events.ts +++ b/packages/features/insights/server/events.ts @@ -13,115 +13,29 @@ interface ITimeRange { type TimeViewType = "week" | "month" | "year" | "day"; class EventsInsights { - static getBookingsInTimeRange = async ( - timeRange: ITimeRange, - where: Prisma.BookingTimeStatusWhereInput - ) => { - const { start, end } = timeRange; - - const events = await prisma.bookingTimeStatus.count({ - where: { - ...where, - createdAt: { - gte: start.toISOString(), - lte: end.toISOString(), - }, - }, - }); - - return events; - }; - - static getCreatedEventsInTimeRange = async ( - timeRange: ITimeRange, - where: Prisma.BookingTimeStatusWhereInput - ) => { - const result = await this.getBookingsInTimeRange(timeRange, where); - - return result; - }; - - static getCancelledEventsInTimeRange = async ( - timeRange: ITimeRange, - where: Prisma.BookingTimeStatusWhereInput - ) => { - const result = await this.getBookingsInTimeRange(timeRange, { - ...where, - timeStatus: "cancelled", - }); - - return result; - }; - - static getCompletedEventsInTimeRange = async ( - timeRange: ITimeRange, - where: Prisma.BookingTimeStatusWhereInput - ) => { - const result = await this.getBookingsInTimeRange(timeRange, { - ...where, - timeStatus: "completed", - }); - - return result; - }; - - static getRescheduledEventsInTimeRange = async ( - timeRange: ITimeRange, - where: Prisma.BookingTimeStatusWhereInput - ) => { - const result = await this.getBookingsInTimeRange(timeRange, { - ...where, - timeStatus: "rescheduled", - }); - - return result; - }; - - static getNoShowHostsInTimeRange = async ( - timeRange: ITimeRange, - where: Prisma.BookingTimeStatusWhereInput - ) => { - const result = await this.getBookingsInTimeRange(timeRange, { - ...where, - noShowHost: true, - }); - - return result; - }; - - static getBaseBookingCountForEventStatus = async (where: Prisma.BookingTimeStatusWhereInput) => { - const baseBookings = await prisma.bookingTimeStatus.count({ + static countGroupedByStatus = async (where: Prisma.BookingTimeStatusWhereInput) => { + const data = await prisma.bookingTimeStatus.groupBy({ where, - }); - - return baseBookings; - }; - - static getTotalCompletedEvents = async (whereConditional: Prisma.BookingTimeStatusWhereInput) => { - return await prisma.bookingTimeStatus.count({ - where: { - ...whereConditional, - timeStatus: "completed", + by: ["timeStatus"], + _count: { + _all: true, }, }); - }; - - static getTotalRescheduledEvents = async (whereConditional: Prisma.BookingTimeStatusWhereInput) => { - return await prisma.bookingTimeStatus.count({ - where: { - ...whereConditional, - timeStatus: "rescheduled", + return data.reduce( + (aggregate: { [x: string]: number }, item) => { + if (typeof item.timeStatus === "string") { + aggregate[item.timeStatus] = item._count._all; + aggregate["_all"] += item._count._all; + } + return aggregate; }, - }); - }; - - static getTotalCancelledEvents = async (whereConditional: Prisma.BookingTimeStatusWhereInput) => { - return await prisma.bookingTimeStatus.count({ - where: { - ...whereConditional, - timeStatus: "cancelled", - }, - }); + { + completed: 0, + rescheduled: 0, + cancelled: 0, + _all: 0, + } + ); }; static getAverageRating = async (whereConditional: Prisma.BookingTimeStatusWhereInput) => { diff --git a/packages/features/insights/server/trpc-router.ts b/packages/features/insights/server/trpc-router.ts index 9650e06464..8cd051ae98 100644 --- a/packages/features/insights/server/trpc-router.ts +++ b/packages/features/insights/server/trpc-router.ts @@ -259,24 +259,8 @@ export const insightsRouter = router({ }, }; - const baseBookingsCount = await EventsInsights.getBaseBookingCountForEventStatus(baseWhereCondition); - const startTimeEndTimeDiff = dayjs(endDate).diff(dayjs(startDate), "day"); - const totalCompleted = await EventsInsights.getTotalCompletedEvents(baseWhereCondition); - - const totalRescheduled = await EventsInsights.getTotalRescheduledEvents(baseWhereCondition); - - const totalCancelled = await EventsInsights.getTotalCancelledEvents(baseWhereCondition); - - const totalRatingsAggregate = await EventsInsights.getAverageRating(baseWhereCondition); - const averageRating = totalRatingsAggregate._avg.rating - ? parseFloat(totalRatingsAggregate._avg.rating.toFixed(1)) - : 0; - - const totalNoShow = await EventsInsights.getTotalNoShows(baseWhereCondition); - const totalCSAT = await EventsInsights.getTotalCSAT(baseWhereCondition); - const lastPeriodStartDate = dayjs(startDate).subtract(startTimeEndTimeDiff, "day"); const lastPeriodEndDate = dayjs(endDate).subtract(startTimeEndTimeDiff, "day"); @@ -289,23 +273,43 @@ export const insightsRouter = router({ teamId: teamId, }; - const lastPeriodBaseBookingsCount = await EventsInsights.getBaseBookingCountForEventStatus( - lastPeriodBaseCondition - ); + const [ + countGroupedByStatus, + totalRatingsAggregate, + totalNoShow, + totalCSAT, + lastPeriodCountGroupedByStatus, + lastPeriodTotalRatingsAggregate, + lastPeriodTotalNoShow, + lastPeriodTotalCSAT, + ] = await Promise.all([ + EventsInsights.countGroupedByStatus(baseWhereCondition), + EventsInsights.getAverageRating(baseWhereCondition), + EventsInsights.getTotalNoShows(baseWhereCondition), + EventsInsights.getTotalCSAT(baseWhereCondition), + EventsInsights.countGroupedByStatus(lastPeriodBaseCondition), + EventsInsights.getAverageRating(lastPeriodBaseCondition), + EventsInsights.getTotalNoShows(lastPeriodBaseCondition), + EventsInsights.getTotalCSAT(lastPeriodBaseCondition), + ]); - const lastPeriodTotalRescheduled = await EventsInsights.getTotalRescheduledEvents( - lastPeriodBaseCondition - ); + const baseBookingsCount = countGroupedByStatus["_all"]; + const totalCompleted = countGroupedByStatus["completed"]; + const totalRescheduled = countGroupedByStatus["rescheduled"]; + const totalCancelled = countGroupedByStatus["cancelled"]; + + const averageRating = totalRatingsAggregate._avg.rating + ? parseFloat(totalRatingsAggregate._avg.rating.toFixed(1)) + : 0; + + const lastPeriodBaseBookingsCount = lastPeriodCountGroupedByStatus["_all"]; + const lastPeriodTotalRescheduled = lastPeriodCountGroupedByStatus["rescheduled"]; + const lastPeriodTotalCancelled = lastPeriodCountGroupedByStatus["cancelled"]; - const lastPeriodTotalCancelled = await EventsInsights.getTotalCancelledEvents(lastPeriodBaseCondition); - const lastPeriodTotalRatingsAggregate = await EventsInsights.getAverageRating(lastPeriodBaseCondition); const lastPeriodAverageRating = lastPeriodTotalRatingsAggregate._avg.rating ? parseFloat(lastPeriodTotalRatingsAggregate._avg.rating.toFixed(1)) : 0; - const lastPeriodTotalNoShow = await EventsInsights.getTotalNoShows(lastPeriodBaseCondition); - const lastPeriodTotalCSAT = await EventsInsights.getTotalCSAT(lastPeriodBaseCondition); - const result = { empty: false, created: { @@ -525,48 +529,25 @@ export const insightsRouter = router({ startDate = dayjs(date).startOf("day"); endDate = dayjs(date).add(6, "day").endOf("day"); } + + whereConditional = { + ...whereConditional, + createdAt: { + gte: startDate.toISOString(), + lte: endDate.toISOString(), + }, + }; + const promisesResult = await Promise.all([ - EventsInsights.getCreatedEventsInTimeRange( - { - start: startDate, - end: endDate, - }, - whereConditional - ), - EventsInsights.getCompletedEventsInTimeRange( - { - start: startDate, - end: endDate, - }, - whereConditional - ), - EventsInsights.getRescheduledEventsInTimeRange( - { - start: startDate, - end: endDate, - }, - whereConditional - ), - EventsInsights.getCancelledEventsInTimeRange( - { - start: startDate, - end: endDate, - }, - whereConditional - ), - EventsInsights.getNoShowHostsInTimeRange( - { - start: startDate, - end: endDate, - }, - whereConditional - ), + EventsInsights.countGroupedByStatus(whereConditional), + EventsInsights.getTotalNoShows(whereConditional), ]); - EventData["Created"] = promisesResult[0]; - EventData["Completed"] = promisesResult[1]; - EventData["Rescheduled"] = promisesResult[2]; - EventData["Cancelled"] = promisesResult[3]; - EventData["No-Show (Host)"] = promisesResult[4]; + + EventData["Created"] = promisesResult[0]["_all"]; + EventData["Completed"] = promisesResult[0]["completed"]; + EventData["Rescheduled"] = promisesResult[0]["rescheduled"]; + EventData["Cancelled"] = promisesResult[0]["cancelled"]; + EventData["No-Show (Host)"] = promisesResult[1]; result.push(EventData); }