uql-orm 0.9.4 → 0.10.1
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 +43 -0
- package/README.md +63 -683
- package/dist/browser/http/http.d.ts +16 -1
- package/dist/browser/http/http.d.ts.map +1 -1
- package/dist/browser/http/http.js +31 -6
- package/dist/browser/http/http.js.map +1 -1
- package/dist/browser/querier/httpQuerier.d.ts +24 -6
- package/dist/browser/querier/httpQuerier.d.ts.map +1 -1
- package/dist/browser/querier/httpQuerier.js +42 -27
- package/dist/browser/querier/httpQuerier.js.map +1 -1
- package/dist/browser/querier/index.d.ts +0 -1
- package/dist/browser/querier/index.d.ts.map +1 -1
- package/dist/browser/querier/index.js +0 -1
- package/dist/browser/querier/index.js.map +1 -1
- package/dist/browser/type/clientQuerier.d.ts +5 -1
- package/dist/browser/type/clientQuerier.d.ts.map +1 -1
- package/dist/browser/type/request.d.ts +9 -10
- package/dist/browser/type/request.d.ts.map +1 -1
- package/dist/browser/uql-browser.min.js +2 -5649
- package/dist/browser/uql-browser.min.js.map +17 -1
- package/dist/dialect/abstractSqlDialect.js.map +1 -1
- package/dist/express/index.d.ts +0 -1
- package/dist/express/index.d.ts.map +1 -1
- package/dist/express/index.js +0 -1
- package/dist/express/index.js.map +1 -1
- package/dist/express/querierMiddleware.d.ts +8 -22
- package/dist/express/querierMiddleware.d.ts.map +1 -1
- package/dist/express/querierMiddleware.js +26 -129
- package/dist/express/querierMiddleware.js.map +1 -1
- package/dist/http/contract.d.ts +99 -0
- package/dist/http/contract.d.ts.map +1 -0
- package/dist/http/contract.js +72 -0
- package/dist/http/contract.js.map +1 -0
- package/dist/http/fetchHandler.d.ts +14 -0
- package/dist/http/fetchHandler.d.ts.map +1 -0
- package/dist/http/fetchHandler.js +63 -0
- package/dist/http/fetchHandler.js.map +1 -0
- package/dist/http/handler.d.ts +77 -0
- package/dist/http/handler.d.ts.map +1 -0
- package/dist/http/handler.js +172 -0
- package/dist/http/handler.js.map +1 -0
- package/dist/http/index.d.ts +5 -0
- package/dist/http/index.d.ts.map +1 -0
- package/dist/http/index.js +5 -0
- package/dist/http/index.js.map +1 -0
- package/dist/http/query.d.ts +12 -0
- package/dist/http/query.d.ts.map +1 -0
- package/dist/http/query.js +49 -0
- package/dist/http/query.js.map +1 -0
- package/dist/mongo/mongoDialect.js.map +1 -1
- package/dist/nestjs/index.d.ts +2 -0
- package/dist/nestjs/index.d.ts.map +1 -0
- package/dist/nestjs/index.js +2 -0
- package/dist/nestjs/index.js.map +1 -0
- package/dist/nestjs/uqlModule.d.ts +29 -0
- package/dist/nestjs/uqlModule.d.ts.map +1 -0
- package/dist/nestjs/uqlModule.js +42 -0
- package/dist/nestjs/uqlModule.js.map +1 -0
- package/dist/schema/schemaASTBuilder.d.ts.map +1 -1
- package/dist/schema/schemaASTBuilder.js.map +1 -1
- package/dist/type/querierPool.d.ts +3 -3
- package/dist/type/querierPool.d.ts.map +1 -1
- package/dist/util/dialect.util.js +2 -2
- package/dist/util/dialect.util.js.map +1 -1
- package/package.json +16 -9
- package/dist/browser/cc-BVTDOeZ_.js +0 -566
- package/dist/browser/cc-BVTDOeZ_.js.map +0 -1
- package/dist/browser/mongoSchemaGenerator-BT2_u1wI.js +0 -735
- package/dist/browser/mongoSchemaGenerator-BT2_u1wI.js.map +0 -1
- package/dist/browser/querier/querier.util.d.ts +0 -3
- package/dist/browser/querier/querier.util.d.ts.map +0 -1
- package/dist/browser/querier/querier.util.js +0 -17
- package/dist/browser/querier/querier.util.js.map +0 -1
- package/dist/express/query.util.d.ts +0 -7
- package/dist/express/query.util.d.ts.map +0 -1
- package/dist/express/query.util.js +0 -27
- package/dist/express/query.util.js.map +0 -1
|
@@ -1,735 +0,0 @@
|
|
|
1
|
-
import { getMeta } from '../entity/index.js';
|
|
2
|
-
import { ObjectId } from 'mongodb';
|
|
3
|
-
import { buildQueryWhereAsMap, normalizeScalarFieldSelection, buildSortMap, hasKeys, getRelationRequestSummary, getKeys, fillOnFields, filterFieldKeys, parseGroupMap, isVectorSearch } from '../util/index.js';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Merge dialect capability defaults with optional overrides from {@link DialectOptions.driverCapabilities}.
|
|
7
|
-
*/ function mergeDialectFeatures(defaults, patch) {
|
|
8
|
-
return patch ? {
|
|
9
|
-
...defaults,
|
|
10
|
-
...patch
|
|
11
|
-
} : defaults;
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Base abstract class for all database dialects (SQL and NoSQL).
|
|
15
|
-
*/ class AbstractDialect {
|
|
16
|
-
constructor(featureDefaults, options = {}){
|
|
17
|
-
this.options = options;
|
|
18
|
-
this.namingStrategy = options.namingStrategy;
|
|
19
|
-
this.features = mergeDialectFeatures(featureDefaults, options.driverCapabilities);
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Resolve the table name for an entity, applying naming strategy if necessary.
|
|
23
|
-
*/ resolveTableName(entity, meta) {
|
|
24
|
-
const name = meta.name ?? entity.name;
|
|
25
|
-
if (name !== entity.name || !this.namingStrategy) {
|
|
26
|
-
return name;
|
|
27
|
-
}
|
|
28
|
-
return this.namingStrategy.tableName(name);
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Resolve the column/field name for a property, applying naming strategy if necessary.
|
|
32
|
-
*/ resolveColumnName(key, field) {
|
|
33
|
-
if (!field || field.name !== key || !this.namingStrategy) {
|
|
34
|
-
return field?.name ?? key;
|
|
35
|
-
}
|
|
36
|
-
return this.namingStrategy.columnName(field.name);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
class MongoDialect extends AbstractDialect {
|
|
41
|
-
static{
|
|
42
|
-
/** Default {@link DialectFeatures} for MongoDB; shared by {@link MongoSchemaGenerator}. */ this.defaultDialectFeatures = {
|
|
43
|
-
explicitJsonCast: false,
|
|
44
|
-
nativeArrays: false,
|
|
45
|
-
supportsJsonb: false,
|
|
46
|
-
returning: false,
|
|
47
|
-
ifNotExists: false,
|
|
48
|
-
indexIfNotExists: false,
|
|
49
|
-
dropTableCascade: false,
|
|
50
|
-
renameColumn: false,
|
|
51
|
-
foreignKeyAlter: false,
|
|
52
|
-
columnComment: false,
|
|
53
|
-
vectorIndexStyle: 'create',
|
|
54
|
-
vectorSupportsLength: false,
|
|
55
|
-
supportsTimestamptz: false,
|
|
56
|
-
defaultStringAsText: false
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
constructor(options = {}){
|
|
60
|
-
super(MongoDialect.defaultDialectFeatures, options), this.dialectName = 'mongodb', this.insertIdStrategy = 'last';
|
|
61
|
-
}
|
|
62
|
-
static{
|
|
63
|
-
this.ID_KEY = '_id';
|
|
64
|
-
}
|
|
65
|
-
static{
|
|
66
|
-
this.VECTOR_INDEX_TYPES = new Set([
|
|
67
|
-
'vectorSearch',
|
|
68
|
-
'hnsw',
|
|
69
|
-
'ivfflat',
|
|
70
|
-
'vector'
|
|
71
|
-
]);
|
|
72
|
-
}
|
|
73
|
-
static{
|
|
74
|
-
this.AGGREGATE_OP_MAP = {
|
|
75
|
-
$count: '$sum',
|
|
76
|
-
$sum: '$sum',
|
|
77
|
-
$avg: '$avg',
|
|
78
|
-
$min: '$min',
|
|
79
|
-
$max: '$max'
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
where(entity, where = {}, { softDelete } = {}) {
|
|
83
|
-
const meta = getMeta(entity);
|
|
84
|
-
const whereMap = buildQueryWhereAsMap(meta, where);
|
|
85
|
-
if (meta.softDelete && (softDelete || softDelete === undefined) && !whereMap[meta.softDelete]) {
|
|
86
|
-
const field = meta.fields[meta.softDelete];
|
|
87
|
-
whereMap[this.resolveColumnName(meta.softDelete, field)] = null;
|
|
88
|
-
}
|
|
89
|
-
const filter = {};
|
|
90
|
-
for (const [rawKey, rawVal] of Object.entries(whereMap)){
|
|
91
|
-
let key = rawKey;
|
|
92
|
-
let val = rawVal;
|
|
93
|
-
if (key === '$and' || key === '$or') {
|
|
94
|
-
filter[key] = val.map((filterIt)=>this.where(entity, filterIt));
|
|
95
|
-
} else {
|
|
96
|
-
const field = meta.fields[key];
|
|
97
|
-
if (key === MongoDialect.ID_KEY || key === meta.id) {
|
|
98
|
-
key = MongoDialect.ID_KEY;
|
|
99
|
-
val = this.getIdValue(val);
|
|
100
|
-
} else if (field) {
|
|
101
|
-
key = this.resolveColumnName(key, field);
|
|
102
|
-
}
|
|
103
|
-
if (val && typeof val === 'object' && !Array.isArray(val) && this.hasOperatorKeys(val)) {
|
|
104
|
-
val = this.transformOperators(val);
|
|
105
|
-
} else if (Array.isArray(val)) {
|
|
106
|
-
val = {
|
|
107
|
-
$in: val
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
filter[key] = val;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
return filter;
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* Check if an object has operator keys (keys starting with $).
|
|
117
|
-
*/ hasOperatorKeys(obj) {
|
|
118
|
-
return Object.keys(obj).some((key)=>key.startsWith('$'));
|
|
119
|
-
}
|
|
120
|
-
mapTableNameRow(row) {
|
|
121
|
-
return row.table_name;
|
|
122
|
-
}
|
|
123
|
-
static{
|
|
124
|
-
/** String operators → { pattern: (v) => regex, caseInsensitive } */ this.REGEX_OP_MAP = {
|
|
125
|
-
$startsWith: {
|
|
126
|
-
wrap: (v)=>`^${v}`,
|
|
127
|
-
ci: false
|
|
128
|
-
},
|
|
129
|
-
$istartsWith: {
|
|
130
|
-
wrap: (v)=>`^${v}`,
|
|
131
|
-
ci: true
|
|
132
|
-
},
|
|
133
|
-
$endsWith: {
|
|
134
|
-
wrap: (v)=>`${v}$`,
|
|
135
|
-
ci: false
|
|
136
|
-
},
|
|
137
|
-
$iendsWith: {
|
|
138
|
-
wrap: (v)=>`${v}$`,
|
|
139
|
-
ci: true
|
|
140
|
-
},
|
|
141
|
-
$includes: {
|
|
142
|
-
wrap: (v)=>String(v),
|
|
143
|
-
ci: false
|
|
144
|
-
},
|
|
145
|
-
$iincludes: {
|
|
146
|
-
wrap: (v)=>String(v),
|
|
147
|
-
ci: true
|
|
148
|
-
},
|
|
149
|
-
$like: {
|
|
150
|
-
wrap: (v)=>String(v).replace(/%/g, '.*').replace(/_/g, '.'),
|
|
151
|
-
ci: false
|
|
152
|
-
},
|
|
153
|
-
$ilike: {
|
|
154
|
-
wrap: (v)=>String(v).replace(/%/g, '.*').replace(/_/g, '.'),
|
|
155
|
-
ci: true
|
|
156
|
-
}
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
static{
|
|
160
|
-
/** MongoDB native operators — pass through as-is. */ this.NATIVE_OPS = new Set([
|
|
161
|
-
'$all',
|
|
162
|
-
'$size',
|
|
163
|
-
'$elemMatch',
|
|
164
|
-
'$eq',
|
|
165
|
-
'$ne',
|
|
166
|
-
'$lt',
|
|
167
|
-
'$lte',
|
|
168
|
-
'$gt',
|
|
169
|
-
'$gte',
|
|
170
|
-
'$in',
|
|
171
|
-
'$nin',
|
|
172
|
-
'$regex',
|
|
173
|
-
'$not'
|
|
174
|
-
]);
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* Transform UQL operators to MongoDB operators.
|
|
178
|
-
*/ transformOperators(ops) {
|
|
179
|
-
const result = {};
|
|
180
|
-
for (const [op, val] of Object.entries(ops)){
|
|
181
|
-
// Native MongoDB operators — pass through directly
|
|
182
|
-
if (MongoDialect.NATIVE_OPS.has(op)) {
|
|
183
|
-
result[op] = val;
|
|
184
|
-
continue;
|
|
185
|
-
}
|
|
186
|
-
// String/pattern → regex operators (8 variants including $like/$ilike)
|
|
187
|
-
const regexEntry = MongoDialect.REGEX_OP_MAP[op];
|
|
188
|
-
if (regexEntry) {
|
|
189
|
-
result['$regex'] = regexEntry.wrap(val);
|
|
190
|
-
if (regexEntry.ci) result['$options'] = 'i';
|
|
191
|
-
continue;
|
|
192
|
-
}
|
|
193
|
-
// Structural transforms
|
|
194
|
-
switch(op){
|
|
195
|
-
case '$between':
|
|
196
|
-
{
|
|
197
|
-
const [min, max] = val;
|
|
198
|
-
result['$gte'] = min;
|
|
199
|
-
result['$lte'] = max;
|
|
200
|
-
break;
|
|
201
|
-
}
|
|
202
|
-
case '$isNull':
|
|
203
|
-
result[val ? '$eq' : '$ne'] = null;
|
|
204
|
-
break;
|
|
205
|
-
case '$isNotNull':
|
|
206
|
-
result[val ? '$ne' : '$eq'] = null;
|
|
207
|
-
break;
|
|
208
|
-
case '$text':
|
|
209
|
-
result['$text'] = {
|
|
210
|
-
$search: val
|
|
211
|
-
};
|
|
212
|
-
break;
|
|
213
|
-
default:
|
|
214
|
-
result[op] = val;
|
|
215
|
-
break;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
return result;
|
|
219
|
-
}
|
|
220
|
-
select(entity, select, exclude) {
|
|
221
|
-
const meta = getMeta(entity);
|
|
222
|
-
if (!select && !exclude) {
|
|
223
|
-
return {};
|
|
224
|
-
}
|
|
225
|
-
const selectedFields = normalizeScalarFieldSelection(meta, select, exclude);
|
|
226
|
-
return selectedFields.reduce((acc, key)=>{
|
|
227
|
-
acc[key] = 1;
|
|
228
|
-
return acc;
|
|
229
|
-
}, {});
|
|
230
|
-
}
|
|
231
|
-
sort(entity, sort) {
|
|
232
|
-
const raw = buildSortMap(sort);
|
|
233
|
-
const normalized = {};
|
|
234
|
-
for (const [key, dir] of Object.entries(raw)){
|
|
235
|
-
normalized[key] = dir === 'desc' || dir === -1 ? -1 : 1;
|
|
236
|
-
}
|
|
237
|
-
return normalized;
|
|
238
|
-
}
|
|
239
|
-
aggregationPipeline(entity, q, relationSummary) {
|
|
240
|
-
const meta = getMeta(entity);
|
|
241
|
-
const where = this.where(entity, q.$where);
|
|
242
|
-
const sort = this.sort(entity, q.$sort);
|
|
243
|
-
const firstPipelineEntry = {};
|
|
244
|
-
if (hasKeys(where)) {
|
|
245
|
-
firstPipelineEntry.$match = where;
|
|
246
|
-
}
|
|
247
|
-
if (hasKeys(sort)) {
|
|
248
|
-
firstPipelineEntry.$sort = sort;
|
|
249
|
-
}
|
|
250
|
-
const pipeline = [];
|
|
251
|
-
if (hasKeys(firstPipelineEntry)) {
|
|
252
|
-
pipeline.push(firstPipelineEntry);
|
|
253
|
-
}
|
|
254
|
-
const relKeys = (relationSummary ?? getRelationRequestSummary(meta, q.$populate)).joinableKeys;
|
|
255
|
-
for (const relKey of relKeys){
|
|
256
|
-
const relOpts = meta.relations[relKey];
|
|
257
|
-
if (!relOpts) continue;
|
|
258
|
-
if (relOpts.cardinality === '1m' || relOpts.cardinality === 'mm') {
|
|
259
|
-
continue;
|
|
260
|
-
}
|
|
261
|
-
const relEntity = relOpts.entity();
|
|
262
|
-
const relMeta = getMeta(relEntity);
|
|
263
|
-
if (relOpts.cardinality === 'm1') {
|
|
264
|
-
const localField = meta.fields[relOpts.references[0].local];
|
|
265
|
-
pipeline.push({
|
|
266
|
-
$lookup: {
|
|
267
|
-
from: this.resolveTableName(relEntity, relMeta),
|
|
268
|
-
localField: this.resolveColumnName(relOpts.references[0].local, localField),
|
|
269
|
-
foreignField: '_id',
|
|
270
|
-
as: relKey
|
|
271
|
-
}
|
|
272
|
-
});
|
|
273
|
-
} else {
|
|
274
|
-
const foreignField = relMeta.fields[relOpts.references[0].foreign];
|
|
275
|
-
const foreignFieldName = this.resolveColumnName(relOpts.references[0].foreign, foreignField);
|
|
276
|
-
const referenceWhere = this.where(relEntity, where);
|
|
277
|
-
const referenceSort = this.sort(relEntity, q.$sort);
|
|
278
|
-
const _id = MongoDialect.ID_KEY;
|
|
279
|
-
const referencePipelineEntry = {
|
|
280
|
-
$match: {
|
|
281
|
-
[foreignFieldName]: referenceWhere[_id]
|
|
282
|
-
}
|
|
283
|
-
};
|
|
284
|
-
if (hasKeys(referenceSort)) {
|
|
285
|
-
referencePipelineEntry.$sort = referenceSort;
|
|
286
|
-
}
|
|
287
|
-
pipeline.push({
|
|
288
|
-
$lookup: {
|
|
289
|
-
from: this.resolveTableName(relEntity, relMeta),
|
|
290
|
-
pipeline: [
|
|
291
|
-
referencePipelineEntry
|
|
292
|
-
],
|
|
293
|
-
as: relKey
|
|
294
|
-
}
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
pipeline.push({
|
|
298
|
-
$unwind: {
|
|
299
|
-
path: `$${relKey}`,
|
|
300
|
-
preserveNullAndEmptyArrays: true
|
|
301
|
-
}
|
|
302
|
-
});
|
|
303
|
-
}
|
|
304
|
-
return pipeline;
|
|
305
|
-
}
|
|
306
|
-
normalizeIds(meta, docs) {
|
|
307
|
-
return docs?.map((doc)=>this.normalizeId(meta, doc));
|
|
308
|
-
}
|
|
309
|
-
normalizeId(meta, doc) {
|
|
310
|
-
if (!doc) {
|
|
311
|
-
return doc;
|
|
312
|
-
}
|
|
313
|
-
const res = doc;
|
|
314
|
-
const _id = MongoDialect.ID_KEY;
|
|
315
|
-
if (res[_id]) {
|
|
316
|
-
res[meta.id] = res[_id];
|
|
317
|
-
if (meta.id !== _id) {
|
|
318
|
-
delete res[_id];
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
for (const key of getKeys(meta.fields)){
|
|
322
|
-
const field = meta.fields[key];
|
|
323
|
-
const dbName = this.resolveColumnName(key, field);
|
|
324
|
-
if (dbName !== key && res[dbName] !== undefined) {
|
|
325
|
-
res[key] = res[dbName];
|
|
326
|
-
delete res[dbName];
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
const relKeys = getKeys(meta.relations).filter((key)=>res[key]);
|
|
330
|
-
for (const relKey of relKeys){
|
|
331
|
-
const relOpts = meta.relations[relKey];
|
|
332
|
-
if (!relOpts) continue;
|
|
333
|
-
const relMeta = getMeta(relOpts.entity());
|
|
334
|
-
res[relKey] = Array.isArray(res[relKey]) ? this.normalizeIds(relMeta, res[relKey]) : this.normalizeId(relMeta, res[relKey]);
|
|
335
|
-
}
|
|
336
|
-
return res;
|
|
337
|
-
}
|
|
338
|
-
getIdValue(value) {
|
|
339
|
-
if (value instanceof ObjectId) {
|
|
340
|
-
return value;
|
|
341
|
-
}
|
|
342
|
-
try {
|
|
343
|
-
return new ObjectId(value);
|
|
344
|
-
} catch (e) {
|
|
345
|
-
return value;
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
getPersistable(meta, payload, callbackKey) {
|
|
349
|
-
return this.getPersistables(meta, payload, callbackKey)[0];
|
|
350
|
-
}
|
|
351
|
-
getPersistables(meta, payload, callbackKey) {
|
|
352
|
-
const payloads = fillOnFields(meta, payload, callbackKey);
|
|
353
|
-
const persistableKeys = filterFieldKeys(meta, payloads[0], callbackKey);
|
|
354
|
-
return payloads.map((it)=>persistableKeys.reduce((acc, key)=>{
|
|
355
|
-
const field = meta.fields[key];
|
|
356
|
-
acc[this.resolveColumnName(key, field)] = it[key];
|
|
357
|
-
return acc;
|
|
358
|
-
}, {}));
|
|
359
|
-
}
|
|
360
|
-
/**
|
|
361
|
-
* Build MongoDB aggregation pipeline stages from a QueryAggregate.
|
|
362
|
-
*/ buildAggregateStages(entity, q) {
|
|
363
|
-
const pipeline = [];
|
|
364
|
-
// $match stage (WHERE equivalent — before grouping)
|
|
365
|
-
if (q.$where) {
|
|
366
|
-
const filter = this.where(entity, q.$where);
|
|
367
|
-
if (hasKeys(filter)) {
|
|
368
|
-
pipeline.push({
|
|
369
|
-
$match: filter
|
|
370
|
-
});
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
// $group stage
|
|
374
|
-
const groupId = {};
|
|
375
|
-
const groupAccumulators = {};
|
|
376
|
-
for (const entry of parseGroupMap(q.$group)){
|
|
377
|
-
if (entry.kind === 'key') {
|
|
378
|
-
groupId[entry.alias] = `$${entry.alias}`;
|
|
379
|
-
} else {
|
|
380
|
-
const mongoOp = MongoDialect.AGGREGATE_OP_MAP[entry.op];
|
|
381
|
-
groupAccumulators[entry.alias] = entry.op === '$count' ? {
|
|
382
|
-
[mongoOp]: 1
|
|
383
|
-
} : {
|
|
384
|
-
[mongoOp]: `$${entry.fieldRef}`
|
|
385
|
-
};
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
pipeline.push({
|
|
389
|
-
$group: {
|
|
390
|
-
_id: hasKeys(groupId) ? groupId : null,
|
|
391
|
-
...groupAccumulators
|
|
392
|
-
}
|
|
393
|
-
});
|
|
394
|
-
// Project stage — rename _id fields back to their original names
|
|
395
|
-
if (hasKeys(groupId)) {
|
|
396
|
-
const project = {
|
|
397
|
-
_id: 0
|
|
398
|
-
};
|
|
399
|
-
for (const alias of Object.keys(groupId)){
|
|
400
|
-
project[alias] = `$_id.${alias}`;
|
|
401
|
-
}
|
|
402
|
-
for (const alias of Object.keys(groupAccumulators)){
|
|
403
|
-
project[alias] = 1;
|
|
404
|
-
}
|
|
405
|
-
pipeline.push({
|
|
406
|
-
$project: project
|
|
407
|
-
});
|
|
408
|
-
}
|
|
409
|
-
// $match stage for HAVING (post-group filtering)
|
|
410
|
-
if (q.$having) {
|
|
411
|
-
const havingFilter = this.buildHavingFilter(q.$having);
|
|
412
|
-
if (hasKeys(havingFilter)) {
|
|
413
|
-
pipeline.push({
|
|
414
|
-
$match: havingFilter
|
|
415
|
-
});
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
// $sort stage
|
|
419
|
-
if (q.$sort) {
|
|
420
|
-
const sort = this.sort(entity, q.$sort);
|
|
421
|
-
if (hasKeys(sort)) {
|
|
422
|
-
pipeline.push({
|
|
423
|
-
$sort: sort
|
|
424
|
-
});
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
// $skip and $limit stages
|
|
428
|
-
if (q.$skip !== undefined) {
|
|
429
|
-
pipeline.push({
|
|
430
|
-
$skip: q.$skip
|
|
431
|
-
});
|
|
432
|
-
}
|
|
433
|
-
if (q.$limit !== undefined) {
|
|
434
|
-
pipeline.push({
|
|
435
|
-
$limit: q.$limit
|
|
436
|
-
});
|
|
437
|
-
}
|
|
438
|
-
return pipeline;
|
|
439
|
-
}
|
|
440
|
-
buildHavingFilter(having) {
|
|
441
|
-
const filter = {};
|
|
442
|
-
for (const [alias, condition] of Object.entries(having)){
|
|
443
|
-
if (condition === undefined) continue;
|
|
444
|
-
if (typeof condition === 'number') {
|
|
445
|
-
filter[alias] = condition;
|
|
446
|
-
} else if (typeof condition === 'object' && condition !== null) {
|
|
447
|
-
filter[alias] = this.transformOperators(condition);
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
return filter;
|
|
451
|
-
}
|
|
452
|
-
/**
|
|
453
|
-
* Separate vector sort entries from regular sort entries.
|
|
454
|
-
* Returns `undefined` if no vector sort is present.
|
|
455
|
-
*/ extractVectorSort(sort) {
|
|
456
|
-
if (!sort) return undefined;
|
|
457
|
-
const raw = buildSortMap(sort);
|
|
458
|
-
let vectorKey;
|
|
459
|
-
let vectorSearch;
|
|
460
|
-
const regularSort = {};
|
|
461
|
-
for (const [key, value] of Object.entries(raw)){
|
|
462
|
-
if (isVectorSearch(value)) {
|
|
463
|
-
vectorKey = key;
|
|
464
|
-
vectorSearch = value;
|
|
465
|
-
} else {
|
|
466
|
-
regularSort[key] = value;
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
if (!vectorKey || !vectorSearch) return undefined;
|
|
470
|
-
return {
|
|
471
|
-
vectorKey,
|
|
472
|
-
vectorSearch,
|
|
473
|
-
regularSort
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
/**
|
|
477
|
-
* Build a `$vectorSearch` aggregation pipeline stage.
|
|
478
|
-
* Merges `$where` into `$vectorSearch.filter` for optimal pre-filtering.
|
|
479
|
-
*/ buildVectorSearchStage(entity, meta, key, search, where, limit) {
|
|
480
|
-
const field = meta.fields[key];
|
|
481
|
-
if (!field) {
|
|
482
|
-
throw new TypeError(`Field '${key}' not found in entity '${meta.name}'`);
|
|
483
|
-
}
|
|
484
|
-
const colName = this.resolveColumnName(key, field);
|
|
485
|
-
// Resolve index name from @Index metadata, or fall back to convention
|
|
486
|
-
const indexMeta = meta.indexes?.find((idx)=>idx.columns.includes(key) && MongoDialect.VECTOR_INDEX_TYPES.has(idx.type));
|
|
487
|
-
const indexName = indexMeta?.name ?? `${colName}_index`;
|
|
488
|
-
const stage = {
|
|
489
|
-
index: indexName,
|
|
490
|
-
path: colName,
|
|
491
|
-
queryVector: [
|
|
492
|
-
...search.$vector
|
|
493
|
-
],
|
|
494
|
-
numCandidates: limit * 10,
|
|
495
|
-
limit
|
|
496
|
-
};
|
|
497
|
-
// Pre-filter: merge $where into $vectorSearch.filter
|
|
498
|
-
if (where) {
|
|
499
|
-
const filter = this.where(entity, where);
|
|
500
|
-
if (hasKeys(filter)) {
|
|
501
|
-
stage['filter'] = filter;
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
return {
|
|
505
|
-
$vectorSearch: stage
|
|
506
|
-
};
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
class MongoSchemaGenerator extends AbstractDialect {
|
|
511
|
-
constructor(namingStrategy, defaultForeignKeyAction){
|
|
512
|
-
super(MongoDialect.defaultDialectFeatures, {
|
|
513
|
-
namingStrategy
|
|
514
|
-
}), this.defaultForeignKeyAction = defaultForeignKeyAction, this.dialectName = 'mongodb', this.insertIdStrategy = 'last';
|
|
515
|
-
}
|
|
516
|
-
generateCreateTable(entity, _options) {
|
|
517
|
-
const meta = getMeta(entity);
|
|
518
|
-
const collectionName = this.resolveTableName(entity, meta);
|
|
519
|
-
const indexes = [];
|
|
520
|
-
for (const key of getKeys(meta.fields)){
|
|
521
|
-
const field = meta.fields[key];
|
|
522
|
-
if (field?.index) {
|
|
523
|
-
const columnName = this.resolveColumnName(key, field);
|
|
524
|
-
const indexName = typeof field.index === 'string' ? field.index : `idx_${collectionName}_${columnName}`;
|
|
525
|
-
indexes.push({
|
|
526
|
-
name: indexName,
|
|
527
|
-
columns: [
|
|
528
|
-
columnName
|
|
529
|
-
],
|
|
530
|
-
unique: !!field.unique
|
|
531
|
-
});
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
return [
|
|
535
|
-
JSON.stringify({
|
|
536
|
-
action: 'createCollection',
|
|
537
|
-
name: collectionName,
|
|
538
|
-
indexes
|
|
539
|
-
})
|
|
540
|
-
];
|
|
541
|
-
}
|
|
542
|
-
generateDropTable(entity) {
|
|
543
|
-
const meta = getMeta(entity);
|
|
544
|
-
const collectionName = this.resolveTableName(entity, meta);
|
|
545
|
-
return JSON.stringify({
|
|
546
|
-
action: 'dropCollection',
|
|
547
|
-
name: collectionName
|
|
548
|
-
});
|
|
549
|
-
}
|
|
550
|
-
generateAlterTable(diff) {
|
|
551
|
-
const statements = [];
|
|
552
|
-
if (diff.indexesToAdd?.length) {
|
|
553
|
-
for (const index of diff.indexesToAdd){
|
|
554
|
-
statements.push(this.generateCreateIndex(diff.tableName, index));
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
return statements;
|
|
558
|
-
}
|
|
559
|
-
generateAlterTableDown(diff) {
|
|
560
|
-
const statements = [];
|
|
561
|
-
if (diff.indexesToAdd?.length) {
|
|
562
|
-
for (const index of diff.indexesToAdd){
|
|
563
|
-
statements.push(this.generateDropIndex(diff.tableName, index.name));
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
return statements;
|
|
567
|
-
}
|
|
568
|
-
generateCreateIndex(tableName, index) {
|
|
569
|
-
const key = {};
|
|
570
|
-
for (const col of index.columns){
|
|
571
|
-
key[col] = 1;
|
|
572
|
-
}
|
|
573
|
-
return JSON.stringify({
|
|
574
|
-
action: 'createIndex',
|
|
575
|
-
collection: tableName,
|
|
576
|
-
name: index.name,
|
|
577
|
-
key,
|
|
578
|
-
options: {
|
|
579
|
-
unique: index.unique,
|
|
580
|
-
name: index.name
|
|
581
|
-
}
|
|
582
|
-
});
|
|
583
|
-
}
|
|
584
|
-
generateDropIndex(tableName, indexName) {
|
|
585
|
-
return JSON.stringify({
|
|
586
|
-
action: 'dropIndex',
|
|
587
|
-
collection: tableName,
|
|
588
|
-
name: indexName
|
|
589
|
-
});
|
|
590
|
-
}
|
|
591
|
-
getSqlType(fieldOptions, fieldType) {
|
|
592
|
-
return '';
|
|
593
|
-
}
|
|
594
|
-
getBooleanType() {
|
|
595
|
-
return '';
|
|
596
|
-
}
|
|
597
|
-
generateColumnDefinitions() {
|
|
598
|
-
return [];
|
|
599
|
-
}
|
|
600
|
-
generateColumnDefinition() {
|
|
601
|
-
return '';
|
|
602
|
-
}
|
|
603
|
-
generateColumnDefinitionFromSchema() {
|
|
604
|
-
return '';
|
|
605
|
-
}
|
|
606
|
-
generateTableConstraints() {
|
|
607
|
-
return [];
|
|
608
|
-
}
|
|
609
|
-
generateAlterColumnStatements() {
|
|
610
|
-
return [];
|
|
611
|
-
}
|
|
612
|
-
getTableOptions() {
|
|
613
|
-
return '';
|
|
614
|
-
}
|
|
615
|
-
generateColumnComment() {
|
|
616
|
-
return '';
|
|
617
|
-
}
|
|
618
|
-
formatDefaultValue() {
|
|
619
|
-
return '';
|
|
620
|
-
}
|
|
621
|
-
generateCreateTableFromNode(table, _options) {
|
|
622
|
-
return [
|
|
623
|
-
JSON.stringify({
|
|
624
|
-
action: 'createCollection',
|
|
625
|
-
name: table.name
|
|
626
|
-
})
|
|
627
|
-
];
|
|
628
|
-
}
|
|
629
|
-
generateCreateIndexFromNode(index) {
|
|
630
|
-
const key = {};
|
|
631
|
-
for (const col of index.columns){
|
|
632
|
-
key[col.name] = 1;
|
|
633
|
-
}
|
|
634
|
-
return JSON.stringify({
|
|
635
|
-
action: 'createIndex',
|
|
636
|
-
collection: index.table.name,
|
|
637
|
-
name: index.name,
|
|
638
|
-
key,
|
|
639
|
-
options: {
|
|
640
|
-
unique: index.unique,
|
|
641
|
-
name: index.name
|
|
642
|
-
}
|
|
643
|
-
});
|
|
644
|
-
}
|
|
645
|
-
generateDropTableFromNode(table) {
|
|
646
|
-
return JSON.stringify({
|
|
647
|
-
action: 'dropCollection',
|
|
648
|
-
name: table.name
|
|
649
|
-
});
|
|
650
|
-
}
|
|
651
|
-
generateCreateTableFromDefinition(table, _options) {
|
|
652
|
-
return [
|
|
653
|
-
JSON.stringify({
|
|
654
|
-
action: 'createCollection',
|
|
655
|
-
name: table.name
|
|
656
|
-
})
|
|
657
|
-
];
|
|
658
|
-
}
|
|
659
|
-
generateDropTableSql(tableName) {
|
|
660
|
-
return JSON.stringify({
|
|
661
|
-
action: 'dropCollection',
|
|
662
|
-
name: tableName
|
|
663
|
-
});
|
|
664
|
-
}
|
|
665
|
-
generateRenameTableSql(oldName, newName) {
|
|
666
|
-
return JSON.stringify({
|
|
667
|
-
action: 'renameCollection',
|
|
668
|
-
from: oldName,
|
|
669
|
-
to: newName
|
|
670
|
-
});
|
|
671
|
-
}
|
|
672
|
-
generateAddColumnSql() {
|
|
673
|
-
return '';
|
|
674
|
-
}
|
|
675
|
-
generateDropColumnSql() {
|
|
676
|
-
return '';
|
|
677
|
-
}
|
|
678
|
-
generateRenameColumnSql() {
|
|
679
|
-
return '';
|
|
680
|
-
}
|
|
681
|
-
generateAlterColumnSql() {
|
|
682
|
-
return '';
|
|
683
|
-
}
|
|
684
|
-
generateCreateIndexSql(tableName, index) {
|
|
685
|
-
return this.generateCreateIndex(tableName, index);
|
|
686
|
-
}
|
|
687
|
-
generateDropIndexSql(tableName, indexName) {
|
|
688
|
-
return this.generateDropIndex(tableName, indexName);
|
|
689
|
-
}
|
|
690
|
-
generateAddForeignKeySql() {
|
|
691
|
-
return '';
|
|
692
|
-
}
|
|
693
|
-
generateDropForeignKeySql() {
|
|
694
|
-
return '';
|
|
695
|
-
}
|
|
696
|
-
diffSchema(entity, currentTable) {
|
|
697
|
-
const meta = getMeta(entity);
|
|
698
|
-
const collectionName = this.resolveTableName(entity, meta);
|
|
699
|
-
if (!currentTable) {
|
|
700
|
-
return {
|
|
701
|
-
tableName: collectionName,
|
|
702
|
-
type: 'create'
|
|
703
|
-
};
|
|
704
|
-
}
|
|
705
|
-
const indexesToAdd = [];
|
|
706
|
-
const existingIndexes = new Set(currentTable.indexes?.map((i)=>i.name) ?? []);
|
|
707
|
-
for (const key of getKeys(meta.fields)){
|
|
708
|
-
const field = meta.fields[key];
|
|
709
|
-
if (field?.index) {
|
|
710
|
-
const columnName = this.resolveColumnName(key, field);
|
|
711
|
-
const indexName = typeof field.index === 'string' ? field.index : `idx_${collectionName}_${columnName}`;
|
|
712
|
-
if (!existingIndexes.has(indexName)) {
|
|
713
|
-
indexesToAdd.push({
|
|
714
|
-
name: indexName,
|
|
715
|
-
columns: [
|
|
716
|
-
columnName
|
|
717
|
-
],
|
|
718
|
-
unique: !!field.unique
|
|
719
|
-
});
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
if (indexesToAdd.length === 0) {
|
|
724
|
-
return undefined;
|
|
725
|
-
}
|
|
726
|
-
return {
|
|
727
|
-
tableName: collectionName,
|
|
728
|
-
type: 'alter',
|
|
729
|
-
indexesToAdd
|
|
730
|
-
};
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
export { MongoSchemaGenerator };
|
|
735
|
-
//# sourceMappingURL=mongoSchemaGenerator-BT2_u1wI.js.map
|