UNPKG

mongo-pipeline-kit

Version:

A feature-rich MongoDB pipeline builder kit for creating, reusing, and managing aggregation pipelines with enhanced JSON support and advanced utilities

193 lines (192 loc) 6.82 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PipelineUtils = void 0; /** * Utility class for pipeline operations */ class PipelineUtils { /** * Parse a JSON string into a pipeline * @param jsonString JSON string representation of a pipeline * @returns Parsed pipeline */ static fromJSON(jsonString) { try { const parsed = JSON.parse(jsonString); if (!Array.isArray(parsed)) { throw new Error("JSON must represent an array of pipeline stages"); } return parsed; } catch (error) { throw new Error(`Failed to parse pipeline JSON: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Convert pipeline to JSON string * @param pipeline The pipeline to convert * @param pretty Whether to format the JSON with indentation * @returns JSON string representation */ static toJSON(pipeline, pretty = false) { return pretty ? JSON.stringify(pipeline, null, 2) : JSON.stringify(pipeline); } /** * Validate pipeline structure * @param pipeline The pipeline to validate * @param options Validation options * @returns True if valid, throws error if invalid */ static validate(pipeline, options = {}) { const { strict = true, allowEmpty = false, maxStages = 1000 } = options; if (!Array.isArray(pipeline)) { throw new Error("Pipeline must be an array"); } if (pipeline.length === 0 && !allowEmpty) { throw new Error("Pipeline cannot be empty"); } if (pipeline.length > maxStages) { throw new Error(`Pipeline exceeds maximum stage limit of ${maxStages}`); } for (const stage of pipeline) { if (!stage || typeof stage !== "object") { throw new Error("Invalid pipeline stage: must be an object"); } if (strict) { const operators = Object.keys(stage); if (operators.length !== 1) { throw new Error("Each pipeline stage must have exactly one operator"); } const operator = operators[0]; if (!operator.startsWith("$")) { throw new Error("Pipeline operators must start with $"); } } } return true; } /** * Clone a pipeline (deep copy) * @param pipeline The pipeline to clone * @returns Cloned pipeline */ static clone(pipeline) { return JSON.parse(JSON.stringify(pipeline)); } /** * Get pipeline statistics * @param pipeline The pipeline to analyze * @returns Statistics about the pipeline */ static getStats(pipeline) { const stageTypes = {}; let totalSize = 0; for (const stage of pipeline) { const operator = Object.keys(stage)[0]; stageTypes[operator] = (stageTypes[operator] || 0) + 1; totalSize += JSON.stringify(stage).length; } // Calculate estimated complexity based on stage types const complexityWeights = { $match: 1, $project: 2, $group: 3, $lookup: 4, $facet: 5, $graphLookup: 4, $addFields: 2, $sort: 1, $limit: 1, $skip: 1, }; const estimatedComplexity = Object.entries(stageTypes).reduce((sum, [operator, count]) => sum + (complexityWeights[operator] || 1) * count, 0); return { stageCount: pipeline.length, stageTypes, totalSize, estimatedComplexity, }; } /** * Filter pipeline stages by type * @param pipeline The pipeline to filter * @param operator The stage operator to filter by * @returns Filtered pipeline stages */ static filterByStageType(pipeline, operator) { return pipeline.filter((stage) => Object.keys(stage)[0] === operator); } /** * Remove stages by type * @param pipeline The pipeline to modify * @param operator The stage operator to remove * @returns New pipeline without the specified stage type */ static removeStageType(pipeline, operator) { return pipeline.filter((stage) => Object.keys(stage)[0] !== operator); } /** * Insert a stage at a specific position * @param pipeline The pipeline to modify * @param index The position to insert at * @param stage The stage to insert * @returns New pipeline with inserted stage */ static insertStage(pipeline, index, stage) { const newPipeline = [...pipeline]; newPipeline.splice(index, 0, stage); return newPipeline; } /** * Replace a stage at a specific position * @param pipeline The pipeline to modify * @param index The position to replace * @param stage The new stage * @returns New pipeline with replaced stage */ static replaceStage(pipeline, index, stage) { const newPipeline = [...pipeline]; newPipeline[index] = stage; return newPipeline; } /** * Get a human-readable description of the pipeline * @param pipeline The pipeline to describe * @returns Formatted description */ static describe(pipeline) { const stats = this.getStats(pipeline); const stageDescriptions = pipeline.map((stage, index) => { const operator = Object.keys(stage)[0]; return `${index + 1}. ${operator}`; }); return `Pipeline Description: - Total Stages: ${stats.stageCount} - Stage Types: ${Object.entries(stats.stageTypes).map(([type, count]) => `${type}(${count})`).join(', ')} - Estimated Complexity: ${stats.estimatedComplexity} - Total Size: ${stats.totalSize} characters Stages: ${stageDescriptions.join('\n')}`; } /** * Compare two pipelines * @param pipeline1 First pipeline * @param pipeline2 Second pipeline * @returns Comparison result */ static compare(pipeline1, pipeline2) { const stats1 = this.getStats(pipeline1); const stats2 = this.getStats(pipeline2); const areEqual = JSON.stringify(pipeline1) === JSON.stringify(pipeline2); return { areEqual, differences: { stageCount: { p1: stats1.stageCount, p2: stats2.stageCount }, stageTypes: { p1: stats1.stageTypes, p2: stats2.stageTypes }, commonStages: Math.min(stats1.stageCount, stats2.stageCount), }, }; } } exports.PipelineUtils = PipelineUtils; //# sourceMappingURL=PipelineUtils.js.map