@mikro-orm/sql 7.0.0-dev.99 → 7.0.0-rc.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 (64) hide show
  1. package/AbstractSqlConnection.d.ts +2 -4
  2. package/AbstractSqlConnection.js +3 -7
  3. package/AbstractSqlDriver.d.ts +82 -23
  4. package/AbstractSqlDriver.js +584 -184
  5. package/AbstractSqlPlatform.d.ts +3 -4
  6. package/AbstractSqlPlatform.js +0 -4
  7. package/PivotCollectionPersister.d.ts +5 -0
  8. package/PivotCollectionPersister.js +30 -12
  9. package/SqlEntityManager.d.ts +2 -2
  10. package/dialects/mysql/{MySqlPlatform.d.ts → BaseMySqlPlatform.d.ts} +3 -2
  11. package/dialects/mysql/{MySqlPlatform.js → BaseMySqlPlatform.js} +5 -1
  12. package/dialects/mysql/MySqlSchemaHelper.d.ts +12 -1
  13. package/dialects/mysql/MySqlSchemaHelper.js +97 -6
  14. package/dialects/mysql/index.d.ts +1 -2
  15. package/dialects/mysql/index.js +1 -2
  16. package/dialects/postgresql/BasePostgreSqlPlatform.d.ts +106 -0
  17. package/dialects/postgresql/BasePostgreSqlPlatform.js +350 -0
  18. package/dialects/postgresql/FullTextType.d.ts +14 -0
  19. package/dialects/postgresql/FullTextType.js +59 -0
  20. package/dialects/postgresql/PostgreSqlExceptionConverter.d.ts +8 -0
  21. package/dialects/postgresql/PostgreSqlExceptionConverter.js +47 -0
  22. package/dialects/postgresql/PostgreSqlSchemaHelper.d.ts +90 -0
  23. package/dialects/postgresql/PostgreSqlSchemaHelper.js +732 -0
  24. package/dialects/postgresql/index.d.ts +3 -0
  25. package/dialects/postgresql/index.js +3 -0
  26. package/dialects/sqlite/BaseSqliteConnection.d.ts +1 -0
  27. package/dialects/sqlite/BaseSqliteConnection.js +13 -0
  28. package/dialects/sqlite/BaseSqlitePlatform.d.ts +6 -0
  29. package/dialects/sqlite/BaseSqlitePlatform.js +12 -0
  30. package/dialects/sqlite/SqliteSchemaHelper.d.ts +25 -0
  31. package/dialects/sqlite/SqliteSchemaHelper.js +145 -19
  32. package/dialects/sqlite/index.d.ts +0 -1
  33. package/dialects/sqlite/index.js +0 -1
  34. package/package.json +5 -6
  35. package/plugin/transformer.d.ts +1 -1
  36. package/plugin/transformer.js +1 -1
  37. package/query/CriteriaNode.d.ts +9 -5
  38. package/query/CriteriaNode.js +16 -15
  39. package/query/CriteriaNodeFactory.d.ts +6 -6
  40. package/query/CriteriaNodeFactory.js +33 -31
  41. package/query/NativeQueryBuilder.d.ts +3 -2
  42. package/query/NativeQueryBuilder.js +1 -2
  43. package/query/ObjectCriteriaNode.js +50 -35
  44. package/query/QueryBuilder.d.ts +548 -79
  45. package/query/QueryBuilder.js +537 -159
  46. package/query/QueryBuilderHelper.d.ts +22 -14
  47. package/query/QueryBuilderHelper.js +158 -69
  48. package/query/ScalarCriteriaNode.js +2 -2
  49. package/query/raw.d.ts +11 -3
  50. package/query/raw.js +1 -2
  51. package/schema/DatabaseSchema.d.ts +15 -2
  52. package/schema/DatabaseSchema.js +143 -15
  53. package/schema/DatabaseTable.d.ts +12 -0
  54. package/schema/DatabaseTable.js +91 -31
  55. package/schema/SchemaComparator.d.ts +8 -0
  56. package/schema/SchemaComparator.js +126 -3
  57. package/schema/SchemaHelper.d.ts +26 -3
  58. package/schema/SchemaHelper.js +98 -11
  59. package/schema/SqlSchemaGenerator.d.ts +10 -0
  60. package/schema/SqlSchemaGenerator.js +137 -9
  61. package/tsconfig.build.tsbuildinfo +1 -0
  62. package/typings.d.ts +74 -36
  63. package/dialects/postgresql/PostgreSqlTableCompiler.d.ts +0 -1
  64. package/dialects/postgresql/PostgreSqlTableCompiler.js +0 -1
@@ -6,26 +6,26 @@ import { ScalarCriteriaNode } from './ScalarCriteriaNode.js';
6
6
  * @internal
7
7
  */
8
8
  export class CriteriaNodeFactory {
9
- static createNode(metadata, entityName, payload, parent, key) {
10
- const customExpression = RawQueryFragment.isKnownFragment(key || '');
11
- const scalar = Utils.isPrimaryKey(payload) || isRaw(payload) || payload instanceof RegExp || payload instanceof Date || customExpression;
9
+ static createNode(metadata, entityName, payload, parent, key, validate = true) {
10
+ const rawField = RawQueryFragment.isKnownFragmentSymbol(key);
11
+ const scalar = Utils.isPrimaryKey(payload) || isRaw(payload) || payload instanceof RegExp || payload instanceof Date || rawField;
12
12
  if (Array.isArray(payload) && !scalar) {
13
- return this.createArrayNode(metadata, entityName, payload, parent, key);
13
+ return this.createArrayNode(metadata, entityName, payload, parent, key, validate);
14
14
  }
15
15
  if (Utils.isPlainObject(payload) && !scalar) {
16
- return this.createObjectNode(metadata, entityName, payload, parent, key);
16
+ return this.createObjectNode(metadata, entityName, payload, parent, key, validate);
17
17
  }
18
- return this.createScalarNode(metadata, entityName, payload, parent, key);
18
+ return this.createScalarNode(metadata, entityName, payload, parent, key, validate);
19
19
  }
20
- static createScalarNode(metadata, entityName, payload, parent, key) {
21
- const node = new ScalarCriteriaNode(metadata, entityName, parent, key);
20
+ static createScalarNode(metadata, entityName, payload, parent, key, validate = true) {
21
+ const node = new ScalarCriteriaNode(metadata, entityName, parent, key, validate);
22
22
  node.payload = payload;
23
23
  return node;
24
24
  }
25
- static createArrayNode(metadata, entityName, payload, parent, key) {
26
- const node = new ArrayCriteriaNode(metadata, entityName, parent, key);
25
+ static createArrayNode(metadata, entityName, payload, parent, key, validate = true) {
26
+ const node = new ArrayCriteriaNode(metadata, entityName, parent, key, validate);
27
27
  node.payload = payload.map((item, index) => {
28
- const n = this.createNode(metadata, entityName, item, node);
28
+ const n = this.createNode(metadata, entityName, item, node, undefined, validate);
29
29
  // we care about branching only for $and
30
30
  if (key === '$and' && payload.length > 1) {
31
31
  n.index = index;
@@ -34,57 +34,59 @@ export class CriteriaNodeFactory {
34
34
  });
35
35
  return node;
36
36
  }
37
- static createObjectNode(metadata, entityName, payload, parent, key) {
37
+ static createObjectNode(metadata, entityName, payload, parent, key, validate = true) {
38
38
  const meta = metadata.find(entityName);
39
- const node = new ObjectCriteriaNode(metadata, entityName, parent, key, true, payload.__strict);
39
+ const node = new ObjectCriteriaNode(metadata, entityName, parent, key, validate, payload.__strict);
40
40
  node.payload = {};
41
- for (const key of Object.keys(payload)) {
42
- node.payload[key] = this.createObjectItemNode(metadata, entityName, node, payload, key, meta);
41
+ for (const k of Utils.getObjectQueryKeys(payload)) {
42
+ node.payload[k] = this.createObjectItemNode(metadata, entityName, node, payload, k, meta, validate);
43
43
  }
44
44
  return node;
45
45
  }
46
- static createObjectItemNode(metadata, entityName, node, payload, key, meta) {
47
- const prop = meta?.properties[key];
48
- const childEntity = prop && prop.kind !== ReferenceKind.SCALAR ? prop.type : entityName;
49
- const isNotEmbedded = prop?.kind !== ReferenceKind.EMBEDDED;
46
+ static createObjectItemNode(metadata, entityName, node, payload, key, meta, validate = true) {
47
+ const rawField = RawQueryFragment.isKnownFragmentSymbol(key);
48
+ const prop = rawField ? null : meta?.properties[key];
49
+ const childEntity = prop && prop.kind !== ReferenceKind.SCALAR ? prop.targetMeta.class : entityName;
50
+ const isNotEmbedded = rawField || prop?.kind !== ReferenceKind.EMBEDDED;
51
+ const val = payload[key];
50
52
  if (isNotEmbedded && prop?.customType instanceof JsonType) {
51
- return this.createScalarNode(metadata, childEntity, payload[key], node, key);
53
+ return this.createScalarNode(metadata, childEntity, val, node, key, validate);
52
54
  }
53
- if (prop?.kind === ReferenceKind.SCALAR && payload[key] != null && Object.keys(payload[key]).some(f => f in GroupOperator)) {
55
+ if (prop?.kind === ReferenceKind.SCALAR && val != null && Object.keys(val).some(f => f in GroupOperator)) {
54
56
  throw ValidationError.cannotUseGroupOperatorsInsideScalars(entityName, prop.name, payload);
55
57
  }
56
58
  if (isNotEmbedded) {
57
- return this.createNode(metadata, childEntity, payload[key], node, key);
59
+ return this.createNode(metadata, childEntity, val, node, key, validate);
58
60
  }
59
- if (payload[key] == null) {
61
+ if (val == null) {
60
62
  const map = Object.keys(prop.embeddedProps).reduce((oo, k) => {
61
63
  oo[prop.embeddedProps[k].name] = null;
62
64
  return oo;
63
65
  }, {});
64
- return this.createNode(metadata, entityName, map, node, key);
66
+ return this.createNode(metadata, entityName, map, node, key, validate);
65
67
  }
66
68
  // array operators can be used on embedded properties
67
69
  const allowedOperators = ['$contains', '$contained', '$overlap'];
68
- const operator = Object.keys(payload[key]).some(f => Utils.isOperator(f) && !allowedOperators.includes(f));
70
+ const operator = Object.keys(val).some(f => Utils.isOperator(f) && !allowedOperators.includes(f));
69
71
  if (operator) {
70
72
  throw ValidationError.cannotUseOperatorsInsideEmbeddables(entityName, prop.name, payload);
71
73
  }
72
- const map = Object.keys(payload[key]).reduce((oo, k) => {
74
+ const map = Object.keys(val).reduce((oo, k) => {
73
75
  const embeddedProp = prop.embeddedProps[k] ?? Object.values(prop.embeddedProps).find(p => p.name === k);
74
76
  if (!embeddedProp && !allowedOperators.includes(k)) {
75
77
  throw ValidationError.invalidEmbeddableQuery(entityName, k, prop.type);
76
78
  }
77
79
  if (embeddedProp) {
78
- oo[embeddedProp.name] = payload[key][k];
80
+ oo[embeddedProp.name] = val[k];
79
81
  }
80
- else if (typeof payload[key][k] === 'object') {
81
- oo[k] = JSON.stringify(payload[key][k]);
82
+ else if (typeof val[k] === 'object') {
83
+ oo[k] = JSON.stringify(val[k]);
82
84
  }
83
85
  else {
84
- oo[k] = payload[key][k];
86
+ oo[k] = val[k];
85
87
  }
86
88
  return oo;
87
89
  }, {});
88
- return this.createNode(metadata, entityName, map, node, key);
90
+ return this.createNode(metadata, entityName, map, node, key, validate);
89
91
  }
90
92
  }
@@ -1,4 +1,4 @@
1
- import { type Dictionary, LockMode, type QueryFlag, RawQueryFragment } from '@mikro-orm/core';
1
+ import { type Dictionary, LockMode, type QueryFlag, RawQueryFragment, type Subquery } from '@mikro-orm/core';
2
2
  import { QueryType } from './enums.js';
3
3
  import type { AbstractSqlPlatform } from '../AbstractSqlPlatform.js';
4
4
  interface Options {
@@ -48,8 +48,9 @@ interface OnConflictClause {
48
48
  };
49
49
  }
50
50
  /** @internal */
51
- export declare class NativeQueryBuilder {
51
+ export declare class NativeQueryBuilder implements Subquery {
52
52
  protected readonly platform: AbstractSqlPlatform;
53
+ readonly __subquery: true;
53
54
  protected type?: QueryType;
54
55
  protected parts: string[];
55
56
  protected params: unknown[];
@@ -27,8 +27,7 @@ export class NativeQueryBuilder {
27
27
  }
28
28
  from(tableName, options) {
29
29
  if (tableName instanceof NativeQueryBuilder) {
30
- const { sql, params } = tableName.compile();
31
- tableName = raw(sql, params);
30
+ tableName = tableName.toRaw();
32
31
  }
33
32
  if (typeof tableName === 'string') {
34
33
  const alias = options?.alias ? ` as ${this.platform.quoteIdentifier(options.alias)}` : '';
@@ -1,21 +1,22 @@
1
1
  import { ALIAS_REPLACEMENT, GroupOperator, QueryFlag, raw, RawQueryFragment, ReferenceKind, Utils, } from '@mikro-orm/core';
2
2
  import { CriteriaNode } from './CriteriaNode.js';
3
3
  import { JoinType, QueryType } from './enums.js';
4
+ const COLLECTION_OPERATORS = ['$some', '$none', '$every', '$size'];
4
5
  /**
5
6
  * @internal
6
7
  */
7
8
  export class ObjectCriteriaNode extends CriteriaNode {
8
9
  process(qb, options) {
9
10
  const matchPopulateJoins = options?.matchPopulateJoins || (this.prop && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(this.prop.kind));
10
- const nestedAlias = qb.getAliasForJoinPath(this.getPath(), { ...options, matchPopulateJoins });
11
+ const nestedAlias = qb.getAliasForJoinPath(this.getPath(options), { ...options, matchPopulateJoins });
11
12
  const ownerAlias = options?.alias || qb.alias;
12
- const keys = Object.keys(this.payload);
13
+ const keys = Utils.getObjectQueryKeys(this.payload);
13
14
  let alias = options?.alias;
14
15
  if (nestedAlias) {
15
16
  alias = nestedAlias;
16
17
  }
17
18
  if (this.shouldAutoJoin(qb, nestedAlias)) {
18
- if (keys.some(k => ['$some', '$none', '$every'].includes(k))) {
19
+ if (keys.some(k => COLLECTION_OPERATORS.includes(k))) {
19
20
  if (![ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(this.prop.kind)) {
20
21
  // ignore collection operators when used on a non-relational property - this can happen when they get into
21
22
  // populateWhere via `infer` on m:n properties with select-in strategy
@@ -31,22 +32,31 @@ export class ObjectCriteriaNode extends CriteriaNode {
31
32
  return [QueryType.SELECT, QueryType.COUNT].includes(qb.type) ? `${knownKey ? alias : ownerAlias}.${pk}` : pk;
32
33
  });
33
34
  for (const key of keys) {
34
- if (!['$some', '$none', '$every'].includes(key)) {
35
+ if (typeof key !== 'string' || !COLLECTION_OPERATORS.includes(key)) {
35
36
  throw new Error('Mixing collection operators with other filters is not allowed.');
36
37
  }
37
38
  const payload = this.payload[key].unwrap();
38
- const qb2 = qb.clone(true);
39
+ const qb2 = qb.clone(true, ['_schema']);
40
+ const joinAlias = qb2.getNextAlias(this.prop.targetMeta.class);
39
41
  const sub = qb2
40
- .from(parentMeta.className)
41
- .innerJoin(this.key, qb2.getNextAlias(this.prop.type))
42
+ .from(parentMeta.class)
43
+ // eslint-disable-next-line no-unexpected-multiline
44
+ [key === '$size' ? 'leftJoin' : 'innerJoin'](this.key, joinAlias)
42
45
  .select(parentMeta.primaryKeys);
43
- if (key === '$every') {
46
+ if (key === '$size') {
47
+ const sizeCondition = typeof payload === 'number' ? { $eq: payload } : payload;
48
+ const pks = this.prop.referencedColumnNames;
49
+ const countExpr = raw(`count(${pks.map(() => '??').join(', ')})`, pks.map(pk => `${joinAlias}.${pk}`));
50
+ sub.groupBy(parentMeta.primaryKeys);
51
+ sub.having({ $and: Object.keys(sizeCondition).map(op => ({ [countExpr]: { [op]: sizeCondition[op] } })) });
52
+ }
53
+ else if (key === '$every') {
44
54
  sub.where({ $not: { [this.key]: payload } });
45
55
  }
46
56
  else {
47
57
  sub.where({ [this.key]: payload });
48
58
  }
49
- const op = key === '$some' ? '$in' : '$nin';
59
+ const op = ['$size', '$some'].includes(key) ? '$in' : '$nin';
50
60
  $and.push({
51
61
  [Utils.getPrimaryKeyHash(primaryKeys)]: { [op]: sub.getNativeQuery().toRaw() },
52
62
  });
@@ -78,7 +88,7 @@ export class ObjectCriteriaNode extends CriteriaNode {
78
88
  const childNode = this.payload[field];
79
89
  const payload = childNode.process(qb, { ...options, alias: this.prop ? alias : ownerAlias });
80
90
  const operator = Utils.isOperator(field);
81
- const isRawField = RawQueryFragment.isKnownFragment(field);
91
+ const isRawField = RawQueryFragment.isKnownFragmentSymbol(field);
82
92
  // we need to keep the prefixing for formulas otherwise we would lose aliasing context when nesting inside group operators
83
93
  const virtual = childNode.prop?.persist === false && !childNode.prop?.formula;
84
94
  // if key is missing, we are inside group operator and we need to prefix with alias
@@ -96,35 +106,40 @@ export class ObjectCriteriaNode extends CriteriaNode {
96
106
  const rawField = RawQueryFragment.getKnownFragment(field);
97
107
  o[raw(rawField.sql.replaceAll(ALIAS_REPLACEMENT, alias), rawField.params)] = payload;
98
108
  }
109
+ else if (!childNode.validate && !childNode.prop && !field.includes('.') && !operator) {
110
+ // wrap unknown fields in raw() to prevent alias prefixing (e.g. raw SQL aliases in HAVING)
111
+ // use '??' placeholder to properly quote the identifier
112
+ o[raw('??', [field])] = payload;
113
+ }
99
114
  else if (primaryKey || virtual || operator || field.includes('.') || ![QueryType.SELECT, QueryType.COUNT].includes(qb.type)) {
100
115
  this.inlineCondition(field.replaceAll(ALIAS_REPLACEMENT, alias), o, payload);
101
116
  }
102
117
  else {
103
- this.inlineCondition(`${alias}.${field}`, o, payload);
118
+ this.inlineCondition(`${alias ?? qb.alias}.${field}`, o, payload);
104
119
  }
105
120
  return o;
106
121
  }, {});
107
122
  }
108
123
  isStrict() {
109
- return this.strict || Object.keys(this.payload).some(key => {
124
+ return this.strict || Utils.getObjectQueryKeys(this.payload).some(key => {
110
125
  return this.payload[key].isStrict();
111
126
  });
112
127
  }
113
128
  unwrap() {
114
- return Object.keys(this.payload).reduce((o, field) => {
129
+ return Utils.getObjectQueryKeys(this.payload).reduce((o, field) => {
115
130
  o[field] = this.payload[field].unwrap();
116
131
  return o;
117
132
  }, {});
118
133
  }
119
134
  willAutoJoin(qb, alias, options) {
120
- const nestedAlias = qb.getAliasForJoinPath(this.getPath(), options);
135
+ const nestedAlias = qb.getAliasForJoinPath(this.getPath(options), options);
121
136
  const ownerAlias = alias || qb.alias;
122
- const keys = Object.keys(this.payload);
137
+ const keys = Utils.getObjectQueryKeys(this.payload);
123
138
  if (nestedAlias) {
124
139
  alias = nestedAlias;
125
140
  }
126
141
  if (this.shouldAutoJoin(qb, nestedAlias)) {
127
- return !keys.some(k => ['$some', '$none', '$every'].includes(k));
142
+ return !keys.some(k => COLLECTION_OPERATORS.includes(k));
128
143
  }
129
144
  return keys.some(field => {
130
145
  const childNode = this.payload[field];
@@ -132,9 +147,9 @@ export class ObjectCriteriaNode extends CriteriaNode {
132
147
  });
133
148
  }
134
149
  shouldInline(payload) {
135
- const customExpression = RawQueryFragment.isKnownFragment(this.key);
136
- const scalar = Utils.isPrimaryKey(payload) || payload instanceof RegExp || payload instanceof Date || customExpression;
137
- const operator = Utils.isObject(payload) && Object.keys(payload).every(k => Utils.isOperator(k, false));
150
+ const rawField = RawQueryFragment.isKnownFragmentSymbol(this.key);
151
+ const scalar = Utils.isPrimaryKey(payload) || payload instanceof RegExp || payload instanceof Date || rawField;
152
+ const operator = Utils.isObject(payload) && Utils.getObjectQueryKeys(payload).every(k => Utils.isOperator(k, false));
138
153
  return !!this.prop && this.prop.kind !== ReferenceKind.SCALAR && !scalar && !operator;
139
154
  }
140
155
  getChildKey(k, prop, childAlias, alias) {
@@ -145,8 +160,8 @@ export class ObjectCriteriaNode extends CriteriaNode {
145
160
  }
146
161
  inlineArrayChildPayload(obj, payload, k, prop, childAlias, alias) {
147
162
  const key = this.getChildKey(k, prop, childAlias);
148
- const value = payload.map((child) => Object.keys(child).reduce((inner, childKey) => {
149
- const key = (this.isPrefixed(childKey) || Utils.isOperator(childKey)) ? childKey : this.aliased(childKey, childAlias);
163
+ const value = payload.map((child) => Utils.getObjectQueryKeys(child).reduce((inner, childKey) => {
164
+ const key = (RawQueryFragment.isKnownFragmentSymbol(childKey) || this.isPrefixed(childKey) || Utils.isOperator(childKey)) ? childKey : this.aliased(childKey, childAlias);
150
165
  inner[key] = child[childKey];
151
166
  return inner;
152
167
  }, {}));
@@ -154,8 +169,11 @@ export class ObjectCriteriaNode extends CriteriaNode {
154
169
  }
155
170
  inlineChildPayload(o, payload, field, alias, childAlias) {
156
171
  const prop = this.metadata.find(this.entityName).properties[field];
157
- for (const k of Object.keys(payload)) {
158
- if (Utils.isOperator(k, false)) {
172
+ for (const k of Utils.getObjectQueryKeys(payload)) {
173
+ if (RawQueryFragment.isKnownFragmentSymbol(k)) {
174
+ o[k] = payload[k];
175
+ }
176
+ else if (Utils.isOperator(k, false)) {
159
177
  const tmp = payload[k];
160
178
  delete payload[k];
161
179
  o[this.aliased(field, alias)] = { [k]: tmp, ...o[this.aliased(field, alias)] };
@@ -167,9 +185,6 @@ export class ObjectCriteriaNode extends CriteriaNode {
167
185
  const key = this.getChildKey(k, prop, childAlias, alias);
168
186
  this.inlineCondition(key, o, payload[k]);
169
187
  }
170
- else if (RawQueryFragment.isKnownFragment(k)) {
171
- o[k] = payload[k];
172
- }
173
188
  else {
174
189
  o[this.aliased(k, childAlias)] = payload[k];
175
190
  }
@@ -194,11 +209,11 @@ export class ObjectCriteriaNode extends CriteriaNode {
194
209
  if (!this.prop || !this.parent) {
195
210
  return false;
196
211
  }
197
- const keys = Object.keys(this.payload);
198
- if (keys.every(k => k.includes('.') && k.startsWith(`${qb.alias}.`))) {
212
+ const keys = Utils.getObjectQueryKeys(this.payload);
213
+ if (keys.every(k => typeof k === 'string' && k.includes('.') && k.startsWith(`${qb.alias}.`))) {
199
214
  return false;
200
215
  }
201
- if (keys.some(k => ['$some', '$none', '$every'].includes(k))) {
216
+ if (keys.some(k => COLLECTION_OPERATORS.includes(k))) {
202
217
  return true;
203
218
  }
204
219
  const meta = this.metadata.find(this.entityName);
@@ -206,21 +221,21 @@ export class ObjectCriteriaNode extends CriteriaNode {
206
221
  const knownKey = [ReferenceKind.SCALAR, ReferenceKind.MANY_TO_ONE, ReferenceKind.EMBEDDED].includes(this.prop.kind) || (this.prop.kind === ReferenceKind.ONE_TO_ONE && this.prop.owner);
207
222
  const operatorKeys = knownKey && keys.every(key => Utils.isOperator(key, false));
208
223
  const primaryKeys = knownKey && keys.every(key => {
209
- if (!meta.primaryKeys.includes(key)) {
224
+ if (typeof key !== 'string' || !meta.primaryKeys.includes(key)) {
210
225
  return false;
211
226
  }
212
227
  if (!Utils.isPlainObject(this.payload[key].payload) || ![ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(meta.properties[key].kind)) {
213
228
  return true;
214
229
  }
215
- return Object.keys(this.payload[key].payload).every(k => meta.properties[key].targetMeta.primaryKeys.includes(k));
230
+ return Utils.getObjectQueryKeys(this.payload[key].payload).every(k => typeof k === 'string' && meta.properties[key].targetMeta.primaryKeys.includes(k));
216
231
  });
217
232
  return !primaryKeys && !nestedAlias && !operatorKeys && !embeddable;
218
233
  }
219
234
  autoJoin(qb, alias, options) {
220
- const nestedAlias = qb.getNextAlias(this.prop?.pivotTable ?? this.entityName);
221
- const customExpression = RawQueryFragment.isKnownFragment(this.key);
222
- const scalar = Utils.isPrimaryKey(this.payload) || this.payload instanceof RegExp || this.payload instanceof Date || customExpression;
223
- const operator = Utils.isPlainObject(this.payload) && Object.keys(this.payload).every(k => Utils.isOperator(k, false));
235
+ const nestedAlias = qb.getNextAlias(this.prop?.pivotEntity ?? this.entityName);
236
+ const rawField = RawQueryFragment.isKnownFragmentSymbol(this.key);
237
+ const scalar = Utils.isPrimaryKey(this.payload) || this.payload instanceof RegExp || this.payload instanceof Date || rawField;
238
+ const operator = Utils.isPlainObject(this.payload) && Utils.getObjectQueryKeys(this.payload).every(k => Utils.isOperator(k, false));
224
239
  const field = `${alias}.${this.prop.name}`;
225
240
  const method = qb.hasFlag(QueryFlag.INFER_POPULATE) ? 'joinAndSelect' : 'join';
226
241
  const path = this.getPath();