@mikro-orm/knex 6.0.0-dev.5 → 6.0.0-dev.50

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 (40) hide show
  1. package/AbstractSqlConnection.d.ts +3 -0
  2. package/AbstractSqlConnection.js +16 -8
  3. package/AbstractSqlDriver.d.ts +11 -11
  4. package/AbstractSqlDriver.js +100 -40
  5. package/AbstractSqlPlatform.d.ts +2 -4
  6. package/AbstractSqlPlatform.js +6 -16
  7. package/README.md +63 -106
  8. package/SqlEntityManager.d.ts +0 -4
  9. package/SqlEntityManager.js +1 -9
  10. package/SqlEntityRepository.d.ts +0 -1
  11. package/SqlEntityRepository.js +0 -1
  12. package/index.d.ts +1 -0
  13. package/index.js +1 -0
  14. package/index.mjs +173 -0
  15. package/package.json +5 -39
  16. package/query/ArrayCriteriaNode.d.ts +3 -3
  17. package/query/CriteriaNode.d.ts +8 -8
  18. package/query/CriteriaNode.js +8 -8
  19. package/query/CriteriaNodeFactory.d.ts +6 -6
  20. package/query/CriteriaNodeFactory.js +11 -11
  21. package/query/ObjectCriteriaNode.d.ts +3 -3
  22. package/query/ObjectCriteriaNode.js +5 -5
  23. package/query/QueryBuilder.d.ts +11 -4
  24. package/query/QueryBuilder.js +141 -36
  25. package/query/QueryBuilderHelper.d.ts +3 -3
  26. package/query/QueryBuilderHelper.js +47 -19
  27. package/query/ScalarCriteriaNode.d.ts +3 -2
  28. package/query/ScalarCriteriaNode.js +9 -6
  29. package/schema/DatabaseSchema.js +3 -3
  30. package/schema/DatabaseTable.d.ts +9 -9
  31. package/schema/DatabaseTable.js +21 -22
  32. package/schema/SchemaComparator.d.ts +4 -4
  33. package/schema/SchemaComparator.js +4 -2
  34. package/schema/SchemaHelper.d.ts +11 -11
  35. package/schema/SchemaHelper.js +3 -3
  36. package/schema/{SchemaGenerator.d.ts → SqlSchemaGenerator.d.ts} +4 -8
  37. package/schema/{SchemaGenerator.js → SqlSchemaGenerator.js} +17 -24
  38. package/schema/index.d.ts +1 -1
  39. package/schema/index.js +1 -1
  40. package/typings.d.ts +14 -13
@@ -1,24 +1,24 @@
1
1
  /// <reference types="node" />
2
2
  import { inspect } from 'util';
3
- import type { EntityProperty, MetadataStorage } from '@mikro-orm/core';
3
+ import type { EntityKey, EntityProperty, MetadataStorage } from '@mikro-orm/core';
4
4
  import type { ICriteriaNode, IQueryBuilder } from '../typings';
5
5
  /**
6
6
  * Helper for working with deeply nested where/orderBy/having criteria. Uses composite pattern to build tree from the payload.
7
7
  * Auto-joins relations and converts payload from { books: { publisher: { name: '...' } } } to { 'publisher_alias.name': '...' }
8
8
  * @internal
9
9
  */
10
- export declare class CriteriaNode implements ICriteriaNode {
10
+ export declare class CriteriaNode<T extends object> implements ICriteriaNode<T> {
11
11
  protected readonly metadata: MetadataStorage;
12
12
  readonly entityName: string;
13
- readonly parent?: ICriteriaNode | undefined;
14
- readonly key?: string | undefined;
13
+ readonly parent?: ICriteriaNode<T> | undefined;
14
+ readonly key?: EntityKey<T> | undefined;
15
15
  payload: any;
16
- prop?: EntityProperty;
16
+ prop?: EntityProperty<T>;
17
17
  index?: number;
18
- constructor(metadata: MetadataStorage, entityName: string, parent?: ICriteriaNode | undefined, key?: string | undefined, validate?: boolean);
19
- process<T>(qb: IQueryBuilder<T>, alias?: string): any;
18
+ constructor(metadata: MetadataStorage, entityName: string, parent?: ICriteriaNode<T> | undefined, key?: EntityKey<T> | undefined, validate?: boolean);
19
+ process(qb: IQueryBuilder<T>, alias?: string): any;
20
20
  shouldInline(payload: any): boolean;
21
- willAutoJoin<T>(qb: IQueryBuilder<T>, alias?: string): boolean;
21
+ willAutoJoin(qb: IQueryBuilder<T>, alias?: string): boolean;
22
22
  shouldRename(payload: any): boolean;
23
23
  renameFieldToPK<T>(qb: IQueryBuilder<T>): string;
24
24
  getPath(addIndex?: boolean): string;
@@ -40,7 +40,7 @@ class CriteriaNode {
40
40
  return false;
41
41
  }
42
42
  shouldRename(payload) {
43
- const type = this.prop ? this.prop.reference : null;
43
+ const type = this.prop ? this.prop.kind : null;
44
44
  const composite = this.prop?.joinColumns ? this.prop.joinColumns.length > 1 : false;
45
45
  const customExpression = CriteriaNode.isCustomExpression(this.key);
46
46
  const scalar = payload === null || core_1.Utils.isPrimaryKey(payload) || payload instanceof RegExp || payload instanceof Date || customExpression;
@@ -49,17 +49,17 @@ class CriteriaNode {
49
49
  return true;
50
50
  }
51
51
  switch (type) {
52
- case core_1.ReferenceType.MANY_TO_ONE: return false;
53
- case core_1.ReferenceType.ONE_TO_ONE: return !this.prop.owner;
54
- case core_1.ReferenceType.ONE_TO_MANY: return scalar || operator;
55
- case core_1.ReferenceType.MANY_TO_MANY: return scalar || operator;
52
+ case core_1.ReferenceKind.MANY_TO_ONE: return false;
53
+ case core_1.ReferenceKind.ONE_TO_ONE: return !this.prop.owner;
54
+ case core_1.ReferenceKind.ONE_TO_MANY: return scalar || operator;
55
+ case core_1.ReferenceKind.MANY_TO_MANY: return scalar || operator;
56
56
  default: return false;
57
57
  }
58
58
  }
59
59
  renameFieldToPK(qb) {
60
60
  const joinAlias = qb.getAliasForJoinPath(this.getPath());
61
61
  const alias = joinAlias ?? qb.alias;
62
- if (this.prop.reference === core_1.ReferenceType.MANY_TO_MANY) {
62
+ if (this.prop.kind === core_1.ReferenceKind.MANY_TO_MANY) {
63
63
  return core_1.Utils.getPrimaryKeyHash(this.prop.inverseJoinColumns.map(col => `${alias}.${col}`));
64
64
  }
65
65
  // if we found a matching join, we need to use the target table column names, as we use that alias instead of the root
@@ -70,7 +70,7 @@ class CriteriaNode {
70
70
  }
71
71
  getPath(addIndex = false) {
72
72
  // use index on parent only if we are processing to-many relation
73
- const addParentIndex = this.prop && [core_1.ReferenceType.ONE_TO_MANY, core_1.ReferenceType.MANY_TO_MANY].includes(this.prop.reference);
73
+ const addParentIndex = this.prop && [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(this.prop.kind);
74
74
  const parentPath = this.parent?.getPath(addParentIndex) ?? this.entityName;
75
75
  const index = addIndex && this.index != null ? `[${this.index}]` : '';
76
76
  // ignore group operators to allow easier mapping (e.g. for orderBy)
@@ -89,7 +89,7 @@ class CriteriaNode {
89
89
  const customExpression = CriteriaNode.isCustomExpression(this.key);
90
90
  const scalar = this.payload === null || core_1.Utils.isPrimaryKey(this.payload) || this.payload instanceof RegExp || this.payload instanceof Date || customExpression;
91
91
  const operator = core_1.Utils.isObject(this.payload) && Object.keys(this.payload).every(k => core_1.Utils.isOperator(k, false));
92
- return this.prop.reference === core_1.ReferenceType.MANY_TO_MANY && (scalar || operator);
92
+ return this.prop.kind === core_1.ReferenceKind.MANY_TO_MANY && (scalar || operator);
93
93
  }
94
94
  getPivotPath(path) {
95
95
  return `${path}[pivot]`;
@@ -1,12 +1,12 @@
1
- import type { Dictionary, EntityMetadata, MetadataStorage } from '@mikro-orm/core';
1
+ import type { Dictionary, EntityKey, EntityMetadata, MetadataStorage } from '@mikro-orm/core';
2
2
  import type { ICriteriaNode } from '../typings';
3
3
  /**
4
4
  * @internal
5
5
  */
6
6
  export declare class CriteriaNodeFactory {
7
- static createNode(metadata: MetadataStorage, entityName: string, payload: any, parent?: ICriteriaNode, key?: string): ICriteriaNode;
8
- static createScalarNode(metadata: MetadataStorage, entityName: string, payload: any, parent?: ICriteriaNode, key?: string): ICriteriaNode;
9
- static createArrayNode(metadata: MetadataStorage, entityName: string, payload: any[], parent?: ICriteriaNode, key?: string): ICriteriaNode;
10
- static createObjectNode(metadata: MetadataStorage, entityName: string, payload: Dictionary, parent?: ICriteriaNode, key?: string): ICriteriaNode;
11
- static createObjectItemNode(metadata: MetadataStorage, entityName: string, node: ICriteriaNode, payload: Dictionary, item: string, meta?: EntityMetadata): ICriteriaNode;
7
+ static createNode<T extends object>(metadata: MetadataStorage, entityName: string, payload: any, parent?: ICriteriaNode<T>, key?: EntityKey<T>): ICriteriaNode<T>;
8
+ static createScalarNode<T extends object>(metadata: MetadataStorage, entityName: string, payload: any, parent?: ICriteriaNode<T>, key?: EntityKey<T>): ICriteriaNode<T>;
9
+ static createArrayNode<T extends object>(metadata: MetadataStorage, entityName: string, payload: any[], parent?: ICriteriaNode<T>, key?: EntityKey<T>): ICriteriaNode<T>;
10
+ static createObjectNode<T extends object>(metadata: MetadataStorage, entityName: string, payload: Dictionary, parent?: ICriteriaNode<T>, key?: EntityKey<T>): ICriteriaNode<T>;
11
+ static createObjectItemNode<T extends object>(metadata: MetadataStorage, entityName: string, node: ICriteriaNode<T>, payload: Dictionary, key: EntityKey<T>, meta?: EntityMetadata<T>): ICriteriaNode<T>;
12
12
  }
@@ -12,7 +12,7 @@ const CriteriaNode_1 = require("./CriteriaNode");
12
12
  class CriteriaNodeFactory {
13
13
  static createNode(metadata, entityName, payload, parent, key) {
14
14
  const customExpression = CriteriaNode_1.CriteriaNode.isCustomExpression(key || '');
15
- const scalar = core_1.Utils.isPrimaryKey(payload) || payload instanceof RegExp || payload instanceof Date || customExpression;
15
+ const scalar = core_1.Utils.isPrimaryKey(payload) || core_1.Utils.isRawSql(payload) || payload instanceof RegExp || payload instanceof Date || customExpression;
16
16
  if (Array.isArray(payload) && !scalar) {
17
17
  return this.createArrayNode(metadata, entityName, payload, parent, key);
18
18
  }
@@ -37,7 +37,7 @@ class CriteriaNodeFactory {
37
37
  }
38
38
  static createObjectNode(metadata, entityName, payload, parent, key) {
39
39
  const meta = metadata.find(entityName);
40
- if (!parent && Object.keys(payload).every(k => meta?.properties[k]?.reference === core_1.ReferenceType.SCALAR)) {
40
+ if (!parent && Object.keys(payload).every(k => meta?.properties[k]?.kind === core_1.ReferenceKind.SCALAR)) {
41
41
  return this.createScalarNode(metadata, entityName, payload, parent, key);
42
42
  }
43
43
  const node = new ObjectCriteriaNode_1.ObjectCriteriaNode(metadata, entityName, parent, key);
@@ -47,24 +47,24 @@ class CriteriaNodeFactory {
47
47
  }, {});
48
48
  return node;
49
49
  }
50
- static createObjectItemNode(metadata, entityName, node, payload, item, meta) {
51
- const prop = meta?.properties[item];
52
- if (prop?.reference !== core_1.ReferenceType.EMBEDDED) {
53
- const childEntity = prop && prop.reference !== core_1.ReferenceType.SCALAR ? prop.type : entityName;
54
- return this.createNode(metadata, childEntity, payload[item], node, item);
50
+ static createObjectItemNode(metadata, entityName, node, payload, key, meta) {
51
+ const prop = meta?.properties[key];
52
+ if (prop?.kind !== core_1.ReferenceKind.EMBEDDED) {
53
+ const childEntity = prop && prop.kind !== core_1.ReferenceKind.SCALAR ? prop.type : entityName;
54
+ return this.createNode(metadata, childEntity, payload[key], node, key);
55
55
  }
56
- const operator = Object.keys(payload[item]).some(f => core_1.Utils.isOperator(f));
56
+ const operator = Object.keys(payload[key]).some(f => core_1.Utils.isOperator(f));
57
57
  if (operator) {
58
58
  throw core_1.ValidationError.cannotUseOperatorsInsideEmbeddables(entityName, prop.name, payload);
59
59
  }
60
- const map = Object.keys(payload[item]).reduce((oo, k) => {
60
+ const map = Object.keys(payload[key]).reduce((oo, k) => {
61
61
  if (!prop.embeddedProps[k]) {
62
62
  throw core_1.ValidationError.invalidEmbeddableQuery(entityName, k, prop.type);
63
63
  }
64
- oo[prop.embeddedProps[k].name] = payload[item][k];
64
+ oo[prop.embeddedProps[k].name] = payload[key][k];
65
65
  return oo;
66
66
  }, {});
67
- return this.createNode(metadata, entityName, map, node, item);
67
+ return this.createNode(metadata, entityName, map, node, key);
68
68
  }
69
69
  }
70
70
  exports.CriteriaNodeFactory = CriteriaNodeFactory;
@@ -3,9 +3,9 @@ import type { IQueryBuilder } from '../typings';
3
3
  /**
4
4
  * @internal
5
5
  */
6
- export declare class ObjectCriteriaNode extends CriteriaNode {
7
- process<T>(qb: IQueryBuilder<T>, alias?: string): any;
8
- willAutoJoin<T>(qb: IQueryBuilder<T>, alias?: string): boolean;
6
+ export declare class ObjectCriteriaNode<T extends object> extends CriteriaNode<T> {
7
+ process(qb: IQueryBuilder<T>, alias?: string): any;
8
+ willAutoJoin(qb: IQueryBuilder<T>, alias?: string): boolean;
9
9
  shouldInline(payload: any): boolean;
10
10
  private inlineChildPayload;
11
11
  private shouldAutoJoin;
@@ -60,7 +60,7 @@ class ObjectCriteriaNode extends CriteriaNode_1.CriteriaNode {
60
60
  const customExpression = ObjectCriteriaNode.isCustomExpression(this.key);
61
61
  const scalar = core_1.Utils.isPrimaryKey(payload) || payload instanceof RegExp || payload instanceof Date || customExpression;
62
62
  const operator = core_1.Utils.isObject(payload) && Object.keys(payload).every(k => core_1.Utils.isOperator(k, false));
63
- return !!this.prop && this.prop.reference !== core_1.ReferenceType.SCALAR && !scalar && !operator;
63
+ return !!this.prop && this.prop.kind !== core_1.ReferenceKind.SCALAR && !scalar && !operator;
64
64
  }
65
65
  inlineChildPayload(o, payload, field, alias, childAlias) {
66
66
  const prop = this.metadata.find(this.entityName).properties[field];
@@ -104,15 +104,15 @@ class ObjectCriteriaNode extends CriteriaNode_1.CriteriaNode {
104
104
  if (!this.prop || !this.parent) {
105
105
  return false;
106
106
  }
107
- const embeddable = this.prop.reference === core_1.ReferenceType.EMBEDDED;
108
- const knownKey = [core_1.ReferenceType.SCALAR, core_1.ReferenceType.MANY_TO_ONE, core_1.ReferenceType.EMBEDDED].includes(this.prop.reference) || (this.prop.reference === core_1.ReferenceType.ONE_TO_ONE && this.prop.owner);
107
+ const embeddable = this.prop.kind === core_1.ReferenceKind.EMBEDDED;
108
+ const knownKey = [core_1.ReferenceKind.SCALAR, core_1.ReferenceKind.MANY_TO_ONE, core_1.ReferenceKind.EMBEDDED].includes(this.prop.kind) || (this.prop.kind === core_1.ReferenceKind.ONE_TO_ONE && this.prop.owner);
109
109
  const operatorKeys = knownKey && Object.keys(this.payload).every(key => core_1.Utils.isOperator(key, false));
110
110
  const primaryKeys = knownKey && Object.keys(this.payload).every(key => {
111
111
  const meta = this.metadata.find(this.entityName);
112
112
  if (!meta.primaryKeys.includes(key)) {
113
113
  return false;
114
114
  }
115
- if (!core_1.Utils.isPlainObject(this.payload[key].payload) || ![core_1.ReferenceType.ONE_TO_ONE, core_1.ReferenceType.MANY_TO_ONE].includes(meta.properties[key].reference)) {
115
+ if (!core_1.Utils.isPlainObject(this.payload[key].payload) || ![core_1.ReferenceKind.ONE_TO_ONE, core_1.ReferenceKind.MANY_TO_ONE].includes(meta.properties[key].kind)) {
116
116
  return true;
117
117
  }
118
118
  return Object.keys(this.payload[key].payload).every(k => meta.properties[key].targetMeta.primaryKeys.includes(k));
@@ -125,7 +125,7 @@ class ObjectCriteriaNode extends CriteriaNode_1.CriteriaNode {
125
125
  const scalar = core_1.Utils.isPrimaryKey(this.payload) || this.payload instanceof RegExp || this.payload instanceof Date || customExpression;
126
126
  const operator = core_1.Utils.isPlainObject(this.payload) && Object.keys(this.payload).every(k => core_1.Utils.isOperator(k, false));
127
127
  const field = `${alias}.${this.prop.name}`;
128
- if (this.prop.reference === core_1.ReferenceType.MANY_TO_MANY && (scalar || operator)) {
128
+ if (this.prop.kind === core_1.ReferenceKind.MANY_TO_MANY && (scalar || operator)) {
129
129
  qb.join(field, nestedAlias, undefined, 'pivotJoin', this.getPath());
130
130
  }
131
131
  else {
@@ -1,10 +1,12 @@
1
+ /// <reference types="node" />
2
+ import { inspect } from 'util';
1
3
  import type { Knex } from 'knex';
2
4
  import type { AnyEntity, ConnectionType, Dictionary, EntityData, EntityName, EntityProperty, FlushMode, GroupOperator, MetadataStorage, ObjectQuery, PopulateOptions, QBFilterQuery, QBQueryOrderMap, QueryResult, RequiredEntityData } from '@mikro-orm/core';
3
5
  import { LockMode, PopulateHint, QueryFlag } from '@mikro-orm/core';
4
6
  import { QueryType } from './enums';
5
7
  import type { AbstractSqlDriver } from '../AbstractSqlDriver';
6
- import { QueryBuilderHelper } from './QueryBuilderHelper';
7
8
  import type { Alias } from './QueryBuilderHelper';
9
+ import { QueryBuilderHelper } from './QueryBuilderHelper';
8
10
  import type { SqlEntityManager } from '../SqlEntityManager';
9
11
  import type { Field } from '../typings';
10
12
  /**
@@ -75,7 +77,7 @@ export declare class QueryBuilder<T extends object = AnyEntity> {
75
77
  /**
76
78
  * @internal
77
79
  */
78
- constructor(entityName: EntityName<T> | QueryBuilder<T>, metadata: MetadataStorage, driver: AbstractSqlDriver, context?: Knex.Transaction<any, any[]> | undefined, alias?: string, connectionType?: ConnectionType | undefined, em?: SqlEntityManager<AbstractSqlDriver<import("../AbstractSqlConnection").AbstractSqlConnection>> | undefined);
80
+ constructor(entityName: EntityName<T> | QueryBuilder<T>, metadata: MetadataStorage, driver: AbstractSqlDriver, context?: Knex.Transaction<any, any[]> | undefined, alias?: string, connectionType?: ConnectionType | undefined, em?: SqlEntityManager<AbstractSqlDriver<import("..").AbstractSqlConnection, import("..").AbstractSqlPlatform>> | undefined);
79
81
  select(fields: Field<T> | Field<T>[], distinct?: boolean): SelectQueryBuilder<T>;
80
82
  addSelect(fields: Field<T> | Field<T>[]): SelectQueryBuilder<T>;
81
83
  distinct(): SelectQueryBuilder<T>;
@@ -90,7 +92,7 @@ export declare class QueryBuilder<T extends object = AnyEntity> {
90
92
  leftJoin(field: string, alias: string, cond?: QBFilterQuery): this;
91
93
  joinAndSelect(field: string, alias: string, cond?: QBFilterQuery, type?: 'leftJoin' | 'innerJoin' | 'pivotJoin', path?: string): SelectQueryBuilder<T>;
92
94
  leftJoinAndSelect(field: string, alias: string, cond?: QBFilterQuery): SelectQueryBuilder<T>;
93
- protected getFieldsForJoinedLoad<U extends object>(prop: EntityProperty<U>, alias: string): Field<U>[];
95
+ protected getFieldsForJoinedLoad(prop: EntityProperty<T>, alias: string): Field<T>[];
94
96
  withSubQuery(subQuery: Knex.QueryBuilder, alias: string): this;
95
97
  where(cond: QBFilterQuery<T>, operator?: keyof typeof GroupOperator): this;
96
98
  where(cond: string, params?: any[], operator?: keyof typeof GroupOperator): this;
@@ -114,7 +116,6 @@ export declare class QueryBuilder<T extends object = AnyEntity> {
114
116
  ref(field: string): Knex.Ref<string, {
115
117
  [x: string]: string;
116
118
  }>;
117
- raw<R = Knex.Raw>(sql: string, bindings?: Knex.RawBinding[] | Knex.ValueDict): R;
118
119
  limit(limit?: number, offset?: number): this;
119
120
  offset(offset?: number): this;
120
121
  withSchema(schema?: string): this;
@@ -179,6 +180,10 @@ export declare class QueryBuilder<T extends object = AnyEntity> {
179
180
  * Executes count query (without offset and limit), returning total count of results
180
181
  */
181
182
  getCount(field?: string | string[], distinct?: boolean): Promise<number>;
183
+ /**
184
+ * Executes the query, returning both array of results and total count query (without offset and limit).
185
+ */
186
+ getResultAndCount(): Promise<[T[], number]>;
182
187
  /**
183
188
  * Provides promise-like interface so we can await the QB instance.
184
189
  */
@@ -195,6 +200,7 @@ export declare class QueryBuilder<T extends object = AnyEntity> {
195
200
  private init;
196
201
  private getQueryBase;
197
202
  private finalize;
203
+ private processPopulateWhere;
198
204
  private hasToManyJoins;
199
205
  private wrapPaginateSubQuery;
200
206
  private wrapModifySubQuery;
@@ -207,6 +213,7 @@ export declare class QueryBuilder<T extends object = AnyEntity> {
207
213
  private createQueryBuilderHelper;
208
214
  private ensureFromClause;
209
215
  private ensureNotFinalized;
216
+ [inspect.custom](depth: number): string;
210
217
  }
211
218
  export interface RunQueryBuilder<T extends object> extends Omit<QueryBuilder<T>, 'getResult' | 'getSingleResult' | 'getResultList' | 'where'> {
212
219
  where(cond: QBFilterQuery<T> | string, params?: keyof typeof GroupOperator | any[], operator?: keyof typeof GroupOperator): this;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.QueryBuilder = void 0;
4
+ const util_1 = require("util");
4
5
  const core_1 = require("@mikro-orm/core");
5
6
  const enums_1 = require("./enums");
6
7
  const QueryBuilderHelper_1 = require("./QueryBuilderHelper");
@@ -115,7 +116,7 @@ class QueryBuilder {
115
116
  this._fields = this.mainAlias.metadata.primaryKeys;
116
117
  }
117
118
  else {
118
- this._fields = [this.raw('*')];
119
+ this._fields = [(0, core_1.raw)('*')];
119
120
  }
120
121
  if (distinct) {
121
122
  this.flags.add(core_1.QueryFlag.DISTINCT);
@@ -277,11 +278,6 @@ class QueryBuilder {
277
278
  ref(field) {
278
279
  return this.knex.ref(field);
279
280
  }
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
281
  limit(limit, offset = 0) {
286
282
  this.ensureNotFinalized();
287
283
  this._limit = limit;
@@ -360,7 +356,8 @@ class QueryBuilder {
360
356
  core_1.Utils.runIfNotEmpty(() => {
361
357
  const queryOrder = this.helper.getQueryOrder(this.type ?? enums_1.QueryType.SELECT, this._orderBy, this._populateMap);
362
358
  if (queryOrder) {
363
- return qb.orderByRaw(queryOrder);
359
+ qb.orderByRaw(queryOrder);
360
+ return;
364
361
  }
365
362
  }, this._orderBy);
366
363
  core_1.Utils.runIfNotEmpty(() => qb.limit(this._limit), this._limit != null);
@@ -372,7 +369,7 @@ class QueryBuilder {
372
369
  if (this.lockMode) {
373
370
  this.helper.getLockSQL(qb, this.lockMode, this.lockTables);
374
371
  }
375
- this.helper.finalize(this.type ?? enums_1.QueryType.SELECT, qb, this.mainAlias.metadata);
372
+ this.helper.finalize(this.type ?? enums_1.QueryType.SELECT, qb, this.mainAlias.metadata, this._data);
376
373
  return qb;
377
374
  }
378
375
  /**
@@ -464,7 +461,27 @@ class QueryBuilder {
464
461
  if (this._joinedProps.size > 0) {
465
462
  res = this.driver.mergeJoinedResult(res, this.mainAlias.metadata);
466
463
  }
467
- return res.map(r => this.em.map(this.mainAlias.entityName, r, { schema: this._schema }));
464
+ const entities = [];
465
+ function propagatePopulateHint(entity, hint) {
466
+ (0, core_1.helper)(entity).__serializationContext.populate ??= hint;
467
+ hint.forEach(pop => {
468
+ const value = entity[pop.field];
469
+ if (core_1.Utils.isEntity(value, true)) {
470
+ (0, core_1.helper)(value).populated();
471
+ propagatePopulateHint(value, pop.children ?? []);
472
+ }
473
+ else if (core_1.Utils.isCollection(value)) {
474
+ value.populated();
475
+ value.getItems(false).forEach(item => propagatePopulateHint(item, pop.children ?? []));
476
+ }
477
+ });
478
+ }
479
+ for (const r of res) {
480
+ const entity = this.em.map(this.mainAlias.entityName, r, { schema: this._schema });
481
+ propagatePopulateHint(entity, this._populate);
482
+ entities.push(entity);
483
+ }
484
+ return entities;
468
485
  }
469
486
  /**
470
487
  * Executes the query, returning the first result or null
@@ -488,11 +505,24 @@ class QueryBuilder {
488
505
  }
489
506
  return res ? +res.count : 0;
490
507
  }
508
+ /**
509
+ * Executes the query, returning both array of results and total count query (without offset and limit).
510
+ */
511
+ async getResultAndCount() {
512
+ return Promise.all([
513
+ this.getResultList(),
514
+ this.getCount(),
515
+ ]);
516
+ }
491
517
  /**
492
518
  * Provides promise-like interface so we can await the QB instance.
493
519
  */
494
520
  then(onfulfilled, onrejected) {
495
- switch (this.type ?? enums_1.QueryType.SELECT) {
521
+ let type = this.type ?? enums_1.QueryType.SELECT;
522
+ if (this.flags.has(core_1.QueryFlag.UPDATE_SUB_QUERY) || this.flags.has(core_1.QueryFlag.DELETE_SUB_QUERY)) {
523
+ type = enums_1.QueryType.UPDATE;
524
+ }
525
+ switch (type) {
496
526
  case enums_1.QueryType.INSERT:
497
527
  case enums_1.QueryType.UPDATE:
498
528
  case enums_1.QueryType.DELETE:
@@ -515,7 +545,10 @@ class QueryBuilder {
515
545
  /* istanbul ignore next */
516
546
  alias = meta?.properties[f]?.fieldNames[0] ?? alias;
517
547
  }
518
- return qb.as(alias);
548
+ const ret = qb.as(alias);
549
+ // tag the instance, so it is possible to detect it easily
550
+ Object.defineProperty(ret, '__as', { enumerable: false, value: alias });
551
+ return ret;
519
552
  }
520
553
  clone() {
521
554
  const qb = new QueryBuilder(this.mainAlias.entityName, this.metadata, this.driver, this.context, this.mainAlias.aliasName, this.connectionType, this.em);
@@ -570,11 +603,11 @@ class QueryBuilder {
570
603
  aliased: !this.type || [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(this.type),
571
604
  });
572
605
  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) {
606
+ path ??= `${(Object.values(this._joins).find(j => j.alias === fromAlias)?.path ?? entityName)}.${prop.name}`;
607
+ if (prop.kind === core_1.ReferenceKind.ONE_TO_MANY) {
575
608
  this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond);
576
609
  }
577
- else if (prop.reference === core_1.ReferenceType.MANY_TO_MANY) {
610
+ else if (prop.kind === core_1.ReferenceKind.MANY_TO_MANY) {
578
611
  let pivotAlias = alias;
579
612
  if (type !== 'pivotJoin') {
580
613
  const oldPivotAlias = this.getAliasForJoinPath(path + '[pivot]');
@@ -585,7 +618,7 @@ class QueryBuilder {
585
618
  Object.assign(this._joins, joins);
586
619
  this.createAlias(prop.pivotEntity, pivotAlias);
587
620
  }
588
- else if (prop.reference === core_1.ReferenceType.ONE_TO_ONE) {
621
+ else if (prop.kind === core_1.ReferenceKind.ONE_TO_ONE) {
589
622
  this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond);
590
623
  }
591
624
  else { // MANY_TO_ONE
@@ -600,16 +633,18 @@ class QueryBuilder {
600
633
  const ret = [];
601
634
  fields.forEach(field => {
602
635
  if (!core_1.Utils.isString(field)) {
603
- return ret.push(field);
636
+ ret.push(field);
637
+ return;
604
638
  }
605
639
  const join = Object.keys(this._joins).find(k => field === k.substring(0, k.indexOf('#')));
606
640
  if (join && type === 'where') {
607
- return ret.push(...this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[join]));
641
+ ret.push(...this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[join]));
642
+ return;
608
643
  }
609
644
  const [a, f] = this.helper.splitField(field);
610
645
  const prop = this.helper.getProperty(f, a);
611
646
  /* istanbul ignore next */
612
- if (prop && [core_1.ReferenceType.ONE_TO_MANY, core_1.ReferenceType.MANY_TO_MANY].includes(prop.reference)) {
647
+ if (prop && [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
613
648
  return;
614
649
  }
615
650
  if (prop?.embedded) {
@@ -617,7 +652,7 @@ class QueryBuilder {
617
652
  ret.push(fieldName);
618
653
  return;
619
654
  }
620
- if (prop?.reference === core_1.ReferenceType.EMBEDDED) {
655
+ if (prop?.kind === core_1.ReferenceKind.EMBEDDED) {
621
656
  if (prop.object) {
622
657
  ret.push(this.helper.mapper(prop.fieldNames[0], this.type));
623
658
  }
@@ -649,7 +684,7 @@ class QueryBuilder {
649
684
  const cols = this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[f]);
650
685
  ret.push(...cols);
651
686
  }
652
- if (this._joins[f].prop.reference !== core_1.ReferenceType.ONE_TO_ONE && this._joins[f].inverseJoinColumns) {
687
+ if (this._joins[f].prop.kind !== core_1.ReferenceKind.ONE_TO_ONE && this._joins[f].inverseJoinColumns) {
653
688
  this._joins[f].inverseJoinColumns.forEach(inverseJoinColumn => {
654
689
  core_1.Utils.renameKey(this._cond, inverseJoinColumn, `${this._joins[f].alias}.${inverseJoinColumn}`);
655
690
  });
@@ -668,7 +703,7 @@ class QueryBuilder {
668
703
  }
669
704
  if (data) {
670
705
  if (core_1.Utils.isEntity(data)) {
671
- data = (0, core_1.helper)(data).toJSON();
706
+ data = this.em?.getComparator().prepareEntity(data) ?? (0, core_1.serialize)(data);
672
707
  }
673
708
  this._data = this.helper.processData(data, this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES));
674
709
  }
@@ -733,7 +768,7 @@ class QueryBuilder {
733
768
  if (meta && this.flags.has(core_1.QueryFlag.AUTO_JOIN_ONE_TO_ONE_OWNER)) {
734
769
  const relationsToPopulate = this._populate.map(({ field }) => field);
735
770
  meta.relations
736
- .filter(prop => prop.reference === core_1.ReferenceType.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
771
+ .filter(prop => prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
737
772
  .map(prop => ({ field: prop.name }))
738
773
  .forEach(item => this._populate.push(item));
739
774
  }
@@ -742,7 +777,8 @@ class QueryBuilder {
742
777
  const aliasedField = `${fromAlias}.${fromField}`;
743
778
  const join = Object.keys(this._joins).find(k => `${aliasedField}#${this._joins[k].alias}` === k);
744
779
  if (join && this._joins[join] && this.helper.isOneToOneInverse(fromField)) {
745
- return this._populateMap[join] = this._joins[join].alias;
780
+ this._populateMap[join] = this._joins[join].alias;
781
+ return;
746
782
  }
747
783
  if (this.metadata.find(field)?.pivotTable) { // pivot table entity
748
784
  this.autoJoinPivotTable(field);
@@ -766,6 +802,7 @@ class QueryBuilder {
766
802
  .filter(field => !this._fields.includes(field))
767
803
  .forEach(field => this.addSelect(field));
768
804
  }
805
+ this.processPopulateWhere();
769
806
  core_1.QueryHelper.processObjectParams(this._data);
770
807
  core_1.QueryHelper.processObjectParams(this._cond);
771
808
  core_1.QueryHelper.processObjectParams(this._having);
@@ -781,17 +818,54 @@ class QueryBuilder {
781
818
  }
782
819
  this.finalized = true;
783
820
  }
821
+ processPopulateWhere() {
822
+ if (this._populateWhere == null || this._populateWhere === core_1.PopulateHint.ALL) {
823
+ return;
824
+ }
825
+ const joins = Object.values(this._joins);
826
+ joins.forEach(join => {
827
+ join.cond_ = join.cond;
828
+ join.cond = {};
829
+ });
830
+ const replaceOnConditions = (cond) => {
831
+ Object.keys(cond).forEach(k => {
832
+ if (core_1.Utils.isOperator(k)) {
833
+ if (Array.isArray(cond[k])) {
834
+ return cond[k].forEach((c) => replaceOnConditions(c));
835
+ }
836
+ return replaceOnConditions(cond[k]);
837
+ }
838
+ const [a] = this.helper.splitField(k);
839
+ const join = joins.find(j => j.alias === a);
840
+ if (join) {
841
+ join.cond = { ...join.cond, [k]: cond[k] };
842
+ }
843
+ });
844
+ };
845
+ if (this._populateWhere === core_1.PopulateHint.INFER) {
846
+ replaceOnConditions(this._cond);
847
+ }
848
+ else if (typeof this._populateWhere === 'object') {
849
+ const cond = CriteriaNodeFactory_1.CriteriaNodeFactory
850
+ .createNode(this.metadata, this.mainAlias.entityName, this._populateWhere)
851
+ .process(this);
852
+ replaceOnConditions(cond);
853
+ }
854
+ }
784
855
  hasToManyJoins() {
785
856
  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);
857
+ return [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(join.prop.kind);
787
858
  });
788
859
  }
789
860
  wrapPaginateSubQuery(meta) {
790
861
  const pks = this.prepareFields(meta.primaryKeys, 'sub-query');
791
862
  const subQuery = this.clone().select(pks).groupBy(pks).limit(this._limit);
863
+ // revert the on conditions added via populateWhere, we want to apply those only once
864
+ Object.values(subQuery._joins).forEach(join => join.cond = join.cond_ ?? {});
792
865
  if (this._offset) {
793
866
  subQuery.offset(this._offset);
794
867
  }
868
+ const addToSelect = [];
795
869
  if (this._orderBy.length > 0) {
796
870
  const orderBy = [];
797
871
  for (const orderMap of this._orderBy) {
@@ -799,28 +873,37 @@ class QueryBuilder {
799
873
  const [a, f] = this.helper.splitField(field);
800
874
  const prop = this.helper.getProperty(f, a);
801
875
  const type = this.platform.castColumn(prop);
802
- orderBy.push({
803
- [`min(${this.ref(this.helper.mapper(field, this.type, undefined, null))}${type})`]: direction,
804
- });
876
+ const fieldName = this.helper.mapper(field, this.type, undefined, null);
877
+ if (!prop?.persist && !prop?.formula) {
878
+ addToSelect.push(fieldName);
879
+ }
880
+ orderBy.push({ [`min(${this.ref(fieldName)}${type})`]: direction });
805
881
  }
806
882
  }
807
883
  subQuery.orderBy(orderBy);
808
884
  }
809
885
  subQuery.finalized = true;
810
886
  const knexQuery = subQuery.as(this.mainAlias.aliasName).clearSelect().select(pks);
887
+ if (addToSelect.length > 0) {
888
+ addToSelect.forEach(prop => {
889
+ const field = this._fields.find(field => {
890
+ if (typeof field === 'object' && field && '__as' in field) {
891
+ return field.__as === prop;
892
+ }
893
+ // not perfect, but should work most of the time, ideally we should check only the alias (`... as alias`)
894
+ return field.toString().includes(prop);
895
+ });
896
+ if (field) {
897
+ knexQuery.select(field);
898
+ }
899
+ });
900
+ }
811
901
  // multiple sub-queries are needed to get around mysql limitations with order by + limit + where in + group by (o.O)
812
902
  // https://stackoverflow.com/questions/17892762/mysql-this-version-of-mysql-doesnt-yet-support-limit-in-all-any-some-subqu
813
903
  const subSubQuery = this.getKnex().select(pks).from(knexQuery);
814
904
  this._limit = undefined;
815
905
  this._offset = undefined;
816
- const cond = this._cond;
817
906
  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
907
  }
825
908
  wrapModifySubQuery(meta) {
826
909
  const subQuery = this.clone();
@@ -864,11 +947,11 @@ class QueryBuilder {
864
947
  fromSubQuery(target, aliasName) {
865
948
  const subQuery = target.getKnexQuery();
866
949
  const { entityName } = target.mainAlias;
867
- aliasName ?? (aliasName = this.getNextAlias(entityName));
950
+ aliasName ??= this.getNextAlias(entityName);
868
951
  this.createMainAlias(entityName, aliasName, subQuery);
869
952
  }
870
953
  fromEntityName(entityName, aliasName) {
871
- aliasName ?? (aliasName = this._mainAlias?.aliasName ?? this.getNextAlias(entityName));
954
+ aliasName ??= this._mainAlias?.aliasName ?? this.getNextAlias(entityName);
872
955
  this.createMainAlias(entityName, aliasName);
873
956
  }
874
957
  createQueryBuilderHelper() {
@@ -885,5 +968,27 @@ class QueryBuilder {
885
968
  throw new Error('This QueryBuilder instance is already finalized, clone it first if you want to modify it.');
886
969
  }
887
970
  }
971
+ /* istanbul ignore next */
972
+ [util_1.inspect.custom](depth) {
973
+ const object = { ...this };
974
+ const hidden = ['metadata', 'driver', 'context', 'platform', 'knex', 'type'];
975
+ Object.keys(object).filter(k => k.startsWith('_')).forEach(k => delete object[k]);
976
+ Object.keys(object).filter(k => object[k] == null).forEach(k => delete object[k]);
977
+ hidden.forEach(k => delete object[k]);
978
+ let prefix = this.type ? this.type.substring(0, 1) + this.type.toLowerCase().substring(1) : '';
979
+ if (this._data) {
980
+ object.data = this._data;
981
+ }
982
+ if (!core_1.Utils.isEmpty(this._cond)) {
983
+ object.where = this._cond;
984
+ }
985
+ if (this._onConflict?.[0]) {
986
+ prefix = 'Upsert';
987
+ object.onConflict = this._onConflict[0];
988
+ }
989
+ const name = this._mainAlias ? `${prefix}QueryBuilder<${this._mainAlias?.entityName}>` : 'QueryBuilder';
990
+ const ret = (0, util_1.inspect)(object, { depth });
991
+ return ret === '[Object]' ? `[${name}]` : name + ' ' + ret;
992
+ }
888
993
  }
889
994
  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';
@@ -48,8 +48,8 @@ export declare class QueryBuilderHelper {
48
48
  private getOperatorReplacement;
49
49
  getQueryOrder(type: QueryType, orderBy: FlatQueryOrderMap | FlatQueryOrderMap[], populate: Dictionary<string>): string;
50
50
  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];
51
+ finalize(type: QueryType, qb: Knex.QueryBuilder, meta?: EntityMetadata, data?: Dictionary): void;
52
+ splitField<T>(field: EntityKey<T>, greedyAlias?: boolean): [string, EntityKey<T>];
53
53
  getLockSQL(qb: Knex.QueryBuilder, lockMode: LockMode, lockTables?: string[]): void;
54
54
  updateVersionProperty(qb: Knex.QueryBuilder, data: Dictionary): void;
55
55
  static isCustomExpression(field: string, hasAlias?: boolean): boolean;