@mikro-orm/knex 6.0.0-dev.7 → 6.0.0-dev.70

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 (41) hide show
  1. package/AbstractSqlConnection.d.ts +5 -2
  2. package/AbstractSqlConnection.js +22 -14
  3. package/AbstractSqlDriver.d.ts +17 -17
  4. package/AbstractSqlDriver.js +111 -48
  5. package/AbstractSqlPlatform.d.ts +1 -0
  6. package/AbstractSqlPlatform.js +20 -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.d.ts +1 -0
  15. package/index.js +1 -0
  16. package/index.mjs +175 -0
  17. package/package.json +5 -5
  18. package/query/ArrayCriteriaNode.d.ts +3 -3
  19. package/query/CriteriaNode.d.ts +8 -8
  20. package/query/CriteriaNode.js +9 -9
  21. package/query/CriteriaNodeFactory.d.ts +6 -6
  22. package/query/CriteriaNodeFactory.js +10 -13
  23. package/query/ObjectCriteriaNode.d.ts +3 -3
  24. package/query/ObjectCriteriaNode.js +12 -8
  25. package/query/QueryBuilder.d.ts +20 -5
  26. package/query/QueryBuilder.js +215 -50
  27. package/query/QueryBuilderHelper.d.ts +3 -5
  28. package/query/QueryBuilderHelper.js +90 -74
  29. package/query/ScalarCriteriaNode.d.ts +3 -2
  30. package/query/ScalarCriteriaNode.js +9 -6
  31. package/schema/DatabaseSchema.d.ts +4 -1
  32. package/schema/DatabaseSchema.js +22 -6
  33. package/schema/DatabaseTable.d.ts +10 -9
  34. package/schema/DatabaseTable.js +25 -24
  35. package/schema/SchemaComparator.d.ts +4 -4
  36. package/schema/SchemaComparator.js +9 -2
  37. package/schema/SchemaHelper.d.ts +14 -12
  38. package/schema/SchemaHelper.js +10 -4
  39. package/schema/SqlSchemaGenerator.d.ts +2 -4
  40. package/schema/SqlSchemaGenerator.js +40 -20
  41. package/typings.d.ts +15 -13
@@ -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,7 +130,7 @@ 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);
@@ -164,9 +179,15 @@ class QueryBuilder {
164
179
  }
165
180
  where(cond, params, operator) {
166
181
  this.ensureNotFinalized();
167
- if (core_1.Utils.isString(cond)) {
168
- cond = { [`(${cond})`]: core_1.Utils.asArray(params) };
169
- operator = operator || '$and';
182
+ const rawField = core_1.RawQueryFragment.getKnownFragment(cond);
183
+ if (rawField) {
184
+ const sql = this.platform.formatQuery(rawField.sql, rawField.params);
185
+ cond = { [(0, core_1.raw)(`(${sql})`)]: core_1.Utils.asArray(params) };
186
+ operator ??= '$and';
187
+ }
188
+ else if (core_1.Utils.isString(cond)) {
189
+ cond = { [(0, core_1.raw)(`(${cond})`, core_1.Utils.asArray(params))]: [] };
190
+ operator ??= '$and';
170
191
  }
171
192
  else {
172
193
  cond = core_1.QueryHelper.processWhere({
@@ -234,7 +255,7 @@ class QueryBuilder {
234
255
  having(cond = {}, params) {
235
256
  this.ensureNotFinalized();
236
257
  if (core_1.Utils.isString(cond)) {
237
- cond = { [`(${cond})`]: core_1.Utils.asArray(params) };
258
+ cond = { [(0, core_1.raw)(`(${cond})`, params)]: [] };
238
259
  }
239
260
  this._having = CriteriaNodeFactory_1.CriteriaNodeFactory.createNode(this.metadata, this.mainAlias.entityName, cond).process(this);
240
261
  return this;
@@ -277,11 +298,6 @@ class QueryBuilder {
277
298
  ref(field) {
278
299
  return this.knex.ref(field);
279
300
  }
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
301
  limit(limit, offset = 0) {
286
302
  this.ensureNotFinalized();
287
303
  this._limit = limit;
@@ -354,13 +370,15 @@ class QueryBuilder {
354
370
  getKnexQuery() {
355
371
  this.finalize();
356
372
  const qb = this.getQueryBase();
373
+ qb.__raw = true; // tag it as there is now way to check via `instanceof`
357
374
  core_1.Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type ?? enums_1.QueryType.SELECT, this._cond, qb), this._cond && !this._onConflict);
358
375
  core_1.Utils.runIfNotEmpty(() => qb.groupBy(this.prepareFields(this._groupBy, 'groupBy')), this._groupBy);
359
376
  core_1.Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type ?? enums_1.QueryType.SELECT, this._having, qb, undefined, 'having'), this._having);
360
377
  core_1.Utils.runIfNotEmpty(() => {
361
378
  const queryOrder = this.helper.getQueryOrder(this.type ?? enums_1.QueryType.SELECT, this._orderBy, this._populateMap);
362
379
  if (queryOrder) {
363
- return qb.orderByRaw(queryOrder);
380
+ qb.orderByRaw(queryOrder);
381
+ return;
364
382
  }
365
383
  }, this._orderBy);
366
384
  core_1.Utils.runIfNotEmpty(() => qb.limit(this._limit), this._limit != null);
@@ -372,26 +390,34 @@ class QueryBuilder {
372
390
  if (this.lockMode) {
373
391
  this.helper.getLockSQL(qb, this.lockMode, this.lockTables);
374
392
  }
375
- this.helper.finalize(this.type ?? enums_1.QueryType.SELECT, qb, this.mainAlias.metadata);
393
+ this.helper.finalize(this.type ?? enums_1.QueryType.SELECT, qb, this.mainAlias.metadata, this._data);
376
394
  return qb;
377
395
  }
378
396
  /**
379
397
  * Returns the query with parameters as wildcards.
380
398
  */
381
399
  getQuery() {
382
- return this.getKnexQuery().toSQL().toNative().sql;
400
+ return this.toQuery().sql;
401
+ }
402
+ toQuery() {
403
+ if (__classPrivateFieldGet(this, _QueryBuilder_query, "f")) {
404
+ return __classPrivateFieldGet(this, _QueryBuilder_query, "f");
405
+ }
406
+ const sql = this.getKnexQuery().toSQL();
407
+ const query = sql.toNative();
408
+ return __classPrivateFieldSet(this, _QueryBuilder_query, { sql: query.sql, params: query.bindings ?? [], _sql: sql }, "f");
383
409
  }
384
410
  /**
385
411
  * Returns the list of all parameters for this query.
386
412
  */
387
413
  getParams() {
388
- return this.getKnexQuery().toSQL().toNative().bindings;
414
+ return this.toQuery().params;
389
415
  }
390
416
  /**
391
417
  * Returns raw interpolated query string with all the parameters inlined.
392
418
  */
393
419
  getFormattedQuery() {
394
- const query = this.getKnexQuery().toSQL();
420
+ const query = this.toQuery()._sql;
395
421
  return this.platform.formatQuery(query.sql, query.bindings);
396
422
  }
397
423
  /**
@@ -433,7 +459,7 @@ class QueryBuilder {
433
459
  return cached.data;
434
460
  }
435
461
  const type = this.connectionType || (method === 'run' ? 'write' : 'read');
436
- const res = await this.driver.getConnection(type).execute(query.sql, query.bindings, method, this.context);
462
+ const res = await this.driver.getConnection(type).execute(query.sql, query.bindings, method, this.context, this.logging);
437
463
  const meta = this.mainAlias.metadata;
438
464
  if (!mapResults || !meta) {
439
465
  await this.em?.storeCache(this._cache, cached, res);
@@ -464,7 +490,27 @@ class QueryBuilder {
464
490
  if (this._joinedProps.size > 0) {
465
491
  res = this.driver.mergeJoinedResult(res, this.mainAlias.metadata);
466
492
  }
467
- return res.map(r => this.em.map(this.mainAlias.entityName, r, { schema: this._schema }));
493
+ const entities = [];
494
+ function propagatePopulateHint(entity, hint) {
495
+ (0, core_1.helper)(entity).__serializationContext.populate ??= hint;
496
+ hint.forEach(pop => {
497
+ const value = entity[pop.field];
498
+ if (core_1.Utils.isEntity(value, true)) {
499
+ (0, core_1.helper)(value).populated();
500
+ propagatePopulateHint(value, pop.children ?? []);
501
+ }
502
+ else if (core_1.Utils.isCollection(value)) {
503
+ value.populated();
504
+ value.getItems(false).forEach(item => propagatePopulateHint(item, pop.children ?? []));
505
+ }
506
+ });
507
+ }
508
+ for (const r of res) {
509
+ const entity = this.em.map(this.mainAlias.entityName, r, { schema: this._schema });
510
+ propagatePopulateHint(entity, this._populate);
511
+ entities.push(entity);
512
+ }
513
+ return entities;
468
514
  }
469
515
  /**
470
516
  * Executes the query, returning the first result or null
@@ -488,11 +534,24 @@ class QueryBuilder {
488
534
  }
489
535
  return res ? +res.count : 0;
490
536
  }
537
+ /**
538
+ * Executes the query, returning both array of results and total count query (without offset and limit).
539
+ */
540
+ async getResultAndCount() {
541
+ return Promise.all([
542
+ this.getResultList(),
543
+ this.getCount(),
544
+ ]);
545
+ }
491
546
  /**
492
547
  * Provides promise-like interface so we can await the QB instance.
493
548
  */
494
549
  then(onfulfilled, onrejected) {
495
- switch (this.type ?? enums_1.QueryType.SELECT) {
550
+ let type = this.type ?? enums_1.QueryType.SELECT;
551
+ if (this.flags.has(core_1.QueryFlag.UPDATE_SUB_QUERY) || this.flags.has(core_1.QueryFlag.DELETE_SUB_QUERY)) {
552
+ type = enums_1.QueryType.UPDATE;
553
+ }
554
+ switch (type) {
496
555
  case enums_1.QueryType.INSERT:
497
556
  case enums_1.QueryType.UPDATE:
498
557
  case enums_1.QueryType.DELETE:
@@ -515,7 +574,10 @@ class QueryBuilder {
515
574
  /* istanbul ignore next */
516
575
  alias = meta?.properties[f]?.fieldNames[0] ?? alias;
517
576
  }
518
- return qb.as(alias);
577
+ const ret = qb.as(alias);
578
+ // tag the instance, so it is possible to detect it easily
579
+ Object.defineProperty(ret, '__as', { enumerable: false, value: alias });
580
+ return ret;
519
581
  }
520
582
  clone() {
521
583
  const qb = new QueryBuilder(this.mainAlias.entityName, this.metadata, this.driver, this.context, this.mainAlias.aliasName, this.connectionType, this.em);
@@ -570,11 +632,11 @@ class QueryBuilder {
570
632
  aliased: !this.type || [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(this.type),
571
633
  });
572
634
  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) {
635
+ path ??= `${(Object.values(this._joins).find(j => j.alias === fromAlias)?.path ?? entityName)}.${prop.name}`;
636
+ if (prop.kind === core_1.ReferenceKind.ONE_TO_MANY) {
575
637
  this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond);
576
638
  }
577
- else if (prop.reference === core_1.ReferenceType.MANY_TO_MANY) {
639
+ else if (prop.kind === core_1.ReferenceKind.MANY_TO_MANY) {
578
640
  let pivotAlias = alias;
579
641
  if (type !== 'pivotJoin') {
580
642
  const oldPivotAlias = this.getAliasForJoinPath(path + '[pivot]');
@@ -585,7 +647,7 @@ class QueryBuilder {
585
647
  Object.assign(this._joins, joins);
586
648
  this.createAlias(prop.pivotEntity, pivotAlias);
587
649
  }
588
- else if (prop.reference === core_1.ReferenceType.ONE_TO_ONE) {
650
+ else if (prop.kind === core_1.ReferenceKind.ONE_TO_ONE) {
589
651
  this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond);
590
652
  }
591
653
  else { // MANY_TO_ONE
@@ -599,17 +661,25 @@ class QueryBuilder {
599
661
  prepareFields(fields, type = 'where') {
600
662
  const ret = [];
601
663
  fields.forEach(field => {
664
+ const rawField = core_1.RawQueryFragment.getKnownFragment(field);
665
+ if (rawField) {
666
+ const sql = this.platform.formatQuery(rawField.sql, rawField.params);
667
+ ret.push(this.knex.raw(sql));
668
+ return;
669
+ }
602
670
  if (!core_1.Utils.isString(field)) {
603
- return ret.push(field);
671
+ ret.push(field);
672
+ return;
604
673
  }
605
674
  const join = Object.keys(this._joins).find(k => field === k.substring(0, k.indexOf('#')));
606
675
  if (join && type === 'where') {
607
- return ret.push(...this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[join]));
676
+ ret.push(...this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[join]));
677
+ return;
608
678
  }
609
679
  const [a, f] = this.helper.splitField(field);
610
680
  const prop = this.helper.getProperty(f, a);
611
681
  /* istanbul ignore next */
612
- if (prop && [core_1.ReferenceType.ONE_TO_MANY, core_1.ReferenceType.MANY_TO_MANY].includes(prop.reference)) {
682
+ if (prop && [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
613
683
  return;
614
684
  }
615
685
  if (prop?.embedded) {
@@ -617,7 +687,7 @@ class QueryBuilder {
617
687
  ret.push(fieldName);
618
688
  return;
619
689
  }
620
- if (prop?.reference === core_1.ReferenceType.EMBEDDED) {
690
+ if (prop?.kind === core_1.ReferenceKind.EMBEDDED) {
621
691
  if (prop.object) {
622
692
  ret.push(this.helper.mapper(prop.fieldNames[0], this.type));
623
693
  }
@@ -640,7 +710,7 @@ class QueryBuilder {
640
710
  });
641
711
  const meta = this.mainAlias.metadata;
642
712
  /* istanbul ignore next */
643
- const requiresSQLConversion = meta?.props.filter(p => p.customType?.convertToJSValueSQL) ?? [];
713
+ const requiresSQLConversion = meta?.props.filter(p => p.hasConvertToJSValueSQL) ?? [];
644
714
  if (this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES) && (fields.includes('*') || fields.includes(`${this.mainAlias.aliasName}.*`)) && requiresSQLConversion.length > 0) {
645
715
  requiresSQLConversion.forEach(p => ret.push(this.helper.mapper(p.name, this.type)));
646
716
  }
@@ -649,7 +719,7 @@ class QueryBuilder {
649
719
  const cols = this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[f]);
650
720
  ret.push(...cols);
651
721
  }
652
- if (this._joins[f].prop.reference !== core_1.ReferenceType.ONE_TO_ONE && this._joins[f].inverseJoinColumns) {
722
+ if (this._joins[f].prop.kind !== core_1.ReferenceKind.ONE_TO_ONE && this._joins[f].inverseJoinColumns) {
653
723
  this._joins[f].inverseJoinColumns.forEach(inverseJoinColumn => {
654
724
  core_1.Utils.renameKey(this._cond, inverseJoinColumn, `${this._joins[f].alias}.${inverseJoinColumn}`);
655
725
  });
@@ -668,7 +738,7 @@ class QueryBuilder {
668
738
  }
669
739
  if (data) {
670
740
  if (core_1.Utils.isEntity(data)) {
671
- data = (0, core_1.helper)(data).toJSON();
741
+ data = this.em?.getComparator().prepareEntity(data) ?? (0, core_1.serialize)(data);
672
742
  }
673
743
  this._data = this.helper.processData(data, this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES));
674
744
  }
@@ -722,6 +792,24 @@ class QueryBuilder {
722
792
  }
723
793
  return qb;
724
794
  }
795
+ applyDiscriminatorCondition() {
796
+ const meta = this.mainAlias.metadata;
797
+ if (!meta?.discriminatorValue) {
798
+ return;
799
+ }
800
+ const types = Object.values(meta.root.discriminatorMap).map(cls => this.metadata.find(cls));
801
+ const children = [];
802
+ const lookUpChildren = (ret, type) => {
803
+ const children = types.filter(meta2 => meta2.extends === type);
804
+ children.forEach(m => lookUpChildren(ret, m.className));
805
+ ret.push(...children.filter(c => c.discriminatorValue));
806
+ return children;
807
+ };
808
+ lookUpChildren(children, meta.className);
809
+ this.andWhere({
810
+ [meta.root.discriminatorColumn]: children.length > 0 ? { $in: [meta.discriminatorValue, ...children.map(c => c.discriminatorValue)] } : meta.discriminatorValue,
811
+ });
812
+ }
725
813
  finalize() {
726
814
  if (this.finalized) {
727
815
  return;
@@ -730,10 +818,11 @@ class QueryBuilder {
730
818
  this.select('*');
731
819
  }
732
820
  const meta = this.mainAlias.metadata;
821
+ this.applyDiscriminatorCondition();
733
822
  if (meta && this.flags.has(core_1.QueryFlag.AUTO_JOIN_ONE_TO_ONE_OWNER)) {
734
823
  const relationsToPopulate = this._populate.map(({ field }) => field);
735
824
  meta.relations
736
- .filter(prop => prop.reference === core_1.ReferenceType.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
825
+ .filter(prop => prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
737
826
  .map(prop => ({ field: prop.name }))
738
827
  .forEach(item => this._populate.push(item));
739
828
  }
@@ -742,7 +831,8 @@ class QueryBuilder {
742
831
  const aliasedField = `${fromAlias}.${fromField}`;
743
832
  const join = Object.keys(this._joins).find(k => `${aliasedField}#${this._joins[k].alias}` === k);
744
833
  if (join && this._joins[join] && this.helper.isOneToOneInverse(fromField)) {
745
- return this._populateMap[join] = this._joins[join].alias;
834
+ this._populateMap[join] = this._joins[join].alias;
835
+ return;
746
836
  }
747
837
  if (this.metadata.find(field)?.pivotTable) { // pivot table entity
748
838
  this.autoJoinPivotTable(field);
@@ -763,9 +853,15 @@ class QueryBuilder {
763
853
  const aliased = this.knex.ref(prop.fieldNames[0]).toString();
764
854
  return `${prop.formula(alias)} as ${aliased}`;
765
855
  })
766
- .filter(field => !this._fields.includes(field))
767
- .forEach(field => this.addSelect(field));
856
+ .filter(field => !this._fields.some(f => {
857
+ if (f instanceof core_1.RawQueryFragment) {
858
+ return f.sql === field && f.params.length === 0;
859
+ }
860
+ return f === field;
861
+ }))
862
+ .forEach(field => this._fields.push((0, core_1.raw)(field)));
768
863
  }
864
+ this.processPopulateWhere();
769
865
  core_1.QueryHelper.processObjectParams(this._data);
770
866
  core_1.QueryHelper.processObjectParams(this._cond);
771
867
  core_1.QueryHelper.processObjectParams(this._having);
@@ -781,17 +877,54 @@ class QueryBuilder {
781
877
  }
782
878
  this.finalized = true;
783
879
  }
880
+ processPopulateWhere() {
881
+ if (this._populateWhere == null || this._populateWhere === core_1.PopulateHint.ALL) {
882
+ return;
883
+ }
884
+ const joins = Object.values(this._joins);
885
+ joins.forEach(join => {
886
+ join.cond_ = join.cond;
887
+ join.cond = {};
888
+ });
889
+ const replaceOnConditions = (cond) => {
890
+ Object.keys(cond).forEach(k => {
891
+ if (core_1.Utils.isOperator(k)) {
892
+ if (Array.isArray(cond[k])) {
893
+ return cond[k].forEach((c) => replaceOnConditions(c));
894
+ }
895
+ return replaceOnConditions(cond[k]);
896
+ }
897
+ const [a] = this.helper.splitField(k);
898
+ const join = joins.find(j => j.alias === a);
899
+ if (join) {
900
+ join.cond = { ...join.cond, [k]: cond[k] };
901
+ }
902
+ });
903
+ };
904
+ if (this._populateWhere === core_1.PopulateHint.INFER) {
905
+ replaceOnConditions(this._cond);
906
+ }
907
+ else if (typeof this._populateWhere === 'object') {
908
+ const cond = CriteriaNodeFactory_1.CriteriaNodeFactory
909
+ .createNode(this.metadata, this.mainAlias.entityName, this._populateWhere)
910
+ .process(this);
911
+ replaceOnConditions(cond);
912
+ }
913
+ }
784
914
  hasToManyJoins() {
785
915
  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);
916
+ return [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(join.prop.kind);
787
917
  });
788
918
  }
789
919
  wrapPaginateSubQuery(meta) {
790
920
  const pks = this.prepareFields(meta.primaryKeys, 'sub-query');
791
921
  const subQuery = this.clone().select(pks).groupBy(pks).limit(this._limit);
922
+ // revert the on conditions added via populateWhere, we want to apply those only once
923
+ Object.values(subQuery._joins).forEach(join => join.cond = join.cond_ ?? {});
792
924
  if (this._offset) {
793
925
  subQuery.offset(this._offset);
794
926
  }
927
+ const addToSelect = [];
795
928
  if (this._orderBy.length > 0) {
796
929
  const orderBy = [];
797
930
  for (const orderMap of this._orderBy) {
@@ -799,28 +932,38 @@ class QueryBuilder {
799
932
  const [a, f] = this.helper.splitField(field);
800
933
  const prop = this.helper.getProperty(f, a);
801
934
  const type = this.platform.castColumn(prop);
802
- orderBy.push({
803
- [`min(${this.ref(this.helper.mapper(field, this.type, undefined, null))}${type})`]: direction,
804
- });
935
+ const fieldName = this.helper.mapper(field, this.type, undefined, null);
936
+ if (!prop?.persist && !prop?.formula) {
937
+ addToSelect.push(fieldName);
938
+ }
939
+ orderBy.push({ [(0, core_1.raw)(`min(${this.ref(fieldName)}${type})`)]: direction });
805
940
  }
806
941
  }
807
942
  subQuery.orderBy(orderBy);
808
943
  }
809
944
  subQuery.finalized = true;
810
945
  const knexQuery = subQuery.as(this.mainAlias.aliasName).clearSelect().select(pks);
946
+ if (addToSelect.length > 0) {
947
+ addToSelect.forEach(prop => {
948
+ const field = this._fields.find(field => {
949
+ if (typeof field === 'object' && field && '__as' in field) {
950
+ return field.__as === prop;
951
+ }
952
+ // not perfect, but should work most of the time, ideally we should check only the alias (`... as alias`)
953
+ return field.toString().includes(prop);
954
+ });
955
+ if (field) {
956
+ knexQuery.select(field);
957
+ }
958
+ });
959
+ }
811
960
  // multiple sub-queries are needed to get around mysql limitations with order by + limit + where in + group by (o.O)
812
961
  // https://stackoverflow.com/questions/17892762/mysql-this-version-of-mysql-doesnt-yet-support-limit-in-all-any-some-subqu
813
962
  const subSubQuery = this.getKnex().select(pks).from(knexQuery);
963
+ subSubQuery.__raw = true; // tag it as there is now way to check via `instanceof`
814
964
  this._limit = undefined;
815
965
  this._offset = undefined;
816
- const cond = this._cond;
817
966
  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
967
  }
825
968
  wrapModifySubQuery(meta) {
826
969
  const subQuery = this.clone();
@@ -841,8 +984,8 @@ class QueryBuilder {
841
984
  const prop = this._cond[pivotMeta.name + '.' + owner.name] || this._orderBy[pivotMeta.name + '.' + owner.name] ? inverse : owner;
842
985
  const pivotAlias = this.getNextAlias(pivotMeta.name);
843
986
  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}`)));
987
+ core_1.Utils.renameKey(this._cond, `${field}.${owner.name}`, core_1.Utils.getPrimaryKeyHash(owner.fieldNames.map(fieldName => `${pivotAlias}.${fieldName}`)), true);
988
+ core_1.Utils.renameKey(this._cond, `${field}.${inverse.name}`, core_1.Utils.getPrimaryKeyHash(inverse.fieldNames.map(fieldName => `${pivotAlias}.${fieldName}`)), true);
846
989
  this._populateMap[field] = this._joins[field].alias;
847
990
  }
848
991
  getSchema(alias) {
@@ -864,11 +1007,11 @@ class QueryBuilder {
864
1007
  fromSubQuery(target, aliasName) {
865
1008
  const subQuery = target.getKnexQuery();
866
1009
  const { entityName } = target.mainAlias;
867
- aliasName ?? (aliasName = this.getNextAlias(entityName));
1010
+ aliasName ??= this.getNextAlias(entityName);
868
1011
  this.createMainAlias(entityName, aliasName, subQuery);
869
1012
  }
870
1013
  fromEntityName(entityName, aliasName) {
871
- aliasName ?? (aliasName = this._mainAlias?.aliasName ?? this.getNextAlias(entityName));
1014
+ aliasName ??= this._mainAlias?.aliasName ?? this.getNextAlias(entityName);
872
1015
  this.createMainAlias(entityName, aliasName);
873
1016
  }
874
1017
  createQueryBuilderHelper() {
@@ -885,5 +1028,27 @@ class QueryBuilder {
885
1028
  throw new Error('This QueryBuilder instance is already finalized, clone it first if you want to modify it.');
886
1029
  }
887
1030
  }
1031
+ /* istanbul ignore next */
1032
+ [(_QueryBuilder_query = new WeakMap(), util_1.inspect.custom)](depth) {
1033
+ const object = { ...this };
1034
+ const hidden = ['metadata', 'driver', 'context', 'platform', 'knex', 'type'];
1035
+ Object.keys(object).filter(k => k.startsWith('_')).forEach(k => delete object[k]);
1036
+ Object.keys(object).filter(k => object[k] == null).forEach(k => delete object[k]);
1037
+ hidden.forEach(k => delete object[k]);
1038
+ let prefix = this.type ? this.type.substring(0, 1) + this.type.toLowerCase().substring(1) : '';
1039
+ if (this._data) {
1040
+ object.data = this._data;
1041
+ }
1042
+ if (!core_1.Utils.isEmpty(this._cond)) {
1043
+ object.where = this._cond;
1044
+ }
1045
+ if (this._onConflict?.[0]) {
1046
+ prefix = 'Upsert';
1047
+ object.onConflict = this._onConflict[0];
1048
+ }
1049
+ const name = this._mainAlias ? `${prefix}QueryBuilder<${this._mainAlias?.entityName}>` : 'QueryBuilder';
1050
+ const ret = (0, util_1.inspect)(object, { depth });
1051
+ return ret === '[Object]' ? `[${name}]` : name + ' ' + ret;
1052
+ }
888
1053
  }
889
1054
  exports.QueryBuilder = QueryBuilder;
@@ -1,5 +1,5 @@
1
1
  import type { Knex } from 'knex';
2
- import type { Dictionary, EntityData, EntityMetadata, EntityProperty, FlatQueryOrderMap, QBFilterQuery } from '@mikro-orm/core';
2
+ import type { Dictionary, EntityData, EntityKey, EntityMetadata, EntityProperty, FlatQueryOrderMap, QBFilterQuery } from '@mikro-orm/core';
3
3
  import { LockMode } from '@mikro-orm/core';
4
4
  import { QueryType } from './enums';
5
5
  import type { Field, JoinOptions } from '../typings';
@@ -43,16 +43,14 @@ export declare class QueryBuilderHelper {
43
43
  }[], qb: Knex.QueryBuilder): void;
44
44
  appendQueryCondition(type: QueryType, cond: any, qb: Knex.QueryBuilder, operator?: '$and' | '$or', method?: 'where' | 'having'): void;
45
45
  private appendQuerySubCondition;
46
- private processCustomExpression;
47
46
  private processObjectSubCondition;
48
47
  private getOperatorReplacement;
49
48
  getQueryOrder(type: QueryType, orderBy: FlatQueryOrderMap | FlatQueryOrderMap[], populate: Dictionary<string>): string;
50
49
  getQueryOrderFromObject(type: QueryType, orderBy: FlatQueryOrderMap, populate: Dictionary<string>): string;
51
- finalize(type: QueryType, qb: Knex.QueryBuilder, meta?: EntityMetadata): void;
52
- splitField(field: string, greedyAlias?: boolean): [string, string];
50
+ finalize(type: QueryType, qb: Knex.QueryBuilder, meta?: EntityMetadata, data?: Dictionary): void;
51
+ splitField<T>(field: EntityKey<T>, greedyAlias?: boolean): [string, EntityKey<T>];
53
52
  getLockSQL(qb: Knex.QueryBuilder, lockMode: LockMode, lockTables?: string[]): void;
54
53
  updateVersionProperty(qb: Knex.QueryBuilder, data: Dictionary): void;
55
- static isCustomExpression(field: string, hasAlias?: boolean): boolean;
56
54
  private prefix;
57
55
  private appendGroupCondition;
58
56
  private isPrefixed;