@mikro-orm/core 7.0.0-dev.29 → 7.0.0-dev.291

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 +67 -60
  2. package/EntityManager.js +275 -257
  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 +118 -35
  16. package/drivers/IDatabaseDriver.d.ts +75 -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 +50 -17
  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 +10 -15
  94. package/platforms/Platform.js +21 -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,11 @@ 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);
154
144
  const results = await em.driver.find(entityName, where, { ctx: em.transactionContext, em, ...options });
155
145
  if (results.length === 0) {
156
146
  await em.storeCache(options.cache, cached, []);
@@ -182,6 +172,60 @@ export class EntityManager {
182
172
  }
183
173
  return unique;
184
174
  }
175
+ /**
176
+ * Finds all entities and returns an async iterable (async generator) that yields results one by one.
177
+ * The results are merged and mapped to entity instances, without adding them to the identity map.
178
+ * You can disable merging by passing the options `{ mergeResults: false }`.
179
+ * With `mergeResults` disabled, to-many collections will contain at most one item, and you will get duplicate
180
+ * root entities when there are multiple items in the populated collection.
181
+ * This is useful for processing large datasets without loading everything into memory at once.
182
+ *
183
+ * ```ts
184
+ * const stream = em.stream(Book, { populate: ['author'] });
185
+ *
186
+ * for await (const book of stream) {
187
+ * // book is an instance of Book entity
188
+ * console.log(book.title, book.author.name);
189
+ * }
190
+ * ```
191
+ */
192
+ async *stream(entityName, options = {}) {
193
+ const em = this.getContext();
194
+ em.prepareOptions(options);
195
+ options.strategy = 'joined';
196
+ await em.tryFlush(entityName, options);
197
+ const where = await em.processWhere(entityName, options.where ?? {}, options, 'read');
198
+ validateParams(where);
199
+ options.orderBy = options.orderBy || {};
200
+ options.populate = await em.preparePopulate(entityName, options);
201
+ const meta = this.metadata.get(entityName);
202
+ options = { ...options };
203
+ // save the original hint value so we know it was infer/all
204
+ options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
205
+ options.populateWhere = this.createPopulateWhere({ ...where }, options);
206
+ options.populateFilter = await this.getJoinedFilters(meta, options);
207
+ const stream = em.driver.stream(entityName, where, {
208
+ ctx: em.transactionContext,
209
+ mapResults: false,
210
+ ...options,
211
+ });
212
+ for await (const data of stream) {
213
+ const fork = em.fork();
214
+ const entity = fork.entityFactory.create(entityName, data, {
215
+ refresh: options.refresh,
216
+ schema: options.schema,
217
+ convertCustomTypes: true,
218
+ });
219
+ helper(entity).setSerializationContext({
220
+ populate: options.populate,
221
+ fields: options.fields,
222
+ exclude: options.exclude,
223
+ });
224
+ await fork.unitOfWork.dispatchOnLoadEvent();
225
+ fork.clear();
226
+ yield entity;
227
+ }
228
+ }
185
229
  /**
186
230
  * Finds all entities of given type, optionally matching the `where` condition provided in the `options` parameter.
187
231
  */
@@ -195,7 +239,7 @@ export class EntityManager {
195
239
  if (options.populateWhere === PopulateHint.ALL) {
196
240
  return { where: {}, populateWhere: options.populateWhere };
197
241
  }
198
- /* v8 ignore next 3 */
242
+ /* v8 ignore next */
199
243
  if (options.populateWhere === PopulateHint.INFER) {
200
244
  return { where, populateWhere: options.populateWhere };
201
245
  }
@@ -204,12 +248,12 @@ export class EntityManager {
204
248
  /**
205
249
  * Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
206
250
  */
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));
251
+ addFilter(options) {
252
+ if (options.entity) {
253
+ options.entity = Utils.asArray(options.entity).map(n => Utils.className(n));
211
254
  }
212
- this.getContext(false).filters[name] = options;
255
+ options.default ??= true;
256
+ this.getContext(false).filters[options.name] = options;
213
257
  }
214
258
  /**
215
259
  * Sets filter parameter values globally inside context defined by this entity manager.
@@ -257,18 +301,18 @@ export class EntityManager {
257
301
  // this method only handles the problem for mongo driver, SQL drivers have their implementation inside QueryBuilder
258
302
  applyDiscriminatorCondition(entityName, where) {
259
303
  const meta = this.metadata.find(entityName);
260
- if (!meta?.discriminatorValue) {
304
+ if (meta?.root.inheritanceType !== 'sti' || !meta?.discriminatorValue) {
261
305
  return where;
262
306
  }
263
- const types = Object.values(meta.root.discriminatorMap).map(cls => this.metadata.find(cls));
307
+ const types = Object.values(meta.root.discriminatorMap).map(cls => this.metadata.get(cls));
264
308
  const children = [];
265
309
  const lookUpChildren = (ret, type) => {
266
310
  const children = types.filter(meta2 => meta2.extends === type);
267
- children.forEach(m => lookUpChildren(ret, m.className));
311
+ children.forEach(m => lookUpChildren(ret, m.class));
268
312
  ret.push(...children.filter(c => c.discriminatorValue));
269
313
  return children;
270
314
  };
271
- lookUpChildren(children, meta.className);
315
+ lookUpChildren(children, meta.class);
272
316
  /* v8 ignore next */
273
317
  where[meta.root.discriminatorColumn] = children.length > 0 ? { $in: [meta.discriminatorValue, ...children.map(c => c.discriminatorValue)] } : meta.discriminatorValue;
274
318
  return where;
@@ -284,60 +328,77 @@ export class EntityManager {
284
328
  }
285
329
  return ret;
286
330
  }
287
- async getJoinedFilters(meta, cond, options) {
331
+ async getJoinedFilters(meta, options) {
332
+ // If user provided populateFilter, merge it with computed filters
333
+ const userFilter = options.populateFilter;
334
+ if (!this.config.get('filtersOnRelations') || !options.populate) {
335
+ return userFilter;
336
+ }
288
337
  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;
338
+ for (const hint of options.populate) {
339
+ const field = hint.field.split(':')[0];
340
+ const prop = meta.properties[field];
341
+ const strategy = getLoadingStrategy(prop.strategy || hint.strategy || options.strategy || this.config.get('loadStrategy'), prop.kind);
342
+ const joined = strategy === LoadStrategy.JOINED && prop.kind !== ReferenceKind.SCALAR;
343
+ if (!joined && !hint.filter) {
344
+ continue;
345
+ }
346
+ const filters = QueryHelper.mergePropertyFilters(prop.filters, options.filters);
347
+ const where = await this.applyFilters(prop.targetMeta.class, {}, filters, 'read', {
348
+ ...options,
349
+ populate: hint.children,
350
+ });
351
+ const where2 = await this.getJoinedFilters(prop.targetMeta, {
352
+ ...options,
353
+ filters,
354
+ populate: hint.children,
355
+ populateWhere: PopulateHint.ALL,
356
+ });
357
+ if (Utils.hasObjectKeys(where)) {
358
+ ret[field] = ret[field] ? { $and: [where, ret[field]] } : where;
359
+ }
360
+ if (where2 && Utils.hasObjectKeys(where2)) {
361
+ if (ret[field]) {
362
+ Utils.merge(ret[field], where2);
302
363
  }
303
- if (Utils.hasObjectKeys(where2)) {
304
- if (ret[field]) {
305
- Utils.merge(ret[field], where2);
306
- }
307
- else {
308
- ret[field] = where2;
309
- }
364
+ else {
365
+ ret[field] = where2;
310
366
  }
311
367
  }
312
368
  }
313
- return ret;
369
+ // Merge user-provided populateFilter with computed filters
370
+ if (userFilter) {
371
+ Utils.merge(ret, userFilter);
372
+ }
373
+ return Utils.hasObjectKeys(ret) ? ret : undefined;
314
374
  }
315
375
  /**
316
376
  * 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
377
  */
318
- async autoJoinRefsForFilters(meta, options) {
319
- if (!meta || !this.config.get('autoJoinRefsForFilters')) {
378
+ async autoJoinRefsForFilters(meta, options, parent) {
379
+ if (!meta || !this.config.get('autoJoinRefsForFilters') || options.filters === false) {
320
380
  return;
321
381
  }
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
382
  const ret = options.populate;
327
- for (const prop of props) {
328
- const cond = await this.applyFilters(prop.type, {}, options.filters ?? {}, 'read', options);
383
+ for (const prop of meta.relations) {
384
+ if (prop.object
385
+ || ![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)
386
+ || !((options.fields?.length ?? 0) === 0 || options.fields?.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
387
+ || (parent?.class === prop.targetMeta.root.class && parent.propName === prop.inversedBy)) {
388
+ continue;
389
+ }
390
+ options = { ...options, filters: QueryHelper.mergePropertyFilters(prop.filters, options.filters) };
391
+ const cond = await this.applyFilters(prop.targetMeta.class, {}, options.filters, 'read', options);
329
392
  if (!Utils.isEmpty(cond)) {
330
393
  const populated = options.populate.filter(({ field }) => field.split(':')[0] === prop.name);
331
394
  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
- }
395
+ for (const hint of populated) {
396
+ if (!hint.all) {
397
+ hint.filter = true;
398
+ }
399
+ const strategy = getLoadingStrategy(prop.strategy || hint.strategy || options.strategy || this.config.get('loadStrategy'), prop.kind);
400
+ if (hint.field === `${prop.name}:ref` || (hint.filter && strategy === LoadStrategy.JOINED)) {
401
+ found = true;
341
402
  }
342
403
  }
343
404
  if (!found) {
@@ -345,21 +406,26 @@ export class EntityManager {
345
406
  }
346
407
  }
347
408
  }
409
+ for (const hint of ret) {
410
+ const [field, ref] = hint.field.split(':');
411
+ const prop = meta?.properties[field];
412
+ if (prop && !ref) {
413
+ hint.children ??= [];
414
+ await this.autoJoinRefsForFilters(prop.targetMeta, { ...options, populate: hint.children }, { class: meta.root.class, propName: prop.name });
415
+ }
416
+ }
348
417
  }
349
418
  /**
350
419
  * @internal
351
420
  */
352
421
  async applyFilters(entityName, where, options, type, findOptions) {
353
- const meta = this.metadata.find(entityName);
422
+ const meta = this.metadata.get(entityName);
354
423
  const filters = [];
355
424
  const ret = [];
356
- if (!meta) {
357
- return where;
358
- }
359
425
  const active = new Set();
360
426
  const push = (source) => {
361
427
  const activeFilters = QueryHelper
362
- .getActiveFilters(entityName, options, source)
428
+ .getActiveFilters(meta, options, source)
363
429
  .filter(f => !active.has(f.name));
364
430
  filters.push(...activeFilters);
365
431
  activeFilters.forEach(f => active.add(f.name));
@@ -374,24 +440,28 @@ export class EntityManager {
374
440
  let cond;
375
441
  if (filter.cond instanceof Function) {
376
442
  // @ts-ignore
377
- const args = Utils.isPlainObject(options[filter.name]) ? options[filter.name] : this.getContext().filterParams[filter.name];
443
+ const args = Utils.isPlainObject(options?.[filter.name]) ? options[filter.name] : this.getContext().filterParams[filter.name];
378
444
  if (!args && filter.cond.length > 0 && filter.args !== false) {
379
445
  throw new Error(`No arguments provided for filter '${filter.name}'`);
380
446
  }
381
- cond = await filter.cond(args, type, this, findOptions, entityName);
447
+ cond = await filter.cond(args, type, this, findOptions, Utils.className(entityName));
382
448
  }
383
449
  else {
384
450
  cond = filter.cond;
385
451
  }
386
- ret.push(QueryHelper.processWhere({
452
+ cond = QueryHelper.processWhere({
387
453
  where: cond,
388
454
  entityName,
389
455
  metadata: this.metadata,
390
456
  platform: this.driver.getPlatform(),
391
457
  aliased: type === 'read',
392
- }));
458
+ });
459
+ if (filter.strict) {
460
+ Object.defineProperty(cond, '__strict', { value: filter.strict, enumerable: false });
461
+ }
462
+ ret.push(cond);
393
463
  }
394
- const conds = [...ret, where].filter(c => Utils.hasObjectKeys(c));
464
+ const conds = [...ret, where].filter(c => Utils.hasObjectKeys(c) || Raw.hasObjectFragments(c));
395
465
  return conds.length > 1 ? { $and: conds } : conds[0];
396
466
  }
397
467
  /**
@@ -402,12 +472,10 @@ export class EntityManager {
402
472
  const em = this.getContext(false);
403
473
  await em.tryFlush(entityName, options);
404
474
  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
- });
475
+ return Promise.all([
476
+ em.find(entityName, where, options),
477
+ em.count(entityName, where, options),
478
+ ]);
411
479
  }
412
480
  /**
413
481
  * Calls `em.find()` and `em.count()` with the same arguments (where applicable) and returns the results as {@apilink Cursor} object.
@@ -423,21 +491,21 @@ export class EntityManager {
423
491
  * - POJO/entity instance
424
492
  *
425
493
  * ```ts
426
- * const currentCursor = await em.findByCursor(User, {}, {
494
+ * const currentCursor = await em.findByCursor(User, {
427
495
  * first: 10,
428
496
  * after: previousCursor, // cursor instance
429
497
  * orderBy: { id: 'desc' },
430
498
  * });
431
499
  *
432
500
  * // to fetch next page
433
- * const nextCursor = await em.findByCursor(User, {}, {
501
+ * const nextCursor = await em.findByCursor(User, {
434
502
  * first: 10,
435
503
  * after: currentCursor.endCursor, // opaque string
436
504
  * orderBy: { id: 'desc' },
437
505
  * });
438
506
  *
439
507
  * // to fetch next page
440
- * const nextCursor2 = await em.findByCursor(User, {}, {
508
+ * const nextCursor2 = await em.findByCursor(User, {
441
509
  * first: 10,
442
510
  * after: { id: lastSeenId }, // entity-like POJO
443
511
  * orderBy: { id: 'desc' },
@@ -465,16 +533,16 @@ export class EntityManager {
465
533
  * }
466
534
  * ```
467
535
  */
468
- async findByCursor(entityName, where, options) {
536
+ async findByCursor(entityName, options) {
469
537
  const em = this.getContext(false);
470
- entityName = Utils.className(entityName);
471
538
  options.overfetch ??= true;
472
- if (Utils.isEmpty(options.orderBy)) {
539
+ options.where ??= {};
540
+ if (Utils.isEmpty(options.orderBy) && !Raw.hasObjectFragments(options.orderBy)) {
473
541
  throw new Error('Explicit `orderBy` option required');
474
542
  }
475
543
  const [entities, count] = options.includeCount !== false
476
- ? await em.findAndCount(entityName, where, options)
477
- : [await em.find(entityName, where, options)];
544
+ ? await em.findAndCount(entityName, options.where, options)
545
+ : [await em.find(entityName, options.where, options)];
478
546
  return new Cursor(entities, count, options, this.metadata.get(entityName));
479
547
  }
480
548
  /**
@@ -486,9 +554,9 @@ export class EntityManager {
486
554
  const ret = await this.refresh(entity, options);
487
555
  if (!ret) {
488
556
  options.failHandler ??= this.config.get('findOneOrFailHandler');
489
- const entityName = entity.constructor.name;
490
- const where = helper(entity).getPrimaryKey();
491
- throw options.failHandler(entityName, where);
557
+ const wrapped = helper(entity);
558
+ const where = wrapped.getPrimaryKey();
559
+ throw options.failHandler(wrapped.__meta.className, where);
492
560
  }
493
561
  return ret;
494
562
  }
@@ -499,9 +567,9 @@ export class EntityManager {
499
567
  */
500
568
  async refresh(entity, options = {}) {
501
569
  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,
570
+ const wrapped = helper(entity);
571
+ const reloaded = await fork.findOne(wrapped.__meta.class, entity, {
572
+ schema: wrapped.__schema,
505
573
  ...options,
506
574
  flushMode: FlushMode.COMMIT,
507
575
  });
@@ -512,16 +580,16 @@ export class EntityManager {
512
580
  }
513
581
  let found = false;
514
582
  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);
583
+ const ref = em.getReference(e.constructor, helper(e).getPrimaryKey());
584
+ const data = helper(e).serialize({ ignoreSerializers: true, includeHidden: true, convertCustomTypes: false });
585
+ em.config.getHydrator(this.metadata).hydrate(ref, helper(ref).__meta, data, em.entityFactory, 'full', false, false);
586
+ Utils.merge(helper(ref).__originalEntityData, this.comparator.prepareEntity(e));
519
587
  found ||= ref === entity;
520
588
  }
521
589
  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);
590
+ const data = helper(reloaded).serialize({ ignoreSerializers: true, includeHidden: true, convertCustomTypes: true });
591
+ em.config.getHydrator(this.metadata).hydrate(entity, wrapped.__meta, data, em.entityFactory, 'full', false, true);
592
+ Utils.merge(wrapped.__originalEntityData, this.comparator.prepareEntity(reloaded));
525
593
  }
526
594
  return entity;
527
595
  }
@@ -537,7 +605,6 @@ export class EntityManager {
537
605
  return ret;
538
606
  }
539
607
  const em = this.getContext();
540
- entityName = Utils.className(entityName);
541
608
  em.prepareOptions(options);
542
609
  let entity = em.unitOfWork.tryGetById(entityName, where, options.schema);
543
610
  // query for a not managed entity which is already in the identity map as it
@@ -549,13 +616,13 @@ export class EntityManager {
549
616
  await em.tryFlush(entityName, options);
550
617
  const meta = em.metadata.get(entityName);
551
618
  where = await em.processWhere(entityName, where, options, 'read');
552
- em.validator.validateEmptyWhere(where);
619
+ validateEmptyWhere(where);
553
620
  em.checkLockRequirements(options.lockMode, meta);
554
- const isOptimisticLocking = !Utils.isDefined(options.lockMode) || options.lockMode === LockMode.OPTIMISTIC;
621
+ const isOptimisticLocking = options.lockMode == null || options.lockMode === LockMode.OPTIMISTIC;
555
622
  if (entity && !em.shouldRefresh(meta, entity, options) && isOptimisticLocking) {
556
623
  return em.lockAndPopulate(meta, entity, where, options);
557
624
  }
558
- em.validator.validateParams(where);
625
+ validateParams(where);
559
626
  options.populate = await em.preparePopulate(entityName, options);
560
627
  const cacheKey = em.cacheKey(entityName, options, 'em.findOne', where);
561
628
  const cached = await em.tryCache(entityName, options.cache, cacheKey, options.refresh, true);
@@ -574,7 +641,7 @@ export class EntityManager {
574
641
  // save the original hint value so we know it was infer/all
575
642
  options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
576
643
  options.populateWhere = this.createPopulateWhere({ ...where }, options);
577
- options.populateFilter = await this.getJoinedFilters(meta, { ...where }, options);
644
+ options.populateFilter = await this.getJoinedFilters(meta, options);
578
645
  const data = await em.driver.findOne(entityName, where, {
579
646
  ctx: em.transactionContext,
580
647
  em,
@@ -615,10 +682,10 @@ export class EntityManager {
615
682
  if (!entity || isStrictViolation) {
616
683
  const key = options.strict ? 'findExactlyOneOrFailHandler' : 'findOneOrFailHandler';
617
684
  options.failHandler ??= this.config.get(key);
618
- entityName = Utils.className(entityName);
685
+ const name = Utils.className(entityName);
619
686
  /* v8 ignore next */
620
687
  where = Utils.isEntity(where) ? helper(where).getPrimaryKey() : where;
621
- throw options.failHandler(entityName, where);
688
+ throw options.failHandler(name, where);
622
689
  }
623
690
  return entity;
624
691
  }
@@ -658,11 +725,11 @@ export class EntityManager {
658
725
  let where;
659
726
  let entity = null;
660
727
  if (data === undefined) {
661
- entityName = entityNameOrEntity.constructor.name;
728
+ entityName = entityNameOrEntity.constructor;
662
729
  data = entityNameOrEntity;
663
730
  }
664
731
  else {
665
- entityName = Utils.className(entityNameOrEntity);
732
+ entityName = entityNameOrEntity;
666
733
  }
667
734
  const meta = this.metadata.get(entityName);
668
735
  const convertCustomTypes = !Utils.isEntity(data);
@@ -685,26 +752,9 @@ export class EntityManager {
685
752
  }
686
753
  }
687
754
  }
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
- }
755
+ where = getWhereCondition(meta, options.onConflictFields, data, where).where;
706
756
  data = QueryHelper.processObjectParams(data);
707
- em.validator.validateParams(data, 'insert data');
757
+ validateParams(data, 'insert data');
708
758
  if (em.eventManager.hasListeners(EventType.beforeUpsert, meta)) {
709
759
  await em.eventManager.dispatchEvent(EventType.beforeUpsert, { entity: data, em, meta }, meta);
710
760
  }
@@ -742,7 +792,7 @@ export class EntityManager {
742
792
  where[meta.primaryKeys[0]] = ret.insertId;
743
793
  }
744
794
  }
745
- const data2 = await this.driver.findOne(meta.className, where, {
795
+ const data2 = await this.driver.findOne(meta.class, where, {
746
796
  fields: returning,
747
797
  ctx: em.transactionContext,
748
798
  convertCustomTypes: true,
@@ -797,11 +847,11 @@ export class EntityManager {
797
847
  let entityName;
798
848
  let propIndex;
799
849
  if (data === undefined) {
800
- entityName = entityNameOrEntity[0].constructor.name;
850
+ entityName = entityNameOrEntity[0].constructor;
801
851
  data = entityNameOrEntity;
802
852
  }
803
853
  else {
804
- entityName = Utils.className(entityNameOrEntity);
854
+ entityName = entityNameOrEntity;
805
855
  }
806
856
  const batchSize = options.batchSize ?? this.config.get('batchSize');
807
857
  if (data.length > batchSize) {
@@ -845,32 +895,18 @@ export class EntityManager {
845
895
  }
846
896
  }
847
897
  }
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);
898
+ const unique = options.onConflictFields ?? meta.props.filter(p => p.unique).map(p => p.name);
899
+ propIndex = !isRaw(unique) && unique.findIndex(p => data[p] ?? data[p.substring(0, p.indexOf('.'))] != null);
900
+ const tmp = getWhereCondition(meta, options.onConflictFields, row, where);
901
+ propIndex = tmp.propIndex;
867
902
  where = QueryHelper.processWhere({
868
- where,
903
+ where: tmp.where,
869
904
  entityName,
870
905
  metadata: this.metadata,
871
906
  platform: this.getPlatform(),
872
907
  });
873
- em.validator.validateParams(row, 'insert data');
908
+ row = QueryHelper.processObjectParams(row);
909
+ validateParams(row, 'insert data');
874
910
  allData.push(row);
875
911
  allWhere.push(where);
876
912
  }
@@ -911,7 +947,7 @@ export class EntityManager {
911
947
  const reloadFields = returning.length > 0 && !(this.getPlatform().usesReturningStatement() && res.rows?.length);
912
948
  if (options.onConflictAction === 'ignore' || (!res.rows?.length && loadPK.size > 0) || reloadFields) {
913
949
  const unique = meta.hydrateProps.filter(p => !p.lazy).map(p => p.name);
914
- const add = new Set(propIndex >= 0 ? [unique[propIndex]] : []);
950
+ const add = new Set(propIndex !== false && propIndex >= 0 ? [unique[propIndex]] : []);
915
951
  for (const cond of loadPK.values()) {
916
952
  Utils.keys(cond).forEach(key => add.add(key));
917
953
  }
@@ -923,7 +959,7 @@ export class EntityManager {
923
959
  where.$or[idx][prop] = item[prop];
924
960
  });
925
961
  });
926
- const data2 = await this.driver.find(meta.className, where, {
962
+ const data2 = await this.driver.find(meta.class, where, {
927
963
  fields: returning.concat(...add).concat(...(Array.isArray(uniqueFields) ? uniqueFields : [])),
928
964
  ctx: em.transactionContext,
929
965
  convertCustomTypes: true,
@@ -940,7 +976,7 @@ export class EntityManager {
940
976
  });
941
977
  return this.comparator.matching(entityName, cond, tmp);
942
978
  });
943
- /* v8 ignore next 3 */
979
+ /* v8 ignore next */
944
980
  if (!row) {
945
981
  throw new Error(`Cannot find matching entity for condition ${JSON.stringify(cond)}`);
946
982
  }
@@ -963,7 +999,7 @@ export class EntityManager {
963
999
  }, {});
964
1000
  return this.comparator.matching(entityName, cond, pk);
965
1001
  });
966
- /* v8 ignore next 3 */
1002
+ /* v8 ignore next */
967
1003
  if (!row) {
968
1004
  throw new Error(`Cannot find matching entity for condition ${JSON.stringify(cond)}`);
969
1005
  }
@@ -1076,11 +1112,11 @@ export class EntityManager {
1076
1112
  em.prepareOptions(options);
1077
1113
  let entityName;
1078
1114
  if (data === undefined) {
1079
- entityName = entityNameOrEntity.constructor.name;
1115
+ entityName = entityNameOrEntity.constructor;
1080
1116
  data = entityNameOrEntity;
1081
1117
  }
1082
1118
  else {
1083
- entityName = Utils.className(entityNameOrEntity);
1119
+ entityName = entityNameOrEntity;
1084
1120
  }
1085
1121
  if (Utils.isEntity(data)) {
1086
1122
  if (options.schema && helper(data).getSchema() == null) {
@@ -1099,7 +1135,7 @@ export class EntityManager {
1099
1135
  return cs.getPrimaryKey();
1100
1136
  }
1101
1137
  data = QueryHelper.processObjectParams(data);
1102
- em.validator.validateParams(data, 'insert data');
1138
+ validateParams(data, 'insert data');
1103
1139
  const res = await em.driver.nativeInsert(entityName, data, { ctx: em.transactionContext, ...options });
1104
1140
  return res.insertId;
1105
1141
  }
@@ -1111,11 +1147,11 @@ export class EntityManager {
1111
1147
  em.prepareOptions(options);
1112
1148
  let entityName;
1113
1149
  if (data === undefined) {
1114
- entityName = entityNameOrEntities[0].constructor.name;
1150
+ entityName = entityNameOrEntities[0].constructor;
1115
1151
  data = entityNameOrEntities;
1116
1152
  }
1117
1153
  else {
1118
- entityName = Utils.className(entityNameOrEntities);
1154
+ entityName = entityNameOrEntities;
1119
1155
  }
1120
1156
  if (data.length === 0) {
1121
1157
  return [];
@@ -1139,7 +1175,7 @@ export class EntityManager {
1139
1175
  return css.map(cs => cs.getPrimaryKey());
1140
1176
  }
1141
1177
  data = data.map(row => QueryHelper.processObjectParams(row));
1142
- data.forEach(row => em.validator.validateParams(row, 'insert data'));
1178
+ data.forEach(row => validateParams(row, 'insert data'));
1143
1179
  const res = await em.driver.nativeInsertMany(entityName, data, { ctx: em.transactionContext, ...options });
1144
1180
  if (res.insertedIds) {
1145
1181
  return res.insertedIds;
@@ -1152,11 +1188,10 @@ export class EntityManager {
1152
1188
  async nativeUpdate(entityName, where, data, options = {}) {
1153
1189
  const em = this.getContext(false);
1154
1190
  em.prepareOptions(options);
1155
- entityName = Utils.className(entityName);
1156
1191
  data = QueryHelper.processObjectParams(data);
1157
1192
  where = await em.processWhere(entityName, where, { ...options, convertCustomTypes: false }, 'update');
1158
- em.validator.validateParams(data, 'update data');
1159
- em.validator.validateParams(where, 'update condition');
1193
+ validateParams(data, 'update data');
1194
+ validateParams(where, 'update condition');
1160
1195
  const res = await em.driver.nativeUpdate(entityName, where, data, { ctx: em.transactionContext, ...options });
1161
1196
  return res.affectedRows;
1162
1197
  }
@@ -1166,9 +1201,8 @@ export class EntityManager {
1166
1201
  async nativeDelete(entityName, where, options = {}) {
1167
1202
  const em = this.getContext(false);
1168
1203
  em.prepareOptions(options);
1169
- entityName = Utils.className(entityName);
1170
1204
  where = await em.processWhere(entityName, where, options, 'delete');
1171
- em.validator.validateParams(where, 'delete condition');
1205
+ validateParams(where, 'delete condition');
1172
1206
  const res = await em.driver.nativeDelete(entityName, where, { ctx: em.transactionContext, ...options });
1173
1207
  return res.affectedRows;
1174
1208
  }
@@ -1176,18 +1210,19 @@ export class EntityManager {
1176
1210
  * Maps raw database result to an entity and merges it to this EntityManager.
1177
1211
  */
1178
1212
  map(entityName, result, options = {}) {
1179
- entityName = Utils.className(entityName);
1180
1213
  const meta = this.metadata.get(entityName);
1181
1214
  const data = this.driver.mapResult(result, meta);
1182
- Object.keys(data).forEach(k => {
1215
+ for (const k of Object.keys(data)) {
1183
1216
  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);
1217
+ if (prop?.kind === ReferenceKind.SCALAR && SCALAR_TYPES.has(prop.runtimeType) && !prop.customType && (prop.setter || !prop.getter)) {
1218
+ validateProperty(prop, data[k], data);
1186
1219
  }
1187
- });
1220
+ }
1188
1221
  return this.merge(entityName, data, {
1189
1222
  convertCustomTypes: true,
1190
- refresh: true, ...options,
1223
+ refresh: true,
1224
+ validate: false,
1225
+ ...options,
1191
1226
  });
1192
1227
  }
1193
1228
  /**
@@ -1196,32 +1231,19 @@ export class EntityManager {
1196
1231
  */
1197
1232
  merge(entityName, data, options = {}) {
1198
1233
  if (Utils.isEntity(entityName)) {
1199
- return this.merge(entityName.constructor.name, entityName, data);
1234
+ return this.merge(entityName.constructor, entityName, data);
1200
1235
  }
1201
1236
  const em = options.disableContextResolution ? this : this.getContext();
1202
1237
  options.schema ??= em._schema;
1203
1238
  options.validate ??= true;
1204
1239
  options.cascade ??= true;
1205
- entityName = Utils.className(entityName);
1206
- if (options.validate) {
1207
- em.validator.validatePrimaryKey(data, em.metadata.get(entityName));
1208
- }
1240
+ validatePrimaryKey(data, em.metadata.get(entityName));
1209
1241
  let entity = em.unitOfWork.tryGetById(entityName, data, options.schema, false);
1210
1242
  if (entity && helper(entity).__managed && helper(entity).__initialized && !options.refresh) {
1211
1243
  return entity;
1212
1244
  }
1213
- const meta = em.metadata.find(entityName);
1214
- const childMeta = em.metadata.getByDiscriminatorColumn(meta, data);
1215
1245
  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
1246
  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
1247
  const visited = options.cascade ? undefined : new Set([entity]);
1226
1248
  em.unitOfWork.merge(entity, visited);
1227
1249
  return entity;
@@ -1248,6 +1270,7 @@ export class EntityManager {
1248
1270
  ...options,
1249
1271
  newEntity: !options.managed,
1250
1272
  merge: options.managed,
1273
+ normalizeAccessors: true,
1251
1274
  });
1252
1275
  options.persist ??= em.config.get('persistOnCreate');
1253
1276
  if (options.persist && !this.getMetadata(entityName).embeddable) {
@@ -1267,7 +1290,7 @@ export class EntityManager {
1267
1290
  getReference(entityName, id, options = {}) {
1268
1291
  options.schema ??= this.schema;
1269
1292
  options.convertCustomTypes ??= false;
1270
- const meta = this.metadata.get(Utils.className(entityName));
1293
+ const meta = this.metadata.get(entityName);
1271
1294
  if (Utils.isPrimaryKey(id)) {
1272
1295
  if (meta.compositePK) {
1273
1296
  throw ValidationError.invalidCompositeIdentifier(meta);
@@ -1288,7 +1311,6 @@ export class EntityManager {
1288
1311
  // Shallow copy options since the object will be modified when deleting orderBy
1289
1312
  options = { ...options };
1290
1313
  em.prepareOptions(options);
1291
- entityName = Utils.className(entityName);
1292
1314
  await em.tryFlush(entityName, options);
1293
1315
  where = await em.processWhere(entityName, where, options, 'read');
1294
1316
  options.populate = await em.preparePopulate(entityName, options);
@@ -1297,8 +1319,8 @@ export class EntityManager {
1297
1319
  const meta = em.metadata.find(entityName);
1298
1320
  options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
1299
1321
  options.populateWhere = this.createPopulateWhere({ ...where }, options);
1300
- options.populateFilter = await this.getJoinedFilters(meta, { ...where }, options);
1301
- em.validator.validateParams(where);
1322
+ options.populateFilter = await this.getJoinedFilters(meta, options);
1323
+ validateParams(where);
1302
1324
  delete options.orderBy;
1303
1325
  const cacheKey = em.cacheKey(entityName, options, 'em.count', where);
1304
1326
  const cached = await em.tryCache(entityName, options.cache, cacheKey);
@@ -1324,7 +1346,7 @@ export class EntityManager {
1324
1346
  for (const ent of entities) {
1325
1347
  if (!Utils.isEntity(ent, true)) {
1326
1348
  /* v8 ignore next */
1327
- const meta = typeof ent === 'object' ? em.metadata.find(ent.constructor.name) : undefined;
1349
+ const meta = typeof ent === 'object' ? em.metadata.find(ent.constructor) : undefined;
1328
1350
  throw ValidationError.notDiscoveredEntity(ent, meta);
1329
1351
  }
1330
1352
  // do not cascade just yet, cascading of entities in persist stack is done when flushing
@@ -1332,13 +1354,6 @@ export class EntityManager {
1332
1354
  }
1333
1355
  return this;
1334
1356
  }
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
1357
  /**
1343
1358
  * Marks entity for removal.
1344
1359
  * A removed entity will be removed from the database at or before transaction commit or as a result of the flush operation.
@@ -1362,13 +1377,6 @@ export class EntityManager {
1362
1377
  }
1363
1378
  return em;
1364
1379
  }
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
1380
  /**
1373
1381
  * Flushes all changes to objects that have been queued up to now to the database.
1374
1382
  * This effectively synchronizes the in-memory state of managed objects with the database.
@@ -1382,7 +1390,6 @@ export class EntityManager {
1382
1390
  async tryFlush(entityName, options) {
1383
1391
  const em = this.getContext();
1384
1392
  const flushMode = options.flushMode ?? em.flushMode ?? em.config.get('flushMode');
1385
- entityName = Utils.className(entityName);
1386
1393
  const meta = em.metadata.get(entityName);
1387
1394
  if (flushMode === FlushMode.COMMIT) {
1388
1395
  return;
@@ -1401,7 +1408,6 @@ export class EntityManager {
1401
1408
  * Checks whether given property can be populated on the entity.
1402
1409
  */
1403
1410
  canPopulate(entityName, property) {
1404
- entityName = Utils.className(entityName);
1405
1411
  // eslint-disable-next-line prefer-const
1406
1412
  let [p, ...parts] = property.split('.');
1407
1413
  const meta = this.metadata.find(entityName);
@@ -1411,12 +1417,11 @@ export class EntityManager {
1411
1417
  if (p.includes(':')) {
1412
1418
  p = p.split(':', 2)[0];
1413
1419
  }
1414
- const ret = p in meta.root.properties;
1415
- if (!ret) {
1416
- return !!this.metadata.find(property)?.pivotTable;
1417
- }
1420
+ // For TPT inheritance, check the entity's own properties, not just the root's
1421
+ // For STI, meta.properties includes all properties anyway
1422
+ const ret = p in meta.properties;
1418
1423
  if (parts.length > 0) {
1419
- return this.canPopulate((meta.root.properties)[p].type, parts.join('.'));
1424
+ return this.canPopulate(meta.properties[p].targetMeta.class, parts.join('.'));
1420
1425
  }
1421
1426
  return ret;
1422
1427
  }
@@ -1430,8 +1435,8 @@ export class EntityManager {
1430
1435
  }
1431
1436
  const em = this.getContext();
1432
1437
  em.prepareOptions(options);
1433
- const entityName = arr[0].constructor.name;
1434
- const preparedPopulate = await em.preparePopulate(entityName, { populate: populate }, options.validate);
1438
+ const entityName = arr[0].constructor;
1439
+ const preparedPopulate = await em.preparePopulate(entityName, { populate: populate, filters: options.filters }, options.validate);
1435
1440
  await em.entityLoader.populate(entityName, arr, preparedPopulate, options);
1436
1441
  return entities;
1437
1442
  }
@@ -1559,7 +1564,6 @@ export class EntityManager {
1559
1564
  */
1560
1565
  getMetadata(entityName) {
1561
1566
  if (entityName) {
1562
- entityName = Utils.className(entityName);
1563
1567
  return this.metadata.get(entityName);
1564
1568
  }
1565
1569
  return this.metadata;
@@ -1588,8 +1592,8 @@ export class EntityManager {
1588
1592
  lockTableAliases: options.lockTableAliases,
1589
1593
  });
1590
1594
  }
1591
- const preparedPopulate = await this.preparePopulate(meta.className, options);
1592
- await this.entityLoader.populate(meta.className, [entity], preparedPopulate, {
1595
+ const preparedPopulate = await this.preparePopulate(meta.class, options);
1596
+ await this.entityLoader.populate(meta.class, [entity], preparedPopulate, {
1593
1597
  ...options,
1594
1598
  ...this.getPopulateWhere(where, options),
1595
1599
  orderBy: options.populateOrderBy ?? options.orderBy,
@@ -1609,6 +1613,7 @@ export class EntityManager {
1609
1613
  return ret;
1610
1614
  }, []);
1611
1615
  }
1616
+ /** @internal */
1612
1617
  async preparePopulate(entityName, options, validate = true) {
1613
1618
  if (options.populate === false) {
1614
1619
  return [];
@@ -1649,13 +1654,13 @@ export class EntityManager {
1649
1654
  options.populate = pruneToOneRelations(meta, this.buildFields(options.fields));
1650
1655
  }
1651
1656
  if (!options.populate) {
1652
- const populate = this.entityLoader.normalizePopulate(entityName, [], options.strategy);
1657
+ const populate = this.entityLoader.normalizePopulate(entityName, [], options.strategy, true, options.exclude);
1653
1658
  await this.autoJoinRefsForFilters(meta, { ...options, populate });
1654
1659
  return populate;
1655
1660
  }
1656
1661
  if (typeof options.populate !== 'boolean') {
1657
1662
  options.populate = Utils.asArray(options.populate).map(field => {
1658
- /* v8 ignore next 3 */
1663
+ /* v8 ignore next */
1659
1664
  if (typeof field === 'boolean' || field === PopulatePath.ALL) {
1660
1665
  return [{ field: meta.primaryKeys[0], strategy: options.strategy, all: !!field }]; //
1661
1666
  }
@@ -1665,24 +1670,27 @@ export class EntityManager {
1665
1670
  options.flags.push(QueryFlag.INFER_POPULATE);
1666
1671
  return [];
1667
1672
  }
1668
- if (Utils.isString(field)) {
1673
+ if (typeof field === 'string') {
1669
1674
  return [{ field, strategy: options.strategy }];
1670
1675
  }
1671
1676
  return [field];
1672
1677
  }).flat();
1673
1678
  }
1674
- const populate = this.entityLoader.normalizePopulate(entityName, options.populate, options.strategy);
1679
+ const populate = this.entityLoader.normalizePopulate(entityName, options.populate, options.strategy, true, options.exclude);
1675
1680
  const invalid = populate.find(({ field }) => !this.canPopulate(entityName, field));
1676
1681
  if (validate && invalid) {
1677
1682
  throw ValidationError.invalidPropertyName(entityName, invalid.field);
1678
1683
  }
1679
1684
  await this.autoJoinRefsForFilters(meta, { ...options, populate });
1680
- return populate.map(field => {
1685
+ for (const field of populate) {
1681
1686
  // force select-in strategy when populating all relations as otherwise we could cause infinite loops when self-referencing
1682
1687
  const all = field.all ?? (Array.isArray(options.populate) && options.populate.includes('*'));
1683
1688
  field.strategy = all ? LoadStrategy.SELECT_IN : (options.strategy ?? field.strategy);
1684
- return field;
1685
- });
1689
+ }
1690
+ if (options.populateHints) {
1691
+ applyPopulateHints(populate, options.populateHints);
1692
+ }
1693
+ return populate;
1686
1694
  }
1687
1695
  /**
1688
1696
  * when the entity is found in identity map, we check if it was partially loaded or we are trying to populate
@@ -1702,7 +1710,7 @@ export class EntityManager {
1702
1710
  return !inlineEmbedded && !prop.lazy && !helper(entity).__loadedProperties.has(prop.name);
1703
1711
  });
1704
1712
  }
1705
- if (autoRefresh) {
1713
+ if (autoRefresh || options.filters) {
1706
1714
  return true;
1707
1715
  }
1708
1716
  if (Array.isArray(options.populate)) {
@@ -1726,7 +1734,7 @@ export class EntityManager {
1726
1734
  for (const k of ['ctx', 'strategy', 'flushMode', 'logging', 'loggerContext']) {
1727
1735
  delete opts[k];
1728
1736
  }
1729
- return [entityName, method, opts, where];
1737
+ return [Utils.className(entityName), method, opts, where];
1730
1738
  }
1731
1739
  /**
1732
1740
  * @internal
@@ -1743,21 +1751,17 @@ export class EntityManager {
1743
1751
  return { key: cacheKey, data: cached };
1744
1752
  }
1745
1753
  let data;
1754
+ const createOptions = {
1755
+ merge: true,
1756
+ convertCustomTypes: false,
1757
+ refresh,
1758
+ recomputeSnapshot: true,
1759
+ };
1746
1760
  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
- }));
1761
+ data = cached.map(item => em.entityFactory.create(entityName, item, createOptions));
1753
1762
  }
1754
1763
  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
- });
1764
+ data = em.entityFactory.create(entityName, cached, createOptions);
1761
1765
  }
1762
1766
  else {
1763
1767
  data = cached;
@@ -1772,7 +1776,7 @@ export class EntityManager {
1772
1776
  config ??= this.config.get('resultCache').global;
1773
1777
  if (config) {
1774
1778
  const em = this.getContext();
1775
- const expiration = Array.isArray(config) ? config[1] : (Utils.isNumber(config) ? config : undefined);
1779
+ const expiration = Array.isArray(config) ? config[1] : (typeof config === 'number' ? config : undefined);
1776
1780
  await em.resultCache.set(key.key, data instanceof Function ? data() : data, '', expiration);
1777
1781
  }
1778
1782
  }
@@ -1805,6 +1809,20 @@ export class EntityManager {
1805
1809
  set schema(schema) {
1806
1810
  this.getContext(false)._schema = schema ?? undefined;
1807
1811
  }
1812
+ /** @internal */
1813
+ async getDataLoader(type) {
1814
+ const em = this.getContext();
1815
+ if (em.loaders[type]) {
1816
+ return em.loaders[type];
1817
+ }
1818
+ const { DataloaderUtils } = await import('@mikro-orm/core/dataloader');
1819
+ const DataLoader = await DataloaderUtils.getDataLoader();
1820
+ switch (type) {
1821
+ case 'ref': return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getRefBatchLoadFn(em)));
1822
+ case '1:m': return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getColBatchLoadFn(em)));
1823
+ case 'm:n': return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getManyToManyColBatchLoadFn(em)));
1824
+ }
1825
+ }
1808
1826
  /**
1809
1827
  * Returns the ID of this EntityManager. Respects the context, so global EM will give you the contextual ID
1810
1828
  * if executed inside request context handler.
@@ -1813,7 +1831,7 @@ export class EntityManager {
1813
1831
  return this.getContext(false)._id;
1814
1832
  }
1815
1833
  /** @ignore */
1816
- [inspect.custom]() {
1834
+ [Symbol.for('nodejs.util.inspect.custom')]() {
1817
1835
  return `[EntityManager<${this.id}>]`;
1818
1836
  }
1819
1837
  }