UNPKG

mongo-bolt

Version:

A lightweight, beginner-friendly MongoDB wrapper with inbuilt caching and simplified joins/indexing.

240 lines (239 loc) 6.7 kB
import { operatorMap } from "./operatorMap.js"; class AggregationBuilder { constructor() { this.pipeline = []; // Keep track of the last $group stage's object for chaining accumulator methods. this.lastGroupStage = null; } ensureGroupStage() { if (!this.lastGroupStage) { throw new Error("Must call group() before using group operators."); } } match(field, condition) { const mongoCondition = {}; for (const [key, value] of Object.entries(condition)) { const mongoOperator = operatorMap[key]; if (!mongoOperator) { throw new Error(`Unsupported operator: ${key}`); } mongoCondition[mongoOperator] = value; } this.pipeline.push({ $match: { [field]: mongoCondition, }, }); return this; } group(groupBy) { let groupStage = { $group: {} }; if (typeof groupBy === "string") { groupStage.$group._id = `$${groupBy}`; } else if (typeof groupBy === "object") { // Use the provided object as the composite key. groupStage.$group._id = groupBy; } else { throw new Error("group() requires a string or an object"); } this.pipeline.push(groupStage); // Save a reference to the internal group object for accumulator methods. this.lastGroupStage = groupStage.$group; return this; } sum(value, key) { this.ensureGroupStage(); this.lastGroupStage[key] = { $sum: `$${value}` }; return this; } avg(value, key) { this.ensureGroupStage(); this.lastGroupStage[key] = { $avg: `$${value}` }; return this; } min(value, key) { this.ensureGroupStage(); this.lastGroupStage[key] = { $min: `$${value}` }; return this; } max(value, key) { this.ensureGroupStage(); this.lastGroupStage[key] = { $max: `$${value}` }; return this; } first(value, key) { this.ensureGroupStage(); this.lastGroupStage[key] = { $first: `$${value}` }; return this; } last(value, key) { this.ensureGroupStage(); this.lastGroupStage[key] = { $last: `$${value}` }; return this; } push(field, key = field) { this.ensureGroupStage(); this.lastGroupStage[key] = { $push: field === "ENTIRE" ? "$$ROOT" : `$${field}`, }; return this; } addToSet(value, key) { this.ensureGroupStage(); this.lastGroupStage[key] = { $addToSet: `$${value}` }; return this; } stdDevPop(value, key) { this.ensureGroupStage(); this.lastGroupStage[key] = { $stdDevPop: `$${value}` }; return this; } stdDevSamp(value, key) { this.ensureGroupStage(); this.lastGroupStage[key] = { $stdDevSamp: `$${value}` }; return this; } mergeObjects(value, key) { this.ensureGroupStage(); this.lastGroupStage[key] = { $mergeObjects: `$${value}` }; return this; } count(key = "count") { this.ensureGroupStage(); this.lastGroupStage[key] = { $sum: 1 }; return this; } project(projection) { this.pipeline.push({ $project: projection, }); return this; } sort(sortFields) { const sortStage = {}; for (const [field, direction] of Object.entries(sortFields)) { if (direction === "asc") { sortStage[field] = 1; } else if (direction === "desc") { sortStage[field] = -1; } else { throw new Error(`Invalid sort direction for "${field}": use "asc" or "desc"`); } } this.pipeline.push({ $sort: sortStage }); return this; // for chaining } limit(n) { this.pipeline.push({ $limit: n, }); return this; } skip(n) { this.pipeline.push({ $skip: n, }); return this; } lookup(from, localField, foreignField, as) { this.pipeline.push({ $lookup: { from, localField: `$${localField}`, foreignField: `$${foreignField}`, as, }, }); return this; } unset(...fields) { this.pipeline.push({ $unset: fields }); return this; } addFields(fields) { this.pipeline.push({ $addFields: fields }); return this; } replaceRoot(newRoot) { this.pipeline.push({ $replaceRoot: { newRoot } }); return this; } replaceWith(replacement) { this.pipeline.push({ $replaceWith: replacement }); return this; } graphLookup(options) { this.pipeline.push({ $graphLookup: options }); return this; } unwind(field) { this.pipeline.push({ $unwind: typeof field === "string" ? `$${field}` : field, }); return this; } bucket(options) { this.pipeline.push({ $bucket: options }); return this; } bucketAuto(options) { this.pipeline.push({ $bucketAuto: options }); return this; } facet(facets) { this.pipeline.push({ $facet: facets }); return this; } merge(options) { this.pipeline.push({ $merge: typeof options === "string" ? { into: options } : options, }); return this; } out(collectionName) { this.pipeline.push({ $out: collectionName, }); return this; } search(searchStage) { this.pipeline.push({ $search: searchStage }); return this; } searchMeta(searchStage) { this.pipeline.push({ $searchMeta: searchStage }); return this; } redact(expression) { this.pipeline.push({ $redact: expression }); return this; } indexStats() { this.pipeline.push({ $indexStats: {} }); return this; } currentOp() { this.pipeline.push({ $currentOp: {} }); return this; } collStats() { this.pipeline.push({ $collStats: {} }); return this; } planCacheStats() { this.pipeline.push({ $planCacheStats: {} }); return this; } listSessions() { this.pipeline.push({ $listSessions: {} }); return this; } build() { return this.pipeline; } } export default AggregationBuilder;