@mikro-orm/knex 6.0.0-dev.27 → 6.0.0-dev.271

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.
Files changed (44) hide show
  1. package/AbstractSqlConnection.d.ts +22 -4
  2. package/AbstractSqlConnection.js +48 -18
  3. package/AbstractSqlDriver.d.ts +32 -24
  4. package/AbstractSqlDriver.js +588 -270
  5. package/AbstractSqlPlatform.d.ts +9 -4
  6. package/AbstractSqlPlatform.js +40 -5
  7. package/MonkeyPatchable.d.ts +1 -0
  8. package/MonkeyPatchable.js +3 -0
  9. package/PivotCollectionPersister.d.ts +17 -0
  10. package/PivotCollectionPersister.js +128 -0
  11. package/README.md +64 -107
  12. package/SqlEntityManager.d.ts +4 -8
  13. package/SqlEntityManager.js +8 -12
  14. package/SqlEntityRepository.d.ts +7 -6
  15. package/SqlEntityRepository.js +11 -8
  16. package/index.mjs +22 -3
  17. package/package.json +8 -8
  18. package/query/ArrayCriteriaNode.d.ts +5 -4
  19. package/query/ArrayCriteriaNode.js +7 -2
  20. package/query/CriteriaNode.d.ts +12 -10
  21. package/query/CriteriaNode.js +22 -9
  22. package/query/CriteriaNodeFactory.d.ts +6 -6
  23. package/query/CriteriaNodeFactory.js +32 -16
  24. package/query/ObjectCriteriaNode.d.ts +5 -4
  25. package/query/ObjectCriteriaNode.js +72 -28
  26. package/query/QueryBuilder.d.ts +58 -26
  27. package/query/QueryBuilder.js +438 -142
  28. package/query/QueryBuilderHelper.d.ts +13 -18
  29. package/query/QueryBuilderHelper.js +191 -164
  30. package/query/ScalarCriteriaNode.d.ts +4 -3
  31. package/query/ScalarCriteriaNode.js +14 -4
  32. package/query/enums.d.ts +7 -0
  33. package/query/enums.js +10 -2
  34. package/schema/DatabaseSchema.d.ts +5 -2
  35. package/schema/DatabaseSchema.js +26 -8
  36. package/schema/DatabaseTable.d.ts +12 -4
  37. package/schema/DatabaseTable.js +371 -46
  38. package/schema/SchemaComparator.d.ts +7 -5
  39. package/schema/SchemaComparator.js +68 -35
  40. package/schema/SchemaHelper.d.ts +7 -5
  41. package/schema/SchemaHelper.js +25 -11
  42. package/schema/SqlSchemaGenerator.d.ts +3 -4
  43. package/schema/SqlSchemaGenerator.js +71 -28
  44. package/typings.d.ts +30 -11
@@ -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
+ loggerContext;
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, loggerContext) {
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
- /** @internal */
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.loggerContext = loggerContext;
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 = [this.raw('*')];
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 = 'innerJoin', path) {
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
- leftJoin(field, alias, cond = {}) {
131
- return this.join(field, alias, cond, 'leftJoin');
167
+ innerJoinLateral(field, alias, cond = {}, schema) {
168
+ this.join(field, alias, cond, enums_1.JoinType.innerJoinLateral, undefined, schema);
169
+ return this;
132
170
  }
133
- joinAndSelect(field, alias, cond = {}, type = 'innerJoin', path) {
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
- const prop = this.joinReference(field, alias, cond, type, path);
138
- this.addSelect(this.getFieldsForJoinedLoad(prop, alias));
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, 'leftJoin');
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
- getFieldsForJoinedLoad(prop, alias) {
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, this._populate))
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
- if (core_1.Utils.isString(cond)) {
169
- cond = { [`(${cond})`]: core_1.Utils.asArray(params) };
170
- operator = operator || '$and';
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})`]: core_1.Utils.asArray(params) };
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 = this._onConflict || [];
246
- this._onConflict.push({ fields: core_1.Utils.asArray(fields).map(f => f.toString()) });
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 `/* ... *&#8205;/`. Some characters are forbidden such as `/*, *&#8205;/` 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 `/*+ ... *&#8205;/`. 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
- core_1.Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type ?? enums_1.QueryType.SELECT, this._cond, qb), this._cond && !this._onConflict);
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(this.type ?? enums_1.QueryType.SELECT, this._having, qb, undefined, 'having'), this._having);
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(this.type ?? enums_1.QueryType.SELECT, this._orderBy, this._populateMap);
363
- if (queryOrder) {
364
- return qb.orderByRaw(queryOrder);
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.helper.appendOnConflictClause(this.type ?? enums_1.QueryType.SELECT, this._onConflict, qb), this._onConflict);
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(this.type ?? enums_1.QueryType.SELECT, qb, this.mainAlias.metadata);
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.getKnexQuery().toSQL().toNative().sql;
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.getKnexQuery().toSQL().toNative().bindings;
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.getKnexQuery().toSQL();
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 = Object.values(this._joins).find(j => j.path === path);
406
- if (path.endsWith('[pivot]') && join) {
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', mapResults = true) {
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 type = this.connectionType || (method === 'run' ? 'write' : 'read');
437
- const res = await this.driver.getConnection(type).execute(query.sql, query.bindings, method, this.context);
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.loggerContext);
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 === 'all' && Array.isArray(res)) {
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
- const mapped = res.map(r => this.driver.mapResult(r, meta, this._populate, this, map));
446
- await this.em?.storeCache(this._cache, cached, mapped);
447
- return mapped;
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
- let res = await this.execute('all', true);
465
- if (this._joinedProps.size > 0) {
466
- res = this.driver.mergeJoinedResult(res, this.mainAlias.metadata);
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
- return res.map(r => this.em.map(this.mainAlias.entityName, r, { schema: this._schema }));
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
- switch (this.type ?? enums_1.QueryType.SELECT) {
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
- return qb.as(alias);
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 entityName = this._aliases[fromAlias]?.entityName;
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 ?? (path = `${(Object.values(this._joins).find(j => j.alias === fromAlias)?.path ?? entityName)}.${prop.name}`);
804
+ path ??= `${(Object.values(this._joins).find(j => j.alias === fromAlias)?.path ?? entityName)}.${prop.name}`;
584
805
  if (prop.kind === core_1.ReferenceKind.ONE_TO_MANY) {
585
- this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond);
806
+ this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond, schema);
586
807
  }
587
808
  else if (prop.kind === core_1.ReferenceKind.MANY_TO_MANY) {
588
809
  let pivotAlias = alias;
589
- if (type !== 'pivotJoin') {
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
819
  else if (prop.kind === core_1.ReferenceKind.ONE_TO_ONE) {
599
- this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond);
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,13 +829,27 @@ 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
- return ret.push(field);
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
- return ret.push(...this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[join]));
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);
@@ -623,19 +858,20 @@ class QueryBuilder {
623
858
  return;
624
859
  }
625
860
  if (prop?.embedded) {
626
- const fieldName = this.helper.mapper(prop.fieldNames[0], this.type);
627
- ret.push(fieldName);
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
866
  if (prop?.kind === core_1.ReferenceKind.EMBEDDED) {
631
867
  if (prop.object) {
632
- ret.push(this.helper.mapper(prop.fieldNames[0], this.type));
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(this.helper.mapper(childProp.fieldNames[0], this.type));
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
- ret.push(this.helper.mapper(field, this.type));
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.customType?.convertToJSValueSQL) ?? [];
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.kind !== core_1.ReferenceKind.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, schema);
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, schema);
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.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
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
- return this._populateMap[join] = this._joins[join].alias;
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
- else if (meta && this.helper.isOneToOneInverse(fromField)) {
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, 'leftJoin');
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.includes(field))
778
- .forEach(field => this.addSelect(field));
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,6 +1051,37 @@ 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
1087
  return [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(join.prop.kind);
@@ -800,9 +1090,12 @@ class QueryBuilder {
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
- orderBy.push({
814
- [`min(${this.ref(this.helper.mapper(field, this.type, undefined, null))}${type})`]: direction,
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 ?? (aliasName = this.getNextAlias(entityName));
1170
+ aliasName ??= this.getNextAlias(entityName);
879
1171
  this.createMainAlias(entityName, aliasName, subQuery);
880
1172
  }
881
1173
  fromEntityName(entityName, aliasName) {
882
- aliasName ?? (aliasName = this._mainAlias?.aliasName ?? this.getNextAlias(entityName));
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
  }