From 7e7e2982b15f8d1b022aea39772ae51b6959b423 Mon Sep 17 00:00:00 2001 From: Aashish John Date: Fri, 5 Jun 2026 04:18:07 +0000 Subject: [PATCH] perf(campaigns): standardize queries for listed statuses --- src/server/api/campaign.js | 148 +++++++-------------------------- src/server/api/lib/campaign.ts | 108 ++++++++++-------------- 2 files changed, 75 insertions(+), 181 deletions(-) diff --git a/src/server/api/campaign.js b/src/server/api/campaign.js index 861c7d05b..75cf997e5 100644 --- a/src/server/api/campaign.js +++ b/src/server/api/campaign.js @@ -9,7 +9,13 @@ import MemoizeHelper, { Buckets, cacheOpts } from "../memoredis"; import { cacheableData, r } from "../models"; import { currentEditors } from "../models/cacheable_queries"; import { accessRequired } from "./errors"; -import { getDeliverabilityStats, invalidScriptFields } from "./lib/campaign"; +import { + buildHasUnassignedContactsSubquery, + buildHasUnhandledRepliesSubquery, + buildHasUnsentInitialsSubquery, + getDeliverabilityStats, + invalidScriptFields +} from "./lib/campaign"; import { symmetricEncrypt } from "./lib/crypto"; import { getMessagingServiceById } from "./lib/message-sending"; import { formatPage } from "./lib/pagination"; @@ -125,6 +131,20 @@ const getCampaignOrganization = async ({ campaignId }) => { return campaign.organization_id; }; +const checkCampaignContactsExist = async (campaign, buildSubquery) => { + if ( + config.HIDE_CAMPAIGN_STATE_VARS_ON_ARCHIVED_CAMPAIGNS && + campaign.is_archived + ) + return false; + + const { rows } = await r.reader.raw("SELECT EXISTS(?) as exists", [ + buildSubquery(campaign.id).whereRaw(`archived = ${campaign.is_archived}`) // partial index friendly + ]); + + return rows[0].exists; +}; + export const resolvers = { JobRequest: { ...sqlResolvers([ @@ -586,124 +606,16 @@ export const resolvers = { return row?.contacts_filename ?? null; }, hasUnassignedContacts: async (campaign) => { - if (config.BAD_BENS_DISABLE_HAS_UNASSIGNED_CONTACTS) { - return false; - } - - if ( - config.HIDE_CAMPAIGN_STATE_VARS_ON_ARCHIVED_CAMPAIGNS && - campaign.is_archived - ) { - return false; - } - - const getHasUnassignedContacts = async ({ campaignId, archived }) => { - // SQL injection for archived = to enable use of partial index - const { rows } = await r.reader.raw( - ` - select exists ( - select 1 - from campaign_contact - where - campaign_id = ? - and assignment_id is null - and archived = ${archived} - and not exists ( - select 1 - from campaign_contact_tag - join tag on campaign_contact_tag.tag_id = tag.id - where tag.is_assignable = false - and campaign_contact_tag.campaign_contact_id = campaign_contact.id - ) - and is_opted_out = false - ) as contact_exists - `, - [campaignId] - ); - - return rows[0] && rows[0].contact_exists; - }; - - return getHasUnassignedContacts({ - campaignId: campaign.id, - archived: campaign.is_archived - }); - }, - hasUnsentInitialMessages: async (campaign) => { - if ( - config.HIDE_CAMPAIGN_STATE_VARS_ON_ARCHIVED_CAMPAIGNS && - campaign.is_archived - ) { - return false; - } - - const getHasUnsentInitialMessages = async ({ campaignId, archived }) => { - const contacts = await r - .reader("campaign_contact") - .select("id") - .where({ - campaign_id: campaignId, - message_status: "needsMessage", - is_opted_out: false - }) - .whereRaw(`archived = ${archived}`) // partial index friendly - .limit(1); - return contacts.length > 0; - }; - - return getHasUnsentInitialMessages({ - campaignId: campaign.id, - archived: campaign.is_archived - }); - }, - hasUnhandledMessages: async (campaign) => { - if ( - config.HIDE_CAMPAIGN_STATE_VARS_ON_ARCHIVED_CAMPAIGNS && - campaign.is_archived - ) { - return false; - } - - const getHasUnhandledMessages = async ({ - campaignId, - archived, - organizationId - }) => { - let contactsQuery = r - .reader("campaign_contact") - .pluck("campaign_contact.id") - .where({ - "campaign_contact.campaign_id": campaignId, - message_status: "needsResponse", - is_opted_out: false - }) - .whereRaw(`archived = ${archived}`) // partial index friendly - .limit(1); - - const notAssignableTagSubQuery = r.reader - .select("campaign_contact_tag.campaign_contact_id") - .from("campaign_contact_tag") - .join("tag", "tag.id", "=", "campaign_contact_tag.tag_id") - .where({ - "tag.organization_id": organizationId - }) - .whereRaw("lower(tag.title) = 'escalated'") - .whereRaw( - "campaign_contact_tag.campaign_contact_id = campaign_contact.id" - ); - - contactsQuery = contactsQuery.whereNotExists(notAssignableTagSubQuery); - - const contacts = await contactsQuery; - return contacts.length > 0; - }; - - return getHasUnhandledMessages({ - campaignId: campaign.id, - archived: campaign.is_archived, - organizationId: campaign.organization_id - }); + if (config.BAD_BENS_DISABLE_HAS_UNASSIGNED_CONTACTS) return false; + return checkCampaignContactsExist( + campaign, + buildHasUnassignedContactsSubquery + ); }, + hasUnsentInitialMessages: async (campaign) => + checkCampaignContactsExist(campaign, buildHasUnsentInitialsSubquery), + hasUnhandledMessages: async (campaign) => + checkCampaignContactsExist(campaign, buildHasUnhandledRepliesSubquery), customFields: async (campaign) => campaign.customFields || cacheableData.campaign.dbCustomFields(campaign.id), diff --git a/src/server/api/lib/campaign.ts b/src/server/api/lib/campaign.ts index 6dbf02fdb..57ba6d821 100644 --- a/src/server/api/lib/campaign.ts +++ b/src/server/api/lib/campaign.ts @@ -4,6 +4,7 @@ import type { CampaignInput, CampaignsFilter } from "@spoke/spoke-codegen"; +import type { Knex } from "knex"; import isEmpty from "lodash/isEmpty"; import isEqual from "lodash/isEqual"; import isNil from "lodash/isNil"; @@ -49,6 +50,32 @@ type DoGetCampaigns = ( options: DoGetCampaignsOptions ) => Promise>; +const textableContacts = () => + r.reader("campaign_contact").where({ is_opted_out: false }); + +// When campaignId is omitted the subquery is correlated to the outer campaign row. +const withCampaignFilter = (q: Knex.QueryBuilder, campaignId?: number) => + campaignId + ? q.where({ campaign_id: campaignId }) + : q.whereRaw("campaign_id = campaign.id"); + +export const buildHasUnsentInitialsSubquery = (campaignId?: number) => { + const contacts = textableContacts().where({ message_status: "needsMessage" }); + return withCampaignFilter(contacts, campaignId); +}; + +export const buildHasUnhandledRepliesSubquery = (campaignId?: number) => { + const contacts = textableContacts().where({ + message_status: "needsResponse" + }); + return withCampaignFilter(contacts, campaignId); +}; + +export const buildHasUnassignedContactsSubquery = (campaignId?: number) => { + const contacts = textableContacts().whereNull("assignment_id"); + return withCampaignFilter(contacts, campaignId); +}; + export const doGetCampaigns: DoGetCampaigns = async ( options: DoGetCampaignsOptions ) => { @@ -90,71 +117,26 @@ export const doGetCampaigns: DoGetCampaigns = async ( ]); } - if (!isNil(hasUnsentInitialMessages)) { - const existsMethod = hasUnsentInitialMessages - ? "whereExists" - : "whereNotExists"; - query[existsMethod]( - r - .reader("campaign_contact") - .select(r.reader.raw("1")) - .whereRaw('"campaign_contact"."campaign_id" = "campaign"."id"') - .whereRaw('"campaign_contact"."archived" = "campaign"."is_archived"') - .where({ message_status: "needsMessage", is_opted_out: false }) - .limit(1) - ); - } + const applySubqueryFilter = ( + filter: boolean | null | undefined, + buildSubquery: () => Knex.QueryBuilder + ) => { + if (isNil(filter)) return; - if (!isNil(hasUnhandledMessages)) { - const existsMethod = hasUnhandledMessages - ? "whereExists" - : "whereNotExists"; - query[existsMethod]( - r - .reader("campaign_contact") - .select(r.reader.raw("1")) - .whereRaw('"campaign_contact"."campaign_id" = "campaign"."id"') - .whereRaw('"campaign_contact"."archived" = "campaign"."is_archived"') - .where({ message_status: "needsResponse", is_opted_out: false }) - .whereNotExists( - r - .reader("campaign_contact_tag") - .join("tag", "campaign_contact_tag.tag_id", "tag.id") - .select(r.reader.raw("1")) - .whereRaw( - '"campaign_contact_tag"."campaign_contact_id" = "campaign_contact"."id"' - ) - .whereRaw("lower(tag.title) = 'escalated'") - ) - .limit(1) - ); - } + const subquery = buildSubquery(); + if (!isNil(isArchived)) + subquery.whereRaw(`campaign_contact.archived = ${isArchived}`); - if (!isNil(hasUnassignedContacts)) { - const existsMethod = hasUnassignedContacts - ? "whereExists" - : "whereNotExists"; - query[existsMethod]( - r - .reader("campaign_contact") - .select(r.reader.raw("1")) - .whereRaw('"campaign_contact"."campaign_id" = "campaign"."id"') - .whereRaw('"campaign_contact"."archived" = "campaign"."is_archived"') - .whereNull("campaign_contact.assignment_id") - .where({ is_opted_out: false }) - .whereNotExists( - r - .reader("campaign_contact_tag") - .join("tag", "campaign_contact_tag.tag_id", "tag.id") - .select(r.reader.raw("1")) - .whereRaw( - '"campaign_contact_tag"."campaign_contact_id" = "campaign_contact"."id"' - ) - .where({ "tag.is_assignable": false }) - ) - .limit(1) - ); - } + if (filter) query.whereExists(subquery); + else query.whereNotExists(subquery); + }; + + applySubqueryFilter(hasUnsentInitialMessages, buildHasUnsentInitialsSubquery); + applySubqueryFilter(hasUnhandledMessages, buildHasUnhandledRepliesSubquery); + applySubqueryFilter( + hasUnassignedContacts, + buildHasUnassignedContactsSubquery + ); const pagerOptions = { first, after }; return formatPage(query, pagerOptions);