@warlock.js/cascade 5.12.0 → 5.13.0
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 +31 -0
- package/cjs/index.cjs +685 -128
- package/cjs/index.cjs.map +1 -1
- package/esm/contracts/database-driver.contract.d.mts +44 -7
- package/esm/contracts/database-driver.contract.d.mts.map +1 -1
- package/esm/contracts/index.d.mts +2 -2
- package/esm/contracts/query-builder.contract.d.mts +61 -1
- package/esm/contracts/query-builder.contract.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-driver.d.mts +8 -4
- package/esm/drivers/mongodb/mongodb-driver.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-driver.mjs +16 -6
- package/esm/drivers/mongodb/mongodb-driver.mjs.map +1 -1
- package/esm/drivers/mongodb/mongodb-query-builder.d.mts +32 -7
- package/esm/drivers/mongodb/mongodb-query-builder.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-query-builder.mjs +56 -8
- package/esm/drivers/mongodb/mongodb-query-builder.mjs.map +1 -1
- package/esm/drivers/mongodb/mongodb-query-parser.d.mts +39 -12
- package/esm/drivers/mongodb/mongodb-query-parser.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-query-parser.mjs +143 -55
- package/esm/drivers/mongodb/mongodb-query-parser.mjs.map +1 -1
- package/esm/drivers/mongodb/mongodb-update-translator.mjs +25 -0
- package/esm/drivers/mongodb/mongodb-update-translator.mjs.map +1 -0
- package/esm/drivers/mongodb/pipeline-stage-object.mjs +24 -0
- package/esm/drivers/mongodb/pipeline-stage-object.mjs.map +1 -0
- package/esm/drivers/mongodb/types.d.mts +7 -1
- package/esm/drivers/mongodb/types.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-driver.d.mts +52 -9
- package/esm/drivers/postgres/postgres-driver.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-driver.mjs +170 -38
- package/esm/drivers/postgres/postgres-driver.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-query-builder.d.mts +17 -1
- package/esm/drivers/postgres/postgres-query-builder.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-query-builder.mjs +31 -0
- package/esm/drivers/postgres/postgres-query-builder.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-update-validator.mjs +36 -0
- package/esm/drivers/postgres/postgres-update-validator.mjs.map +1 -0
- package/esm/errors/unsupported-lean-operation.error.d.mts +25 -0
- package/esm/errors/unsupported-lean-operation.error.d.mts.map +1 -0
- package/esm/errors/unsupported-lean-operation.error.mjs +31 -0
- package/esm/errors/unsupported-lean-operation.error.mjs.map +1 -0
- package/esm/errors/unsupported-query-operation.error.d.mts +30 -0
- package/esm/errors/unsupported-query-operation.error.d.mts.map +1 -0
- package/esm/errors/unsupported-query-operation.error.mjs +37 -0
- package/esm/errors/unsupported-query-operation.error.mjs.map +1 -0
- package/esm/errors/unsupported-update-operation.error.d.mts +30 -0
- package/esm/errors/unsupported-update-operation.error.d.mts.map +1 -0
- package/esm/errors/unsupported-update-operation.error.mjs +37 -0
- package/esm/errors/unsupported-update-operation.error.mjs.map +1 -0
- package/esm/index.d.mts +6 -3
- package/esm/index.mjs +4 -1
- package/esm/model/methods/query-methods.mjs +22 -6
- package/esm/model/methods/query-methods.mjs.map +1 -1
- package/esm/model/model.d.mts +28 -11
- package/esm/model/model.d.mts.map +1 -1
- package/esm/model/model.mjs +30 -13
- package/esm/model/model.mjs.map +1 -1
- package/esm/query-builder/lean-records.mjs +34 -0
- package/esm/query-builder/lean-records.mjs.map +1 -0
- package/esm/query-builder/query-builder.d.mts +13 -1
- package/esm/query-builder/query-builder.d.mts.map +1 -1
- package/esm/query-builder/query-builder.mjs +16 -0
- package/esm/query-builder/query-builder.mjs.map +1 -1
- package/llms-full.txt +72 -5
- package/llms.txt +3 -3
- package/package.json +4 -4
- package/skills/aggregate-data/SKILL.md +24 -1
- package/skills/perform-atomic-ops/SKILL.md +38 -3
- package/skills/query-data/SKILL.md +10 -1
|
@@ -71,6 +71,31 @@ declare class MongoQueryParser {
|
|
|
71
71
|
* ```
|
|
72
72
|
*/
|
|
73
73
|
parse(): any[];
|
|
74
|
+
/**
|
|
75
|
+
* Apply a raw escape-hatch operation to the pipeline built so far.
|
|
76
|
+
*
|
|
77
|
+
* - `joinRaw`: appends the caller-built stages verbatim.
|
|
78
|
+
* - `raw`: calls the callback with the pipeline array. A returned array
|
|
79
|
+
* replaces the pipeline; `undefined` keeps the (possibly mutated) array.
|
|
80
|
+
*
|
|
81
|
+
* @throws UnsupportedQueryOperationError when a `raw()` callback leaves
|
|
82
|
+
* something other than an array of stage objects
|
|
83
|
+
*/
|
|
84
|
+
private applyRawOperation;
|
|
85
|
+
/**
|
|
86
|
+
* Order a `$sort` against the select `$project` it follows.
|
|
87
|
+
*
|
|
88
|
+
* A `$sort` after a `$project` cannot see fields the projection dropped, so
|
|
89
|
+
* `select(["title"]).orderBy("likes")` would not sort. The sort moves before
|
|
90
|
+
* the projection, unless it sorts by a field the projection computes
|
|
91
|
+
* (`selectRaw({ score: ... }).orderBy("score")`), which only exists after it:
|
|
92
|
+
*
|
|
93
|
+
* - no computed sort key: `$sort`, `$project`
|
|
94
|
+
* - only computed or selected sort keys: `$project`, `$sort` (unchanged)
|
|
95
|
+
* - computed AND unselected sort keys: `$addFields` (the computed keys),
|
|
96
|
+
* `$sort`, `$project` (those keys passed through)
|
|
97
|
+
*/
|
|
98
|
+
private placeSortAroundProjection;
|
|
74
99
|
/**
|
|
75
100
|
* Reorder operations so filters run before projections, mirroring SQL
|
|
76
101
|
* semantics: in `select(...).where(...)`, the WHERE always applies to the
|
|
@@ -78,11 +103,13 @@ declare class MongoQueryParser {
|
|
|
78
103
|
* strips the filter column would run before the `$match` and silently drop
|
|
79
104
|
* every document (`select(["a"]).where("b", x)` → `[]`).
|
|
80
105
|
*
|
|
81
|
-
* Only *mergeable*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
106
|
+
* Only *mergeable* operations move, and only within a segment of neighboring
|
|
107
|
+
* mergeable `$match` / `$project` / `$sort` operations. Inside a segment the
|
|
108
|
+
* order is `$match`, then `$project`, then `$sort` (each keeping call order),
|
|
109
|
+
* and {@link placeSortAroundProjection} then decides whether the sort runs
|
|
110
|
+
* before the projection. Any other operation — `$group`, `$lookup`,
|
|
111
|
+
* `$limit`, `$skip`, `$setWindowFields`, raw escapes, having-style
|
|
112
|
+
* post-group matches, `$sample` — is a barrier: nothing moves across it.
|
|
86
113
|
* So `groupBy(...).where(...)` still filters AFTER the group, and
|
|
87
114
|
* `limit(...)` / `random()` keep their call-order meaning.
|
|
88
115
|
*/
|
|
@@ -98,15 +125,15 @@ declare class MongoQueryParser {
|
|
|
98
125
|
*/
|
|
99
126
|
private trackCountDistinctAliases;
|
|
100
127
|
/**
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
* used for grouping, making the results more intuitive.
|
|
128
|
+
* Build the `$project` that renames `_id` back to the grouped field name(s)
|
|
129
|
+
* right after a `$group` stage with tracked field names, so results carry
|
|
130
|
+
* the grouping columns instead of MongoDB's `_id`.
|
|
105
131
|
*
|
|
106
|
-
* @param
|
|
107
|
-
* @
|
|
132
|
+
* @param stage - The stage just emitted
|
|
133
|
+
* @param stageIndex - Its index in the pipeline
|
|
134
|
+
* @returns The renaming `$project` stage, or null when none is needed
|
|
108
135
|
*/
|
|
109
|
-
private
|
|
136
|
+
private buildGroupRenameStage;
|
|
110
137
|
/**
|
|
111
138
|
* Convert the parsed pipeline to a pretty-printed string for debugging.
|
|
112
139
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mongodb-query-parser.d.mts","names":[],"sources":["../../../../../../../../cascade/src/drivers/mongodb/mongodb-query-parser.ts"],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"mongodb-query-parser.d.mts","names":[],"sources":["../../../../../../../../cascade/src/drivers/mongodb/mongodb-query-parser.ts"],"mappings":";;;;;;;AAkBA;KAAY,uBAAA;6CAEV,UAAA,EAAY,UAAA,EAEA;EAAZ,UAAA,EAAY,SAAA,IAE6B;EAAzC,gBAAA,QAAwB,iBAAA;AAAA;;;;;;;AAAiB;AAW3C;cAAa,gBAAA;;;;mBAIM,UAAA;EAUA;;;EAAA,iBALA,UAAA;EA0BW;;;EAAA,iBArBX,gBAAA;EA4KT;;;;EAAA,iBAtKS,eAAA;EAsXV;;;;;;EAAA,iBA9WU,oBAAA;EAg/BT;;;;;cAz+BW,OAAA,EAAS,uBAAuB;EA2lC3C;;;;;;;;;;;;;;;;;;;;EAjkCD,KAAA;EAqxCC;;;;;;;;;;EAAA,QA3rCA,iBAAA;EAm2CA;;;;;;;;;;;;AA6dgB;EA7dhB,QAh0CA,yBAAA;;;;;;;;;;;;;;;;;;UAuDA,WAAA;;;;UAkCA,oBAAA;;;;;;UAkCA,yBAAA;;;;;;;;;;UAyBA,qBAAA;;;;;;;;;;;;;;;;;;;;;;;;EA4DD,cAAA;;;;;;;;UAmCC,eAAA;;;;;;;;UAoDA,UAAA;;;;;;;;;;;UAyEA,eAAA;EAAA,QA8KA,iBAAA;;;;;;;;;UAkBA,sBAAA;;;;;;;UA8EA,mBAAA;;;;;;;;;;;;;UAoNA,oBAAA;;;;;;;;;UAgBA,sBAAA;;;;;;;UAyBA,gBAAA;EAAA,QAYA,oBAAA;;;;;;;;UA2BA,4BAAA;EAAA,QAsBA,aAAA;EAAA,QAYA,qBAAA;EAAA,QASA,4BAAA;EAAA,QAWA,UAAA;EAAA,QAIA,0BAAA;EAAA,QAOA,wBAAA;EAAA,QAKA,uBAAA;EAAA,QAKA,kBAAA;EAAA,QAgBA,sBAAA;EAAA,QAiBA,0BAAA;EAAA,QAQA,+BAAA;EAAA,QAMA,6BAAA;EAAA,QAMA,wBAAA;EAAA,QASA,sBAAA;EAAA,QAQA,yBAAA;EAAA,QASA,kBAAA;EAAA,QAWA,UAAA;EAAA,QAMA,QAAA;EAAA,QAMA,aAAA;EAAA,QAIA,qBAAA;;;;;;UAeA,qBAAA;EAAA,QAmCA,kBAAA;EAAA,QAeA,2BAAA;EAAA,QA0BA,uBAAA;EAAA,QAoBA,wBAAA;EAAA,QAwBA,qBAAA;EAAA,QAMA,wBAAA;EAAA,QAMA,mBAAA;EAAA,QAeA,mBAAA;;;;;UAkBA,0BAAA;EAAA,QAaA,cAAA;EAAA,QAYA,qBAAA;EAAA,QAMA,uBAAA;;;;;;;UAsBA,iBAAA;;;;;;;UAgHA,cAAA;;;;;;;UA2BA,eAAA;EAAA,QA6DA,iBAAA;;;;;;;;UAgBA,+BAAA;;;;;;;;;;UAoCA,qBAAA;;;;;;;UA0BA,sBAAA;;;;;;;UA4BA,4BAAA;;;;;;;;;;;;;UAuFA,uBAAA;EAAA,QAoCA,YAAA;;;;;;;UAgDA,gBAAA;AAAA"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { UnsafeRawExpressionError } from "../../errors/unsafe-raw-expression.error.mjs";
|
|
2
|
+
import { UnsupportedQueryOperationError } from "../../errors/unsupported-query-operation.error.mjs";
|
|
2
3
|
import { isAggregateExpression } from "../../expressions/aggregate-expressions.mjs";
|
|
3
4
|
import { escapeRegex, resolveLikePattern } from "../../utils/escape-regex.mjs";
|
|
5
|
+
import { isPipelineStageObject } from "./pipeline-stage-object.mjs";
|
|
4
6
|
import { colors } from "@mongez/copper";
|
|
5
7
|
|
|
6
8
|
//#region ../cascade/src/drivers/mongodb/mongodb-query-parser.ts
|
|
@@ -68,42 +70,110 @@ var MongoQueryParser = class {
|
|
|
68
70
|
* ```
|
|
69
71
|
*/
|
|
70
72
|
parse() {
|
|
73
|
+
this.groupFieldNames.clear();
|
|
74
|
+
this.countDistinctAliases.clear();
|
|
71
75
|
const pipeline = [];
|
|
72
76
|
let currentStage = null;
|
|
73
77
|
let currentBuffer = [];
|
|
74
|
-
|
|
75
|
-
|
|
78
|
+
let trailingProjectIndex = -1;
|
|
79
|
+
const emit = (stage, operations) => {
|
|
80
|
+
if (stage === "$raw") {
|
|
81
|
+
for (const op of operations) this.applyRawOperation(pipeline, op);
|
|
82
|
+
trailingProjectIndex = -1;
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const builtStage = this.buildStage(stage, operations);
|
|
86
|
+
if (!builtStage) return;
|
|
87
|
+
if (builtStage.$sort && trailingProjectIndex >= 0 && trailingProjectIndex === pipeline.length - 1) {
|
|
88
|
+
const projectStage = pipeline.pop();
|
|
89
|
+
pipeline.push(...this.placeSortAroundProjection(projectStage, builtStage));
|
|
90
|
+
trailingProjectIndex = -1;
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const stageIndex = pipeline.length;
|
|
94
|
+
pipeline.push(builtStage);
|
|
95
|
+
trailingProjectIndex = stage === "$project" ? stageIndex : -1;
|
|
96
|
+
this.trackGroupFieldNames(stage, operations, stageIndex);
|
|
97
|
+
const renameStage = this.buildGroupRenameStage(builtStage, stageIndex);
|
|
98
|
+
if (renameStage) pipeline.push(renameStage);
|
|
99
|
+
};
|
|
100
|
+
for (const op of this.orderStages(this.operations)) {
|
|
101
|
+
if (op.mergeable && op.stage === currentStage) {
|
|
102
|
+
currentBuffer.push(op);
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
76
105
|
if (currentBuffer.length > 0) {
|
|
77
|
-
|
|
78
|
-
if (builtStage) {
|
|
79
|
-
const stageIndex = pipeline.length;
|
|
80
|
-
pipeline.push(builtStage);
|
|
81
|
-
this.trackGroupFieldNames(currentStage, currentBuffer, stageIndex);
|
|
82
|
-
}
|
|
106
|
+
emit(currentStage, currentBuffer);
|
|
83
107
|
currentBuffer = [];
|
|
84
108
|
}
|
|
85
109
|
if (op.mergeable) {
|
|
86
110
|
currentStage = op.stage;
|
|
87
111
|
currentBuffer.push(op);
|
|
88
112
|
} else {
|
|
89
|
-
|
|
90
|
-
if (builtStage) {
|
|
91
|
-
const stageIndex = pipeline.length;
|
|
92
|
-
pipeline.push(builtStage);
|
|
93
|
-
this.trackGroupFieldNames(op.stage, [op], stageIndex);
|
|
94
|
-
}
|
|
113
|
+
emit(op.stage, [op]);
|
|
95
114
|
currentStage = null;
|
|
96
115
|
}
|
|
97
116
|
}
|
|
98
|
-
if (currentBuffer.length > 0)
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
117
|
+
if (currentBuffer.length > 0) emit(currentStage, currentBuffer);
|
|
118
|
+
return pipeline;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Apply a raw escape-hatch operation to the pipeline built so far.
|
|
122
|
+
*
|
|
123
|
+
* - `joinRaw`: appends the caller-built stages verbatim.
|
|
124
|
+
* - `raw`: calls the callback with the pipeline array. A returned array
|
|
125
|
+
* replaces the pipeline; `undefined` keeps the (possibly mutated) array.
|
|
126
|
+
*
|
|
127
|
+
* @throws UnsupportedQueryOperationError when a `raw()` callback leaves
|
|
128
|
+
* something other than an array of stage objects
|
|
129
|
+
*/
|
|
130
|
+
applyRawOperation(pipeline, op) {
|
|
131
|
+
if (op.type === "joinRaw") {
|
|
132
|
+
pipeline.push(...op.data.stages);
|
|
133
|
+
return;
|
|
105
134
|
}
|
|
106
|
-
|
|
135
|
+
const result = op.data.builder(pipeline);
|
|
136
|
+
const next = result === void 0 ? pipeline : result;
|
|
137
|
+
if (!Array.isArray(next) || !next.every(isPipelineStageObject)) throw new UnsupportedQueryOperationError("raw", "mongodb", "The raw() callback receives the aggregation pipeline array and must return an array of stage objects, or mutate that array and return nothing.");
|
|
138
|
+
if (next !== pipeline) pipeline.splice(0, pipeline.length, ...next);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Order a `$sort` against the select `$project` it follows.
|
|
142
|
+
*
|
|
143
|
+
* A `$sort` after a `$project` cannot see fields the projection dropped, so
|
|
144
|
+
* `select(["title"]).orderBy("likes")` would not sort. The sort moves before
|
|
145
|
+
* the projection, unless it sorts by a field the projection computes
|
|
146
|
+
* (`selectRaw({ score: ... }).orderBy("score")`), which only exists after it:
|
|
147
|
+
*
|
|
148
|
+
* - no computed sort key: `$sort`, `$project`
|
|
149
|
+
* - only computed or selected sort keys: `$project`, `$sort` (unchanged)
|
|
150
|
+
* - computed AND unselected sort keys: `$addFields` (the computed keys),
|
|
151
|
+
* `$sort`, `$project` (those keys passed through)
|
|
152
|
+
*/
|
|
153
|
+
placeSortAroundProjection(projectStage, sortStage) {
|
|
154
|
+
const projection = projectStage.$project;
|
|
155
|
+
const sortKeys = Object.keys(sortStage.$sort);
|
|
156
|
+
const isInclusionFlag = (value) => value === 1 || value === true || value === 0 || value === false;
|
|
157
|
+
const computedKeys = sortKeys.filter((key) => {
|
|
158
|
+
const value = projection[key];
|
|
159
|
+
return value !== void 0 && !isInclusionFlag(value) && value !== `$${key}`;
|
|
160
|
+
});
|
|
161
|
+
if (computedKeys.length === 0) return [sortStage, projectStage];
|
|
162
|
+
if (!sortKeys.some((key) => {
|
|
163
|
+
const value = projection[key];
|
|
164
|
+
return value === void 0 || value === 0 || value === false;
|
|
165
|
+
})) return [projectStage, sortStage];
|
|
166
|
+
const addFields = {};
|
|
167
|
+
const passThrough = { ...projection };
|
|
168
|
+
for (const key of computedKeys) {
|
|
169
|
+
addFields[key] = projection[key];
|
|
170
|
+
passThrough[key] = 1;
|
|
171
|
+
}
|
|
172
|
+
return [
|
|
173
|
+
{ $addFields: addFields },
|
|
174
|
+
sortStage,
|
|
175
|
+
{ $project: passThrough }
|
|
176
|
+
];
|
|
107
177
|
}
|
|
108
178
|
/**
|
|
109
179
|
* Reorder operations so filters run before projections, mirroring SQL
|
|
@@ -112,11 +182,13 @@ var MongoQueryParser = class {
|
|
|
112
182
|
* strips the filter column would run before the `$match` and silently drop
|
|
113
183
|
* every document (`select(["a"]).where("b", x)` → `[]`).
|
|
114
184
|
*
|
|
115
|
-
* Only *mergeable*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
185
|
+
* Only *mergeable* operations move, and only within a segment of neighboring
|
|
186
|
+
* mergeable `$match` / `$project` / `$sort` operations. Inside a segment the
|
|
187
|
+
* order is `$match`, then `$project`, then `$sort` (each keeping call order),
|
|
188
|
+
* and {@link placeSortAroundProjection} then decides whether the sort runs
|
|
189
|
+
* before the projection. Any other operation — `$group`, `$lookup`,
|
|
190
|
+
* `$limit`, `$skip`, `$setWindowFields`, raw escapes, having-style
|
|
191
|
+
* post-group matches, `$sample` — is a barrier: nothing moves across it.
|
|
120
192
|
* So `groupBy(...).where(...)` still filters AFTER the group, and
|
|
121
193
|
* `limit(...)` / `random()` keep their call-order meaning.
|
|
122
194
|
*/
|
|
@@ -126,7 +198,8 @@ var MongoQueryParser = class {
|
|
|
126
198
|
const flushSegment = () => {
|
|
127
199
|
if (segment.length === 0) return;
|
|
128
200
|
reordered.push(...segment.filter((op) => op.stage === "$match"));
|
|
129
|
-
reordered.push(...segment.filter((op) => op.stage
|
|
201
|
+
reordered.push(...segment.filter((op) => op.stage === "$project"));
|
|
202
|
+
reordered.push(...segment.filter((op) => op.stage === "$sort"));
|
|
130
203
|
segment = [];
|
|
131
204
|
};
|
|
132
205
|
for (const op of operations) if (op.mergeable && (op.stage === "$match" || op.stage === "$project" || op.stage === "$sort")) segment.push(op);
|
|
@@ -165,35 +238,26 @@ var MongoQueryParser = class {
|
|
|
165
238
|
if (distinctAliases.size > 0) this.countDistinctAliases.set(stageIndex, distinctAliases);
|
|
166
239
|
}
|
|
167
240
|
/**
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
* used for grouping, making the results more intuitive.
|
|
241
|
+
* Build the `$project` that renames `_id` back to the grouped field name(s)
|
|
242
|
+
* right after a `$group` stage with tracked field names, so results carry
|
|
243
|
+
* the grouping columns instead of MongoDB's `_id`.
|
|
172
244
|
*
|
|
173
|
-
* @param
|
|
174
|
-
* @
|
|
245
|
+
* @param stage - The stage just emitted
|
|
246
|
+
* @param stageIndex - Its index in the pipeline
|
|
247
|
+
* @returns The renaming `$project` stage, or null when none is needed
|
|
175
248
|
*/
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
for (const field of aggregateFields) if (distinctAliases?.has(field)) projection[field] = { $size: `$${field}` };
|
|
189
|
-
else projection[field] = 1;
|
|
190
|
-
if (Object.keys(projection).length > 0) {
|
|
191
|
-
projection._id = 0;
|
|
192
|
-
processed.push({ $project: projection });
|
|
193
|
-
}
|
|
194
|
-
} else processed.push(stage);
|
|
195
|
-
}
|
|
196
|
-
return processed;
|
|
249
|
+
buildGroupRenameStage(stage, stageIndex) {
|
|
250
|
+
if (!stage.$group || !this.groupFieldNames.has(stageIndex)) return null;
|
|
251
|
+
const fieldNames = this.groupFieldNames.get(stageIndex);
|
|
252
|
+
const projection = {};
|
|
253
|
+
if (typeof fieldNames === "string") projection[fieldNames] = "$_id";
|
|
254
|
+
else if (Array.isArray(fieldNames) && fieldNames.length > 0) for (const fieldName of fieldNames) projection[fieldName] = `$_id.${fieldName}`;
|
|
255
|
+
const distinctAliases = this.countDistinctAliases.get(stageIndex);
|
|
256
|
+
const aggregateFields = Object.keys(stage.$group).filter((key) => key !== "_id");
|
|
257
|
+
for (const field of aggregateFields) projection[field] = distinctAliases?.has(field) ? { $size: `$${field}` } : 1;
|
|
258
|
+
if (Object.keys(projection).length === 0) return null;
|
|
259
|
+
projection._id = 0;
|
|
260
|
+
return { $project: projection };
|
|
197
261
|
}
|
|
198
262
|
/**
|
|
199
263
|
* Convert the parsed pipeline to a pretty-printed string for debugging.
|
|
@@ -293,6 +357,23 @@ var MongoQueryParser = class {
|
|
|
293
357
|
const [first] = operations;
|
|
294
358
|
return first === void 0 ? null : { $setWindowFields: first.data.spec };
|
|
295
359
|
}
|
|
360
|
+
case "$unwind": {
|
|
361
|
+
const [first] = operations;
|
|
362
|
+
if (first === void 0) return null;
|
|
363
|
+
const { path, ...options } = first.data;
|
|
364
|
+
return Object.keys(options).length === 0 ? { $unwind: path } : { $unwind: {
|
|
365
|
+
path,
|
|
366
|
+
...options
|
|
367
|
+
} };
|
|
368
|
+
}
|
|
369
|
+
case "$addFields": {
|
|
370
|
+
const [first] = operations;
|
|
371
|
+
return first === void 0 ? null : { $addFields: first.data };
|
|
372
|
+
}
|
|
373
|
+
case "$vectorSearch": {
|
|
374
|
+
const [first] = operations;
|
|
375
|
+
return first === void 0 ? null : { $vectorSearch: first.data };
|
|
376
|
+
}
|
|
296
377
|
default: return null;
|
|
297
378
|
}
|
|
298
379
|
}
|
|
@@ -1109,6 +1190,13 @@ var MongoQueryParser = class {
|
|
|
1109
1190
|
const op = operations[0];
|
|
1110
1191
|
if (op === void 0) return null;
|
|
1111
1192
|
const options = op.data;
|
|
1193
|
+
if (Array.isArray(options.pipeline)) return { $lookup: {
|
|
1194
|
+
from: options.table,
|
|
1195
|
+
...options.localField !== void 0 ? { localField: options.localField } : {},
|
|
1196
|
+
...options.foreignField !== void 0 ? { foreignField: options.foreignField } : {},
|
|
1197
|
+
as: options.alias || options.table,
|
|
1198
|
+
pipeline: options.pipeline
|
|
1199
|
+
} };
|
|
1112
1200
|
return { $lookup: {
|
|
1113
1201
|
from: options.table,
|
|
1114
1202
|
localField: options.localField,
|