@mikro-orm/core 7.0.0-dev.30 → 7.0.0-dev.300

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 (209) hide show
  1. package/EntityManager.d.ts +68 -60
  2. package/EntityManager.js +290 -259
  3. package/MikroORM.d.ts +44 -35
  4. package/MikroORM.js +109 -142
  5. package/README.md +2 -0
  6. package/cache/FileCacheAdapter.d.ts +1 -2
  7. package/cache/FileCacheAdapter.js +18 -11
  8. package/cache/GeneratedCacheAdapter.d.ts +0 -1
  9. package/cache/GeneratedCacheAdapter.js +0 -2
  10. package/cache/index.d.ts +0 -1
  11. package/cache/index.js +0 -1
  12. package/connections/Connection.d.ts +12 -5
  13. package/connections/Connection.js +21 -12
  14. package/drivers/DatabaseDriver.d.ts +25 -16
  15. package/drivers/DatabaseDriver.js +119 -36
  16. package/drivers/IDatabaseDriver.d.ts +118 -23
  17. package/entity/BaseEntity.d.ts +63 -4
  18. package/entity/BaseEntity.js +0 -3
  19. package/entity/Collection.d.ts +101 -29
  20. package/entity/Collection.js +436 -104
  21. package/entity/EntityAssigner.js +17 -17
  22. package/entity/EntityFactory.d.ts +7 -1
  23. package/entity/EntityFactory.js +87 -55
  24. package/entity/EntityHelper.d.ts +2 -2
  25. package/entity/EntityHelper.js +57 -19
  26. package/entity/EntityLoader.d.ts +11 -10
  27. package/entity/EntityLoader.js +213 -82
  28. package/entity/EntityRepository.d.ts +28 -8
  29. package/entity/EntityRepository.js +8 -2
  30. package/entity/PolymorphicRef.d.ts +12 -0
  31. package/entity/PolymorphicRef.js +18 -0
  32. package/entity/Reference.d.ts +1 -5
  33. package/entity/Reference.js +15 -11
  34. package/entity/WrappedEntity.d.ts +3 -8
  35. package/entity/WrappedEntity.js +2 -7
  36. package/entity/defineEntity.d.ts +526 -310
  37. package/entity/defineEntity.js +134 -290
  38. package/entity/index.d.ts +2 -2
  39. package/entity/index.js +2 -2
  40. package/entity/utils.d.ts +6 -1
  41. package/entity/utils.js +34 -1
  42. package/entity/validators.d.ts +11 -0
  43. package/entity/validators.js +65 -0
  44. package/enums.d.ts +8 -6
  45. package/enums.js +2 -1
  46. package/errors.d.ts +20 -10
  47. package/errors.js +55 -23
  48. package/events/EventManager.d.ts +2 -1
  49. package/events/EventManager.js +19 -11
  50. package/hydration/Hydrator.js +1 -2
  51. package/hydration/ObjectHydrator.d.ts +4 -4
  52. package/hydration/ObjectHydrator.js +79 -34
  53. package/index.d.ts +2 -2
  54. package/index.js +1 -2
  55. package/logging/DefaultLogger.d.ts +1 -1
  56. package/logging/DefaultLogger.js +1 -0
  57. package/logging/SimpleLogger.d.ts +1 -1
  58. package/logging/colors.d.ts +1 -1
  59. package/logging/colors.js +7 -6
  60. package/logging/index.d.ts +1 -0
  61. package/logging/index.js +1 -0
  62. package/logging/inspect.d.ts +2 -0
  63. package/logging/inspect.js +11 -0
  64. package/metadata/EntitySchema.d.ts +47 -23
  65. package/metadata/EntitySchema.js +92 -33
  66. package/metadata/MetadataDiscovery.d.ts +64 -9
  67. package/metadata/MetadataDiscovery.js +782 -325
  68. package/metadata/MetadataProvider.d.ts +11 -2
  69. package/metadata/MetadataProvider.js +66 -2
  70. package/metadata/MetadataStorage.d.ts +13 -11
  71. package/metadata/MetadataStorage.js +72 -39
  72. package/metadata/MetadataValidator.d.ts +32 -9
  73. package/metadata/MetadataValidator.js +196 -41
  74. package/metadata/discover-entities.d.ts +5 -0
  75. package/metadata/discover-entities.js +40 -0
  76. package/metadata/index.d.ts +1 -1
  77. package/metadata/index.js +1 -1
  78. package/metadata/types.d.ts +577 -0
  79. package/metadata/types.js +1 -0
  80. package/naming-strategy/AbstractNamingStrategy.d.ts +16 -4
  81. package/naming-strategy/AbstractNamingStrategy.js +20 -2
  82. package/naming-strategy/EntityCaseNamingStrategy.d.ts +3 -3
  83. package/naming-strategy/EntityCaseNamingStrategy.js +6 -5
  84. package/naming-strategy/MongoNamingStrategy.d.ts +3 -3
  85. package/naming-strategy/MongoNamingStrategy.js +6 -6
  86. package/naming-strategy/NamingStrategy.d.ts +28 -4
  87. package/naming-strategy/UnderscoreNamingStrategy.d.ts +3 -3
  88. package/naming-strategy/UnderscoreNamingStrategy.js +6 -6
  89. package/not-supported.d.ts +2 -0
  90. package/not-supported.js +4 -0
  91. package/package.json +22 -11
  92. package/platforms/ExceptionConverter.js +1 -1
  93. package/platforms/Platform.d.ts +11 -15
  94. package/platforms/Platform.js +24 -44
  95. package/serialization/EntitySerializer.d.ts +6 -3
  96. package/serialization/EntitySerializer.js +46 -26
  97. package/serialization/EntityTransformer.js +33 -21
  98. package/serialization/SerializationContext.d.ts +6 -6
  99. package/serialization/SerializationContext.js +3 -3
  100. package/types/ArrayType.d.ts +1 -1
  101. package/types/ArrayType.js +2 -3
  102. package/types/BigIntType.js +1 -1
  103. package/types/BlobType.d.ts +0 -1
  104. package/types/BlobType.js +0 -3
  105. package/types/BooleanType.d.ts +1 -0
  106. package/types/BooleanType.js +3 -0
  107. package/types/DecimalType.js +2 -2
  108. package/types/DoubleType.js +1 -1
  109. package/types/EnumArrayType.js +1 -2
  110. package/types/JsonType.d.ts +1 -1
  111. package/types/JsonType.js +7 -2
  112. package/types/TinyIntType.js +1 -1
  113. package/types/Type.d.ts +2 -4
  114. package/types/Type.js +3 -3
  115. package/types/Uint8ArrayType.d.ts +0 -1
  116. package/types/Uint8ArrayType.js +1 -4
  117. package/types/index.d.ts +1 -1
  118. package/typings.d.ts +412 -155
  119. package/typings.js +99 -44
  120. package/unit-of-work/ChangeSet.d.ts +4 -6
  121. package/unit-of-work/ChangeSet.js +4 -5
  122. package/unit-of-work/ChangeSetComputer.d.ts +3 -8
  123. package/unit-of-work/ChangeSetComputer.js +41 -20
  124. package/unit-of-work/ChangeSetPersister.d.ts +13 -12
  125. package/unit-of-work/ChangeSetPersister.js +94 -36
  126. package/unit-of-work/CommitOrderCalculator.d.ts +12 -10
  127. package/unit-of-work/CommitOrderCalculator.js +13 -13
  128. package/unit-of-work/IdentityMap.d.ts +12 -0
  129. package/unit-of-work/IdentityMap.js +39 -1
  130. package/unit-of-work/UnitOfWork.d.ts +27 -3
  131. package/unit-of-work/UnitOfWork.js +248 -90
  132. package/utils/AbstractMigrator.d.ts +101 -0
  133. package/utils/AbstractMigrator.js +305 -0
  134. package/utils/AbstractSchemaGenerator.d.ts +5 -5
  135. package/utils/AbstractSchemaGenerator.js +28 -17
  136. package/utils/AsyncContext.d.ts +6 -0
  137. package/utils/AsyncContext.js +42 -0
  138. package/utils/Configuration.d.ts +795 -211
  139. package/utils/Configuration.js +153 -194
  140. package/utils/ConfigurationLoader.d.ts +1 -52
  141. package/utils/ConfigurationLoader.js +1 -330
  142. package/utils/Cursor.d.ts +0 -3
  143. package/utils/Cursor.js +24 -11
  144. package/utils/DataloaderUtils.d.ts +10 -5
  145. package/utils/DataloaderUtils.js +29 -12
  146. package/utils/EntityComparator.d.ts +16 -9
  147. package/utils/EntityComparator.js +154 -56
  148. package/utils/QueryHelper.d.ts +18 -6
  149. package/utils/QueryHelper.js +76 -23
  150. package/utils/RawQueryFragment.d.ts +28 -34
  151. package/utils/RawQueryFragment.js +35 -71
  152. package/utils/RequestContext.js +2 -2
  153. package/utils/TransactionContext.js +2 -2
  154. package/utils/TransactionManager.js +9 -6
  155. package/utils/Utils.d.ts +15 -126
  156. package/utils/Utils.js +80 -382
  157. package/utils/clone.js +8 -23
  158. package/utils/env-vars.d.ts +7 -0
  159. package/utils/env-vars.js +97 -0
  160. package/utils/fs-utils.d.ts +34 -0
  161. package/utils/fs-utils.js +196 -0
  162. package/utils/index.d.ts +1 -3
  163. package/utils/index.js +1 -3
  164. package/utils/upsert-utils.d.ts +9 -4
  165. package/utils/upsert-utils.js +46 -3
  166. package/decorators/Check.d.ts +0 -3
  167. package/decorators/Check.js +0 -13
  168. package/decorators/CreateRequestContext.d.ts +0 -3
  169. package/decorators/CreateRequestContext.js +0 -32
  170. package/decorators/Embeddable.d.ts +0 -8
  171. package/decorators/Embeddable.js +0 -11
  172. package/decorators/Embedded.d.ts +0 -12
  173. package/decorators/Embedded.js +0 -18
  174. package/decorators/Entity.d.ts +0 -33
  175. package/decorators/Entity.js +0 -12
  176. package/decorators/Enum.d.ts +0 -9
  177. package/decorators/Enum.js +0 -16
  178. package/decorators/Filter.d.ts +0 -2
  179. package/decorators/Filter.js +0 -8
  180. package/decorators/Formula.d.ts +0 -4
  181. package/decorators/Formula.js +0 -15
  182. package/decorators/Indexed.d.ts +0 -19
  183. package/decorators/Indexed.js +0 -20
  184. package/decorators/ManyToMany.d.ts +0 -42
  185. package/decorators/ManyToMany.js +0 -14
  186. package/decorators/ManyToOne.d.ts +0 -34
  187. package/decorators/ManyToOne.js +0 -14
  188. package/decorators/OneToMany.d.ts +0 -28
  189. package/decorators/OneToMany.js +0 -17
  190. package/decorators/OneToOne.d.ts +0 -28
  191. package/decorators/OneToOne.js +0 -7
  192. package/decorators/PrimaryKey.d.ts +0 -8
  193. package/decorators/PrimaryKey.js +0 -20
  194. package/decorators/Property.d.ts +0 -250
  195. package/decorators/Property.js +0 -32
  196. package/decorators/Transactional.d.ts +0 -14
  197. package/decorators/Transactional.js +0 -28
  198. package/decorators/hooks.d.ts +0 -16
  199. package/decorators/hooks.js +0 -47
  200. package/decorators/index.d.ts +0 -17
  201. package/decorators/index.js +0 -17
  202. package/entity/ArrayCollection.d.ts +0 -118
  203. package/entity/ArrayCollection.js +0 -407
  204. package/entity/EntityValidator.d.ts +0 -19
  205. package/entity/EntityValidator.js +0 -150
  206. package/metadata/ReflectMetadataProvider.d.ts +0 -8
  207. package/metadata/ReflectMetadataProvider.js +0 -44
  208. package/utils/resolveContextProvider.d.ts +0 -10
  209. package/utils/resolveContextProvider.js +0 -28
package/EntityManager.js CHANGED
@@ -1,15 +1,12 @@
1
- import { inspect } from 'node:util';
2
- import DataLoader from 'dataloader';
3
- import { getOnConflictReturningFields } from './utils/upsert-utils.js';
1
+ import { getOnConflictReturningFields, getWhereCondition } from './utils/upsert-utils.js';
4
2
  import { Utils } from './utils/Utils.js';
5
3
  import { Cursor } from './utils/Cursor.js';
6
- import { DataloaderUtils } from './utils/DataloaderUtils.js';
7
4
  import { QueryHelper } from './utils/QueryHelper.js';
8
5
  import { TransactionContext } from './utils/TransactionContext.js';
9
- import { isRaw, RawQueryFragment } from './utils/RawQueryFragment.js';
6
+ import { isRaw, Raw } from './utils/RawQueryFragment.js';
10
7
  import { EntityFactory } from './entity/EntityFactory.js';
11
8
  import { EntityAssigner } from './entity/EntityAssigner.js';
12
- import { EntityValidator } from './entity/EntityValidator.js';
9
+ import { validateEmptyWhere, validateParams, validatePrimaryKey, validateProperty } from './entity/validators.js';
13
10
  import { EntityLoader } from './entity/EntityLoader.js';
14
11
  import { Reference } from './entity/Reference.js';
15
12
  import { helper } from './entity/wrap.js';
@@ -19,7 +16,7 @@ import { EventType, FlushMode, LoadStrategy, LockMode, PopulateHint, PopulatePat
19
16
  import { EventManager } from './events/EventManager.js';
20
17
  import { TransactionEventBroadcaster } from './events/TransactionEventBroadcaster.js';
21
18
  import { OptimisticLockError, ValidationError } from './errors.js';
22
- import { getLoadingStrategy } from './entity/utils.js';
19
+ import { applyPopulateHints, getLoadingStrategy } from './entity/utils.js';
23
20
  import { TransactionManager } from './utils/TransactionManager.js';
24
21
  /**
25
22
  * The EntityManager is the central access point to ORM functionality. It is a facade to all different ORM subsystems
@@ -36,11 +33,8 @@ export class EntityManager {
36
33
  _id = EntityManager.counter++;
37
34
  global = false;
38
35
  name;
39
- refLoader = new DataLoader(DataloaderUtils.getRefBatchLoadFn(this));
40
- colLoader = new DataLoader(DataloaderUtils.getColBatchLoadFn(this));
41
- colLoaderMtoN = new DataLoader(DataloaderUtils.getManyToManyColBatchLoadFn(this));
42
- validator;
43
- repositoryMap = {};
36
+ loaders = {};
37
+ repositoryMap = new Map();
44
38
  entityLoader;
45
39
  comparator;
46
40
  entityFactory;
@@ -64,7 +58,6 @@ export class EntityManager {
64
58
  this.eventManager = eventManager;
65
59
  this.entityLoader = new EntityLoader(this);
66
60
  this.name = this.config.get('contextName');
67
- this.validator = new EntityValidator(this.config.get('strict'));
68
61
  this.comparator = this.config.getComparator(this.metadata);
69
62
  this.resultCache = this.config.getResultCacheAdapter();
70
63
  this.disableTransactions = this.config.get('disableTransactions');
@@ -94,13 +87,12 @@ export class EntityManager {
94
87
  * Gets repository for given entity. You can pass either string name or entity class reference.
95
88
  */
96
89
  getRepository(entityName) {
97
- entityName = Utils.className(entityName);
98
- if (!this.repositoryMap[entityName]) {
99
- const meta = this.metadata.get(entityName);
90
+ const meta = this.metadata.get(entityName);
91
+ if (!this.repositoryMap.has(meta)) {
100
92
  const RepositoryClass = this.config.getRepositoryClass(meta.repository);
101
- this.repositoryMap[entityName] = new RepositoryClass(this, entityName);
93
+ this.repositoryMap.set(meta, new RepositoryClass(this, entityName));
102
94
  }
103
- return this.repositoryMap[entityName];
95
+ return this.repositoryMap.get(meta);
104
96
  }
105
97
  /**
106
98
  * Shortcut for `em.getRepository()`.
@@ -108,12 +100,6 @@ export class EntityManager {
108
100
  repo(entityName) {
109
101
  return this.getRepository(entityName);
110
102
  }
111
- /**
112
- * Gets EntityValidator instance
113
- */
114
- getValidator() {
115
- return this.validator;
116
- }
117
103
  /**
118
104
  * Finds all entities matching your `where` query. You can pass additional options via the `options` parameter.
119
105
  */
@@ -128,10 +114,15 @@ export class EntityManager {
128
114
  const em = this.getContext();
129
115
  em.prepareOptions(options);
130
116
  await em.tryFlush(entityName, options);
131
- entityName = Utils.className(entityName);
132
117
  where = await em.processWhere(entityName, where, options, 'read');
133
- em.validator.validateParams(where);
134
- options.orderBy = options.orderBy || {};
118
+ validateParams(where);
119
+ const meta = this.metadata.get(entityName);
120
+ if (meta.orderBy) {
121
+ options.orderBy = QueryHelper.mergeOrderBy(options.orderBy, meta.orderBy);
122
+ }
123
+ else {
124
+ options.orderBy ??= {};
125
+ }
135
126
  options.populate = await em.preparePopulate(entityName, options);
136
127
  const populate = options.populate;
137
128
  const cacheKey = em.cacheKey(entityName, options, 'em.find', where);
@@ -145,12 +136,12 @@ export class EntityManager {
145
136
  });
146
137
  return cached.data;
147
138
  }
148
- const meta = this.metadata.get(entityName);
149
139
  options = { ...options };
150
140
  // save the original hint value so we know it was infer/all
151
141
  options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
152
142
  options.populateWhere = this.createPopulateWhere({ ...where }, options);
153
- options.populateFilter = await this.getJoinedFilters(meta, { ...where }, options);
143
+ options.populateFilter = await this.getJoinedFilters(meta, options);
144
+ await em.processUnionWhere(entityName, options, 'read');
154
145
  const results = await em.driver.find(entityName, where, { ctx: em.transactionContext, em, ...options });
155
146
  if (results.length === 0) {
156
147
  await em.storeCache(options.cache, cached, []);
@@ -182,6 +173,60 @@ export class EntityManager {
182
173
  }
183
174
  return unique;
184
175
  }
176
+ /**
177
+ * Finds all entities and returns an async iterable (async generator) that yields results one by one.
178
+ * The results are merged and mapped to entity instances, without adding them to the identity map.
179
+ * You can disable merging by passing the options `{ mergeResults: false }`.
180
+ * With `mergeResults` disabled, to-many collections will contain at most one item, and you will get duplicate
181
+ * root entities when there are multiple items in the populated collection.
182
+ * This is useful for processing large datasets without loading everything into memory at once.
183
+ *
184
+ * ```ts
185
+ * const stream = em.stream(Book, { populate: ['author'] });
186
+ *
187
+ * for await (const book of stream) {
188
+ * // book is an instance of Book entity
189
+ * console.log(book.title, book.author.name);
190
+ * }
191
+ * ```
192
+ */
193
+ async *stream(entityName, options = {}) {
194
+ const em = this.getContext();
195
+ em.prepareOptions(options);
196
+ options.strategy = 'joined';
197
+ await em.tryFlush(entityName, options);
198
+ const where = await em.processWhere(entityName, options.where ?? {}, options, 'read');
199
+ validateParams(where);
200
+ options.orderBy = options.orderBy || {};
201
+ options.populate = await em.preparePopulate(entityName, options);
202
+ const meta = this.metadata.get(entityName);
203
+ options = { ...options };
204
+ // save the original hint value so we know it was infer/all
205
+ options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
206
+ options.populateWhere = this.createPopulateWhere({ ...where }, options);
207
+ options.populateFilter = await this.getJoinedFilters(meta, options);
208
+ const stream = em.driver.stream(entityName, where, {
209
+ ctx: em.transactionContext,
210
+ mapResults: false,
211
+ ...options,
212
+ });
213
+ for await (const data of stream) {
214
+ const fork = em.fork();
215
+ const entity = fork.entityFactory.create(entityName, data, {
216
+ refresh: options.refresh,
217
+ schema: options.schema,
218
+ convertCustomTypes: true,
219
+ });
220
+ helper(entity).setSerializationContext({
221
+ populate: options.populate,
222
+ fields: options.fields,
223
+ exclude: options.exclude,
224
+ });
225
+ await fork.unitOfWork.dispatchOnLoadEvent();
226
+ fork.clear();
227
+ yield entity;
228
+ }
229
+ }
185
230
  /**
186
231
  * Finds all entities of given type, optionally matching the `where` condition provided in the `options` parameter.
187
232
  */
@@ -195,7 +240,7 @@ export class EntityManager {
195
240
  if (options.populateWhere === PopulateHint.ALL) {
196
241
  return { where: {}, populateWhere: options.populateWhere };
197
242
  }
198
- /* v8 ignore next 3 */
243
+ /* v8 ignore next */
199
244
  if (options.populateWhere === PopulateHint.INFER) {
200
245
  return { where, populateWhere: options.populateWhere };
201
246
  }
@@ -204,12 +249,12 @@ export class EntityManager {
204
249
  /**
205
250
  * Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
206
251
  */
207
- addFilter(name, cond, entityName, enabled = true) {
208
- const options = { name, cond, default: enabled };
209
- if (entityName) {
210
- options.entity = Utils.asArray(entityName).map(n => Utils.className(n));
252
+ addFilter(options) {
253
+ if (options.entity) {
254
+ options.entity = Utils.asArray(options.entity).map(n => Utils.className(n));
211
255
  }
212
- this.getContext(false).filters[name] = options;
256
+ options.default ??= true;
257
+ this.getContext(false).filters[options.name] = options;
213
258
  }
214
259
  /**
215
260
  * Sets filter parameter values globally inside context defined by this entity manager.
@@ -254,21 +299,29 @@ export class EntityManager {
254
299
  where = this.applyDiscriminatorCondition(entityName, where);
255
300
  return where;
256
301
  }
302
+ async processUnionWhere(entityName, options, type) {
303
+ if (options.unionWhere?.length) {
304
+ if (!this.driver.getPlatform().supportsUnionWhere()) {
305
+ throw new Error(`unionWhere is only supported on SQL drivers`);
306
+ }
307
+ options.unionWhere = await Promise.all(options.unionWhere.map(branch => this.processWhere(entityName, branch, options, type)));
308
+ }
309
+ }
257
310
  // this method only handles the problem for mongo driver, SQL drivers have their implementation inside QueryBuilder
258
311
  applyDiscriminatorCondition(entityName, where) {
259
312
  const meta = this.metadata.find(entityName);
260
- if (!meta?.discriminatorValue) {
313
+ if (meta?.root.inheritanceType !== 'sti' || !meta?.discriminatorValue) {
261
314
  return where;
262
315
  }
263
- const types = Object.values(meta.root.discriminatorMap).map(cls => this.metadata.find(cls));
316
+ const types = Object.values(meta.root.discriminatorMap).map(cls => this.metadata.get(cls));
264
317
  const children = [];
265
318
  const lookUpChildren = (ret, type) => {
266
319
  const children = types.filter(meta2 => meta2.extends === type);
267
- children.forEach(m => lookUpChildren(ret, m.className));
320
+ children.forEach(m => lookUpChildren(ret, m.class));
268
321
  ret.push(...children.filter(c => c.discriminatorValue));
269
322
  return children;
270
323
  };
271
- lookUpChildren(children, meta.className);
324
+ lookUpChildren(children, meta.class);
272
325
  /* v8 ignore next */
273
326
  where[meta.root.discriminatorColumn] = children.length > 0 ? { $in: [meta.discriminatorValue, ...children.map(c => c.discriminatorValue)] } : meta.discriminatorValue;
274
327
  return where;
@@ -284,60 +337,77 @@ export class EntityManager {
284
337
  }
285
338
  return ret;
286
339
  }
287
- async getJoinedFilters(meta, cond, options) {
340
+ async getJoinedFilters(meta, options) {
341
+ // If user provided populateFilter, merge it with computed filters
342
+ const userFilter = options.populateFilter;
343
+ if (!this.config.get('filtersOnRelations') || !options.populate) {
344
+ return userFilter;
345
+ }
288
346
  const ret = {};
289
- if (options.populate) {
290
- for (const hint of options.populate) {
291
- const field = hint.field.split(':')[0];
292
- const prop = meta.properties[field];
293
- const strategy = getLoadingStrategy(prop.strategy || hint.strategy || options.strategy || this.config.get('loadStrategy'), prop.kind);
294
- const joined = strategy === LoadStrategy.JOINED && prop.kind !== ReferenceKind.SCALAR;
295
- if (!joined && !hint.filter) {
296
- continue;
297
- }
298
- const where = await this.applyFilters(prop.type, {}, options.filters ?? {}, 'read', { ...options, populate: hint.children });
299
- const where2 = await this.getJoinedFilters(prop.targetMeta, {}, { ...options, populate: hint.children, populateWhere: PopulateHint.ALL });
300
- if (Utils.hasObjectKeys(where)) {
301
- ret[field] = ret[field] ? { $and: [where, ret[field]] } : where;
347
+ for (const hint of options.populate) {
348
+ const field = hint.field.split(':')[0];
349
+ const prop = meta.properties[field];
350
+ const strategy = getLoadingStrategy(prop.strategy || hint.strategy || options.strategy || this.config.get('loadStrategy'), prop.kind);
351
+ const joined = strategy === LoadStrategy.JOINED && prop.kind !== ReferenceKind.SCALAR;
352
+ if (!joined && !hint.filter) {
353
+ continue;
354
+ }
355
+ const filters = QueryHelper.mergePropertyFilters(prop.filters, options.filters);
356
+ const where = await this.applyFilters(prop.targetMeta.class, {}, filters, 'read', {
357
+ ...options,
358
+ populate: hint.children,
359
+ });
360
+ const where2 = await this.getJoinedFilters(prop.targetMeta, {
361
+ ...options,
362
+ filters,
363
+ populate: hint.children,
364
+ populateWhere: PopulateHint.ALL,
365
+ });
366
+ if (Utils.hasObjectKeys(where)) {
367
+ ret[field] = ret[field] ? { $and: [where, ret[field]] } : where;
368
+ }
369
+ if (where2 && Utils.hasObjectKeys(where2)) {
370
+ if (ret[field]) {
371
+ Utils.merge(ret[field], where2);
302
372
  }
303
- if (Utils.hasObjectKeys(where2)) {
304
- if (ret[field]) {
305
- Utils.merge(ret[field], where2);
306
- }
307
- else {
308
- ret[field] = where2;
309
- }
373
+ else {
374
+ ret[field] = where2;
310
375
  }
311
376
  }
312
377
  }
313
- return ret;
378
+ // Merge user-provided populateFilter with computed filters
379
+ if (userFilter) {
380
+ Utils.merge(ret, userFilter);
381
+ }
382
+ return Utils.hasObjectKeys(ret) ? ret : undefined;
314
383
  }
315
384
  /**
316
385
  * When filters are active on M:1 or 1:1 relations, we need to ref join them eagerly as they might affect the FK value.
317
386
  */
318
- async autoJoinRefsForFilters(meta, options) {
319
- if (!meta || !this.config.get('autoJoinRefsForFilters')) {
387
+ async autoJoinRefsForFilters(meta, options, parent) {
388
+ if (!meta || !this.config.get('autoJoinRefsForFilters') || options.filters === false) {
320
389
  return;
321
390
  }
322
- const props = meta.relations.filter(prop => {
323
- return !prop.object && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)
324
- && ((options.fields?.length ?? 0) === 0 || options.fields?.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)));
325
- });
326
391
  const ret = options.populate;
327
- for (const prop of props) {
328
- const cond = await this.applyFilters(prop.type, {}, options.filters ?? {}, 'read', options);
392
+ for (const prop of meta.relations) {
393
+ if (prop.object
394
+ || ![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)
395
+ || !((options.fields?.length ?? 0) === 0 || options.fields?.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
396
+ || (parent?.class === prop.targetMeta.root.class && parent.propName === prop.inversedBy)) {
397
+ continue;
398
+ }
399
+ options = { ...options, filters: QueryHelper.mergePropertyFilters(prop.filters, options.filters) };
400
+ const cond = await this.applyFilters(prop.targetMeta.class, {}, options.filters, 'read', options);
329
401
  if (!Utils.isEmpty(cond)) {
330
402
  const populated = options.populate.filter(({ field }) => field.split(':')[0] === prop.name);
331
403
  let found = false;
332
- if (populated.length > 0) {
333
- for (const hint of populated) {
334
- if (!hint.all) {
335
- hint.filter = true;
336
- found = true;
337
- }
338
- else if (hint.field === `${prop.name}:ref`) {
339
- found = true;
340
- }
404
+ for (const hint of populated) {
405
+ if (!hint.all) {
406
+ hint.filter = true;
407
+ }
408
+ const strategy = getLoadingStrategy(prop.strategy || hint.strategy || options.strategy || this.config.get('loadStrategy'), prop.kind);
409
+ if (hint.field === `${prop.name}:ref` || (hint.filter && strategy === LoadStrategy.JOINED)) {
410
+ found = true;
341
411
  }
342
412
  }
343
413
  if (!found) {
@@ -345,21 +415,26 @@ export class EntityManager {
345
415
  }
346
416
  }
347
417
  }
418
+ for (const hint of ret) {
419
+ const [field, ref] = hint.field.split(':');
420
+ const prop = meta?.properties[field];
421
+ if (prop && !ref) {
422
+ hint.children ??= [];
423
+ await this.autoJoinRefsForFilters(prop.targetMeta, { ...options, populate: hint.children }, { class: meta.root.class, propName: prop.name });
424
+ }
425
+ }
348
426
  }
349
427
  /**
350
428
  * @internal
351
429
  */
352
430
  async applyFilters(entityName, where, options, type, findOptions) {
353
- const meta = this.metadata.find(entityName);
431
+ const meta = this.metadata.get(entityName);
354
432
  const filters = [];
355
433
  const ret = [];
356
- if (!meta) {
357
- return where;
358
- }
359
434
  const active = new Set();
360
435
  const push = (source) => {
361
436
  const activeFilters = QueryHelper
362
- .getActiveFilters(entityName, options, source)
437
+ .getActiveFilters(meta, options, source)
363
438
  .filter(f => !active.has(f.name));
364
439
  filters.push(...activeFilters);
365
440
  activeFilters.forEach(f => active.add(f.name));
@@ -374,24 +449,28 @@ export class EntityManager {
374
449
  let cond;
375
450
  if (filter.cond instanceof Function) {
376
451
  // @ts-ignore
377
- const args = Utils.isPlainObject(options[filter.name]) ? options[filter.name] : this.getContext().filterParams[filter.name];
452
+ const args = Utils.isPlainObject(options?.[filter.name]) ? options[filter.name] : this.getContext().filterParams[filter.name];
378
453
  if (!args && filter.cond.length > 0 && filter.args !== false) {
379
454
  throw new Error(`No arguments provided for filter '${filter.name}'`);
380
455
  }
381
- cond = await filter.cond(args, type, this, findOptions, entityName);
456
+ cond = await filter.cond(args, type, this, findOptions, Utils.className(entityName));
382
457
  }
383
458
  else {
384
459
  cond = filter.cond;
385
460
  }
386
- ret.push(QueryHelper.processWhere({
461
+ cond = QueryHelper.processWhere({
387
462
  where: cond,
388
463
  entityName,
389
464
  metadata: this.metadata,
390
465
  platform: this.driver.getPlatform(),
391
466
  aliased: type === 'read',
392
- }));
467
+ });
468
+ if (filter.strict) {
469
+ Object.defineProperty(cond, '__strict', { value: filter.strict, enumerable: false });
470
+ }
471
+ ret.push(cond);
393
472
  }
394
- const conds = [...ret, where].filter(c => Utils.hasObjectKeys(c));
473
+ const conds = [...ret, where].filter(c => Utils.hasObjectKeys(c) || Raw.hasObjectFragments(c));
395
474
  return conds.length > 1 ? { $and: conds } : conds[0];
396
475
  }
397
476
  /**
@@ -402,12 +481,10 @@ export class EntityManager {
402
481
  const em = this.getContext(false);
403
482
  await em.tryFlush(entityName, options);
404
483
  options.flushMode = 'commit'; // do not try to auto flush again
405
- return RawQueryFragment.run(async () => {
406
- return Promise.all([
407
- em.find(entityName, where, options),
408
- em.count(entityName, where, options),
409
- ]);
410
- });
484
+ return Promise.all([
485
+ em.find(entityName, where, options),
486
+ em.count(entityName, where, options),
487
+ ]);
411
488
  }
412
489
  /**
413
490
  * Calls `em.find()` and `em.count()` with the same arguments (where applicable) and returns the results as {@apilink Cursor} object.
@@ -423,21 +500,21 @@ export class EntityManager {
423
500
  * - POJO/entity instance
424
501
  *
425
502
  * ```ts
426
- * const currentCursor = await em.findByCursor(User, {}, {
503
+ * const currentCursor = await em.findByCursor(User, {
427
504
  * first: 10,
428
505
  * after: previousCursor, // cursor instance
429
506
  * orderBy: { id: 'desc' },
430
507
  * });
431
508
  *
432
509
  * // to fetch next page
433
- * const nextCursor = await em.findByCursor(User, {}, {
510
+ * const nextCursor = await em.findByCursor(User, {
434
511
  * first: 10,
435
512
  * after: currentCursor.endCursor, // opaque string
436
513
  * orderBy: { id: 'desc' },
437
514
  * });
438
515
  *
439
516
  * // to fetch next page
440
- * const nextCursor2 = await em.findByCursor(User, {}, {
517
+ * const nextCursor2 = await em.findByCursor(User, {
441
518
  * first: 10,
442
519
  * after: { id: lastSeenId }, // entity-like POJO
443
520
  * orderBy: { id: 'desc' },
@@ -465,16 +542,16 @@ export class EntityManager {
465
542
  * }
466
543
  * ```
467
544
  */
468
- async findByCursor(entityName, where, options) {
545
+ async findByCursor(entityName, options) {
469
546
  const em = this.getContext(false);
470
- entityName = Utils.className(entityName);
471
547
  options.overfetch ??= true;
472
- if (Utils.isEmpty(options.orderBy)) {
548
+ options.where ??= {};
549
+ if (Utils.isEmpty(options.orderBy) && !Raw.hasObjectFragments(options.orderBy)) {
473
550
  throw new Error('Explicit `orderBy` option required');
474
551
  }
475
552
  const [entities, count] = options.includeCount !== false
476
- ? await em.findAndCount(entityName, where, options)
477
- : [await em.find(entityName, where, options)];
553
+ ? await em.findAndCount(entityName, options.where, options)
554
+ : [await em.find(entityName, options.where, options)];
478
555
  return new Cursor(entities, count, options, this.metadata.get(entityName));
479
556
  }
480
557
  /**
@@ -486,9 +563,9 @@ export class EntityManager {
486
563
  const ret = await this.refresh(entity, options);
487
564
  if (!ret) {
488
565
  options.failHandler ??= this.config.get('findOneOrFailHandler');
489
- const entityName = entity.constructor.name;
490
- const where = helper(entity).getPrimaryKey();
491
- throw options.failHandler(entityName, where);
566
+ const wrapped = helper(entity);
567
+ const where = wrapped.getPrimaryKey();
568
+ throw options.failHandler(wrapped.__meta.className, where);
492
569
  }
493
570
  return ret;
494
571
  }
@@ -499,9 +576,9 @@ export class EntityManager {
499
576
  */
500
577
  async refresh(entity, options = {}) {
501
578
  const fork = this.fork({ keepTransactionContext: true });
502
- const entityName = entity.constructor.name;
503
- const reloaded = await fork.findOne(entityName, entity, {
504
- schema: helper(entity).__schema,
579
+ const wrapped = helper(entity);
580
+ const reloaded = await fork.findOne(wrapped.__meta.class, entity, {
581
+ schema: wrapped.__schema,
505
582
  ...options,
506
583
  flushMode: FlushMode.COMMIT,
507
584
  });
@@ -512,16 +589,16 @@ export class EntityManager {
512
589
  }
513
590
  let found = false;
514
591
  for (const e of fork.unitOfWork.getIdentityMap()) {
515
- const ref = em.getReference(e.constructor.name, helper(e).getPrimaryKey());
516
- const data = helper(e).serialize({ ignoreSerializers: true, includeHidden: true });
517
- em.config.getHydrator(this.metadata).hydrate(ref, helper(ref).__meta, data, em.entityFactory, 'full', false, true);
518
- helper(ref).__originalEntityData = this.comparator.prepareEntity(e);
592
+ const ref = em.getReference(e.constructor, helper(e).getPrimaryKey());
593
+ const data = helper(e).serialize({ ignoreSerializers: true, includeHidden: true, convertCustomTypes: false });
594
+ em.config.getHydrator(this.metadata).hydrate(ref, helper(ref).__meta, data, em.entityFactory, 'full', false, false);
595
+ Utils.merge(helper(ref).__originalEntityData, this.comparator.prepareEntity(e));
519
596
  found ||= ref === entity;
520
597
  }
521
598
  if (!found) {
522
- const data = helper(reloaded).serialize({ ignoreSerializers: true, includeHidden: true });
523
- em.config.getHydrator(this.metadata).hydrate(entity, helper(entity).__meta, data, em.entityFactory, 'full', false, true);
524
- helper(entity).__originalEntityData = this.comparator.prepareEntity(reloaded);
599
+ const data = helper(reloaded).serialize({ ignoreSerializers: true, includeHidden: true, convertCustomTypes: true });
600
+ em.config.getHydrator(this.metadata).hydrate(entity, wrapped.__meta, data, em.entityFactory, 'full', false, true);
601
+ Utils.merge(wrapped.__originalEntityData, this.comparator.prepareEntity(reloaded));
525
602
  }
526
603
  return entity;
527
604
  }
@@ -537,7 +614,6 @@ export class EntityManager {
537
614
  return ret;
538
615
  }
539
616
  const em = this.getContext();
540
- entityName = Utils.className(entityName);
541
617
  em.prepareOptions(options);
542
618
  let entity = em.unitOfWork.tryGetById(entityName, where, options.schema);
543
619
  // query for a not managed entity which is already in the identity map as it
@@ -549,13 +625,13 @@ export class EntityManager {
549
625
  await em.tryFlush(entityName, options);
550
626
  const meta = em.metadata.get(entityName);
551
627
  where = await em.processWhere(entityName, where, options, 'read');
552
- em.validator.validateEmptyWhere(where);
628
+ validateEmptyWhere(where);
553
629
  em.checkLockRequirements(options.lockMode, meta);
554
- const isOptimisticLocking = !Utils.isDefined(options.lockMode) || options.lockMode === LockMode.OPTIMISTIC;
630
+ const isOptimisticLocking = options.lockMode == null || options.lockMode === LockMode.OPTIMISTIC;
555
631
  if (entity && !em.shouldRefresh(meta, entity, options) && isOptimisticLocking) {
556
632
  return em.lockAndPopulate(meta, entity, where, options);
557
633
  }
558
- em.validator.validateParams(where);
634
+ validateParams(where);
559
635
  options.populate = await em.preparePopulate(entityName, options);
560
636
  const cacheKey = em.cacheKey(entityName, options, 'em.findOne', where);
561
637
  const cached = await em.tryCache(entityName, options.cache, cacheKey, options.refresh, true);
@@ -574,7 +650,8 @@ export class EntityManager {
574
650
  // save the original hint value so we know it was infer/all
575
651
  options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
576
652
  options.populateWhere = this.createPopulateWhere({ ...where }, options);
577
- options.populateFilter = await this.getJoinedFilters(meta, { ...where }, options);
653
+ options.populateFilter = await this.getJoinedFilters(meta, options);
654
+ await em.processUnionWhere(entityName, options, 'read');
578
655
  const data = await em.driver.findOne(entityName, where, {
579
656
  ctx: em.transactionContext,
580
657
  em,
@@ -615,10 +692,10 @@ export class EntityManager {
615
692
  if (!entity || isStrictViolation) {
616
693
  const key = options.strict ? 'findExactlyOneOrFailHandler' : 'findOneOrFailHandler';
617
694
  options.failHandler ??= this.config.get(key);
618
- entityName = Utils.className(entityName);
695
+ const name = Utils.className(entityName);
619
696
  /* v8 ignore next */
620
697
  where = Utils.isEntity(where) ? helper(where).getPrimaryKey() : where;
621
- throw options.failHandler(entityName, where);
698
+ throw options.failHandler(name, where);
622
699
  }
623
700
  return entity;
624
701
  }
@@ -658,11 +735,11 @@ export class EntityManager {
658
735
  let where;
659
736
  let entity = null;
660
737
  if (data === undefined) {
661
- entityName = entityNameOrEntity.constructor.name;
738
+ entityName = entityNameOrEntity.constructor;
662
739
  data = entityNameOrEntity;
663
740
  }
664
741
  else {
665
- entityName = Utils.className(entityNameOrEntity);
742
+ entityName = entityNameOrEntity;
666
743
  }
667
744
  const meta = this.metadata.get(entityName);
668
745
  const convertCustomTypes = !Utils.isEntity(data);
@@ -685,26 +762,9 @@ export class EntityManager {
685
762
  }
686
763
  }
687
764
  }
688
- const unique = options.onConflictFields ?? meta.props.filter(p => p.unique).map(p => p.name);
689
- const propIndex = !isRaw(unique) && unique.findIndex(p => data[p] != null);
690
- if (options.onConflictFields || where == null) {
691
- if (propIndex !== false && propIndex >= 0) {
692
- where = { [unique[propIndex]]: data[unique[propIndex]] };
693
- }
694
- else if (meta.uniques.length > 0) {
695
- for (const u of meta.uniques) {
696
- if (Utils.asArray(u.properties).every(p => data[p] != null)) {
697
- where = Utils.asArray(u.properties).reduce((o, key) => {
698
- o[key] = data[key];
699
- return o;
700
- }, {});
701
- break;
702
- }
703
- }
704
- }
705
- }
765
+ where = getWhereCondition(meta, options.onConflictFields, data, where).where;
706
766
  data = QueryHelper.processObjectParams(data);
707
- em.validator.validateParams(data, 'insert data');
767
+ validateParams(data, 'insert data');
708
768
  if (em.eventManager.hasListeners(EventType.beforeUpsert, meta)) {
709
769
  await em.eventManager.dispatchEvent(EventType.beforeUpsert, { entity: data, em, meta }, meta);
710
770
  }
@@ -742,7 +802,7 @@ export class EntityManager {
742
802
  where[meta.primaryKeys[0]] = ret.insertId;
743
803
  }
744
804
  }
745
- const data2 = await this.driver.findOne(meta.className, where, {
805
+ const data2 = await this.driver.findOne(meta.class, where, {
746
806
  fields: returning,
747
807
  ctx: em.transactionContext,
748
808
  convertCustomTypes: true,
@@ -797,11 +857,11 @@ export class EntityManager {
797
857
  let entityName;
798
858
  let propIndex;
799
859
  if (data === undefined) {
800
- entityName = entityNameOrEntity[0].constructor.name;
860
+ entityName = entityNameOrEntity[0].constructor;
801
861
  data = entityNameOrEntity;
802
862
  }
803
863
  else {
804
- entityName = Utils.className(entityNameOrEntity);
864
+ entityName = entityNameOrEntity;
805
865
  }
806
866
  const batchSize = options.batchSize ?? this.config.get('batchSize');
807
867
  if (data.length > batchSize) {
@@ -845,32 +905,18 @@ export class EntityManager {
845
905
  }
846
906
  }
847
907
  }
848
- const unique = meta.props.filter(p => p.unique).map(p => p.name);
849
- propIndex = unique.findIndex(p => row[p] != null);
850
- if (options.onConflictFields || where == null) {
851
- if (propIndex >= 0) {
852
- where = { [unique[propIndex]]: row[unique[propIndex]] };
853
- }
854
- else if (meta.uniques.length > 0) {
855
- for (const u of meta.uniques) {
856
- if (Utils.asArray(u.properties).every(p => row[p] != null)) {
857
- where = Utils.asArray(u.properties).reduce((o, key) => {
858
- o[key] = row[key];
859
- return o;
860
- }, {});
861
- break;
862
- }
863
- }
864
- }
865
- }
866
- row = QueryHelper.processObjectParams(row);
908
+ const unique = options.onConflictFields ?? meta.props.filter(p => p.unique).map(p => p.name);
909
+ propIndex = !isRaw(unique) && unique.findIndex(p => data[p] ?? data[p.substring(0, p.indexOf('.'))] != null);
910
+ const tmp = getWhereCondition(meta, options.onConflictFields, row, where);
911
+ propIndex = tmp.propIndex;
867
912
  where = QueryHelper.processWhere({
868
- where,
913
+ where: tmp.where,
869
914
  entityName,
870
915
  metadata: this.metadata,
871
916
  platform: this.getPlatform(),
872
917
  });
873
- em.validator.validateParams(row, 'insert data');
918
+ row = QueryHelper.processObjectParams(row);
919
+ validateParams(row, 'insert data');
874
920
  allData.push(row);
875
921
  allWhere.push(where);
876
922
  }
@@ -911,7 +957,7 @@ export class EntityManager {
911
957
  const reloadFields = returning.length > 0 && !(this.getPlatform().usesReturningStatement() && res.rows?.length);
912
958
  if (options.onConflictAction === 'ignore' || (!res.rows?.length && loadPK.size > 0) || reloadFields) {
913
959
  const unique = meta.hydrateProps.filter(p => !p.lazy).map(p => p.name);
914
- const add = new Set(propIndex >= 0 ? [unique[propIndex]] : []);
960
+ const add = new Set(propIndex !== false && propIndex >= 0 ? [unique[propIndex]] : []);
915
961
  for (const cond of loadPK.values()) {
916
962
  Utils.keys(cond).forEach(key => add.add(key));
917
963
  }
@@ -923,7 +969,7 @@ export class EntityManager {
923
969
  where.$or[idx][prop] = item[prop];
924
970
  });
925
971
  });
926
- const data2 = await this.driver.find(meta.className, where, {
972
+ const data2 = await this.driver.find(meta.class, where, {
927
973
  fields: returning.concat(...add).concat(...(Array.isArray(uniqueFields) ? uniqueFields : [])),
928
974
  ctx: em.transactionContext,
929
975
  convertCustomTypes: true,
@@ -940,7 +986,7 @@ export class EntityManager {
940
986
  });
941
987
  return this.comparator.matching(entityName, cond, tmp);
942
988
  });
943
- /* v8 ignore next 3 */
989
+ /* v8 ignore next */
944
990
  if (!row) {
945
991
  throw new Error(`Cannot find matching entity for condition ${JSON.stringify(cond)}`);
946
992
  }
@@ -963,7 +1009,7 @@ export class EntityManager {
963
1009
  }, {});
964
1010
  return this.comparator.matching(entityName, cond, pk);
965
1011
  });
966
- /* v8 ignore next 3 */
1012
+ /* v8 ignore next */
967
1013
  if (!row) {
968
1014
  throw new Error(`Cannot find matching entity for condition ${JSON.stringify(cond)}`);
969
1015
  }
@@ -1076,11 +1122,11 @@ export class EntityManager {
1076
1122
  em.prepareOptions(options);
1077
1123
  let entityName;
1078
1124
  if (data === undefined) {
1079
- entityName = entityNameOrEntity.constructor.name;
1125
+ entityName = entityNameOrEntity.constructor;
1080
1126
  data = entityNameOrEntity;
1081
1127
  }
1082
1128
  else {
1083
- entityName = Utils.className(entityNameOrEntity);
1129
+ entityName = entityNameOrEntity;
1084
1130
  }
1085
1131
  if (Utils.isEntity(data)) {
1086
1132
  if (options.schema && helper(data).getSchema() == null) {
@@ -1099,7 +1145,7 @@ export class EntityManager {
1099
1145
  return cs.getPrimaryKey();
1100
1146
  }
1101
1147
  data = QueryHelper.processObjectParams(data);
1102
- em.validator.validateParams(data, 'insert data');
1148
+ validateParams(data, 'insert data');
1103
1149
  const res = await em.driver.nativeInsert(entityName, data, { ctx: em.transactionContext, ...options });
1104
1150
  return res.insertId;
1105
1151
  }
@@ -1111,11 +1157,11 @@ export class EntityManager {
1111
1157
  em.prepareOptions(options);
1112
1158
  let entityName;
1113
1159
  if (data === undefined) {
1114
- entityName = entityNameOrEntities[0].constructor.name;
1160
+ entityName = entityNameOrEntities[0].constructor;
1115
1161
  data = entityNameOrEntities;
1116
1162
  }
1117
1163
  else {
1118
- entityName = Utils.className(entityNameOrEntities);
1164
+ entityName = entityNameOrEntities;
1119
1165
  }
1120
1166
  if (data.length === 0) {
1121
1167
  return [];
@@ -1139,7 +1185,7 @@ export class EntityManager {
1139
1185
  return css.map(cs => cs.getPrimaryKey());
1140
1186
  }
1141
1187
  data = data.map(row => QueryHelper.processObjectParams(row));
1142
- data.forEach(row => em.validator.validateParams(row, 'insert data'));
1188
+ data.forEach(row => validateParams(row, 'insert data'));
1143
1189
  const res = await em.driver.nativeInsertMany(entityName, data, { ctx: em.transactionContext, ...options });
1144
1190
  if (res.insertedIds) {
1145
1191
  return res.insertedIds;
@@ -1152,12 +1198,12 @@ export class EntityManager {
1152
1198
  async nativeUpdate(entityName, where, data, options = {}) {
1153
1199
  const em = this.getContext(false);
1154
1200
  em.prepareOptions(options);
1155
- entityName = Utils.className(entityName);
1201
+ await em.processUnionWhere(entityName, options, 'update');
1156
1202
  data = QueryHelper.processObjectParams(data);
1157
1203
  where = await em.processWhere(entityName, where, { ...options, convertCustomTypes: false }, 'update');
1158
- em.validator.validateParams(data, 'update data');
1159
- em.validator.validateParams(where, 'update condition');
1160
- const res = await em.driver.nativeUpdate(entityName, where, data, { ctx: em.transactionContext, ...options });
1204
+ validateParams(data, 'update data');
1205
+ validateParams(where, 'update condition');
1206
+ const res = await em.driver.nativeUpdate(entityName, where, data, { ctx: em.transactionContext, em, ...options });
1161
1207
  return res.affectedRows;
1162
1208
  }
1163
1209
  /**
@@ -1166,28 +1212,29 @@ export class EntityManager {
1166
1212
  async nativeDelete(entityName, where, options = {}) {
1167
1213
  const em = this.getContext(false);
1168
1214
  em.prepareOptions(options);
1169
- entityName = Utils.className(entityName);
1215
+ await em.processUnionWhere(entityName, options, 'delete');
1170
1216
  where = await em.processWhere(entityName, where, options, 'delete');
1171
- em.validator.validateParams(where, 'delete condition');
1172
- const res = await em.driver.nativeDelete(entityName, where, { ctx: em.transactionContext, ...options });
1217
+ validateParams(where, 'delete condition');
1218
+ const res = await em.driver.nativeDelete(entityName, where, { ctx: em.transactionContext, em, ...options });
1173
1219
  return res.affectedRows;
1174
1220
  }
1175
1221
  /**
1176
1222
  * Maps raw database result to an entity and merges it to this EntityManager.
1177
1223
  */
1178
1224
  map(entityName, result, options = {}) {
1179
- entityName = Utils.className(entityName);
1180
1225
  const meta = this.metadata.get(entityName);
1181
1226
  const data = this.driver.mapResult(result, meta);
1182
- Object.keys(data).forEach(k => {
1227
+ for (const k of Object.keys(data)) {
1183
1228
  const prop = meta.properties[k];
1184
- if (prop && prop.kind === ReferenceKind.SCALAR && SCALAR_TYPES.includes(prop.runtimeType) && !prop.customType && (prop.setter || !prop.getter)) {
1185
- data[k] = this.validator.validateProperty(prop, data[k], data);
1229
+ if (prop?.kind === ReferenceKind.SCALAR && SCALAR_TYPES.has(prop.runtimeType) && !prop.customType && (prop.setter || !prop.getter)) {
1230
+ validateProperty(prop, data[k], data);
1186
1231
  }
1187
- });
1232
+ }
1188
1233
  return this.merge(entityName, data, {
1189
1234
  convertCustomTypes: true,
1190
- refresh: true, ...options,
1235
+ refresh: true,
1236
+ validate: false,
1237
+ ...options,
1191
1238
  });
1192
1239
  }
1193
1240
  /**
@@ -1196,32 +1243,19 @@ export class EntityManager {
1196
1243
  */
1197
1244
  merge(entityName, data, options = {}) {
1198
1245
  if (Utils.isEntity(entityName)) {
1199
- return this.merge(entityName.constructor.name, entityName, data);
1246
+ return this.merge(entityName.constructor, entityName, data);
1200
1247
  }
1201
1248
  const em = options.disableContextResolution ? this : this.getContext();
1202
1249
  options.schema ??= em._schema;
1203
1250
  options.validate ??= true;
1204
1251
  options.cascade ??= true;
1205
- entityName = Utils.className(entityName);
1206
- if (options.validate) {
1207
- em.validator.validatePrimaryKey(data, em.metadata.get(entityName));
1208
- }
1252
+ validatePrimaryKey(data, em.metadata.get(entityName));
1209
1253
  let entity = em.unitOfWork.tryGetById(entityName, data, options.schema, false);
1210
1254
  if (entity && helper(entity).__managed && helper(entity).__initialized && !options.refresh) {
1211
1255
  return entity;
1212
1256
  }
1213
- const meta = em.metadata.find(entityName);
1214
- const childMeta = em.metadata.getByDiscriminatorColumn(meta, data);
1215
1257
  const dataIsEntity = Utils.isEntity(data);
1216
- if (options.keepIdentity && entity && dataIsEntity && entity !== data) {
1217
- helper(entity).__data = helper(data).__data;
1218
- helper(entity).__originalEntityData = helper(data).__originalEntityData;
1219
- return entity;
1220
- }
1221
1258
  entity = dataIsEntity ? data : em.entityFactory.create(entityName, data, { merge: true, ...options });
1222
- if (options.validate) {
1223
- em.validator.validate(entity, data, childMeta ?? meta);
1224
- }
1225
1259
  const visited = options.cascade ? undefined : new Set([entity]);
1226
1260
  em.unitOfWork.merge(entity, visited);
1227
1261
  return entity;
@@ -1248,6 +1282,7 @@ export class EntityManager {
1248
1282
  ...options,
1249
1283
  newEntity: !options.managed,
1250
1284
  merge: options.managed,
1285
+ normalizeAccessors: true,
1251
1286
  });
1252
1287
  options.persist ??= em.config.get('persistOnCreate');
1253
1288
  if (options.persist && !this.getMetadata(entityName).embeddable) {
@@ -1267,7 +1302,7 @@ export class EntityManager {
1267
1302
  getReference(entityName, id, options = {}) {
1268
1303
  options.schema ??= this.schema;
1269
1304
  options.convertCustomTypes ??= false;
1270
- const meta = this.metadata.get(Utils.className(entityName));
1305
+ const meta = this.metadata.get(entityName);
1271
1306
  if (Utils.isPrimaryKey(id)) {
1272
1307
  if (meta.compositePK) {
1273
1308
  throw ValidationError.invalidCompositeIdentifier(meta);
@@ -1288,7 +1323,6 @@ export class EntityManager {
1288
1323
  // Shallow copy options since the object will be modified when deleting orderBy
1289
1324
  options = { ...options };
1290
1325
  em.prepareOptions(options);
1291
- entityName = Utils.className(entityName);
1292
1326
  await em.tryFlush(entityName, options);
1293
1327
  where = await em.processWhere(entityName, where, options, 'read');
1294
1328
  options.populate = await em.preparePopulate(entityName, options);
@@ -1297,9 +1331,10 @@ export class EntityManager {
1297
1331
  const meta = em.metadata.find(entityName);
1298
1332
  options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
1299
1333
  options.populateWhere = this.createPopulateWhere({ ...where }, options);
1300
- options.populateFilter = await this.getJoinedFilters(meta, { ...where }, options);
1301
- em.validator.validateParams(where);
1334
+ options.populateFilter = await this.getJoinedFilters(meta, options);
1335
+ validateParams(where);
1302
1336
  delete options.orderBy;
1337
+ await em.processUnionWhere(entityName, options, 'read');
1303
1338
  const cacheKey = em.cacheKey(entityName, options, 'em.count', where);
1304
1339
  const cached = await em.tryCache(entityName, options.cache, cacheKey);
1305
1340
  if (cached?.data !== undefined) {
@@ -1324,7 +1359,7 @@ export class EntityManager {
1324
1359
  for (const ent of entities) {
1325
1360
  if (!Utils.isEntity(ent, true)) {
1326
1361
  /* v8 ignore next */
1327
- const meta = typeof ent === 'object' ? em.metadata.find(ent.constructor.name) : undefined;
1362
+ const meta = typeof ent === 'object' ? em.metadata.find(ent.constructor) : undefined;
1328
1363
  throw ValidationError.notDiscoveredEntity(ent, meta);
1329
1364
  }
1330
1365
  // do not cascade just yet, cascading of entities in persist stack is done when flushing
@@ -1332,13 +1367,6 @@ export class EntityManager {
1332
1367
  }
1333
1368
  return this;
1334
1369
  }
1335
- /**
1336
- * Persists your entity immediately, flushing all not yet persisted changes to the database too.
1337
- * Equivalent to `em.persist(e).flush()`.
1338
- */
1339
- async persistAndFlush(entity) {
1340
- await this.persist(entity).flush();
1341
- }
1342
1370
  /**
1343
1371
  * Marks entity for removal.
1344
1372
  * A removed entity will be removed from the database at or before transaction commit or as a result of the flush operation.
@@ -1362,13 +1390,6 @@ export class EntityManager {
1362
1390
  }
1363
1391
  return em;
1364
1392
  }
1365
- /**
1366
- * Removes an entity instance immediately, flushing all not yet persisted changes to the database too.
1367
- * Equivalent to `em.remove(e).flush()`
1368
- */
1369
- async removeAndFlush(entity) {
1370
- await this.remove(entity).flush();
1371
- }
1372
1393
  /**
1373
1394
  * Flushes all changes to objects that have been queued up to now to the database.
1374
1395
  * This effectively synchronizes the in-memory state of managed objects with the database.
@@ -1382,7 +1403,6 @@ export class EntityManager {
1382
1403
  async tryFlush(entityName, options) {
1383
1404
  const em = this.getContext();
1384
1405
  const flushMode = options.flushMode ?? em.flushMode ?? em.config.get('flushMode');
1385
- entityName = Utils.className(entityName);
1386
1406
  const meta = em.metadata.get(entityName);
1387
1407
  if (flushMode === FlushMode.COMMIT) {
1388
1408
  return;
@@ -1401,7 +1421,6 @@ export class EntityManager {
1401
1421
  * Checks whether given property can be populated on the entity.
1402
1422
  */
1403
1423
  canPopulate(entityName, property) {
1404
- entityName = Utils.className(entityName);
1405
1424
  // eslint-disable-next-line prefer-const
1406
1425
  let [p, ...parts] = property.split('.');
1407
1426
  const meta = this.metadata.find(entityName);
@@ -1411,12 +1430,11 @@ export class EntityManager {
1411
1430
  if (p.includes(':')) {
1412
1431
  p = p.split(':', 2)[0];
1413
1432
  }
1414
- const ret = p in meta.root.properties;
1415
- if (!ret) {
1416
- return !!this.metadata.find(property)?.pivotTable;
1417
- }
1433
+ // For TPT inheritance, check the entity's own properties, not just the root's
1434
+ // For STI, meta.properties includes all properties anyway
1435
+ const ret = p in meta.properties;
1418
1436
  if (parts.length > 0) {
1419
- return this.canPopulate((meta.root.properties)[p].type, parts.join('.'));
1437
+ return this.canPopulate(meta.properties[p].targetMeta.class, parts.join('.'));
1420
1438
  }
1421
1439
  return ret;
1422
1440
  }
@@ -1430,8 +1448,8 @@ export class EntityManager {
1430
1448
  }
1431
1449
  const em = this.getContext();
1432
1450
  em.prepareOptions(options);
1433
- const entityName = arr[0].constructor.name;
1434
- const preparedPopulate = await em.preparePopulate(entityName, { populate: populate }, options.validate);
1451
+ const entityName = arr[0].constructor;
1452
+ const preparedPopulate = await em.preparePopulate(entityName, { populate: populate, filters: options.filters }, options.validate);
1435
1453
  await em.entityLoader.populate(entityName, arr, preparedPopulate, options);
1436
1454
  return entities;
1437
1455
  }
@@ -1559,7 +1577,6 @@ export class EntityManager {
1559
1577
  */
1560
1578
  getMetadata(entityName) {
1561
1579
  if (entityName) {
1562
- entityName = Utils.className(entityName);
1563
1580
  return this.metadata.get(entityName);
1564
1581
  }
1565
1582
  return this.metadata;
@@ -1588,8 +1605,8 @@ export class EntityManager {
1588
1605
  lockTableAliases: options.lockTableAliases,
1589
1606
  });
1590
1607
  }
1591
- const preparedPopulate = await this.preparePopulate(meta.className, options);
1592
- await this.entityLoader.populate(meta.className, [entity], preparedPopulate, {
1608
+ const preparedPopulate = await this.preparePopulate(meta.class, options);
1609
+ await this.entityLoader.populate(meta.class, [entity], preparedPopulate, {
1593
1610
  ...options,
1594
1611
  ...this.getPopulateWhere(where, options),
1595
1612
  orderBy: options.populateOrderBy ?? options.orderBy,
@@ -1609,6 +1626,7 @@ export class EntityManager {
1609
1626
  return ret;
1610
1627
  }, []);
1611
1628
  }
1629
+ /** @internal */
1612
1630
  async preparePopulate(entityName, options, validate = true) {
1613
1631
  if (options.populate === false) {
1614
1632
  return [];
@@ -1649,13 +1667,13 @@ export class EntityManager {
1649
1667
  options.populate = pruneToOneRelations(meta, this.buildFields(options.fields));
1650
1668
  }
1651
1669
  if (!options.populate) {
1652
- const populate = this.entityLoader.normalizePopulate(entityName, [], options.strategy);
1670
+ const populate = this.entityLoader.normalizePopulate(entityName, [], options.strategy, true, options.exclude);
1653
1671
  await this.autoJoinRefsForFilters(meta, { ...options, populate });
1654
1672
  return populate;
1655
1673
  }
1656
1674
  if (typeof options.populate !== 'boolean') {
1657
1675
  options.populate = Utils.asArray(options.populate).map(field => {
1658
- /* v8 ignore next 3 */
1676
+ /* v8 ignore next */
1659
1677
  if (typeof field === 'boolean' || field === PopulatePath.ALL) {
1660
1678
  return [{ field: meta.primaryKeys[0], strategy: options.strategy, all: !!field }]; //
1661
1679
  }
@@ -1665,24 +1683,27 @@ export class EntityManager {
1665
1683
  options.flags.push(QueryFlag.INFER_POPULATE);
1666
1684
  return [];
1667
1685
  }
1668
- if (Utils.isString(field)) {
1686
+ if (typeof field === 'string') {
1669
1687
  return [{ field, strategy: options.strategy }];
1670
1688
  }
1671
1689
  return [field];
1672
1690
  }).flat();
1673
1691
  }
1674
- const populate = this.entityLoader.normalizePopulate(entityName, options.populate, options.strategy);
1692
+ const populate = this.entityLoader.normalizePopulate(entityName, options.populate, options.strategy, true, options.exclude);
1675
1693
  const invalid = populate.find(({ field }) => !this.canPopulate(entityName, field));
1676
1694
  if (validate && invalid) {
1677
1695
  throw ValidationError.invalidPropertyName(entityName, invalid.field);
1678
1696
  }
1679
1697
  await this.autoJoinRefsForFilters(meta, { ...options, populate });
1680
- return populate.map(field => {
1698
+ for (const field of populate) {
1681
1699
  // force select-in strategy when populating all relations as otherwise we could cause infinite loops when self-referencing
1682
1700
  const all = field.all ?? (Array.isArray(options.populate) && options.populate.includes('*'));
1683
1701
  field.strategy = all ? LoadStrategy.SELECT_IN : (options.strategy ?? field.strategy);
1684
- return field;
1685
- });
1702
+ }
1703
+ if (options.populateHints) {
1704
+ applyPopulateHints(populate, options.populateHints);
1705
+ }
1706
+ return populate;
1686
1707
  }
1687
1708
  /**
1688
1709
  * when the entity is found in identity map, we check if it was partially loaded or we are trying to populate
@@ -1702,7 +1723,7 @@ export class EntityManager {
1702
1723
  return !inlineEmbedded && !prop.lazy && !helper(entity).__loadedProperties.has(prop.name);
1703
1724
  });
1704
1725
  }
1705
- if (autoRefresh) {
1726
+ if (autoRefresh || options.filters) {
1706
1727
  return true;
1707
1728
  }
1708
1729
  if (Array.isArray(options.populate)) {
@@ -1726,7 +1747,7 @@ export class EntityManager {
1726
1747
  for (const k of ['ctx', 'strategy', 'flushMode', 'logging', 'loggerContext']) {
1727
1748
  delete opts[k];
1728
1749
  }
1729
- return [entityName, method, opts, where];
1750
+ return [Utils.className(entityName), method, opts, where];
1730
1751
  }
1731
1752
  /**
1732
1753
  * @internal
@@ -1743,21 +1764,17 @@ export class EntityManager {
1743
1764
  return { key: cacheKey, data: cached };
1744
1765
  }
1745
1766
  let data;
1767
+ const createOptions = {
1768
+ merge: true,
1769
+ convertCustomTypes: false,
1770
+ refresh,
1771
+ recomputeSnapshot: true,
1772
+ };
1746
1773
  if (Array.isArray(cached) && merge) {
1747
- data = cached.map(item => em.entityFactory.create(entityName, item, {
1748
- merge: true,
1749
- convertCustomTypes: true,
1750
- refresh,
1751
- recomputeSnapshot: true,
1752
- }));
1774
+ data = cached.map(item => em.entityFactory.create(entityName, item, createOptions));
1753
1775
  }
1754
1776
  else if (Utils.isObject(cached) && merge) {
1755
- data = em.entityFactory.create(entityName, cached, {
1756
- merge: true,
1757
- convertCustomTypes: true,
1758
- refresh,
1759
- recomputeSnapshot: true,
1760
- });
1777
+ data = em.entityFactory.create(entityName, cached, createOptions);
1761
1778
  }
1762
1779
  else {
1763
1780
  data = cached;
@@ -1772,7 +1789,7 @@ export class EntityManager {
1772
1789
  config ??= this.config.get('resultCache').global;
1773
1790
  if (config) {
1774
1791
  const em = this.getContext();
1775
- const expiration = Array.isArray(config) ? config[1] : (Utils.isNumber(config) ? config : undefined);
1792
+ const expiration = Array.isArray(config) ? config[1] : (typeof config === 'number' ? config : undefined);
1776
1793
  await em.resultCache.set(key.key, data instanceof Function ? data() : data, '', expiration);
1777
1794
  }
1778
1795
  }
@@ -1805,6 +1822,20 @@ export class EntityManager {
1805
1822
  set schema(schema) {
1806
1823
  this.getContext(false)._schema = schema ?? undefined;
1807
1824
  }
1825
+ /** @internal */
1826
+ async getDataLoader(type) {
1827
+ const em = this.getContext();
1828
+ if (em.loaders[type]) {
1829
+ return em.loaders[type];
1830
+ }
1831
+ const { DataloaderUtils } = await import('@mikro-orm/core/dataloader');
1832
+ const DataLoader = await DataloaderUtils.getDataLoader();
1833
+ switch (type) {
1834
+ case 'ref': return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getRefBatchLoadFn(em)));
1835
+ case '1:m': return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getColBatchLoadFn(em)));
1836
+ case 'm:n': return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getManyToManyColBatchLoadFn(em)));
1837
+ }
1838
+ }
1808
1839
  /**
1809
1840
  * Returns the ID of this EntityManager. Respects the context, so global EM will give you the contextual ID
1810
1841
  * if executed inside request context handler.
@@ -1813,7 +1844,7 @@ export class EntityManager {
1813
1844
  return this.getContext(false)._id;
1814
1845
  }
1815
1846
  /** @ignore */
1816
- [inspect.custom]() {
1847
+ [Symbol.for('nodejs.util.inspect.custom')]() {
1817
1848
  return `[EntityManager<${this.id}>]`;
1818
1849
  }
1819
1850
  }