@warlock.js/cascade 5.1.0 → 5.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.md +4 -0
- package/cjs/index.cjs.map +1 -1
- package/esm/context/database-data-source-context.d.mts.map +1 -1
- package/esm/context/database-transaction-context.d.mts.map +1 -1
- package/esm/contracts/query-builder.contract.d.mts +13 -0
- package/esm/contracts/query-builder.contract.d.mts.map +1 -1
- package/esm/data-source/data-source-registry.d.mts.map +1 -1
- package/esm/data-source/data-source.d.mts.map +1 -1
- package/esm/database-dirty-tracker.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-blueprint.mjs.map +1 -1
- package/esm/drivers/mongodb/mongodb-driver.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-driver.mjs.map +1 -1
- package/esm/drivers/mongodb/mongodb-id-generator.mjs.map +1 -1
- package/esm/drivers/mongodb/mongodb-migration-driver.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-migration-driver.mjs.map +1 -1
- package/esm/drivers/mongodb/mongodb-query-builder.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-query-builder.mjs.map +1 -1
- package/esm/drivers/mongodb/mongodb-query-parser.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-query-parser.mjs.map +1 -1
- package/esm/drivers/mongodb/mongodb-sync-adapter.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-blueprint.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-blueprint.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-dialect.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-driver.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-driver.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-migration-driver.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-migration-driver.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-query-builder.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-query-builder.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-query-parser.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-query-parser.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-sql-serializer.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-sync-adapter.mjs.map +1 -1
- package/esm/events/model-events.d.mts.map +1 -1
- package/esm/events/model-events.mjs.map +1 -1
- package/esm/expressions/column-expressions.d.mts.map +1 -1
- package/esm/index.d.mts +2 -2
- package/esm/migration/column-builder.d.mts.map +1 -1
- package/esm/migration/column-helpers.d.mts.map +1 -1
- package/esm/migration/foreign-key-builder.d.mts.map +1 -1
- package/esm/migration/migration-runner.d.mts +23 -3
- package/esm/migration/migration-runner.d.mts.map +1 -1
- package/esm/migration/migration-runner.mjs.map +1 -1
- package/esm/migration/migration.d.mts +1 -1
- package/esm/migration/migration.d.mts.map +1 -1
- package/esm/migration/migration.mjs.map +1 -1
- package/esm/migration/sql-grammar.mjs.map +1 -1
- package/esm/model/methods/accessor-methods.mjs.map +1 -1
- package/esm/model/methods/delete-methods.mjs.map +1 -1
- package/esm/model/methods/hydration-methods.mjs.map +1 -1
- package/esm/model/methods/instance-event-methods.mjs.map +1 -1
- package/esm/model/methods/meta-methods.mjs.map +1 -1
- package/esm/model/methods/pivot-methods.mjs.map +1 -1
- package/esm/model/methods/query-methods.mjs.map +1 -1
- package/esm/model/methods/restore-methods.mjs.map +1 -1
- package/esm/model/methods/scope-methods.mjs.map +1 -1
- package/esm/model/methods/serialization-methods.mjs.map +1 -1
- package/esm/model/methods/static-event-methods.mjs.map +1 -1
- package/esm/model/methods/write-methods.mjs.map +1 -1
- package/esm/model/model.d.mts.map +1 -1
- package/esm/model/model.mjs.map +1 -1
- package/esm/model/register-model.d.mts.map +1 -1
- package/esm/model/register-model.mjs.map +1 -1
- package/esm/operations/database.mjs.map +1 -1
- package/esm/operations/migrations.d.mts.map +1 -1
- package/esm/operations/migrations.mjs.map +1 -1
- package/esm/query-builder/query-builder.d.mts.map +1 -1
- package/esm/query-builder/query-builder.mjs.map +1 -1
- package/esm/relations/key-conventions.mjs.map +1 -1
- package/esm/relations/pivot-operations.d.mts.map +1 -1
- package/esm/relations/pivot-operations.mjs.map +1 -1
- package/esm/relations/relation-loader.d.mts.map +1 -1
- package/esm/relations/relation-loader.mjs.map +1 -1
- package/esm/remover/database-remover.mjs.map +1 -1
- package/esm/restorer/database-restorer.mjs.map +1 -1
- package/esm/sync/model-sync-operation.d.mts.map +1 -1
- package/esm/sync/model-sync-operation.mjs.map +1 -1
- package/esm/sync/model-sync.d.mts.map +1 -1
- package/esm/sync/sync-manager.mjs.map +1 -1
- package/esm/utils/connect-to-database.d.mts.map +1 -1
- package/esm/utils/connect-to-database.mjs.map +1 -1
- package/esm/utils/define-model.d.mts.map +1 -1
- package/esm/utils/escape-regex.mjs.map +1 -1
- package/esm/utils/is-valid-date-value.mjs.map +1 -1
- package/esm/utils/sanitize-filter.d.mts.map +1 -1
- package/esm/validation/database-writer-validation-error.d.mts.map +1 -1
- package/esm/validation/database-writer-validation-error.mjs.map +1 -1
- package/esm/validation/mutators/embed-mutator.mjs.map +1 -1
- package/esm/validation/plugins/embed-validator-plugin.mjs.map +1 -1
- package/esm/validation/rules/database-model-rule.mjs.map +1 -1
- package/esm/validation/rules/exists-rule.mjs.map +1 -1
- package/esm/validation/rules/unique-rule.mjs.map +1 -1
- package/esm/writer/database-writer.d.mts.map +1 -1
- package/esm/writer/database-writer.mjs.map +1 -1
- package/llms-full.txt +2 -0
- package/package.json +7 -4
- package/skills/cascade-basics/SKILL.md +3 -1
- package/skills/write-migration/SKILL.md +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mongodb-query-parser.mjs","names":[],"sources":["../../../../../../../../cascade/src/drivers/mongodb/mongodb-query-parser.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport type { Collection } from \"mongodb\";\r\nimport type { GroupByInput, RawExpression, WhereOperator } from \"../../contracts\";\r\nimport { UnsafeRawExpressionError } from \"../../errors/unsafe-raw-expression.error\";\r\nimport {\r\n isAggregateExpression,\r\n type AggregateExpression,\r\n} from \"../../expressions/aggregate-expressions\";\r\nimport type { ColumnExpression } from \"../../expressions/column-expressions\";\r\nimport { escapeRegex, resolveLikePattern } from \"../../utils/escape-regex\";\r\nimport type { MongoQueryBuilder } from \"./mongodb-query-builder\";\r\nimport type { Operation, PipelineStage } from \"./types\";\r\n\r\n/**\r\n * Options for configuring the MongoDB query parser.\r\n */\r\nexport type MongoQueryParserOptions = {\r\n /** The MongoDB collection being queried */\r\n collection: Collection;\r\n /** The ordered list of operations to parse */\r\n operations: Operation[];\r\n /** Factory method for creating sub-builders (used for callbacks) */\r\n createSubBuilder: () => MongoQueryBuilder;\r\n};\r\n\r\n/**\r\n * Parses query builder operations into MongoDB aggregation pipeline.\r\n *\r\n * This parser is responsible for converting the abstract operations collected\r\n * by the query builder into a concrete MongoDB aggregation pipeline. It handles\r\n * intelligent grouping of mergeable operations (like multiple where clauses)\r\n * into single pipeline stages for optimal performance.\r\n */\r\nexport class MongoQueryParser {\r\n /**\r\n * The MongoDB collection being queried.\r\n */\r\n private readonly collection: Collection;\r\n\r\n /**\r\n * The ordered list of operations to parse.\r\n */\r\n private readonly operations: Operation[];\r\n\r\n /**\r\n * Factory for creating sub-builders (used when resolving callbacks).\r\n */\r\n private readonly createSubBuilder: () => MongoQueryBuilder;\r\n\r\n /**\r\n * Track group field names for automatic _id renaming.\r\n * Maps pipeline index to field names.\r\n */\r\n private readonly groupFieldNames = new Map<number, string | string[]>();\r\n\r\n /**\r\n * Track `countDistinct` aggregate aliases per group stage. The renaming\r\n * `$project` finalizes these with `{ $size: \"$alias\" }` over the set built\r\n * by `$addToSet` in the `$group` stage.\r\n * Maps pipeline index to the set of aliases needing `$size` finalization.\r\n */\r\n private readonly countDistinctAliases = new Map<number, Set<string>>();\r\n\r\n /**\r\n * Create a new MongoDB query parser.\r\n *\r\n * @param options - Configuration options for the parser\r\n */\r\n public constructor(options: MongoQueryParserOptions) {\r\n this.collection = options.collection;\r\n this.operations = options.operations;\r\n this.createSubBuilder = options.createSubBuilder;\r\n }\r\n\r\n /**\r\n * Parse the operations into a MongoDB aggregation pipeline.\r\n *\r\n * This method intelligently groups mergeable operations (e.g., multiple where\r\n * clauses) into single pipeline stages while maintaining the correct execution\r\n * order for non-mergeable operations.\r\n *\r\n * @returns The MongoDB aggregation pipeline\r\n *\r\n * @example\r\n * ```typescript\r\n * const parser = new MongoQueryParser({ collection, operations });\r\n * const pipeline = parser.parse();\r\n * // [\r\n * // { $match: { status: 'active', age: { $gt: 18 } } },\r\n * // { $sort: { createdAt: -1 } },\r\n * // { $limit: 10 }\r\n * // ]\r\n * ```\r\n */\r\n public parse(): any[] {\r\n const pipeline: any[] = [];\r\n let currentStage: PipelineStage | null = null;\r\n let currentBuffer: Operation[] = [];\r\n\r\n for (const op of this.orderStages(this.operations)) {\r\n if (op.mergeable && op.stage === currentStage) {\r\n // Same mergeable stage, add to buffer\r\n currentBuffer.push(op);\r\n } else {\r\n // Different stage or non-mergeable, flush buffer\r\n if (currentBuffer.length > 0) {\r\n const builtStage = this.buildStage(currentStage!, currentBuffer);\r\n if (builtStage) {\r\n const stageIndex = pipeline.length;\r\n pipeline.push(builtStage);\r\n // Track field names for group stages with aggregates\r\n this.trackGroupFieldNames(currentStage!, currentBuffer, stageIndex);\r\n }\r\n currentBuffer = [];\r\n }\r\n\r\n if (op.mergeable) {\r\n // Start new buffer\r\n currentStage = op.stage;\r\n currentBuffer.push(op);\r\n } else {\r\n // Non-mergeable, add directly\r\n const builtStage = this.buildStage(op.stage, [op]);\r\n if (builtStage) {\r\n const stageIndex = pipeline.length;\r\n pipeline.push(builtStage);\r\n // Track field names for group stages with aggregates\r\n this.trackGroupFieldNames(op.stage, [op], stageIndex);\r\n }\r\n currentStage = null;\r\n }\r\n }\r\n }\r\n\r\n // Flush remaining buffer\r\n if (currentBuffer.length > 0) {\r\n const builtStage = this.buildStage(currentStage!, currentBuffer);\r\n if (builtStage) {\r\n const stageIndex = pipeline.length;\r\n pipeline.push(builtStage);\r\n // Track field names for group stages with aggregates\r\n this.trackGroupFieldNames(currentStage!, currentBuffer, stageIndex);\r\n }\r\n }\r\n\r\n // Post-process: Rename _id to actual field names after $group stages with aggregates\r\n return this.postProcessGroupStages(pipeline);\r\n }\r\n\r\n /**\r\n * Reorder operations so filters run before projections, mirroring SQL\r\n * semantics: in `select(...).where(...)`, the WHERE always applies to the\r\n * source columns regardless of call order. Without this, a `$project` that\r\n * strips the filter column would run before the `$match` and silently drop\r\n * every document (`select([\"a\"]).where(\"b\", x)` → `[]`).\r\n *\r\n * Only *mergeable* `$match` operations are hoisted, and only within a\r\n * segment of neighboring mergeable `$match` / `$project` / `$sort`\r\n * operations. Any other operation — `$group`, `$lookup`, `$limit`, `$skip`,\r\n * `$setWindowFields`, or a non-mergeable op (raw escapes, having-style\r\n * post-group matches, `$sample`) — is a barrier: nothing moves across it.\r\n * So `groupBy(...).where(...)` still filters AFTER the group, and\r\n * `limit(...)` / `random()` keep their call-order meaning.\r\n */\r\n private orderStages(operations: Operation[]): Operation[] {\r\n const reordered: Operation[] = [];\r\n let segment: Operation[] = [];\r\n\r\n const flushSegment = (): void => {\r\n if (segment.length === 0) {\r\n return;\r\n }\r\n reordered.push(...segment.filter((op) => op.stage === \"$match\"));\r\n reordered.push(...segment.filter((op) => op.stage !== \"$match\"));\r\n segment = [];\r\n };\r\n\r\n for (const op of operations) {\r\n const isReorderable =\r\n op.mergeable &&\r\n (op.stage === \"$match\" || op.stage === \"$project\" || op.stage === \"$sort\");\r\n\r\n if (isReorderable) {\r\n segment.push(op);\r\n } else {\r\n flushSegment();\r\n reordered.push(op);\r\n }\r\n }\r\n\r\n flushSegment();\r\n return reordered;\r\n }\r\n\r\n /**\r\n * Track field names for group stages that need _id renaming.\r\n */\r\n private trackGroupFieldNames(\r\n stage: PipelineStage,\r\n operations: Operation[],\r\n stageIndex: number,\r\n ): void {\r\n if (stage === \"$group\") {\r\n const op = operations[0];\r\n if (op.type === \"groupByWithAggregates\" && op.data.fields) {\r\n const fieldNames = this.extractGroupFieldNames(op.data.fields);\r\n if (fieldNames) {\r\n this.groupFieldNames.set(stageIndex, fieldNames);\r\n }\r\n\r\n this.trackCountDistinctAliases(stageIndex, op.data.aggregates);\r\n } else if (op.type === \"groupByDate\") {\r\n // The `$dateTrunc` bucket becomes `_id`; rename it back to the column.\r\n this.groupFieldNames.set(stageIndex, op.data.column as string);\r\n this.trackCountDistinctAliases(stageIndex, op.data.aggregates ?? {});\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Record which aggregate aliases in a group stage are `countDistinct` so the\r\n * renaming `$project` can finalize them with `{ $size: \"$alias\" }` over the\r\n * set built by `$addToSet` (the standard distinct-count-per-group pattern).\r\n */\r\n private trackCountDistinctAliases(\r\n stageIndex: number,\r\n aggregates: Record<string, RawExpression>,\r\n ): void {\r\n const distinctAliases = new Set<string>();\r\n for (const [alias, expression] of Object.entries(aggregates)) {\r\n if (isAggregateExpression(expression) && expression.__agg === \"countDistinct\") {\r\n distinctAliases.add(alias);\r\n }\r\n }\r\n\r\n if (distinctAliases.size > 0) {\r\n this.countDistinctAliases.set(stageIndex, distinctAliases);\r\n }\r\n }\r\n\r\n /**\r\n * Post-process pipeline to rename _id fields after $group stages.\r\n *\r\n * This automatically renames MongoDB's `_id` field to the actual field name(s)\r\n * used for grouping, making the results more intuitive.\r\n *\r\n * @param pipeline - The aggregation pipeline\r\n * @returns The processed pipeline\r\n */\r\n private postProcessGroupStages(pipeline: any[]): any[] {\r\n const processed: any[] = [];\r\n\r\n for (let i = 0; i < pipeline.length; i++) {\r\n const stage = pipeline[i];\r\n\r\n // Check if this is a $group stage that needs _id renaming\r\n if (stage.$group && this.groupFieldNames.has(i)) {\r\n const fieldNames = this.groupFieldNames.get(i)!;\r\n\r\n // Add the $group stage\r\n processed.push(stage);\r\n\r\n // Add a $project stage to rename _id\r\n const projection: Record<string, unknown> = {};\r\n\r\n if (typeof fieldNames === \"string\") {\r\n // Single field: rename _id to field name\r\n projection[fieldNames] = \"$_id\";\r\n } else if (Array.isArray(fieldNames) && fieldNames.length > 0) {\r\n // Multiple fields: _id is an object, spread it\r\n for (const fieldName of fieldNames) {\r\n projection[fieldName] = `$_id.${fieldName}`;\r\n }\r\n }\r\n\r\n // Include all aggregate fields. countDistinct aliases are finalized\r\n // with `{ $size: \"$alias\" }` over the set built by `$addToSet`; every\r\n // other aggregate is projected through as-is.\r\n const distinctAliases = this.countDistinctAliases.get(i);\r\n const aggregateFields = Object.keys(stage.$group).filter((key) => key !== \"_id\");\r\n for (const field of aggregateFields) {\r\n if (distinctAliases?.has(field)) {\r\n projection[field] = { $size: `$${field}` };\r\n } else {\r\n projection[field] = 1;\r\n }\r\n }\r\n\r\n if (Object.keys(projection).length > 0) {\r\n // now unselect the _id field\r\n projection._id = 0;\r\n processed.push({ $project: projection });\r\n }\r\n } else {\r\n // Regular stage, add as-is\r\n processed.push(stage);\r\n }\r\n }\r\n\r\n return processed;\r\n }\r\n\r\n /**\r\n * Convert the parsed pipeline to a pretty-printed string for debugging.\r\n *\r\n * This method formats the MongoDB aggregation pipeline in a human-readable\r\n * way, making it easier to understand and debug complex queries.\r\n *\r\n * @returns A formatted string representation of the pipeline\r\n *\r\n * @example\r\n * ```typescript\r\n * const parser = new MongoQueryParser({ collection, operations });\r\n * console.log(parser.toPrettyString());\r\n * // Output:\r\n * // MongoDB Aggregation Pipeline:\r\n * // ════════════════════════════\r\n * // Stage 1: $match\r\n * // status: \"active\"\r\n * // age: { $gt: 18 }\r\n * //\r\n * // Stage 2: $sort\r\n * // createdAt: -1\r\n * ```\r\n */\r\n public toPrettyString(): string {\r\n const pipeline = this.parse();\r\n\r\n if (pipeline.length === 0) {\r\n return \"MongoDB Aggregation Pipeline: (empty)\";\r\n }\r\n\r\n let output = \"MongoDB Aggregation Pipeline:\\n\";\r\n output += \"═\".repeat(50) + \"\\n\";\r\n\r\n pipeline.forEach((stage, index) => {\r\n const stageName = Object.keys(stage)[0];\r\n const stageData = stage[stageName];\r\n\r\n if (index > 0) {\r\n output += \"\\n\";\r\n }\r\n\r\n output += `Stage ${index + 1}: ${colors.redBright(stageName)}\\n`;\r\n output += this.formatStageData(stageData, 2);\r\n });\r\n\r\n return output;\r\n }\r\n\r\n /**\r\n * Format stage data with proper indentation.\r\n *\r\n * @param data - The stage data to format\r\n * @param indent - The indentation level\r\n * @returns Formatted string\r\n */\r\n private formatStageData(data: any, indent: number = 0): string {\r\n const spaces = \" \".repeat(indent);\r\n\r\n if (typeof data !== \"object\" || data === null) {\r\n return `${spaces}${JSON.stringify(data)}\\n`;\r\n }\r\n\r\n if (Array.isArray(data)) {\r\n if (data.length === 0) return `${spaces}[]`;\r\n\r\n let result = \"\";\r\n data.forEach((item, index) => {\r\n result += `${spaces}[${colors.magenta(index)}]:\\n`;\r\n result += this.formatStageData(item, indent + 2);\r\n });\r\n return result;\r\n }\r\n\r\n let result = \"\";\r\n Object.entries(data).forEach(([key, value]) => {\r\n const isOperator = key.startsWith(\"$\");\r\n const coloredKey = isOperator ? colors.magentaBright(key) : colors.blue(key);\r\n\r\n if (typeof value === \"object\" && value !== null && !Array.isArray(value)) {\r\n result += `${spaces}${coloredKey}:\\n`;\r\n result += this.formatStageData(value, indent + 2);\r\n } else if (Array.isArray(value)) {\r\n result += `${spaces}${coloredKey}:\\n`;\r\n result += this.formatStageData(value, indent + 2);\r\n } else {\r\n const formattedValue =\r\n typeof value === \"number\"\r\n ? colors.yellowBright(value)\r\n : typeof value === \"boolean\"\r\n ? colors.cyanBright(value.toString())\r\n : typeof value === \"string\"\r\n ? colors.greenBright(JSON.stringify(value))\r\n : colors.greenBright(String(value));\r\n result += `${spaces}${coloredKey}: ${formattedValue}\\n`;\r\n }\r\n });\r\n\r\n return result.endsWith(\"\\n\") ? result : `${result}\\n`;\r\n }\r\n\r\n /**\r\n * Build a single pipeline stage from a group of operations.\r\n *\r\n * @param stage - The pipeline stage type\r\n * @param operations - The operations to build the stage from\r\n * @returns The built pipeline stage or null if no stage should be added\r\n */\r\n private buildStage(stage: PipelineStage, operations: Operation[]): any {\r\n switch (stage) {\r\n case \"$match\":\r\n return this.buildMatchStage(operations);\r\n case \"$project\":\r\n return this.buildProjectStage(operations);\r\n case \"$sort\":\r\n return this.buildSortStage(operations);\r\n case \"$group\":\r\n return this.buildGroupStage(operations);\r\n case \"$lookup\":\r\n return this.buildLookupStage(operations);\r\n case \"$limit\":\r\n return { $limit: operations[0].data.value };\r\n case \"$skip\":\r\n return { $skip: operations[0].data.value };\r\n case \"$setWindowFields\":\r\n return {\r\n $setWindowFields: operations[0].data.spec,\r\n };\r\n default:\r\n return null;\r\n }\r\n }\r\n\r\n /**\r\n * Build a $match stage from where operations.\r\n *\r\n * Query building strategy:\r\n * - Top-level where() + orWhere() = Pure OR\r\n * - Use callbacks for AND + OR grouping\r\n *\r\n * @param operations - The where operations\r\n * @returns The $match stage or null\r\n */\r\n private buildMatchStage(operations: Operation[]): any {\r\n const andFilter: Record<string, any> = {};\r\n const orClauses: any[] = [];\r\n const pendingSimpleWhere: any[] = [];\r\n let topLevelOrMode = false;\r\n\r\n const pushOr = (clause: any): void => {\r\n if (!clause) {\r\n return;\r\n }\r\n\r\n if (this.isPureOrCondition(clause)) {\r\n orClauses.push(...clause.$or);\r\n return;\r\n }\r\n\r\n if (Array.isArray(clause)) {\r\n orClauses.push(...clause);\r\n return;\r\n }\r\n\r\n orClauses.push(clause);\r\n };\r\n\r\n const mergeAnd = (condition: any): void => {\r\n if (!condition) {\r\n return;\r\n }\r\n\r\n Object.entries(condition).forEach(([key, value]) => {\r\n if (key === \"$or\") {\r\n pushOr(value);\r\n return;\r\n }\r\n\r\n if (\r\n value &&\r\n typeof value === \"object\" &&\r\n !Array.isArray(value) &&\r\n andFilter[key] &&\r\n typeof andFilter[key] === \"object\" &&\r\n !Array.isArray(andFilter[key])\r\n ) {\r\n andFilter[key] = { ...andFilter[key], ...value };\r\n } else {\r\n andFilter[key] = value;\r\n }\r\n });\r\n };\r\n\r\n const queueSimpleWhere = (condition: any): void => {\r\n if (!condition) {\r\n return;\r\n }\r\n if (topLevelOrMode) {\r\n pushOr(condition);\r\n } else {\r\n pendingSimpleWhere.push(condition);\r\n }\r\n };\r\n\r\n const enterTopLevelOrMode = (): void => {\r\n if (topLevelOrMode) {\r\n return;\r\n }\r\n topLevelOrMode = true;\r\n while (pendingSimpleWhere.length > 0) {\r\n const condition = pendingSimpleWhere.shift();\r\n if (condition) {\r\n pushOr(condition);\r\n }\r\n }\r\n };\r\n\r\n const flushPendingSimpleWhere = (): void => {\r\n if (pendingSimpleWhere.length === 0) {\r\n return;\r\n }\r\n if (topLevelOrMode) {\r\n pendingSimpleWhere.forEach(pushOr);\r\n } else {\r\n pendingSimpleWhere.forEach(mergeAnd);\r\n }\r\n pendingSimpleWhere.length = 0;\r\n };\r\n\r\n for (const op of operations) {\r\n if (op.type === \"where:callback\" || op.type === \"orWhere:callback\") {\r\n flushPendingSimpleWhere();\r\n const callbackCondition = this.buildCallbackCondition(op.data);\r\n if (!callbackCondition) {\r\n continue;\r\n }\r\n\r\n const treatAsOr =\r\n op.type === \"orWhere:callback\" ||\r\n (topLevelOrMode && !this.isPureOrCondition(callbackCondition)) ||\r\n this.isPureOrCondition(callbackCondition);\r\n\r\n if (treatAsOr) {\r\n if (op.type === \"orWhere:callback\") {\r\n enterTopLevelOrMode();\r\n }\r\n pushOr(callbackCondition);\r\n } else {\r\n mergeAnd(callbackCondition);\r\n }\r\n continue;\r\n }\r\n\r\n if (op.type === \"where:object\") {\r\n queueSimpleWhere(op.data);\r\n continue;\r\n }\r\n\r\n if (\r\n op.type === \"where:not\" ||\r\n op.type === \"orWhere:not\" ||\r\n op.type === \"where:exists\" ||\r\n op.type === \"where:notExists\"\r\n ) {\r\n const negated = op.type === \"where:not\" || op.type === \"where:notExists\";\r\n const nested = this.buildCallbackCondition(op.data.callback);\r\n if (nested) {\r\n const condition = negated ? { $nor: [nested] } : nested;\r\n if (op.type.startsWith(\"orWhere\")) {\r\n enterTopLevelOrMode();\r\n pushOr(condition);\r\n } else {\r\n queueSimpleWhere(condition);\r\n }\r\n }\r\n continue;\r\n }\r\n\r\n if (op.type === \"orWhere:object\") {\r\n enterTopLevelOrMode();\r\n pushOr(op.data);\r\n continue;\r\n }\r\n\r\n const condition = this.buildWhereCondition(op);\r\n if (!condition) {\r\n continue;\r\n }\r\n\r\n if (op.type.startsWith(\"orWhere\")) {\r\n enterTopLevelOrMode();\r\n pushOr(condition);\r\n } else {\r\n queueSimpleWhere(condition);\r\n }\r\n }\r\n\r\n flushPendingSimpleWhere();\r\n\r\n const hasAnd = Object.keys(andFilter).length > 0;\r\n const hasOr = orClauses.length > 0;\r\n\r\n if (!hasAnd && !hasOr) {\r\n return null;\r\n }\r\n\r\n const match: any = {};\r\n if (hasAnd) {\r\n Object.assign(match, andFilter);\r\n }\r\n if (hasOr) {\r\n match.$or = orClauses;\r\n }\r\n\r\n return { $match: match };\r\n }\r\n\r\n private isPureOrCondition(condition: any): condition is { $or: any[] } {\r\n return (\r\n condition &&\r\n typeof condition === \"object\" &&\r\n !Array.isArray(condition) &&\r\n Object.keys(condition).length === 1 &&\r\n Array.isArray((condition as any).$or)\r\n );\r\n }\r\n\r\n /**\r\n * Build a condition from a callback-based where clause.\r\n * Creates a sub-builder, executes the callback, and extracts the conditions.\r\n * If callback has orWhere, all conditions become OR.\r\n *\r\n * @param callback - The callback function\r\n * @returns The built condition or null\r\n */\r\n private buildCallbackCondition(callback: any): any {\r\n // Create a temporary sub-builder\r\n const subBuilder = this.createSubBuilder();\r\n\r\n // Execute the callback with the sub-builder\r\n callback(subBuilder);\r\n\r\n // Extract only match operations from the sub-builder\r\n const matchOps = subBuilder.operations.filter((op: Operation) => op.stage === \"$match\");\r\n\r\n if (matchOps.length === 0) {\r\n return null;\r\n }\r\n\r\n const andFilter: Record<string, any> = {};\r\n const orClauses: any[] = [];\r\n const hasInternalOr = matchOps.some((op) => op.type.startsWith(\"orWhere\"));\r\n\r\n const pushOr = (clause: any): void => {\r\n if (!clause) {\r\n return;\r\n }\r\n if (this.isPureOrCondition(clause)) {\r\n orClauses.push(...clause.$or);\r\n return;\r\n }\r\n orClauses.push(clause);\r\n };\r\n\r\n if (hasInternalOr) {\r\n for (const op of matchOps) {\r\n if (op.type === \"where:callback\" || op.type === \"orWhere:callback\") {\r\n const nestedCondition = this.buildCallbackCondition(op.data);\r\n if (nestedCondition) {\r\n pushOr(nestedCondition);\r\n }\r\n continue;\r\n }\r\n\r\n if (op.type === \"where:object\" || op.type === \"orWhere:object\") {\r\n pushOr(op.data);\r\n continue;\r\n }\r\n\r\n const condition = this.buildWhereCondition(op);\r\n if (condition) {\r\n pushOr(condition);\r\n }\r\n }\r\n\r\n return orClauses.length > 0 ? { $or: orClauses } : null;\r\n }\r\n\r\n for (const op of matchOps) {\r\n if (op.type === \"where:callback\") {\r\n const nestedCondition = this.buildCallbackCondition(op.data);\r\n if (nestedCondition) {\r\n Object.assign(andFilter, nestedCondition);\r\n }\r\n } else if (op.type === \"where:object\") {\r\n Object.assign(andFilter, op.data);\r\n } else {\r\n const condition = this.buildWhereCondition(op);\r\n if (condition) {\r\n Object.assign(andFilter, condition);\r\n }\r\n }\r\n }\r\n\r\n return Object.keys(andFilter).length > 0 ? andFilter : null;\r\n }\r\n\r\n /**\r\n * Build a MongoDB filter condition from a where operation.\r\n *\r\n * @param op - The operation to build\r\n * @returns The MongoDB filter condition\r\n */\r\n private buildWhereCondition(op: Operation): any {\r\n const { field, operator, value } = op.data;\r\n\r\n switch (op.type) {\r\n case \"where\":\r\n case \"orWhere\":\r\n return this.buildOperatorCondition(field, operator, value);\r\n\r\n case \"whereIn\":\r\n return { [field]: { $in: value || op.data.values } };\r\n\r\n case \"whereNotIn\":\r\n return { [field]: { $nin: value || op.data.values } };\r\n\r\n case \"whereNull\":\r\n return { [field]: null };\r\n\r\n case \"whereNotNull\":\r\n return { [field]: { $ne: null } };\r\n\r\n case \"whereBetween\":\r\n return {\r\n [field]: {\r\n $gte: op.data.range[0],\r\n $lte: op.data.range[1],\r\n },\r\n };\r\n\r\n case \"whereNotBetween\":\r\n return {\r\n [field]: {\r\n $not: {\r\n $gte: op.data.range[0],\r\n $lte: op.data.range[1],\r\n },\r\n },\r\n };\r\n\r\n case \"whereLike\":\r\n return {\r\n [field]: { $regex: this.buildLikeRegexSource(op.data), $options: \"i\" },\r\n };\r\n\r\n case \"whereNotLike\":\r\n return {\r\n [field]: {\r\n $not: { $regex: this.buildLikeRegexSource(op.data), $options: \"i\" },\r\n },\r\n };\r\n\r\n case \"whereStartsWith\":\r\n return {\r\n [field]: { $regex: `^${escapeRegex(String(op.data.value))}`, $options: \"i\" },\r\n };\r\n\r\n case \"whereNotStartsWith\":\r\n return {\r\n [field]: {\r\n $not: { $regex: `^${escapeRegex(String(op.data.value))}`, $options: \"i\" },\r\n },\r\n };\r\n\r\n case \"whereEndsWith\":\r\n return {\r\n [field]: { $regex: `${escapeRegex(String(op.data.value))}$`, $options: \"i\" },\r\n };\r\n\r\n case \"whereNotEndsWith\":\r\n return {\r\n [field]: {\r\n $not: { $regex: `${escapeRegex(String(op.data.value))}$`, $options: \"i\" },\r\n },\r\n };\r\n\r\n case \"whereExists\":\r\n return { [field]: { $exists: true } };\r\n\r\n case \"whereNotExists\":\r\n return { [field]: { $exists: false } };\r\n\r\n case \"whereSize\":\r\n if (op.data.operator === \"=\") {\r\n return { [field]: { $size: op.data.size } };\r\n } else {\r\n const mongoOp = this.getMongoOperator(op.data.operator);\r\n return {\r\n $expr: {\r\n [mongoOp]: [{ $size: `$${field}` }, op.data.size],\r\n },\r\n };\r\n }\r\n\r\n case \"textSearch\":\r\n return {\r\n $text: { $search: op.data.query },\r\n ...(op.data.filters || {}),\r\n };\r\n\r\n case \"whereRaw\":\r\n case \"orWhereRaw\":\r\n return this.resolveRawExpression(\r\n op.data.expression as RawExpression,\r\n op.data.bindings,\r\n );\r\n\r\n case \"whereColumn\":\r\n case \"orWhereColumn\":\r\n return this.buildColumnComparison(op.data.first, op.data.operator, op.data.second);\r\n\r\n case \"whereBetweenColumns\":\r\n return this.buildBetweenColumnsCondition(\r\n op.data.field,\r\n op.data.lowerColumn,\r\n op.data.upperColumn,\r\n );\r\n\r\n case \"whereDate\":\r\n case \"whereDateEquals\":\r\n return this.buildDateEqualityCondition(op.data.field, op.data.value);\r\n\r\n case \"whereDateBefore\":\r\n return this.buildDateBeforeCondition(op.data.field, op.data.value);\r\n\r\n case \"whereDateAfter\":\r\n return this.buildDateAfterCondition(op.data.field, op.data.value);\r\n\r\n case \"whereTime\":\r\n return this.buildTimeCondition(op.data.field, op.data.value);\r\n\r\n case \"whereDay\":\r\n return this.buildDatePartCondition(op.data.field, \"$dayOfMonth\", op.data.value);\r\n\r\n case \"whereMonth\":\r\n return this.buildDatePartCondition(op.data.field, \"$month\", op.data.value);\r\n\r\n case \"whereYear\":\r\n return this.buildDatePartCondition(op.data.field, \"$year\", op.data.value);\r\n\r\n case \"whereJsonContains\":\r\n return this.buildJsonContainsCondition(op.data.path, op.data.value);\r\n\r\n case \"whereJsonDoesntContain\":\r\n return this.buildJsonDoesntContainCondition(op.data.path, op.data.value);\r\n\r\n case \"whereJsonContainsKey\":\r\n return this.buildJsonContainsKeyCondition(op.data.path);\r\n\r\n case \"whereJsonLength\":\r\n return this.buildJsonLengthCondition(\r\n op.data.path,\r\n op.data.operator,\r\n op.data.value,\r\n );\r\n\r\n case \"whereJsonIsArray\":\r\n return this.buildJsonTypeCondition(op.data.path, \"array\");\r\n\r\n case \"whereJsonIsObject\":\r\n return this.buildJsonTypeCondition(op.data.path, \"object\");\r\n\r\n case \"whereArrayLength\":\r\n return this.buildArrayLengthCondition(\r\n op.data.field,\r\n op.data.operator,\r\n op.data.value,\r\n );\r\n\r\n case \"whereFullText\":\r\n case \"orWhereFullText\":\r\n return { $text: { $search: op.data.query } };\r\n\r\n case \"whereSearch\":\r\n return {\r\n [op.data.field]: {\r\n // Search text is the most user-facing input in the whole API —\r\n // always a literal, never a pattern.\r\n $regex: escapeRegex(String(op.data.query)),\r\n $options: \"i\",\r\n },\r\n };\r\n\r\n case \"where:not\":\r\n case \"orWhere:not\": {\r\n const nestedNot = this.buildCallbackCondition(op.data.callback);\r\n return nestedNot ? { $nor: [nestedNot] } : null;\r\n }\r\n\r\n case \"where:exists\":\r\n return this.buildCallbackCondition(op.data.callback);\r\n\r\n case \"where:notExists\": {\r\n const nestedExists = this.buildCallbackCondition(op.data.callback);\r\n return nestedExists ? { $nor: [nestedExists] } : null;\r\n }\r\n\r\n case \"whereArrayContains\":\r\n if (op.data.key) {\r\n return {\r\n [field]: {\r\n $elemMatch: { [op.data.key]: op.data.value },\r\n },\r\n };\r\n } else {\r\n return { [field]: op.data.value };\r\n }\r\n\r\n default:\r\n return null;\r\n }\r\n }\r\n\r\n /**\r\n * Resolve the `$regex` source for a whereLike/whereNotLike operation.\r\n *\r\n * A `RegExp` argument is developer-authored and used verbatim; a string is\r\n * treated as a literal LIKE pattern (`%` aside) so that search input cannot\r\n * inject regex operators or a catastrophically backtracking pattern. The base\r\n * (driver-agnostic) query builder flattens a `RegExp` to its source string and\r\n * flags it with `isRegExp`, which is honored here for the same reason.\r\n *\r\n * @param data - The operation data (`{ pattern, isRegExp? }`)\r\n * @returns Regex source for `$regex`\r\n */\r\n private buildLikeRegexSource(data: any): string {\r\n if (data.isRegExp === true && typeof data.pattern === \"string\") {\r\n return data.pattern;\r\n }\r\n\r\n return resolveLikePattern(data.pattern);\r\n }\r\n\r\n /**\r\n * Build a condition based on the operator.\r\n *\r\n * @param field - The field name\r\n * @param operator - The comparison operator\r\n * @param value - The value to compare\r\n * @returns The MongoDB filter condition\r\n */\r\n private buildOperatorCondition(field: string, operator: string, value: unknown): any {\r\n switch (operator) {\r\n case \"=\":\r\n return { [field]: value };\r\n case \"!=\":\r\n return { [field]: { $ne: value } };\r\n case \">\":\r\n return { [field]: { $gt: value } };\r\n case \">=\":\r\n return { [field]: { $gte: value } };\r\n case \"<\":\r\n return { [field]: { $lt: value } };\r\n case \"<=\":\r\n return { [field]: { $lte: value } };\r\n default:\r\n return { [field]: value };\r\n }\r\n }\r\n\r\n /**\r\n * Get MongoDB operator from comparison operator.\r\n *\r\n * @param operator - The comparison operator\r\n * @returns The MongoDB operator\r\n */\r\n private getMongoOperator(operator: string): string {\r\n const map: Record<string, string> = {\r\n \"=\": \"$eq\",\r\n \"!=\": \"$ne\",\r\n \">\": \"$gt\",\r\n \">=\": \"$gte\",\r\n \"<\": \"$lt\",\r\n \"<=\": \"$lte\",\r\n };\r\n return map[operator] || \"$eq\";\r\n }\r\n\r\n private resolveRawExpression(expression: RawExpression, _bindings?: unknown[]): any {\r\n if (typeof expression === \"string\") {\r\n // String expressions used to compile to { $where: \"<js>\" } — a\r\n // server-side JavaScript execution sink inside mongod (and an unindexed\r\n // full-scan even when trusted). Refuse them outright.\r\n throw new UnsafeRawExpressionError(\r\n `whereRaw()/orWhereRaw() string expressions are not supported on the MongoDB driver: ` +\r\n `they would compile to a {$where: \"<js>\"} filter, which executes JavaScript on the database server. ` +\r\n `Use the object form instead, e.g. whereRaw({ $expr: { $gt: [\"$stock\", \"$reserved\"] } }).`,\r\n );\r\n }\r\n\r\n if (typeof expression === \"object\" && expression !== null) {\r\n this.assertNoJsExecutionOperators(expression);\r\n return expression;\r\n }\r\n\r\n return null;\r\n }\r\n\r\n /**\r\n * The object form of whereRaw() is forwarded to MongoDB verbatim, so it must\r\n * not smuggle in the operators that execute JavaScript inside mongod\r\n * ($where, and $function/$accumulator which can nest under $expr).\r\n * Aggregation operators like $expr/$gt remain valid — that is the object\r\n * form's purpose.\r\n */\r\n private assertNoJsExecutionOperators(value: unknown): void {\r\n if (Array.isArray(value)) {\r\n for (const item of value) {\r\n this.assertNoJsExecutionOperators(item);\r\n }\r\n return;\r\n }\r\n\r\n if (typeof value !== \"object\" || value === null) return;\r\n\r\n for (const [key, nested] of Object.entries(value as Record<string, unknown>)) {\r\n if (key === \"$where\" || key === \"$function\" || key === \"$accumulator\") {\r\n throw new UnsafeRawExpressionError(\r\n `whereRaw()/orWhereRaw() expressions must not contain \"${key}\": ` +\r\n `it executes JavaScript on the database server. ` +\r\n `Express the condition with query operators instead, e.g. whereRaw({ $expr: { $gt: [\"$stock\", \"$reserved\"] } }).`,\r\n );\r\n }\r\n this.assertNoJsExecutionOperators(nested);\r\n }\r\n }\r\n\r\n private bindRawString(expression: string, bindings?: unknown[]): string {\r\n if (!bindings || bindings.length === 0) {\r\n return expression;\r\n }\r\n\r\n let index = 0;\r\n return expression.replace(/\\?/g, () => {\r\n const value = bindings[index++];\r\n return value === undefined ? \"?\" : JSON.stringify(value);\r\n });\r\n }\r\n\r\n private buildColumnComparison(first: string, operator: WhereOperator, second: string): any {\r\n const mongoOperator = this.getMongoOperator(operator);\r\n return {\r\n $expr: {\r\n [mongoOperator]: [this.wrapColumn(first), this.wrapColumn(second)],\r\n },\r\n };\r\n }\r\n\r\n private buildBetweenColumnsCondition(field: string, lower: string, upper: string): any {\r\n return {\r\n $expr: {\r\n $and: [\r\n { $gte: [this.wrapColumn(field), this.wrapColumn(lower)] },\r\n { $lte: [this.wrapColumn(field), this.wrapColumn(upper)] },\r\n ],\r\n },\r\n };\r\n }\r\n\r\n private wrapColumn(column: string): string {\r\n return column.startsWith(\"$\") ? column : `$${column}`;\r\n }\r\n\r\n private buildDateEqualityCondition(field: string, value: Date | string): any {\r\n const target = this.normalizeDateInput(value);\r\n const start = this.startOfDay(target);\r\n const end = this.endOfDay(target);\r\n return { [field]: { $gte: start, $lte: end } };\r\n }\r\n\r\n private buildDateBeforeCondition(field: string, value: Date | string): any {\r\n const target = this.startOfDay(this.normalizeDateInput(value));\r\n return { [field]: { $lt: target } };\r\n }\r\n\r\n private buildDateAfterCondition(field: string, value: Date | string): any {\r\n const target = this.endOfDay(this.normalizeDateInput(value));\r\n return { [field]: { $gt: target } };\r\n }\r\n\r\n private buildTimeCondition(field: string, value: string): any {\r\n return {\r\n $expr: {\r\n $eq: [\r\n {\r\n $dateToString: {\r\n format: \"%H:%M\",\r\n date: `$${field}`,\r\n },\r\n },\r\n value,\r\n ],\r\n },\r\n };\r\n }\r\n\r\n private buildDatePartCondition(\r\n field: string,\r\n operator: \"$dayOfMonth\" | \"$month\" | \"$year\",\r\n value: number,\r\n ): any {\r\n return {\r\n $expr: {\r\n $eq: [\r\n {\r\n [operator]: `$${field}`,\r\n },\r\n value,\r\n ],\r\n },\r\n };\r\n }\r\n\r\n private buildJsonContainsCondition(path: string, value: unknown): any {\r\n const fieldPath = this.normalizePath(path);\r\n if (Array.isArray(value)) {\r\n return { [fieldPath]: { $all: value } };\r\n }\r\n return { [fieldPath]: value };\r\n }\r\n\r\n private buildJsonDoesntContainCondition(path: string, value: unknown): any {\r\n const fieldPath = this.normalizePath(path);\r\n const values = Array.isArray(value) ? value : [value];\r\n return { [fieldPath]: { $nin: values } };\r\n }\r\n\r\n private buildJsonContainsKeyCondition(path: string): any {\r\n return {\r\n [this.normalizePath(path)]: { $exists: true },\r\n };\r\n }\r\n\r\n private buildJsonLengthCondition(path: string, operator: WhereOperator, value: number): any {\r\n const mongoOperator = this.getMongoOperator(operator);\r\n return {\r\n $expr: {\r\n [mongoOperator]: [{ $size: { $ifNull: [`$${this.normalizePath(path)}`, []] } }, value],\r\n },\r\n };\r\n }\r\n\r\n private buildJsonTypeCondition(path: string, type: string): any {\r\n return {\r\n $expr: {\r\n $eq: [{ $type: `$${this.normalizePath(path)}` }, type],\r\n },\r\n };\r\n }\r\n\r\n private buildArrayLengthCondition(field: string, operator: WhereOperator, value: number): any {\r\n const mongoOperator = this.getMongoOperator(operator);\r\n return {\r\n $expr: {\r\n [mongoOperator]: [{ $size: { $ifNull: [`$${field}`, []] } }, value],\r\n },\r\n };\r\n }\r\n\r\n private normalizeDateInput(value: Date | string): Date {\r\n if (value instanceof Date) {\r\n return value;\r\n }\r\n const parsed = new Date(value);\r\n if (Number.isNaN(parsed.getTime())) {\r\n throw new Error(`Invalid date value: ${value}`);\r\n }\r\n return parsed;\r\n }\r\n\r\n private startOfDay(date: Date): Date {\r\n const copy = new Date(date);\r\n copy.setHours(0, 0, 0, 0);\r\n return copy;\r\n }\r\n\r\n private endOfDay(date: Date): Date {\r\n const copy = new Date(date);\r\n copy.setHours(23, 59, 59, 999);\r\n return copy;\r\n }\r\n\r\n private normalizePath(path: string): string {\r\n return path.replace(/->/g, \".\");\r\n }\r\n\r\n private applyProjectionFields(\r\n projection: Record<string, unknown>,\r\n fields: string[],\r\n value: 0 | 1,\r\n ): void {\r\n for (const field of fields) {\r\n projection[field] = value;\r\n }\r\n }\r\n\r\n /**\r\n * Apply projection object with aliases and inclusion/exclusion.\r\n * @param projection - The projection object to modify\r\n * @param projectionObj - The projection specification\r\n */\r\n private applyProjectionObject(\r\n projection: Record<string, unknown>,\r\n projectionObj: Record<string, unknown>,\r\n ): void {\r\n for (const [field, value] of Object.entries(projectionObj)) {\r\n // Handle boolean values (true = 1, false = 0)\r\n if (typeof value === \"boolean\") {\r\n projection[field] = value ? 1 : 0;\r\n continue;\r\n }\r\n\r\n // Handle numeric values (0 or 1)\r\n if (typeof value === \"number\") {\r\n projection[field] = value;\r\n continue;\r\n }\r\n\r\n // Handle string values (aliases)\r\n if (typeof value === \"string\") {\r\n // Alias: project the field with a new name\r\n projection[value] = `$${field}`;\r\n continue;\r\n }\r\n\r\n // Handle complex expressions (objects)\r\n if (typeof value === \"object\" && value !== null) {\r\n projection[field] = value;\r\n continue;\r\n }\r\n\r\n // Default: include the field\r\n projection[field] = 1;\r\n }\r\n }\r\n\r\n private applyRawProjection(\r\n projection: Record<string, unknown>,\r\n expression: RawExpression,\r\n bindings?: unknown[],\r\n ): void {\r\n const resolved = this.resolveProjectionExpression(expression, bindings);\r\n if (!resolved) {\r\n return;\r\n }\r\n\r\n if (typeof resolved === \"object\" && resolved !== null && !Array.isArray(resolved)) {\r\n Object.assign(projection, resolved as Record<string, unknown>);\r\n }\r\n }\r\n\r\n private resolveProjectionExpression(\r\n expression: RawExpression | unknown,\r\n bindings?: unknown[],\r\n ): any {\r\n if (typeof expression === \"string\") {\r\n const source =\r\n bindings && expression.includes(\"?\")\r\n ? this.bindRawString(expression, bindings)\r\n : expression;\r\n if (source.startsWith(\":\")) {\r\n return source.slice(1);\r\n }\r\n return this.normalizeFieldReference(source);\r\n }\r\n\r\n if (typeof expression === \"object\" && expression !== null && !(expression instanceof Date)) {\r\n return expression;\r\n }\r\n\r\n if (typeof expression === \"number\" || typeof expression === \"boolean\") {\r\n return expression;\r\n }\r\n\r\n return expression;\r\n }\r\n\r\n private normalizeFieldReference(value: string | RawExpression): any {\r\n if (typeof value === \"string\") {\r\n if (value.startsWith(\":\")) {\r\n return value.slice(1);\r\n }\r\n // If already a field reference, return as-is\r\n if (value.startsWith(\"$\")) {\r\n return value;\r\n }\r\n // Check if it's a string literal (contains spaces or special chars)\r\n // Field paths are typically: alphanumeric, underscore, dot only\r\n if (!/^[a-zA-Z0-9_.]+$/.test(value)) {\r\n return value; // Return as literal\r\n }\r\n // Otherwise, treat as field reference\r\n return `$${value}`;\r\n }\r\n return value;\r\n }\r\n\r\n private buildAggregateProjection(field: string, aggregate: string): any {\r\n if (aggregate === \"count\") {\r\n return this.buildArraySizeExpression(field);\r\n }\r\n\r\n const map: Record<string, string> = {\r\n sum: \"$sum\",\r\n avg: \"$avg\",\r\n min: \"$min\",\r\n max: \"$max\",\r\n first: \"$first\",\r\n last: \"$last\",\r\n };\r\n\r\n const operator = map[aggregate];\r\n if (!operator) {\r\n return null;\r\n }\r\n\r\n return {\r\n [operator]: this.normalizeFieldReference(field),\r\n };\r\n }\r\n\r\n private buildExistsProjection(field: string): any {\r\n return {\r\n $ne: [{ $type: `$${field}` }, \"missing\"],\r\n };\r\n }\r\n\r\n private buildArraySizeExpression(field: string): any {\r\n return {\r\n $size: { $ifNull: [`$${field}`, []] },\r\n };\r\n }\r\n\r\n private buildCaseExpression(\r\n cases: Array<{ when: RawExpression; then: RawExpression | unknown }>,\r\n otherwise: RawExpression | unknown,\r\n ): any {\r\n return {\r\n $switch: {\r\n branches: cases.map((item) => ({\r\n case: this.resolveProjectionExpression(item.when),\r\n then: this.resolveLiteralOrExpression(item.then),\r\n })),\r\n default: this.resolveLiteralOrExpression(otherwise),\r\n },\r\n };\r\n }\r\n\r\n private buildCondExpression(\r\n condition: RawExpression,\r\n thenValue: RawExpression | unknown,\r\n elseValue: RawExpression | unknown,\r\n ): any {\r\n return {\r\n $cond: [\r\n this.resolveProjectionExpression(condition),\r\n this.resolveLiteralOrExpression(thenValue),\r\n this.resolveLiteralOrExpression(elseValue),\r\n ],\r\n };\r\n }\r\n\r\n /**\r\n * Resolve a value as a literal (if it's a plain string) or as an expression.\r\n * Used for `then`/`default` values in CASE/WHEN expressions.\r\n */\r\n private resolveLiteralOrExpression(value: RawExpression | unknown): any {\r\n // If it's a string that starts with $, treat as field reference\r\n if (typeof value === \"string\" && value.startsWith(\"$\")) {\r\n return value;\r\n }\r\n // If it's a plain string (not starting with $), treat as literal\r\n if (typeof value === \"string\") {\r\n return value;\r\n }\r\n // For objects (expressions), numbers, booleans, etc., use normal resolution\r\n return this.resolveProjectionExpression(value);\r\n }\r\n\r\n private inferJsonAlias(path: string): string {\r\n const normalized = this.normalizePath(path);\r\n const segments = normalized.split(\".\");\r\n return segments[segments.length - 1];\r\n }\r\n\r\n private buildConcatExpression(values: Array<string | RawExpression>): any {\r\n return {\r\n $concat: values.map((value) => this.normalizeFieldReference(value)),\r\n };\r\n }\r\n\r\n private buildCoalesceExpression(values: Array<string | RawExpression>): any {\r\n if (values.length === 0) {\r\n return null;\r\n }\r\n\r\n let expression = this.normalizeFieldReference(values[values.length - 1]);\r\n\r\n for (let index = values.length - 2; index >= 0; index--) {\r\n expression = {\r\n $ifNull: [this.normalizeFieldReference(values[index]), expression],\r\n };\r\n }\r\n\r\n return expression;\r\n }\r\n\r\n /**\r\n * Build a $project stage from select operations.\r\n *\r\n * @param operations - The select operations\r\n * @returns The $project stage or null\r\n */\r\n private buildProjectStage(operations: Operation[]): any {\r\n if (operations.length === 0) {\r\n return null;\r\n }\r\n\r\n const projection: Record<string, unknown> = {};\r\n const driverCallbacks: Array<(projection: Record<string, unknown>) => void> = [];\r\n\r\n for (const op of operations) {\r\n switch (op.type) {\r\n case \"select\":\r\n // Handle new projection format with aliases\r\n if (op.data.projection) {\r\n this.applyProjectionObject(projection, op.data.projection);\r\n } else if (op.data.fields) {\r\n this.applyProjectionFields(projection, op.data.fields, 1);\r\n }\r\n break;\r\n\r\n case \"deselect\":\r\n this.applyProjectionFields(projection, op.data.fields, 0);\r\n break;\r\n\r\n case \"addSelect\":\r\n this.applyProjectionFields(projection, op.data.fields, 1);\r\n break;\r\n\r\n case \"selectRaw\":\r\n this.applyRawProjection(projection, op.data.expression, op.data.bindings);\r\n break;\r\n\r\n case \"selectSub\":\r\n case \"addSelectSub\": {\r\n const expr = this.resolveProjectionExpression(op.data.expression, op.data.bindings);\r\n if (expr !== undefined) {\r\n projection[op.data.alias] = expr;\r\n }\r\n break;\r\n }\r\n\r\n case \"selectAggregate\":\r\n projection[op.data.alias] = this.buildAggregateProjection(\r\n op.data.field,\r\n op.data.aggregate,\r\n );\r\n break;\r\n\r\n case \"selectExists\":\r\n projection[op.data.alias] = this.buildExistsProjection(op.data.field);\r\n break;\r\n\r\n case \"selectCount\":\r\n projection[op.data.alias] = this.buildArraySizeExpression(op.data.field);\r\n break;\r\n\r\n case \"selectCase\":\r\n projection[op.data.alias] = this.buildCaseExpression(\r\n op.data.cases,\r\n op.data.otherwise,\r\n );\r\n break;\r\n\r\n case \"selectWhen\":\r\n projection[op.data.alias] = this.buildCondExpression(\r\n op.data.condition,\r\n op.data.thenValue,\r\n op.data.elseValue,\r\n );\r\n break;\r\n\r\n case \"selectDriverProjection\":\r\n driverCallbacks.push(op.data.callback);\r\n break;\r\n\r\n case \"selectJson\": {\r\n const alias = op.data.alias ?? this.inferJsonAlias(op.data.path);\r\n projection[alias] = this.normalizeFieldReference(\r\n `$${this.normalizePath(op.data.path)}`,\r\n );\r\n break;\r\n }\r\n\r\n case \"selectJsonRaw\": {\r\n projection[op.data.alias] = this.resolveProjectionExpression(op.data.expression);\r\n break;\r\n }\r\n\r\n case \"deselectJson\":\r\n projection[this.normalizePath(op.data.path)] = 0;\r\n break;\r\n\r\n case \"selectConcat\":\r\n projection[op.data.alias] = this.buildConcatExpression(op.data.fields);\r\n break;\r\n\r\n case \"selectCoalesce\":\r\n projection[op.data.alias] = this.buildCoalesceExpression(op.data.fields);\r\n break;\r\n\r\n default:\r\n break;\r\n }\r\n }\r\n\r\n for (const callback of driverCallbacks) {\r\n callback(projection);\r\n }\r\n\r\n return Object.keys(projection).length > 0 ? { $project: projection } : null;\r\n }\r\n\r\n /**\r\n * Build a $sort stage from order operations.\r\n *\r\n * @param operations - The order operations\r\n * @returns The $sort stage or null\r\n */\r\n private buildSortStage(operations: Operation[]): any {\r\n const sort: any = {};\r\n\r\n for (const op of operations) {\r\n switch (op.type) {\r\n case \"orderBy\":\r\n sort[op.data.field] = op.data.direction === \"asc\" ? 1 : -1;\r\n break;\r\n\r\n case \"orderByRandom\":\r\n return { $sample: { size: op.data.limit } };\r\n\r\n case \"orderByRaw\":\r\n // TODO: Handle raw expressions\r\n break;\r\n }\r\n }\r\n\r\n return Object.keys(sort).length > 0 ? { $sort: sort } : null;\r\n }\r\n\r\n /**\r\n * Build a $group stage from group operations.\r\n *\r\n * @param operations - The group operations\r\n * @returns The $group stage or null\r\n */\r\n private buildGroupStage(operations: Operation[]): any {\r\n const op = operations[0];\r\n\r\n switch (op.type) {\r\n case \"groupBy\": {\r\n const stage = this.buildGroupByStage(op.data.fields);\r\n if (stage) {\r\n return stage;\r\n }\r\n break;\r\n }\r\n case \"groupByWithAggregates\": {\r\n const stage = this.buildGroupByWithAggregatesStage(\r\n op.data.fields,\r\n op.data.aggregates,\r\n );\r\n if (stage) {\r\n return stage;\r\n }\r\n break;\r\n }\r\n case \"groupByDate\": {\r\n const stage = this.buildGroupByDateStage(\r\n op.data.column,\r\n op.data.unit,\r\n op.data.aggregates ?? {},\r\n );\r\n if (stage) {\r\n return stage;\r\n }\r\n break;\r\n }\r\n case \"groupByRaw\": {\r\n const expression = op.data.expression;\r\n if (expression && typeof expression === \"object\") {\r\n return { $group: expression };\r\n }\r\n // If expression is not an object, it might be a string or other type\r\n // In that case, we should still return it as a $group stage\r\n if (expression) {\r\n return { $group: { _id: expression } };\r\n }\r\n break;\r\n }\r\n case \"distinct\": {\r\n const stage = this.buildGroupByStage(op.data.fields);\r\n if (stage) {\r\n return stage;\r\n }\r\n break;\r\n }\r\n default:\r\n break;\r\n }\r\n\r\n return null;\r\n }\r\n\r\n private buildGroupByStage(fields: GroupByInput): any {\r\n const groupId = this.buildGroupId(fields);\r\n if (!groupId) {\r\n return null;\r\n }\r\n\r\n return { $group: { _id: groupId } };\r\n }\r\n\r\n /**\r\n * Build a $group stage with aggregates from group operations.\r\n *\r\n * @param fields - Fields to group by\r\n * @param aggregates - Aggregate operations (abstract or raw)\r\n * @returns The $group stage or null\r\n */\r\n private buildGroupByWithAggregatesStage(\r\n fields: GroupByInput,\r\n aggregates: Record<string, RawExpression>,\r\n ): any {\r\n const groupId = this.buildGroupId(fields);\r\n if (!groupId) {\r\n return null;\r\n }\r\n\r\n const groupStage: Record<string, unknown> = {\r\n _id: groupId,\r\n };\r\n\r\n // Translate each aggregate expression\r\n for (const [alias, expression] of Object.entries(aggregates)) {\r\n if (isAggregateExpression(expression)) {\r\n // Translate abstract expression to MongoDB format\r\n groupStage[alias] = this.translateAggregateExpression(expression);\r\n } else {\r\n // Use raw expression as-is (already in MongoDB format)\r\n groupStage[alias] = expression;\r\n }\r\n }\r\n\r\n return { $group: groupStage };\r\n }\r\n\r\n /**\r\n * Build a `$group` stage that buckets documents by a `$dateTrunc` of a date\r\n * field, optionally running aggregates over each bucket.\r\n *\r\n * @param column - The date field to bucket\r\n * @param unit - The bucket granularity\r\n * @param aggregates - Aggregate operations (abstract or raw)\r\n * @returns The `$group` stage\r\n */\r\n private buildGroupByDateStage(\r\n column: string,\r\n unit: string,\r\n aggregates: Record<string, RawExpression>,\r\n ): any {\r\n const groupStage: Record<string, unknown> = {\r\n _id: { $dateTrunc: { date: `$${column}`, unit } },\r\n };\r\n\r\n for (const [alias, expression] of Object.entries(aggregates)) {\r\n if (isAggregateExpression(expression)) {\r\n groupStage[alias] = this.translateAggregateExpression(expression);\r\n } else {\r\n groupStage[alias] = expression;\r\n }\r\n }\r\n\r\n return { $group: groupStage };\r\n }\r\n\r\n /**\r\n * Extract field names from GroupByInput for renaming _id.\r\n *\r\n * @param fields - The grouping fields\r\n * @returns Field name(s) to use for renaming _id\r\n */\r\n private extractGroupFieldNames(fields: GroupByInput): string | string[] | null {\r\n if (typeof fields === \"string\") {\r\n return fields;\r\n }\r\n\r\n if (Array.isArray(fields)) {\r\n const allStrings = fields.every((field) => typeof field === \"string\");\r\n if (allStrings) {\r\n return fields as string[];\r\n }\r\n // For complex arrays, return null (don't rename)\r\n return null;\r\n }\r\n\r\n if (typeof fields === \"object\" && fields !== null) {\r\n // For object syntax, use the keys as field names\r\n return Object.keys(fields);\r\n }\r\n\r\n return null;\r\n }\r\n\r\n /**\r\n * Translate an abstract aggregate expression to MongoDB format.\r\n *\r\n * @param expr - Abstract aggregate expression\r\n * @returns MongoDB aggregation expression\r\n */\r\n private translateAggregateExpression(expr: AggregateExpression): Record<string, unknown> {\r\n switch (expr.__agg) {\r\n case \"count\":\r\n return { $sum: 1 };\r\n\r\n case \"countDistinct\":\r\n if (!expr.__field) {\r\n throw new Error(\"Count distinct aggregate requires a field name\");\r\n }\r\n // Accumulate the set of distinct values in the $group stage; the\r\n // renaming $project then finalizes it with `{ $size: \"$alias\" }`\r\n // (the standard distinct-count-per-group pattern — `$size` is not a\r\n // valid $group accumulator).\r\n return { $addToSet: `$${expr.__field}` };\r\n\r\n case \"sum\":\r\n // When a composed column expression is present, sum operates on it\r\n // (e.g. SUM(price * quantity) → { $sum: { $multiply: [...] } }) instead\r\n // of a bare field. This is the only aggregate that accepts `__expr`.\r\n if (expr.__expr) {\r\n return { $sum: this.columnExpressionToMongo(expr.__expr) };\r\n }\r\n if (!expr.__field) {\r\n throw new Error(\"Sum aggregate requires a field name\");\r\n }\r\n return { $sum: `$${expr.__field}` };\r\n\r\n case \"avg\":\r\n if (!expr.__field) {\r\n throw new Error(\"Average aggregate requires a field name\");\r\n }\r\n return { $avg: `$${expr.__field}` };\r\n\r\n case \"min\":\r\n if (!expr.__field) {\r\n throw new Error(\"Min aggregate requires a field name\");\r\n }\r\n return { $min: `$${expr.__field}` };\r\n\r\n case \"max\":\r\n if (!expr.__field) {\r\n throw new Error(\"Max aggregate requires a field name\");\r\n }\r\n return { $max: `$${expr.__field}` };\r\n\r\n case \"first\":\r\n if (!expr.__field) {\r\n throw new Error(\"First aggregate requires a field name\");\r\n }\r\n return { $first: `$${expr.__field}` };\r\n\r\n case \"last\":\r\n if (!expr.__field) {\r\n throw new Error(\"Last aggregate requires a field name\");\r\n }\r\n return { $last: `$${expr.__field}` };\r\n\r\n case \"distinct\":\r\n if (!expr.__field) {\r\n throw new Error(\"Distinct aggregate requires a field name\");\r\n }\r\n return { $distinct: `$${expr.__field}` };\r\n\r\n case \"floor\":\r\n if (!expr.__field) {\r\n throw new Error(\"Floor aggregate requires a field name\");\r\n }\r\n\r\n return { $floor: `$${expr.__field}` };\r\n\r\n default:\r\n throw new Error(`Unknown aggregate function: ${expr.__agg}`);\r\n }\r\n }\r\n\r\n /**\r\n * Compile a typed {@link ColumnExpression} tree into a MongoDB aggregation\r\n * expression.\r\n *\r\n * Column references become `$field` paths; literals are emitted verbatim;\r\n * arithmetic ops map to `$add` / `$subtract` / `$multiply` / `$divide`. The\r\n * `raw` node is rejected — a raw SQL fragment is not portable to a MongoDB\r\n * pipeline, so callers must use the typed combinators (or `groupByRaw`) here.\r\n *\r\n * @param expression - The expression tree to compile\r\n * @returns A MongoDB aggregation expression (e.g. `{ $multiply: [\"$price\", \"$quantity\"] }`)\r\n */\r\n private columnExpressionToMongo(expression: ColumnExpression): unknown {\r\n switch (expression.__expr) {\r\n case \"column\":\r\n return `$${expression.column}`;\r\n\r\n case \"literal\":\r\n return expression.value;\r\n\r\n case \"raw\":\r\n throw new Error(\r\n `$agg.sumRaw / $expr.raw is not portable to a MongoDB pipeline — a raw ` +\r\n `SQL fragment has no MongoDB equivalent. Use the typed $expr ` +\r\n `combinators ($expr.mul / $expr.add / $expr.sub / $expr.div / $expr.col / $expr.lit) or groupByRaw instead.`,\r\n );\r\n\r\n case \"add\":\r\n case \"subtract\":\r\n case \"multiply\":\r\n case \"divide\": {\r\n const operator = {\r\n add: \"$add\",\r\n subtract: \"$subtract\",\r\n multiply: \"$multiply\",\r\n divide: \"$divide\",\r\n }[expression.__expr];\r\n\r\n return {\r\n [operator]: expression.operands.map((operand) => this.columnExpressionToMongo(operand)),\r\n };\r\n }\r\n\r\n default:\r\n throw new Error(`Unsupported column expression node: ${JSON.stringify(expression)}`);\r\n }\r\n }\r\n\r\n private buildGroupId(fields: GroupByInput): any {\r\n if (!fields) {\r\n return null;\r\n }\r\n\r\n if (typeof fields === \"string\") {\r\n return `$${fields}`;\r\n }\r\n\r\n if (Array.isArray(fields)) {\r\n if (fields.length === 0) {\r\n return null;\r\n }\r\n\r\n const allStrings = fields.every((field) => typeof field === \"string\");\r\n if (allStrings) {\r\n const result: Record<string, string> = {};\r\n for (const field of fields as string[]) {\r\n result[field] = `$${field}`;\r\n }\r\n return result;\r\n }\r\n\r\n // Array of objects - merge them to build complex _id structures\r\n return (fields as Record<string, unknown>[]).reduce((acc, item) => ({ ...acc, ...item }), {});\r\n }\r\n\r\n if (typeof fields === \"object\") {\r\n const normalized: Record<string, unknown> = {};\r\n Object.entries(fields).forEach(([key, value]) => {\r\n if (typeof value === \"string\" && !value.startsWith(\"$\")) {\r\n normalized[key] = `$${value}`;\r\n } else {\r\n normalized[key] = value;\r\n }\r\n });\r\n return normalized;\r\n }\r\n\r\n return null;\r\n }\r\n\r\n /**\r\n * Build a $lookup stage from join operations.\r\n *\r\n * @param operations - The join operations\r\n * @returns The $lookup stage or null\r\n */\r\n private buildLookupStage(operations: Operation[]): any {\r\n const op = operations[0];\r\n const options = op.data;\r\n\r\n return {\r\n $lookup: {\r\n from: options.table,\r\n localField: options.localField,\r\n foreignField: options.foreignField,\r\n as: options.alias || options.table,\r\n },\r\n };\r\n }\r\n}\r\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mongodb-query-parser.mjs","names":[],"sources":["../../../../../../../../cascade/src/drivers/mongodb/mongodb-query-parser.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport type { Collection } from \"mongodb\";\r\nimport type { GroupByInput, RawExpression, WhereOperator } from \"../../contracts\";\r\nimport { UnsafeRawExpressionError } from \"../../errors/unsafe-raw-expression.error\";\r\nimport {\r\n isAggregateExpression,\r\n type AggregateExpression,\r\n} from \"../../expressions/aggregate-expressions\";\r\nimport type { ColumnExpression } from \"../../expressions/column-expressions\";\r\nimport { escapeRegex, resolveLikePattern } from \"../../utils/escape-regex\";\r\nimport type { MongoQueryBuilder } from \"./mongodb-query-builder\";\r\nimport type { Operation, PipelineStage } from \"./types\";\r\n\r\n/**\r\n * Options for configuring the MongoDB query parser.\r\n */\r\nexport type MongoQueryParserOptions = {\r\n /** The MongoDB collection being queried */\r\n collection: Collection;\r\n /** The ordered list of operations to parse */\r\n operations: Operation[];\r\n /** Factory method for creating sub-builders (used for callbacks) */\r\n createSubBuilder: () => MongoQueryBuilder;\r\n};\r\n\r\n/**\r\n * Parses query builder operations into MongoDB aggregation pipeline.\r\n *\r\n * This parser is responsible for converting the abstract operations collected\r\n * by the query builder into a concrete MongoDB aggregation pipeline. It handles\r\n * intelligent grouping of mergeable operations (like multiple where clauses)\r\n * into single pipeline stages for optimal performance.\r\n */\r\nexport class MongoQueryParser {\r\n /**\r\n * The MongoDB collection being queried.\r\n */\r\n private readonly collection: Collection;\r\n\r\n /**\r\n * The ordered list of operations to parse.\r\n */\r\n private readonly operations: Operation[];\r\n\r\n /**\r\n * Factory for creating sub-builders (used when resolving callbacks).\r\n */\r\n private readonly createSubBuilder: () => MongoQueryBuilder;\r\n\r\n /**\r\n * Track group field names for automatic _id renaming.\r\n * Maps pipeline index to field names.\r\n */\r\n private readonly groupFieldNames = new Map<number, string | string[]>();\r\n\r\n /**\r\n * Track `countDistinct` aggregate aliases per group stage. The renaming\r\n * `$project` finalizes these with `{ $size: \"$alias\" }` over the set built\r\n * by `$addToSet` in the `$group` stage.\r\n * Maps pipeline index to the set of aliases needing `$size` finalization.\r\n */\r\n private readonly countDistinctAliases = new Map<number, Set<string>>();\r\n\r\n /**\r\n * Create a new MongoDB query parser.\r\n *\r\n * @param options - Configuration options for the parser\r\n */\r\n public constructor(options: MongoQueryParserOptions) {\r\n this.collection = options.collection;\r\n this.operations = options.operations;\r\n this.createSubBuilder = options.createSubBuilder;\r\n }\r\n\r\n /**\r\n * Parse the operations into a MongoDB aggregation pipeline.\r\n *\r\n * This method intelligently groups mergeable operations (e.g., multiple where\r\n * clauses) into single pipeline stages while maintaining the correct execution\r\n * order for non-mergeable operations.\r\n *\r\n * @returns The MongoDB aggregation pipeline\r\n *\r\n * @example\r\n * ```typescript\r\n * const parser = new MongoQueryParser({ collection, operations });\r\n * const pipeline = parser.parse();\r\n * // [\r\n * // { $match: { status: 'active', age: { $gt: 18 } } },\r\n * // { $sort: { createdAt: -1 } },\r\n * // { $limit: 10 }\r\n * // ]\r\n * ```\r\n */\r\n public parse(): any[] {\r\n const pipeline: any[] = [];\r\n let currentStage: PipelineStage | null = null;\r\n let currentBuffer: Operation[] = [];\r\n\r\n for (const op of this.orderStages(this.operations)) {\r\n if (op.mergeable && op.stage === currentStage) {\r\n // Same mergeable stage, add to buffer\r\n currentBuffer.push(op);\r\n } else {\r\n // Different stage or non-mergeable, flush buffer\r\n if (currentBuffer.length > 0) {\r\n const builtStage = this.buildStage(currentStage!, currentBuffer);\r\n if (builtStage) {\r\n const stageIndex = pipeline.length;\r\n pipeline.push(builtStage);\r\n // Track field names for group stages with aggregates\r\n this.trackGroupFieldNames(currentStage!, currentBuffer, stageIndex);\r\n }\r\n currentBuffer = [];\r\n }\r\n\r\n if (op.mergeable) {\r\n // Start new buffer\r\n currentStage = op.stage;\r\n currentBuffer.push(op);\r\n } else {\r\n // Non-mergeable, add directly\r\n const builtStage = this.buildStage(op.stage, [op]);\r\n if (builtStage) {\r\n const stageIndex = pipeline.length;\r\n pipeline.push(builtStage);\r\n // Track field names for group stages with aggregates\r\n this.trackGroupFieldNames(op.stage, [op], stageIndex);\r\n }\r\n currentStage = null;\r\n }\r\n }\r\n }\r\n\r\n // Flush remaining buffer\r\n if (currentBuffer.length > 0) {\r\n const builtStage = this.buildStage(currentStage!, currentBuffer);\r\n if (builtStage) {\r\n const stageIndex = pipeline.length;\r\n pipeline.push(builtStage);\r\n // Track field names for group stages with aggregates\r\n this.trackGroupFieldNames(currentStage!, currentBuffer, stageIndex);\r\n }\r\n }\r\n\r\n // Post-process: Rename _id to actual field names after $group stages with aggregates\r\n return this.postProcessGroupStages(pipeline);\r\n }\r\n\r\n /**\r\n * Reorder operations so filters run before projections, mirroring SQL\r\n * semantics: in `select(...).where(...)`, the WHERE always applies to the\r\n * source columns regardless of call order. Without this, a `$project` that\r\n * strips the filter column would run before the `$match` and silently drop\r\n * every document (`select([\"a\"]).where(\"b\", x)` → `[]`).\r\n *\r\n * Only *mergeable* `$match` operations are hoisted, and only within a\r\n * segment of neighboring mergeable `$match` / `$project` / `$sort`\r\n * operations. Any other operation — `$group`, `$lookup`, `$limit`, `$skip`,\r\n * `$setWindowFields`, or a non-mergeable op (raw escapes, having-style\r\n * post-group matches, `$sample`) — is a barrier: nothing moves across it.\r\n * So `groupBy(...).where(...)` still filters AFTER the group, and\r\n * `limit(...)` / `random()` keep their call-order meaning.\r\n */\r\n private orderStages(operations: Operation[]): Operation[] {\r\n const reordered: Operation[] = [];\r\n let segment: Operation[] = [];\r\n\r\n const flushSegment = (): void => {\r\n if (segment.length === 0) {\r\n return;\r\n }\r\n reordered.push(...segment.filter((op) => op.stage === \"$match\"));\r\n reordered.push(...segment.filter((op) => op.stage !== \"$match\"));\r\n segment = [];\r\n };\r\n\r\n for (const op of operations) {\r\n const isReorderable =\r\n op.mergeable &&\r\n (op.stage === \"$match\" || op.stage === \"$project\" || op.stage === \"$sort\");\r\n\r\n if (isReorderable) {\r\n segment.push(op);\r\n } else {\r\n flushSegment();\r\n reordered.push(op);\r\n }\r\n }\r\n\r\n flushSegment();\r\n return reordered;\r\n }\r\n\r\n /**\r\n * Track field names for group stages that need _id renaming.\r\n */\r\n private trackGroupFieldNames(\r\n stage: PipelineStage,\r\n operations: Operation[],\r\n stageIndex: number,\r\n ): void {\r\n if (stage === \"$group\") {\r\n const op = operations[0];\r\n if (op.type === \"groupByWithAggregates\" && op.data.fields) {\r\n const fieldNames = this.extractGroupFieldNames(op.data.fields);\r\n if (fieldNames) {\r\n this.groupFieldNames.set(stageIndex, fieldNames);\r\n }\r\n\r\n this.trackCountDistinctAliases(stageIndex, op.data.aggregates);\r\n } else if (op.type === \"groupByDate\") {\r\n // The `$dateTrunc` bucket becomes `_id`; rename it back to the column.\r\n this.groupFieldNames.set(stageIndex, op.data.column as string);\r\n this.trackCountDistinctAliases(stageIndex, op.data.aggregates ?? {});\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Record which aggregate aliases in a group stage are `countDistinct` so the\r\n * renaming `$project` can finalize them with `{ $size: \"$alias\" }` over the\r\n * set built by `$addToSet` (the standard distinct-count-per-group pattern).\r\n */\r\n private trackCountDistinctAliases(\r\n stageIndex: number,\r\n aggregates: Record<string, RawExpression>,\r\n ): void {\r\n const distinctAliases = new Set<string>();\r\n for (const [alias, expression] of Object.entries(aggregates)) {\r\n if (isAggregateExpression(expression) && expression.__agg === \"countDistinct\") {\r\n distinctAliases.add(alias);\r\n }\r\n }\r\n\r\n if (distinctAliases.size > 0) {\r\n this.countDistinctAliases.set(stageIndex, distinctAliases);\r\n }\r\n }\r\n\r\n /**\r\n * Post-process pipeline to rename _id fields after $group stages.\r\n *\r\n * This automatically renames MongoDB's `_id` field to the actual field name(s)\r\n * used for grouping, making the results more intuitive.\r\n *\r\n * @param pipeline - The aggregation pipeline\r\n * @returns The processed pipeline\r\n */\r\n private postProcessGroupStages(pipeline: any[]): any[] {\r\n const processed: any[] = [];\r\n\r\n for (let i = 0; i < pipeline.length; i++) {\r\n const stage = pipeline[i];\r\n\r\n // Check if this is a $group stage that needs _id renaming\r\n if (stage.$group && this.groupFieldNames.has(i)) {\r\n const fieldNames = this.groupFieldNames.get(i)!;\r\n\r\n // Add the $group stage\r\n processed.push(stage);\r\n\r\n // Add a $project stage to rename _id\r\n const projection: Record<string, unknown> = {};\r\n\r\n if (typeof fieldNames === \"string\") {\r\n // Single field: rename _id to field name\r\n projection[fieldNames] = \"$_id\";\r\n } else if (Array.isArray(fieldNames) && fieldNames.length > 0) {\r\n // Multiple fields: _id is an object, spread it\r\n for (const fieldName of fieldNames) {\r\n projection[fieldName] = `$_id.${fieldName}`;\r\n }\r\n }\r\n\r\n // Include all aggregate fields. countDistinct aliases are finalized\r\n // with `{ $size: \"$alias\" }` over the set built by `$addToSet`; every\r\n // other aggregate is projected through as-is.\r\n const distinctAliases = this.countDistinctAliases.get(i);\r\n const aggregateFields = Object.keys(stage.$group).filter((key) => key !== \"_id\");\r\n for (const field of aggregateFields) {\r\n if (distinctAliases?.has(field)) {\r\n projection[field] = { $size: `$${field}` };\r\n } else {\r\n projection[field] = 1;\r\n }\r\n }\r\n\r\n if (Object.keys(projection).length > 0) {\r\n // now unselect the _id field\r\n projection._id = 0;\r\n processed.push({ $project: projection });\r\n }\r\n } else {\r\n // Regular stage, add as-is\r\n processed.push(stage);\r\n }\r\n }\r\n\r\n return processed;\r\n }\r\n\r\n /**\r\n * Convert the parsed pipeline to a pretty-printed string for debugging.\r\n *\r\n * This method formats the MongoDB aggregation pipeline in a human-readable\r\n * way, making it easier to understand and debug complex queries.\r\n *\r\n * @returns A formatted string representation of the pipeline\r\n *\r\n * @example\r\n * ```typescript\r\n * const parser = new MongoQueryParser({ collection, operations });\r\n * console.log(parser.toPrettyString());\r\n * // Output:\r\n * // MongoDB Aggregation Pipeline:\r\n * // ════════════════════════════\r\n * // Stage 1: $match\r\n * // status: \"active\"\r\n * // age: { $gt: 18 }\r\n * //\r\n * // Stage 2: $sort\r\n * // createdAt: -1\r\n * ```\r\n */\r\n public toPrettyString(): string {\r\n const pipeline = this.parse();\r\n\r\n if (pipeline.length === 0) {\r\n return \"MongoDB Aggregation Pipeline: (empty)\";\r\n }\r\n\r\n let output = \"MongoDB Aggregation Pipeline:\\n\";\r\n output += \"═\".repeat(50) + \"\\n\";\r\n\r\n pipeline.forEach((stage, index) => {\r\n const stageName = Object.keys(stage)[0];\r\n const stageData = stage[stageName];\r\n\r\n if (index > 0) {\r\n output += \"\\n\";\r\n }\r\n\r\n output += `Stage ${index + 1}: ${colors.redBright(stageName)}\\n`;\r\n output += this.formatStageData(stageData, 2);\r\n });\r\n\r\n return output;\r\n }\r\n\r\n /**\r\n * Format stage data with proper indentation.\r\n *\r\n * @param data - The stage data to format\r\n * @param indent - The indentation level\r\n * @returns Formatted string\r\n */\r\n private formatStageData(data: any, indent: number = 0): string {\r\n const spaces = \" \".repeat(indent);\r\n\r\n if (typeof data !== \"object\" || data === null) {\r\n return `${spaces}${JSON.stringify(data)}\\n`;\r\n }\r\n\r\n if (Array.isArray(data)) {\r\n if (data.length === 0) return `${spaces}[]`;\r\n\r\n let result = \"\";\r\n data.forEach((item, index) => {\r\n result += `${spaces}[${colors.magenta(index)}]:\\n`;\r\n result += this.formatStageData(item, indent + 2);\r\n });\r\n return result;\r\n }\r\n\r\n let result = \"\";\r\n Object.entries(data).forEach(([key, value]) => {\r\n const isOperator = key.startsWith(\"$\");\r\n const coloredKey = isOperator ? colors.magentaBright(key) : colors.blue(key);\r\n\r\n if (typeof value === \"object\" && value !== null && !Array.isArray(value)) {\r\n result += `${spaces}${coloredKey}:\\n`;\r\n result += this.formatStageData(value, indent + 2);\r\n } else if (Array.isArray(value)) {\r\n result += `${spaces}${coloredKey}:\\n`;\r\n result += this.formatStageData(value, indent + 2);\r\n } else {\r\n const formattedValue =\r\n typeof value === \"number\"\r\n ? colors.yellowBright(value)\r\n : typeof value === \"boolean\"\r\n ? colors.cyanBright(value.toString())\r\n : typeof value === \"string\"\r\n ? colors.greenBright(JSON.stringify(value))\r\n : colors.greenBright(String(value));\r\n result += `${spaces}${coloredKey}: ${formattedValue}\\n`;\r\n }\r\n });\r\n\r\n return result.endsWith(\"\\n\") ? result : `${result}\\n`;\r\n }\r\n\r\n /**\r\n * Build a single pipeline stage from a group of operations.\r\n *\r\n * @param stage - The pipeline stage type\r\n * @param operations - The operations to build the stage from\r\n * @returns The built pipeline stage or null if no stage should be added\r\n */\r\n private buildStage(stage: PipelineStage, operations: Operation[]): any {\r\n switch (stage) {\r\n case \"$match\":\r\n return this.buildMatchStage(operations);\r\n case \"$project\":\r\n return this.buildProjectStage(operations);\r\n case \"$sort\":\r\n return this.buildSortStage(operations);\r\n case \"$group\":\r\n return this.buildGroupStage(operations);\r\n case \"$lookup\":\r\n return this.buildLookupStage(operations);\r\n case \"$limit\":\r\n return { $limit: operations[0].data.value };\r\n case \"$skip\":\r\n return { $skip: operations[0].data.value };\r\n case \"$setWindowFields\":\r\n return {\r\n $setWindowFields: operations[0].data.spec,\r\n };\r\n default:\r\n return null;\r\n }\r\n }\r\n\r\n /**\r\n * Build a $match stage from where operations.\r\n *\r\n * Query building strategy:\r\n * - Top-level where() + orWhere() = Pure OR\r\n * - Use callbacks for AND + OR grouping\r\n *\r\n * @param operations - The where operations\r\n * @returns The $match stage or null\r\n */\r\n private buildMatchStage(operations: Operation[]): any {\r\n const andFilter: Record<string, any> = {};\r\n const orClauses: any[] = [];\r\n const pendingSimpleWhere: any[] = [];\r\n let topLevelOrMode = false;\r\n\r\n const pushOr = (clause: any): void => {\r\n if (!clause) {\r\n return;\r\n }\r\n\r\n if (this.isPureOrCondition(clause)) {\r\n orClauses.push(...clause.$or);\r\n return;\r\n }\r\n\r\n if (Array.isArray(clause)) {\r\n orClauses.push(...clause);\r\n return;\r\n }\r\n\r\n orClauses.push(clause);\r\n };\r\n\r\n const mergeAnd = (condition: any): void => {\r\n if (!condition) {\r\n return;\r\n }\r\n\r\n Object.entries(condition).forEach(([key, value]) => {\r\n if (key === \"$or\") {\r\n pushOr(value);\r\n return;\r\n }\r\n\r\n if (\r\n value &&\r\n typeof value === \"object\" &&\r\n !Array.isArray(value) &&\r\n andFilter[key] &&\r\n typeof andFilter[key] === \"object\" &&\r\n !Array.isArray(andFilter[key])\r\n ) {\r\n andFilter[key] = { ...andFilter[key], ...value };\r\n } else {\r\n andFilter[key] = value;\r\n }\r\n });\r\n };\r\n\r\n const queueSimpleWhere = (condition: any): void => {\r\n if (!condition) {\r\n return;\r\n }\r\n if (topLevelOrMode) {\r\n pushOr(condition);\r\n } else {\r\n pendingSimpleWhere.push(condition);\r\n }\r\n };\r\n\r\n const enterTopLevelOrMode = (): void => {\r\n if (topLevelOrMode) {\r\n return;\r\n }\r\n topLevelOrMode = true;\r\n while (pendingSimpleWhere.length > 0) {\r\n const condition = pendingSimpleWhere.shift();\r\n if (condition) {\r\n pushOr(condition);\r\n }\r\n }\r\n };\r\n\r\n const flushPendingSimpleWhere = (): void => {\r\n if (pendingSimpleWhere.length === 0) {\r\n return;\r\n }\r\n if (topLevelOrMode) {\r\n pendingSimpleWhere.forEach(pushOr);\r\n } else {\r\n pendingSimpleWhere.forEach(mergeAnd);\r\n }\r\n pendingSimpleWhere.length = 0;\r\n };\r\n\r\n for (const op of operations) {\r\n if (op.type === \"where:callback\" || op.type === \"orWhere:callback\") {\r\n flushPendingSimpleWhere();\r\n const callbackCondition = this.buildCallbackCondition(op.data);\r\n if (!callbackCondition) {\r\n continue;\r\n }\r\n\r\n const treatAsOr =\r\n op.type === \"orWhere:callback\" ||\r\n (topLevelOrMode && !this.isPureOrCondition(callbackCondition)) ||\r\n this.isPureOrCondition(callbackCondition);\r\n\r\n if (treatAsOr) {\r\n if (op.type === \"orWhere:callback\") {\r\n enterTopLevelOrMode();\r\n }\r\n pushOr(callbackCondition);\r\n } else {\r\n mergeAnd(callbackCondition);\r\n }\r\n continue;\r\n }\r\n\r\n if (op.type === \"where:object\") {\r\n queueSimpleWhere(op.data);\r\n continue;\r\n }\r\n\r\n if (\r\n op.type === \"where:not\" ||\r\n op.type === \"orWhere:not\" ||\r\n op.type === \"where:exists\" ||\r\n op.type === \"where:notExists\"\r\n ) {\r\n const negated = op.type === \"where:not\" || op.type === \"where:notExists\";\r\n const nested = this.buildCallbackCondition(op.data.callback);\r\n if (nested) {\r\n const condition = negated ? { $nor: [nested] } : nested;\r\n if (op.type.startsWith(\"orWhere\")) {\r\n enterTopLevelOrMode();\r\n pushOr(condition);\r\n } else {\r\n queueSimpleWhere(condition);\r\n }\r\n }\r\n continue;\r\n }\r\n\r\n if (op.type === \"orWhere:object\") {\r\n enterTopLevelOrMode();\r\n pushOr(op.data);\r\n continue;\r\n }\r\n\r\n const condition = this.buildWhereCondition(op);\r\n if (!condition) {\r\n continue;\r\n }\r\n\r\n if (op.type.startsWith(\"orWhere\")) {\r\n enterTopLevelOrMode();\r\n pushOr(condition);\r\n } else {\r\n queueSimpleWhere(condition);\r\n }\r\n }\r\n\r\n flushPendingSimpleWhere();\r\n\r\n const hasAnd = Object.keys(andFilter).length > 0;\r\n const hasOr = orClauses.length > 0;\r\n\r\n if (!hasAnd && !hasOr) {\r\n return null;\r\n }\r\n\r\n const match: any = {};\r\n if (hasAnd) {\r\n Object.assign(match, andFilter);\r\n }\r\n if (hasOr) {\r\n match.$or = orClauses;\r\n }\r\n\r\n return { $match: match };\r\n }\r\n\r\n private isPureOrCondition(condition: any): condition is { $or: any[] } {\r\n return (\r\n condition &&\r\n typeof condition === \"object\" &&\r\n !Array.isArray(condition) &&\r\n Object.keys(condition).length === 1 &&\r\n Array.isArray((condition as any).$or)\r\n );\r\n }\r\n\r\n /**\r\n * Build a condition from a callback-based where clause.\r\n * Creates a sub-builder, executes the callback, and extracts the conditions.\r\n * If callback has orWhere, all conditions become OR.\r\n *\r\n * @param callback - The callback function\r\n * @returns The built condition or null\r\n */\r\n private buildCallbackCondition(callback: any): any {\r\n // Create a temporary sub-builder\r\n const subBuilder = this.createSubBuilder();\r\n\r\n // Execute the callback with the sub-builder\r\n callback(subBuilder);\r\n\r\n // Extract only match operations from the sub-builder\r\n const matchOps = subBuilder.operations.filter((op: Operation) => op.stage === \"$match\");\r\n\r\n if (matchOps.length === 0) {\r\n return null;\r\n }\r\n\r\n const andFilter: Record<string, any> = {};\r\n const orClauses: any[] = [];\r\n const hasInternalOr = matchOps.some((op) => op.type.startsWith(\"orWhere\"));\r\n\r\n const pushOr = (clause: any): void => {\r\n if (!clause) {\r\n return;\r\n }\r\n if (this.isPureOrCondition(clause)) {\r\n orClauses.push(...clause.$or);\r\n return;\r\n }\r\n orClauses.push(clause);\r\n };\r\n\r\n if (hasInternalOr) {\r\n for (const op of matchOps) {\r\n if (op.type === \"where:callback\" || op.type === \"orWhere:callback\") {\r\n const nestedCondition = this.buildCallbackCondition(op.data);\r\n if (nestedCondition) {\r\n pushOr(nestedCondition);\r\n }\r\n continue;\r\n }\r\n\r\n if (op.type === \"where:object\" || op.type === \"orWhere:object\") {\r\n pushOr(op.data);\r\n continue;\r\n }\r\n\r\n const condition = this.buildWhereCondition(op);\r\n if (condition) {\r\n pushOr(condition);\r\n }\r\n }\r\n\r\n return orClauses.length > 0 ? { $or: orClauses } : null;\r\n }\r\n\r\n for (const op of matchOps) {\r\n if (op.type === \"where:callback\") {\r\n const nestedCondition = this.buildCallbackCondition(op.data);\r\n if (nestedCondition) {\r\n Object.assign(andFilter, nestedCondition);\r\n }\r\n } else if (op.type === \"where:object\") {\r\n Object.assign(andFilter, op.data);\r\n } else {\r\n const condition = this.buildWhereCondition(op);\r\n if (condition) {\r\n Object.assign(andFilter, condition);\r\n }\r\n }\r\n }\r\n\r\n return Object.keys(andFilter).length > 0 ? andFilter : null;\r\n }\r\n\r\n /**\r\n * Build a MongoDB filter condition from a where operation.\r\n *\r\n * @param op - The operation to build\r\n * @returns The MongoDB filter condition\r\n */\r\n private buildWhereCondition(op: Operation): any {\r\n const { field, operator, value } = op.data;\r\n\r\n switch (op.type) {\r\n case \"where\":\r\n case \"orWhere\":\r\n return this.buildOperatorCondition(field, operator, value);\r\n\r\n case \"whereIn\":\r\n return { [field]: { $in: value || op.data.values } };\r\n\r\n case \"whereNotIn\":\r\n return { [field]: { $nin: value || op.data.values } };\r\n\r\n case \"whereNull\":\r\n return { [field]: null };\r\n\r\n case \"whereNotNull\":\r\n return { [field]: { $ne: null } };\r\n\r\n case \"whereBetween\":\r\n return {\r\n [field]: {\r\n $gte: op.data.range[0],\r\n $lte: op.data.range[1],\r\n },\r\n };\r\n\r\n case \"whereNotBetween\":\r\n return {\r\n [field]: {\r\n $not: {\r\n $gte: op.data.range[0],\r\n $lte: op.data.range[1],\r\n },\r\n },\r\n };\r\n\r\n case \"whereLike\":\r\n return {\r\n [field]: { $regex: this.buildLikeRegexSource(op.data), $options: \"i\" },\r\n };\r\n\r\n case \"whereNotLike\":\r\n return {\r\n [field]: {\r\n $not: { $regex: this.buildLikeRegexSource(op.data), $options: \"i\" },\r\n },\r\n };\r\n\r\n case \"whereStartsWith\":\r\n return {\r\n [field]: { $regex: `^${escapeRegex(String(op.data.value))}`, $options: \"i\" },\r\n };\r\n\r\n case \"whereNotStartsWith\":\r\n return {\r\n [field]: {\r\n $not: { $regex: `^${escapeRegex(String(op.data.value))}`, $options: \"i\" },\r\n },\r\n };\r\n\r\n case \"whereEndsWith\":\r\n return {\r\n [field]: { $regex: `${escapeRegex(String(op.data.value))}$`, $options: \"i\" },\r\n };\r\n\r\n case \"whereNotEndsWith\":\r\n return {\r\n [field]: {\r\n $not: { $regex: `${escapeRegex(String(op.data.value))}$`, $options: \"i\" },\r\n },\r\n };\r\n\r\n case \"whereExists\":\r\n return { [field]: { $exists: true } };\r\n\r\n case \"whereNotExists\":\r\n return { [field]: { $exists: false } };\r\n\r\n case \"whereSize\":\r\n if (op.data.operator === \"=\") {\r\n return { [field]: { $size: op.data.size } };\r\n } else {\r\n const mongoOp = this.getMongoOperator(op.data.operator);\r\n return {\r\n $expr: {\r\n [mongoOp]: [{ $size: `$${field}` }, op.data.size],\r\n },\r\n };\r\n }\r\n\r\n case \"textSearch\":\r\n return {\r\n $text: { $search: op.data.query },\r\n ...(op.data.filters || {}),\r\n };\r\n\r\n case \"whereRaw\":\r\n case \"orWhereRaw\":\r\n return this.resolveRawExpression(\r\n op.data.expression as RawExpression,\r\n op.data.bindings,\r\n );\r\n\r\n case \"whereColumn\":\r\n case \"orWhereColumn\":\r\n return this.buildColumnComparison(op.data.first, op.data.operator, op.data.second);\r\n\r\n case \"whereBetweenColumns\":\r\n return this.buildBetweenColumnsCondition(\r\n op.data.field,\r\n op.data.lowerColumn,\r\n op.data.upperColumn,\r\n );\r\n\r\n case \"whereDate\":\r\n case \"whereDateEquals\":\r\n return this.buildDateEqualityCondition(op.data.field, op.data.value);\r\n\r\n case \"whereDateBefore\":\r\n return this.buildDateBeforeCondition(op.data.field, op.data.value);\r\n\r\n case \"whereDateAfter\":\r\n return this.buildDateAfterCondition(op.data.field, op.data.value);\r\n\r\n case \"whereTime\":\r\n return this.buildTimeCondition(op.data.field, op.data.value);\r\n\r\n case \"whereDay\":\r\n return this.buildDatePartCondition(op.data.field, \"$dayOfMonth\", op.data.value);\r\n\r\n case \"whereMonth\":\r\n return this.buildDatePartCondition(op.data.field, \"$month\", op.data.value);\r\n\r\n case \"whereYear\":\r\n return this.buildDatePartCondition(op.data.field, \"$year\", op.data.value);\r\n\r\n case \"whereJsonContains\":\r\n return this.buildJsonContainsCondition(op.data.path, op.data.value);\r\n\r\n case \"whereJsonDoesntContain\":\r\n return this.buildJsonDoesntContainCondition(op.data.path, op.data.value);\r\n\r\n case \"whereJsonContainsKey\":\r\n return this.buildJsonContainsKeyCondition(op.data.path);\r\n\r\n case \"whereJsonLength\":\r\n return this.buildJsonLengthCondition(\r\n op.data.path,\r\n op.data.operator,\r\n op.data.value,\r\n );\r\n\r\n case \"whereJsonIsArray\":\r\n return this.buildJsonTypeCondition(op.data.path, \"array\");\r\n\r\n case \"whereJsonIsObject\":\r\n return this.buildJsonTypeCondition(op.data.path, \"object\");\r\n\r\n case \"whereArrayLength\":\r\n return this.buildArrayLengthCondition(\r\n op.data.field,\r\n op.data.operator,\r\n op.data.value,\r\n );\r\n\r\n case \"whereFullText\":\r\n case \"orWhereFullText\":\r\n return { $text: { $search: op.data.query } };\r\n\r\n case \"whereSearch\":\r\n return {\r\n [op.data.field]: {\r\n // Search text is the most user-facing input in the whole API —\r\n // always a literal, never a pattern.\r\n $regex: escapeRegex(String(op.data.query)),\r\n $options: \"i\",\r\n },\r\n };\r\n\r\n case \"where:not\":\r\n case \"orWhere:not\": {\r\n const nestedNot = this.buildCallbackCondition(op.data.callback);\r\n return nestedNot ? { $nor: [nestedNot] } : null;\r\n }\r\n\r\n case \"where:exists\":\r\n return this.buildCallbackCondition(op.data.callback);\r\n\r\n case \"where:notExists\": {\r\n const nestedExists = this.buildCallbackCondition(op.data.callback);\r\n return nestedExists ? { $nor: [nestedExists] } : null;\r\n }\r\n\r\n case \"whereArrayContains\":\r\n if (op.data.key) {\r\n return {\r\n [field]: {\r\n $elemMatch: { [op.data.key]: op.data.value },\r\n },\r\n };\r\n } else {\r\n return { [field]: op.data.value };\r\n }\r\n\r\n default:\r\n return null;\r\n }\r\n }\r\n\r\n /**\r\n * Resolve the `$regex` source for a whereLike/whereNotLike operation.\r\n *\r\n * A `RegExp` argument is developer-authored and used verbatim; a string is\r\n * treated as a literal LIKE pattern (`%` aside) so that search input cannot\r\n * inject regex operators or a catastrophically backtracking pattern. The base\r\n * (driver-agnostic) query builder flattens a `RegExp` to its source string and\r\n * flags it with `isRegExp`, which is honored here for the same reason.\r\n *\r\n * @param data - The operation data (`{ pattern, isRegExp? }`)\r\n * @returns Regex source for `$regex`\r\n */\r\n private buildLikeRegexSource(data: any): string {\r\n if (data.isRegExp === true && typeof data.pattern === \"string\") {\r\n return data.pattern;\r\n }\r\n\r\n return resolveLikePattern(data.pattern);\r\n }\r\n\r\n /**\r\n * Build a condition based on the operator.\r\n *\r\n * @param field - The field name\r\n * @param operator - The comparison operator\r\n * @param value - The value to compare\r\n * @returns The MongoDB filter condition\r\n */\r\n private buildOperatorCondition(field: string, operator: string, value: unknown): any {\r\n switch (operator) {\r\n case \"=\":\r\n return { [field]: value };\r\n case \"!=\":\r\n return { [field]: { $ne: value } };\r\n case \">\":\r\n return { [field]: { $gt: value } };\r\n case \">=\":\r\n return { [field]: { $gte: value } };\r\n case \"<\":\r\n return { [field]: { $lt: value } };\r\n case \"<=\":\r\n return { [field]: { $lte: value } };\r\n default:\r\n return { [field]: value };\r\n }\r\n }\r\n\r\n /**\r\n * Get MongoDB operator from comparison operator.\r\n *\r\n * @param operator - The comparison operator\r\n * @returns The MongoDB operator\r\n */\r\n private getMongoOperator(operator: string): string {\r\n const map: Record<string, string> = {\r\n \"=\": \"$eq\",\r\n \"!=\": \"$ne\",\r\n \">\": \"$gt\",\r\n \">=\": \"$gte\",\r\n \"<\": \"$lt\",\r\n \"<=\": \"$lte\",\r\n };\r\n return map[operator] || \"$eq\";\r\n }\r\n\r\n private resolveRawExpression(expression: RawExpression, _bindings?: unknown[]): any {\r\n if (typeof expression === \"string\") {\r\n // String expressions used to compile to { $where: \"<js>\" } — a\r\n // server-side JavaScript execution sink inside mongod (and an unindexed\r\n // full-scan even when trusted). Refuse them outright.\r\n throw new UnsafeRawExpressionError(\r\n `whereRaw()/orWhereRaw() string expressions are not supported on the MongoDB driver: ` +\r\n `they would compile to a {$where: \"<js>\"} filter, which executes JavaScript on the database server. ` +\r\n `Use the object form instead, e.g. whereRaw({ $expr: { $gt: [\"$stock\", \"$reserved\"] } }).`,\r\n );\r\n }\r\n\r\n if (typeof expression === \"object\" && expression !== null) {\r\n this.assertNoJsExecutionOperators(expression);\r\n return expression;\r\n }\r\n\r\n return null;\r\n }\r\n\r\n /**\r\n * The object form of whereRaw() is forwarded to MongoDB verbatim, so it must\r\n * not smuggle in the operators that execute JavaScript inside mongod\r\n * ($where, and $function/$accumulator which can nest under $expr).\r\n * Aggregation operators like $expr/$gt remain valid — that is the object\r\n * form's purpose.\r\n */\r\n private assertNoJsExecutionOperators(value: unknown): void {\r\n if (Array.isArray(value)) {\r\n for (const item of value) {\r\n this.assertNoJsExecutionOperators(item);\r\n }\r\n return;\r\n }\r\n\r\n if (typeof value !== \"object\" || value === null) return;\r\n\r\n for (const [key, nested] of Object.entries(value as Record<string, unknown>)) {\r\n if (key === \"$where\" || key === \"$function\" || key === \"$accumulator\") {\r\n throw new UnsafeRawExpressionError(\r\n `whereRaw()/orWhereRaw() expressions must not contain \"${key}\": ` +\r\n `it executes JavaScript on the database server. ` +\r\n `Express the condition with query operators instead, e.g. whereRaw({ $expr: { $gt: [\"$stock\", \"$reserved\"] } }).`,\r\n );\r\n }\r\n this.assertNoJsExecutionOperators(nested);\r\n }\r\n }\r\n\r\n private bindRawString(expression: string, bindings?: unknown[]): string {\r\n if (!bindings || bindings.length === 0) {\r\n return expression;\r\n }\r\n\r\n let index = 0;\r\n return expression.replace(/\\?/g, () => {\r\n const value = bindings[index++];\r\n return value === undefined ? \"?\" : JSON.stringify(value);\r\n });\r\n }\r\n\r\n private buildColumnComparison(first: string, operator: WhereOperator, second: string): any {\r\n const mongoOperator = this.getMongoOperator(operator);\r\n return {\r\n $expr: {\r\n [mongoOperator]: [this.wrapColumn(first), this.wrapColumn(second)],\r\n },\r\n };\r\n }\r\n\r\n private buildBetweenColumnsCondition(field: string, lower: string, upper: string): any {\r\n return {\r\n $expr: {\r\n $and: [\r\n { $gte: [this.wrapColumn(field), this.wrapColumn(lower)] },\r\n { $lte: [this.wrapColumn(field), this.wrapColumn(upper)] },\r\n ],\r\n },\r\n };\r\n }\r\n\r\n private wrapColumn(column: string): string {\r\n return column.startsWith(\"$\") ? column : `$${column}`;\r\n }\r\n\r\n private buildDateEqualityCondition(field: string, value: Date | string): any {\r\n const target = this.normalizeDateInput(value);\r\n const start = this.startOfDay(target);\r\n const end = this.endOfDay(target);\r\n return { [field]: { $gte: start, $lte: end } };\r\n }\r\n\r\n private buildDateBeforeCondition(field: string, value: Date | string): any {\r\n const target = this.startOfDay(this.normalizeDateInput(value));\r\n return { [field]: { $lt: target } };\r\n }\r\n\r\n private buildDateAfterCondition(field: string, value: Date | string): any {\r\n const target = this.endOfDay(this.normalizeDateInput(value));\r\n return { [field]: { $gt: target } };\r\n }\r\n\r\n private buildTimeCondition(field: string, value: string): any {\r\n return {\r\n $expr: {\r\n $eq: [\r\n {\r\n $dateToString: {\r\n format: \"%H:%M\",\r\n date: `$${field}`,\r\n },\r\n },\r\n value,\r\n ],\r\n },\r\n };\r\n }\r\n\r\n private buildDatePartCondition(\r\n field: string,\r\n operator: \"$dayOfMonth\" | \"$month\" | \"$year\",\r\n value: number,\r\n ): any {\r\n return {\r\n $expr: {\r\n $eq: [\r\n {\r\n [operator]: `$${field}`,\r\n },\r\n value,\r\n ],\r\n },\r\n };\r\n }\r\n\r\n private buildJsonContainsCondition(path: string, value: unknown): any {\r\n const fieldPath = this.normalizePath(path);\r\n if (Array.isArray(value)) {\r\n return { [fieldPath]: { $all: value } };\r\n }\r\n return { [fieldPath]: value };\r\n }\r\n\r\n private buildJsonDoesntContainCondition(path: string, value: unknown): any {\r\n const fieldPath = this.normalizePath(path);\r\n const values = Array.isArray(value) ? value : [value];\r\n return { [fieldPath]: { $nin: values } };\r\n }\r\n\r\n private buildJsonContainsKeyCondition(path: string): any {\r\n return {\r\n [this.normalizePath(path)]: { $exists: true },\r\n };\r\n }\r\n\r\n private buildJsonLengthCondition(path: string, operator: WhereOperator, value: number): any {\r\n const mongoOperator = this.getMongoOperator(operator);\r\n return {\r\n $expr: {\r\n [mongoOperator]: [{ $size: { $ifNull: [`$${this.normalizePath(path)}`, []] } }, value],\r\n },\r\n };\r\n }\r\n\r\n private buildJsonTypeCondition(path: string, type: string): any {\r\n return {\r\n $expr: {\r\n $eq: [{ $type: `$${this.normalizePath(path)}` }, type],\r\n },\r\n };\r\n }\r\n\r\n private buildArrayLengthCondition(field: string, operator: WhereOperator, value: number): any {\r\n const mongoOperator = this.getMongoOperator(operator);\r\n return {\r\n $expr: {\r\n [mongoOperator]: [{ $size: { $ifNull: [`$${field}`, []] } }, value],\r\n },\r\n };\r\n }\r\n\r\n private normalizeDateInput(value: Date | string): Date {\r\n if (value instanceof Date) {\r\n return value;\r\n }\r\n const parsed = new Date(value);\r\n if (Number.isNaN(parsed.getTime())) {\r\n throw new Error(`Invalid date value: ${value}`);\r\n }\r\n return parsed;\r\n }\r\n\r\n private startOfDay(date: Date): Date {\r\n const copy = new Date(date);\r\n copy.setHours(0, 0, 0, 0);\r\n return copy;\r\n }\r\n\r\n private endOfDay(date: Date): Date {\r\n const copy = new Date(date);\r\n copy.setHours(23, 59, 59, 999);\r\n return copy;\r\n }\r\n\r\n private normalizePath(path: string): string {\r\n return path.replace(/->/g, \".\");\r\n }\r\n\r\n private applyProjectionFields(\r\n projection: Record<string, unknown>,\r\n fields: string[],\r\n value: 0 | 1,\r\n ): void {\r\n for (const field of fields) {\r\n projection[field] = value;\r\n }\r\n }\r\n\r\n /**\r\n * Apply projection object with aliases and inclusion/exclusion.\r\n * @param projection - The projection object to modify\r\n * @param projectionObj - The projection specification\r\n */\r\n private applyProjectionObject(\r\n projection: Record<string, unknown>,\r\n projectionObj: Record<string, unknown>,\r\n ): void {\r\n for (const [field, value] of Object.entries(projectionObj)) {\r\n // Handle boolean values (true = 1, false = 0)\r\n if (typeof value === \"boolean\") {\r\n projection[field] = value ? 1 : 0;\r\n continue;\r\n }\r\n\r\n // Handle numeric values (0 or 1)\r\n if (typeof value === \"number\") {\r\n projection[field] = value;\r\n continue;\r\n }\r\n\r\n // Handle string values (aliases)\r\n if (typeof value === \"string\") {\r\n // Alias: project the field with a new name\r\n projection[value] = `$${field}`;\r\n continue;\r\n }\r\n\r\n // Handle complex expressions (objects)\r\n if (typeof value === \"object\" && value !== null) {\r\n projection[field] = value;\r\n continue;\r\n }\r\n\r\n // Default: include the field\r\n projection[field] = 1;\r\n }\r\n }\r\n\r\n private applyRawProjection(\r\n projection: Record<string, unknown>,\r\n expression: RawExpression,\r\n bindings?: unknown[],\r\n ): void {\r\n const resolved = this.resolveProjectionExpression(expression, bindings);\r\n if (!resolved) {\r\n return;\r\n }\r\n\r\n if (typeof resolved === \"object\" && resolved !== null && !Array.isArray(resolved)) {\r\n Object.assign(projection, resolved as Record<string, unknown>);\r\n }\r\n }\r\n\r\n private resolveProjectionExpression(\r\n expression: RawExpression | unknown,\r\n bindings?: unknown[],\r\n ): any {\r\n if (typeof expression === \"string\") {\r\n const source =\r\n bindings && expression.includes(\"?\")\r\n ? this.bindRawString(expression, bindings)\r\n : expression;\r\n if (source.startsWith(\":\")) {\r\n return source.slice(1);\r\n }\r\n return this.normalizeFieldReference(source);\r\n }\r\n\r\n if (typeof expression === \"object\" && expression !== null && !(expression instanceof Date)) {\r\n return expression;\r\n }\r\n\r\n if (typeof expression === \"number\" || typeof expression === \"boolean\") {\r\n return expression;\r\n }\r\n\r\n return expression;\r\n }\r\n\r\n private normalizeFieldReference(value: string | RawExpression): any {\r\n if (typeof value === \"string\") {\r\n if (value.startsWith(\":\")) {\r\n return value.slice(1);\r\n }\r\n // If already a field reference, return as-is\r\n if (value.startsWith(\"$\")) {\r\n return value;\r\n }\r\n // Check if it's a string literal (contains spaces or special chars)\r\n // Field paths are typically: alphanumeric, underscore, dot only\r\n if (!/^[a-zA-Z0-9_.]+$/.test(value)) {\r\n return value; // Return as literal\r\n }\r\n // Otherwise, treat as field reference\r\n return `$${value}`;\r\n }\r\n return value;\r\n }\r\n\r\n private buildAggregateProjection(field: string, aggregate: string): any {\r\n if (aggregate === \"count\") {\r\n return this.buildArraySizeExpression(field);\r\n }\r\n\r\n const map: Record<string, string> = {\r\n sum: \"$sum\",\r\n avg: \"$avg\",\r\n min: \"$min\",\r\n max: \"$max\",\r\n first: \"$first\",\r\n last: \"$last\",\r\n };\r\n\r\n const operator = map[aggregate];\r\n if (!operator) {\r\n return null;\r\n }\r\n\r\n return {\r\n [operator]: this.normalizeFieldReference(field),\r\n };\r\n }\r\n\r\n private buildExistsProjection(field: string): any {\r\n return {\r\n $ne: [{ $type: `$${field}` }, \"missing\"],\r\n };\r\n }\r\n\r\n private buildArraySizeExpression(field: string): any {\r\n return {\r\n $size: { $ifNull: [`$${field}`, []] },\r\n };\r\n }\r\n\r\n private buildCaseExpression(\r\n cases: Array<{ when: RawExpression; then: RawExpression | unknown }>,\r\n otherwise: RawExpression | unknown,\r\n ): any {\r\n return {\r\n $switch: {\r\n branches: cases.map((item) => ({\r\n case: this.resolveProjectionExpression(item.when),\r\n then: this.resolveLiteralOrExpression(item.then),\r\n })),\r\n default: this.resolveLiteralOrExpression(otherwise),\r\n },\r\n };\r\n }\r\n\r\n private buildCondExpression(\r\n condition: RawExpression,\r\n thenValue: RawExpression | unknown,\r\n elseValue: RawExpression | unknown,\r\n ): any {\r\n return {\r\n $cond: [\r\n this.resolveProjectionExpression(condition),\r\n this.resolveLiteralOrExpression(thenValue),\r\n this.resolveLiteralOrExpression(elseValue),\r\n ],\r\n };\r\n }\r\n\r\n /**\r\n * Resolve a value as a literal (if it's a plain string) or as an expression.\r\n * Used for `then`/`default` values in CASE/WHEN expressions.\r\n */\r\n private resolveLiteralOrExpression(value: RawExpression | unknown): any {\r\n // If it's a string that starts with $, treat as field reference\r\n if (typeof value === \"string\" && value.startsWith(\"$\")) {\r\n return value;\r\n }\r\n // If it's a plain string (not starting with $), treat as literal\r\n if (typeof value === \"string\") {\r\n return value;\r\n }\r\n // For objects (expressions), numbers, booleans, etc., use normal resolution\r\n return this.resolveProjectionExpression(value);\r\n }\r\n\r\n private inferJsonAlias(path: string): string {\r\n const normalized = this.normalizePath(path);\r\n const segments = normalized.split(\".\");\r\n return segments[segments.length - 1];\r\n }\r\n\r\n private buildConcatExpression(values: Array<string | RawExpression>): any {\r\n return {\r\n $concat: values.map((value) => this.normalizeFieldReference(value)),\r\n };\r\n }\r\n\r\n private buildCoalesceExpression(values: Array<string | RawExpression>): any {\r\n if (values.length === 0) {\r\n return null;\r\n }\r\n\r\n let expression = this.normalizeFieldReference(values[values.length - 1]);\r\n\r\n for (let index = values.length - 2; index >= 0; index--) {\r\n expression = {\r\n $ifNull: [this.normalizeFieldReference(values[index]), expression],\r\n };\r\n }\r\n\r\n return expression;\r\n }\r\n\r\n /**\r\n * Build a $project stage from select operations.\r\n *\r\n * @param operations - The select operations\r\n * @returns The $project stage or null\r\n */\r\n private buildProjectStage(operations: Operation[]): any {\r\n if (operations.length === 0) {\r\n return null;\r\n }\r\n\r\n const projection: Record<string, unknown> = {};\r\n const driverCallbacks: Array<(projection: Record<string, unknown>) => void> = [];\r\n\r\n for (const op of operations) {\r\n switch (op.type) {\r\n case \"select\":\r\n // Handle new projection format with aliases\r\n if (op.data.projection) {\r\n this.applyProjectionObject(projection, op.data.projection);\r\n } else if (op.data.fields) {\r\n this.applyProjectionFields(projection, op.data.fields, 1);\r\n }\r\n break;\r\n\r\n case \"deselect\":\r\n this.applyProjectionFields(projection, op.data.fields, 0);\r\n break;\r\n\r\n case \"addSelect\":\r\n this.applyProjectionFields(projection, op.data.fields, 1);\r\n break;\r\n\r\n case \"selectRaw\":\r\n this.applyRawProjection(projection, op.data.expression, op.data.bindings);\r\n break;\r\n\r\n case \"selectSub\":\r\n case \"addSelectSub\": {\r\n const expr = this.resolveProjectionExpression(op.data.expression, op.data.bindings);\r\n if (expr !== undefined) {\r\n projection[op.data.alias] = expr;\r\n }\r\n break;\r\n }\r\n\r\n case \"selectAggregate\":\r\n projection[op.data.alias] = this.buildAggregateProjection(\r\n op.data.field,\r\n op.data.aggregate,\r\n );\r\n break;\r\n\r\n case \"selectExists\":\r\n projection[op.data.alias] = this.buildExistsProjection(op.data.field);\r\n break;\r\n\r\n case \"selectCount\":\r\n projection[op.data.alias] = this.buildArraySizeExpression(op.data.field);\r\n break;\r\n\r\n case \"selectCase\":\r\n projection[op.data.alias] = this.buildCaseExpression(\r\n op.data.cases,\r\n op.data.otherwise,\r\n );\r\n break;\r\n\r\n case \"selectWhen\":\r\n projection[op.data.alias] = this.buildCondExpression(\r\n op.data.condition,\r\n op.data.thenValue,\r\n op.data.elseValue,\r\n );\r\n break;\r\n\r\n case \"selectDriverProjection\":\r\n driverCallbacks.push(op.data.callback);\r\n break;\r\n\r\n case \"selectJson\": {\r\n const alias = op.data.alias ?? this.inferJsonAlias(op.data.path);\r\n projection[alias] = this.normalizeFieldReference(\r\n `$${this.normalizePath(op.data.path)}`,\r\n );\r\n break;\r\n }\r\n\r\n case \"selectJsonRaw\": {\r\n projection[op.data.alias] = this.resolveProjectionExpression(op.data.expression);\r\n break;\r\n }\r\n\r\n case \"deselectJson\":\r\n projection[this.normalizePath(op.data.path)] = 0;\r\n break;\r\n\r\n case \"selectConcat\":\r\n projection[op.data.alias] = this.buildConcatExpression(op.data.fields);\r\n break;\r\n\r\n case \"selectCoalesce\":\r\n projection[op.data.alias] = this.buildCoalesceExpression(op.data.fields);\r\n break;\r\n\r\n default:\r\n break;\r\n }\r\n }\r\n\r\n for (const callback of driverCallbacks) {\r\n callback(projection);\r\n }\r\n\r\n return Object.keys(projection).length > 0 ? { $project: projection } : null;\r\n }\r\n\r\n /**\r\n * Build a $sort stage from order operations.\r\n *\r\n * @param operations - The order operations\r\n * @returns The $sort stage or null\r\n */\r\n private buildSortStage(operations: Operation[]): any {\r\n const sort: any = {};\r\n\r\n for (const op of operations) {\r\n switch (op.type) {\r\n case \"orderBy\":\r\n sort[op.data.field] = op.data.direction === \"asc\" ? 1 : -1;\r\n break;\r\n\r\n case \"orderByRandom\":\r\n return { $sample: { size: op.data.limit } };\r\n\r\n case \"orderByRaw\":\r\n // TODO: Handle raw expressions\r\n break;\r\n }\r\n }\r\n\r\n return Object.keys(sort).length > 0 ? { $sort: sort } : null;\r\n }\r\n\r\n /**\r\n * Build a $group stage from group operations.\r\n *\r\n * @param operations - The group operations\r\n * @returns The $group stage or null\r\n */\r\n private buildGroupStage(operations: Operation[]): any {\r\n const op = operations[0];\r\n\r\n switch (op.type) {\r\n case \"groupBy\": {\r\n const stage = this.buildGroupByStage(op.data.fields);\r\n if (stage) {\r\n return stage;\r\n }\r\n break;\r\n }\r\n case \"groupByWithAggregates\": {\r\n const stage = this.buildGroupByWithAggregatesStage(\r\n op.data.fields,\r\n op.data.aggregates,\r\n );\r\n if (stage) {\r\n return stage;\r\n }\r\n break;\r\n }\r\n case \"groupByDate\": {\r\n const stage = this.buildGroupByDateStage(\r\n op.data.column,\r\n op.data.unit,\r\n op.data.aggregates ?? {},\r\n );\r\n if (stage) {\r\n return stage;\r\n }\r\n break;\r\n }\r\n case \"groupByRaw\": {\r\n const expression = op.data.expression;\r\n if (expression && typeof expression === \"object\") {\r\n return { $group: expression };\r\n }\r\n // If expression is not an object, it might be a string or other type\r\n // In that case, we should still return it as a $group stage\r\n if (expression) {\r\n return { $group: { _id: expression } };\r\n }\r\n break;\r\n }\r\n case \"distinct\": {\r\n const stage = this.buildGroupByStage(op.data.fields);\r\n if (stage) {\r\n return stage;\r\n }\r\n break;\r\n }\r\n default:\r\n break;\r\n }\r\n\r\n return null;\r\n }\r\n\r\n private buildGroupByStage(fields: GroupByInput): any {\r\n const groupId = this.buildGroupId(fields);\r\n if (!groupId) {\r\n return null;\r\n }\r\n\r\n return { $group: { _id: groupId } };\r\n }\r\n\r\n /**\r\n * Build a $group stage with aggregates from group operations.\r\n *\r\n * @param fields - Fields to group by\r\n * @param aggregates - Aggregate operations (abstract or raw)\r\n * @returns The $group stage or null\r\n */\r\n private buildGroupByWithAggregatesStage(\r\n fields: GroupByInput,\r\n aggregates: Record<string, RawExpression>,\r\n ): any {\r\n const groupId = this.buildGroupId(fields);\r\n if (!groupId) {\r\n return null;\r\n }\r\n\r\n const groupStage: Record<string, unknown> = {\r\n _id: groupId,\r\n };\r\n\r\n // Translate each aggregate expression\r\n for (const [alias, expression] of Object.entries(aggregates)) {\r\n if (isAggregateExpression(expression)) {\r\n // Translate abstract expression to MongoDB format\r\n groupStage[alias] = this.translateAggregateExpression(expression);\r\n } else {\r\n // Use raw expression as-is (already in MongoDB format)\r\n groupStage[alias] = expression;\r\n }\r\n }\r\n\r\n return { $group: groupStage };\r\n }\r\n\r\n /**\r\n * Build a `$group` stage that buckets documents by a `$dateTrunc` of a date\r\n * field, optionally running aggregates over each bucket.\r\n *\r\n * @param column - The date field to bucket\r\n * @param unit - The bucket granularity\r\n * @param aggregates - Aggregate operations (abstract or raw)\r\n * @returns The `$group` stage\r\n */\r\n private buildGroupByDateStage(\r\n column: string,\r\n unit: string,\r\n aggregates: Record<string, RawExpression>,\r\n ): any {\r\n const groupStage: Record<string, unknown> = {\r\n _id: { $dateTrunc: { date: `$${column}`, unit } },\r\n };\r\n\r\n for (const [alias, expression] of Object.entries(aggregates)) {\r\n if (isAggregateExpression(expression)) {\r\n groupStage[alias] = this.translateAggregateExpression(expression);\r\n } else {\r\n groupStage[alias] = expression;\r\n }\r\n }\r\n\r\n return { $group: groupStage };\r\n }\r\n\r\n /**\r\n * Extract field names from GroupByInput for renaming _id.\r\n *\r\n * @param fields - The grouping fields\r\n * @returns Field name(s) to use for renaming _id\r\n */\r\n private extractGroupFieldNames(fields: GroupByInput): string | string[] | null {\r\n if (typeof fields === \"string\") {\r\n return fields;\r\n }\r\n\r\n if (Array.isArray(fields)) {\r\n const allStrings = fields.every((field) => typeof field === \"string\");\r\n if (allStrings) {\r\n return fields as string[];\r\n }\r\n // For complex arrays, return null (don't rename)\r\n return null;\r\n }\r\n\r\n if (typeof fields === \"object\" && fields !== null) {\r\n // For object syntax, use the keys as field names\r\n return Object.keys(fields);\r\n }\r\n\r\n return null;\r\n }\r\n\r\n /**\r\n * Translate an abstract aggregate expression to MongoDB format.\r\n *\r\n * @param expr - Abstract aggregate expression\r\n * @returns MongoDB aggregation expression\r\n */\r\n private translateAggregateExpression(expr: AggregateExpression): Record<string, unknown> {\r\n switch (expr.__agg) {\r\n case \"count\":\r\n return { $sum: 1 };\r\n\r\n case \"countDistinct\":\r\n if (!expr.__field) {\r\n throw new Error(\"Count distinct aggregate requires a field name\");\r\n }\r\n // Accumulate the set of distinct values in the $group stage; the\r\n // renaming $project then finalizes it with `{ $size: \"$alias\" }`\r\n // (the standard distinct-count-per-group pattern — `$size` is not a\r\n // valid $group accumulator).\r\n return { $addToSet: `$${expr.__field}` };\r\n\r\n case \"sum\":\r\n // When a composed column expression is present, sum operates on it\r\n // (e.g. SUM(price * quantity) → { $sum: { $multiply: [...] } }) instead\r\n // of a bare field. This is the only aggregate that accepts `__expr`.\r\n if (expr.__expr) {\r\n return { $sum: this.columnExpressionToMongo(expr.__expr) };\r\n }\r\n if (!expr.__field) {\r\n throw new Error(\"Sum aggregate requires a field name\");\r\n }\r\n return { $sum: `$${expr.__field}` };\r\n\r\n case \"avg\":\r\n if (!expr.__field) {\r\n throw new Error(\"Average aggregate requires a field name\");\r\n }\r\n return { $avg: `$${expr.__field}` };\r\n\r\n case \"min\":\r\n if (!expr.__field) {\r\n throw new Error(\"Min aggregate requires a field name\");\r\n }\r\n return { $min: `$${expr.__field}` };\r\n\r\n case \"max\":\r\n if (!expr.__field) {\r\n throw new Error(\"Max aggregate requires a field name\");\r\n }\r\n return { $max: `$${expr.__field}` };\r\n\r\n case \"first\":\r\n if (!expr.__field) {\r\n throw new Error(\"First aggregate requires a field name\");\r\n }\r\n return { $first: `$${expr.__field}` };\r\n\r\n case \"last\":\r\n if (!expr.__field) {\r\n throw new Error(\"Last aggregate requires a field name\");\r\n }\r\n return { $last: `$${expr.__field}` };\r\n\r\n case \"distinct\":\r\n if (!expr.__field) {\r\n throw new Error(\"Distinct aggregate requires a field name\");\r\n }\r\n return { $distinct: `$${expr.__field}` };\r\n\r\n case \"floor\":\r\n if (!expr.__field) {\r\n throw new Error(\"Floor aggregate requires a field name\");\r\n }\r\n\r\n return { $floor: `$${expr.__field}` };\r\n\r\n default:\r\n throw new Error(`Unknown aggregate function: ${expr.__agg}`);\r\n }\r\n }\r\n\r\n /**\r\n * Compile a typed {@link ColumnExpression} tree into a MongoDB aggregation\r\n * expression.\r\n *\r\n * Column references become `$field` paths; literals are emitted verbatim;\r\n * arithmetic ops map to `$add` / `$subtract` / `$multiply` / `$divide`. The\r\n * `raw` node is rejected — a raw SQL fragment is not portable to a MongoDB\r\n * pipeline, so callers must use the typed combinators (or `groupByRaw`) here.\r\n *\r\n * @param expression - The expression tree to compile\r\n * @returns A MongoDB aggregation expression (e.g. `{ $multiply: [\"$price\", \"$quantity\"] }`)\r\n */\r\n private columnExpressionToMongo(expression: ColumnExpression): unknown {\r\n switch (expression.__expr) {\r\n case \"column\":\r\n return `$${expression.column}`;\r\n\r\n case \"literal\":\r\n return expression.value;\r\n\r\n case \"raw\":\r\n throw new Error(\r\n `$agg.sumRaw / $expr.raw is not portable to a MongoDB pipeline — a raw ` +\r\n `SQL fragment has no MongoDB equivalent. Use the typed $expr ` +\r\n `combinators ($expr.mul / $expr.add / $expr.sub / $expr.div / $expr.col / $expr.lit) or groupByRaw instead.`,\r\n );\r\n\r\n case \"add\":\r\n case \"subtract\":\r\n case \"multiply\":\r\n case \"divide\": {\r\n const operator = {\r\n add: \"$add\",\r\n subtract: \"$subtract\",\r\n multiply: \"$multiply\",\r\n divide: \"$divide\",\r\n }[expression.__expr];\r\n\r\n return {\r\n [operator]: expression.operands.map((operand) => this.columnExpressionToMongo(operand)),\r\n };\r\n }\r\n\r\n default:\r\n throw new Error(`Unsupported column expression node: ${JSON.stringify(expression)}`);\r\n }\r\n }\r\n\r\n private buildGroupId(fields: GroupByInput): any {\r\n if (!fields) {\r\n return null;\r\n }\r\n\r\n if (typeof fields === \"string\") {\r\n return `$${fields}`;\r\n }\r\n\r\n if (Array.isArray(fields)) {\r\n if (fields.length === 0) {\r\n return null;\r\n }\r\n\r\n const allStrings = fields.every((field) => typeof field === \"string\");\r\n if (allStrings) {\r\n const result: Record<string, string> = {};\r\n for (const field of fields as string[]) {\r\n result[field] = `$${field}`;\r\n }\r\n return result;\r\n }\r\n\r\n // Array of objects - merge them to build complex _id structures\r\n return (fields as Record<string, unknown>[]).reduce((acc, item) => ({ ...acc, ...item }), {});\r\n }\r\n\r\n if (typeof fields === \"object\") {\r\n const normalized: Record<string, unknown> = {};\r\n Object.entries(fields).forEach(([key, value]) => {\r\n if (typeof value === \"string\" && !value.startsWith(\"$\")) {\r\n normalized[key] = `$${value}`;\r\n } else {\r\n normalized[key] = value;\r\n }\r\n });\r\n return normalized;\r\n }\r\n\r\n return null;\r\n }\r\n\r\n /**\r\n * Build a $lookup stage from join operations.\r\n *\r\n * @param operations - The join operations\r\n * @returns The $lookup stage or null\r\n */\r\n private buildLookupStage(operations: Operation[]): any {\r\n const op = operations[0];\r\n const options = op.data;\r\n\r\n return {\r\n $lookup: {\r\n from: options.table,\r\n localField: options.localField,\r\n foreignField: options.foreignField,\r\n as: options.alias || options.table,\r\n },\r\n };\r\n }\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;AAiCA,IAAa,mBAAb,MAA8B;;;;CAI5B,AAAiB;;;;CAKjB,AAAiB;;;;CAKjB,AAAiB;;;;;CAMjB,AAAiB,kCAAkB,IAAI,IAA+B;;;;;;;CAQtE,AAAiB,uCAAuB,IAAI,IAAyB;;;;;;CAOrE,AAAO,YAAY,SAAkC;EACnD,KAAK,aAAa,QAAQ;EAC1B,KAAK,aAAa,QAAQ;EAC1B,KAAK,mBAAmB,QAAQ;CAClC;;;;;;;;;;;;;;;;;;;;;CAsBA,AAAO,QAAe;EACpB,MAAM,WAAkB,CAAC;EACzB,IAAI,eAAqC;EACzC,IAAI,gBAA6B,CAAC;EAElC,KAAK,MAAM,MAAM,KAAK,YAAY,KAAK,UAAU,GAC/C,IAAI,GAAG,aAAa,GAAG,UAAU,cAE/B,cAAc,KAAK,EAAE;OAChB;GAEL,IAAI,cAAc,SAAS,GAAG;IAC5B,MAAM,aAAa,KAAK,WAAW,cAAe,aAAa;IAC/D,IAAI,YAAY;KACd,MAAM,aAAa,SAAS;KAC5B,SAAS,KAAK,UAAU;KAExB,KAAK,qBAAqB,cAAe,eAAe,UAAU;IACpE;IACA,gBAAgB,CAAC;GACnB;GAEA,IAAI,GAAG,WAAW;IAEhB,eAAe,GAAG;IAClB,cAAc,KAAK,EAAE;GACvB,OAAO;IAEL,MAAM,aAAa,KAAK,WAAW,GAAG,OAAO,CAAC,EAAE,CAAC;IACjD,IAAI,YAAY;KACd,MAAM,aAAa,SAAS;KAC5B,SAAS,KAAK,UAAU;KAExB,KAAK,qBAAqB,GAAG,OAAO,CAAC,EAAE,GAAG,UAAU;IACtD;IACA,eAAe;GACjB;EACF;EAIF,IAAI,cAAc,SAAS,GAAG;GAC5B,MAAM,aAAa,KAAK,WAAW,cAAe,aAAa;GAC/D,IAAI,YAAY;IACd,MAAM,aAAa,SAAS;IAC5B,SAAS,KAAK,UAAU;IAExB,KAAK,qBAAqB,cAAe,eAAe,UAAU;GACpE;EACF;EAGA,OAAO,KAAK,uBAAuB,QAAQ;CAC7C;;;;;;;;;;;;;;;;CAiBA,AAAQ,YAAY,YAAsC;EACxD,MAAM,YAAyB,CAAC;EAChC,IAAI,UAAuB,CAAC;EAE5B,MAAM,qBAA2B;GAC/B,IAAI,QAAQ,WAAW,GACrB;GAEF,UAAU,KAAK,GAAG,QAAQ,QAAQ,OAAO,GAAG,UAAU,QAAQ,CAAC;GAC/D,UAAU,KAAK,GAAG,QAAQ,QAAQ,OAAO,GAAG,UAAU,QAAQ,CAAC;GAC/D,UAAU,CAAC;EACb;EAEA,KAAK,MAAM,MAAM,YAKf,IAHE,GAAG,cACF,GAAG,UAAU,YAAY,GAAG,UAAU,cAAc,GAAG,UAAU,UAGlE,QAAQ,KAAK,EAAE;OACV;GACL,aAAa;GACb,UAAU,KAAK,EAAE;EACnB;EAGF,aAAa;EACb,OAAO;CACT;;;;CAKA,AAAQ,qBACN,OACA,YACA,YACM;EACN,IAAI,UAAU,UAAU;GACtB,MAAM,KAAK,WAAW;GACtB,IAAI,GAAG,SAAS,2BAA2B,GAAG,KAAK,QAAQ;IACzD,MAAM,aAAa,KAAK,uBAAuB,GAAG,KAAK,MAAM;IAC7D,IAAI,YACF,KAAK,gBAAgB,IAAI,YAAY,UAAU;IAGjD,KAAK,0BAA0B,YAAY,GAAG,KAAK,UAAU;GAC/D,OAAO,IAAI,GAAG,SAAS,eAAe;IAEpC,KAAK,gBAAgB,IAAI,YAAY,GAAG,KAAK,MAAgB;IAC7D,KAAK,0BAA0B,YAAY,GAAG,KAAK,cAAc,CAAC,CAAC;GACrE;EACF;CACF;;;;;;CAOA,AAAQ,0BACN,YACA,YACM;EACN,MAAM,kCAAkB,IAAI,IAAY;EACxC,KAAK,MAAM,CAAC,OAAO,eAAe,OAAO,QAAQ,UAAU,GACzD,IAAI,sBAAsB,UAAU,KAAK,WAAW,UAAU,iBAC5D,gBAAgB,IAAI,KAAK;EAI7B,IAAI,gBAAgB,OAAO,GACzB,KAAK,qBAAqB,IAAI,YAAY,eAAe;CAE7D;;;;;;;;;;CAWA,AAAQ,uBAAuB,UAAwB;EACrD,MAAM,YAAmB,CAAC;EAE1B,KAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;GACxC,MAAM,QAAQ,SAAS;GAGvB,IAAI,MAAM,UAAU,KAAK,gBAAgB,IAAI,CAAC,GAAG;IAC/C,MAAM,aAAa,KAAK,gBAAgB,IAAI,CAAC;IAG7C,UAAU,KAAK,KAAK;IAGpB,MAAM,aAAsC,CAAC;IAE7C,IAAI,OAAO,eAAe,UAExB,WAAW,cAAc;SACpB,IAAI,MAAM,QAAQ,UAAU,KAAK,WAAW,SAAS,GAE1D,KAAK,MAAM,aAAa,YACtB,WAAW,aAAa,QAAQ;IAOpC,MAAM,kBAAkB,KAAK,qBAAqB,IAAI,CAAC;IACvD,MAAM,kBAAkB,OAAO,KAAK,MAAM,MAAM,EAAE,QAAQ,QAAQ,QAAQ,KAAK;IAC/E,KAAK,MAAM,SAAS,iBAClB,IAAI,iBAAiB,IAAI,KAAK,GAC5B,WAAW,SAAS,EAAE,OAAO,IAAI,QAAQ;SAEzC,WAAW,SAAS;IAIxB,IAAI,OAAO,KAAK,UAAU,EAAE,SAAS,GAAG;KAEtC,WAAW,MAAM;KACjB,UAAU,KAAK,EAAE,UAAU,WAAW,CAAC;IACzC;GACF,OAEE,UAAU,KAAK,KAAK;EAExB;EAEA,OAAO;CACT;;;;;;;;;;;;;;;;;;;;;;;;CAyBA,AAAO,iBAAyB;EAC9B,MAAM,WAAW,KAAK,MAAM;EAE5B,IAAI,SAAS,WAAW,GACtB,OAAO;EAGT,IAAI,SAAS;EACb,UAAU,IAAI,OAAO,EAAE,IAAI;EAE3B,SAAS,SAAS,OAAO,UAAU;GACjC,MAAM,YAAY,OAAO,KAAK,KAAK,EAAE;GACrC,MAAM,YAAY,MAAM;GAExB,IAAI,QAAQ,GACV,UAAU;GAGZ,UAAU,SAAS,QAAQ,EAAE,IAAI,OAAO,UAAU,SAAS,EAAE;GAC7D,UAAU,KAAK,gBAAgB,WAAW,CAAC;EAC7C,CAAC;EAED,OAAO;CACT;;;;;;;;CASA,AAAQ,gBAAgB,MAAW,SAAiB,GAAW;EAC7D,MAAM,SAAS,IAAI,OAAO,MAAM;EAEhC,IAAI,OAAO,SAAS,YAAY,SAAS,MACvC,OAAO,GAAG,SAAS,KAAK,UAAU,IAAI,EAAE;EAG1C,IAAI,MAAM,QAAQ,IAAI,GAAG;GACvB,IAAI,KAAK,WAAW,GAAG,OAAO,GAAG,OAAO;GAExC,IAAI,SAAS;GACb,KAAK,SAAS,MAAM,UAAU;IAC5B,UAAU,GAAG,OAAO,GAAG,OAAO,QAAQ,KAAK,EAAE;IAC7C,UAAU,KAAK,gBAAgB,MAAM,SAAS,CAAC;GACjD,CAAC;GACD,OAAO;EACT;EAEA,IAAI,SAAS;EACb,OAAO,QAAQ,IAAI,EAAE,SAAS,CAAC,KAAK,WAAW;GAE7C,MAAM,aADa,IAAI,WAAW,GACN,IAAI,OAAO,cAAc,GAAG,IAAI,OAAO,KAAK,GAAG;GAE3E,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK,GAAG;IACxE,UAAU,GAAG,SAAS,WAAW;IACjC,UAAU,KAAK,gBAAgB,OAAO,SAAS,CAAC;GAClD,OAAO,IAAI,MAAM,QAAQ,KAAK,GAAG;IAC/B,UAAU,GAAG,SAAS,WAAW;IACjC,UAAU,KAAK,gBAAgB,OAAO,SAAS,CAAC;GAClD,OAAO;IACL,MAAM,iBACJ,OAAO,UAAU,WACb,OAAO,aAAa,KAAK,IACzB,OAAO,UAAU,YACf,OAAO,WAAW,MAAM,SAAS,CAAC,IAClC,OAAO,UAAU,WACf,OAAO,YAAY,KAAK,UAAU,KAAK,CAAC,IACxC,OAAO,YAAY,OAAO,KAAK,CAAC;IAC1C,UAAU,GAAG,SAAS,WAAW,IAAI,eAAe;GACtD;EACF,CAAC;EAED,OAAO,OAAO,SAAS,IAAI,IAAI,SAAS,GAAG,OAAO;CACpD;;;;;;;;CASA,AAAQ,WAAW,OAAsB,YAA8B;EACrE,QAAQ,OAAR;GACE,KAAK,UACH,OAAO,KAAK,gBAAgB,UAAU;GACxC,KAAK,YACH,OAAO,KAAK,kBAAkB,UAAU;GAC1C,KAAK,SACH,OAAO,KAAK,eAAe,UAAU;GACvC,KAAK,UACH,OAAO,KAAK,gBAAgB,UAAU;GACxC,KAAK,WACH,OAAO,KAAK,iBAAiB,UAAU;GACzC,KAAK,UACH,OAAO,EAAE,QAAQ,WAAW,GAAG,KAAK,MAAM;GAC5C,KAAK,SACH,OAAO,EAAE,OAAO,WAAW,GAAG,KAAK,MAAM;GAC3C,KAAK,oBACH,OAAO,EACL,kBAAkB,WAAW,GAAG,KAAK,KACvC;GACF,SACE,OAAO;EACX;CACF;;;;;;;;;;;CAYA,AAAQ,gBAAgB,YAA8B;EACpD,MAAM,YAAiC,CAAC;EACxC,MAAM,YAAmB,CAAC;EAC1B,MAAM,qBAA4B,CAAC;EACnC,IAAI,iBAAiB;EAErB,MAAM,UAAU,WAAsB;GACpC,IAAI,CAAC,QACH;GAGF,IAAI,KAAK,kBAAkB,MAAM,GAAG;IAClC,UAAU,KAAK,GAAG,OAAO,GAAG;IAC5B;GACF;GAEA,IAAI,MAAM,QAAQ,MAAM,GAAG;IACzB,UAAU,KAAK,GAAG,MAAM;IACxB;GACF;GAEA,UAAU,KAAK,MAAM;EACvB;EAEA,MAAM,YAAY,cAAyB;GACzC,IAAI,CAAC,WACH;GAGF,OAAO,QAAQ,SAAS,EAAE,SAAS,CAAC,KAAK,WAAW;IAClD,IAAI,QAAQ,OAAO;KACjB,OAAO,KAAK;KACZ;IACF;IAEA,IACE,SACA,OAAO,UAAU,YACjB,CAAC,MAAM,QAAQ,KAAK,KACpB,UAAU,QACV,OAAO,UAAU,SAAS,YAC1B,CAAC,MAAM,QAAQ,UAAU,IAAI,GAE7B,UAAU,OAAO;KAAE,GAAG,UAAU;KAAM,GAAG;IAAM;SAE/C,UAAU,OAAO;GAErB,CAAC;EACH;EAEA,MAAM,oBAAoB,cAAyB;GACjD,IAAI,CAAC,WACH;GAEF,IAAI,gBACF,OAAO,SAAS;QAEhB,mBAAmB,KAAK,SAAS;EAErC;EAEA,MAAM,4BAAkC;GACtC,IAAI,gBACF;GAEF,iBAAiB;GACjB,OAAO,mBAAmB,SAAS,GAAG;IACpC,MAAM,YAAY,mBAAmB,MAAM;IAC3C,IAAI,WACF,OAAO,SAAS;GAEpB;EACF;EAEA,MAAM,gCAAsC;GAC1C,IAAI,mBAAmB,WAAW,GAChC;GAEF,IAAI,gBACF,mBAAmB,QAAQ,MAAM;QAEjC,mBAAmB,QAAQ,QAAQ;GAErC,mBAAmB,SAAS;EAC9B;EAEA,KAAK,MAAM,MAAM,YAAY;GAC3B,IAAI,GAAG,SAAS,oBAAoB,GAAG,SAAS,oBAAoB;IAClE,wBAAwB;IACxB,MAAM,oBAAoB,KAAK,uBAAuB,GAAG,IAAI;IAC7D,IAAI,CAAC,mBACH;IAQF,IAJE,GAAG,SAAS,sBACX,kBAAkB,CAAC,KAAK,kBAAkB,iBAAiB,KAC5D,KAAK,kBAAkB,iBAAiB,GAE3B;KACb,IAAI,GAAG,SAAS,oBACd,oBAAoB;KAEtB,OAAO,iBAAiB;IAC1B,OACE,SAAS,iBAAiB;IAE5B;GACF;GAEA,IAAI,GAAG,SAAS,gBAAgB;IAC9B,iBAAiB,GAAG,IAAI;IACxB;GACF;GAEA,IACE,GAAG,SAAS,eACZ,GAAG,SAAS,iBACZ,GAAG,SAAS,kBACZ,GAAG,SAAS,mBACZ;IACA,MAAM,UAAU,GAAG,SAAS,eAAe,GAAG,SAAS;IACvD,MAAM,SAAS,KAAK,uBAAuB,GAAG,KAAK,QAAQ;IAC3D,IAAI,QAAQ;KACV,MAAM,YAAY,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI;KACjD,IAAI,GAAG,KAAK,WAAW,SAAS,GAAG;MACjC,oBAAoB;MACpB,OAAO,SAAS;KAClB,OACE,iBAAiB,SAAS;IAE9B;IACA;GACF;GAEA,IAAI,GAAG,SAAS,kBAAkB;IAChC,oBAAoB;IACpB,OAAO,GAAG,IAAI;IACd;GACF;GAEA,MAAM,YAAY,KAAK,oBAAoB,EAAE;GAC7C,IAAI,CAAC,WACH;GAGF,IAAI,GAAG,KAAK,WAAW,SAAS,GAAG;IACjC,oBAAoB;IACpB,OAAO,SAAS;GAClB,OACE,iBAAiB,SAAS;EAE9B;EAEA,wBAAwB;EAExB,MAAM,SAAS,OAAO,KAAK,SAAS,EAAE,SAAS;EAC/C,MAAM,QAAQ,UAAU,SAAS;EAEjC,IAAI,CAAC,UAAU,CAAC,OACd,OAAO;EAGT,MAAM,QAAa,CAAC;EACpB,IAAI,QACF,OAAO,OAAO,OAAO,SAAS;EAEhC,IAAI,OACF,MAAM,MAAM;EAGd,OAAO,EAAE,QAAQ,MAAM;CACzB;CAEA,AAAQ,kBAAkB,WAA6C;EACrE,OACE,aACA,OAAO,cAAc,YACrB,CAAC,MAAM,QAAQ,SAAS,KACxB,OAAO,KAAK,SAAS,EAAE,WAAW,KAClC,MAAM,QAAS,UAAkB,GAAG;CAExC;;;;;;;;;CAUA,AAAQ,uBAAuB,UAAoB;EAEjD,MAAM,aAAa,KAAK,iBAAiB;EAGzC,SAAS,UAAU;EAGnB,MAAM,WAAW,WAAW,WAAW,QAAQ,OAAkB,GAAG,UAAU,QAAQ;EAEtF,IAAI,SAAS,WAAW,GACtB,OAAO;EAGT,MAAM,YAAiC,CAAC;EACxC,MAAM,YAAmB,CAAC;EAC1B,MAAM,gBAAgB,SAAS,MAAM,OAAO,GAAG,KAAK,WAAW,SAAS,CAAC;EAEzE,MAAM,UAAU,WAAsB;GACpC,IAAI,CAAC,QACH;GAEF,IAAI,KAAK,kBAAkB,MAAM,GAAG;IAClC,UAAU,KAAK,GAAG,OAAO,GAAG;IAC5B;GACF;GACA,UAAU,KAAK,MAAM;EACvB;EAEA,IAAI,eAAe;GACjB,KAAK,MAAM,MAAM,UAAU;IACzB,IAAI,GAAG,SAAS,oBAAoB,GAAG,SAAS,oBAAoB;KAClE,MAAM,kBAAkB,KAAK,uBAAuB,GAAG,IAAI;KAC3D,IAAI,iBACF,OAAO,eAAe;KAExB;IACF;IAEA,IAAI,GAAG,SAAS,kBAAkB,GAAG,SAAS,kBAAkB;KAC9D,OAAO,GAAG,IAAI;KACd;IACF;IAEA,MAAM,YAAY,KAAK,oBAAoB,EAAE;IAC7C,IAAI,WACF,OAAO,SAAS;GAEpB;GAEA,OAAO,UAAU,SAAS,IAAI,EAAE,KAAK,UAAU,IAAI;EACrD;EAEA,KAAK,MAAM,MAAM,UACf,IAAI,GAAG,SAAS,kBAAkB;GAChC,MAAM,kBAAkB,KAAK,uBAAuB,GAAG,IAAI;GAC3D,IAAI,iBACF,OAAO,OAAO,WAAW,eAAe;EAE5C,OAAO,IAAI,GAAG,SAAS,gBACrB,OAAO,OAAO,WAAW,GAAG,IAAI;OAC3B;GACL,MAAM,YAAY,KAAK,oBAAoB,EAAE;GAC7C,IAAI,WACF,OAAO,OAAO,WAAW,SAAS;EAEtC;EAGF,OAAO,OAAO,KAAK,SAAS,EAAE,SAAS,IAAI,YAAY;CACzD;;;;;;;CAQA,AAAQ,oBAAoB,IAAoB;EAC9C,MAAM,EAAE,OAAO,UAAU,UAAU,GAAG;EAEtC,QAAQ,GAAG,MAAX;GACE,KAAK;GACL,KAAK,WACH,OAAO,KAAK,uBAAuB,OAAO,UAAU,KAAK;GAE3D,KAAK,WACH,OAAO,GAAG,QAAQ,EAAE,KAAK,SAAS,GAAG,KAAK,OAAO,EAAE;GAErD,KAAK,cACH,OAAO,GAAG,QAAQ,EAAE,MAAM,SAAS,GAAG,KAAK,OAAO,EAAE;GAEtD,KAAK,aACH,OAAO,GAAG,QAAQ,KAAK;GAEzB,KAAK,gBACH,OAAO,GAAG,QAAQ,EAAE,KAAK,KAAK,EAAE;GAElC,KAAK,gBACH,OAAO,GACJ,QAAQ;IACP,MAAM,GAAG,KAAK,MAAM;IACpB,MAAM,GAAG,KAAK,MAAM;GACtB,EACF;GAEF,KAAK,mBACH,OAAO,GACJ,QAAQ,EACP,MAAM;IACJ,MAAM,GAAG,KAAK,MAAM;IACpB,MAAM,GAAG,KAAK,MAAM;GACtB,EACF,EACF;GAEF,KAAK,aACH,OAAO,GACJ,QAAQ;IAAE,QAAQ,KAAK,qBAAqB,GAAG,IAAI;IAAG,UAAU;GAAI,EACvE;GAEF,KAAK,gBACH,OAAO,GACJ,QAAQ,EACP,MAAM;IAAE,QAAQ,KAAK,qBAAqB,GAAG,IAAI;IAAG,UAAU;GAAI,EACpE,EACF;GAEF,KAAK,mBACH,OAAO,GACJ,QAAQ;IAAE,QAAQ,IAAI,YAAY,OAAO,GAAG,KAAK,KAAK,CAAC;IAAK,UAAU;GAAI,EAC7E;GAEF,KAAK,sBACH,OAAO,GACJ,QAAQ,EACP,MAAM;IAAE,QAAQ,IAAI,YAAY,OAAO,GAAG,KAAK,KAAK,CAAC;IAAK,UAAU;GAAI,EAC1E,EACF;GAEF,KAAK,iBACH,OAAO,GACJ,QAAQ;IAAE,QAAQ,GAAG,YAAY,OAAO,GAAG,KAAK,KAAK,CAAC,EAAE;IAAI,UAAU;GAAI,EAC7E;GAEF,KAAK,oBACH,OAAO,GACJ,QAAQ,EACP,MAAM;IAAE,QAAQ,GAAG,YAAY,OAAO,GAAG,KAAK,KAAK,CAAC,EAAE;IAAI,UAAU;GAAI,EAC1E,EACF;GAEF,KAAK,eACH,OAAO,GAAG,QAAQ,EAAE,SAAS,KAAK,EAAE;GAEtC,KAAK,kBACH,OAAO,GAAG,QAAQ,EAAE,SAAS,MAAM,EAAE;GAEvC,KAAK,aACH,IAAI,GAAG,KAAK,aAAa,KACvB,OAAO,GAAG,QAAQ,EAAE,OAAO,GAAG,KAAK,KAAK,EAAE;QAG1C,OAAO,EACL,OAAO,GAFO,KAAK,iBAAiB,GAAG,KAAK,QAGnC,IAAI,CAAC,EAAE,OAAO,IAAI,QAAQ,GAAG,GAAG,KAAK,IAAI,EAClD,EACF;GAGJ,KAAK,cACH,OAAO;IACL,OAAO,EAAE,SAAS,GAAG,KAAK,MAAM;IAChC,GAAI,GAAG,KAAK,WAAW,CAAC;GAC1B;GAEF,KAAK;GACL,KAAK,cACH,OAAO,KAAK,qBACV,GAAG,KAAK,YACR,GAAG,KAAK,QACV;GAEF,KAAK;GACL,KAAK,iBACH,OAAO,KAAK,sBAAsB,GAAG,KAAK,OAAO,GAAG,KAAK,UAAU,GAAG,KAAK,MAAM;GAEnF,KAAK,uBACH,OAAO,KAAK,6BACV,GAAG,KAAK,OACR,GAAG,KAAK,aACR,GAAG,KAAK,WACV;GAEF,KAAK;GACL,KAAK,mBACH,OAAO,KAAK,2BAA2B,GAAG,KAAK,OAAO,GAAG,KAAK,KAAK;GAErE,KAAK,mBACH,OAAO,KAAK,yBAAyB,GAAG,KAAK,OAAO,GAAG,KAAK,KAAK;GAEnE,KAAK,kBACH,OAAO,KAAK,wBAAwB,GAAG,KAAK,OAAO,GAAG,KAAK,KAAK;GAElE,KAAK,aACH,OAAO,KAAK,mBAAmB,GAAG,KAAK,OAAO,GAAG,KAAK,KAAK;GAE7D,KAAK,YACH,OAAO,KAAK,uBAAuB,GAAG,KAAK,OAAO,eAAe,GAAG,KAAK,KAAK;GAEhF,KAAK,cACH,OAAO,KAAK,uBAAuB,GAAG,KAAK,OAAO,UAAU,GAAG,KAAK,KAAK;GAE3E,KAAK,aACH,OAAO,KAAK,uBAAuB,GAAG,KAAK,OAAO,SAAS,GAAG,KAAK,KAAK;GAE1E,KAAK,qBACH,OAAO,KAAK,2BAA2B,GAAG,KAAK,MAAM,GAAG,KAAK,KAAK;GAEpE,KAAK,0BACH,OAAO,KAAK,gCAAgC,GAAG,KAAK,MAAM,GAAG,KAAK,KAAK;GAEzE,KAAK,wBACH,OAAO,KAAK,8BAA8B,GAAG,KAAK,IAAI;GAExD,KAAK,mBACH,OAAO,KAAK,yBACV,GAAG,KAAK,MACR,GAAG,KAAK,UACR,GAAG,KAAK,KACV;GAEF,KAAK,oBACH,OAAO,KAAK,uBAAuB,GAAG,KAAK,MAAM,OAAO;GAE1D,KAAK,qBACH,OAAO,KAAK,uBAAuB,GAAG,KAAK,MAAM,QAAQ;GAE3D,KAAK,oBACH,OAAO,KAAK,0BACV,GAAG,KAAK,OACR,GAAG,KAAK,UACR,GAAG,KAAK,KACV;GAEF,KAAK;GACL,KAAK,mBACH,OAAO,EAAE,OAAO,EAAE,SAAS,GAAG,KAAK,MAAM,EAAE;GAE7C,KAAK,eACH,OAAO,GACJ,GAAG,KAAK,QAAQ;IAGf,QAAQ,YAAY,OAAO,GAAG,KAAK,KAAK,CAAC;IACzC,UAAU;GACZ,EACF;GAEF,KAAK;GACL,KAAK,eAAe;IAClB,MAAM,YAAY,KAAK,uBAAuB,GAAG,KAAK,QAAQ;IAC9D,OAAO,YAAY,EAAE,MAAM,CAAC,SAAS,EAAE,IAAI;GAC7C;GAEA,KAAK,gBACH,OAAO,KAAK,uBAAuB,GAAG,KAAK,QAAQ;GAErD,KAAK,mBAAmB;IACtB,MAAM,eAAe,KAAK,uBAAuB,GAAG,KAAK,QAAQ;IACjE,OAAO,eAAe,EAAE,MAAM,CAAC,YAAY,EAAE,IAAI;GACnD;GAEA,KAAK,sBACH,IAAI,GAAG,KAAK,KACV,OAAO,GACJ,QAAQ,EACP,YAAY,GAAG,GAAG,KAAK,MAAM,GAAG,KAAK,MAAM,EAC7C,EACF;QAEA,OAAO,GAAG,QAAQ,GAAG,KAAK,MAAM;GAGpC,SACE,OAAO;EACX;CACF;;;;;;;;;;;;;CAcA,AAAQ,qBAAqB,MAAmB;EAC9C,IAAI,KAAK,aAAa,QAAQ,OAAO,KAAK,YAAY,UACpD,OAAO,KAAK;EAGd,OAAO,mBAAmB,KAAK,OAAO;CACxC;;;;;;;;;CAUA,AAAQ,uBAAuB,OAAe,UAAkB,OAAqB;EACnF,QAAQ,UAAR;GACE,KAAK,KACH,OAAO,GAAG,QAAQ,MAAM;GAC1B,KAAK,MACH,OAAO,GAAG,QAAQ,EAAE,KAAK,MAAM,EAAE;GACnC,KAAK,KACH,OAAO,GAAG,QAAQ,EAAE,KAAK,MAAM,EAAE;GACnC,KAAK,MACH,OAAO,GAAG,QAAQ,EAAE,MAAM,MAAM,EAAE;GACpC,KAAK,KACH,OAAO,GAAG,QAAQ,EAAE,KAAK,MAAM,EAAE;GACnC,KAAK,MACH,OAAO,GAAG,QAAQ,EAAE,MAAM,MAAM,EAAE;GACpC,SACE,OAAO,GAAG,QAAQ,MAAM;EAC5B;CACF;;;;;;;CAQA,AAAQ,iBAAiB,UAA0B;EASjD,OAAO;GAPL,KAAK;GACL,MAAM;GACN,KAAK;GACL,MAAM;GACN,KAAK;GACL,MAAM;EAEC,EAAE,aAAa;CAC1B;CAEA,AAAQ,qBAAqB,YAA2B,WAA4B;EAClF,IAAI,OAAO,eAAe,UAIxB,MAAM,IAAI,yBACR,uRAGF;EAGF,IAAI,OAAO,eAAe,YAAY,eAAe,MAAM;GACzD,KAAK,6BAA6B,UAAU;GAC5C,OAAO;EACT;EAEA,OAAO;CACT;;;;;;;;CASA,AAAQ,6BAA6B,OAAsB;EACzD,IAAI,MAAM,QAAQ,KAAK,GAAG;GACxB,KAAK,MAAM,QAAQ,OACjB,KAAK,6BAA6B,IAAI;GAExC;EACF;EAEA,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM;EAEjD,KAAK,MAAM,CAAC,KAAK,WAAW,OAAO,QAAQ,KAAgC,GAAG;GAC5E,IAAI,QAAQ,YAAY,QAAQ,eAAe,QAAQ,gBACrD,MAAM,IAAI,yBACR,yDAAyD,IAAI,kKAG/D;GAEF,KAAK,6BAA6B,MAAM;EAC1C;CACF;CAEA,AAAQ,cAAc,YAAoB,UAA8B;EACtE,IAAI,CAAC,YAAY,SAAS,WAAW,GACnC,OAAO;EAGT,IAAI,QAAQ;EACZ,OAAO,WAAW,QAAQ,aAAa;GACrC,MAAM,QAAQ,SAAS;GACvB,OAAO,UAAU,SAAY,MAAM,KAAK,UAAU,KAAK;EACzD,CAAC;CACH;CAEA,AAAQ,sBAAsB,OAAe,UAAyB,QAAqB;EAEzF,OAAO,EACL,OAAO,GAFa,KAAK,iBAAiB,QAG3B,IAAI,CAAC,KAAK,WAAW,KAAK,GAAG,KAAK,WAAW,MAAM,CAAC,EACnE,EACF;CACF;CAEA,AAAQ,6BAA6B,OAAe,OAAe,OAAoB;EACrF,OAAO,EACL,OAAO,EACL,MAAM,CACJ,EAAE,MAAM,CAAC,KAAK,WAAW,KAAK,GAAG,KAAK,WAAW,KAAK,CAAC,EAAE,GACzD,EAAE,MAAM,CAAC,KAAK,WAAW,KAAK,GAAG,KAAK,WAAW,KAAK,CAAC,EAAE,CAC3D,EACF,EACF;CACF;CAEA,AAAQ,WAAW,QAAwB;EACzC,OAAO,OAAO,WAAW,GAAG,IAAI,SAAS,IAAI;CAC/C;CAEA,AAAQ,2BAA2B,OAAe,OAA2B;EAC3E,MAAM,SAAS,KAAK,mBAAmB,KAAK;EAC5C,MAAM,QAAQ,KAAK,WAAW,MAAM;EACpC,MAAM,MAAM,KAAK,SAAS,MAAM;EAChC,OAAO,GAAG,QAAQ;GAAE,MAAM;GAAO,MAAM;EAAI,EAAE;CAC/C;CAEA,AAAQ,yBAAyB,OAAe,OAA2B;EACzE,MAAM,SAAS,KAAK,WAAW,KAAK,mBAAmB,KAAK,CAAC;EAC7D,OAAO,GAAG,QAAQ,EAAE,KAAK,OAAO,EAAE;CACpC;CAEA,AAAQ,wBAAwB,OAAe,OAA2B;EACxE,MAAM,SAAS,KAAK,SAAS,KAAK,mBAAmB,KAAK,CAAC;EAC3D,OAAO,GAAG,QAAQ,EAAE,KAAK,OAAO,EAAE;CACpC;CAEA,AAAQ,mBAAmB,OAAe,OAAoB;EAC5D,OAAO,EACL,OAAO,EACL,KAAK,CACH,EACE,eAAe;GACb,QAAQ;GACR,MAAM,IAAI;EACZ,EACF,GACA,KACF,EACF,EACF;CACF;CAEA,AAAQ,uBACN,OACA,UACA,OACK;EACL,OAAO,EACL,OAAO,EACL,KAAK,CACH,GACG,WAAW,IAAI,QAClB,GACA,KACF,EACF,EACF;CACF;CAEA,AAAQ,2BAA2B,MAAc,OAAqB;EACpE,MAAM,YAAY,KAAK,cAAc,IAAI;EACzC,IAAI,MAAM,QAAQ,KAAK,GACrB,OAAO,GAAG,YAAY,EAAE,MAAM,MAAM,EAAE;EAExC,OAAO,GAAG,YAAY,MAAM;CAC9B;CAEA,AAAQ,gCAAgC,MAAc,OAAqB;EACzE,MAAM,YAAY,KAAK,cAAc,IAAI;EACzC,MAAM,SAAS,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;EACpD,OAAO,GAAG,YAAY,EAAE,MAAM,OAAO,EAAE;CACzC;CAEA,AAAQ,8BAA8B,MAAmB;EACvD,OAAO,GACJ,KAAK,cAAc,IAAI,IAAI,EAAE,SAAS,KAAK,EAC9C;CACF;CAEA,AAAQ,yBAAyB,MAAc,UAAyB,OAAoB;EAE1F,OAAO,EACL,OAAO,GAFa,KAAK,iBAAiB,QAG3B,IAAI,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,IAAI,KAAK,cAAc,IAAI,KAAK,CAAC,CAAC,EAAE,EAAE,GAAG,KAAK,EACvF,EACF;CACF;CAEA,AAAQ,uBAAuB,MAAc,MAAmB;EAC9D,OAAO,EACL,OAAO,EACL,KAAK,CAAC,EAAE,OAAO,IAAI,KAAK,cAAc,IAAI,IAAI,GAAG,IAAI,EACvD,EACF;CACF;CAEA,AAAQ,0BAA0B,OAAe,UAAyB,OAAoB;EAE5F,OAAO,EACL,OAAO,GAFa,KAAK,iBAAiB,QAG3B,IAAI,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,IAAI,SAAS,CAAC,CAAC,EAAE,EAAE,GAAG,KAAK,EACpE,EACF;CACF;CAEA,AAAQ,mBAAmB,OAA4B;EACrD,IAAI,iBAAiB,MACnB,OAAO;EAET,MAAM,SAAS,IAAI,KAAK,KAAK;EAC7B,IAAI,OAAO,MAAM,OAAO,QAAQ,CAAC,GAC/B,MAAM,IAAI,MAAM,uBAAuB,OAAO;EAEhD,OAAO;CACT;CAEA,AAAQ,WAAW,MAAkB;EACnC,MAAM,OAAO,IAAI,KAAK,IAAI;EAC1B,KAAK,SAAS,GAAG,GAAG,GAAG,CAAC;EACxB,OAAO;CACT;CAEA,AAAQ,SAAS,MAAkB;EACjC,MAAM,OAAO,IAAI,KAAK,IAAI;EAC1B,KAAK,SAAS,IAAI,IAAI,IAAI,GAAG;EAC7B,OAAO;CACT;CAEA,AAAQ,cAAc,MAAsB;EAC1C,OAAO,KAAK,QAAQ,OAAO,GAAG;CAChC;CAEA,AAAQ,sBACN,YACA,QACA,OACM;EACN,KAAK,MAAM,SAAS,QAClB,WAAW,SAAS;CAExB;;;;;;CAOA,AAAQ,sBACN,YACA,eACM;EACN,KAAK,MAAM,CAAC,OAAO,UAAU,OAAO,QAAQ,aAAa,GAAG;GAE1D,IAAI,OAAO,UAAU,WAAW;IAC9B,WAAW,SAAS,QAAQ,IAAI;IAChC;GACF;GAGA,IAAI,OAAO,UAAU,UAAU;IAC7B,WAAW,SAAS;IACpB;GACF;GAGA,IAAI,OAAO,UAAU,UAAU;IAE7B,WAAW,SAAS,IAAI;IACxB;GACF;GAGA,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM;IAC/C,WAAW,SAAS;IACpB;GACF;GAGA,WAAW,SAAS;EACtB;CACF;CAEA,AAAQ,mBACN,YACA,YACA,UACM;EACN,MAAM,WAAW,KAAK,4BAA4B,YAAY,QAAQ;EACtE,IAAI,CAAC,UACH;EAGF,IAAI,OAAO,aAAa,YAAY,aAAa,QAAQ,CAAC,MAAM,QAAQ,QAAQ,GAC9E,OAAO,OAAO,YAAY,QAAmC;CAEjE;CAEA,AAAQ,4BACN,YACA,UACK;EACL,IAAI,OAAO,eAAe,UAAU;GAClC,MAAM,SACJ,YAAY,WAAW,SAAS,GAAG,IAC/B,KAAK,cAAc,YAAY,QAAQ,IACvC;GACN,IAAI,OAAO,WAAW,GAAG,GACvB,OAAO,OAAO,MAAM,CAAC;GAEvB,OAAO,KAAK,wBAAwB,MAAM;EAC5C;EAEA,IAAI,OAAO,eAAe,YAAY,eAAe,QAAQ,EAAE,sBAAsB,OACnF,OAAO;EAGT,IAAI,OAAO,eAAe,YAAY,OAAO,eAAe,WAC1D,OAAO;EAGT,OAAO;CACT;CAEA,AAAQ,wBAAwB,OAAoC;EAClE,IAAI,OAAO,UAAU,UAAU;GAC7B,IAAI,MAAM,WAAW,GAAG,GACtB,OAAO,MAAM,MAAM,CAAC;GAGtB,IAAI,MAAM,WAAW,GAAG,GACtB,OAAO;GAIT,IAAI,CAAC,mBAAmB,KAAK,KAAK,GAChC,OAAO;GAGT,OAAO,IAAI;EACb;EACA,OAAO;CACT;CAEA,AAAQ,yBAAyB,OAAe,WAAwB;EACtE,IAAI,cAAc,SAChB,OAAO,KAAK,yBAAyB,KAAK;EAY5C,MAAM,WAAW;GARf,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK;GACL,OAAO;GACP,MAAM;EAGW,EAAE;EACrB,IAAI,CAAC,UACH,OAAO;EAGT,OAAO,GACJ,WAAW,KAAK,wBAAwB,KAAK,EAChD;CACF;CAEA,AAAQ,sBAAsB,OAAoB;EAChD,OAAO,EACL,KAAK,CAAC,EAAE,OAAO,IAAI,QAAQ,GAAG,SAAS,EACzC;CACF;CAEA,AAAQ,yBAAyB,OAAoB;EACnD,OAAO,EACL,OAAO,EAAE,SAAS,CAAC,IAAI,SAAS,CAAC,CAAC,EAAE,EACtC;CACF;CAEA,AAAQ,oBACN,OACA,WACK;EACL,OAAO,EACL,SAAS;GACP,UAAU,MAAM,KAAK,UAAU;IAC7B,MAAM,KAAK,4BAA4B,KAAK,IAAI;IAChD,MAAM,KAAK,2BAA2B,KAAK,IAAI;GACjD,EAAE;GACF,SAAS,KAAK,2BAA2B,SAAS;EACpD,EACF;CACF;CAEA,AAAQ,oBACN,WACA,WACA,WACK;EACL,OAAO,EACL,OAAO;GACL,KAAK,4BAA4B,SAAS;GAC1C,KAAK,2BAA2B,SAAS;GACzC,KAAK,2BAA2B,SAAS;EAC3C,EACF;CACF;;;;;CAMA,AAAQ,2BAA2B,OAAqC;EAEtE,IAAI,OAAO,UAAU,YAAY,MAAM,WAAW,GAAG,GACnD,OAAO;EAGT,IAAI,OAAO,UAAU,UACnB,OAAO;EAGT,OAAO,KAAK,4BAA4B,KAAK;CAC/C;CAEA,AAAQ,eAAe,MAAsB;EAE3C,MAAM,WADa,KAAK,cAAc,IACZ,EAAE,MAAM,GAAG;EACrC,OAAO,SAAS,SAAS,SAAS;CACpC;CAEA,AAAQ,sBAAsB,QAA4C;EACxE,OAAO,EACL,SAAS,OAAO,KAAK,UAAU,KAAK,wBAAwB,KAAK,CAAC,EACpE;CACF;CAEA,AAAQ,wBAAwB,QAA4C;EAC1E,IAAI,OAAO,WAAW,GACpB,OAAO;EAGT,IAAI,aAAa,KAAK,wBAAwB,OAAO,OAAO,SAAS,EAAE;EAEvE,KAAK,IAAI,QAAQ,OAAO,SAAS,GAAG,SAAS,GAAG,SAC9C,aAAa,EACX,SAAS,CAAC,KAAK,wBAAwB,OAAO,MAAM,GAAG,UAAU,EACnE;EAGF,OAAO;CACT;;;;;;;CAQA,AAAQ,kBAAkB,YAA8B;EACtD,IAAI,WAAW,WAAW,GACxB,OAAO;EAGT,MAAM,aAAsC,CAAC;EAC7C,MAAM,kBAAwE,CAAC;EAE/E,KAAK,MAAM,MAAM,YACf,QAAQ,GAAG,MAAX;GACE,KAAK;IAEH,IAAI,GAAG,KAAK,YACV,KAAK,sBAAsB,YAAY,GAAG,KAAK,UAAU;SACpD,IAAI,GAAG,KAAK,QACjB,KAAK,sBAAsB,YAAY,GAAG,KAAK,QAAQ,CAAC;IAE1D;GAEF,KAAK;IACH,KAAK,sBAAsB,YAAY,GAAG,KAAK,QAAQ,CAAC;IACxD;GAEF,KAAK;IACH,KAAK,sBAAsB,YAAY,GAAG,KAAK,QAAQ,CAAC;IACxD;GAEF,KAAK;IACH,KAAK,mBAAmB,YAAY,GAAG,KAAK,YAAY,GAAG,KAAK,QAAQ;IACxE;GAEF,KAAK;GACL,KAAK,gBAAgB;IACnB,MAAM,OAAO,KAAK,4BAA4B,GAAG,KAAK,YAAY,GAAG,KAAK,QAAQ;IAClF,IAAI,SAAS,QACX,WAAW,GAAG,KAAK,SAAS;IAE9B;GACF;GAEA,KAAK;IACH,WAAW,GAAG,KAAK,SAAS,KAAK,yBAC/B,GAAG,KAAK,OACR,GAAG,KAAK,SACV;IACA;GAEF,KAAK;IACH,WAAW,GAAG,KAAK,SAAS,KAAK,sBAAsB,GAAG,KAAK,KAAK;IACpE;GAEF,KAAK;IACH,WAAW,GAAG,KAAK,SAAS,KAAK,yBAAyB,GAAG,KAAK,KAAK;IACvE;GAEF,KAAK;IACH,WAAW,GAAG,KAAK,SAAS,KAAK,oBAC/B,GAAG,KAAK,OACR,GAAG,KAAK,SACV;IACA;GAEF,KAAK;IACH,WAAW,GAAG,KAAK,SAAS,KAAK,oBAC/B,GAAG,KAAK,WACR,GAAG,KAAK,WACR,GAAG,KAAK,SACV;IACA;GAEF,KAAK;IACH,gBAAgB,KAAK,GAAG,KAAK,QAAQ;IACrC;GAEF,KAAK,cAAc;IACjB,MAAM,QAAQ,GAAG,KAAK,SAAS,KAAK,eAAe,GAAG,KAAK,IAAI;IAC/D,WAAW,SAAS,KAAK,wBACvB,IAAI,KAAK,cAAc,GAAG,KAAK,IAAI,GACrC;IACA;GACF;GAEA,KAAK;IACH,WAAW,GAAG,KAAK,SAAS,KAAK,4BAA4B,GAAG,KAAK,UAAU;IAC/E;GAGF,KAAK;IACH,WAAW,KAAK,cAAc,GAAG,KAAK,IAAI,KAAK;IAC/C;GAEF,KAAK;IACH,WAAW,GAAG,KAAK,SAAS,KAAK,sBAAsB,GAAG,KAAK,MAAM;IACrE;GAEF,KAAK;IACH,WAAW,GAAG,KAAK,SAAS,KAAK,wBAAwB,GAAG,KAAK,MAAM;IACvE;GAEF,SACE;EACJ;EAGF,KAAK,MAAM,YAAY,iBACrB,SAAS,UAAU;EAGrB,OAAO,OAAO,KAAK,UAAU,EAAE,SAAS,IAAI,EAAE,UAAU,WAAW,IAAI;CACzE;;;;;;;CAQA,AAAQ,eAAe,YAA8B;EACnD,MAAM,OAAY,CAAC;EAEnB,KAAK,MAAM,MAAM,YACf,QAAQ,GAAG,MAAX;GACE,KAAK;IACH,KAAK,GAAG,KAAK,SAAS,GAAG,KAAK,cAAc,QAAQ,IAAI;IACxD;GAEF,KAAK,iBACH,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,KAAK,MAAM,EAAE;GAE5C,KAAK,cAEH;EACJ;EAGF,OAAO,OAAO,KAAK,IAAI,EAAE,SAAS,IAAI,EAAE,OAAO,KAAK,IAAI;CAC1D;;;;;;;CAQA,AAAQ,gBAAgB,YAA8B;EACpD,MAAM,KAAK,WAAW;EAEtB,QAAQ,GAAG,MAAX;GACE,KAAK,WAAW;IACd,MAAM,QAAQ,KAAK,kBAAkB,GAAG,KAAK,MAAM;IACnD,IAAI,OACF,OAAO;IAET;GACF;GACA,KAAK,yBAAyB;IAC5B,MAAM,QAAQ,KAAK,gCACjB,GAAG,KAAK,QACR,GAAG,KAAK,UACV;IACA,IAAI,OACF,OAAO;IAET;GACF;GACA,KAAK,eAAe;IAClB,MAAM,QAAQ,KAAK,sBACjB,GAAG,KAAK,QACR,GAAG,KAAK,MACR,GAAG,KAAK,cAAc,CAAC,CACzB;IACA,IAAI,OACF,OAAO;IAET;GACF;GACA,KAAK,cAAc;IACjB,MAAM,aAAa,GAAG,KAAK;IAC3B,IAAI,cAAc,OAAO,eAAe,UACtC,OAAO,EAAE,QAAQ,WAAW;IAI9B,IAAI,YACF,OAAO,EAAE,QAAQ,EAAE,KAAK,WAAW,EAAE;IAEvC;GACF;GACA,KAAK,YAAY;IACf,MAAM,QAAQ,KAAK,kBAAkB,GAAG,KAAK,MAAM;IACnD,IAAI,OACF,OAAO;IAET;GACF;GACA,SACE;EACJ;EAEA,OAAO;CACT;CAEA,AAAQ,kBAAkB,QAA2B;EACnD,MAAM,UAAU,KAAK,aAAa,MAAM;EACxC,IAAI,CAAC,SACH,OAAO;EAGT,OAAO,EAAE,QAAQ,EAAE,KAAK,QAAQ,EAAE;CACpC;;;;;;;;CASA,AAAQ,gCACN,QACA,YACK;EACL,MAAM,UAAU,KAAK,aAAa,MAAM;EACxC,IAAI,CAAC,SACH,OAAO;EAGT,MAAM,aAAsC,EAC1C,KAAK,QACP;EAGA,KAAK,MAAM,CAAC,OAAO,eAAe,OAAO,QAAQ,UAAU,GACzD,IAAI,sBAAsB,UAAU,GAElC,WAAW,SAAS,KAAK,6BAA6B,UAAU;OAGhE,WAAW,SAAS;EAIxB,OAAO,EAAE,QAAQ,WAAW;CAC9B;;;;;;;;;;CAWA,AAAQ,sBACN,QACA,MACA,YACK;EACL,MAAM,aAAsC,EAC1C,KAAK,EAAE,YAAY;GAAE,MAAM,IAAI;GAAU;EAAK,EAAE,EAClD;EAEA,KAAK,MAAM,CAAC,OAAO,eAAe,OAAO,QAAQ,UAAU,GACzD,IAAI,sBAAsB,UAAU,GAClC,WAAW,SAAS,KAAK,6BAA6B,UAAU;OAEhE,WAAW,SAAS;EAIxB,OAAO,EAAE,QAAQ,WAAW;CAC9B;;;;;;;CAQA,AAAQ,uBAAuB,QAAgD;EAC7E,IAAI,OAAO,WAAW,UACpB,OAAO;EAGT,IAAI,MAAM,QAAQ,MAAM,GAAG;GAEzB,IADmB,OAAO,OAAO,UAAU,OAAO,UAAU,QAC/C,GACX,OAAO;GAGT,OAAO;EACT;EAEA,IAAI,OAAO,WAAW,YAAY,WAAW,MAE3C,OAAO,OAAO,KAAK,MAAM;EAG3B,OAAO;CACT;;;;;;;CAQA,AAAQ,6BAA6B,MAAoD;EACvF,QAAQ,KAAK,OAAb;GACE,KAAK,SACH,OAAO,EAAE,MAAM,EAAE;GAEnB,KAAK;IACH,IAAI,CAAC,KAAK,SACR,MAAM,IAAI,MAAM,gDAAgD;IAMlE,OAAO,EAAE,WAAW,IAAI,KAAK,UAAU;GAEzC,KAAK;IAIH,IAAI,KAAK,QACP,OAAO,EAAE,MAAM,KAAK,wBAAwB,KAAK,MAAM,EAAE;IAE3D,IAAI,CAAC,KAAK,SACR,MAAM,IAAI,MAAM,qCAAqC;IAEvD,OAAO,EAAE,MAAM,IAAI,KAAK,UAAU;GAEpC,KAAK;IACH,IAAI,CAAC,KAAK,SACR,MAAM,IAAI,MAAM,yCAAyC;IAE3D,OAAO,EAAE,MAAM,IAAI,KAAK,UAAU;GAEpC,KAAK;IACH,IAAI,CAAC,KAAK,SACR,MAAM,IAAI,MAAM,qCAAqC;IAEvD,OAAO,EAAE,MAAM,IAAI,KAAK,UAAU;GAEpC,KAAK;IACH,IAAI,CAAC,KAAK,SACR,MAAM,IAAI,MAAM,qCAAqC;IAEvD,OAAO,EAAE,MAAM,IAAI,KAAK,UAAU;GAEpC,KAAK;IACH,IAAI,CAAC,KAAK,SACR,MAAM,IAAI,MAAM,uCAAuC;IAEzD,OAAO,EAAE,QAAQ,IAAI,KAAK,UAAU;GAEtC,KAAK;IACH,IAAI,CAAC,KAAK,SACR,MAAM,IAAI,MAAM,sCAAsC;IAExD,OAAO,EAAE,OAAO,IAAI,KAAK,UAAU;GAErC,KAAK;IACH,IAAI,CAAC,KAAK,SACR,MAAM,IAAI,MAAM,0CAA0C;IAE5D,OAAO,EAAE,WAAW,IAAI,KAAK,UAAU;GAEzC,KAAK;IACH,IAAI,CAAC,KAAK,SACR,MAAM,IAAI,MAAM,uCAAuC;IAGzD,OAAO,EAAE,QAAQ,IAAI,KAAK,UAAU;GAEtC,SACE,MAAM,IAAI,MAAM,+BAA+B,KAAK,OAAO;EAC/D;CACF;;;;;;;;;;;;;CAcA,AAAQ,wBAAwB,YAAuC;EACrE,QAAQ,WAAW,QAAnB;GACE,KAAK,UACH,OAAO,IAAI,WAAW;GAExB,KAAK,WACH,OAAO,WAAW;GAEpB,KAAK,OACH,MAAM,IAAI,MACR,8OAGF;GAEF,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK,UAQH,OAAO,GAPU;IACf,KAAK;IACL,UAAU;IACV,UAAU;IACV,QAAQ;GACV,EAAE,WAAW,UAGC,WAAW,SAAS,KAAK,YAAY,KAAK,wBAAwB,OAAO,CAAC,EACxF;GAGF,SACE,MAAM,IAAI,MAAM,uCAAuC,KAAK,UAAU,UAAU,GAAG;EACvF;CACF;CAEA,AAAQ,aAAa,QAA2B;EAC9C,IAAI,CAAC,QACH,OAAO;EAGT,IAAI,OAAO,WAAW,UACpB,OAAO,IAAI;EAGb,IAAI,MAAM,QAAQ,MAAM,GAAG;GACzB,IAAI,OAAO,WAAW,GACpB,OAAO;GAIT,IADmB,OAAO,OAAO,UAAU,OAAO,UAAU,QAC/C,GAAG;IACd,MAAM,SAAiC,CAAC;IACxC,KAAK,MAAM,SAAS,QAClB,OAAO,SAAS,IAAI;IAEtB,OAAO;GACT;GAGA,OAAQ,OAAqC,QAAQ,KAAK,UAAU;IAAE,GAAG;IAAK,GAAG;GAAK,IAAI,CAAC,CAAC;EAC9F;EAEA,IAAI,OAAO,WAAW,UAAU;GAC9B,MAAM,aAAsC,CAAC;GAC7C,OAAO,QAAQ,MAAM,EAAE,SAAS,CAAC,KAAK,WAAW;IAC/C,IAAI,OAAO,UAAU,YAAY,CAAC,MAAM,WAAW,GAAG,GACpD,WAAW,OAAO,IAAI;SAEtB,WAAW,OAAO;GAEtB,CAAC;GACD,OAAO;EACT;EAEA,OAAO;CACT;;;;;;;CAQA,AAAQ,iBAAiB,YAA8B;EAErD,MAAM,UADK,WAAW,GACH;EAEnB,OAAO,EACL,SAAS;GACP,MAAM,QAAQ;GACd,YAAY,QAAQ;GACpB,cAAc,QAAQ;GACtB,IAAI,QAAQ,SAAS,QAAQ;EAC/B,EACF;CACF;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mongodb-sync-adapter.mjs","names":[],"sources":["../../../../../../../../cascade/src/drivers/mongodb/mongodb-sync-adapter.ts"],"sourcesContent":["/**\n * MongoDB-specific sync adapter implementation.\n *\n * @module cascade-next/drivers/mongodb/mongodb-sync-adapter\n */\n\nimport type { SyncAdapterContract, SyncInstruction } from \"../../contracts/sync-adapter.contract\";\nimport type { MongoDbDriver } from \"./mongodb-driver\";\n\n/**\n * MongoDB implementation of the sync adapter.\n * Handles array updates using positional operators and arrayFilters.\n * Automatically participates in active transactions via the driver's session context.\n */\nexport class MongoSyncAdapter implements SyncAdapterContract {\n private readonly driver: MongoDbDriver;\n\n /**\n * Creates a new MongoDB sync adapter.\n *\n * @param driver - The MongoDB driver instance (provides session-aware operations)\n */\n public constructor(driver: MongoDbDriver) {\n this.driver = driver;\n }\n\n /**\n * Executes a batch of sync instructions.\n *\n * @param instructions - Array of sync instructions\n * @returns Total number of documents affected\n */\n public async executeBatch(instructions: SyncInstruction[]): Promise<number> {\n let totalAffected = 0;\n\n for (const instruction of instructions) {\n const affected = instruction.isArrayUpdate\n ? await this.executeArrayUpdate(instruction)\n : await this.executeOne(instruction);\n\n totalAffected += affected;\n }\n\n return totalAffected;\n }\n\n /**\n * Executes a single sync instruction.\n * Uses the driver's updateMany to automatically participate in active transactions.\n *\n * @param instruction - The sync instruction\n * @returns Number of documents affected\n */\n public async executeOne(instruction: SyncInstruction): Promise<number> {\n const result = await this.driver.updateMany(\n instruction.targetTable,\n instruction.filter,\n instruction.update,\n );\n\n return result.modifiedCount;\n }\n\n /**\n * Executes an array update using MongoDB positional operators.\n * Uses the driver's updateMany to automatically participate in active transactions.\n *\n * @param instruction - The sync instruction with array info\n * @returns Number of documents affected\n */\n public async executeArrayUpdate(instruction: SyncInstruction): Promise<number> {\n if (!instruction.arrayField || !instruction.identifierField) {\n throw new Error(\"Array update requires arrayField and identifierField to be specified\");\n }\n\n // Strategy 1: Try positional operator $ (simpler, faster)\n // Works when filter already identifies the array element\n if (this.canUsePositionalOperator(instruction)) {\n const result = await this.driver.updateMany(\n instruction.targetTable,\n instruction.filter,\n instruction.update,\n );\n return result.modifiedCount;\n }\n\n // Strategy 2: Use arrayFilters (more flexible)\n // Works for any array update scenario\n return await this.executeWithArrayFilters(instruction);\n }\n\n /**\n * Checks if positional operator $ can be used.\n * Requires filter to already match the array element.\n *\n * @param instruction - The sync instruction\n * @returns True if positional operator can be used\n */\n private canUsePositionalOperator(instruction: SyncInstruction): boolean {\n // Check if filter already includes array element match\n const arrayElementFilter = `${instruction.arrayField}.${instruction.identifierField}`;\n return arrayElementFilter in instruction.filter;\n }\n\n /**\n * Executes array update using arrayFilters.\n * Uses the driver's updateMany with arrayFilters option to participate in transactions.\n *\n * @param instruction - The sync instruction\n * @returns Number of documents affected\n */\n private async executeWithArrayFilters(instruction: SyncInstruction): Promise<number> {\n // Build arrayFilters to match array elements\n const arrayFilters = [\n {\n [`elem.${instruction.identifierField}`]: instruction.identifierValue,\n },\n ];\n\n // Transform update to use array filter placeholder\n const transformedUpdate = this.transformUpdateForArrayFilters(\n instruction.update,\n instruction.arrayField!,\n );\n\n // Build optimized filter to reduce documents scanned\n const optimizedFilter = this.buildOptimizedFilter(\n instruction.filter,\n instruction.arrayField!,\n instruction.identifierField!,\n );\n\n // Use driver's updateMany (session-aware) with arrayFilters\n const result = await this.driver.updateMany(\n instruction.targetTable,\n optimizedFilter,\n transformedUpdate,\n { arrayFilters },\n );\n\n return result.modifiedCount;\n }\n\n /**\n * Builds an optimized filter to reduce the number of documents scanned.\n * Adds array existence check when filter doesn't already match array elements.\n *\n * @param originalFilter - The original filter from the instruction\n * @param arrayField - The array field path\n * @param identifierField - The identifier field within array elements\n * @returns Optimized filter\n */\n private buildOptimizedFilter(\n originalFilter: Record<string, unknown>,\n arrayField: string,\n identifierField: string,\n ): Record<string, unknown> {\n // If filter already has array element match, use it as-is\n const arrayElementFilter = `${arrayField}.${identifierField}`;\n if (arrayElementFilter in originalFilter) {\n return originalFilter;\n }\n\n // Otherwise, add array existence check to avoid full collection scan\n return {\n ...originalFilter,\n [arrayField]: { $exists: true, $ne: [] }, // Array exists and not empty\n };\n }\n\n /**\n * Transforms update operation to use arrayFilters placeholder.\n *\n * @param update - Original update operation\n * @param arrayField - Array field path\n * @returns Transformed update operation\n */\n private transformUpdateForArrayFilters(\n update: Record<string, unknown>,\n arrayField: string,\n ): Record<string, unknown> {\n const transformed: Record<string, unknown> = {};\n\n for (const [operator, fields] of Object.entries(update)) {\n if (typeof fields === \"object\" && fields !== null) {\n const transformedFields: Record<string, unknown> = {};\n\n for (const [field, value] of Object.entries(fields)) {\n // Replace positional $ with arrayFilters placeholder $[elem]\n const transformedField = field.replace(`${arrayField}.$`, `${arrayField}.$[elem]`);\n transformedFields[transformedField] = value;\n }\n\n transformed[operator] = transformedFields;\n }\n }\n\n return transformed;\n }\n}\n"],"mappings":";;;;;;AAcA,IAAa,mBAAb,MAA6D;CAC3D,AAAiB;;;;;;CAOjB,AAAO,YAAY,QAAuB;EACxC,KAAK,SAAS;CAChB;;;;;;;CAQA,MAAa,aAAa,cAAkD;EAC1E,IAAI,gBAAgB;EAEpB,KAAK,MAAM,eAAe,cAAc;GACtC,MAAM,WAAW,YAAY,gBACzB,MAAM,KAAK,mBAAmB,WAAW,IACzC,MAAM,KAAK,WAAW,WAAW;GAErC,iBAAiB;EACnB;EAEA,OAAO;CACT;;;;;;;;CASA,MAAa,WAAW,aAA+C;EAOrE,QAAO,MANc,KAAK,OAAO,WAC/B,YAAY,aACZ,YAAY,QACZ,YAAY,MACd,
|
|
1
|
+
{"version":3,"file":"mongodb-sync-adapter.mjs","names":[],"sources":["../../../../../../../../cascade/src/drivers/mongodb/mongodb-sync-adapter.ts"],"sourcesContent":["/**\n * MongoDB-specific sync adapter implementation.\n *\n * @module cascade-next/drivers/mongodb/mongodb-sync-adapter\n */\n\nimport type { SyncAdapterContract, SyncInstruction } from \"../../contracts/sync-adapter.contract\";\nimport type { MongoDbDriver } from \"./mongodb-driver\";\n\n/**\n * MongoDB implementation of the sync adapter.\n * Handles array updates using positional operators and arrayFilters.\n * Automatically participates in active transactions via the driver's session context.\n */\nexport class MongoSyncAdapter implements SyncAdapterContract {\n private readonly driver: MongoDbDriver;\n\n /**\n * Creates a new MongoDB sync adapter.\n *\n * @param driver - The MongoDB driver instance (provides session-aware operations)\n */\n public constructor(driver: MongoDbDriver) {\n this.driver = driver;\n }\n\n /**\n * Executes a batch of sync instructions.\n *\n * @param instructions - Array of sync instructions\n * @returns Total number of documents affected\n */\n public async executeBatch(instructions: SyncInstruction[]): Promise<number> {\n let totalAffected = 0;\n\n for (const instruction of instructions) {\n const affected = instruction.isArrayUpdate\n ? await this.executeArrayUpdate(instruction)\n : await this.executeOne(instruction);\n\n totalAffected += affected;\n }\n\n return totalAffected;\n }\n\n /**\n * Executes a single sync instruction.\n * Uses the driver's updateMany to automatically participate in active transactions.\n *\n * @param instruction - The sync instruction\n * @returns Number of documents affected\n */\n public async executeOne(instruction: SyncInstruction): Promise<number> {\n const result = await this.driver.updateMany(\n instruction.targetTable,\n instruction.filter,\n instruction.update,\n );\n\n return result.modifiedCount;\n }\n\n /**\n * Executes an array update using MongoDB positional operators.\n * Uses the driver's updateMany to automatically participate in active transactions.\n *\n * @param instruction - The sync instruction with array info\n * @returns Number of documents affected\n */\n public async executeArrayUpdate(instruction: SyncInstruction): Promise<number> {\n if (!instruction.arrayField || !instruction.identifierField) {\n throw new Error(\"Array update requires arrayField and identifierField to be specified\");\n }\n\n // Strategy 1: Try positional operator $ (simpler, faster)\n // Works when filter already identifies the array element\n if (this.canUsePositionalOperator(instruction)) {\n const result = await this.driver.updateMany(\n instruction.targetTable,\n instruction.filter,\n instruction.update,\n );\n return result.modifiedCount;\n }\n\n // Strategy 2: Use arrayFilters (more flexible)\n // Works for any array update scenario\n return await this.executeWithArrayFilters(instruction);\n }\n\n /**\n * Checks if positional operator $ can be used.\n * Requires filter to already match the array element.\n *\n * @param instruction - The sync instruction\n * @returns True if positional operator can be used\n */\n private canUsePositionalOperator(instruction: SyncInstruction): boolean {\n // Check if filter already includes array element match\n const arrayElementFilter = `${instruction.arrayField}.${instruction.identifierField}`;\n return arrayElementFilter in instruction.filter;\n }\n\n /**\n * Executes array update using arrayFilters.\n * Uses the driver's updateMany with arrayFilters option to participate in transactions.\n *\n * @param instruction - The sync instruction\n * @returns Number of documents affected\n */\n private async executeWithArrayFilters(instruction: SyncInstruction): Promise<number> {\n // Build arrayFilters to match array elements\n const arrayFilters = [\n {\n [`elem.${instruction.identifierField}`]: instruction.identifierValue,\n },\n ];\n\n // Transform update to use array filter placeholder\n const transformedUpdate = this.transformUpdateForArrayFilters(\n instruction.update,\n instruction.arrayField!,\n );\n\n // Build optimized filter to reduce documents scanned\n const optimizedFilter = this.buildOptimizedFilter(\n instruction.filter,\n instruction.arrayField!,\n instruction.identifierField!,\n );\n\n // Use driver's updateMany (session-aware) with arrayFilters\n const result = await this.driver.updateMany(\n instruction.targetTable,\n optimizedFilter,\n transformedUpdate,\n { arrayFilters },\n );\n\n return result.modifiedCount;\n }\n\n /**\n * Builds an optimized filter to reduce the number of documents scanned.\n * Adds array existence check when filter doesn't already match array elements.\n *\n * @param originalFilter - The original filter from the instruction\n * @param arrayField - The array field path\n * @param identifierField - The identifier field within array elements\n * @returns Optimized filter\n */\n private buildOptimizedFilter(\n originalFilter: Record<string, unknown>,\n arrayField: string,\n identifierField: string,\n ): Record<string, unknown> {\n // If filter already has array element match, use it as-is\n const arrayElementFilter = `${arrayField}.${identifierField}`;\n if (arrayElementFilter in originalFilter) {\n return originalFilter;\n }\n\n // Otherwise, add array existence check to avoid full collection scan\n return {\n ...originalFilter,\n [arrayField]: { $exists: true, $ne: [] }, // Array exists and not empty\n };\n }\n\n /**\n * Transforms update operation to use arrayFilters placeholder.\n *\n * @param update - Original update operation\n * @param arrayField - Array field path\n * @returns Transformed update operation\n */\n private transformUpdateForArrayFilters(\n update: Record<string, unknown>,\n arrayField: string,\n ): Record<string, unknown> {\n const transformed: Record<string, unknown> = {};\n\n for (const [operator, fields] of Object.entries(update)) {\n if (typeof fields === \"object\" && fields !== null) {\n const transformedFields: Record<string, unknown> = {};\n\n for (const [field, value] of Object.entries(fields)) {\n // Replace positional $ with arrayFilters placeholder $[elem]\n const transformedField = field.replace(`${arrayField}.$`, `${arrayField}.$[elem]`);\n transformedFields[transformedField] = value;\n }\n\n transformed[operator] = transformedFields;\n }\n }\n\n return transformed;\n }\n}\n"],"mappings":";;;;;;AAcA,IAAa,mBAAb,MAA6D;CAC3D,AAAiB;;;;;;CAOjB,AAAO,YAAY,QAAuB;EACxC,KAAK,SAAS;CAChB;;;;;;;CAQA,MAAa,aAAa,cAAkD;EAC1E,IAAI,gBAAgB;EAEpB,KAAK,MAAM,eAAe,cAAc;GACtC,MAAM,WAAW,YAAY,gBACzB,MAAM,KAAK,mBAAmB,WAAW,IACzC,MAAM,KAAK,WAAW,WAAW;GAErC,iBAAiB;EACnB;EAEA,OAAO;CACT;;;;;;;;CASA,MAAa,WAAW,aAA+C;EAOrE,QAAO,MANc,KAAK,OAAO,WAC/B,YAAY,aACZ,YAAY,QACZ,YAAY,MACd,GAEc;CAChB;;;;;;;;CASA,MAAa,mBAAmB,aAA+C;EAC7E,IAAI,CAAC,YAAY,cAAc,CAAC,YAAY,iBAC1C,MAAM,IAAI,MAAM,sEAAsE;EAKxF,IAAI,KAAK,yBAAyB,WAAW,GAM3C,QAAO,MALc,KAAK,OAAO,WAC/B,YAAY,aACZ,YAAY,QACZ,YAAY,MACd,GACc;EAKhB,OAAO,MAAM,KAAK,wBAAwB,WAAW;CACvD;;;;;;;;CASA,AAAQ,yBAAyB,aAAuC;EAGtE,OAAO,GADuB,YAAY,WAAW,GAAG,YAAY,qBACvC,YAAY;CAC3C;;;;;;;;CASA,MAAc,wBAAwB,aAA+C;EAEnF,MAAM,eAAe,CACnB,GACG,QAAQ,YAAY,oBAAoB,YAAY,gBACvD,CACF;EAGA,MAAM,oBAAoB,KAAK,+BAC7B,YAAY,QACZ,YAAY,UACd;EAGA,MAAM,kBAAkB,KAAK,qBAC3B,YAAY,QACZ,YAAY,YACZ,YAAY,eACd;EAUA,QAAO,MAPc,KAAK,OAAO,WAC/B,YAAY,aACZ,iBACA,mBACA,EAAE,aAAa,CACjB,GAEc;CAChB;;;;;;;;;;CAWA,AAAQ,qBACN,gBACA,YACA,iBACyB;EAGzB,IAAI,GAD0B,WAAW,GAAG,qBAClB,gBACxB,OAAO;EAIT,OAAO;GACL,GAAG;IACF,aAAa;IAAE,SAAS;IAAM,KAAK,CAAC;GAAE;EACzC;CACF;;;;;;;;CASA,AAAQ,+BACN,QACA,YACyB;EACzB,MAAM,cAAuC,CAAC;EAE9C,KAAK,MAAM,CAAC,UAAU,WAAW,OAAO,QAAQ,MAAM,GACpD,IAAI,OAAO,WAAW,YAAY,WAAW,MAAM;GACjD,MAAM,oBAA6C,CAAC;GAEpD,KAAK,MAAM,CAAC,OAAO,UAAU,OAAO,QAAQ,MAAM,GAAG;IAEnD,MAAM,mBAAmB,MAAM,QAAQ,GAAG,WAAW,KAAK,GAAG,WAAW,SAAS;IACjF,kBAAkB,oBAAoB;GACxC;GAEA,YAAY,YAAY;EAC1B;EAGF,OAAO;CACT;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres-blueprint.d.mts","names":[],"sources":["../../../../../../../../cascade/src/drivers/postgres/postgres-blueprint.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;cAgCa,iBAAA,YAA6B,uBAAA;EAAA,iBAMJ,MAAA;EA6EK;;;;;cA7EL,MAAA,EAAQ,cAAA;;;;;;EAO/B,UAAA,
|
|
1
|
+
{"version":3,"file":"postgres-blueprint.d.mts","names":[],"sources":["../../../../../../../../cascade/src/drivers/postgres/postgres-blueprint.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;cAgCa,iBAAA,YAA6B,uBAAA;EAAA,iBAMJ,MAAA;EA6EK;;;;;cA7EL,MAAA,EAAQ,cAAA;;;;;;EAO/B,UAAA,CAAA,GAAc,OAAA;;;;;;;EAkBd,WAAA,CAAY,KAAA,WAAgB,OAAA,CAAQ,qBAAA;;;;;;;EAoDpC,WAAA,CAAY,KAAA,WAAgB,OAAA;;;;;;;EAmB5B,WAAA,CAAY,KAAA,WAAgB,OAAA;AAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres-blueprint.mjs","names":[],"sources":["../../../../../../../../cascade/src/drivers/postgres/postgres-blueprint.ts"],"sourcesContent":["/**\n * PostgreSQL Blueprint\n *\n * Implements the DriverBlueprintContract for querying PostgreSQL\n * information schema metadata.\n *\n * @module cascade/drivers/postgres\n */\n\nimport type {\n DriverBlueprintContract,\n TableIndexInformation,\n} from \"../../contracts/driver-blueprint.contract\";\nimport type { PostgresDriver } from \"./postgres-driver\";\n\n/**\n * PostgreSQL Blueprint.\n *\n * Provides methods for introspecting the database schema\n * via PostgreSQL's information_schema and pg_catalog.\n *\n * @example\n * ```typescript\n * const blueprint = driver.blueprint;\n *\n * // Get all tables\n * const tables = await blueprint.listTables();\n *\n * // Get columns for a table\n * const columns = await blueprint.listColumns('users');\n * ```\n */\nexport class PostgresBlueprint implements DriverBlueprintContract {\n /**\n * Create a new blueprint.\n *\n * @param driver - The PostgreSQL driver instance\n */\n public constructor(private readonly driver: PostgresDriver) {}\n\n /**\n * Get all table names in the database.\n *\n * @returns Array of table names\n */\n public async listTables(): Promise<string[]> {\n const result = await this.driver.query<{ table_name: string }>(\n `SELECT table_name \n FROM information_schema.tables \n WHERE table_schema = 'public' \n AND table_type = 'BASE TABLE'\n ORDER BY table_name`,\n );\n\n return result.rows.map((row) => row.table_name);\n }\n\n /**\n * Get all indexes for a table.\n *\n * @param table - Table name\n * @returns Array of index information\n */\n public async listIndexes(table: string): Promise<TableIndexInformation[]> {\n const result = await this.driver.query<{\n indexname: string;\n indexdef: string;\n }>(\n `SELECT indexname, indexdef\n FROM pg_indexes\n WHERE schemaname = 'public'\n AND tablename = $1`,\n [table],\n );\n\n return result.rows.map((row) => {\n const isUnique = row.indexdef.includes(\"UNIQUE\");\n const isPrimary = row.indexname.endsWith(\"_pkey\");\n\n // Extract columns from indexdef\n const columnsMatch = row.indexdef.match(/\\(([^)]+)\\)/);\n const columns = columnsMatch\n ? columnsMatch[1].split(\",\").map((c) => c.trim().replace(/\"/g, \"\"))\n : [];\n\n // Determine index type\n let type = \"btree\"; // default\n if (row.indexdef.includes(\"USING GIN\")) type = \"gin\";\n else if (row.indexdef.includes(\"USING GIST\")) type = \"gist\";\n else if (row.indexdef.includes(\"USING HASH\")) type = \"hash\";\n else if (row.indexdef.includes(\"USING ivfflat\")) type = \"ivfflat\";\n\n // Check for partial index\n const isPartial = row.indexdef.includes(\"WHERE\");\n\n return {\n name: row.indexname,\n columns,\n type,\n unique: isUnique || isPrimary,\n partial: isPartial,\n options: {\n primary: isPrimary,\n definition: row.indexdef,\n },\n };\n });\n }\n\n /**\n * Get all column names for a table.\n *\n * @param table - Table name\n * @returns Array of column names\n */\n public async listColumns(table: string): Promise<string[]> {\n const result = await this.driver.query<{ column_name: string }>(\n `SELECT column_name\n FROM information_schema.columns\n WHERE table_schema = 'public'\n AND table_name = $1\n ORDER BY ordinal_position`,\n [table],\n );\n\n return result.rows.map((row) => row.column_name);\n }\n\n /**\n * Check if a table exists.\n *\n * @param table - Table name\n * @returns Whether the table exists\n */\n public async tableExists(table: string): Promise<boolean> {\n const result = await this.driver.query<{ exists: boolean }>(\n `SELECT EXISTS (\n SELECT FROM information_schema.tables \n WHERE table_schema = 'public' \n AND table_name = $1\n )`,\n [table],\n );\n\n return result.rows[0]?.exists ?? false;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAgCA,IAAa,oBAAb,MAAkE;CAM5B;;;;;;CAApC,AAAO,YAAY,AAAiB,QAAwB;EAAxB;CAAyB;;;;;;CAO7D,MAAa,aAAgC;EAS3C,QAAO,MARc,KAAK,OAAO,MAC/B;;;;2BAKF,
|
|
1
|
+
{"version":3,"file":"postgres-blueprint.mjs","names":[],"sources":["../../../../../../../../cascade/src/drivers/postgres/postgres-blueprint.ts"],"sourcesContent":["/**\n * PostgreSQL Blueprint\n *\n * Implements the DriverBlueprintContract for querying PostgreSQL\n * information schema metadata.\n *\n * @module cascade/drivers/postgres\n */\n\nimport type {\n DriverBlueprintContract,\n TableIndexInformation,\n} from \"../../contracts/driver-blueprint.contract\";\nimport type { PostgresDriver } from \"./postgres-driver\";\n\n/**\n * PostgreSQL Blueprint.\n *\n * Provides methods for introspecting the database schema\n * via PostgreSQL's information_schema and pg_catalog.\n *\n * @example\n * ```typescript\n * const blueprint = driver.blueprint;\n *\n * // Get all tables\n * const tables = await blueprint.listTables();\n *\n * // Get columns for a table\n * const columns = await blueprint.listColumns('users');\n * ```\n */\nexport class PostgresBlueprint implements DriverBlueprintContract {\n /**\n * Create a new blueprint.\n *\n * @param driver - The PostgreSQL driver instance\n */\n public constructor(private readonly driver: PostgresDriver) {}\n\n /**\n * Get all table names in the database.\n *\n * @returns Array of table names\n */\n public async listTables(): Promise<string[]> {\n const result = await this.driver.query<{ table_name: string }>(\n `SELECT table_name \n FROM information_schema.tables \n WHERE table_schema = 'public' \n AND table_type = 'BASE TABLE'\n ORDER BY table_name`,\n );\n\n return result.rows.map((row) => row.table_name);\n }\n\n /**\n * Get all indexes for a table.\n *\n * @param table - Table name\n * @returns Array of index information\n */\n public async listIndexes(table: string): Promise<TableIndexInformation[]> {\n const result = await this.driver.query<{\n indexname: string;\n indexdef: string;\n }>(\n `SELECT indexname, indexdef\n FROM pg_indexes\n WHERE schemaname = 'public'\n AND tablename = $1`,\n [table],\n );\n\n return result.rows.map((row) => {\n const isUnique = row.indexdef.includes(\"UNIQUE\");\n const isPrimary = row.indexname.endsWith(\"_pkey\");\n\n // Extract columns from indexdef\n const columnsMatch = row.indexdef.match(/\\(([^)]+)\\)/);\n const columns = columnsMatch\n ? columnsMatch[1].split(\",\").map((c) => c.trim().replace(/\"/g, \"\"))\n : [];\n\n // Determine index type\n let type = \"btree\"; // default\n if (row.indexdef.includes(\"USING GIN\")) type = \"gin\";\n else if (row.indexdef.includes(\"USING GIST\")) type = \"gist\";\n else if (row.indexdef.includes(\"USING HASH\")) type = \"hash\";\n else if (row.indexdef.includes(\"USING ivfflat\")) type = \"ivfflat\";\n\n // Check for partial index\n const isPartial = row.indexdef.includes(\"WHERE\");\n\n return {\n name: row.indexname,\n columns,\n type,\n unique: isUnique || isPrimary,\n partial: isPartial,\n options: {\n primary: isPrimary,\n definition: row.indexdef,\n },\n };\n });\n }\n\n /**\n * Get all column names for a table.\n *\n * @param table - Table name\n * @returns Array of column names\n */\n public async listColumns(table: string): Promise<string[]> {\n const result = await this.driver.query<{ column_name: string }>(\n `SELECT column_name\n FROM information_schema.columns\n WHERE table_schema = 'public'\n AND table_name = $1\n ORDER BY ordinal_position`,\n [table],\n );\n\n return result.rows.map((row) => row.column_name);\n }\n\n /**\n * Check if a table exists.\n *\n * @param table - Table name\n * @returns Whether the table exists\n */\n public async tableExists(table: string): Promise<boolean> {\n const result = await this.driver.query<{ exists: boolean }>(\n `SELECT EXISTS (\n SELECT FROM information_schema.tables \n WHERE table_schema = 'public' \n AND table_name = $1\n )`,\n [table],\n );\n\n return result.rows[0]?.exists ?? false;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAgCA,IAAa,oBAAb,MAAkE;CAM5B;;;;;;CAApC,AAAO,YAAY,AAAiB,QAAwB;EAAxB;CAAyB;;;;;;CAO7D,MAAa,aAAgC;EAS3C,QAAO,MARc,KAAK,OAAO,MAC/B;;;;2BAKF,GAEc,KAAK,KAAK,QAAQ,IAAI,UAAU;CAChD;;;;;;;CAQA,MAAa,YAAY,OAAiD;EAYxE,QAAO,MAXc,KAAK,OAAO,MAI/B;;;4BAIA,CAAC,KAAK,CACR,GAEc,KAAK,KAAK,QAAQ;GAC9B,MAAM,WAAW,IAAI,SAAS,SAAS,QAAQ;GAC/C,MAAM,YAAY,IAAI,UAAU,SAAS,OAAO;GAGhD,MAAM,eAAe,IAAI,SAAS,MAAM,aAAa;GACrD,MAAM,UAAU,eACZ,aAAa,GAAG,MAAM,GAAG,EAAE,KAAK,MAAM,EAAE,KAAK,EAAE,QAAQ,MAAM,EAAE,CAAC,IAChE,CAAC;GAGL,IAAI,OAAO;GACX,IAAI,IAAI,SAAS,SAAS,WAAW,GAAG,OAAO;QAC1C,IAAI,IAAI,SAAS,SAAS,YAAY,GAAG,OAAO;QAChD,IAAI,IAAI,SAAS,SAAS,YAAY,GAAG,OAAO;QAChD,IAAI,IAAI,SAAS,SAAS,eAAe,GAAG,OAAO;GAGxD,MAAM,YAAY,IAAI,SAAS,SAAS,OAAO;GAE/C,OAAO;IACL,MAAM,IAAI;IACV;IACA;IACA,QAAQ,YAAY;IACpB,SAAS;IACT,SAAS;KACP,SAAS;KACT,YAAY,IAAI;IAClB;GACF;EACF,CAAC;CACH;;;;;;;CAQA,MAAa,YAAY,OAAkC;EAUzD,QAAO,MATc,KAAK,OAAO,MAC/B;;;;mCAKA,CAAC,KAAK,CACR,GAEc,KAAK,KAAK,QAAQ,IAAI,WAAW;CACjD;;;;;;;CAQA,MAAa,YAAY,OAAiC;EAUxD,QAAO,MATc,KAAK,OAAO,MAC/B;;;;UAKA,CAAC,KAAK,CACR,GAEc,KAAK,IAAI,UAAU;CACnC;AACF"}
|