@mikro-orm/knex 6.0.0-dev.12 → 6.0.0-dev.120
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/AbstractSqlConnection.d.ts +8 -4
- package/AbstractSqlConnection.js +30 -18
- package/AbstractSqlDriver.d.ts +21 -23
- package/AbstractSqlDriver.js +378 -221
- package/AbstractSqlPlatform.d.ts +5 -4
- package/AbstractSqlPlatform.js +34 -5
- package/MonkeyPatchable.d.ts +1 -0
- package/MonkeyPatchable.js +3 -0
- package/PivotCollectionPersister.d.ts +17 -0
- package/PivotCollectionPersister.js +128 -0
- package/README.md +64 -107
- package/SqlEntityManager.d.ts +2 -6
- package/SqlEntityManager.js +5 -9
- package/SqlEntityRepository.d.ts +7 -6
- package/SqlEntityRepository.js +11 -8
- package/index.mjs +23 -6
- package/package.json +8 -8
- package/query/ArrayCriteriaNode.d.ts +3 -3
- package/query/CriteriaNode.d.ts +8 -8
- package/query/CriteriaNode.js +16 -9
- package/query/CriteriaNodeFactory.d.ts +6 -6
- package/query/CriteriaNodeFactory.js +17 -13
- package/query/ObjectCriteriaNode.d.ts +3 -3
- package/query/ObjectCriteriaNode.js +12 -8
- package/query/QueryBuilder.d.ts +44 -20
- package/query/QueryBuilder.js +371 -130
- package/query/QueryBuilderHelper.d.ts +10 -15
- package/query/QueryBuilderHelper.js +197 -158
- package/query/ScalarCriteriaNode.d.ts +3 -2
- package/query/ScalarCriteriaNode.js +9 -6
- package/query/enums.js +1 -1
- package/schema/DatabaseSchema.d.ts +5 -2
- package/schema/DatabaseSchema.js +27 -9
- package/schema/DatabaseTable.d.ts +4 -2
- package/schema/DatabaseTable.js +66 -32
- package/schema/SchemaComparator.js +15 -3
- package/schema/SchemaHelper.d.ts +6 -4
- package/schema/SchemaHelper.js +11 -4
- package/schema/SqlSchemaGenerator.d.ts +3 -6
- package/schema/SqlSchemaGenerator.js +61 -28
- package/typings.d.ts +13 -7
package/query/QueryBuilder.js
CHANGED
|
@@ -26,6 +26,12 @@ const CriteriaNodeFactory_1 = require("./CriteriaNodeFactory");
|
|
|
26
26
|
* ```
|
|
27
27
|
*/
|
|
28
28
|
class QueryBuilder {
|
|
29
|
+
metadata;
|
|
30
|
+
driver;
|
|
31
|
+
context;
|
|
32
|
+
connectionType;
|
|
33
|
+
em;
|
|
34
|
+
logging;
|
|
29
35
|
get mainAlias() {
|
|
30
36
|
this.ensureFromClause();
|
|
31
37
|
return this._mainAlias;
|
|
@@ -37,31 +43,56 @@ class QueryBuilder {
|
|
|
37
43
|
this.ensureFromClause();
|
|
38
44
|
return this._helper;
|
|
39
45
|
}
|
|
46
|
+
/** @internal */
|
|
47
|
+
type;
|
|
48
|
+
/** @internal */
|
|
49
|
+
_fields;
|
|
50
|
+
/** @internal */
|
|
51
|
+
_populate = [];
|
|
52
|
+
/** @internal */
|
|
53
|
+
_populateWhere;
|
|
54
|
+
/** @internal */
|
|
55
|
+
_populateMap = {};
|
|
56
|
+
aliasCounter = 0;
|
|
57
|
+
flags = new Set([core_1.QueryFlag.CONVERT_CUSTOM_TYPES]);
|
|
58
|
+
finalized = false;
|
|
59
|
+
_joins = {};
|
|
60
|
+
_explicitAlias = false;
|
|
61
|
+
_schema;
|
|
62
|
+
_cond = {};
|
|
63
|
+
_data;
|
|
64
|
+
_orderBy = [];
|
|
65
|
+
_groupBy = [];
|
|
66
|
+
_having = {};
|
|
67
|
+
_returning;
|
|
68
|
+
_onConflict;
|
|
69
|
+
_limit;
|
|
70
|
+
_offset;
|
|
71
|
+
_distinctOn;
|
|
72
|
+
_joinedProps = new Map();
|
|
73
|
+
_cache;
|
|
74
|
+
_indexHint;
|
|
75
|
+
_comments = [];
|
|
76
|
+
_hintComments = [];
|
|
77
|
+
flushMode;
|
|
78
|
+
lockMode;
|
|
79
|
+
lockTables;
|
|
80
|
+
subQueries = {};
|
|
81
|
+
_mainAlias;
|
|
82
|
+
_aliases = {};
|
|
83
|
+
_helper;
|
|
84
|
+
platform;
|
|
85
|
+
knex;
|
|
40
86
|
/**
|
|
41
87
|
* @internal
|
|
42
88
|
*/
|
|
43
|
-
constructor(entityName, metadata, driver, context, alias, connectionType, em) {
|
|
89
|
+
constructor(entityName, metadata, driver, context, alias, connectionType, em, logging) {
|
|
44
90
|
this.metadata = metadata;
|
|
45
91
|
this.driver = driver;
|
|
46
92
|
this.context = context;
|
|
47
93
|
this.connectionType = connectionType;
|
|
48
94
|
this.em = em;
|
|
49
|
-
|
|
50
|
-
this._populate = [];
|
|
51
|
-
/** @internal */
|
|
52
|
-
this._populateMap = {};
|
|
53
|
-
this.aliasCounter = 0;
|
|
54
|
-
this.flags = new Set([core_1.QueryFlag.CONVERT_CUSTOM_TYPES]);
|
|
55
|
-
this.finalized = false;
|
|
56
|
-
this._joins = {};
|
|
57
|
-
this._explicitAlias = false;
|
|
58
|
-
this._cond = {};
|
|
59
|
-
this._orderBy = [];
|
|
60
|
-
this._groupBy = [];
|
|
61
|
-
this._having = {};
|
|
62
|
-
this._joinedProps = new Map();
|
|
63
|
-
this.subQueries = {};
|
|
64
|
-
this._aliases = {};
|
|
95
|
+
this.logging = logging;
|
|
65
96
|
this.platform = this.driver.getPlatform();
|
|
66
97
|
this.knex = this.driver.getConnection(this.connectionType).getKnex();
|
|
67
98
|
if (alias) {
|
|
@@ -116,27 +147,39 @@ class QueryBuilder {
|
|
|
116
147
|
this._fields = this.mainAlias.metadata.primaryKeys;
|
|
117
148
|
}
|
|
118
149
|
else {
|
|
119
|
-
this._fields = [
|
|
150
|
+
this._fields = [(0, core_1.raw)('*')];
|
|
120
151
|
}
|
|
121
152
|
if (distinct) {
|
|
122
153
|
this.flags.add(core_1.QueryFlag.DISTINCT);
|
|
123
154
|
}
|
|
124
155
|
return this.init(enums_1.QueryType.COUNT);
|
|
125
156
|
}
|
|
126
|
-
join(field, alias, cond = {}, type = 'innerJoin', path) {
|
|
127
|
-
this.joinReference(field, alias, cond, type, path);
|
|
157
|
+
join(field, alias, cond = {}, type = 'innerJoin', path, schema) {
|
|
158
|
+
this.joinReference(field, alias, cond, type, path, schema);
|
|
159
|
+
return this;
|
|
160
|
+
}
|
|
161
|
+
innerJoin(field, alias, cond = {}, schema) {
|
|
162
|
+
this.join(field, alias, cond, 'innerJoin', undefined, schema);
|
|
128
163
|
return this;
|
|
129
164
|
}
|
|
130
|
-
leftJoin(field, alias, cond = {}) {
|
|
131
|
-
return this.join(field, alias, cond, 'leftJoin');
|
|
165
|
+
leftJoin(field, alias, cond = {}, schema) {
|
|
166
|
+
return this.join(field, alias, cond, 'leftJoin', undefined, schema);
|
|
132
167
|
}
|
|
133
|
-
joinAndSelect(field, alias, cond = {}, type = 'innerJoin', path) {
|
|
168
|
+
joinAndSelect(field, alias, cond = {}, type = 'innerJoin', path, fields, schema) {
|
|
134
169
|
if (!this.type) {
|
|
135
170
|
this.select('*');
|
|
136
171
|
}
|
|
137
|
-
|
|
138
|
-
|
|
172
|
+
let subquery;
|
|
173
|
+
if (Array.isArray(field)) {
|
|
174
|
+
subquery = field[1] instanceof QueryBuilder ? field[1].getFormattedQuery() : field[1].toString();
|
|
175
|
+
field = field[0];
|
|
176
|
+
}
|
|
177
|
+
const prop = this.joinReference(field, alias, cond, type, path, schema);
|
|
139
178
|
const [fromAlias] = this.helper.splitField(field);
|
|
179
|
+
if (subquery) {
|
|
180
|
+
this._joins[`${fromAlias}.${prop.name}#${alias}`].subquery = subquery;
|
|
181
|
+
}
|
|
182
|
+
this.addSelect(this.getFieldsForJoinedLoad(prop, alias, fields));
|
|
140
183
|
const populate = this._joinedProps.get(fromAlias);
|
|
141
184
|
const item = { field: prop.name, strategy: core_1.LoadStrategy.JOINED, children: [] };
|
|
142
185
|
if (populate) {
|
|
@@ -148,13 +191,16 @@ class QueryBuilder {
|
|
|
148
191
|
this._joinedProps.set(alias, item);
|
|
149
192
|
return this;
|
|
150
193
|
}
|
|
151
|
-
leftJoinAndSelect(field, alias, cond = {}) {
|
|
152
|
-
return this.joinAndSelect(field, alias, cond, 'leftJoin');
|
|
194
|
+
leftJoinAndSelect(field, alias, cond = {}, fields, schema) {
|
|
195
|
+
return this.joinAndSelect(field, alias, cond, 'leftJoin', undefined, fields, schema);
|
|
196
|
+
}
|
|
197
|
+
innerJoinAndSelect(field, alias, cond = {}, fields, schema) {
|
|
198
|
+
return this.joinAndSelect(field, alias, cond, 'innerJoin', undefined, fields, schema);
|
|
153
199
|
}
|
|
154
|
-
getFieldsForJoinedLoad(prop, alias) {
|
|
200
|
+
getFieldsForJoinedLoad(prop, alias, explicitFields) {
|
|
155
201
|
const fields = [];
|
|
156
202
|
prop.targetMeta.props
|
|
157
|
-
.filter(prop => this.platform.shouldHaveColumn(prop, this._populate))
|
|
203
|
+
.filter(prop => explicitFields ? explicitFields.includes(prop.name) || prop.primary : this.platform.shouldHaveColumn(prop, this._populate))
|
|
158
204
|
.forEach(prop => fields.push(...this.driver.mapPropToFieldNames(this, prop, alias)));
|
|
159
205
|
return fields;
|
|
160
206
|
}
|
|
@@ -165,9 +211,15 @@ class QueryBuilder {
|
|
|
165
211
|
}
|
|
166
212
|
where(cond, params, operator) {
|
|
167
213
|
this.ensureNotFinalized();
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
214
|
+
const rawField = core_1.RawQueryFragment.getKnownFragment(cond);
|
|
215
|
+
if (rawField) {
|
|
216
|
+
const sql = this.platform.formatQuery(rawField.sql, rawField.params);
|
|
217
|
+
cond = { [(0, core_1.raw)(`(${sql})`)]: core_1.Utils.asArray(params) };
|
|
218
|
+
operator ??= '$and';
|
|
219
|
+
}
|
|
220
|
+
else if (core_1.Utils.isString(cond)) {
|
|
221
|
+
cond = { [(0, core_1.raw)(`(${cond})`, core_1.Utils.asArray(params))]: [] };
|
|
222
|
+
operator ??= '$and';
|
|
171
223
|
}
|
|
172
224
|
else {
|
|
173
225
|
cond = core_1.QueryHelper.processWhere({
|
|
@@ -222,6 +274,7 @@ class QueryBuilder {
|
|
|
222
274
|
aliasMap: this.getAliasMap(),
|
|
223
275
|
aliased: !this.type || [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(this.type),
|
|
224
276
|
convertCustomTypes: false,
|
|
277
|
+
type: 'orderBy',
|
|
225
278
|
});
|
|
226
279
|
this._orderBy.push(CriteriaNodeFactory_1.CriteriaNodeFactory.createNode(this.metadata, this.mainAlias.entityName, processed).process(this));
|
|
227
280
|
});
|
|
@@ -235,15 +288,22 @@ class QueryBuilder {
|
|
|
235
288
|
having(cond = {}, params) {
|
|
236
289
|
this.ensureNotFinalized();
|
|
237
290
|
if (core_1.Utils.isString(cond)) {
|
|
238
|
-
cond = { [`(${cond})
|
|
291
|
+
cond = { [(0, core_1.raw)(`(${cond})`, params)]: [] };
|
|
239
292
|
}
|
|
240
293
|
this._having = CriteriaNodeFactory_1.CriteriaNodeFactory.createNode(this.metadata, this.mainAlias.entityName, cond).process(this);
|
|
241
294
|
return this;
|
|
242
295
|
}
|
|
243
296
|
onConflict(fields = []) {
|
|
297
|
+
const meta = this.mainAlias.metadata;
|
|
244
298
|
this.ensureNotFinalized();
|
|
245
|
-
this._onConflict
|
|
246
|
-
this._onConflict.push({
|
|
299
|
+
this._onConflict ??= [];
|
|
300
|
+
this._onConflict.push({
|
|
301
|
+
fields: core_1.Utils.asArray(fields).flatMap(f => {
|
|
302
|
+
const key = f.toString();
|
|
303
|
+
/* istanbul ignore next */
|
|
304
|
+
return meta.properties[key]?.fieldNames ?? [key];
|
|
305
|
+
}),
|
|
306
|
+
});
|
|
247
307
|
return this;
|
|
248
308
|
}
|
|
249
309
|
ignore() {
|
|
@@ -263,6 +323,10 @@ class QueryBuilder {
|
|
|
263
323
|
this._onConflict[this._onConflict.length - 1].merge = data;
|
|
264
324
|
return this;
|
|
265
325
|
}
|
|
326
|
+
returning(fields) {
|
|
327
|
+
this._returning = fields != null ? core_1.Utils.asArray(fields) : fields;
|
|
328
|
+
return this;
|
|
329
|
+
}
|
|
266
330
|
/**
|
|
267
331
|
* @internal
|
|
268
332
|
*/
|
|
@@ -272,17 +336,6 @@ class QueryBuilder {
|
|
|
272
336
|
this._populateWhere = populateWhere;
|
|
273
337
|
return this;
|
|
274
338
|
}
|
|
275
|
-
/**
|
|
276
|
-
* @internal
|
|
277
|
-
*/
|
|
278
|
-
ref(field) {
|
|
279
|
-
return this.knex.ref(field);
|
|
280
|
-
}
|
|
281
|
-
raw(sql, bindings = []) {
|
|
282
|
-
const raw = this.knex.raw(sql, bindings);
|
|
283
|
-
raw.__raw = true; // tag it as there is now way to check via `instanceof`
|
|
284
|
-
return raw;
|
|
285
|
-
}
|
|
286
339
|
limit(limit, offset = 0) {
|
|
287
340
|
this.ensureNotFinalized();
|
|
288
341
|
this._limit = limit;
|
|
@@ -338,6 +391,24 @@ class QueryBuilder {
|
|
|
338
391
|
this._indexHint = sql;
|
|
339
392
|
return this;
|
|
340
393
|
}
|
|
394
|
+
/**
|
|
395
|
+
* Prepend comment to the sql query using the syntax `/* ... *‍/`. Some characters are forbidden such as `/*, *‍/` and `?`.
|
|
396
|
+
*/
|
|
397
|
+
comment(comment) {
|
|
398
|
+
this.ensureNotFinalized();
|
|
399
|
+
this._comments.push(...core_1.Utils.asArray(comment));
|
|
400
|
+
return this;
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Add hints to the query using comment-like syntax `/*+ ... *‍/`. MySQL and Oracle use this syntax for optimizer hints.
|
|
404
|
+
* Also various DB proxies and routers use this syntax to pass hints to alter their behavior. In other dialects the hints
|
|
405
|
+
* are ignored as simple comments.
|
|
406
|
+
*/
|
|
407
|
+
hintComment(comment) {
|
|
408
|
+
this.ensureNotFinalized();
|
|
409
|
+
this._hintComments.push(...core_1.Utils.asArray(comment));
|
|
410
|
+
return this;
|
|
411
|
+
}
|
|
341
412
|
from(target, aliasName) {
|
|
342
413
|
this.ensureNotFinalized();
|
|
343
414
|
if (target instanceof QueryBuilder) {
|
|
@@ -355,17 +426,21 @@ class QueryBuilder {
|
|
|
355
426
|
getKnexQuery() {
|
|
356
427
|
this.finalize();
|
|
357
428
|
const qb = this.getQueryBase();
|
|
429
|
+
qb.__raw = true; // tag it as there is now way to check via `instanceof`
|
|
358
430
|
core_1.Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type ?? enums_1.QueryType.SELECT, this._cond, qb), this._cond && !this._onConflict);
|
|
359
431
|
core_1.Utils.runIfNotEmpty(() => qb.groupBy(this.prepareFields(this._groupBy, 'groupBy')), this._groupBy);
|
|
360
432
|
core_1.Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type ?? enums_1.QueryType.SELECT, this._having, qb, undefined, 'having'), this._having);
|
|
361
433
|
core_1.Utils.runIfNotEmpty(() => {
|
|
362
434
|
const queryOrder = this.helper.getQueryOrder(this.type ?? enums_1.QueryType.SELECT, this._orderBy, this._populateMap);
|
|
363
435
|
if (queryOrder) {
|
|
364
|
-
|
|
436
|
+
qb.orderByRaw(queryOrder);
|
|
437
|
+
return;
|
|
365
438
|
}
|
|
366
439
|
}, this._orderBy);
|
|
367
440
|
core_1.Utils.runIfNotEmpty(() => qb.limit(this._limit), this._limit != null);
|
|
368
441
|
core_1.Utils.runIfNotEmpty(() => qb.offset(this._offset), this._offset);
|
|
442
|
+
core_1.Utils.runIfNotEmpty(() => this._comments.forEach(comment => qb.comment(comment)), this._comments);
|
|
443
|
+
core_1.Utils.runIfNotEmpty(() => this._hintComments.forEach(comment => qb.hintComment(comment)), this._hintComments);
|
|
369
444
|
core_1.Utils.runIfNotEmpty(() => this.helper.appendOnConflictClause(this.type ?? enums_1.QueryType.SELECT, this._onConflict, qb), this._onConflict);
|
|
370
445
|
if (this.type === enums_1.QueryType.TRUNCATE && this.platform.usesCascadeStatement()) {
|
|
371
446
|
return this.knex.raw(qb.toSQL().toNative().sql + ' cascade');
|
|
@@ -373,26 +448,35 @@ class QueryBuilder {
|
|
|
373
448
|
if (this.lockMode) {
|
|
374
449
|
this.helper.getLockSQL(qb, this.lockMode, this.lockTables);
|
|
375
450
|
}
|
|
376
|
-
this.helper.finalize(this.type ?? enums_1.QueryType.SELECT, qb, this.mainAlias.metadata);
|
|
451
|
+
this.helper.finalize(this.type ?? enums_1.QueryType.SELECT, qb, this.mainAlias.metadata, this._data, this._returning);
|
|
377
452
|
return qb;
|
|
378
453
|
}
|
|
379
454
|
/**
|
|
380
455
|
* Returns the query with parameters as wildcards.
|
|
381
456
|
*/
|
|
382
457
|
getQuery() {
|
|
383
|
-
return this.
|
|
458
|
+
return this.toQuery().sql;
|
|
459
|
+
}
|
|
460
|
+
#query;
|
|
461
|
+
toQuery() {
|
|
462
|
+
if (this.#query) {
|
|
463
|
+
return this.#query;
|
|
464
|
+
}
|
|
465
|
+
const sql = this.getKnexQuery().toSQL();
|
|
466
|
+
const query = sql.toNative();
|
|
467
|
+
return this.#query = { sql: query.sql, params: query.bindings ?? [], _sql: sql };
|
|
384
468
|
}
|
|
385
469
|
/**
|
|
386
470
|
* Returns the list of all parameters for this query.
|
|
387
471
|
*/
|
|
388
472
|
getParams() {
|
|
389
|
-
return this.
|
|
473
|
+
return this.toQuery().params;
|
|
390
474
|
}
|
|
391
475
|
/**
|
|
392
476
|
* Returns raw interpolated query string with all the parameters inlined.
|
|
393
477
|
*/
|
|
394
478
|
getFormattedQuery() {
|
|
395
|
-
const query = this.
|
|
479
|
+
const query = this.toQuery()._sql;
|
|
396
480
|
return this.platform.formatQuery(query.sql, query.bindings);
|
|
397
481
|
}
|
|
398
482
|
/**
|
|
@@ -424,7 +508,10 @@ class QueryBuilder {
|
|
|
424
508
|
* Executes this QB and returns the raw results, mapped to the property names (unless disabled via last parameter).
|
|
425
509
|
* Use `method` to specify what kind of result you want to get (array/single/meta).
|
|
426
510
|
*/
|
|
427
|
-
async execute(method = 'all',
|
|
511
|
+
async execute(method = 'all', options) {
|
|
512
|
+
options = typeof options === 'boolean' ? { mapResults: options } : (options ?? {});
|
|
513
|
+
options.mergeResults ??= true;
|
|
514
|
+
options.mapResults ??= true;
|
|
428
515
|
if (!this.connectionType && method !== 'run' && [enums_1.QueryType.INSERT, enums_1.QueryType.UPDATE, enums_1.QueryType.DELETE, enums_1.QueryType.TRUNCATE].includes(this.type ?? enums_1.QueryType.SELECT)) {
|
|
429
516
|
this.connectionType = 'write';
|
|
430
517
|
}
|
|
@@ -433,21 +520,33 @@ class QueryBuilder {
|
|
|
433
520
|
if (cached?.data) {
|
|
434
521
|
return cached.data;
|
|
435
522
|
}
|
|
436
|
-
const
|
|
437
|
-
const
|
|
523
|
+
const write = method === 'run' || !this.platform.getConfig().get('preferReadReplicas');
|
|
524
|
+
const type = this.connectionType || (write ? 'write' : 'read');
|
|
525
|
+
const res = await this.driver.getConnection(type).execute(query.sql, query.bindings, method, this.context, this.logging);
|
|
438
526
|
const meta = this.mainAlias.metadata;
|
|
439
|
-
if (!mapResults || !meta) {
|
|
527
|
+
if (!options.mapResults || !meta) {
|
|
440
528
|
await this.em?.storeCache(this._cache, cached, res);
|
|
441
529
|
return res;
|
|
442
530
|
}
|
|
443
|
-
if (method === '
|
|
531
|
+
if (method === 'run') {
|
|
532
|
+
return res;
|
|
533
|
+
}
|
|
534
|
+
const joinedProps = this.driver.joinedProps(meta, this._populate);
|
|
535
|
+
let mapped;
|
|
536
|
+
if (Array.isArray(res)) {
|
|
444
537
|
const map = {};
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
538
|
+
mapped = res.map(r => this.driver.mapResult(r, meta, this._populate, this, map));
|
|
539
|
+
if (options.mergeResults && joinedProps.length > 0) {
|
|
540
|
+
mapped = this.driver.mergeJoinedResult(mapped, this.mainAlias.metadata, joinedProps);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
else {
|
|
544
|
+
mapped = [this.driver.mapResult(res, meta, joinedProps, this)];
|
|
448
545
|
}
|
|
449
|
-
const mapped = this.driver.mapResult(res, meta, this._populate, this);
|
|
450
546
|
await this.em?.storeCache(this._cache, cached, mapped);
|
|
547
|
+
if (method === 'get') {
|
|
548
|
+
return mapped[0];
|
|
549
|
+
}
|
|
451
550
|
return mapped;
|
|
452
551
|
}
|
|
453
552
|
/**
|
|
@@ -461,11 +560,28 @@ class QueryBuilder {
|
|
|
461
560
|
*/
|
|
462
561
|
async getResultList() {
|
|
463
562
|
await this.em.tryFlush(this.mainAlias.entityName, { flushMode: this.flushMode });
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
563
|
+
const res = await this.execute('all', true);
|
|
564
|
+
const entities = [];
|
|
565
|
+
function propagatePopulateHint(entity, hint) {
|
|
566
|
+
(0, core_1.helper)(entity).__serializationContext.populate ??= hint;
|
|
567
|
+
hint.forEach(pop => {
|
|
568
|
+
const value = entity[pop.field];
|
|
569
|
+
if (core_1.Utils.isEntity(value, true)) {
|
|
570
|
+
(0, core_1.helper)(value).populated();
|
|
571
|
+
propagatePopulateHint(value, pop.children ?? []);
|
|
572
|
+
}
|
|
573
|
+
else if (core_1.Utils.isCollection(value)) {
|
|
574
|
+
value.populated();
|
|
575
|
+
value.getItems(false).forEach(item => propagatePopulateHint(item, pop.children ?? []));
|
|
576
|
+
}
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
for (const r of res) {
|
|
580
|
+
const entity = this.em.map(this.mainAlias.entityName, r, { schema: this._schema });
|
|
581
|
+
propagatePopulateHint(entity, this._populate);
|
|
582
|
+
entities.push(entity);
|
|
467
583
|
}
|
|
468
|
-
return
|
|
584
|
+
return entities;
|
|
469
585
|
}
|
|
470
586
|
/**
|
|
471
587
|
* Executes the query, returning the first result or null
|
|
@@ -483,17 +599,30 @@ class QueryBuilder {
|
|
|
483
599
|
res = await this.execute('get', false);
|
|
484
600
|
}
|
|
485
601
|
else {
|
|
486
|
-
const qb = this.clone();
|
|
602
|
+
const qb = this.type === undefined ? this : this.clone();
|
|
487
603
|
qb.count(field, distinct ?? qb.hasToManyJoins()).limit(undefined).offset(undefined).orderBy([]);
|
|
488
604
|
res = await qb.execute('get', false);
|
|
489
605
|
}
|
|
490
606
|
return res ? +res.count : 0;
|
|
491
607
|
}
|
|
608
|
+
/**
|
|
609
|
+
* Executes the query, returning both array of results and total count query (without offset and limit).
|
|
610
|
+
*/
|
|
611
|
+
async getResultAndCount() {
|
|
612
|
+
return Promise.all([
|
|
613
|
+
this.getResultList(),
|
|
614
|
+
this.getCount(),
|
|
615
|
+
]);
|
|
616
|
+
}
|
|
492
617
|
/**
|
|
493
618
|
* Provides promise-like interface so we can await the QB instance.
|
|
494
619
|
*/
|
|
495
620
|
then(onfulfilled, onrejected) {
|
|
496
|
-
|
|
621
|
+
let type = this.type ?? enums_1.QueryType.SELECT;
|
|
622
|
+
if (this.flags.has(core_1.QueryFlag.UPDATE_SUB_QUERY) || this.flags.has(core_1.QueryFlag.DELETE_SUB_QUERY)) {
|
|
623
|
+
type = enums_1.QueryType.UPDATE;
|
|
624
|
+
}
|
|
625
|
+
switch (type) {
|
|
497
626
|
case enums_1.QueryType.INSERT:
|
|
498
627
|
case enums_1.QueryType.UPDATE:
|
|
499
628
|
case enums_1.QueryType.DELETE:
|
|
@@ -516,7 +645,10 @@ class QueryBuilder {
|
|
|
516
645
|
/* istanbul ignore next */
|
|
517
646
|
alias = meta?.properties[f]?.fieldNames[0] ?? alias;
|
|
518
647
|
}
|
|
519
|
-
|
|
648
|
+
const ret = qb.as(alias);
|
|
649
|
+
// tag the instance, so it is possible to detect it easily
|
|
650
|
+
Object.defineProperty(ret, '__as', { enumerable: false, value: alias });
|
|
651
|
+
return ret;
|
|
520
652
|
}
|
|
521
653
|
clone() {
|
|
522
654
|
const qb = new QueryBuilder(this.mainAlias.entityName, this.metadata, this.driver, this.context, this.mainAlias.aliasName, this.connectionType, this.em);
|
|
@@ -524,7 +656,8 @@ class QueryBuilder {
|
|
|
524
656
|
// clone array/object properties
|
|
525
657
|
const properties = [
|
|
526
658
|
'flags', '_populate', '_populateWhere', '_populateMap', '_joins', '_joinedProps', '_cond', '_data', '_orderBy',
|
|
527
|
-
'_schema', '_indexHint', '_cache', 'subQueries', 'lockMode', 'lockTables',
|
|
659
|
+
'_schema', '_indexHint', '_cache', 'subQueries', 'lockMode', 'lockTables', '_groupBy', '_having', '_returning',
|
|
660
|
+
'_comments', '_hintComments',
|
|
528
661
|
];
|
|
529
662
|
properties.forEach(prop => qb[prop] = core_1.Utils.copy(this[prop]));
|
|
530
663
|
/* istanbul ignore else */
|
|
@@ -552,14 +685,39 @@ class QueryBuilder {
|
|
|
552
685
|
}
|
|
553
686
|
return qb;
|
|
554
687
|
}
|
|
555
|
-
joinReference(field, alias, cond, type, path) {
|
|
688
|
+
joinReference(field, alias, cond, type, path, schema) {
|
|
556
689
|
this.ensureNotFinalized();
|
|
690
|
+
if (typeof field === 'object') {
|
|
691
|
+
const prop = {
|
|
692
|
+
name: '__subquery__',
|
|
693
|
+
kind: core_1.ReferenceKind.MANY_TO_ONE,
|
|
694
|
+
};
|
|
695
|
+
if (field instanceof QueryBuilder) {
|
|
696
|
+
prop.type = field.mainAlias.entityName;
|
|
697
|
+
prop.targetMeta = field.mainAlias.metadata;
|
|
698
|
+
field = field.getKnexQuery();
|
|
699
|
+
}
|
|
700
|
+
this._joins[`${this.alias}.${prop.name}#${alias}`] = {
|
|
701
|
+
prop,
|
|
702
|
+
alias,
|
|
703
|
+
type,
|
|
704
|
+
cond,
|
|
705
|
+
schema,
|
|
706
|
+
subquery: field.toString(),
|
|
707
|
+
ownerAlias: this.alias,
|
|
708
|
+
};
|
|
709
|
+
return prop;
|
|
710
|
+
}
|
|
557
711
|
const [fromAlias, fromField] = this.helper.splitField(field);
|
|
558
|
-
const
|
|
712
|
+
const q = (str) => `'${str}'`;
|
|
713
|
+
if (!this._aliases[fromAlias]) {
|
|
714
|
+
throw new Error(`Trying to join ${q(fromField)} with alias ${q(fromAlias)}, but ${q(fromAlias)} is not a known alias. Available aliases are: ${Object.keys(this._aliases).map(q).join(', ')}.`);
|
|
715
|
+
}
|
|
716
|
+
const entityName = this._aliases[fromAlias].entityName;
|
|
559
717
|
const meta = this.metadata.get(entityName);
|
|
560
718
|
const prop = meta.properties[fromField];
|
|
561
719
|
if (!prop) {
|
|
562
|
-
throw new Error(`Trying to join ${field}, but ${fromField} is not a defined relation on ${meta.className}
|
|
720
|
+
throw new Error(`Trying to join ${q(field)}, but ${q(fromField)} is not a defined relation on ${meta.className}.`);
|
|
563
721
|
}
|
|
564
722
|
this.createAlias(prop.type, alias);
|
|
565
723
|
cond = core_1.QueryHelper.processWhere({
|
|
@@ -571,26 +729,26 @@ class QueryBuilder {
|
|
|
571
729
|
aliased: !this.type || [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(this.type),
|
|
572
730
|
});
|
|
573
731
|
let aliasedName = `${fromAlias}.${prop.name}#${alias}`;
|
|
574
|
-
path
|
|
575
|
-
if (prop.
|
|
576
|
-
this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond);
|
|
732
|
+
path ??= `${(Object.values(this._joins).find(j => j.alias === fromAlias)?.path ?? entityName)}.${prop.name}`;
|
|
733
|
+
if (prop.kind === core_1.ReferenceKind.ONE_TO_MANY) {
|
|
734
|
+
this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond, schema);
|
|
577
735
|
}
|
|
578
|
-
else if (prop.
|
|
736
|
+
else if (prop.kind === core_1.ReferenceKind.MANY_TO_MANY) {
|
|
579
737
|
let pivotAlias = alias;
|
|
580
738
|
if (type !== 'pivotJoin') {
|
|
581
739
|
const oldPivotAlias = this.getAliasForJoinPath(path + '[pivot]');
|
|
582
740
|
pivotAlias = oldPivotAlias ?? this.getNextAlias(prop.pivotEntity);
|
|
583
741
|
aliasedName = `${fromAlias}.${prop.name}#${pivotAlias}`;
|
|
584
742
|
}
|
|
585
|
-
const joins = this.helper.joinManyToManyReference(prop, fromAlias, alias, pivotAlias, type, cond, path);
|
|
743
|
+
const joins = this.helper.joinManyToManyReference(prop, fromAlias, alias, pivotAlias, type, cond, path, schema);
|
|
586
744
|
Object.assign(this._joins, joins);
|
|
587
745
|
this.createAlias(prop.pivotEntity, pivotAlias);
|
|
588
746
|
}
|
|
589
|
-
else if (prop.
|
|
590
|
-
this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond);
|
|
747
|
+
else if (prop.kind === core_1.ReferenceKind.ONE_TO_ONE) {
|
|
748
|
+
this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond, schema);
|
|
591
749
|
}
|
|
592
750
|
else { // MANY_TO_ONE
|
|
593
|
-
this._joins[aliasedName] = this.helper.joinManyToOneReference(prop, fromAlias, alias, type, cond);
|
|
751
|
+
this._joins[aliasedName] = this.helper.joinManyToOneReference(prop, fromAlias, alias, type, cond, schema);
|
|
594
752
|
}
|
|
595
753
|
if (!this._joins[aliasedName].path && path) {
|
|
596
754
|
this._joins[aliasedName].path = path;
|
|
@@ -599,34 +757,49 @@ class QueryBuilder {
|
|
|
599
757
|
}
|
|
600
758
|
prepareFields(fields, type = 'where') {
|
|
601
759
|
const ret = [];
|
|
760
|
+
const getFieldName = (name) => {
|
|
761
|
+
if (type === 'groupBy') {
|
|
762
|
+
return this.helper.mapper(name, this.type, undefined, null);
|
|
763
|
+
}
|
|
764
|
+
return this.helper.mapper(name, this.type);
|
|
765
|
+
};
|
|
602
766
|
fields.forEach(field => {
|
|
767
|
+
const rawField = core_1.RawQueryFragment.getKnownFragment(field);
|
|
768
|
+
if (rawField) {
|
|
769
|
+
const sql = this.platform.formatQuery(rawField.sql, rawField.params);
|
|
770
|
+
ret.push(this.knex.raw(sql));
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
603
773
|
if (!core_1.Utils.isString(field)) {
|
|
604
|
-
|
|
774
|
+
ret.push(field);
|
|
775
|
+
return;
|
|
605
776
|
}
|
|
606
777
|
const join = Object.keys(this._joins).find(k => field === k.substring(0, k.indexOf('#')));
|
|
607
778
|
if (join && type === 'where') {
|
|
608
|
-
|
|
779
|
+
ret.push(...this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[join]));
|
|
780
|
+
return;
|
|
609
781
|
}
|
|
610
782
|
const [a, f] = this.helper.splitField(field);
|
|
611
783
|
const prop = this.helper.getProperty(f, a);
|
|
612
784
|
/* istanbul ignore next */
|
|
613
|
-
if (prop && [core_1.
|
|
785
|
+
if (prop && [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
|
|
614
786
|
return;
|
|
615
787
|
}
|
|
616
788
|
if (prop?.embedded) {
|
|
617
|
-
const
|
|
618
|
-
|
|
789
|
+
const name = prop.embeddedPath?.join('.') ?? prop.fieldNames[0];
|
|
790
|
+
const aliased = this._aliases[a] ? `${a}.${name}` : name;
|
|
791
|
+
ret.push(getFieldName(aliased));
|
|
619
792
|
return;
|
|
620
793
|
}
|
|
621
|
-
if (prop?.
|
|
794
|
+
if (prop?.kind === core_1.ReferenceKind.EMBEDDED) {
|
|
622
795
|
if (prop.object) {
|
|
623
|
-
ret.push(
|
|
796
|
+
ret.push(getFieldName(prop.fieldNames[0]));
|
|
624
797
|
}
|
|
625
798
|
else {
|
|
626
799
|
const nest = (prop) => {
|
|
627
800
|
for (const childProp of Object.values(prop.embeddedProps)) {
|
|
628
|
-
if (childProp.fieldNames) {
|
|
629
|
-
ret.push(
|
|
801
|
+
if (childProp.fieldNames && (childProp.kind !== core_1.ReferenceKind.EMBEDDED || childProp.object) && childProp.persist !== false) {
|
|
802
|
+
ret.push(getFieldName(childProp.fieldNames[0]));
|
|
630
803
|
}
|
|
631
804
|
else {
|
|
632
805
|
nest(childProp);
|
|
@@ -637,11 +810,15 @@ class QueryBuilder {
|
|
|
637
810
|
}
|
|
638
811
|
return;
|
|
639
812
|
}
|
|
640
|
-
|
|
813
|
+
if (prop && prop.fieldNames.length > 1) {
|
|
814
|
+
ret.push(...prop.fieldNames.map(f => getFieldName(f)));
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
ret.push(getFieldName(field));
|
|
641
818
|
});
|
|
642
819
|
const meta = this.mainAlias.metadata;
|
|
643
820
|
/* istanbul ignore next */
|
|
644
|
-
const requiresSQLConversion = meta?.props.filter(p => p.
|
|
821
|
+
const requiresSQLConversion = meta?.props.filter(p => p.hasConvertToJSValueSQL && p.persist !== false) ?? [];
|
|
645
822
|
if (this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES) && (fields.includes('*') || fields.includes(`${this.mainAlias.aliasName}.*`)) && requiresSQLConversion.length > 0) {
|
|
646
823
|
requiresSQLConversion.forEach(p => ret.push(this.helper.mapper(p.name, this.type)));
|
|
647
824
|
}
|
|
@@ -650,7 +827,7 @@ class QueryBuilder {
|
|
|
650
827
|
const cols = this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[f]);
|
|
651
828
|
ret.push(...cols);
|
|
652
829
|
}
|
|
653
|
-
if (this._joins[f].prop.
|
|
830
|
+
if (this._joins[f].prop.kind !== core_1.ReferenceKind.ONE_TO_ONE && this._joins[f].inverseJoinColumns) {
|
|
654
831
|
this._joins[f].inverseJoinColumns.forEach(inverseJoinColumn => {
|
|
655
832
|
core_1.Utils.renameKey(this._cond, inverseJoinColumn, `${this._joins[f].alias}.${inverseJoinColumn}`);
|
|
656
833
|
});
|
|
@@ -669,9 +846,9 @@ class QueryBuilder {
|
|
|
669
846
|
}
|
|
670
847
|
if (data) {
|
|
671
848
|
if (core_1.Utils.isEntity(data)) {
|
|
672
|
-
data = (0, core_1.
|
|
849
|
+
data = this.em?.getComparator().prepareEntity(data) ?? (0, core_1.serialize)(data);
|
|
673
850
|
}
|
|
674
|
-
this._data = this.helper.processData(data, this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES));
|
|
851
|
+
this._data = this.helper.processData(data, this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES), false);
|
|
675
852
|
}
|
|
676
853
|
if (cond) {
|
|
677
854
|
this.where(cond);
|
|
@@ -681,6 +858,8 @@ class QueryBuilder {
|
|
|
681
858
|
getQueryBase() {
|
|
682
859
|
const qb = this.getKnex();
|
|
683
860
|
const schema = this.getSchema(this.mainAlias);
|
|
861
|
+
// Joined tables doesn't need to belong to the same schema as the main table
|
|
862
|
+
const joinSchema = this._schema ?? this.em?.schema ?? schema;
|
|
684
863
|
if (schema) {
|
|
685
864
|
qb.withSchema(schema);
|
|
686
865
|
}
|
|
@@ -699,12 +878,12 @@ class QueryBuilder {
|
|
|
699
878
|
else if (this.flags.has(core_1.QueryFlag.DISTINCT)) {
|
|
700
879
|
qb.distinct();
|
|
701
880
|
}
|
|
702
|
-
this.helper.processJoins(qb, this._joins,
|
|
881
|
+
this.helper.processJoins(qb, this._joins, joinSchema);
|
|
703
882
|
break;
|
|
704
883
|
case enums_1.QueryType.COUNT: {
|
|
705
884
|
const m = this.flags.has(core_1.QueryFlag.DISTINCT) ? 'countDistinct' : 'count';
|
|
706
885
|
qb[m]({ count: this._fields.map(f => this.helper.mapper(f, this.type)) });
|
|
707
|
-
this.helper.processJoins(qb, this._joins,
|
|
886
|
+
this.helper.processJoins(qb, this._joins, joinSchema);
|
|
708
887
|
break;
|
|
709
888
|
}
|
|
710
889
|
case enums_1.QueryType.INSERT:
|
|
@@ -723,6 +902,24 @@ class QueryBuilder {
|
|
|
723
902
|
}
|
|
724
903
|
return qb;
|
|
725
904
|
}
|
|
905
|
+
applyDiscriminatorCondition() {
|
|
906
|
+
const meta = this.mainAlias.metadata;
|
|
907
|
+
if (!meta?.discriminatorValue) {
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
const types = Object.values(meta.root.discriminatorMap).map(cls => this.metadata.find(cls));
|
|
911
|
+
const children = [];
|
|
912
|
+
const lookUpChildren = (ret, type) => {
|
|
913
|
+
const children = types.filter(meta2 => meta2.extends === type);
|
|
914
|
+
children.forEach(m => lookUpChildren(ret, m.className));
|
|
915
|
+
ret.push(...children.filter(c => c.discriminatorValue));
|
|
916
|
+
return children;
|
|
917
|
+
};
|
|
918
|
+
lookUpChildren(children, meta.className);
|
|
919
|
+
this.andWhere({
|
|
920
|
+
[meta.root.discriminatorColumn]: children.length > 0 ? { $in: [meta.discriminatorValue, ...children.map(c => c.discriminatorValue)] } : meta.discriminatorValue,
|
|
921
|
+
});
|
|
922
|
+
}
|
|
726
923
|
finalize() {
|
|
727
924
|
if (this.finalized) {
|
|
728
925
|
return;
|
|
@@ -731,10 +928,11 @@ class QueryBuilder {
|
|
|
731
928
|
this.select('*');
|
|
732
929
|
}
|
|
733
930
|
const meta = this.mainAlias.metadata;
|
|
931
|
+
this.applyDiscriminatorCondition();
|
|
734
932
|
if (meta && this.flags.has(core_1.QueryFlag.AUTO_JOIN_ONE_TO_ONE_OWNER)) {
|
|
735
933
|
const relationsToPopulate = this._populate.map(({ field }) => field);
|
|
736
934
|
meta.relations
|
|
737
|
-
.filter(prop => prop.
|
|
935
|
+
.filter(prop => prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
|
|
738
936
|
.map(prop => ({ field: prop.name }))
|
|
739
937
|
.forEach(item => this._populate.push(item));
|
|
740
938
|
}
|
|
@@ -743,12 +941,10 @@ class QueryBuilder {
|
|
|
743
941
|
const aliasedField = `${fromAlias}.${fromField}`;
|
|
744
942
|
const join = Object.keys(this._joins).find(k => `${aliasedField}#${this._joins[k].alias}` === k);
|
|
745
943
|
if (join && this._joins[join] && this.helper.isOneToOneInverse(fromField)) {
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
if (this.metadata.find(field)?.pivotTable) { // pivot table entity
|
|
749
|
-
this.autoJoinPivotTable(field);
|
|
944
|
+
this._populateMap[join] = this._joins[join].alias;
|
|
945
|
+
return;
|
|
750
946
|
}
|
|
751
|
-
|
|
947
|
+
if (meta && this.helper.isOneToOneInverse(fromField)) {
|
|
752
948
|
const prop = meta.properties[fromField];
|
|
753
949
|
const alias = this.getNextAlias(prop.pivotEntity ?? prop.type);
|
|
754
950
|
const aliasedName = `${fromAlias}.${prop.name}#${alias}`;
|
|
@@ -764,14 +960,20 @@ class QueryBuilder {
|
|
|
764
960
|
const aliased = this.knex.ref(prop.fieldNames[0]).toString();
|
|
765
961
|
return `${prop.formula(alias)} as ${aliased}`;
|
|
766
962
|
})
|
|
767
|
-
.filter(field => !this._fields.
|
|
768
|
-
|
|
963
|
+
.filter(field => !this._fields.some(f => {
|
|
964
|
+
if (f instanceof core_1.RawQueryFragment) {
|
|
965
|
+
return f.sql === field && f.params.length === 0;
|
|
966
|
+
}
|
|
967
|
+
return f === field;
|
|
968
|
+
}))
|
|
969
|
+
.forEach(field => this._fields.push((0, core_1.raw)(field)));
|
|
769
970
|
}
|
|
971
|
+
this.processPopulateWhere();
|
|
770
972
|
core_1.QueryHelper.processObjectParams(this._data);
|
|
771
973
|
core_1.QueryHelper.processObjectParams(this._cond);
|
|
772
974
|
core_1.QueryHelper.processObjectParams(this._having);
|
|
773
|
-
// automatically enable paginate flag when we detect to-many joins
|
|
774
|
-
if (!this.flags.has(core_1.QueryFlag.DISABLE_PAGINATE) && this.hasToManyJoins()) {
|
|
975
|
+
// automatically enable paginate flag when we detect to-many joins, but only if there is no `group by` clause
|
|
976
|
+
if (!this.flags.has(core_1.QueryFlag.DISABLE_PAGINATE) && this._groupBy.length === 0 && this.hasToManyJoins()) {
|
|
775
977
|
this.flags.add(core_1.QueryFlag.PAGINATE);
|
|
776
978
|
}
|
|
777
979
|
if (meta && this.flags.has(core_1.QueryFlag.PAGINATE) && (this._limit > 0 || this._offset > 0)) {
|
|
@@ -782,17 +984,54 @@ class QueryBuilder {
|
|
|
782
984
|
}
|
|
783
985
|
this.finalized = true;
|
|
784
986
|
}
|
|
987
|
+
processPopulateWhere() {
|
|
988
|
+
if (this._populateWhere == null || this._populateWhere === core_1.PopulateHint.ALL) {
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
const joins = Object.values(this._joins);
|
|
992
|
+
joins.forEach(join => {
|
|
993
|
+
join.cond_ = join.cond;
|
|
994
|
+
join.cond = {};
|
|
995
|
+
});
|
|
996
|
+
const replaceOnConditions = (cond) => {
|
|
997
|
+
Object.keys(cond).forEach(k => {
|
|
998
|
+
if (core_1.Utils.isOperator(k)) {
|
|
999
|
+
if (Array.isArray(cond[k])) {
|
|
1000
|
+
return cond[k].forEach((c) => replaceOnConditions(c));
|
|
1001
|
+
}
|
|
1002
|
+
return replaceOnConditions(cond[k]);
|
|
1003
|
+
}
|
|
1004
|
+
const [a] = this.helper.splitField(k);
|
|
1005
|
+
const join = joins.find(j => j.alias === a);
|
|
1006
|
+
if (join) {
|
|
1007
|
+
join.cond = { ...join.cond, [k]: cond[k] };
|
|
1008
|
+
}
|
|
1009
|
+
});
|
|
1010
|
+
};
|
|
1011
|
+
if (this._populateWhere === core_1.PopulateHint.INFER) {
|
|
1012
|
+
replaceOnConditions(this._cond);
|
|
1013
|
+
}
|
|
1014
|
+
else if (typeof this._populateWhere === 'object') {
|
|
1015
|
+
const cond = CriteriaNodeFactory_1.CriteriaNodeFactory
|
|
1016
|
+
.createNode(this.metadata, this.mainAlias.entityName, this._populateWhere)
|
|
1017
|
+
.process(this);
|
|
1018
|
+
replaceOnConditions(cond);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
785
1021
|
hasToManyJoins() {
|
|
786
1022
|
return Object.values(this._joins).some(join => {
|
|
787
|
-
return [core_1.
|
|
1023
|
+
return [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(join.prop.kind);
|
|
788
1024
|
});
|
|
789
1025
|
}
|
|
790
1026
|
wrapPaginateSubQuery(meta) {
|
|
791
1027
|
const pks = this.prepareFields(meta.primaryKeys, 'sub-query');
|
|
792
1028
|
const subQuery = this.clone().select(pks).groupBy(pks).limit(this._limit);
|
|
1029
|
+
// revert the on conditions added via populateWhere, we want to apply those only once
|
|
1030
|
+
Object.values(subQuery._joins).forEach(join => join.cond = join.cond_ ?? {});
|
|
793
1031
|
if (this._offset) {
|
|
794
1032
|
subQuery.offset(this._offset);
|
|
795
1033
|
}
|
|
1034
|
+
const addToSelect = [];
|
|
796
1035
|
if (this._orderBy.length > 0) {
|
|
797
1036
|
const orderBy = [];
|
|
798
1037
|
for (const orderMap of this._orderBy) {
|
|
@@ -800,28 +1039,38 @@ class QueryBuilder {
|
|
|
800
1039
|
const [a, f] = this.helper.splitField(field);
|
|
801
1040
|
const prop = this.helper.getProperty(f, a);
|
|
802
1041
|
const type = this.platform.castColumn(prop);
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
1042
|
+
const fieldName = this.helper.mapper(field, this.type, undefined, null);
|
|
1043
|
+
if (!prop?.persist && !prop?.formula) {
|
|
1044
|
+
addToSelect.push(fieldName);
|
|
1045
|
+
}
|
|
1046
|
+
orderBy.push({ [(0, core_1.raw)(`min(${this.knex.ref(fieldName)}${type})`)]: direction });
|
|
806
1047
|
}
|
|
807
1048
|
}
|
|
808
1049
|
subQuery.orderBy(orderBy);
|
|
809
1050
|
}
|
|
810
1051
|
subQuery.finalized = true;
|
|
811
1052
|
const knexQuery = subQuery.as(this.mainAlias.aliasName).clearSelect().select(pks);
|
|
1053
|
+
if (addToSelect.length > 0) {
|
|
1054
|
+
addToSelect.forEach(prop => {
|
|
1055
|
+
const field = this._fields.find(field => {
|
|
1056
|
+
if (typeof field === 'object' && field && '__as' in field) {
|
|
1057
|
+
return field.__as === prop;
|
|
1058
|
+
}
|
|
1059
|
+
// not perfect, but should work most of the time, ideally we should check only the alias (`... as alias`)
|
|
1060
|
+
return field.toString().includes(prop);
|
|
1061
|
+
});
|
|
1062
|
+
if (field) {
|
|
1063
|
+
knexQuery.select(field);
|
|
1064
|
+
}
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
812
1067
|
// multiple sub-queries are needed to get around mysql limitations with order by + limit + where in + group by (o.O)
|
|
813
1068
|
// https://stackoverflow.com/questions/17892762/mysql-this-version-of-mysql-doesnt-yet-support-limit-in-all-any-some-subqu
|
|
814
1069
|
const subSubQuery = this.getKnex().select(pks).from(knexQuery);
|
|
1070
|
+
subSubQuery.__raw = true; // tag it as there is now way to check via `instanceof`
|
|
815
1071
|
this._limit = undefined;
|
|
816
1072
|
this._offset = undefined;
|
|
817
|
-
const cond = this._cond;
|
|
818
1073
|
this.select(this._fields).where({ [core_1.Utils.getPrimaryKeyHash(meta.primaryKeys)]: { $in: subSubQuery } });
|
|
819
|
-
if (this._populateWhere === core_1.PopulateHint.INFER) {
|
|
820
|
-
this.andWhere(cond);
|
|
821
|
-
}
|
|
822
|
-
else if (typeof this._populateWhere === 'object') {
|
|
823
|
-
this.andWhere(this._populateWhere);
|
|
824
|
-
}
|
|
825
1074
|
}
|
|
826
1075
|
wrapModifySubQuery(meta) {
|
|
827
1076
|
const subQuery = this.clone();
|
|
@@ -835,21 +1084,10 @@ class QueryBuilder {
|
|
|
835
1084
|
[core_1.Utils.getPrimaryKeyHash(meta.primaryKeys)]: { $in: subSubQuery },
|
|
836
1085
|
});
|
|
837
1086
|
}
|
|
838
|
-
autoJoinPivotTable(field) {
|
|
839
|
-
const pivotMeta = this.metadata.find(field);
|
|
840
|
-
const owner = pivotMeta.relations[0];
|
|
841
|
-
const inverse = pivotMeta.relations[1];
|
|
842
|
-
const prop = this._cond[pivotMeta.name + '.' + owner.name] || this._orderBy[pivotMeta.name + '.' + owner.name] ? inverse : owner;
|
|
843
|
-
const pivotAlias = this.getNextAlias(pivotMeta.name);
|
|
844
|
-
this._joins[field] = this.helper.joinPivotTable(field, prop, this.mainAlias.aliasName, pivotAlias, 'leftJoin');
|
|
845
|
-
core_1.Utils.renameKey(this._cond, `${field}.${owner.name}`, core_1.Utils.getPrimaryKeyHash(owner.fieldNames.map(fieldName => `${pivotAlias}.${fieldName}`)));
|
|
846
|
-
core_1.Utils.renameKey(this._cond, `${field}.${inverse.name}`, core_1.Utils.getPrimaryKeyHash(inverse.fieldNames.map(fieldName => `${pivotAlias}.${fieldName}`)));
|
|
847
|
-
this._populateMap[field] = this._joins[field].alias;
|
|
848
|
-
}
|
|
849
1087
|
getSchema(alias) {
|
|
850
1088
|
const { metadata } = alias;
|
|
851
1089
|
const metaSchema = metadata?.schema && metadata.schema !== '*' ? metadata.schema : undefined;
|
|
852
|
-
return this._schema ?? metaSchema ?? this.em?.config.get('schema');
|
|
1090
|
+
return this._schema ?? metaSchema ?? this.em?.schema ?? this.em?.config.get('schema');
|
|
853
1091
|
}
|
|
854
1092
|
createAlias(entityName, aliasName, subQuery) {
|
|
855
1093
|
const metadata = this.metadata.find(entityName);
|
|
@@ -865,11 +1103,11 @@ class QueryBuilder {
|
|
|
865
1103
|
fromSubQuery(target, aliasName) {
|
|
866
1104
|
const subQuery = target.getKnexQuery();
|
|
867
1105
|
const { entityName } = target.mainAlias;
|
|
868
|
-
aliasName
|
|
1106
|
+
aliasName ??= this.getNextAlias(entityName);
|
|
869
1107
|
this.createMainAlias(entityName, aliasName, subQuery);
|
|
870
1108
|
}
|
|
871
1109
|
fromEntityName(entityName, aliasName) {
|
|
872
|
-
aliasName
|
|
1110
|
+
aliasName ??= this._mainAlias?.aliasName ?? this.getNextAlias(entityName);
|
|
873
1111
|
this.createMainAlias(entityName, aliasName);
|
|
874
1112
|
}
|
|
875
1113
|
createQueryBuilderHelper() {
|
|
@@ -897,6 +1135,9 @@ class QueryBuilder {
|
|
|
897
1135
|
if (this._data) {
|
|
898
1136
|
object.data = this._data;
|
|
899
1137
|
}
|
|
1138
|
+
if (this._schema) {
|
|
1139
|
+
object.schema = this._schema;
|
|
1140
|
+
}
|
|
900
1141
|
if (!core_1.Utils.isEmpty(this._cond)) {
|
|
901
1142
|
object.where = this._cond;
|
|
902
1143
|
}
|