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