From 0b803da5fb93d2242d4451bb1cac20b0afd3df26 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 7 Jan 2025 16:27:35 -0800 Subject: [PATCH] rewrite query to use aggregation pipeline --- Server/db/mongo/modules/monitorModule.js | 144 ++++++++++++++++++----- Server/utils/dataUtils.js | 3 +- 2 files changed, 115 insertions(+), 32 deletions(-) diff --git a/Server/db/mongo/modules/monitorModule.js b/Server/db/mongo/modules/monitorModule.js index c296de34b..84c7ff971 100644 --- a/Server/db/mongo/modules/monitorModule.js +++ b/Server/db/mongo/modules/monitorModule.js @@ -12,6 +12,7 @@ import { buildUptimeDetailsPipeline, buildHardwareDetailsPipeline, } from "./monitorModuleQueries.js"; +import { ObjectId } from "mongodb"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -29,6 +30,15 @@ const CHECK_MODEL_LOOKUP = { hardware: HardwareCheck, }; +const MODEL_TYPE_MAPPING = { + http: "checks", + ping: "checks", + docker: "checks", + port: "checks", + pagespeed: "pagespeedchecks", + hardware: "hardwarechecks", +}; + /** * Get all monitors * @async @@ -570,6 +580,7 @@ const getMonitorsByTeamId = async (req, res) => { } = req.query; const monitorQuery = { teamId: req.params.teamId }; + const monitorCount = await Monitor.countDocuments(monitorQuery); if (type !== undefined) { monitorQuery.type = Array.isArray(type) ? { $in: type } : type; @@ -582,46 +593,117 @@ const getMonitorsByTeamId = async (req, res) => { { url: { $regex: filter, $options: "i" } }, ]; } - const monitorCount = await Monitor.countDocuments(monitorQuery); // Pagination const skip = page && rowsPerPage ? page * rowsPerPage : 0; // Build Sort option - const sort = field ? { [field]: order === "asc" ? 1 : -1 } : {}; + const sort = { [field]: order === "asc" ? 1 : -1 }; - const monitors = await Monitor.find(monitorQuery) - .skip(skip) - .limit(rowsPerPage) - .sort(sort); - - // Early return if limit is set to -1, indicating we don't want any checks - if (limit === "-1") { - return { monitors, monitorCount }; + const matchStage = { teamId: new ObjectId(req.params.teamId) }; + if (type !== undefined) { + matchStage.type = Array.isArray(type) ? { $in: type } : type; + } + if (filter !== undefined) { + matchStage.$or = [ + { name: { $regex: filter, $options: "i" } }, + { url: { $regex: filter, $options: "i" } }, + ]; } - // Map each monitor to include its associated checks - const monitorsWithChecks = await Promise.all( - monitors.map(async (monitor) => { - let model = CHECK_MODEL_LOOKUP[monitor.type]; - // Checks are order newest -> oldest - let checks = await model - .find({ - monitorId: monitor._id, - ...(status && { status }), - }) - .sort({ createdAt: checkOrder === "asc" ? 1 : -1 }) + let result = await Monitor.aggregate([ + { $match: matchStage }, + { $skip: parseInt(skip) }, + ...(rowsPerPage ? [{ $limit: parseInt(rowsPerPage) }] : []), + { $sort: sort }, + { + $lookup: { + from: "checks", + let: { monitorId: "$_id" }, + pipeline: [ + { + $match: { + $expr: { $eq: ["$monitorId", "$$monitorId"] }, + ...(status && { status }), + }, + }, + { $sort: { createdAt: checkOrder === "asc" ? 1 : -1 } }, + { $limit: parseInt(limit) || 0 }, + ], + as: "standardchecks", + }, + }, + { + $lookup: { + from: "pagespeedchecks", + let: { monitorId: "$_id" }, + pipeline: [ + { + $match: { + $expr: { $eq: ["$monitorId", "$$monitorId"] }, + ...(status && { status }), + }, + }, + { $sort: { createdAt: checkOrder === "asc" ? 1 : -1 } }, + { $limit: parseInt(limit) || 0 }, + ], + as: "pagespeedchecks", + }, + }, + { + $lookup: { + from: "hardwarechecks", + let: { monitorId: "$_id" }, + pipeline: [ + { + $match: { + $expr: { $eq: ["$monitorId", "$$monitorId"] }, + ...(status && { status }), + }, + }, + { $sort: { createdAt: checkOrder === "asc" ? 1 : -1 } }, + { $limit: parseInt(limit) || 0 }, + ], + as: "hardwarechecks", + }, + }, + { + $addFields: { + checks: { + $switch: { + branches: [ + { + case: { $in: ["$type", ["http", "ping", "docker", "port"]] }, + then: "$standardchecks", + }, + { + case: { $eq: ["$type", "pagespeed"] }, + then: "$pagespeedchecks", + }, + { + case: { $eq: ["$type", "hardware"] }, + then: "$hardwarechecks", + }, + ], + default: [], + }, + }, + }, + }, + { + $project: { + standardchecks: 0, + pagespeedchecks: 0, + hardwarechecks: 0, + }, + }, + ]); + result = result.map((monitor) => { + monitor.checks = NormalizeData(monitor.checks, 10, 100); + return monitor; + }); - .limit(limit || 0); - - //Normalize checks if requested - if (normalize !== undefined) { - checks = NormalizeData(checks, 10, 100); - } - return { ...monitor.toObject(), checks }; - }) - ); - return { monitors: monitorsWithChecks, monitorCount }; + return { monitors: result, monitorCount }; } catch (error) { error.service = SERVICE_NAME; error.method = "getMonitorsByTeamId"; diff --git a/Server/utils/dataUtils.js b/Server/utils/dataUtils.js index fd10a4257..8fbb22821 100644 --- a/Server/utils/dataUtils.js +++ b/Server/utils/dataUtils.js @@ -26,6 +26,7 @@ const NormalizeData = (checks, rangeMin, rangeMax) => { const min = calculatePercentile(checks, 0); const max = calculatePercentile(checks, 95); const normalizedChecks = checks.map((check) => { + console.log(check); const originalResponseTime = check.responseTime; // Normalize the response time between 1 and 100 let normalizedResponseTime = @@ -38,7 +39,7 @@ const NormalizeData = (checks, rangeMin, rangeMax) => { Math.min(rangeMax, normalizedResponseTime) ); return { - ...check._doc, + ...check, responseTime: normalizedResponseTime, originalResponseTime: originalResponseTime, };