import { CityName } from "../utils/types"; import { STATS_MAX_TIME_MS, UNCLASSIFIED } from "./tickets.constants"; import { getTicketsCollection } from "./tickets.getters"; import type { TicketStatsDateRange } from "./tickets.types"; const effectiveSubjectIdExpr = { $ifNull: [ "$externalCallFields.event_subject_id", { $ifNull: [ "$externalCallFields.event_sub_subject_id", "$externalCallFields.event_sub_subject_id2", ], }, ], }; const departmentsSubjectsLookup = (cityName: CityName) => ({ $lookup: { from: "departmentsSubjects", let: { sid: "$effectiveSubjectId", c: cityName }, pipeline: [ { $match: { $expr: { $and: [ { $eq: ["$cityName", "$$c"] }, { $or: [ { $eq: ["$subject_id", { $ifNull: ["$$sid", ""] }] }, { $eq: ["$sub_subject_id", { $ifNull: ["$$sid", ""] }] }, { $eq: ["$sub_subject_id2", { $ifNull: ["$$sid", ""] }] }, ], }, ], }, }, }, { $limit: 1 }, ], as: "subj", }, }); const subjectLabelExpr = { $cond: { if: { $gt: [{ $size: "$subj" }, 0] }, then: { $ifNull: [{ $arrayElemAt: ["$subj.subjectName", 0] }, UNCLASSIFIED], }, else: UNCLASSIFIED, }, }; const ticketDateMatch = (dateRange: TicketStatsDateRange) => ({ createdAt: { $gte: dateRange.from, $lte: dateRange.to }, }); /** Legacy draft detection for tickets created before isDraft existed. */ export const isDraftSubjectIdExpr = { $or: [ { $eq: [{ $type: "$externalCallFields.event_subject_id" }, "missing"] }, { $eq: ["$externalCallFields.event_subject_id", null] }, { $regexMatch: { input: { $ifNull: ["$externalCallFields.event_subject_id", ""] }, regex: /^\s*$/, }, }, ], }; /** * Draft when isDraft is true, not draft when isDraft is false, * otherwise fall back to legacy subject-id heuristic for older tickets. */ export const isDraftTicketExpr = { $or: [ { $eq: ["$isDraft", true] }, { $and: [{ $ne: ["$isDraft", false] }, isDraftSubjectIdExpr], }, ], }; export const ticketsWithCallSidMatch = ( cityName: CityName, dateRange: TicketStatsDateRange, ) => ({ cityName, callSid: { $exists: true, $nin: [null, ""] }, ...ticketDateMatch(dateRange), }); export const subjectResolutionStages = ( cityName: CityName, ): Record[] => [ { $addFields: { effectiveSubjectId: effectiveSubjectIdExpr } }, departmentsSubjectsLookup(cityName), { $addFields: { subject: subjectLabelExpr } }, ]; export const runTicketStatsAggregate = async ( pipeline: Record[], ): Promise => { const rows = await getTicketsCollection() .aggregate(pipeline, { maxTimeMS: STATS_MAX_TIME_MS }) .toArray(); return rows as T[]; };