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

171 lines 5.88 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PipelineComposer = void 0; const types_1 = require("./types"); /** * Class for composing and reusing MongoDB pipeline segments */ class PipelineComposer { constructor() { this.pipeline = []; } /** * Compose multiple pipelines into one * @param pipelines The pipelines to compose */ compose(...pipelines) { if (pipelines.length === 0) { throw new types_1.CompositionError("At least one pipeline must be provided for composition"); } // Validate all pipelines before composition pipelines.forEach((pipeline, index) => { if (!this.validate(pipeline)) { throw new types_1.CompositionError(`Invalid pipeline at index ${index}`); } }); // Flatten and combine all pipelines this.pipeline = pipelines.reduce((acc, curr) => [...acc, ...curr], []); return this; } /** * Extend an existing pipeline with another pipeline * @param basePipeline The base pipeline to extend * @param extension The pipeline to extend with */ extend(basePipeline, extension) { if (!this.validate(basePipeline)) { throw new types_1.CompositionError("Invalid base pipeline"); } if (!this.validate(extension)) { throw new types_1.CompositionError("Invalid extension pipeline"); } this.pipeline = [...basePipeline, ...extension]; return this; } /** * Validate a pipeline structure * @param pipeline The pipeline to validate * @param options Validation options */ validate(pipeline, options = {}) { const { strict = true, allowEmpty = false, maxStages = 1000 } = options; // Check if pipeline is an array if (!Array.isArray(pipeline)) { throw new types_1.ValidationError("Pipeline must be an array"); } // Check for empty pipeline if (pipeline.length === 0 && !allowEmpty) { throw new types_1.ValidationError("Pipeline cannot be empty"); } // Check for maximum stages if (pipeline.length > maxStages) { throw new types_1.ValidationError(`Pipeline exceeds maximum stage limit of ${maxStages}`); } // Validate each stage for (const stage of pipeline) { if (!stage || typeof stage !== "object") { throw new types_1.ValidationError("Invalid pipeline stage: must be an object"); } if (strict) { // Check that each stage has exactly one operator const operators = Object.keys(stage); if (operators.length !== 1) { throw new types_1.ValidationError("Each pipeline stage must have exactly one operator"); } // Check that the operator starts with $ const operator = operators[0]; if (!operator.startsWith("$")) { throw new types_1.ValidationError("Pipeline operators must start with $"); } } } return true; } /** * Build the final composed pipeline */ build() { return [...this.pipeline]; } /** * Clear the current pipeline */ clear() { this.pipeline = []; return this; } /** * Get the current number of stages in the pipeline */ getStageCount() { return this.pipeline.length; } /** * Get a specific stage from the pipeline * @param index The index of the stage to retrieve */ getStage(index) { if (index < 0 || index >= this.pipeline.length) { throw new Error("Invalid stage index"); } return this.pipeline[index]; } /** * Check if the pipeline contains a specific stage type * @param operator The stage operator to check for (e.g., '$match', '$group') */ hasStage(operator) { return this.pipeline.some((stage) => Object.keys(stage)[0] === operator); } /** * Get all stages of a specific type * @param operator The stage operator to filter by */ getStagesByType(operator) { return this.pipeline.filter((stage) => Object.keys(stage)[0] === operator); } /** * Get the pipeline as a JSON string * @param pretty Whether to format the JSON with indentation * @returns JSON string representation of the pipeline */ toJSON(pretty = false) { const pipeline = this.build(); return pretty ? JSON.stringify(pipeline, null, 2) : JSON.stringify(pipeline); } /** * Get the pipeline as a plain object (not stringified) * @returns Object representation of the pipeline */ toObject() { return this.build(); } /** * Export pipeline with metadata * @param metadata Additional metadata to include * @returns Object with pipeline and metadata */ exportWithMetadata(metadata = {}) { return { pipeline: this.build(), metadata: { stageCount: this.getStageCount(), createdAt: new Date().toISOString(), ...metadata, }, }; } /** * Get a human-readable string representation of the pipeline * @returns Formatted string showing pipeline stages */ toString() { const stages = this.pipeline.map((stage, index) => { const operator = Object.keys(stage)[0]; return `${index + 1}. ${operator}`; }); return `Composed Pipeline with ${this.pipeline.length} stages:\n${stages.join('\n')}`; } } exports.PipelineComposer = PipelineComposer; //# sourceMappingURL=PipelineComposer.js.map