rewrite query to use aggregation pipeline

This commit is contained in:
Alex Holliday
2025-01-07 16:27:35 -08:00
parent 787bb09235
commit 0b803da5fb
2 changed files with 115 additions and 32 deletions

View File

@@ -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";

View File

@@ -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,
};