From f923413671e869854611912268bdc868e28b2f4d Mon Sep 17 00:00:00 2001 From: prabalsingh24 Date: Tue, 9 Jun 2020 10:57:56 +0530 Subject: [PATCH] fix n+1 problem for zoom account availability (#82) * fix n+1 problem for zoom account availability * spead options and reduce statements * replace findOne with findAll * reduce one loop * don't remove some functions * fix conflicts * use findAll and loaderBaseClass * remove redundant code and excludeTopic-->excludeEvent * fix function name --- api/graphql/query/Resource/availability.js | 15 ++--- api/graphql/query/ZoomAccount/availability.js | 11 ++-- api/routes/graphql/index.js | 2 + .../ZoomAccount/Loaders/Availability.js | 64 +++++++++++++++++++ api/services/ZoomAccount/index.js | 4 +- 5 files changed, 81 insertions(+), 15 deletions(-) create mode 100644 api/services/ZoomAccount/Loaders/Availability.js diff --git a/api/graphql/query/Resource/availability.js b/api/graphql/query/Resource/availability.js index 02ecdd6..cba538e 100644 --- a/api/graphql/query/Resource/availability.js +++ b/api/graphql/query/Resource/availability.js @@ -10,17 +10,16 @@ class ZoomAccountAvailability extends BaseResolver { }); const topic = await resource.getTopic(); - if (this.parent.subject_type === 'ZoomAccount') - return ZoomAccount.findAvailaibilityDuring( - this.parent.subject_id, - { + if (this.parent.subject_type === 'ZoomAccount') { + return await this.ctx.loaders.zoomAccountAvailability.load({ + id: this.parent.id, + dateTimeRange: { start_at: topic.start_at, end_at: topic.end_at, }, - { - excludeTopics: [topic.id], - }, - ); + excludeEvents: [topic.id], + }); + } //NOTE(naman) For a new resource add finding availability logic here return false; diff --git a/api/graphql/query/ZoomAccount/availability.js b/api/graphql/query/ZoomAccount/availability.js index 0017a42..5587080 100644 --- a/api/graphql/query/ZoomAccount/availability.js +++ b/api/graphql/query/ZoomAccount/availability.js @@ -1,12 +1,11 @@ -import ZoomAccount from 'Services/ZoomAccount'; import BaseResolver from 'Graphql/base/Resolver'; class ZoomAccountAvailability extends BaseResolver { - resolve = () => - ZoomAccount.findAvailaibilityDuring( - this.parent.id, - this.args.dateTimeRange, - ); + resolve = async () => + await this.ctx.loaders.zoomAccountAvailability.load({ + id: this.parent.id, + dateTimeRange: this.args.dateTimeRange, + }); } export default ZoomAccountAvailability.resolver(); diff --git a/api/routes/graphql/index.js b/api/routes/graphql/index.js index 1732495..442606d 100644 --- a/api/routes/graphql/index.js +++ b/api/routes/graphql/index.js @@ -5,6 +5,7 @@ import { ApolloServer } from 'apollo-server-micro'; import authenticate from 'Middlewares/authenticate'; import CalendarEventInvite from 'Services/CalendarEventInvite'; import User from 'Services/User'; +import ZoomAccount from 'Services/ZoomAccount'; const server = new ApolloServer({ schema, @@ -13,6 +14,7 @@ const server = new ApolloServer({ loaders: { userAvailability: User.getAvailabilityLoader(), eventInviteStatus: CalendarEventInvite.getStatusLoader(), + zoomAccountAvailability: ZoomAccount.getAvailabilityLoader() }, }), }); diff --git a/api/services/ZoomAccount/Loaders/Availability.js b/api/services/ZoomAccount/Loaders/Availability.js new file mode 100644 index 0000000..6d9c995 --- /dev/null +++ b/api/services/ZoomAccount/Loaders/Availability.js @@ -0,0 +1,64 @@ +import Models from 'Models'; +import BaseLoader from 'Services/BaseModelService/Loader'; +import overlapDateTimeClause from 'Utils/overlapDateTimeClause'; + +export default class Availability extends BaseLoader { + load = async () => { + const accounts = await Models.ZoomAccount.findAll({ + include: [ + { + model: Models.Resource, + as: 'uses', + include: [ + { + model: Models.CalendarEvent, + as: 'calendarEvent', + where: { + [Models.Sequelize.Op.or]: this.keys.map( + ({ id, dateTimeRange, excludeEvents = [] }) => ({ + [Models.Sequelize.Op.and]: [ + overlapDateTimeClause(dateTimeRange), + { + '$zoom_account.id$': id, + }, + { + id: { + [Models.Sequelize.Op.notIn]: excludeEvents, + }, + }, + ], + }), + ), + }, + required: true, + }, + ], + required: true, + }, + ], + }); + + return this.keys.map( + (key) => + !accounts.find((account) => this.equateKeyAndAccount(key, account)), + ); + }; + + equateKeyAndAccount({ id, excludedEvents = [], dateTimeRange }, account) { + if ( + account.id !== id || + excludedEvents.includes(account['uses.calendarEvent.id']) + ) + return false; + + if ( + new Date(account['uses.calendarEvent.start_at']) > + new Date(dateTimeRange.end_at) || + new Date(account['uses.calendarEvent.end_at']) < + new Date(dateTimeRange.start_at) + ) + return false; + + return true; + } +} diff --git a/api/services/ZoomAccount/index.js b/api/services/ZoomAccount/index.js index 8c737aa..f04cc13 100644 --- a/api/services/ZoomAccount/index.js +++ b/api/services/ZoomAccount/index.js @@ -4,6 +4,7 @@ import BaseModelService, { requireInstance, } from 'Services/BaseModelService'; import { utilizedResourceClause } from './utils'; +import Availability from './Loaders/Availability'; export default class ZoomAccount extends BaseModelService { static findAllInUse(...args) { @@ -15,10 +16,11 @@ export default class ZoomAccount extends BaseModelService { where: { id }, ...utilizedResourceClause(...args), }); - return !data; } + static getAvailabilityLoader = Availability.loader(); + @requireInstance ifAvailableDuring(dateTimeRange, options) { return ZoomAccount.findAvailaibilityDuring(