@mikro-orm/sql 7.0.2 → 7.0.3-dev.0

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 (86) hide show
  1. package/AbstractSqlConnection.d.ts +58 -94
  2. package/AbstractSqlConnection.js +238 -235
  3. package/AbstractSqlDriver.d.ts +155 -411
  4. package/AbstractSqlDriver.js +1937 -2061
  5. package/AbstractSqlPlatform.d.ts +73 -83
  6. package/AbstractSqlPlatform.js +158 -162
  7. package/PivotCollectionPersister.d.ts +15 -33
  8. package/PivotCollectionPersister.js +160 -158
  9. package/SqlEntityManager.d.ts +22 -67
  10. package/SqlEntityManager.js +38 -54
  11. package/SqlEntityRepository.d.ts +14 -14
  12. package/SqlEntityRepository.js +23 -23
  13. package/dialects/mssql/MsSqlNativeQueryBuilder.d.ts +12 -12
  14. package/dialects/mssql/MsSqlNativeQueryBuilder.js +194 -192
  15. package/dialects/mysql/BaseMySqlPlatform.d.ts +45 -64
  16. package/dialects/mysql/BaseMySqlPlatform.js +131 -134
  17. package/dialects/mysql/MySqlExceptionConverter.d.ts +6 -6
  18. package/dialects/mysql/MySqlExceptionConverter.js +77 -91
  19. package/dialects/mysql/MySqlNativeQueryBuilder.d.ts +3 -3
  20. package/dialects/mysql/MySqlNativeQueryBuilder.js +69 -66
  21. package/dialects/mysql/MySqlSchemaHelper.d.ts +39 -39
  22. package/dialects/mysql/MySqlSchemaHelper.js +319 -327
  23. package/dialects/oracledb/OracleDialect.d.ts +52 -81
  24. package/dialects/oracledb/OracleDialect.js +149 -155
  25. package/dialects/oracledb/OracleNativeQueryBuilder.d.ts +12 -12
  26. package/dialects/oracledb/OracleNativeQueryBuilder.js +236 -232
  27. package/dialects/postgresql/BasePostgreSqlPlatform.d.ts +105 -108
  28. package/dialects/postgresql/BasePostgreSqlPlatform.js +350 -351
  29. package/dialects/postgresql/FullTextType.d.ts +6 -10
  30. package/dialects/postgresql/FullTextType.js +51 -51
  31. package/dialects/postgresql/PostgreSqlExceptionConverter.d.ts +5 -5
  32. package/dialects/postgresql/PostgreSqlExceptionConverter.js +43 -55
  33. package/dialects/postgresql/PostgreSqlNativeQueryBuilder.d.ts +1 -1
  34. package/dialects/postgresql/PostgreSqlNativeQueryBuilder.js +4 -4
  35. package/dialects/postgresql/PostgreSqlSchemaHelper.d.ts +82 -102
  36. package/dialects/postgresql/PostgreSqlSchemaHelper.js +683 -711
  37. package/dialects/sqlite/BaseSqliteConnection.d.ts +5 -3
  38. package/dialects/sqlite/BaseSqliteConnection.js +19 -21
  39. package/dialects/sqlite/NodeSqliteDialect.d.ts +1 -1
  40. package/dialects/sqlite/NodeSqliteDialect.js +23 -23
  41. package/dialects/sqlite/SqliteDriver.d.ts +1 -1
  42. package/dialects/sqlite/SqliteDriver.js +3 -3
  43. package/dialects/sqlite/SqliteExceptionConverter.d.ts +6 -6
  44. package/dialects/sqlite/SqliteExceptionConverter.js +51 -67
  45. package/dialects/sqlite/SqliteNativeQueryBuilder.d.ts +2 -2
  46. package/dialects/sqlite/SqliteNativeQueryBuilder.js +7 -7
  47. package/dialects/sqlite/SqlitePlatform.d.ts +72 -63
  48. package/dialects/sqlite/SqlitePlatform.js +139 -139
  49. package/dialects/sqlite/SqliteSchemaHelper.d.ts +60 -70
  50. package/dialects/sqlite/SqliteSchemaHelper.js +520 -533
  51. package/package.json +2 -2
  52. package/plugin/index.d.ts +35 -42
  53. package/plugin/index.js +36 -43
  54. package/plugin/transformer.d.ts +94 -117
  55. package/plugin/transformer.js +881 -890
  56. package/query/ArrayCriteriaNode.d.ts +4 -4
  57. package/query/ArrayCriteriaNode.js +18 -18
  58. package/query/CriteriaNode.d.ts +25 -35
  59. package/query/CriteriaNode.js +123 -133
  60. package/query/CriteriaNodeFactory.d.ts +6 -49
  61. package/query/CriteriaNodeFactory.js +94 -97
  62. package/query/NativeQueryBuilder.d.ts +118 -118
  63. package/query/NativeQueryBuilder.js +480 -484
  64. package/query/ObjectCriteriaNode.d.ts +12 -12
  65. package/query/ObjectCriteriaNode.js +282 -298
  66. package/query/QueryBuilder.d.ts +904 -1546
  67. package/query/QueryBuilder.js +2145 -2270
  68. package/query/QueryBuilderHelper.d.ts +72 -153
  69. package/query/QueryBuilderHelper.js +1028 -1079
  70. package/query/ScalarCriteriaNode.d.ts +3 -3
  71. package/query/ScalarCriteriaNode.js +46 -53
  72. package/query/enums.d.ts +14 -14
  73. package/query/enums.js +14 -14
  74. package/query/raw.d.ts +6 -16
  75. package/query/raw.js +10 -10
  76. package/schema/DatabaseSchema.d.ts +50 -73
  77. package/schema/DatabaseSchema.js +307 -331
  78. package/schema/DatabaseTable.d.ts +73 -96
  79. package/schema/DatabaseTable.js +927 -1012
  80. package/schema/SchemaComparator.d.ts +54 -58
  81. package/schema/SchemaComparator.js +719 -745
  82. package/schema/SchemaHelper.d.ts +95 -109
  83. package/schema/SchemaHelper.js +659 -675
  84. package/schema/SqlSchemaGenerator.d.ts +58 -78
  85. package/schema/SqlSchemaGenerator.js +501 -535
  86. package/typings.d.ts +266 -380
@@ -4,16 +4,16 @@ import type { ICriteriaNodeProcessOptions, IQueryBuilder } from '../typings.js';
4
4
  * @internal
5
5
  */
6
6
  export declare class ObjectCriteriaNode<T extends object> extends CriteriaNode<T> {
7
- process(qb: IQueryBuilder<T>, options?: ICriteriaNodeProcessOptions): any;
8
- isStrict(): boolean;
9
- unwrap(): any;
10
- willAutoJoin(qb: IQueryBuilder<T>, alias?: string, options?: ICriteriaNodeProcessOptions): boolean;
11
- shouldInline(payload: any): boolean;
12
- private getChildKey;
13
- private inlineArrayChildPayload;
14
- private inlineChildPayload;
15
- private inlineCondition;
16
- private shouldAutoJoin;
17
- private autoJoin;
18
- private isPrefixed;
7
+ process(qb: IQueryBuilder<T>, options?: ICriteriaNodeProcessOptions): any;
8
+ isStrict(): boolean;
9
+ unwrap(): any;
10
+ willAutoJoin(qb: IQueryBuilder<T>, alias?: string, options?: ICriteriaNodeProcessOptions): boolean;
11
+ shouldInline(payload: any): boolean;
12
+ private getChildKey;
13
+ private inlineArrayChildPayload;
14
+ private inlineChildPayload;
15
+ private inlineCondition;
16
+ private shouldAutoJoin;
17
+ private autoJoin;
18
+ private isPrefixed;
19
19
  }
@@ -1,12 +1,4 @@
1
- import {
2
- ALIAS_REPLACEMENT,
3
- GroupOperator,
4
- QueryFlag,
5
- raw,
6
- RawQueryFragment,
7
- ReferenceKind,
8
- Utils,
9
- } from '@mikro-orm/core';
1
+ import { ALIAS_REPLACEMENT, GroupOperator, QueryFlag, raw, RawQueryFragment, ReferenceKind, Utils, } from '@mikro-orm/core';
10
2
  import { CriteriaNode } from './CriteriaNode.js';
11
3
  import { JoinType, QueryType } from './enums.js';
12
4
  const COLLECTION_OPERATORS = ['$some', '$none', '$every', '$size'];
@@ -14,311 +6,303 @@ const COLLECTION_OPERATORS = ['$some', '$none', '$every', '$size'];
14
6
  * @internal
15
7
  */
16
8
  export class ObjectCriteriaNode extends CriteriaNode {
17
- process(qb, options) {
18
- const matchPopulateJoins =
19
- options?.matchPopulateJoins ||
20
- (this.prop && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(this.prop.kind));
21
- const nestedAlias = qb.getAliasForJoinPath(this.getPath(options), { ...options, matchPopulateJoins });
22
- const ownerAlias = options?.alias || qb.alias;
23
- const keys = Utils.getObjectQueryKeys(this.payload);
24
- let alias = options?.alias;
25
- if (nestedAlias) {
26
- alias = nestedAlias;
27
- }
28
- if (this.shouldAutoJoin(qb, nestedAlias)) {
29
- if (keys.some(k => COLLECTION_OPERATORS.includes(k))) {
30
- if (![ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(this.prop.kind)) {
31
- // ignore collection operators when used on a non-relational property - this can happen when they get into
32
- // populateWhere via `infer` on m:n properties with select-in strategy
33
- if (this.parent?.parent) {
34
- // we validate only usage on top level
35
- return {};
36
- }
37
- throw new Error(
38
- `Collection operators can be used only inside a collection property context, but it was used for ${this.getPath()}.`,
39
- );
9
+ process(qb, options) {
10
+ const matchPopulateJoins = options?.matchPopulateJoins ||
11
+ (this.prop && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(this.prop.kind));
12
+ const nestedAlias = qb.getAliasForJoinPath(this.getPath(options), { ...options, matchPopulateJoins });
13
+ const ownerAlias = options?.alias || qb.alias;
14
+ const keys = Utils.getObjectQueryKeys(this.payload);
15
+ let alias = options?.alias;
16
+ if (nestedAlias) {
17
+ alias = nestedAlias;
40
18
  }
41
- const $and = [];
42
- const knownKey =
43
- [ReferenceKind.SCALAR, ReferenceKind.MANY_TO_ONE, ReferenceKind.EMBEDDED].includes(this.prop.kind) ||
44
- (this.prop.kind === ReferenceKind.ONE_TO_ONE && this.prop.owner);
45
- const parentMeta = this.metadata.find(this.parent.entityName);
46
- const primaryKeys = parentMeta.primaryKeys.map(pk => {
47
- return [QueryType.SELECT, QueryType.COUNT].includes(qb.type) ? `${knownKey ? alias : ownerAlias}.${pk}` : pk;
48
- });
49
- for (const key of keys) {
50
- if (typeof key !== 'string' || !COLLECTION_OPERATORS.includes(key)) {
51
- throw new Error('Mixing collection operators with other filters is not allowed.');
52
- }
53
- const payload = this.payload[key].unwrap();
54
- const qb2 = qb.clone(true, ['schema']);
55
- const joinAlias = qb2.getNextAlias(this.prop.targetMeta.class);
56
- const sub = qb2
57
- .from(parentMeta.class)
58
- // eslint-disable-next-line no-unexpected-multiline
59
- [key === '$size' ? 'leftJoin' : 'innerJoin'](this.key, joinAlias)
60
- .select(parentMeta.primaryKeys);
61
- if (key === '$size') {
62
- const sizeCondition = typeof payload === 'number' ? { $eq: payload } : payload;
63
- const pks = this.prop.referencedColumnNames;
64
- const countExpr = raw(
65
- `count(${pks.map(() => '??').join(', ')})`,
66
- pks.map(pk => `${joinAlias}.${pk}`),
67
- );
68
- sub.groupBy(parentMeta.primaryKeys);
69
- sub.having({
70
- $and: Object.keys(sizeCondition).map(op => ({ [countExpr]: { [op]: sizeCondition[op] } })),
71
- });
72
- } else if (key === '$every') {
73
- sub.where({ $not: { [this.key]: payload } });
74
- } else {
75
- sub.where({ [this.key]: payload });
76
- }
77
- const op = ['$size', '$some'].includes(key) ? '$in' : '$nin';
78
- $and.push({
79
- [Utils.getPrimaryKeyHash(primaryKeys)]: { [op]: sub.getNativeQuery().toRaw() },
80
- });
19
+ if (this.shouldAutoJoin(qb, nestedAlias)) {
20
+ if (keys.some(k => COLLECTION_OPERATORS.includes(k))) {
21
+ if (![ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(this.prop.kind)) {
22
+ // ignore collection operators when used on a non-relational property - this can happen when they get into
23
+ // populateWhere via `infer` on m:n properties with select-in strategy
24
+ if (this.parent?.parent) {
25
+ // we validate only usage on top level
26
+ return {};
27
+ }
28
+ throw new Error(`Collection operators can be used only inside a collection property context, but it was used for ${this.getPath()}.`);
29
+ }
30
+ const $and = [];
31
+ const knownKey = [ReferenceKind.SCALAR, ReferenceKind.MANY_TO_ONE, ReferenceKind.EMBEDDED].includes(this.prop.kind) ||
32
+ (this.prop.kind === ReferenceKind.ONE_TO_ONE && this.prop.owner);
33
+ const parentMeta = this.metadata.find(this.parent.entityName);
34
+ const primaryKeys = parentMeta.primaryKeys.map(pk => {
35
+ return [QueryType.SELECT, QueryType.COUNT].includes(qb.type) ? `${knownKey ? alias : ownerAlias}.${pk}` : pk;
36
+ });
37
+ for (const key of keys) {
38
+ if (typeof key !== 'string' || !COLLECTION_OPERATORS.includes(key)) {
39
+ throw new Error('Mixing collection operators with other filters is not allowed.');
40
+ }
41
+ const payload = this.payload[key].unwrap();
42
+ const qb2 = qb.clone(true, ['schema']);
43
+ const joinAlias = qb2.getNextAlias(this.prop.targetMeta.class);
44
+ const sub = qb2
45
+ .from(parentMeta.class)
46
+ // eslint-disable-next-line no-unexpected-multiline
47
+ [key === '$size' ? 'leftJoin' : 'innerJoin'](this.key, joinAlias)
48
+ .select(parentMeta.primaryKeys);
49
+ if (key === '$size') {
50
+ const sizeCondition = typeof payload === 'number' ? { $eq: payload } : payload;
51
+ const pks = this.prop.referencedColumnNames;
52
+ const countExpr = raw(`count(${pks.map(() => '??').join(', ')})`, pks.map(pk => `${joinAlias}.${pk}`));
53
+ sub.groupBy(parentMeta.primaryKeys);
54
+ sub.having({
55
+ $and: Object.keys(sizeCondition).map(op => ({ [countExpr]: { [op]: sizeCondition[op] } })),
56
+ });
57
+ }
58
+ else if (key === '$every') {
59
+ sub.where({ $not: { [this.key]: payload } });
60
+ }
61
+ else {
62
+ sub.where({ [this.key]: payload });
63
+ }
64
+ const op = ['$size', '$some'].includes(key) ? '$in' : '$nin';
65
+ $and.push({
66
+ [Utils.getPrimaryKeyHash(primaryKeys)]: { [op]: sub.getNativeQuery().toRaw() },
67
+ });
68
+ }
69
+ if ($and.length === 1) {
70
+ return $and[0];
71
+ }
72
+ return { $and };
73
+ }
74
+ alias = this.autoJoin(qb, ownerAlias, options);
81
75
  }
82
- if ($and.length === 1) {
83
- return $and[0];
76
+ if (this.prop && nestedAlias) {
77
+ const toOneProperty = [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(this.prop.kind);
78
+ // if the property is nullable and the filter is strict, we need to use left join, so we mimic the inner join behaviour
79
+ // with an exclusive condition on the join columns:
80
+ // - if the owning column is null, the row is missing, we don't apply the filter
81
+ // - if the target column is not null, the row is matched, we apply the filter
82
+ if (toOneProperty && this.prop.nullable && this.isStrict()) {
83
+ const key = this.prop.owner ? this.prop.name : this.prop.referencedPKs;
84
+ qb.andWhere({
85
+ $or: [
86
+ { [ownerAlias + '.' + key]: null },
87
+ { [nestedAlias + '.' + Utils.getPrimaryKeyHash(this.prop.referencedPKs)]: { $ne: null } },
88
+ ],
89
+ });
90
+ }
84
91
  }
85
- return { $and };
86
- }
87
- alias = this.autoJoin(qb, ownerAlias, options);
88
- }
89
- if (this.prop && nestedAlias) {
90
- const toOneProperty = [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(this.prop.kind);
91
- // if the property is nullable and the filter is strict, we need to use left join, so we mimic the inner join behaviour
92
- // with an exclusive condition on the join columns:
93
- // - if the owning column is null, the row is missing, we don't apply the filter
94
- // - if the target column is not null, the row is matched, we apply the filter
95
- if (toOneProperty && this.prop.nullable && this.isStrict()) {
96
- const key = this.prop.owner ? this.prop.name : this.prop.referencedPKs;
97
- qb.andWhere({
98
- $or: [
99
- { [ownerAlias + '.' + key]: null },
100
- { [nestedAlias + '.' + Utils.getPrimaryKeyHash(this.prop.referencedPKs)]: { $ne: null } },
101
- ],
102
- });
103
- }
92
+ return keys.reduce((o, field) => {
93
+ const childNode = this.payload[field];
94
+ const payload = childNode.process(qb, { ...options, alias: this.prop ? alias : ownerAlias });
95
+ const operator = Utils.isOperator(field);
96
+ const isRawField = RawQueryFragment.isKnownFragmentSymbol(field);
97
+ // we need to keep the prefixing for formulas otherwise we would lose aliasing context when nesting inside group operators
98
+ const virtual = childNode.prop?.persist === false && !childNode.prop?.formula && !!options?.type;
99
+ // if key is missing, we are inside group operator and we need to prefix with alias
100
+ const primaryKey = this.key && this.metadata.find(this.entityName)?.primaryKeys.includes(field);
101
+ const isToOne = childNode.prop && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(childNode.prop.kind);
102
+ if (childNode.shouldInline(payload)) {
103
+ const childAlias = qb.getAliasForJoinPath(childNode.getPath(), { preferNoBranch: isToOne, ...options });
104
+ const a = qb.helper.isTableNameAliasRequired(qb.type) ? alias : undefined;
105
+ this.inlineChildPayload(o, payload, field, a, childAlias);
106
+ }
107
+ else if (childNode.shouldRename(payload)) {
108
+ this.inlineCondition(childNode.renameFieldToPK(qb, alias), o, payload);
109
+ }
110
+ else if (isRawField) {
111
+ const rawField = RawQueryFragment.getKnownFragment(field);
112
+ o[raw(rawField.sql.replaceAll(ALIAS_REPLACEMENT, alias), rawField.params)] = payload;
113
+ }
114
+ else if (!childNode.validate && !childNode.prop && !field.includes('.') && !operator) {
115
+ // wrap unknown fields in raw() to prevent alias prefixing (e.g. raw SQL aliases in HAVING)
116
+ // use '??' placeholder to properly quote the identifier
117
+ o[raw('??', [field])] = payload;
118
+ }
119
+ else if (primaryKey ||
120
+ virtual ||
121
+ operator ||
122
+ field.includes('.') ||
123
+ ![QueryType.SELECT, QueryType.COUNT].includes(qb.type)) {
124
+ this.inlineCondition(field.replaceAll(ALIAS_REPLACEMENT, alias), o, payload);
125
+ }
126
+ else {
127
+ this.inlineCondition(`${alias ?? qb.alias}.${field}`, o, payload);
128
+ }
129
+ return o;
130
+ }, {});
104
131
  }
105
- return keys.reduce((o, field) => {
106
- const childNode = this.payload[field];
107
- const payload = childNode.process(qb, { ...options, alias: this.prop ? alias : ownerAlias });
108
- const operator = Utils.isOperator(field);
109
- const isRawField = RawQueryFragment.isKnownFragmentSymbol(field);
110
- // we need to keep the prefixing for formulas otherwise we would lose aliasing context when nesting inside group operators
111
- const virtual = childNode.prop?.persist === false && !childNode.prop?.formula && !!options?.type;
112
- // if key is missing, we are inside group operator and we need to prefix with alias
113
- const primaryKey = this.key && this.metadata.find(this.entityName)?.primaryKeys.includes(field);
114
- const isToOne =
115
- childNode.prop && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(childNode.prop.kind);
116
- if (childNode.shouldInline(payload)) {
117
- const childAlias = qb.getAliasForJoinPath(childNode.getPath(), { preferNoBranch: isToOne, ...options });
118
- const a = qb.helper.isTableNameAliasRequired(qb.type) ? alias : undefined;
119
- this.inlineChildPayload(o, payload, field, a, childAlias);
120
- } else if (childNode.shouldRename(payload)) {
121
- this.inlineCondition(childNode.renameFieldToPK(qb, alias), o, payload);
122
- } else if (isRawField) {
123
- const rawField = RawQueryFragment.getKnownFragment(field);
124
- o[raw(rawField.sql.replaceAll(ALIAS_REPLACEMENT, alias), rawField.params)] = payload;
125
- } else if (!childNode.validate && !childNode.prop && !field.includes('.') && !operator) {
126
- // wrap unknown fields in raw() to prevent alias prefixing (e.g. raw SQL aliases in HAVING)
127
- // use '??' placeholder to properly quote the identifier
128
- o[raw('??', [field])] = payload;
129
- } else if (
130
- primaryKey ||
131
- virtual ||
132
- operator ||
133
- field.includes('.') ||
134
- ![QueryType.SELECT, QueryType.COUNT].includes(qb.type)
135
- ) {
136
- this.inlineCondition(field.replaceAll(ALIAS_REPLACEMENT, alias), o, payload);
137
- } else {
138
- this.inlineCondition(`${alias ?? qb.alias}.${field}`, o, payload);
139
- }
140
- return o;
141
- }, {});
142
- }
143
- isStrict() {
144
- return (
145
- this.strict ||
146
- Utils.getObjectQueryKeys(this.payload).some(key => {
147
- return this.payload[key].isStrict();
148
- })
149
- );
150
- }
151
- unwrap() {
152
- return Utils.getObjectQueryKeys(this.payload).reduce((o, field) => {
153
- o[field] = this.payload[field].unwrap();
154
- return o;
155
- }, {});
156
- }
157
- willAutoJoin(qb, alias, options) {
158
- const nestedAlias = qb.getAliasForJoinPath(this.getPath(options), options);
159
- const ownerAlias = alias || qb.alias;
160
- const keys = Utils.getObjectQueryKeys(this.payload);
161
- if (nestedAlias) {
162
- alias = nestedAlias;
132
+ isStrict() {
133
+ return (this.strict ||
134
+ Utils.getObjectQueryKeys(this.payload).some(key => {
135
+ return this.payload[key].isStrict();
136
+ }));
163
137
  }
164
- if (this.shouldAutoJoin(qb, nestedAlias)) {
165
- return !keys.some(k => COLLECTION_OPERATORS.includes(k));
138
+ unwrap() {
139
+ return Utils.getObjectQueryKeys(this.payload).reduce((o, field) => {
140
+ o[field] = this.payload[field].unwrap();
141
+ return o;
142
+ }, {});
166
143
  }
167
- return keys.some(field => {
168
- const childNode = this.payload[field];
169
- return childNode.willAutoJoin(qb, this.prop ? alias : ownerAlias, options);
170
- });
171
- }
172
- shouldInline(payload) {
173
- const rawField = RawQueryFragment.isKnownFragmentSymbol(this.key);
174
- const scalar = Utils.isPrimaryKey(payload) || payload instanceof RegExp || payload instanceof Date || rawField;
175
- const operator =
176
- Utils.isObject(payload) &&
177
- Utils.getObjectQueryKeys(payload).every(k => {
178
- if (k === '$not' && Utils.isPlainObject(payload[k])) {
179
- // $not wrapping non-operator conditions (entity props) should be inlined
180
- return Utils.getObjectQueryKeys(payload[k]).every(ik => Utils.isOperator(ik, false));
144
+ willAutoJoin(qb, alias, options) {
145
+ const nestedAlias = qb.getAliasForJoinPath(this.getPath(options), options);
146
+ const ownerAlias = alias || qb.alias;
147
+ const keys = Utils.getObjectQueryKeys(this.payload);
148
+ if (nestedAlias) {
149
+ alias = nestedAlias;
181
150
  }
182
- return Utils.isOperator(k, false);
183
- });
184
- return !!this.prop && this.prop.kind !== ReferenceKind.SCALAR && !scalar && !operator;
185
- }
186
- getChildKey(k, prop, childAlias, alias) {
187
- const idx = prop.referencedPKs.indexOf(k);
188
- return idx !== -1 && !childAlias && ![ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind)
189
- ? this.aliased(prop.joinColumns[idx], alias)
190
- : k;
191
- }
192
- inlineArrayChildPayload(obj, payload, k, prop, childAlias, alias) {
193
- const key = this.getChildKey(k, prop, childAlias);
194
- const value = payload.map(child =>
195
- Utils.getObjectQueryKeys(child).reduce((inner, childKey) => {
196
- const key =
197
- RawQueryFragment.isKnownFragmentSymbol(childKey) || this.isPrefixed(childKey) || Utils.isOperator(childKey)
198
- ? childKey
199
- : this.aliased(childKey, childAlias);
200
- inner[key] = child[childKey];
201
- return inner;
202
- }, {}),
203
- );
204
- this.inlineCondition(key, obj, value);
205
- }
206
- inlineChildPayload(o, payload, field, alias, childAlias) {
207
- const prop = this.metadata.find(this.entityName).properties[field];
208
- for (const k of Utils.getObjectQueryKeys(payload)) {
209
- if (RawQueryFragment.isKnownFragmentSymbol(k)) {
210
- o[k] = payload[k];
211
- } else if (
212
- k === '$not' &&
213
- Utils.isPlainObject(payload[k]) &&
214
- Utils.getObjectQueryKeys(payload[k]).some(ik => !Utils.isOperator(ik, false))
215
- ) {
216
- // $not wraps entity conditions (from auto-join), inline at current level
217
- this.inlineCondition(k, o, payload[k]);
218
- } else if (Utils.isOperator(k, false)) {
219
- const tmp = payload[k];
220
- delete payload[k];
221
- o[this.aliased(field, alias)] = { [k]: tmp, ...o[this.aliased(field, alias)] };
222
- } else if (k in GroupOperator && Array.isArray(payload[k])) {
223
- this.inlineArrayChildPayload(o, payload[k], k, prop, childAlias, alias);
224
- } else if (this.isPrefixed(k) || Utils.isOperator(k) || !childAlias) {
225
- const key = this.getChildKey(k, prop, childAlias, alias);
226
- this.inlineCondition(key, o, payload[k]);
227
- } else {
228
- o[this.aliased(k, childAlias)] = payload[k];
229
- }
151
+ if (this.shouldAutoJoin(qb, nestedAlias)) {
152
+ return !keys.some(k => COLLECTION_OPERATORS.includes(k));
153
+ }
154
+ return keys.some(field => {
155
+ const childNode = this.payload[field];
156
+ return childNode.willAutoJoin(qb, this.prop ? alias : ownerAlias, options);
157
+ });
230
158
  }
231
- }
232
- inlineCondition(key, o, value) {
233
- if (!(key in o)) {
234
- o[key] = value;
235
- return;
159
+ shouldInline(payload) {
160
+ const rawField = RawQueryFragment.isKnownFragmentSymbol(this.key);
161
+ const scalar = Utils.isPrimaryKey(payload) ||
162
+ payload instanceof RegExp ||
163
+ payload instanceof Date ||
164
+ rawField;
165
+ const operator = Utils.isObject(payload) &&
166
+ Utils.getObjectQueryKeys(payload).every(k => {
167
+ if (k === '$not' && Utils.isPlainObject(payload[k])) {
168
+ // $not wrapping non-operator conditions (entity props) should be inlined
169
+ return Utils.getObjectQueryKeys(payload[k]).every(ik => Utils.isOperator(ik, false));
170
+ }
171
+ return Utils.isOperator(k, false);
172
+ });
173
+ return !!this.prop && this.prop.kind !== ReferenceKind.SCALAR && !scalar && !operator;
236
174
  }
237
- /* v8 ignore next */
238
- if (key === '$and') {
239
- o.$and.push({ [key]: value });
240
- return;
175
+ getChildKey(k, prop, childAlias, alias) {
176
+ const idx = prop.referencedPKs.indexOf(k);
177
+ return idx !== -1 && !childAlias && ![ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind)
178
+ ? this.aliased(prop.joinColumns[idx], alias)
179
+ : k;
241
180
  }
242
- const $and = o.$and ?? [];
243
- $and.push({ [key]: o[key] }, { [key]: value });
244
- delete o[key];
245
- o.$and = $and;
246
- }
247
- shouldAutoJoin(qb, nestedAlias) {
248
- if (!this.prop || !this.parent) {
249
- return false;
181
+ inlineArrayChildPayload(obj, payload, k, prop, childAlias, alias) {
182
+ const key = this.getChildKey(k, prop, childAlias);
183
+ const value = payload.map((child) => Utils.getObjectQueryKeys(child).reduce((inner, childKey) => {
184
+ const key = RawQueryFragment.isKnownFragmentSymbol(childKey) || this.isPrefixed(childKey) || Utils.isOperator(childKey)
185
+ ? childKey
186
+ : this.aliased(childKey, childAlias);
187
+ inner[key] = child[childKey];
188
+ return inner;
189
+ }, {}));
190
+ this.inlineCondition(key, obj, value);
250
191
  }
251
- const keys = Utils.getObjectQueryKeys(this.payload);
252
- if (keys.every(k => typeof k === 'string' && k.includes('.') && k.startsWith(`${qb.alias}.`))) {
253
- return false;
192
+ inlineChildPayload(o, payload, field, alias, childAlias) {
193
+ const prop = this.metadata.find(this.entityName).properties[field];
194
+ for (const k of Utils.getObjectQueryKeys(payload)) {
195
+ if (RawQueryFragment.isKnownFragmentSymbol(k)) {
196
+ o[k] = payload[k];
197
+ }
198
+ else if (k === '$not' &&
199
+ Utils.isPlainObject(payload[k]) &&
200
+ Utils.getObjectQueryKeys(payload[k]).some(ik => !Utils.isOperator(ik, false))) {
201
+ // $not wraps entity conditions (from auto-join), inline at current level
202
+ this.inlineCondition(k, o, payload[k]);
203
+ }
204
+ else if (Utils.isOperator(k, false)) {
205
+ const tmp = payload[k];
206
+ delete payload[k];
207
+ o[this.aliased(field, alias)] = { [k]: tmp, ...o[this.aliased(field, alias)] };
208
+ }
209
+ else if (k in GroupOperator && Array.isArray(payload[k])) {
210
+ this.inlineArrayChildPayload(o, payload[k], k, prop, childAlias, alias);
211
+ }
212
+ else if (this.isPrefixed(k) || Utils.isOperator(k) || !childAlias) {
213
+ const key = this.getChildKey(k, prop, childAlias, alias);
214
+ this.inlineCondition(key, o, payload[k]);
215
+ }
216
+ else {
217
+ o[this.aliased(k, childAlias)] = payload[k];
218
+ }
219
+ }
254
220
  }
255
- if (keys.some(k => COLLECTION_OPERATORS.includes(k))) {
256
- return true;
221
+ inlineCondition(key, o, value) {
222
+ if (!(key in o)) {
223
+ o[key] = value;
224
+ return;
225
+ }
226
+ /* v8 ignore next */
227
+ if (key === '$and') {
228
+ o.$and.push({ [key]: value });
229
+ return;
230
+ }
231
+ const $and = o.$and ?? [];
232
+ $and.push({ [key]: o[key] }, { [key]: value });
233
+ delete o[key];
234
+ o.$and = $and;
235
+ }
236
+ shouldAutoJoin(qb, nestedAlias) {
237
+ if (!this.prop || !this.parent) {
238
+ return false;
239
+ }
240
+ const keys = Utils.getObjectQueryKeys(this.payload);
241
+ if (keys.every(k => typeof k === 'string' && k.includes('.') && k.startsWith(`${qb.alias}.`))) {
242
+ return false;
243
+ }
244
+ if (keys.some(k => COLLECTION_OPERATORS.includes(k))) {
245
+ return true;
246
+ }
247
+ const meta = this.metadata.find(this.entityName);
248
+ const embeddable = this.prop.kind === ReferenceKind.EMBEDDED;
249
+ const knownKey = [ReferenceKind.SCALAR, ReferenceKind.MANY_TO_ONE, ReferenceKind.EMBEDDED].includes(this.prop.kind) ||
250
+ (this.prop.kind === ReferenceKind.ONE_TO_ONE && this.prop.owner);
251
+ const operatorKeys = knownKey &&
252
+ keys.every(key => {
253
+ if (key === '$not') {
254
+ // $not wraps conditions like $and/$or, check if it wraps entity property conditions (needs auto-join)
255
+ // vs simple operator conditions on the FK (doesn't need auto-join)
256
+ const childPayload = this.payload[key].payload;
257
+ if (Utils.isPlainObject(childPayload)) {
258
+ return Utils.getObjectQueryKeys(childPayload).every(k => Utils.isOperator(k, false));
259
+ }
260
+ }
261
+ return Utils.isOperator(key, false);
262
+ });
263
+ const primaryKeys = knownKey &&
264
+ keys.every(key => {
265
+ if (typeof key !== 'string' || !meta.primaryKeys.includes(key)) {
266
+ return false;
267
+ }
268
+ if (!Utils.isPlainObject(this.payload[key].payload) ||
269
+ ![ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(meta.properties[key].kind)) {
270
+ return true;
271
+ }
272
+ return Utils.getObjectQueryKeys(this.payload[key].payload).every(k => typeof k === 'string' && meta.properties[key].targetMeta.primaryKeys.includes(k));
273
+ });
274
+ return !primaryKeys && !nestedAlias && !operatorKeys && !embeddable;
257
275
  }
258
- const meta = this.metadata.find(this.entityName);
259
- const embeddable = this.prop.kind === ReferenceKind.EMBEDDED;
260
- const knownKey =
261
- [ReferenceKind.SCALAR, ReferenceKind.MANY_TO_ONE, ReferenceKind.EMBEDDED].includes(this.prop.kind) ||
262
- (this.prop.kind === ReferenceKind.ONE_TO_ONE && this.prop.owner);
263
- const operatorKeys =
264
- knownKey &&
265
- keys.every(key => {
266
- if (key === '$not') {
267
- // $not wraps conditions like $and/$or, check if it wraps entity property conditions (needs auto-join)
268
- // vs simple operator conditions on the FK (doesn't need auto-join)
269
- const childPayload = this.payload[key].payload;
270
- if (Utils.isPlainObject(childPayload)) {
271
- return Utils.getObjectQueryKeys(childPayload).every(k => Utils.isOperator(k, false));
272
- }
276
+ autoJoin(qb, alias, options) {
277
+ const nestedAlias = qb.getNextAlias(this.prop?.pivotEntity ?? this.entityName);
278
+ const rawField = RawQueryFragment.isKnownFragmentSymbol(this.key);
279
+ const scalar = Utils.isPrimaryKey(this.payload) ||
280
+ this.payload instanceof RegExp ||
281
+ this.payload instanceof Date ||
282
+ rawField;
283
+ const operator = Utils.isPlainObject(this.payload) &&
284
+ Utils.getObjectQueryKeys(this.payload).every(k => Utils.isOperator(k, false));
285
+ const field = `${alias}.${this.prop.name}`;
286
+ const method = qb.hasFlag(QueryFlag.INFER_POPULATE) ? 'joinAndSelect' : 'join';
287
+ const path = this.getPath();
288
+ if (this.prop.kind === ReferenceKind.MANY_TO_MANY && (scalar || operator)) {
289
+ qb.join(field, nestedAlias, undefined, JoinType.pivotJoin, path);
273
290
  }
274
- return Utils.isOperator(key, false);
275
- });
276
- const primaryKeys =
277
- knownKey &&
278
- keys.every(key => {
279
- if (typeof key !== 'string' || !meta.primaryKeys.includes(key)) {
280
- return false;
291
+ else {
292
+ const prev = qb.state.fields?.slice();
293
+ const toOneProperty = [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(this.prop.kind);
294
+ const joinType = toOneProperty && !this.prop.nullable ? JoinType.innerJoin : JoinType.leftJoin;
295
+ qb[method](field, nestedAlias, undefined, joinType, path);
296
+ if (!qb.hasFlag(QueryFlag.INFER_POPULATE)) {
297
+ qb.state.fields = prev;
298
+ }
281
299
  }
282
- if (
283
- !Utils.isPlainObject(this.payload[key].payload) ||
284
- ![ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(meta.properties[key].kind)
285
- ) {
286
- return true;
300
+ if (options?.type !== 'orderBy') {
301
+ qb.scheduleFilterCheck(path);
287
302
  }
288
- return Utils.getObjectQueryKeys(this.payload[key].payload).every(
289
- k => typeof k === 'string' && meta.properties[key].targetMeta.primaryKeys.includes(k),
290
- );
291
- });
292
- return !primaryKeys && !nestedAlias && !operatorKeys && !embeddable;
293
- }
294
- autoJoin(qb, alias, options) {
295
- const nestedAlias = qb.getNextAlias(this.prop?.pivotEntity ?? this.entityName);
296
- const rawField = RawQueryFragment.isKnownFragmentSymbol(this.key);
297
- const scalar =
298
- Utils.isPrimaryKey(this.payload) || this.payload instanceof RegExp || this.payload instanceof Date || rawField;
299
- const operator =
300
- Utils.isPlainObject(this.payload) &&
301
- Utils.getObjectQueryKeys(this.payload).every(k => Utils.isOperator(k, false));
302
- const field = `${alias}.${this.prop.name}`;
303
- const method = qb.hasFlag(QueryFlag.INFER_POPULATE) ? 'joinAndSelect' : 'join';
304
- const path = this.getPath();
305
- if (this.prop.kind === ReferenceKind.MANY_TO_MANY && (scalar || operator)) {
306
- qb.join(field, nestedAlias, undefined, JoinType.pivotJoin, path);
307
- } else {
308
- const prev = qb.state.fields?.slice();
309
- const toOneProperty = [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(this.prop.kind);
310
- const joinType = toOneProperty && !this.prop.nullable ? JoinType.innerJoin : JoinType.leftJoin;
311
- qb[method](field, nestedAlias, undefined, joinType, path);
312
- if (!qb.hasFlag(QueryFlag.INFER_POPULATE)) {
313
- qb.state.fields = prev;
314
- }
303
+ return nestedAlias;
315
304
  }
316
- if (options?.type !== 'orderBy') {
317
- qb.scheduleFilterCheck(path);
305
+ isPrefixed(field) {
306
+ return !!/\w+\./.exec(field);
318
307
  }
319
- return nestedAlias;
320
- }
321
- isPrefixed(field) {
322
- return !!/\w+\./.exec(field);
323
- }
324
308
  }