@mikro-orm/sql 7.0.4-dev.9 → 7.0.4

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 (87) hide show
  1. package/AbstractSqlConnection.d.ts +94 -58
  2. package/AbstractSqlConnection.js +235 -238
  3. package/AbstractSqlDriver.d.ts +410 -155
  4. package/AbstractSqlDriver.js +2064 -1941
  5. package/AbstractSqlPlatform.d.ts +83 -73
  6. package/AbstractSqlPlatform.js +162 -158
  7. package/PivotCollectionPersister.d.ts +33 -15
  8. package/PivotCollectionPersister.js +158 -160
  9. package/README.md +1 -1
  10. package/SqlEntityManager.d.ts +67 -22
  11. package/SqlEntityManager.js +54 -38
  12. package/SqlEntityRepository.d.ts +14 -14
  13. package/SqlEntityRepository.js +23 -23
  14. package/dialects/mssql/MsSqlNativeQueryBuilder.d.ts +12 -12
  15. package/dialects/mssql/MsSqlNativeQueryBuilder.js +192 -194
  16. package/dialects/mysql/BaseMySqlPlatform.d.ts +64 -45
  17. package/dialects/mysql/BaseMySqlPlatform.js +134 -131
  18. package/dialects/mysql/MySqlExceptionConverter.d.ts +6 -6
  19. package/dialects/mysql/MySqlExceptionConverter.js +91 -77
  20. package/dialects/mysql/MySqlNativeQueryBuilder.d.ts +3 -3
  21. package/dialects/mysql/MySqlNativeQueryBuilder.js +66 -69
  22. package/dialects/mysql/MySqlSchemaHelper.d.ts +39 -39
  23. package/dialects/mysql/MySqlSchemaHelper.js +327 -319
  24. package/dialects/oracledb/OracleDialect.d.ts +81 -52
  25. package/dialects/oracledb/OracleDialect.js +155 -149
  26. package/dialects/oracledb/OracleNativeQueryBuilder.d.ts +12 -12
  27. package/dialects/oracledb/OracleNativeQueryBuilder.js +232 -236
  28. package/dialects/postgresql/BasePostgreSqlPlatform.d.ts +109 -105
  29. package/dialects/postgresql/BasePostgreSqlPlatform.js +354 -350
  30. package/dialects/postgresql/FullTextType.d.ts +10 -6
  31. package/dialects/postgresql/FullTextType.js +51 -51
  32. package/dialects/postgresql/PostgreSqlExceptionConverter.d.ts +5 -5
  33. package/dialects/postgresql/PostgreSqlExceptionConverter.js +55 -43
  34. package/dialects/postgresql/PostgreSqlNativeQueryBuilder.d.ts +1 -1
  35. package/dialects/postgresql/PostgreSqlNativeQueryBuilder.js +4 -4
  36. package/dialects/postgresql/PostgreSqlSchemaHelper.d.ts +102 -82
  37. package/dialects/postgresql/PostgreSqlSchemaHelper.js +733 -705
  38. package/dialects/sqlite/BaseSqliteConnection.d.ts +3 -5
  39. package/dialects/sqlite/BaseSqliteConnection.js +21 -19
  40. package/dialects/sqlite/NodeSqliteDialect.d.ts +1 -1
  41. package/dialects/sqlite/NodeSqliteDialect.js +23 -23
  42. package/dialects/sqlite/SqliteDriver.d.ts +1 -1
  43. package/dialects/sqlite/SqliteDriver.js +3 -3
  44. package/dialects/sqlite/SqliteExceptionConverter.d.ts +6 -6
  45. package/dialects/sqlite/SqliteExceptionConverter.js +67 -51
  46. package/dialects/sqlite/SqliteNativeQueryBuilder.d.ts +2 -2
  47. package/dialects/sqlite/SqliteNativeQueryBuilder.js +7 -7
  48. package/dialects/sqlite/SqlitePlatform.d.ts +63 -72
  49. package/dialects/sqlite/SqlitePlatform.js +139 -139
  50. package/dialects/sqlite/SqliteSchemaHelper.d.ts +70 -60
  51. package/dialects/sqlite/SqliteSchemaHelper.js +533 -520
  52. package/package.json +3 -3
  53. package/plugin/index.d.ts +42 -35
  54. package/plugin/index.js +43 -36
  55. package/plugin/transformer.d.ts +117 -94
  56. package/plugin/transformer.js +890 -881
  57. package/query/ArrayCriteriaNode.d.ts +4 -4
  58. package/query/ArrayCriteriaNode.js +18 -18
  59. package/query/CriteriaNode.d.ts +35 -25
  60. package/query/CriteriaNode.js +133 -123
  61. package/query/CriteriaNodeFactory.d.ts +49 -6
  62. package/query/CriteriaNodeFactory.js +97 -94
  63. package/query/NativeQueryBuilder.d.ts +118 -118
  64. package/query/NativeQueryBuilder.js +484 -480
  65. package/query/ObjectCriteriaNode.d.ts +12 -12
  66. package/query/ObjectCriteriaNode.js +298 -282
  67. package/query/QueryBuilder.d.ts +1557 -905
  68. package/query/QueryBuilder.js +2322 -2192
  69. package/query/QueryBuilderHelper.d.ts +153 -72
  70. package/query/QueryBuilderHelper.js +1079 -1028
  71. package/query/ScalarCriteriaNode.d.ts +3 -3
  72. package/query/ScalarCriteriaNode.js +53 -46
  73. package/query/enums.d.ts +14 -14
  74. package/query/enums.js +14 -14
  75. package/query/raw.d.ts +16 -6
  76. package/query/raw.js +10 -10
  77. package/schema/DatabaseSchema.d.ts +73 -50
  78. package/schema/DatabaseSchema.js +331 -307
  79. package/schema/DatabaseTable.d.ts +96 -73
  80. package/schema/DatabaseTable.js +1012 -927
  81. package/schema/SchemaComparator.d.ts +70 -66
  82. package/schema/SchemaComparator.js +766 -740
  83. package/schema/SchemaHelper.d.ts +109 -95
  84. package/schema/SchemaHelper.js +675 -659
  85. package/schema/SqlSchemaGenerator.d.ts +78 -58
  86. package/schema/SqlSchemaGenerator.js +535 -501
  87. package/typings.d.ts +380 -266
@@ -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,4 +1,12 @@
1
- import { ALIAS_REPLACEMENT, GroupOperator, QueryFlag, raw, RawQueryFragment, ReferenceKind, Utils, } from '@mikro-orm/core';
1
+ import {
2
+ ALIAS_REPLACEMENT,
3
+ GroupOperator,
4
+ QueryFlag,
5
+ raw,
6
+ RawQueryFragment,
7
+ ReferenceKind,
8
+ Utils,
9
+ } from '@mikro-orm/core';
2
10
  import { CriteriaNode } from './CriteriaNode.js';
3
11
  import { JoinType, QueryType } from './enums.js';
4
12
  const COLLECTION_OPERATORS = ['$some', '$none', '$every', '$size'];
@@ -6,303 +14,311 @@ const COLLECTION_OPERATORS = ['$some', '$none', '$every', '$size'];
6
14
  * @internal
7
15
  */
8
16
  export class ObjectCriteriaNode extends CriteriaNode {
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;
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
+ );
18
40
  }
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);
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
+ });
75
81
  }
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
- }
82
+ if ($and.length === 1) {
83
+ return $and[0];
91
84
  }
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
- }, {});
85
+ return { $and };
86
+ }
87
+ alias = this.autoJoin(qb, ownerAlias, options);
131
88
  }
132
- isStrict() {
133
- return (this.strict ||
134
- Utils.getObjectQueryKeys(this.payload).some(key => {
135
- return this.payload[key].isStrict();
136
- }));
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
+ }
137
104
  }
138
- unwrap() {
139
- return Utils.getObjectQueryKeys(this.payload).reduce((o, field) => {
140
- o[field] = this.payload[field].unwrap();
141
- return o;
142
- }, {});
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;
143
163
  }
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;
150
- }
151
- if (this.shouldAutoJoin(qb, nestedAlias)) {
152
- return !keys.some(k => COLLECTION_OPERATORS.includes(k));
164
+ if (this.shouldAutoJoin(qb, nestedAlias)) {
165
+ return !keys.some(k => COLLECTION_OPERATORS.includes(k));
166
+ }
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));
153
181
  }
154
- return keys.some(field => {
155
- const childNode = this.payload[field];
156
- return childNode.willAutoJoin(qb, this.prop ? alias : ownerAlias, options);
157
- });
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
+ }
158
230
  }
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;
231
+ }
232
+ inlineCondition(key, o, value) {
233
+ if (!(key in o)) {
234
+ o[key] = value;
235
+ return;
174
236
  }
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;
237
+ /* v8 ignore next */
238
+ if (key === '$and') {
239
+ o.$and.push({ [key]: value });
240
+ return;
180
241
  }
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);
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;
191
250
  }
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
- }
251
+ const keys = Utils.getObjectQueryKeys(this.payload);
252
+ if (keys.every(k => typeof k === 'string' && k.includes('.') && k.startsWith(`${qb.alias}.`))) {
253
+ return false;
220
254
  }
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;
255
+ if (keys.some(k => COLLECTION_OPERATORS.includes(k))) {
256
+ return true;
275
257
  }
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);
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
+ }
290
273
  }
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
- }
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;
299
281
  }
300
- if (options?.type !== 'orderBy') {
301
- qb.scheduleFilterCheck(path);
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;
302
287
  }
303
- return nestedAlias;
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
+ }
304
315
  }
305
- isPrefixed(field) {
306
- return !!/\w+\./.exec(field);
316
+ if (options?.type !== 'orderBy') {
317
+ qb.scheduleFilterCheck(path);
307
318
  }
319
+ return nestedAlias;
320
+ }
321
+ isPrefixed(field) {
322
+ return !!/\w+\./.exec(field);
323
+ }
308
324
  }