@mikro-orm/core 7.0.0-dev.32 → 7.0.0-dev.321

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 (216) hide show
  1. package/EntityManager.d.ts +71 -63
  2. package/EntityManager.js +365 -283
  3. package/MikroORM.d.ts +44 -35
  4. package/MikroORM.js +109 -142
  5. package/README.md +7 -4
  6. package/cache/FileCacheAdapter.d.ts +1 -2
  7. package/cache/FileCacheAdapter.js +19 -14
  8. package/cache/GeneratedCacheAdapter.d.ts +0 -1
  9. package/cache/GeneratedCacheAdapter.js +0 -2
  10. package/cache/index.d.ts +1 -2
  11. package/cache/index.js +0 -2
  12. package/connections/Connection.d.ts +12 -5
  13. package/connections/Connection.js +37 -15
  14. package/drivers/DatabaseDriver.d.ts +25 -18
  15. package/drivers/DatabaseDriver.js +144 -45
  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 +473 -115
  21. package/entity/EntityAssigner.js +37 -25
  22. package/entity/EntityFactory.d.ts +7 -1
  23. package/entity/EntityFactory.js +116 -64
  24. package/entity/EntityHelper.d.ts +2 -2
  25. package/entity/EntityHelper.js +69 -27
  26. package/entity/EntityLoader.d.ts +11 -10
  27. package/entity/EntityLoader.js +264 -102
  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 +2 -6
  33. package/entity/Reference.js +52 -19
  34. package/entity/WrappedEntity.d.ts +3 -8
  35. package/entity/WrappedEntity.js +6 -7
  36. package/entity/defineEntity.d.ts +525 -311
  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 +46 -11
  42. package/entity/validators.d.ts +11 -0
  43. package/entity/validators.js +66 -0
  44. package/enums.d.ts +8 -6
  45. package/enums.js +13 -17
  46. package/errors.d.ts +20 -10
  47. package/errors.js +63 -31
  48. package/events/EventManager.d.ts +2 -1
  49. package/events/EventManager.js +24 -13
  50. package/events/index.d.ts +1 -1
  51. package/events/index.js +0 -1
  52. package/exceptions.js +9 -2
  53. package/hydration/Hydrator.js +1 -2
  54. package/hydration/ObjectHydrator.d.ts +4 -4
  55. package/hydration/ObjectHydrator.js +105 -46
  56. package/index.d.ts +2 -2
  57. package/index.js +1 -2
  58. package/logging/DefaultLogger.d.ts +1 -1
  59. package/logging/DefaultLogger.js +3 -4
  60. package/logging/SimpleLogger.d.ts +1 -1
  61. package/logging/colors.d.ts +1 -1
  62. package/logging/colors.js +5 -7
  63. package/logging/index.d.ts +2 -1
  64. package/logging/index.js +1 -1
  65. package/logging/inspect.d.ts +2 -0
  66. package/logging/inspect.js +11 -0
  67. package/metadata/EntitySchema.d.ts +47 -23
  68. package/metadata/EntitySchema.js +103 -34
  69. package/metadata/MetadataDiscovery.d.ts +64 -9
  70. package/metadata/MetadataDiscovery.js +867 -354
  71. package/metadata/MetadataProvider.d.ts +11 -2
  72. package/metadata/MetadataProvider.js +71 -2
  73. package/metadata/MetadataStorage.d.ts +13 -11
  74. package/metadata/MetadataStorage.js +72 -41
  75. package/metadata/MetadataValidator.d.ts +32 -9
  76. package/metadata/MetadataValidator.js +214 -44
  77. package/metadata/discover-entities.d.ts +5 -0
  78. package/metadata/discover-entities.js +40 -0
  79. package/metadata/index.d.ts +1 -1
  80. package/metadata/index.js +0 -1
  81. package/metadata/types.d.ts +577 -0
  82. package/metadata/types.js +1 -0
  83. package/naming-strategy/AbstractNamingStrategy.d.ts +16 -4
  84. package/naming-strategy/AbstractNamingStrategy.js +26 -5
  85. package/naming-strategy/EntityCaseNamingStrategy.d.ts +3 -3
  86. package/naming-strategy/EntityCaseNamingStrategy.js +6 -5
  87. package/naming-strategy/MongoNamingStrategy.d.ts +3 -3
  88. package/naming-strategy/MongoNamingStrategy.js +6 -6
  89. package/naming-strategy/NamingStrategy.d.ts +28 -4
  90. package/naming-strategy/UnderscoreNamingStrategy.d.ts +3 -3
  91. package/naming-strategy/UnderscoreNamingStrategy.js +6 -6
  92. package/naming-strategy/index.d.ts +1 -1
  93. package/naming-strategy/index.js +0 -1
  94. package/not-supported.d.ts +2 -0
  95. package/not-supported.js +8 -0
  96. package/package.json +47 -36
  97. package/platforms/ExceptionConverter.js +1 -1
  98. package/platforms/Platform.d.ts +33 -15
  99. package/platforms/Platform.js +125 -69
  100. package/serialization/EntitySerializer.d.ts +6 -3
  101. package/serialization/EntitySerializer.js +53 -29
  102. package/serialization/EntityTransformer.js +33 -21
  103. package/serialization/SerializationContext.d.ts +6 -6
  104. package/serialization/SerializationContext.js +4 -4
  105. package/types/ArrayType.d.ts +1 -1
  106. package/types/ArrayType.js +2 -3
  107. package/types/BigIntType.js +1 -1
  108. package/types/BlobType.d.ts +0 -1
  109. package/types/BlobType.js +0 -3
  110. package/types/BooleanType.d.ts +1 -0
  111. package/types/BooleanType.js +3 -0
  112. package/types/DecimalType.js +2 -2
  113. package/types/DoubleType.js +1 -1
  114. package/types/EnumArrayType.js +1 -2
  115. package/types/JsonType.d.ts +1 -1
  116. package/types/JsonType.js +7 -2
  117. package/types/TinyIntType.js +1 -1
  118. package/types/Type.d.ts +2 -4
  119. package/types/Type.js +3 -3
  120. package/types/Uint8ArrayType.d.ts +0 -1
  121. package/types/Uint8ArrayType.js +1 -4
  122. package/types/UuidType.d.ts +2 -0
  123. package/types/UuidType.js +14 -2
  124. package/types/index.d.ts +3 -2
  125. package/typings.d.ts +427 -170
  126. package/typings.js +100 -45
  127. package/unit-of-work/ChangeSet.d.ts +4 -6
  128. package/unit-of-work/ChangeSet.js +8 -9
  129. package/unit-of-work/ChangeSetComputer.d.ts +3 -8
  130. package/unit-of-work/ChangeSetComputer.js +49 -26
  131. package/unit-of-work/ChangeSetPersister.d.ts +13 -12
  132. package/unit-of-work/ChangeSetPersister.js +107 -44
  133. package/unit-of-work/CommitOrderCalculator.d.ts +12 -10
  134. package/unit-of-work/CommitOrderCalculator.js +17 -15
  135. package/unit-of-work/IdentityMap.d.ts +12 -0
  136. package/unit-of-work/IdentityMap.js +39 -1
  137. package/unit-of-work/UnitOfWork.d.ts +34 -4
  138. package/unit-of-work/UnitOfWork.js +294 -107
  139. package/utils/AbstractMigrator.d.ts +101 -0
  140. package/utils/AbstractMigrator.js +303 -0
  141. package/utils/AbstractSchemaGenerator.d.ts +5 -5
  142. package/utils/AbstractSchemaGenerator.js +30 -18
  143. package/utils/AsyncContext.d.ts +6 -0
  144. package/utils/AsyncContext.js +42 -0
  145. package/utils/Configuration.d.ts +796 -211
  146. package/utils/Configuration.js +160 -197
  147. package/utils/ConfigurationLoader.d.ts +1 -52
  148. package/utils/ConfigurationLoader.js +1 -330
  149. package/utils/Cursor.d.ts +0 -3
  150. package/utils/Cursor.js +29 -14
  151. package/utils/DataloaderUtils.d.ts +10 -5
  152. package/utils/DataloaderUtils.js +42 -22
  153. package/utils/EntityComparator.d.ts +16 -9
  154. package/utils/EntityComparator.js +202 -96
  155. package/utils/QueryHelper.d.ts +34 -7
  156. package/utils/QueryHelper.js +183 -72
  157. package/utils/RawQueryFragment.d.ts +28 -34
  158. package/utils/RawQueryFragment.js +37 -72
  159. package/utils/RequestContext.js +2 -2
  160. package/utils/TransactionContext.js +2 -2
  161. package/utils/TransactionManager.js +11 -7
  162. package/utils/Utils.d.ts +16 -127
  163. package/utils/Utils.js +106 -401
  164. package/utils/clone.js +13 -23
  165. package/utils/env-vars.d.ts +7 -0
  166. package/utils/env-vars.js +98 -0
  167. package/utils/fs-utils.d.ts +34 -0
  168. package/utils/fs-utils.js +193 -0
  169. package/utils/index.d.ts +1 -3
  170. package/utils/index.js +1 -3
  171. package/utils/upsert-utils.d.ts +9 -4
  172. package/utils/upsert-utils.js +51 -5
  173. package/decorators/Check.d.ts +0 -3
  174. package/decorators/Check.js +0 -13
  175. package/decorators/CreateRequestContext.d.ts +0 -3
  176. package/decorators/CreateRequestContext.js +0 -32
  177. package/decorators/Embeddable.d.ts +0 -8
  178. package/decorators/Embeddable.js +0 -11
  179. package/decorators/Embedded.d.ts +0 -12
  180. package/decorators/Embedded.js +0 -18
  181. package/decorators/Entity.d.ts +0 -33
  182. package/decorators/Entity.js +0 -12
  183. package/decorators/Enum.d.ts +0 -9
  184. package/decorators/Enum.js +0 -16
  185. package/decorators/Filter.d.ts +0 -2
  186. package/decorators/Filter.js +0 -8
  187. package/decorators/Formula.d.ts +0 -4
  188. package/decorators/Formula.js +0 -15
  189. package/decorators/Indexed.d.ts +0 -19
  190. package/decorators/Indexed.js +0 -20
  191. package/decorators/ManyToMany.d.ts +0 -42
  192. package/decorators/ManyToMany.js +0 -14
  193. package/decorators/ManyToOne.d.ts +0 -34
  194. package/decorators/ManyToOne.js +0 -14
  195. package/decorators/OneToMany.d.ts +0 -28
  196. package/decorators/OneToMany.js +0 -17
  197. package/decorators/OneToOne.d.ts +0 -28
  198. package/decorators/OneToOne.js +0 -7
  199. package/decorators/PrimaryKey.d.ts +0 -8
  200. package/decorators/PrimaryKey.js +0 -20
  201. package/decorators/Property.d.ts +0 -250
  202. package/decorators/Property.js +0 -32
  203. package/decorators/Transactional.d.ts +0 -14
  204. package/decorators/Transactional.js +0 -28
  205. package/decorators/hooks.d.ts +0 -16
  206. package/decorators/hooks.js +0 -47
  207. package/decorators/index.d.ts +0 -17
  208. package/decorators/index.js +0 -17
  209. package/entity/ArrayCollection.d.ts +0 -118
  210. package/entity/ArrayCollection.js +0 -407
  211. package/entity/EntityValidator.d.ts +0 -19
  212. package/entity/EntityValidator.js +0 -150
  213. package/metadata/ReflectMetadataProvider.d.ts +0 -8
  214. package/metadata/ReflectMetadataProvider.js +0 -44
  215. package/utils/resolveContextProvider.d.ts +0 -10
  216. 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,11 +114,16 @@ 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 || {};
135
- options.populate = await em.preparePopulate(entityName, options);
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
+ }
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);
138
129
  const cached = await em.tryCache(entityName, options.cache, cacheKey, options.refresh, true);
@@ -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,23 +299,34 @@ 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
- where[meta.root.discriminatorColumn] = children.length > 0 ? { $in: [meta.discriminatorValue, ...children.map(c => c.discriminatorValue)] } : meta.discriminatorValue;
326
+ where[meta.root.discriminatorColumn] =
327
+ children.length > 0
328
+ ? { $in: [meta.discriminatorValue, ...children.map(c => c.discriminatorValue)] }
329
+ : meta.discriminatorValue;
274
330
  return where;
275
331
  }
276
332
  createPopulateWhere(cond, options) {
@@ -284,60 +340,78 @@ export class EntityManager {
284
340
  }
285
341
  return ret;
286
342
  }
287
- async getJoinedFilters(meta, cond, options) {
343
+ async getJoinedFilters(meta, options) {
344
+ // If user provided populateFilter, merge it with computed filters
345
+ const userFilter = options.populateFilter;
346
+ if (!this.config.get('filtersOnRelations') || !options.populate) {
347
+ return userFilter;
348
+ }
288
349
  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;
350
+ for (const hint of options.populate) {
351
+ const field = hint.field.split(':')[0];
352
+ const prop = meta.properties[field];
353
+ const strategy = getLoadingStrategy(prop.strategy || hint.strategy || options.strategy || this.config.get('loadStrategy'), prop.kind);
354
+ const joined = strategy === LoadStrategy.JOINED && prop.kind !== ReferenceKind.SCALAR;
355
+ if (!joined && !hint.filter) {
356
+ continue;
357
+ }
358
+ const filters = QueryHelper.mergePropertyFilters(prop.filters, options.filters);
359
+ const where = await this.applyFilters(prop.targetMeta.class, {}, filters, 'read', {
360
+ ...options,
361
+ populate: hint.children,
362
+ });
363
+ const where2 = await this.getJoinedFilters(prop.targetMeta, {
364
+ ...options,
365
+ filters,
366
+ populate: hint.children,
367
+ populateWhere: PopulateHint.ALL,
368
+ });
369
+ if (Utils.hasObjectKeys(where)) {
370
+ ret[field] = ret[field] ? { $and: [where, ret[field]] } : where;
371
+ }
372
+ if (where2 && Utils.hasObjectKeys(where2)) {
373
+ if (ret[field]) {
374
+ Utils.merge(ret[field], where2);
302
375
  }
303
- if (Utils.hasObjectKeys(where2)) {
304
- if (ret[field]) {
305
- Utils.merge(ret[field], where2);
306
- }
307
- else {
308
- ret[field] = where2;
309
- }
376
+ else {
377
+ ret[field] = where2;
310
378
  }
311
379
  }
312
380
  }
313
- return ret;
381
+ // Merge user-provided populateFilter with computed filters
382
+ if (userFilter) {
383
+ Utils.merge(ret, userFilter);
384
+ }
385
+ return Utils.hasObjectKeys(ret) ? ret : undefined;
314
386
  }
315
387
  /**
316
388
  * 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
389
  */
318
- async autoJoinRefsForFilters(meta, options) {
319
- if (!meta || !this.config.get('autoJoinRefsForFilters')) {
390
+ async autoJoinRefsForFilters(meta, options, parent) {
391
+ if (!meta || !this.config.get('autoJoinRefsForFilters') || options.filters === false) {
320
392
  return;
321
393
  }
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
394
  const ret = options.populate;
327
- for (const prop of props) {
328
- const cond = await this.applyFilters(prop.type, {}, options.filters ?? {}, 'read', options);
395
+ for (const prop of meta.relations) {
396
+ if (prop.object ||
397
+ ![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) ||
398
+ !((options.fields?.length ?? 0) === 0 ||
399
+ options.fields?.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`))) ||
400
+ (parent?.class === prop.targetMeta.root.class && parent.propName === prop.inversedBy)) {
401
+ continue;
402
+ }
403
+ options = { ...options, filters: QueryHelper.mergePropertyFilters(prop.filters, options.filters) };
404
+ const cond = await this.applyFilters(prop.targetMeta.class, {}, options.filters, 'read', options);
329
405
  if (!Utils.isEmpty(cond)) {
330
406
  const populated = options.populate.filter(({ field }) => field.split(':')[0] === prop.name);
331
407
  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
- }
408
+ for (const hint of populated) {
409
+ if (!hint.all) {
410
+ hint.filter = true;
411
+ }
412
+ const strategy = getLoadingStrategy(prop.strategy || hint.strategy || options.strategy || this.config.get('loadStrategy'), prop.kind);
413
+ if (hint.field === `${prop.name}:ref` || (hint.filter && strategy === LoadStrategy.JOINED)) {
414
+ found = true;
341
415
  }
342
416
  }
343
417
  if (!found) {
@@ -345,22 +419,25 @@ export class EntityManager {
345
419
  }
346
420
  }
347
421
  }
422
+ for (const hint of ret) {
423
+ const [field, ref] = hint.field.split(':');
424
+ const prop = meta?.properties[field];
425
+ if (prop && !ref) {
426
+ hint.children ??= [];
427
+ await this.autoJoinRefsForFilters(prop.targetMeta, { ...options, populate: hint.children }, { class: meta.root.class, propName: prop.name });
428
+ }
429
+ }
348
430
  }
349
431
  /**
350
432
  * @internal
351
433
  */
352
434
  async applyFilters(entityName, where, options, type, findOptions) {
353
- const meta = this.metadata.find(entityName);
435
+ const meta = this.metadata.get(entityName);
354
436
  const filters = [];
355
437
  const ret = [];
356
- if (!meta) {
357
- return where;
358
- }
359
438
  const active = new Set();
360
439
  const push = (source) => {
361
- const activeFilters = QueryHelper
362
- .getActiveFilters(entityName, options, source)
363
- .filter(f => !active.has(f.name));
440
+ const activeFilters = QueryHelper.getActiveFilters(meta, options, source).filter(f => !active.has(f.name));
364
441
  filters.push(...activeFilters);
365
442
  activeFilters.forEach(f => active.add(f.name));
366
443
  };
@@ -374,24 +451,29 @@ export class EntityManager {
374
451
  let cond;
375
452
  if (filter.cond instanceof Function) {
376
453
  // @ts-ignore
377
- const args = Utils.isPlainObject(options[filter.name]) ? options[filter.name] : this.getContext().filterParams[filter.name];
454
+ // oxfmt-ignore
455
+ const args = Utils.isPlainObject(options?.[filter.name]) ? options[filter.name] : this.getContext().filterParams[filter.name];
378
456
  if (!args && filter.cond.length > 0 && filter.args !== false) {
379
457
  throw new Error(`No arguments provided for filter '${filter.name}'`);
380
458
  }
381
- cond = await filter.cond(args, type, this, findOptions, entityName);
459
+ cond = await filter.cond(args, type, this, findOptions, Utils.className(entityName));
382
460
  }
383
461
  else {
384
462
  cond = filter.cond;
385
463
  }
386
- ret.push(QueryHelper.processWhere({
464
+ cond = QueryHelper.processWhere({
387
465
  where: cond,
388
466
  entityName,
389
467
  metadata: this.metadata,
390
468
  platform: this.driver.getPlatform(),
391
469
  aliased: type === 'read',
392
- }));
470
+ });
471
+ if (filter.strict) {
472
+ Object.defineProperty(cond, '__strict', { value: filter.strict, enumerable: false });
473
+ }
474
+ ret.push(cond);
393
475
  }
394
- const conds = [...ret, where].filter(c => Utils.hasObjectKeys(c));
476
+ const conds = [...ret, where].filter(c => Utils.hasObjectKeys(c) || Raw.hasObjectFragments(c));
395
477
  return conds.length > 1 ? { $and: conds } : conds[0];
396
478
  }
397
479
  /**
@@ -402,12 +484,10 @@ export class EntityManager {
402
484
  const em = this.getContext(false);
403
485
  await em.tryFlush(entityName, options);
404
486
  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
- });
487
+ return Promise.all([
488
+ em.find(entityName, where, options),
489
+ em.count(entityName, where, options),
490
+ ]);
411
491
  }
412
492
  /**
413
493
  * Calls `em.find()` and `em.count()` with the same arguments (where applicable) and returns the results as {@apilink Cursor} object.
@@ -423,21 +503,21 @@ export class EntityManager {
423
503
  * - POJO/entity instance
424
504
  *
425
505
  * ```ts
426
- * const currentCursor = await em.findByCursor(User, {}, {
506
+ * const currentCursor = await em.findByCursor(User, {
427
507
  * first: 10,
428
508
  * after: previousCursor, // cursor instance
429
509
  * orderBy: { id: 'desc' },
430
510
  * });
431
511
  *
432
512
  * // to fetch next page
433
- * const nextCursor = await em.findByCursor(User, {}, {
513
+ * const nextCursor = await em.findByCursor(User, {
434
514
  * first: 10,
435
515
  * after: currentCursor.endCursor, // opaque string
436
516
  * orderBy: { id: 'desc' },
437
517
  * });
438
518
  *
439
519
  * // to fetch next page
440
- * const nextCursor2 = await em.findByCursor(User, {}, {
520
+ * const nextCursor2 = await em.findByCursor(User, {
441
521
  * first: 10,
442
522
  * after: { id: lastSeenId }, // entity-like POJO
443
523
  * orderBy: { id: 'desc' },
@@ -465,16 +545,16 @@ export class EntityManager {
465
545
  * }
466
546
  * ```
467
547
  */
468
- async findByCursor(entityName, where, options) {
548
+ async findByCursor(entityName, options) {
469
549
  const em = this.getContext(false);
470
- entityName = Utils.className(entityName);
471
550
  options.overfetch ??= true;
472
- if (Utils.isEmpty(options.orderBy)) {
551
+ options.where ??= {};
552
+ if (Utils.isEmpty(options.orderBy) && !Raw.hasObjectFragments(options.orderBy)) {
473
553
  throw new Error('Explicit `orderBy` option required');
474
554
  }
475
555
  const [entities, count] = options.includeCount !== false
476
- ? await em.findAndCount(entityName, where, options)
477
- : [await em.find(entityName, where, options)];
556
+ ? await em.findAndCount(entityName, options.where, options)
557
+ : [await em.find(entityName, options.where, options)];
478
558
  return new Cursor(entities, count, options, this.metadata.get(entityName));
479
559
  }
480
560
  /**
@@ -486,9 +566,9 @@ export class EntityManager {
486
566
  const ret = await this.refresh(entity, options);
487
567
  if (!ret) {
488
568
  options.failHandler ??= this.config.get('findOneOrFailHandler');
489
- const entityName = entity.constructor.name;
490
- const where = helper(entity).getPrimaryKey();
491
- throw options.failHandler(entityName, where);
569
+ const wrapped = helper(entity);
570
+ const where = wrapped.getPrimaryKey();
571
+ throw options.failHandler(wrapped.__meta.className, where);
492
572
  }
493
573
  return ret;
494
574
  }
@@ -499,9 +579,9 @@ export class EntityManager {
499
579
  */
500
580
  async refresh(entity, options = {}) {
501
581
  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,
582
+ const wrapped = helper(entity);
583
+ const reloaded = await fork.findOne(wrapped.__meta.class, entity, {
584
+ schema: wrapped.__schema,
505
585
  ...options,
506
586
  flushMode: FlushMode.COMMIT,
507
587
  });
@@ -512,16 +592,22 @@ export class EntityManager {
512
592
  }
513
593
  let found = false;
514
594
  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);
595
+ const ref = em.getReference(e.constructor, helper(e).getPrimaryKey());
596
+ const data = helper(e).serialize({ ignoreSerializers: true, includeHidden: true, convertCustomTypes: false });
597
+ em.config
598
+ .getHydrator(this.metadata)
599
+ .hydrate(ref, helper(ref).__meta, data, em.entityFactory, 'full', false, false);
600
+ Utils.merge(helper(ref).__originalEntityData, this.comparator.prepareEntity(e));
519
601
  found ||= ref === entity;
520
602
  }
521
603
  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);
604
+ const data = helper(reloaded).serialize({
605
+ ignoreSerializers: true,
606
+ includeHidden: true,
607
+ convertCustomTypes: true,
608
+ });
609
+ em.config.getHydrator(this.metadata).hydrate(entity, wrapped.__meta, data, em.entityFactory, 'full', false, true);
610
+ Utils.merge(wrapped.__originalEntityData, this.comparator.prepareEntity(reloaded));
525
611
  }
526
612
  return entity;
527
613
  }
@@ -537,7 +623,6 @@ export class EntityManager {
537
623
  return ret;
538
624
  }
539
625
  const em = this.getContext();
540
- entityName = Utils.className(entityName);
541
626
  em.prepareOptions(options);
542
627
  let entity = em.unitOfWork.tryGetById(entityName, where, options.schema);
543
628
  // query for a not managed entity which is already in the identity map as it
@@ -549,14 +634,14 @@ export class EntityManager {
549
634
  await em.tryFlush(entityName, options);
550
635
  const meta = em.metadata.get(entityName);
551
636
  where = await em.processWhere(entityName, where, options, 'read');
552
- em.validator.validateEmptyWhere(where);
637
+ validateEmptyWhere(where);
553
638
  em.checkLockRequirements(options.lockMode, meta);
554
- const isOptimisticLocking = !Utils.isDefined(options.lockMode) || options.lockMode === LockMode.OPTIMISTIC;
639
+ const isOptimisticLocking = options.lockMode == null || options.lockMode === LockMode.OPTIMISTIC;
555
640
  if (entity && !em.shouldRefresh(meta, entity, options) && isOptimisticLocking) {
556
641
  return em.lockAndPopulate(meta, entity, where, options);
557
642
  }
558
- em.validator.validateParams(where);
559
- options.populate = await em.preparePopulate(entityName, options);
643
+ validateParams(where);
644
+ options.populate = (await em.preparePopulate(entityName, options));
560
645
  const cacheKey = em.cacheKey(entityName, options, 'em.findOne', where);
561
646
  const cached = await em.tryCache(entityName, options.cache, cacheKey, options.refresh, true);
562
647
  if (cached?.data !== undefined) {
@@ -574,7 +659,8 @@ export class EntityManager {
574
659
  // save the original hint value so we know it was infer/all
575
660
  options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
576
661
  options.populateWhere = this.createPopulateWhere({ ...where }, options);
577
- options.populateFilter = await this.getJoinedFilters(meta, { ...where }, options);
662
+ options.populateFilter = await this.getJoinedFilters(meta, options);
663
+ await em.processUnionWhere(entityName, options, 'read');
578
664
  const data = await em.driver.findOne(entityName, where, {
579
665
  ctx: em.transactionContext,
580
666
  em,
@@ -615,10 +701,10 @@ export class EntityManager {
615
701
  if (!entity || isStrictViolation) {
616
702
  const key = options.strict ? 'findExactlyOneOrFailHandler' : 'findOneOrFailHandler';
617
703
  options.failHandler ??= this.config.get(key);
618
- entityName = Utils.className(entityName);
704
+ const name = Utils.className(entityName);
619
705
  /* v8 ignore next */
620
706
  where = Utils.isEntity(where) ? helper(where).getPrimaryKey() : where;
621
- throw options.failHandler(entityName, where);
707
+ throw options.failHandler(name, where);
622
708
  }
623
709
  return entity;
624
710
  }
@@ -658,11 +744,11 @@ export class EntityManager {
658
744
  let where;
659
745
  let entity = null;
660
746
  if (data === undefined) {
661
- entityName = entityNameOrEntity.constructor.name;
747
+ entityName = entityNameOrEntity.constructor;
662
748
  data = entityNameOrEntity;
663
749
  }
664
750
  else {
665
- entityName = Utils.className(entityNameOrEntity);
751
+ entityName = entityNameOrEntity;
666
752
  }
667
753
  const meta = this.metadata.get(entityName);
668
754
  const convertCustomTypes = !Utils.isEntity(data);
@@ -685,26 +771,9 @@ export class EntityManager {
685
771
  }
686
772
  }
687
773
  }
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
- }
774
+ where = getWhereCondition(meta, options.onConflictFields, data, where).where;
706
775
  data = QueryHelper.processObjectParams(data);
707
- em.validator.validateParams(data, 'insert data');
776
+ validateParams(data, 'insert data');
708
777
  if (em.eventManager.hasListeners(EventType.beforeUpsert, meta)) {
709
778
  await em.eventManager.dispatchEvent(EventType.beforeUpsert, { entity: data, em, meta }, meta);
710
779
  }
@@ -720,9 +789,12 @@ export class EntityManager {
720
789
  initialized: true,
721
790
  schema: options.schema,
722
791
  });
723
- const uniqueFields = options.onConflictFields ?? (Utils.isPlainObject(where) ? Object.keys(where) : meta.primaryKeys);
792
+ const uniqueFields = options.onConflictFields ??
793
+ (Utils.isPlainObject(where) ? Object.keys(where) : meta.primaryKeys);
724
794
  const returning = getOnConflictReturningFields(meta, data, uniqueFields, options);
725
- if (options.onConflictAction === 'ignore' || !helper(entity).hasPrimaryKey() || (returning.length > 0 && !(this.getPlatform().usesReturningStatement() && ret.row))) {
795
+ if (options.onConflictAction === 'ignore' ||
796
+ !helper(entity).hasPrimaryKey() ||
797
+ (returning.length > 0 && !(this.getPlatform().usesReturningStatement() && ret.row))) {
726
798
  const where = {};
727
799
  if (Array.isArray(uniqueFields)) {
728
800
  for (const prop of uniqueFields) {
@@ -742,7 +814,7 @@ export class EntityManager {
742
814
  where[meta.primaryKeys[0]] = ret.insertId;
743
815
  }
744
816
  }
745
- const data2 = await this.driver.findOne(meta.className, where, {
817
+ const data2 = await this.driver.findOne(meta.class, where, {
746
818
  fields: returning,
747
819
  ctx: em.transactionContext,
748
820
  convertCustomTypes: true,
@@ -797,11 +869,11 @@ export class EntityManager {
797
869
  let entityName;
798
870
  let propIndex;
799
871
  if (data === undefined) {
800
- entityName = entityNameOrEntity[0].constructor.name;
872
+ entityName = entityNameOrEntity[0].constructor;
801
873
  data = entityNameOrEntity;
802
874
  }
803
875
  else {
804
- entityName = Utils.className(entityNameOrEntity);
876
+ entityName = entityNameOrEntity;
805
877
  }
806
878
  const batchSize = options.batchSize ?? this.config.get('batchSize');
807
879
  if (data.length > batchSize) {
@@ -845,32 +917,20 @@ export class EntityManager {
845
917
  }
846
918
  }
847
919
  }
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);
920
+ const unique = options.onConflictFields ?? meta.props.filter(p => p.unique).map(p => p.name);
921
+ propIndex =
922
+ !isRaw(unique) &&
923
+ unique.findIndex(p => data[p] ?? data[p.substring(0, p.indexOf('.'))] != null);
924
+ const tmp = getWhereCondition(meta, options.onConflictFields, row, where);
925
+ propIndex = tmp.propIndex;
867
926
  where = QueryHelper.processWhere({
868
- where,
927
+ where: tmp.where,
869
928
  entityName,
870
929
  metadata: this.metadata,
871
930
  platform: this.getPlatform(),
872
931
  });
873
- em.validator.validateParams(row, 'insert data');
932
+ row = QueryHelper.processObjectParams(row);
933
+ validateParams(row, 'insert data');
874
934
  allData.push(row);
875
935
  allWhere.push(where);
876
936
  }
@@ -893,12 +953,16 @@ export class EntityManager {
893
953
  entitiesByData.clear();
894
954
  const loadPK = new Map();
895
955
  allData.forEach((row, i) => {
896
- em.unitOfWork.getChangeSetPersister().mapReturnedValues(Utils.isEntity(data[i]) ? data[i] : null, Utils.isEntity(data[i]) ? {} : data[i], res.rows?.[i], meta, true);
897
- const entity = Utils.isEntity(data[i]) ? data[i] : em.entityFactory.create(entityName, row, {
898
- refresh: true,
899
- initialized: true,
900
- schema: options.schema,
901
- });
956
+ em.unitOfWork
957
+ .getChangeSetPersister()
958
+ .mapReturnedValues(Utils.isEntity(data[i]) ? data[i] : null, Utils.isEntity(data[i]) ? {} : data[i], res.rows?.[i], meta, true);
959
+ const entity = Utils.isEntity(data[i])
960
+ ? data[i]
961
+ : em.entityFactory.create(entityName, row, {
962
+ refresh: true,
963
+ initialized: true,
964
+ schema: options.schema,
965
+ });
902
966
  if (!helper(entity).hasPrimaryKey()) {
903
967
  loadPK.set(entity, allWhere[i]);
904
968
  }
@@ -906,12 +970,15 @@ export class EntityManager {
906
970
  entitiesByData.set(row, entity);
907
971
  });
908
972
  // skip if we got the PKs via returning statement (`rows`)
973
+ // oxfmt-ignore
909
974
  const uniqueFields = options.onConflictFields ?? (Utils.isPlainObject(allWhere[0]) ? Object.keys(allWhere[0]).flatMap(key => Utils.splitPrimaryKeys(key)) : meta.primaryKeys);
910
975
  const returning = getOnConflictReturningFields(meta, data[0], uniqueFields, options);
911
976
  const reloadFields = returning.length > 0 && !(this.getPlatform().usesReturningStatement() && res.rows?.length);
912
977
  if (options.onConflictAction === 'ignore' || (!res.rows?.length && loadPK.size > 0) || reloadFields) {
913
978
  const unique = meta.hydrateProps.filter(p => !p.lazy).map(p => p.name);
914
- const add = new Set(propIndex >= 0 ? [unique[propIndex]] : []);
979
+ const add = new Set(propIndex !== false && propIndex >= 0
980
+ ? [unique[propIndex]]
981
+ : []);
915
982
  for (const cond of loadPK.values()) {
916
983
  Utils.keys(cond).forEach(key => add.add(key));
917
984
  }
@@ -923,8 +990,10 @@ export class EntityManager {
923
990
  where.$or[idx][prop] = item[prop];
924
991
  });
925
992
  });
926
- const data2 = await this.driver.find(meta.className, where, {
927
- fields: returning.concat(...add).concat(...(Array.isArray(uniqueFields) ? uniqueFields : [])),
993
+ const data2 = await this.driver.find(meta.class, where, {
994
+ fields: returning
995
+ .concat(...add)
996
+ .concat(...(Array.isArray(uniqueFields) ? uniqueFields : [])),
928
997
  ctx: em.transactionContext,
929
998
  convertCustomTypes: true,
930
999
  connectionType: 'write',
@@ -940,7 +1009,7 @@ export class EntityManager {
940
1009
  });
941
1010
  return this.comparator.matching(entityName, cond, tmp);
942
1011
  });
943
- /* v8 ignore next 3 */
1012
+ /* v8 ignore next */
944
1013
  if (!row) {
945
1014
  throw new Error(`Cannot find matching entity for condition ${JSON.stringify(cond)}`);
946
1015
  }
@@ -963,7 +1032,7 @@ export class EntityManager {
963
1032
  }, {});
964
1033
  return this.comparator.matching(entityName, cond, pk);
965
1034
  });
966
- /* v8 ignore next 3 */
1035
+ /* v8 ignore next */
967
1036
  if (!row) {
968
1037
  throw new Error(`Cannot find matching entity for condition ${JSON.stringify(cond)}`);
969
1038
  }
@@ -1076,11 +1145,11 @@ export class EntityManager {
1076
1145
  em.prepareOptions(options);
1077
1146
  let entityName;
1078
1147
  if (data === undefined) {
1079
- entityName = entityNameOrEntity.constructor.name;
1148
+ entityName = entityNameOrEntity.constructor;
1080
1149
  data = entityNameOrEntity;
1081
1150
  }
1082
1151
  else {
1083
- entityName = Utils.className(entityNameOrEntity);
1152
+ entityName = entityNameOrEntity;
1084
1153
  }
1085
1154
  if (Utils.isEntity(data)) {
1086
1155
  if (options.schema && helper(data).getSchema() == null) {
@@ -1099,8 +1168,11 @@ export class EntityManager {
1099
1168
  return cs.getPrimaryKey();
1100
1169
  }
1101
1170
  data = QueryHelper.processObjectParams(data);
1102
- em.validator.validateParams(data, 'insert data');
1103
- const res = await em.driver.nativeInsert(entityName, data, { ctx: em.transactionContext, ...options });
1171
+ validateParams(data, 'insert data');
1172
+ const res = await em.driver.nativeInsert(entityName, data, {
1173
+ ctx: em.transactionContext,
1174
+ ...options,
1175
+ });
1104
1176
  return res.insertId;
1105
1177
  }
1106
1178
  /**
@@ -1111,11 +1183,11 @@ export class EntityManager {
1111
1183
  em.prepareOptions(options);
1112
1184
  let entityName;
1113
1185
  if (data === undefined) {
1114
- entityName = entityNameOrEntities[0].constructor.name;
1186
+ entityName = entityNameOrEntities[0].constructor;
1115
1187
  data = entityNameOrEntities;
1116
1188
  }
1117
1189
  else {
1118
- entityName = Utils.className(entityNameOrEntities);
1190
+ entityName = entityNameOrEntities;
1119
1191
  }
1120
1192
  if (data.length === 0) {
1121
1193
  return [];
@@ -1139,8 +1211,11 @@ export class EntityManager {
1139
1211
  return css.map(cs => cs.getPrimaryKey());
1140
1212
  }
1141
1213
  data = data.map(row => QueryHelper.processObjectParams(row));
1142
- data.forEach(row => em.validator.validateParams(row, 'insert data'));
1143
- const res = await em.driver.nativeInsertMany(entityName, data, { ctx: em.transactionContext, ...options });
1214
+ data.forEach(row => validateParams(row, 'insert data'));
1215
+ const res = await em.driver.nativeInsertMany(entityName, data, {
1216
+ ctx: em.transactionContext,
1217
+ ...options,
1218
+ });
1144
1219
  if (res.insertedIds) {
1145
1220
  return res.insertedIds;
1146
1221
  }
@@ -1152,12 +1227,16 @@ export class EntityManager {
1152
1227
  async nativeUpdate(entityName, where, data, options = {}) {
1153
1228
  const em = this.getContext(false);
1154
1229
  em.prepareOptions(options);
1155
- entityName = Utils.className(entityName);
1230
+ await em.processUnionWhere(entityName, options, 'update');
1156
1231
  data = QueryHelper.processObjectParams(data);
1157
1232
  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 });
1233
+ validateParams(data, 'update data');
1234
+ validateParams(where, 'update condition');
1235
+ const res = await em.driver.nativeUpdate(entityName, where, data, {
1236
+ ctx: em.transactionContext,
1237
+ em,
1238
+ ...options,
1239
+ });
1161
1240
  return res.affectedRows;
1162
1241
  }
1163
1242
  /**
@@ -1166,28 +1245,36 @@ export class EntityManager {
1166
1245
  async nativeDelete(entityName, where, options = {}) {
1167
1246
  const em = this.getContext(false);
1168
1247
  em.prepareOptions(options);
1169
- entityName = Utils.className(entityName);
1170
- 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 });
1248
+ await em.processUnionWhere(entityName, options, 'delete');
1249
+ where = (await em.processWhere(entityName, where, options, 'delete'));
1250
+ validateParams(where, 'delete condition');
1251
+ const res = await em.driver.nativeDelete(entityName, where, {
1252
+ ctx: em.transactionContext,
1253
+ em,
1254
+ ...options,
1255
+ });
1173
1256
  return res.affectedRows;
1174
1257
  }
1175
1258
  /**
1176
1259
  * Maps raw database result to an entity and merges it to this EntityManager.
1177
1260
  */
1178
1261
  map(entityName, result, options = {}) {
1179
- entityName = Utils.className(entityName);
1180
1262
  const meta = this.metadata.get(entityName);
1181
1263
  const data = this.driver.mapResult(result, meta);
1182
- Object.keys(data).forEach(k => {
1264
+ for (const k of Object.keys(data)) {
1183
1265
  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);
1266
+ if (prop?.kind === ReferenceKind.SCALAR &&
1267
+ SCALAR_TYPES.has(prop.runtimeType) &&
1268
+ !prop.customType &&
1269
+ (prop.setter || !prop.getter)) {
1270
+ validateProperty(prop, data[k], data);
1186
1271
  }
1187
- });
1272
+ }
1188
1273
  return this.merge(entityName, data, {
1189
1274
  convertCustomTypes: true,
1190
- refresh: true, ...options,
1275
+ refresh: true,
1276
+ validate: false,
1277
+ ...options,
1191
1278
  });
1192
1279
  }
1193
1280
  /**
@@ -1196,32 +1283,21 @@ export class EntityManager {
1196
1283
  */
1197
1284
  merge(entityName, data, options = {}) {
1198
1285
  if (Utils.isEntity(entityName)) {
1199
- return this.merge(entityName.constructor.name, entityName, data);
1286
+ return this.merge(entityName.constructor, entityName, data);
1200
1287
  }
1201
1288
  const em = options.disableContextResolution ? this : this.getContext();
1202
1289
  options.schema ??= em._schema;
1203
1290
  options.validate ??= true;
1204
1291
  options.cascade ??= true;
1205
- entityName = Utils.className(entityName);
1206
- if (options.validate) {
1207
- em.validator.validatePrimaryKey(data, em.metadata.get(entityName));
1208
- }
1292
+ validatePrimaryKey(data, em.metadata.get(entityName));
1209
1293
  let entity = em.unitOfWork.tryGetById(entityName, data, options.schema, false);
1210
1294
  if (entity && helper(entity).__managed && helper(entity).__initialized && !options.refresh) {
1211
1295
  return entity;
1212
1296
  }
1213
- const meta = em.metadata.find(entityName);
1214
- const childMeta = em.metadata.getByDiscriminatorColumn(meta, data);
1215
1297
  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
- entity = dataIsEntity ? data : em.entityFactory.create(entityName, data, { merge: true, ...options });
1222
- if (options.validate) {
1223
- em.validator.validate(entity, data, childMeta ?? meta);
1224
- }
1298
+ entity = dataIsEntity
1299
+ ? data
1300
+ : em.entityFactory.create(entityName, data, { merge: true, ...options });
1225
1301
  const visited = options.cascade ? undefined : new Set([entity]);
1226
1302
  em.unitOfWork.merge(entity, visited);
1227
1303
  return entity;
@@ -1248,6 +1324,7 @@ export class EntityManager {
1248
1324
  ...options,
1249
1325
  newEntity: !options.managed,
1250
1326
  merge: options.managed,
1327
+ normalizeAccessors: true,
1251
1328
  });
1252
1329
  options.persist ??= em.config.get('persistOnCreate');
1253
1330
  if (options.persist && !this.getMetadata(entityName).embeddable) {
@@ -1267,7 +1344,7 @@ export class EntityManager {
1267
1344
  getReference(entityName, id, options = {}) {
1268
1345
  options.schema ??= this.schema;
1269
1346
  options.convertCustomTypes ??= false;
1270
- const meta = this.metadata.get(Utils.className(entityName));
1347
+ const meta = this.metadata.get(entityName);
1271
1348
  if (Utils.isPrimaryKey(id)) {
1272
1349
  if (meta.compositePK) {
1273
1350
  throw ValidationError.invalidCompositeIdentifier(meta);
@@ -1288,18 +1365,18 @@ export class EntityManager {
1288
1365
  // Shallow copy options since the object will be modified when deleting orderBy
1289
1366
  options = { ...options };
1290
1367
  em.prepareOptions(options);
1291
- entityName = Utils.className(entityName);
1292
1368
  await em.tryFlush(entityName, options);
1293
1369
  where = await em.processWhere(entityName, where, options, 'read');
1294
- options.populate = await em.preparePopulate(entityName, options);
1370
+ options.populate = (await em.preparePopulate(entityName, options));
1295
1371
  options = { ...options };
1296
1372
  // save the original hint value so we know it was infer/all
1297
1373
  const meta = em.metadata.find(entityName);
1298
1374
  options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
1299
1375
  options.populateWhere = this.createPopulateWhere({ ...where }, options);
1300
- options.populateFilter = await this.getJoinedFilters(meta, { ...where }, options);
1301
- em.validator.validateParams(where);
1376
+ options.populateFilter = await this.getJoinedFilters(meta, options);
1377
+ validateParams(where);
1302
1378
  delete options.orderBy;
1379
+ await em.processUnionWhere(entityName, options, 'read');
1303
1380
  const cacheKey = em.cacheKey(entityName, options, 'em.count', where);
1304
1381
  const cached = await em.tryCache(entityName, options.cache, cacheKey);
1305
1382
  if (cached?.data !== undefined) {
@@ -1324,7 +1401,7 @@ export class EntityManager {
1324
1401
  for (const ent of entities) {
1325
1402
  if (!Utils.isEntity(ent, true)) {
1326
1403
  /* v8 ignore next */
1327
- const meta = typeof ent === 'object' ? em.metadata.find(ent.constructor.name) : undefined;
1404
+ const meta = typeof ent === 'object' ? em.metadata.find(ent.constructor) : undefined;
1328
1405
  throw ValidationError.notDiscoveredEntity(ent, meta);
1329
1406
  }
1330
1407
  // do not cascade just yet, cascading of entities in persist stack is done when flushing
@@ -1332,13 +1409,6 @@ export class EntityManager {
1332
1409
  }
1333
1410
  return this;
1334
1411
  }
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
1412
  /**
1343
1413
  * Marks entity for removal.
1344
1414
  * A removed entity will be removed from the database at or before transaction commit or as a result of the flush operation.
@@ -1362,13 +1432,6 @@ export class EntityManager {
1362
1432
  }
1363
1433
  return em;
1364
1434
  }
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
1435
  /**
1373
1436
  * Flushes all changes to objects that have been queued up to now to the database.
1374
1437
  * This effectively synchronizes the in-memory state of managed objects with the database.
@@ -1382,7 +1445,6 @@ export class EntityManager {
1382
1445
  async tryFlush(entityName, options) {
1383
1446
  const em = this.getContext();
1384
1447
  const flushMode = options.flushMode ?? em.flushMode ?? em.config.get('flushMode');
1385
- entityName = Utils.className(entityName);
1386
1448
  const meta = em.metadata.get(entityName);
1387
1449
  if (flushMode === FlushMode.COMMIT) {
1388
1450
  return;
@@ -1401,7 +1463,6 @@ export class EntityManager {
1401
1463
  * Checks whether given property can be populated on the entity.
1402
1464
  */
1403
1465
  canPopulate(entityName, property) {
1404
- entityName = Utils.className(entityName);
1405
1466
  // eslint-disable-next-line prefer-const
1406
1467
  let [p, ...parts] = property.split('.');
1407
1468
  const meta = this.metadata.find(entityName);
@@ -1411,12 +1472,11 @@ export class EntityManager {
1411
1472
  if (p.includes(':')) {
1412
1473
  p = p.split(':', 2)[0];
1413
1474
  }
1414
- const ret = p in meta.root.properties;
1415
- if (!ret) {
1416
- return !!this.metadata.find(property)?.pivotTable;
1417
- }
1475
+ // For TPT inheritance, check the entity's own properties, not just the root's
1476
+ // For STI, meta.properties includes all properties anyway
1477
+ const ret = p in meta.properties;
1418
1478
  if (parts.length > 0) {
1419
- return this.canPopulate((meta.root.properties)[p].type, parts.join('.'));
1479
+ return this.canPopulate(meta.properties[p].targetMeta.class, parts.join('.'));
1420
1480
  }
1421
1481
  return ret;
1422
1482
  }
@@ -1430,8 +1490,8 @@ export class EntityManager {
1430
1490
  }
1431
1491
  const em = this.getContext();
1432
1492
  em.prepareOptions(options);
1433
- const entityName = arr[0].constructor.name;
1434
- const preparedPopulate = await em.preparePopulate(entityName, { populate: populate }, options.validate);
1493
+ const entityName = arr[0].constructor;
1494
+ const preparedPopulate = await em.preparePopulate(entityName, { populate: populate, filters: options.filters }, options.validate);
1435
1495
  await em.entityLoader.populate(entityName, arr, preparedPopulate, options);
1436
1496
  return entities;
1437
1497
  }
@@ -1454,7 +1514,8 @@ export class EntityManager {
1454
1514
  em.config.set('allowGlobalContext', true);
1455
1515
  const fork = new em.constructor(em.config, em.driver, em.metadata, options.useContext, eventManager);
1456
1516
  fork.setFlushMode(options.flushMode ?? em.flushMode);
1457
- fork.disableTransactions = options.disableTransactions ?? this.disableTransactions ?? this.config.get('disableTransactions');
1517
+ fork.disableTransactions =
1518
+ options.disableTransactions ?? this.disableTransactions ?? this.config.get('disableTransactions');
1458
1519
  em.config.set('allowGlobalContext', allowGlobalContext);
1459
1520
  if (options.keepTransactionContext) {
1460
1521
  fork.transactionContext = em.transactionContext;
@@ -1559,7 +1620,6 @@ export class EntityManager {
1559
1620
  */
1560
1621
  getMetadata(entityName) {
1561
1622
  if (entityName) {
1562
- entityName = Utils.className(entityName);
1563
1623
  return this.metadata.get(entityName);
1564
1624
  }
1565
1625
  return this.metadata;
@@ -1588,8 +1648,8 @@ export class EntityManager {
1588
1648
  lockTableAliases: options.lockTableAliases,
1589
1649
  });
1590
1650
  }
1591
- const preparedPopulate = await this.preparePopulate(meta.className, options);
1592
- await this.entityLoader.populate(meta.className, [entity], preparedPopulate, {
1651
+ const preparedPopulate = await this.preparePopulate(meta.class, options);
1652
+ await this.entityLoader.populate(meta.class, [entity], preparedPopulate, {
1593
1653
  ...options,
1594
1654
  ...this.getPopulateWhere(where, options),
1595
1655
  orderBy: options.populateOrderBy ?? options.orderBy,
@@ -1609,6 +1669,7 @@ export class EntityManager {
1609
1669
  return ret;
1610
1670
  }, []);
1611
1671
  }
1672
+ /** @internal */
1612
1673
  async preparePopulate(entityName, options, validate = true) {
1613
1674
  if (options.populate === false) {
1614
1675
  return [];
@@ -1621,11 +1682,14 @@ export class EntityManager {
1621
1682
  const ret = [];
1622
1683
  for (let field of fields) {
1623
1684
  if (field === PopulatePath.ALL || field.startsWith(`${PopulatePath.ALL}.`)) {
1624
- ret.push(...meta.props.filter(prop => prop.lazy || [ReferenceKind.SCALAR, ReferenceKind.EMBEDDED].includes(prop.kind)).map(prop => prop.name));
1685
+ ret.push(...meta.props
1686
+ .filter(prop => prop.lazy || [ReferenceKind.SCALAR, ReferenceKind.EMBEDDED].includes(prop.kind))
1687
+ .map(prop => prop.name));
1625
1688
  continue;
1626
1689
  }
1627
1690
  field = field.split(':')[0];
1628
- if (!field.includes('.') && ![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(meta.properties[field].kind)) {
1691
+ if (!field.includes('.') &&
1692
+ ![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(meta.properties[field].kind)) {
1629
1693
  ret.push(field);
1630
1694
  continue;
1631
1695
  }
@@ -1649,13 +1713,14 @@ export class EntityManager {
1649
1713
  options.populate = pruneToOneRelations(meta, this.buildFields(options.fields));
1650
1714
  }
1651
1715
  if (!options.populate) {
1652
- const populate = this.entityLoader.normalizePopulate(entityName, [], options.strategy);
1716
+ const populate = this.entityLoader.normalizePopulate(entityName, [], options.strategy, true, options.exclude);
1653
1717
  await this.autoJoinRefsForFilters(meta, { ...options, populate });
1654
1718
  return populate;
1655
1719
  }
1656
1720
  if (typeof options.populate !== 'boolean') {
1657
- options.populate = Utils.asArray(options.populate).map(field => {
1658
- /* v8 ignore next 3 */
1721
+ options.populate = Utils.asArray(options.populate)
1722
+ .map(field => {
1723
+ /* v8 ignore next */
1659
1724
  if (typeof field === 'boolean' || field === PopulatePath.ALL) {
1660
1725
  return [{ field: meta.primaryKeys[0], strategy: options.strategy, all: !!field }]; //
1661
1726
  }
@@ -1665,24 +1730,28 @@ export class EntityManager {
1665
1730
  options.flags.push(QueryFlag.INFER_POPULATE);
1666
1731
  return [];
1667
1732
  }
1668
- if (Utils.isString(field)) {
1733
+ if (typeof field === 'string') {
1669
1734
  return [{ field, strategy: options.strategy }];
1670
1735
  }
1671
1736
  return [field];
1672
- }).flat();
1737
+ })
1738
+ .flat();
1673
1739
  }
1674
- const populate = this.entityLoader.normalizePopulate(entityName, options.populate, options.strategy);
1740
+ const populate = this.entityLoader.normalizePopulate(entityName, options.populate, options.strategy, true, options.exclude);
1675
1741
  const invalid = populate.find(({ field }) => !this.canPopulate(entityName, field));
1676
1742
  if (validate && invalid) {
1677
1743
  throw ValidationError.invalidPropertyName(entityName, invalid.field);
1678
1744
  }
1679
1745
  await this.autoJoinRefsForFilters(meta, { ...options, populate });
1680
- return populate.map(field => {
1746
+ for (const field of populate) {
1681
1747
  // force select-in strategy when populating all relations as otherwise we could cause infinite loops when self-referencing
1682
1748
  const all = field.all ?? (Array.isArray(options.populate) && options.populate.includes('*'));
1683
1749
  field.strategy = all ? LoadStrategy.SELECT_IN : (options.strategy ?? field.strategy);
1684
- return field;
1685
- });
1750
+ }
1751
+ if (options.populateHints) {
1752
+ applyPopulateHints(populate, options.populateHints);
1753
+ }
1754
+ return populate;
1686
1755
  }
1687
1756
  /**
1688
1757
  * when the entity is found in identity map, we check if it was partially loaded or we are trying to populate
@@ -1702,7 +1771,7 @@ export class EntityManager {
1702
1771
  return !inlineEmbedded && !prop.lazy && !helper(entity).__loadedProperties.has(prop.name);
1703
1772
  });
1704
1773
  }
1705
- if (autoRefresh) {
1774
+ if (autoRefresh || options.filters) {
1706
1775
  return true;
1707
1776
  }
1708
1777
  if (Array.isArray(options.populate)) {
@@ -1726,7 +1795,7 @@ export class EntityManager {
1726
1795
  for (const k of ['ctx', 'strategy', 'flushMode', 'logging', 'loggerContext']) {
1727
1796
  delete opts[k];
1728
1797
  }
1729
- return [entityName, method, opts, where];
1798
+ return [Utils.className(entityName), method, opts, where];
1730
1799
  }
1731
1800
  /**
1732
1801
  * @internal
@@ -1743,21 +1812,17 @@ export class EntityManager {
1743
1812
  return { key: cacheKey, data: cached };
1744
1813
  }
1745
1814
  let data;
1815
+ const createOptions = {
1816
+ merge: true,
1817
+ convertCustomTypes: false,
1818
+ refresh,
1819
+ recomputeSnapshot: true,
1820
+ };
1746
1821
  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
- }));
1822
+ data = cached.map(item => em.entityFactory.create(entityName, item, createOptions));
1753
1823
  }
1754
1824
  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
- });
1825
+ data = em.entityFactory.create(entityName, cached, createOptions);
1761
1826
  }
1762
1827
  else {
1763
1828
  data = cached;
@@ -1772,7 +1837,7 @@ export class EntityManager {
1772
1837
  config ??= this.config.get('resultCache').global;
1773
1838
  if (config) {
1774
1839
  const em = this.getContext();
1775
- const expiration = Array.isArray(config) ? config[1] : (Utils.isNumber(config) ? config : undefined);
1840
+ const expiration = Array.isArray(config) ? config[1] : typeof config === 'number' ? config : undefined;
1776
1841
  await em.resultCache.set(key.key, data instanceof Function ? data() : data, '', expiration);
1777
1842
  }
1778
1843
  }
@@ -1805,6 +1870,23 @@ export class EntityManager {
1805
1870
  set schema(schema) {
1806
1871
  this.getContext(false)._schema = schema ?? undefined;
1807
1872
  }
1873
+ /** @internal */
1874
+ async getDataLoader(type) {
1875
+ const em = this.getContext();
1876
+ if (em.loaders[type]) {
1877
+ return em.loaders[type];
1878
+ }
1879
+ const { DataloaderUtils } = await import('@mikro-orm/core/dataloader');
1880
+ const DataLoader = await DataloaderUtils.getDataLoader();
1881
+ switch (type) {
1882
+ case 'ref':
1883
+ return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getRefBatchLoadFn(em)));
1884
+ case '1:m':
1885
+ return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getColBatchLoadFn(em)));
1886
+ case 'm:n':
1887
+ return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getManyToManyColBatchLoadFn(em)));
1888
+ }
1889
+ }
1808
1890
  /**
1809
1891
  * Returns the ID of this EntityManager. Respects the context, so global EM will give you the contextual ID
1810
1892
  * if executed inside request context handler.
@@ -1813,7 +1895,7 @@ export class EntityManager {
1813
1895
  return this.getContext(false)._id;
1814
1896
  }
1815
1897
  /** @ignore */
1816
- [inspect.custom]() {
1898
+ [Symbol.for('nodejs.util.inspect.custom')]() {
1817
1899
  return `[EntityManager<${this.id}>]`;
1818
1900
  }
1819
1901
  }