@mikro-orm/knex 6.0.0-dev.8 → 6.0.0-dev.80

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 (38) hide show
  1. package/AbstractSqlConnection.d.ts +5 -2
  2. package/AbstractSqlConnection.js +25 -17
  3. package/AbstractSqlDriver.d.ts +18 -18
  4. package/AbstractSqlDriver.js +117 -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.mjs +19 -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 +25 -7
  24. package/query/QueryBuilder.js +237 -61
  25. package/query/QueryBuilderHelper.d.ts +3 -5
  26. package/query/QueryBuilderHelper.js +96 -74
  27. package/query/ScalarCriteriaNode.d.ts +3 -2
  28. package/query/ScalarCriteriaNode.js +9 -6
  29. package/schema/DatabaseSchema.d.ts +4 -1
  30. package/schema/DatabaseSchema.js +22 -6
  31. package/schema/DatabaseTable.d.ts +2 -1
  32. package/schema/DatabaseTable.js +25 -24
  33. package/schema/SchemaComparator.js +9 -2
  34. package/schema/SchemaHelper.d.ts +5 -3
  35. package/schema/SchemaHelper.js +10 -4
  36. package/schema/SqlSchemaGenerator.d.ts +3 -5
  37. package/schema/SqlSchemaGenerator.js +41 -20
  38. package/typings.d.ts +8 -5
@@ -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);
@@ -129,12 +144,12 @@ class QueryBuilder {
129
144
  leftJoin(field, alias, cond = {}) {
130
145
  return this.join(field, alias, cond, 'leftJoin');
131
146
  }
132
- joinAndSelect(field, alias, cond = {}, type = 'innerJoin', path) {
147
+ joinAndSelect(field, alias, cond = {}, type = 'innerJoin', path, fields) {
133
148
  if (!this.type) {
134
149
  this.select('*');
135
150
  }
136
151
  const prop = this.joinReference(field, alias, cond, type, path);
137
- this.addSelect(this.getFieldsForJoinedLoad(prop, alias));
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) {
166
+ return this.joinAndSelect(field, alias, cond, 'leftJoin', undefined, fields);
152
167
  }
153
- getFieldsForJoinedLoad(prop, alias) {
168
+ innerJoinAndSelect(field, alias, cond = {}, fields) {
169
+ return this.joinAndSelect(field, alias, cond, 'innerJoin', undefined, fields);
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
  */
@@ -277,11 +305,6 @@ class QueryBuilder {
277
305
  ref(field) {
278
306
  return this.knex.ref(field);
279
307
  }
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
308
  limit(limit, offset = 0) {
286
309
  this.ensureNotFinalized();
287
310
  this._limit = limit;
@@ -354,13 +377,15 @@ class QueryBuilder {
354
377
  getKnexQuery() {
355
378
  this.finalize();
356
379
  const qb = this.getQueryBase();
380
+ qb.__raw = true; // tag it as there is now way to check via `instanceof`
357
381
  core_1.Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type ?? enums_1.QueryType.SELECT, this._cond, qb), this._cond && !this._onConflict);
358
382
  core_1.Utils.runIfNotEmpty(() => qb.groupBy(this.prepareFields(this._groupBy, 'groupBy')), this._groupBy);
359
383
  core_1.Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type ?? enums_1.QueryType.SELECT, this._having, qb, undefined, 'having'), this._having);
360
384
  core_1.Utils.runIfNotEmpty(() => {
361
385
  const queryOrder = this.helper.getQueryOrder(this.type ?? enums_1.QueryType.SELECT, this._orderBy, this._populateMap);
362
386
  if (queryOrder) {
363
- return qb.orderByRaw(queryOrder);
387
+ qb.orderByRaw(queryOrder);
388
+ return;
364
389
  }
365
390
  }, this._orderBy);
366
391
  core_1.Utils.runIfNotEmpty(() => qb.limit(this._limit), this._limit != null);
@@ -372,26 +397,34 @@ class QueryBuilder {
372
397
  if (this.lockMode) {
373
398
  this.helper.getLockSQL(qb, this.lockMode, this.lockTables);
374
399
  }
375
- this.helper.finalize(this.type ?? enums_1.QueryType.SELECT, qb, this.mainAlias.metadata);
400
+ this.helper.finalize(this.type ?? enums_1.QueryType.SELECT, qb, this.mainAlias.metadata, this._data, this._returning);
376
401
  return qb;
377
402
  }
378
403
  /**
379
404
  * Returns the query with parameters as wildcards.
380
405
  */
381
406
  getQuery() {
382
- return this.getKnexQuery().toSQL().toNative().sql;
407
+ return this.toQuery().sql;
408
+ }
409
+ toQuery() {
410
+ if (__classPrivateFieldGet(this, _QueryBuilder_query, "f")) {
411
+ return __classPrivateFieldGet(this, _QueryBuilder_query, "f");
412
+ }
413
+ const sql = this.getKnexQuery().toSQL();
414
+ const query = sql.toNative();
415
+ return __classPrivateFieldSet(this, _QueryBuilder_query, { sql: query.sql, params: query.bindings ?? [], _sql: sql }, "f");
383
416
  }
384
417
  /**
385
418
  * Returns the list of all parameters for this query.
386
419
  */
387
420
  getParams() {
388
- return this.getKnexQuery().toSQL().toNative().bindings;
421
+ return this.toQuery().params;
389
422
  }
390
423
  /**
391
424
  * Returns raw interpolated query string with all the parameters inlined.
392
425
  */
393
426
  getFormattedQuery() {
394
- const query = this.getKnexQuery().toSQL();
427
+ const query = this.toQuery()._sql;
395
428
  return this.platform.formatQuery(query.sql, query.bindings);
396
429
  }
397
430
  /**
@@ -433,7 +466,7 @@ class QueryBuilder {
433
466
  return cached.data;
434
467
  }
435
468
  const type = this.connectionType || (method === 'run' ? 'write' : 'read');
436
- const res = await this.driver.getConnection(type).execute(query.sql, query.bindings, method, this.context);
469
+ const res = await this.driver.getConnection(type).execute(query.sql, query.bindings, method, this.context, this.logging);
437
470
  const meta = this.mainAlias.metadata;
438
471
  if (!mapResults || !meta) {
439
472
  await this.em?.storeCache(this._cache, cached, res);
@@ -464,7 +497,27 @@ class QueryBuilder {
464
497
  if (this._joinedProps.size > 0) {
465
498
  res = this.driver.mergeJoinedResult(res, this.mainAlias.metadata);
466
499
  }
467
- return res.map(r => this.em.map(this.mainAlias.entityName, r, { schema: this._schema }));
500
+ const entities = [];
501
+ function propagatePopulateHint(entity, hint) {
502
+ (0, core_1.helper)(entity).__serializationContext.populate ??= hint;
503
+ hint.forEach(pop => {
504
+ const value = entity[pop.field];
505
+ if (core_1.Utils.isEntity(value, true)) {
506
+ (0, core_1.helper)(value).populated();
507
+ propagatePopulateHint(value, pop.children ?? []);
508
+ }
509
+ else if (core_1.Utils.isCollection(value)) {
510
+ value.populated();
511
+ value.getItems(false).forEach(item => propagatePopulateHint(item, pop.children ?? []));
512
+ }
513
+ });
514
+ }
515
+ for (const r of res) {
516
+ const entity = this.em.map(this.mainAlias.entityName, r, { schema: this._schema });
517
+ propagatePopulateHint(entity, this._populate);
518
+ entities.push(entity);
519
+ }
520
+ return entities;
468
521
  }
469
522
  /**
470
523
  * Executes the query, returning the first result or null
@@ -488,11 +541,24 @@ class QueryBuilder {
488
541
  }
489
542
  return res ? +res.count : 0;
490
543
  }
544
+ /**
545
+ * Executes the query, returning both array of results and total count query (without offset and limit).
546
+ */
547
+ async getResultAndCount() {
548
+ return Promise.all([
549
+ this.getResultList(),
550
+ this.getCount(),
551
+ ]);
552
+ }
491
553
  /**
492
554
  * Provides promise-like interface so we can await the QB instance.
493
555
  */
494
556
  then(onfulfilled, onrejected) {
495
- switch (this.type ?? enums_1.QueryType.SELECT) {
557
+ let type = this.type ?? enums_1.QueryType.SELECT;
558
+ if (this.flags.has(core_1.QueryFlag.UPDATE_SUB_QUERY) || this.flags.has(core_1.QueryFlag.DELETE_SUB_QUERY)) {
559
+ type = enums_1.QueryType.UPDATE;
560
+ }
561
+ switch (type) {
496
562
  case enums_1.QueryType.INSERT:
497
563
  case enums_1.QueryType.UPDATE:
498
564
  case enums_1.QueryType.DELETE:
@@ -515,7 +581,10 @@ class QueryBuilder {
515
581
  /* istanbul ignore next */
516
582
  alias = meta?.properties[f]?.fieldNames[0] ?? alias;
517
583
  }
518
- return qb.as(alias);
584
+ const ret = qb.as(alias);
585
+ // tag the instance, so it is possible to detect it easily
586
+ Object.defineProperty(ret, '__as', { enumerable: false, value: alias });
587
+ return ret;
519
588
  }
520
589
  clone() {
521
590
  const qb = new QueryBuilder(this.mainAlias.entityName, this.metadata, this.driver, this.context, this.mainAlias.aliasName, this.connectionType, this.em);
@@ -523,7 +592,7 @@ class QueryBuilder {
523
592
  // clone array/object properties
524
593
  const properties = [
525
594
  'flags', '_populate', '_populateWhere', '_populateMap', '_joins', '_joinedProps', '_cond', '_data', '_orderBy',
526
- '_schema', '_indexHint', '_cache', 'subQueries', 'lockMode', 'lockTables',
595
+ '_schema', '_indexHint', '_cache', 'subQueries', 'lockMode', 'lockTables', '_groupBy', '_having', '_returning',
527
596
  ];
528
597
  properties.forEach(prop => qb[prop] = core_1.Utils.copy(this[prop]));
529
598
  /* istanbul ignore else */
@@ -554,11 +623,15 @@ class QueryBuilder {
554
623
  joinReference(field, alias, cond, type, path) {
555
624
  this.ensureNotFinalized();
556
625
  const [fromAlias, fromField] = this.helper.splitField(field);
557
- const entityName = this._aliases[fromAlias]?.entityName;
626
+ const q = (str) => `'${str}'`;
627
+ if (!this._aliases[fromAlias]) {
628
+ 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(', ')}.`);
629
+ }
630
+ const entityName = this._aliases[fromAlias].entityName;
558
631
  const meta = this.metadata.get(entityName);
559
632
  const prop = meta.properties[fromField];
560
633
  if (!prop) {
561
- throw new Error(`Trying to join ${field}, but ${fromField} is not a defined relation on ${meta.className}`);
634
+ throw new Error(`Trying to join ${q(field)}, but ${q(fromField)} is not a defined relation on ${meta.className}.`);
562
635
  }
563
636
  this.createAlias(prop.type, alias);
564
637
  cond = core_1.QueryHelper.processWhere({
@@ -570,11 +643,11 @@ class QueryBuilder {
570
643
  aliased: !this.type || [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(this.type),
571
644
  });
572
645
  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) {
646
+ path ??= `${(Object.values(this._joins).find(j => j.alias === fromAlias)?.path ?? entityName)}.${prop.name}`;
647
+ if (prop.kind === core_1.ReferenceKind.ONE_TO_MANY) {
575
648
  this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond);
576
649
  }
577
- else if (prop.reference === core_1.ReferenceType.MANY_TO_MANY) {
650
+ else if (prop.kind === core_1.ReferenceKind.MANY_TO_MANY) {
578
651
  let pivotAlias = alias;
579
652
  if (type !== 'pivotJoin') {
580
653
  const oldPivotAlias = this.getAliasForJoinPath(path + '[pivot]');
@@ -585,7 +658,7 @@ class QueryBuilder {
585
658
  Object.assign(this._joins, joins);
586
659
  this.createAlias(prop.pivotEntity, pivotAlias);
587
660
  }
588
- else if (prop.reference === core_1.ReferenceType.ONE_TO_ONE) {
661
+ else if (prop.kind === core_1.ReferenceKind.ONE_TO_ONE) {
589
662
  this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond);
590
663
  }
591
664
  else { // MANY_TO_ONE
@@ -599,17 +672,25 @@ class QueryBuilder {
599
672
  prepareFields(fields, type = 'where') {
600
673
  const ret = [];
601
674
  fields.forEach(field => {
675
+ const rawField = core_1.RawQueryFragment.getKnownFragment(field);
676
+ if (rawField) {
677
+ const sql = this.platform.formatQuery(rawField.sql, rawField.params);
678
+ ret.push(this.knex.raw(sql));
679
+ return;
680
+ }
602
681
  if (!core_1.Utils.isString(field)) {
603
- return ret.push(field);
682
+ ret.push(field);
683
+ return;
604
684
  }
605
685
  const join = Object.keys(this._joins).find(k => field === k.substring(0, k.indexOf('#')));
606
686
  if (join && type === 'where') {
607
- return ret.push(...this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[join]));
687
+ ret.push(...this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[join]));
688
+ return;
608
689
  }
609
690
  const [a, f] = this.helper.splitField(field);
610
691
  const prop = this.helper.getProperty(f, a);
611
692
  /* istanbul ignore next */
612
- if (prop && [core_1.ReferenceType.ONE_TO_MANY, core_1.ReferenceType.MANY_TO_MANY].includes(prop.reference)) {
693
+ if (prop && [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
613
694
  return;
614
695
  }
615
696
  if (prop?.embedded) {
@@ -617,7 +698,7 @@ class QueryBuilder {
617
698
  ret.push(fieldName);
618
699
  return;
619
700
  }
620
- if (prop?.reference === core_1.ReferenceType.EMBEDDED) {
701
+ if (prop?.kind === core_1.ReferenceKind.EMBEDDED) {
621
702
  if (prop.object) {
622
703
  ret.push(this.helper.mapper(prop.fieldNames[0], this.type));
623
704
  }
@@ -640,7 +721,7 @@ class QueryBuilder {
640
721
  });
641
722
  const meta = this.mainAlias.metadata;
642
723
  /* istanbul ignore next */
643
- const requiresSQLConversion = meta?.props.filter(p => p.customType?.convertToJSValueSQL) ?? [];
724
+ const requiresSQLConversion = meta?.props.filter(p => p.hasConvertToJSValueSQL) ?? [];
644
725
  if (this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES) && (fields.includes('*') || fields.includes(`${this.mainAlias.aliasName}.*`)) && requiresSQLConversion.length > 0) {
645
726
  requiresSQLConversion.forEach(p => ret.push(this.helper.mapper(p.name, this.type)));
646
727
  }
@@ -649,7 +730,7 @@ class QueryBuilder {
649
730
  const cols = this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[f]);
650
731
  ret.push(...cols);
651
732
  }
652
- if (this._joins[f].prop.reference !== core_1.ReferenceType.ONE_TO_ONE && this._joins[f].inverseJoinColumns) {
733
+ if (this._joins[f].prop.kind !== core_1.ReferenceKind.ONE_TO_ONE && this._joins[f].inverseJoinColumns) {
653
734
  this._joins[f].inverseJoinColumns.forEach(inverseJoinColumn => {
654
735
  core_1.Utils.renameKey(this._cond, inverseJoinColumn, `${this._joins[f].alias}.${inverseJoinColumn}`);
655
736
  });
@@ -668,7 +749,7 @@ class QueryBuilder {
668
749
  }
669
750
  if (data) {
670
751
  if (core_1.Utils.isEntity(data)) {
671
- data = (0, core_1.helper)(data).toJSON();
752
+ data = this.em?.getComparator().prepareEntity(data) ?? (0, core_1.serialize)(data);
672
753
  }
673
754
  this._data = this.helper.processData(data, this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES));
674
755
  }
@@ -722,6 +803,24 @@ class QueryBuilder {
722
803
  }
723
804
  return qb;
724
805
  }
806
+ applyDiscriminatorCondition() {
807
+ const meta = this.mainAlias.metadata;
808
+ if (!meta?.discriminatorValue) {
809
+ return;
810
+ }
811
+ const types = Object.values(meta.root.discriminatorMap).map(cls => this.metadata.find(cls));
812
+ const children = [];
813
+ const lookUpChildren = (ret, type) => {
814
+ const children = types.filter(meta2 => meta2.extends === type);
815
+ children.forEach(m => lookUpChildren(ret, m.className));
816
+ ret.push(...children.filter(c => c.discriminatorValue));
817
+ return children;
818
+ };
819
+ lookUpChildren(children, meta.className);
820
+ this.andWhere({
821
+ [meta.root.discriminatorColumn]: children.length > 0 ? { $in: [meta.discriminatorValue, ...children.map(c => c.discriminatorValue)] } : meta.discriminatorValue,
822
+ });
823
+ }
725
824
  finalize() {
726
825
  if (this.finalized) {
727
826
  return;
@@ -730,10 +829,11 @@ class QueryBuilder {
730
829
  this.select('*');
731
830
  }
732
831
  const meta = this.mainAlias.metadata;
832
+ this.applyDiscriminatorCondition();
733
833
  if (meta && this.flags.has(core_1.QueryFlag.AUTO_JOIN_ONE_TO_ONE_OWNER)) {
734
834
  const relationsToPopulate = this._populate.map(({ field }) => field);
735
835
  meta.relations
736
- .filter(prop => prop.reference === core_1.ReferenceType.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
836
+ .filter(prop => prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
737
837
  .map(prop => ({ field: prop.name }))
738
838
  .forEach(item => this._populate.push(item));
739
839
  }
@@ -742,7 +842,8 @@ class QueryBuilder {
742
842
  const aliasedField = `${fromAlias}.${fromField}`;
743
843
  const join = Object.keys(this._joins).find(k => `${aliasedField}#${this._joins[k].alias}` === k);
744
844
  if (join && this._joins[join] && this.helper.isOneToOneInverse(fromField)) {
745
- return this._populateMap[join] = this._joins[join].alias;
845
+ this._populateMap[join] = this._joins[join].alias;
846
+ return;
746
847
  }
747
848
  if (this.metadata.find(field)?.pivotTable) { // pivot table entity
748
849
  this.autoJoinPivotTable(field);
@@ -763,14 +864,20 @@ class QueryBuilder {
763
864
  const aliased = this.knex.ref(prop.fieldNames[0]).toString();
764
865
  return `${prop.formula(alias)} as ${aliased}`;
765
866
  })
766
- .filter(field => !this._fields.includes(field))
767
- .forEach(field => this.addSelect(field));
867
+ .filter(field => !this._fields.some(f => {
868
+ if (f instanceof core_1.RawQueryFragment) {
869
+ return f.sql === field && f.params.length === 0;
870
+ }
871
+ return f === field;
872
+ }))
873
+ .forEach(field => this._fields.push((0, core_1.raw)(field)));
768
874
  }
875
+ this.processPopulateWhere();
769
876
  core_1.QueryHelper.processObjectParams(this._data);
770
877
  core_1.QueryHelper.processObjectParams(this._cond);
771
878
  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()) {
879
+ // automatically enable paginate flag when we detect to-many joins, but only if there is no `group by` clause
880
+ if (!this.flags.has(core_1.QueryFlag.DISABLE_PAGINATE) && this._groupBy.length === 0 && this.hasToManyJoins()) {
774
881
  this.flags.add(core_1.QueryFlag.PAGINATE);
775
882
  }
776
883
  if (meta && this.flags.has(core_1.QueryFlag.PAGINATE) && (this._limit > 0 || this._offset > 0)) {
@@ -781,17 +888,54 @@ class QueryBuilder {
781
888
  }
782
889
  this.finalized = true;
783
890
  }
891
+ processPopulateWhere() {
892
+ if (this._populateWhere == null || this._populateWhere === core_1.PopulateHint.ALL) {
893
+ return;
894
+ }
895
+ const joins = Object.values(this._joins);
896
+ joins.forEach(join => {
897
+ join.cond_ = join.cond;
898
+ join.cond = {};
899
+ });
900
+ const replaceOnConditions = (cond) => {
901
+ Object.keys(cond).forEach(k => {
902
+ if (core_1.Utils.isOperator(k)) {
903
+ if (Array.isArray(cond[k])) {
904
+ return cond[k].forEach((c) => replaceOnConditions(c));
905
+ }
906
+ return replaceOnConditions(cond[k]);
907
+ }
908
+ const [a] = this.helper.splitField(k);
909
+ const join = joins.find(j => j.alias === a);
910
+ if (join) {
911
+ join.cond = { ...join.cond, [k]: cond[k] };
912
+ }
913
+ });
914
+ };
915
+ if (this._populateWhere === core_1.PopulateHint.INFER) {
916
+ replaceOnConditions(this._cond);
917
+ }
918
+ else if (typeof this._populateWhere === 'object') {
919
+ const cond = CriteriaNodeFactory_1.CriteriaNodeFactory
920
+ .createNode(this.metadata, this.mainAlias.entityName, this._populateWhere)
921
+ .process(this);
922
+ replaceOnConditions(cond);
923
+ }
924
+ }
784
925
  hasToManyJoins() {
785
926
  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);
927
+ return [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(join.prop.kind);
787
928
  });
788
929
  }
789
930
  wrapPaginateSubQuery(meta) {
790
931
  const pks = this.prepareFields(meta.primaryKeys, 'sub-query');
791
932
  const subQuery = this.clone().select(pks).groupBy(pks).limit(this._limit);
933
+ // revert the on conditions added via populateWhere, we want to apply those only once
934
+ Object.values(subQuery._joins).forEach(join => join.cond = join.cond_ ?? {});
792
935
  if (this._offset) {
793
936
  subQuery.offset(this._offset);
794
937
  }
938
+ const addToSelect = [];
795
939
  if (this._orderBy.length > 0) {
796
940
  const orderBy = [];
797
941
  for (const orderMap of this._orderBy) {
@@ -799,28 +943,38 @@ class QueryBuilder {
799
943
  const [a, f] = this.helper.splitField(field);
800
944
  const prop = this.helper.getProperty(f, a);
801
945
  const type = this.platform.castColumn(prop);
802
- orderBy.push({
803
- [`min(${this.ref(this.helper.mapper(field, this.type, undefined, null))}${type})`]: direction,
804
- });
946
+ const fieldName = this.helper.mapper(field, this.type, undefined, null);
947
+ if (!prop?.persist && !prop?.formula) {
948
+ addToSelect.push(fieldName);
949
+ }
950
+ orderBy.push({ [(0, core_1.raw)(`min(${this.ref(fieldName)}${type})`)]: direction });
805
951
  }
806
952
  }
807
953
  subQuery.orderBy(orderBy);
808
954
  }
809
955
  subQuery.finalized = true;
810
956
  const knexQuery = subQuery.as(this.mainAlias.aliasName).clearSelect().select(pks);
957
+ if (addToSelect.length > 0) {
958
+ addToSelect.forEach(prop => {
959
+ const field = this._fields.find(field => {
960
+ if (typeof field === 'object' && field && '__as' in field) {
961
+ return field.__as === prop;
962
+ }
963
+ // not perfect, but should work most of the time, ideally we should check only the alias (`... as alias`)
964
+ return field.toString().includes(prop);
965
+ });
966
+ if (field) {
967
+ knexQuery.select(field);
968
+ }
969
+ });
970
+ }
811
971
  // multiple sub-queries are needed to get around mysql limitations with order by + limit + where in + group by (o.O)
812
972
  // https://stackoverflow.com/questions/17892762/mysql-this-version-of-mysql-doesnt-yet-support-limit-in-all-any-some-subqu
813
973
  const subSubQuery = this.getKnex().select(pks).from(knexQuery);
974
+ subSubQuery.__raw = true; // tag it as there is now way to check via `instanceof`
814
975
  this._limit = undefined;
815
976
  this._offset = undefined;
816
- const cond = this._cond;
817
977
  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
978
  }
825
979
  wrapModifySubQuery(meta) {
826
980
  const subQuery = this.clone();
@@ -841,8 +995,8 @@ class QueryBuilder {
841
995
  const prop = this._cond[pivotMeta.name + '.' + owner.name] || this._orderBy[pivotMeta.name + '.' + owner.name] ? inverse : owner;
842
996
  const pivotAlias = this.getNextAlias(pivotMeta.name);
843
997
  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}`)));
998
+ core_1.Utils.renameKey(this._cond, `${field}.${owner.name}`, core_1.Utils.getPrimaryKeyHash(owner.fieldNames.map(fieldName => `${pivotAlias}.${fieldName}`)), true);
999
+ core_1.Utils.renameKey(this._cond, `${field}.${inverse.name}`, core_1.Utils.getPrimaryKeyHash(inverse.fieldNames.map(fieldName => `${pivotAlias}.${fieldName}`)), true);
846
1000
  this._populateMap[field] = this._joins[field].alias;
847
1001
  }
848
1002
  getSchema(alias) {
@@ -864,11 +1018,11 @@ class QueryBuilder {
864
1018
  fromSubQuery(target, aliasName) {
865
1019
  const subQuery = target.getKnexQuery();
866
1020
  const { entityName } = target.mainAlias;
867
- aliasName ?? (aliasName = this.getNextAlias(entityName));
1021
+ aliasName ??= this.getNextAlias(entityName);
868
1022
  this.createMainAlias(entityName, aliasName, subQuery);
869
1023
  }
870
1024
  fromEntityName(entityName, aliasName) {
871
- aliasName ?? (aliasName = this._mainAlias?.aliasName ?? this.getNextAlias(entityName));
1025
+ aliasName ??= this._mainAlias?.aliasName ?? this.getNextAlias(entityName);
872
1026
  this.createMainAlias(entityName, aliasName);
873
1027
  }
874
1028
  createQueryBuilderHelper() {
@@ -885,5 +1039,27 @@ class QueryBuilder {
885
1039
  throw new Error('This QueryBuilder instance is already finalized, clone it first if you want to modify it.');
886
1040
  }
887
1041
  }
1042
+ /* istanbul ignore next */
1043
+ [(_QueryBuilder_query = new WeakMap(), util_1.inspect.custom)](depth) {
1044
+ const object = { ...this };
1045
+ const hidden = ['metadata', 'driver', 'context', 'platform', 'knex', 'type'];
1046
+ Object.keys(object).filter(k => k.startsWith('_')).forEach(k => delete object[k]);
1047
+ Object.keys(object).filter(k => object[k] == null).forEach(k => delete object[k]);
1048
+ hidden.forEach(k => delete object[k]);
1049
+ let prefix = this.type ? this.type.substring(0, 1) + this.type.toLowerCase().substring(1) : '';
1050
+ if (this._data) {
1051
+ object.data = this._data;
1052
+ }
1053
+ if (!core_1.Utils.isEmpty(this._cond)) {
1054
+ object.where = this._cond;
1055
+ }
1056
+ if (this._onConflict?.[0]) {
1057
+ prefix = 'Upsert';
1058
+ object.onConflict = this._onConflict[0];
1059
+ }
1060
+ const name = this._mainAlias ? `${prefix}QueryBuilder<${this._mainAlias?.entityName}>` : 'QueryBuilder';
1061
+ const ret = (0, util_1.inspect)(object, { depth });
1062
+ return ret === '[Object]' ? `[${name}]` : name + ' ' + ret;
1063
+ }
888
1064
  }
889
1065
  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, returning?: Field<any>[]): 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;