@mikro-orm/knex 6.0.0-dev.9 → 6.0.0-dev.91

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 (39) hide show
  1. package/AbstractSqlConnection.d.ts +5 -2
  2. package/AbstractSqlConnection.js +25 -17
  3. package/AbstractSqlDriver.d.ts +19 -18
  4. package/AbstractSqlDriver.js +171 -70
  5. package/AbstractSqlPlatform.d.ts +1 -0
  6. package/AbstractSqlPlatform.js +26 -5
  7. package/MonkeyPatchable.d.ts +1 -0
  8. package/MonkeyPatchable.js +3 -0
  9. package/README.md +64 -107
  10. package/SqlEntityManager.d.ts +2 -5
  11. package/SqlEntityManager.js +5 -9
  12. package/SqlEntityRepository.d.ts +6 -4
  13. package/SqlEntityRepository.js +10 -8
  14. package/index.mjs +20 -6
  15. package/package.json +7 -7
  16. package/query/ArrayCriteriaNode.d.ts +3 -3
  17. package/query/CriteriaNode.d.ts +8 -8
  18. package/query/CriteriaNode.js +9 -9
  19. package/query/CriteriaNodeFactory.d.ts +6 -6
  20. package/query/CriteriaNodeFactory.js +10 -13
  21. package/query/ObjectCriteriaNode.d.ts +3 -3
  22. package/query/ObjectCriteriaNode.js +12 -8
  23. package/query/QueryBuilder.d.ts +27 -16
  24. package/query/QueryBuilder.js +250 -91
  25. package/query/QueryBuilderHelper.d.ts +6 -9
  26. package/query/QueryBuilderHelper.js +112 -96
  27. package/query/ScalarCriteriaNode.d.ts +3 -2
  28. package/query/ScalarCriteriaNode.js +9 -6
  29. package/query/enums.js +1 -1
  30. package/schema/DatabaseSchema.d.ts +4 -1
  31. package/schema/DatabaseSchema.js +22 -6
  32. package/schema/DatabaseTable.d.ts +2 -1
  33. package/schema/DatabaseTable.js +25 -24
  34. package/schema/SchemaComparator.js +9 -2
  35. package/schema/SchemaHelper.d.ts +5 -3
  36. package/schema/SchemaHelper.js +10 -4
  37. package/schema/SqlSchemaGenerator.d.ts +3 -5
  38. package/schema/SqlSchemaGenerator.js +41 -20
  39. package/typings.d.ts +10 -7
@@ -1,6 +1,19 @@
1
1
  "use strict";
2
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
5
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
6
+ };
7
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
8
+ if (kind === "m") throw new TypeError("Private method is not writable");
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
10
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
11
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
12
+ };
13
+ var _QueryBuilder_query;
2
14
  Object.defineProperty(exports, "__esModule", { value: true });
3
15
  exports.QueryBuilder = void 0;
16
+ const util_1 = require("util");
4
17
  const core_1 = require("@mikro-orm/core");
5
18
  const enums_1 = require("./enums");
6
19
  const QueryBuilderHelper_1 = require("./QueryBuilderHelper");
@@ -39,12 +52,13 @@ class QueryBuilder {
39
52
  /**
40
53
  * @internal
41
54
  */
42
- constructor(entityName, metadata, driver, context, alias, connectionType, em) {
55
+ constructor(entityName, metadata, driver, context, alias, connectionType, em, logging) {
43
56
  this.metadata = metadata;
44
57
  this.driver = driver;
45
58
  this.context = context;
46
59
  this.connectionType = connectionType;
47
60
  this.em = em;
61
+ this.logging = logging;
48
62
  /** @internal */
49
63
  this._populate = [];
50
64
  /** @internal */
@@ -63,6 +77,7 @@ class QueryBuilder {
63
77
  this._aliases = {};
64
78
  this.platform = this.driver.getPlatform();
65
79
  this.knex = this.driver.getConnection(this.connectionType).getKnex();
80
+ _QueryBuilder_query.set(this, void 0);
66
81
  if (alias) {
67
82
  this.aliasCounter++;
68
83
  this._explicitAlias = true;
@@ -115,26 +130,26 @@ class QueryBuilder {
115
130
  this._fields = this.mainAlias.metadata.primaryKeys;
116
131
  }
117
132
  else {
118
- this._fields = [this.raw('*')];
133
+ this._fields = [(0, core_1.raw)('*')];
119
134
  }
120
135
  if (distinct) {
121
136
  this.flags.add(core_1.QueryFlag.DISTINCT);
122
137
  }
123
138
  return this.init(enums_1.QueryType.COUNT);
124
139
  }
125
- join(field, alias, cond = {}, type = 'innerJoin', path) {
126
- this.joinReference(field, alias, cond, type, path);
140
+ join(field, alias, cond = {}, type = 'innerJoin', path, schema) {
141
+ this.joinReference(field, alias, cond, type, path, schema);
127
142
  return this;
128
143
  }
129
- leftJoin(field, alias, cond = {}) {
130
- return this.join(field, alias, cond, 'leftJoin');
144
+ leftJoin(field, alias, cond = {}, schema) {
145
+ return this.join(field, alias, cond, 'leftJoin', undefined, schema);
131
146
  }
132
- joinAndSelect(field, alias, cond = {}, type = 'innerJoin', path) {
147
+ joinAndSelect(field, alias, cond = {}, type = 'innerJoin', path, fields, schema) {
133
148
  if (!this.type) {
134
149
  this.select('*');
135
150
  }
136
- const prop = this.joinReference(field, alias, cond, type, path);
137
- this.addSelect(this.getFieldsForJoinedLoad(prop, alias));
151
+ const prop = this.joinReference(field, alias, cond, type, path, schema);
152
+ this.addSelect(this.getFieldsForJoinedLoad(prop, alias, fields));
138
153
  const [fromAlias] = this.helper.splitField(field);
139
154
  const populate = this._joinedProps.get(fromAlias);
140
155
  const item = { field: prop.name, strategy: core_1.LoadStrategy.JOINED, children: [] };
@@ -147,13 +162,16 @@ class QueryBuilder {
147
162
  this._joinedProps.set(alias, item);
148
163
  return this;
149
164
  }
150
- leftJoinAndSelect(field, alias, cond = {}) {
151
- return this.joinAndSelect(field, alias, cond, 'leftJoin');
165
+ leftJoinAndSelect(field, alias, cond = {}, fields, schema) {
166
+ return this.joinAndSelect(field, alias, cond, 'leftJoin', undefined, fields, schema);
152
167
  }
153
- getFieldsForJoinedLoad(prop, alias) {
168
+ innerJoinAndSelect(field, alias, cond = {}, fields, schema) {
169
+ return this.joinAndSelect(field, alias, cond, 'innerJoin', undefined, fields, schema);
170
+ }
171
+ getFieldsForJoinedLoad(prop, alias, explicitFields) {
154
172
  const fields = [];
155
173
  prop.targetMeta.props
156
- .filter(prop => this.platform.shouldHaveColumn(prop, this._populate))
174
+ .filter(prop => explicitFields ? explicitFields.includes(prop.name) || prop.primary : this.platform.shouldHaveColumn(prop, this._populate))
157
175
  .forEach(prop => fields.push(...this.driver.mapPropToFieldNames(this, prop, alias)));
158
176
  return fields;
159
177
  }
@@ -164,9 +182,15 @@ class QueryBuilder {
164
182
  }
165
183
  where(cond, params, operator) {
166
184
  this.ensureNotFinalized();
167
- if (core_1.Utils.isString(cond)) {
168
- cond = { [`(${cond})`]: core_1.Utils.asArray(params) };
169
- operator = operator || '$and';
185
+ const rawField = core_1.RawQueryFragment.getKnownFragment(cond);
186
+ if (rawField) {
187
+ const sql = this.platform.formatQuery(rawField.sql, rawField.params);
188
+ cond = { [(0, core_1.raw)(`(${sql})`)]: core_1.Utils.asArray(params) };
189
+ operator ??= '$and';
190
+ }
191
+ else if (core_1.Utils.isString(cond)) {
192
+ cond = { [(0, core_1.raw)(`(${cond})`, core_1.Utils.asArray(params))]: [] };
193
+ operator ??= '$and';
170
194
  }
171
195
  else {
172
196
  cond = core_1.QueryHelper.processWhere({
@@ -234,7 +258,7 @@ class QueryBuilder {
234
258
  having(cond = {}, params) {
235
259
  this.ensureNotFinalized();
236
260
  if (core_1.Utils.isString(cond)) {
237
- cond = { [`(${cond})`]: core_1.Utils.asArray(params) };
261
+ cond = { [(0, core_1.raw)(`(${cond})`, params)]: [] };
238
262
  }
239
263
  this._having = CriteriaNodeFactory_1.CriteriaNodeFactory.createNode(this.metadata, this.mainAlias.entityName, cond).process(this);
240
264
  return this;
@@ -262,6 +286,10 @@ class QueryBuilder {
262
286
  this._onConflict[this._onConflict.length - 1].merge = data;
263
287
  return this;
264
288
  }
289
+ returning(fields) {
290
+ this._returning = fields != null ? core_1.Utils.asArray(fields) : fields;
291
+ return this;
292
+ }
265
293
  /**
266
294
  * @internal
267
295
  */
@@ -271,17 +299,6 @@ class QueryBuilder {
271
299
  this._populateWhere = populateWhere;
272
300
  return this;
273
301
  }
274
- /**
275
- * @internal
276
- */
277
- ref(field) {
278
- return this.knex.ref(field);
279
- }
280
- raw(sql, bindings = []) {
281
- const raw = this.knex.raw(sql, bindings);
282
- raw.__raw = true; // tag it as there is now way to check via `instanceof`
283
- return raw;
284
- }
285
302
  limit(limit, offset = 0) {
286
303
  this.ensureNotFinalized();
287
304
  this._limit = limit;
@@ -354,13 +371,15 @@ class QueryBuilder {
354
371
  getKnexQuery() {
355
372
  this.finalize();
356
373
  const qb = this.getQueryBase();
374
+ qb.__raw = true; // tag it as there is now way to check via `instanceof`
357
375
  core_1.Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type ?? enums_1.QueryType.SELECT, this._cond, qb), this._cond && !this._onConflict);
358
376
  core_1.Utils.runIfNotEmpty(() => qb.groupBy(this.prepareFields(this._groupBy, 'groupBy')), this._groupBy);
359
377
  core_1.Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type ?? enums_1.QueryType.SELECT, this._having, qb, undefined, 'having'), this._having);
360
378
  core_1.Utils.runIfNotEmpty(() => {
361
379
  const queryOrder = this.helper.getQueryOrder(this.type ?? enums_1.QueryType.SELECT, this._orderBy, this._populateMap);
362
380
  if (queryOrder) {
363
- return qb.orderByRaw(queryOrder);
381
+ qb.orderByRaw(queryOrder);
382
+ return;
364
383
  }
365
384
  }, this._orderBy);
366
385
  core_1.Utils.runIfNotEmpty(() => qb.limit(this._limit), this._limit != null);
@@ -372,26 +391,34 @@ class QueryBuilder {
372
391
  if (this.lockMode) {
373
392
  this.helper.getLockSQL(qb, this.lockMode, this.lockTables);
374
393
  }
375
- this.helper.finalize(this.type ?? enums_1.QueryType.SELECT, qb, this.mainAlias.metadata);
394
+ this.helper.finalize(this.type ?? enums_1.QueryType.SELECT, qb, this.mainAlias.metadata, this._data, this._returning);
376
395
  return qb;
377
396
  }
378
397
  /**
379
398
  * Returns the query with parameters as wildcards.
380
399
  */
381
400
  getQuery() {
382
- return this.getKnexQuery().toSQL().toNative().sql;
401
+ return this.toQuery().sql;
402
+ }
403
+ toQuery() {
404
+ if (__classPrivateFieldGet(this, _QueryBuilder_query, "f")) {
405
+ return __classPrivateFieldGet(this, _QueryBuilder_query, "f");
406
+ }
407
+ const sql = this.getKnexQuery().toSQL();
408
+ const query = sql.toNative();
409
+ return __classPrivateFieldSet(this, _QueryBuilder_query, { sql: query.sql, params: query.bindings ?? [], _sql: sql }, "f");
383
410
  }
384
411
  /**
385
412
  * Returns the list of all parameters for this query.
386
413
  */
387
414
  getParams() {
388
- return this.getKnexQuery().toSQL().toNative().bindings;
415
+ return this.toQuery().params;
389
416
  }
390
417
  /**
391
418
  * Returns raw interpolated query string with all the parameters inlined.
392
419
  */
393
420
  getFormattedQuery() {
394
- const query = this.getKnexQuery().toSQL();
421
+ const query = this.toQuery()._sql;
395
422
  return this.platform.formatQuery(query.sql, query.bindings);
396
423
  }
397
424
  /**
@@ -433,7 +460,7 @@ class QueryBuilder {
433
460
  return cached.data;
434
461
  }
435
462
  const type = this.connectionType || (method === 'run' ? 'write' : 'read');
436
- const res = await this.driver.getConnection(type).execute(query.sql, query.bindings, method, this.context);
463
+ const res = await this.driver.getConnection(type).execute(query.sql, query.bindings, method, this.context, this.logging);
437
464
  const meta = this.mainAlias.metadata;
438
465
  if (!mapResults || !meta) {
439
466
  await this.em?.storeCache(this._cache, cached, res);
@@ -464,7 +491,27 @@ class QueryBuilder {
464
491
  if (this._joinedProps.size > 0) {
465
492
  res = this.driver.mergeJoinedResult(res, this.mainAlias.metadata);
466
493
  }
467
- return res.map(r => this.em.map(this.mainAlias.entityName, r, { schema: this._schema }));
494
+ const entities = [];
495
+ function propagatePopulateHint(entity, hint) {
496
+ (0, core_1.helper)(entity).__serializationContext.populate ??= hint;
497
+ hint.forEach(pop => {
498
+ const value = entity[pop.field];
499
+ if (core_1.Utils.isEntity(value, true)) {
500
+ (0, core_1.helper)(value).populated();
501
+ propagatePopulateHint(value, pop.children ?? []);
502
+ }
503
+ else if (core_1.Utils.isCollection(value)) {
504
+ value.populated();
505
+ value.getItems(false).forEach(item => propagatePopulateHint(item, pop.children ?? []));
506
+ }
507
+ });
508
+ }
509
+ for (const r of res) {
510
+ const entity = this.em.map(this.mainAlias.entityName, r, { schema: this._schema });
511
+ propagatePopulateHint(entity, this._populate);
512
+ entities.push(entity);
513
+ }
514
+ return entities;
468
515
  }
469
516
  /**
470
517
  * Executes the query, returning the first result or null
@@ -482,17 +529,30 @@ class QueryBuilder {
482
529
  res = await this.execute('get', false);
483
530
  }
484
531
  else {
485
- const qb = this.clone();
532
+ const qb = this.type === undefined ? this : this.clone();
486
533
  qb.count(field, distinct ?? qb.hasToManyJoins()).limit(undefined).offset(undefined).orderBy([]);
487
534
  res = await qb.execute('get', false);
488
535
  }
489
536
  return res ? +res.count : 0;
490
537
  }
538
+ /**
539
+ * Executes the query, returning both array of results and total count query (without offset and limit).
540
+ */
541
+ async getResultAndCount() {
542
+ return Promise.all([
543
+ this.getResultList(),
544
+ this.getCount(),
545
+ ]);
546
+ }
491
547
  /**
492
548
  * Provides promise-like interface so we can await the QB instance.
493
549
  */
494
550
  then(onfulfilled, onrejected) {
495
- switch (this.type ?? enums_1.QueryType.SELECT) {
551
+ let type = this.type ?? enums_1.QueryType.SELECT;
552
+ if (this.flags.has(core_1.QueryFlag.UPDATE_SUB_QUERY) || this.flags.has(core_1.QueryFlag.DELETE_SUB_QUERY)) {
553
+ type = enums_1.QueryType.UPDATE;
554
+ }
555
+ switch (type) {
496
556
  case enums_1.QueryType.INSERT:
497
557
  case enums_1.QueryType.UPDATE:
498
558
  case enums_1.QueryType.DELETE:
@@ -515,7 +575,10 @@ class QueryBuilder {
515
575
  /* istanbul ignore next */
516
576
  alias = meta?.properties[f]?.fieldNames[0] ?? alias;
517
577
  }
518
- return qb.as(alias);
578
+ const ret = qb.as(alias);
579
+ // tag the instance, so it is possible to detect it easily
580
+ Object.defineProperty(ret, '__as', { enumerable: false, value: alias });
581
+ return ret;
519
582
  }
520
583
  clone() {
521
584
  const qb = new QueryBuilder(this.mainAlias.entityName, this.metadata, this.driver, this.context, this.mainAlias.aliasName, this.connectionType, this.em);
@@ -523,7 +586,7 @@ class QueryBuilder {
523
586
  // clone array/object properties
524
587
  const properties = [
525
588
  'flags', '_populate', '_populateWhere', '_populateMap', '_joins', '_joinedProps', '_cond', '_data', '_orderBy',
526
- '_schema', '_indexHint', '_cache', 'subQueries', 'lockMode', 'lockTables',
589
+ '_schema', '_indexHint', '_cache', 'subQueries', 'lockMode', 'lockTables', '_groupBy', '_having', '_returning',
527
590
  ];
528
591
  properties.forEach(prop => qb[prop] = core_1.Utils.copy(this[prop]));
529
592
  /* istanbul ignore else */
@@ -551,14 +614,18 @@ class QueryBuilder {
551
614
  }
552
615
  return qb;
553
616
  }
554
- joinReference(field, alias, cond, type, path) {
617
+ joinReference(field, alias, cond, type, path, schema) {
555
618
  this.ensureNotFinalized();
556
619
  const [fromAlias, fromField] = this.helper.splitField(field);
557
- const entityName = this._aliases[fromAlias]?.entityName;
620
+ const q = (str) => `'${str}'`;
621
+ if (!this._aliases[fromAlias]) {
622
+ 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(', ')}.`);
623
+ }
624
+ const entityName = this._aliases[fromAlias].entityName;
558
625
  const meta = this.metadata.get(entityName);
559
626
  const prop = meta.properties[fromField];
560
627
  if (!prop) {
561
- throw new Error(`Trying to join ${field}, but ${fromField} is not a defined relation on ${meta.className}`);
628
+ throw new Error(`Trying to join ${q(field)}, but ${q(fromField)} is not a defined relation on ${meta.className}.`);
562
629
  }
563
630
  this.createAlias(prop.type, alias);
564
631
  cond = core_1.QueryHelper.processWhere({
@@ -570,26 +637,26 @@ class QueryBuilder {
570
637
  aliased: !this.type || [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(this.type),
571
638
  });
572
639
  let aliasedName = `${fromAlias}.${prop.name}#${alias}`;
573
- path ?? (path = `${(Object.values(this._joins).find(j => j.alias === fromAlias)?.path ?? entityName)}.${prop.name}`);
574
- if (prop.reference === core_1.ReferenceType.ONE_TO_MANY) {
575
- this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond);
640
+ path ??= `${(Object.values(this._joins).find(j => j.alias === fromAlias)?.path ?? entityName)}.${prop.name}`;
641
+ if (prop.kind === core_1.ReferenceKind.ONE_TO_MANY) {
642
+ this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond, schema);
576
643
  }
577
- else if (prop.reference === core_1.ReferenceType.MANY_TO_MANY) {
644
+ else if (prop.kind === core_1.ReferenceKind.MANY_TO_MANY) {
578
645
  let pivotAlias = alias;
579
646
  if (type !== 'pivotJoin') {
580
647
  const oldPivotAlias = this.getAliasForJoinPath(path + '[pivot]');
581
648
  pivotAlias = oldPivotAlias ?? this.getNextAlias(prop.pivotEntity);
582
649
  aliasedName = `${fromAlias}.${prop.name}#${pivotAlias}`;
583
650
  }
584
- const joins = this.helper.joinManyToManyReference(prop, fromAlias, alias, pivotAlias, type, cond, path);
651
+ const joins = this.helper.joinManyToManyReference(prop, fromAlias, alias, pivotAlias, type, cond, path, schema);
585
652
  Object.assign(this._joins, joins);
586
653
  this.createAlias(prop.pivotEntity, pivotAlias);
587
654
  }
588
- else if (prop.reference === core_1.ReferenceType.ONE_TO_ONE) {
589
- this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond);
655
+ else if (prop.kind === core_1.ReferenceKind.ONE_TO_ONE) {
656
+ this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond, schema);
590
657
  }
591
658
  else { // MANY_TO_ONE
592
- this._joins[aliasedName] = this.helper.joinManyToOneReference(prop, fromAlias, alias, type, cond);
659
+ this._joins[aliasedName] = this.helper.joinManyToOneReference(prop, fromAlias, alias, type, cond, schema);
593
660
  }
594
661
  if (!this._joins[aliasedName].path && path) {
595
662
  this._joins[aliasedName].path = path;
@@ -599,17 +666,25 @@ class QueryBuilder {
599
666
  prepareFields(fields, type = 'where') {
600
667
  const ret = [];
601
668
  fields.forEach(field => {
669
+ const rawField = core_1.RawQueryFragment.getKnownFragment(field);
670
+ if (rawField) {
671
+ const sql = this.platform.formatQuery(rawField.sql, rawField.params);
672
+ ret.push(this.knex.raw(sql));
673
+ return;
674
+ }
602
675
  if (!core_1.Utils.isString(field)) {
603
- return ret.push(field);
676
+ ret.push(field);
677
+ return;
604
678
  }
605
679
  const join = Object.keys(this._joins).find(k => field === k.substring(0, k.indexOf('#')));
606
680
  if (join && type === 'where') {
607
- return ret.push(...this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[join]));
681
+ ret.push(...this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[join]));
682
+ return;
608
683
  }
609
684
  const [a, f] = this.helper.splitField(field);
610
685
  const prop = this.helper.getProperty(f, a);
611
686
  /* istanbul ignore next */
612
- if (prop && [core_1.ReferenceType.ONE_TO_MANY, core_1.ReferenceType.MANY_TO_MANY].includes(prop.reference)) {
687
+ if (prop && [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
613
688
  return;
614
689
  }
615
690
  if (prop?.embedded) {
@@ -617,7 +692,7 @@ class QueryBuilder {
617
692
  ret.push(fieldName);
618
693
  return;
619
694
  }
620
- if (prop?.reference === core_1.ReferenceType.EMBEDDED) {
695
+ if (prop?.kind === core_1.ReferenceKind.EMBEDDED) {
621
696
  if (prop.object) {
622
697
  ret.push(this.helper.mapper(prop.fieldNames[0], this.type));
623
698
  }
@@ -640,7 +715,7 @@ class QueryBuilder {
640
715
  });
641
716
  const meta = this.mainAlias.metadata;
642
717
  /* istanbul ignore next */
643
- const requiresSQLConversion = meta?.props.filter(p => p.customType?.convertToJSValueSQL) ?? [];
718
+ const requiresSQLConversion = meta?.props.filter(p => p.hasConvertToJSValueSQL) ?? [];
644
719
  if (this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES) && (fields.includes('*') || fields.includes(`${this.mainAlias.aliasName}.*`)) && requiresSQLConversion.length > 0) {
645
720
  requiresSQLConversion.forEach(p => ret.push(this.helper.mapper(p.name, this.type)));
646
721
  }
@@ -649,7 +724,7 @@ class QueryBuilder {
649
724
  const cols = this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[f]);
650
725
  ret.push(...cols);
651
726
  }
652
- if (this._joins[f].prop.reference !== core_1.ReferenceType.ONE_TO_ONE && this._joins[f].inverseJoinColumns) {
727
+ if (this._joins[f].prop.kind !== core_1.ReferenceKind.ONE_TO_ONE && this._joins[f].inverseJoinColumns) {
653
728
  this._joins[f].inverseJoinColumns.forEach(inverseJoinColumn => {
654
729
  core_1.Utils.renameKey(this._cond, inverseJoinColumn, `${this._joins[f].alias}.${inverseJoinColumn}`);
655
730
  });
@@ -668,7 +743,7 @@ class QueryBuilder {
668
743
  }
669
744
  if (data) {
670
745
  if (core_1.Utils.isEntity(data)) {
671
- data = (0, core_1.helper)(data).toJSON();
746
+ data = this.em?.getComparator().prepareEntity(data) ?? (0, core_1.serialize)(data);
672
747
  }
673
748
  this._data = this.helper.processData(data, this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES));
674
749
  }
@@ -722,6 +797,24 @@ class QueryBuilder {
722
797
  }
723
798
  return qb;
724
799
  }
800
+ applyDiscriminatorCondition() {
801
+ const meta = this.mainAlias.metadata;
802
+ if (!meta?.discriminatorValue) {
803
+ return;
804
+ }
805
+ const types = Object.values(meta.root.discriminatorMap).map(cls => this.metadata.find(cls));
806
+ const children = [];
807
+ const lookUpChildren = (ret, type) => {
808
+ const children = types.filter(meta2 => meta2.extends === type);
809
+ children.forEach(m => lookUpChildren(ret, m.className));
810
+ ret.push(...children.filter(c => c.discriminatorValue));
811
+ return children;
812
+ };
813
+ lookUpChildren(children, meta.className);
814
+ this.andWhere({
815
+ [meta.root.discriminatorColumn]: children.length > 0 ? { $in: [meta.discriminatorValue, ...children.map(c => c.discriminatorValue)] } : meta.discriminatorValue,
816
+ });
817
+ }
725
818
  finalize() {
726
819
  if (this.finalized) {
727
820
  return;
@@ -730,10 +823,11 @@ class QueryBuilder {
730
823
  this.select('*');
731
824
  }
732
825
  const meta = this.mainAlias.metadata;
826
+ this.applyDiscriminatorCondition();
733
827
  if (meta && this.flags.has(core_1.QueryFlag.AUTO_JOIN_ONE_TO_ONE_OWNER)) {
734
828
  const relationsToPopulate = this._populate.map(({ field }) => field);
735
829
  meta.relations
736
- .filter(prop => prop.reference === core_1.ReferenceType.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
830
+ .filter(prop => prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
737
831
  .map(prop => ({ field: prop.name }))
738
832
  .forEach(item => this._populate.push(item));
739
833
  }
@@ -742,12 +836,10 @@ class QueryBuilder {
742
836
  const aliasedField = `${fromAlias}.${fromField}`;
743
837
  const join = Object.keys(this._joins).find(k => `${aliasedField}#${this._joins[k].alias}` === k);
744
838
  if (join && this._joins[join] && this.helper.isOneToOneInverse(fromField)) {
745
- return this._populateMap[join] = this._joins[join].alias;
746
- }
747
- if (this.metadata.find(field)?.pivotTable) { // pivot table entity
748
- this.autoJoinPivotTable(field);
839
+ this._populateMap[join] = this._joins[join].alias;
840
+ return;
749
841
  }
750
- else if (meta && this.helper.isOneToOneInverse(fromField)) {
842
+ if (meta && this.helper.isOneToOneInverse(fromField)) {
751
843
  const prop = meta.properties[fromField];
752
844
  const alias = this.getNextAlias(prop.pivotEntity ?? prop.type);
753
845
  const aliasedName = `${fromAlias}.${prop.name}#${alias}`;
@@ -763,14 +855,20 @@ class QueryBuilder {
763
855
  const aliased = this.knex.ref(prop.fieldNames[0]).toString();
764
856
  return `${prop.formula(alias)} as ${aliased}`;
765
857
  })
766
- .filter(field => !this._fields.includes(field))
767
- .forEach(field => this.addSelect(field));
858
+ .filter(field => !this._fields.some(f => {
859
+ if (f instanceof core_1.RawQueryFragment) {
860
+ return f.sql === field && f.params.length === 0;
861
+ }
862
+ return f === field;
863
+ }))
864
+ .forEach(field => this._fields.push((0, core_1.raw)(field)));
768
865
  }
866
+ this.processPopulateWhere();
769
867
  core_1.QueryHelper.processObjectParams(this._data);
770
868
  core_1.QueryHelper.processObjectParams(this._cond);
771
869
  core_1.QueryHelper.processObjectParams(this._having);
772
- // automatically enable paginate flag when we detect to-many joins
773
- if (!this.flags.has(core_1.QueryFlag.DISABLE_PAGINATE) && this.hasToManyJoins()) {
870
+ // automatically enable paginate flag when we detect to-many joins, but only if there is no `group by` clause
871
+ if (!this.flags.has(core_1.QueryFlag.DISABLE_PAGINATE) && this._groupBy.length === 0 && this.hasToManyJoins()) {
774
872
  this.flags.add(core_1.QueryFlag.PAGINATE);
775
873
  }
776
874
  if (meta && this.flags.has(core_1.QueryFlag.PAGINATE) && (this._limit > 0 || this._offset > 0)) {
@@ -781,17 +879,54 @@ class QueryBuilder {
781
879
  }
782
880
  this.finalized = true;
783
881
  }
882
+ processPopulateWhere() {
883
+ if (this._populateWhere == null || this._populateWhere === core_1.PopulateHint.ALL) {
884
+ return;
885
+ }
886
+ const joins = Object.values(this._joins);
887
+ joins.forEach(join => {
888
+ join.cond_ = join.cond;
889
+ join.cond = {};
890
+ });
891
+ const replaceOnConditions = (cond) => {
892
+ Object.keys(cond).forEach(k => {
893
+ if (core_1.Utils.isOperator(k)) {
894
+ if (Array.isArray(cond[k])) {
895
+ return cond[k].forEach((c) => replaceOnConditions(c));
896
+ }
897
+ return replaceOnConditions(cond[k]);
898
+ }
899
+ const [a] = this.helper.splitField(k);
900
+ const join = joins.find(j => j.alias === a);
901
+ if (join) {
902
+ join.cond = { ...join.cond, [k]: cond[k] };
903
+ }
904
+ });
905
+ };
906
+ if (this._populateWhere === core_1.PopulateHint.INFER) {
907
+ replaceOnConditions(this._cond);
908
+ }
909
+ else if (typeof this._populateWhere === 'object') {
910
+ const cond = CriteriaNodeFactory_1.CriteriaNodeFactory
911
+ .createNode(this.metadata, this.mainAlias.entityName, this._populateWhere)
912
+ .process(this);
913
+ replaceOnConditions(cond);
914
+ }
915
+ }
784
916
  hasToManyJoins() {
785
917
  return Object.values(this._joins).some(join => {
786
- return [core_1.ReferenceType.ONE_TO_MANY, core_1.ReferenceType.MANY_TO_MANY].includes(join.prop.reference);
918
+ return [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(join.prop.kind);
787
919
  });
788
920
  }
789
921
  wrapPaginateSubQuery(meta) {
790
922
  const pks = this.prepareFields(meta.primaryKeys, 'sub-query');
791
923
  const subQuery = this.clone().select(pks).groupBy(pks).limit(this._limit);
924
+ // revert the on conditions added via populateWhere, we want to apply those only once
925
+ Object.values(subQuery._joins).forEach(join => join.cond = join.cond_ ?? {});
792
926
  if (this._offset) {
793
927
  subQuery.offset(this._offset);
794
928
  }
929
+ const addToSelect = [];
795
930
  if (this._orderBy.length > 0) {
796
931
  const orderBy = [];
797
932
  for (const orderMap of this._orderBy) {
@@ -799,28 +934,38 @@ class QueryBuilder {
799
934
  const [a, f] = this.helper.splitField(field);
800
935
  const prop = this.helper.getProperty(f, a);
801
936
  const type = this.platform.castColumn(prop);
802
- orderBy.push({
803
- [`min(${this.ref(this.helper.mapper(field, this.type, undefined, null))}${type})`]: direction,
804
- });
937
+ const fieldName = this.helper.mapper(field, this.type, undefined, null);
938
+ if (!prop?.persist && !prop?.formula) {
939
+ addToSelect.push(fieldName);
940
+ }
941
+ orderBy.push({ [(0, core_1.raw)(`min(${this.knex.ref(fieldName)}${type})`)]: direction });
805
942
  }
806
943
  }
807
944
  subQuery.orderBy(orderBy);
808
945
  }
809
946
  subQuery.finalized = true;
810
947
  const knexQuery = subQuery.as(this.mainAlias.aliasName).clearSelect().select(pks);
948
+ if (addToSelect.length > 0) {
949
+ addToSelect.forEach(prop => {
950
+ const field = this._fields.find(field => {
951
+ if (typeof field === 'object' && field && '__as' in field) {
952
+ return field.__as === prop;
953
+ }
954
+ // not perfect, but should work most of the time, ideally we should check only the alias (`... as alias`)
955
+ return field.toString().includes(prop);
956
+ });
957
+ if (field) {
958
+ knexQuery.select(field);
959
+ }
960
+ });
961
+ }
811
962
  // multiple sub-queries are needed to get around mysql limitations with order by + limit + where in + group by (o.O)
812
963
  // https://stackoverflow.com/questions/17892762/mysql-this-version-of-mysql-doesnt-yet-support-limit-in-all-any-some-subqu
813
964
  const subSubQuery = this.getKnex().select(pks).from(knexQuery);
965
+ subSubQuery.__raw = true; // tag it as there is now way to check via `instanceof`
814
966
  this._limit = undefined;
815
967
  this._offset = undefined;
816
- const cond = this._cond;
817
968
  this.select(this._fields).where({ [core_1.Utils.getPrimaryKeyHash(meta.primaryKeys)]: { $in: subSubQuery } });
818
- if (this._populateWhere === core_1.PopulateHint.INFER) {
819
- this.andWhere(cond);
820
- }
821
- else if (typeof this._populateWhere === 'object') {
822
- this.andWhere(this._populateWhere);
823
- }
824
969
  }
825
970
  wrapModifySubQuery(meta) {
826
971
  const subQuery = this.clone();
@@ -834,17 +979,6 @@ class QueryBuilder {
834
979
  [core_1.Utils.getPrimaryKeyHash(meta.primaryKeys)]: { $in: subSubQuery },
835
980
  });
836
981
  }
837
- autoJoinPivotTable(field) {
838
- const pivotMeta = this.metadata.find(field);
839
- const owner = pivotMeta.relations[0];
840
- const inverse = pivotMeta.relations[1];
841
- const prop = this._cond[pivotMeta.name + '.' + owner.name] || this._orderBy[pivotMeta.name + '.' + owner.name] ? inverse : owner;
842
- const pivotAlias = this.getNextAlias(pivotMeta.name);
843
- this._joins[field] = this.helper.joinPivotTable(field, prop, this.mainAlias.aliasName, pivotAlias, 'leftJoin');
844
- core_1.Utils.renameKey(this._cond, `${field}.${owner.name}`, core_1.Utils.getPrimaryKeyHash(owner.fieldNames.map(fieldName => `${pivotAlias}.${fieldName}`)));
845
- core_1.Utils.renameKey(this._cond, `${field}.${inverse.name}`, core_1.Utils.getPrimaryKeyHash(inverse.fieldNames.map(fieldName => `${pivotAlias}.${fieldName}`)));
846
- this._populateMap[field] = this._joins[field].alias;
847
- }
848
982
  getSchema(alias) {
849
983
  const { metadata } = alias;
850
984
  const metaSchema = metadata?.schema && metadata.schema !== '*' ? metadata.schema : undefined;
@@ -864,11 +998,11 @@ class QueryBuilder {
864
998
  fromSubQuery(target, aliasName) {
865
999
  const subQuery = target.getKnexQuery();
866
1000
  const { entityName } = target.mainAlias;
867
- aliasName ?? (aliasName = this.getNextAlias(entityName));
1001
+ aliasName ??= this.getNextAlias(entityName);
868
1002
  this.createMainAlias(entityName, aliasName, subQuery);
869
1003
  }
870
1004
  fromEntityName(entityName, aliasName) {
871
- aliasName ?? (aliasName = this._mainAlias?.aliasName ?? this.getNextAlias(entityName));
1005
+ aliasName ??= this._mainAlias?.aliasName ?? this.getNextAlias(entityName);
872
1006
  this.createMainAlias(entityName, aliasName);
873
1007
  }
874
1008
  createQueryBuilderHelper() {
@@ -885,5 +1019,30 @@ class QueryBuilder {
885
1019
  throw new Error('This QueryBuilder instance is already finalized, clone it first if you want to modify it.');
886
1020
  }
887
1021
  }
1022
+ /* istanbul ignore next */
1023
+ [(_QueryBuilder_query = new WeakMap(), util_1.inspect.custom)](depth) {
1024
+ const object = { ...this };
1025
+ const hidden = ['metadata', 'driver', 'context', 'platform', 'knex', 'type'];
1026
+ Object.keys(object).filter(k => k.startsWith('_')).forEach(k => delete object[k]);
1027
+ Object.keys(object).filter(k => object[k] == null).forEach(k => delete object[k]);
1028
+ hidden.forEach(k => delete object[k]);
1029
+ let prefix = this.type ? this.type.substring(0, 1) + this.type.toLowerCase().substring(1) : '';
1030
+ if (this._data) {
1031
+ object.data = this._data;
1032
+ }
1033
+ if (this._schema) {
1034
+ object.schema = this._schema;
1035
+ }
1036
+ if (!core_1.Utils.isEmpty(this._cond)) {
1037
+ object.where = this._cond;
1038
+ }
1039
+ if (this._onConflict?.[0]) {
1040
+ prefix = 'Upsert';
1041
+ object.onConflict = this._onConflict[0];
1042
+ }
1043
+ const name = this._mainAlias ? `${prefix}QueryBuilder<${this._mainAlias?.entityName}>` : 'QueryBuilder';
1044
+ const ret = (0, util_1.inspect)(object, { depth });
1045
+ return ret === '[Object]' ? `[${name}]` : name + ' ' + ret;
1046
+ }
888
1047
  }
889
1048
  exports.QueryBuilder = QueryBuilder;