@mikro-orm/core 7.0.0-dev.2 → 7.0.0-dev.200

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 (210) hide show
  1. package/EntityManager.d.ts +111 -61
  2. package/EntityManager.js +346 -300
  3. package/MikroORM.d.ts +44 -35
  4. package/MikroORM.js +103 -143
  5. package/README.md +3 -2
  6. package/cache/FileCacheAdapter.d.ts +1 -1
  7. package/cache/FileCacheAdapter.js +8 -7
  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 +16 -7
  13. package/connections/Connection.js +23 -14
  14. package/drivers/DatabaseDriver.d.ts +25 -16
  15. package/drivers/DatabaseDriver.js +80 -35
  16. package/drivers/IDatabaseDriver.d.ts +47 -17
  17. package/entity/BaseEntity.d.ts +2 -2
  18. package/entity/BaseEntity.js +0 -3
  19. package/entity/Collection.d.ts +95 -31
  20. package/entity/Collection.js +444 -102
  21. package/entity/EntityAssigner.d.ts +1 -1
  22. package/entity/EntityAssigner.js +26 -18
  23. package/entity/EntityFactory.d.ts +13 -1
  24. package/entity/EntityFactory.js +88 -54
  25. package/entity/EntityHelper.d.ts +2 -2
  26. package/entity/EntityHelper.js +38 -15
  27. package/entity/EntityLoader.d.ts +8 -7
  28. package/entity/EntityLoader.js +134 -80
  29. package/entity/EntityRepository.d.ts +24 -4
  30. package/entity/EntityRepository.js +8 -2
  31. package/entity/Reference.d.ts +9 -12
  32. package/entity/Reference.js +34 -9
  33. package/entity/WrappedEntity.d.ts +2 -7
  34. package/entity/WrappedEntity.js +3 -8
  35. package/entity/defineEntity.d.ts +585 -0
  36. package/entity/defineEntity.js +533 -0
  37. package/entity/index.d.ts +3 -2
  38. package/entity/index.js +3 -2
  39. package/entity/utils.d.ts +7 -0
  40. package/entity/utils.js +16 -4
  41. package/entity/validators.d.ts +11 -0
  42. package/entity/validators.js +65 -0
  43. package/enums.d.ts +22 -6
  44. package/enums.js +15 -1
  45. package/errors.d.ts +23 -9
  46. package/errors.js +59 -21
  47. package/events/EventManager.d.ts +2 -1
  48. package/events/EventManager.js +19 -11
  49. package/events/EventSubscriber.d.ts +3 -1
  50. package/hydration/Hydrator.js +1 -2
  51. package/hydration/ObjectHydrator.d.ts +4 -4
  52. package/hydration/ObjectHydrator.js +53 -33
  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 +26 -26
  65. package/metadata/EntitySchema.js +82 -51
  66. package/metadata/MetadataDiscovery.d.ts +7 -10
  67. package/metadata/MetadataDiscovery.js +408 -335
  68. package/metadata/MetadataProvider.d.ts +11 -2
  69. package/metadata/MetadataProvider.js +46 -2
  70. package/metadata/MetadataStorage.d.ts +13 -11
  71. package/metadata/MetadataStorage.js +70 -37
  72. package/metadata/MetadataValidator.d.ts +17 -9
  73. package/metadata/MetadataValidator.js +100 -42
  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 +502 -0
  79. package/metadata/types.js +1 -0
  80. package/naming-strategy/AbstractNamingStrategy.d.ts +12 -4
  81. package/naming-strategy/AbstractNamingStrategy.js +14 -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 +24 -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 +19 -11
  92. package/platforms/ExceptionConverter.js +1 -1
  93. package/platforms/Platform.d.ts +7 -13
  94. package/platforms/Platform.js +20 -43
  95. package/serialization/EntitySerializer.d.ts +5 -0
  96. package/serialization/EntitySerializer.js +47 -27
  97. package/serialization/EntityTransformer.js +28 -18
  98. package/serialization/SerializationContext.d.ts +6 -6
  99. package/serialization/SerializationContext.js +16 -13
  100. package/types/ArrayType.d.ts +1 -1
  101. package/types/ArrayType.js +2 -3
  102. package/types/BigIntType.d.ts +9 -6
  103. package/types/BigIntType.js +4 -1
  104. package/types/BlobType.d.ts +0 -1
  105. package/types/BlobType.js +0 -3
  106. package/types/BooleanType.d.ts +2 -1
  107. package/types/BooleanType.js +3 -0
  108. package/types/DecimalType.d.ts +6 -4
  109. package/types/DecimalType.js +3 -3
  110. package/types/DoubleType.js +2 -2
  111. package/types/EnumArrayType.js +1 -2
  112. package/types/JsonType.d.ts +1 -1
  113. package/types/JsonType.js +7 -2
  114. package/types/TinyIntType.js +1 -1
  115. package/types/Type.d.ts +2 -4
  116. package/types/Type.js +3 -3
  117. package/types/Uint8ArrayType.d.ts +0 -1
  118. package/types/Uint8ArrayType.js +1 -4
  119. package/types/index.d.ts +1 -1
  120. package/typings.d.ts +300 -140
  121. package/typings.js +62 -44
  122. package/unit-of-work/ChangeSet.d.ts +2 -6
  123. package/unit-of-work/ChangeSet.js +4 -5
  124. package/unit-of-work/ChangeSetComputer.d.ts +1 -3
  125. package/unit-of-work/ChangeSetComputer.js +26 -13
  126. package/unit-of-work/ChangeSetPersister.d.ts +5 -4
  127. package/unit-of-work/ChangeSetPersister.js +77 -35
  128. package/unit-of-work/CommitOrderCalculator.d.ts +12 -10
  129. package/unit-of-work/CommitOrderCalculator.js +13 -13
  130. package/unit-of-work/IdentityMap.d.ts +12 -0
  131. package/unit-of-work/IdentityMap.js +39 -1
  132. package/unit-of-work/UnitOfWork.d.ts +23 -3
  133. package/unit-of-work/UnitOfWork.js +199 -106
  134. package/utils/AbstractSchemaGenerator.d.ts +5 -5
  135. package/utils/AbstractSchemaGenerator.js +22 -17
  136. package/utils/AsyncContext.d.ts +6 -0
  137. package/utils/AsyncContext.js +42 -0
  138. package/utils/Configuration.d.ts +779 -207
  139. package/utils/Configuration.js +146 -190
  140. package/utils/ConfigurationLoader.d.ts +1 -54
  141. package/utils/ConfigurationLoader.js +1 -352
  142. package/utils/Cursor.d.ts +3 -6
  143. package/utils/Cursor.js +27 -11
  144. package/utils/DataloaderUtils.d.ts +15 -5
  145. package/utils/DataloaderUtils.js +65 -17
  146. package/utils/EntityComparator.d.ts +13 -9
  147. package/utils/EntityComparator.js +164 -89
  148. package/utils/QueryHelper.d.ts +14 -6
  149. package/utils/QueryHelper.js +88 -26
  150. package/utils/RawQueryFragment.d.ts +48 -25
  151. package/utils/RawQueryFragment.js +67 -66
  152. package/utils/RequestContext.js +2 -2
  153. package/utils/TransactionContext.js +2 -2
  154. package/utils/TransactionManager.d.ts +65 -0
  155. package/utils/TransactionManager.js +223 -0
  156. package/utils/Utils.d.ts +13 -120
  157. package/utils/Utils.js +104 -375
  158. package/utils/clone.js +8 -23
  159. package/utils/env-vars.d.ts +7 -0
  160. package/utils/env-vars.js +97 -0
  161. package/utils/fs-utils.d.ts +32 -0
  162. package/utils/fs-utils.js +178 -0
  163. package/utils/index.d.ts +2 -1
  164. package/utils/index.js +2 -1
  165. package/utils/upsert-utils.d.ts +9 -4
  166. package/utils/upsert-utils.js +55 -4
  167. package/decorators/Check.d.ts +0 -3
  168. package/decorators/Check.js +0 -13
  169. package/decorators/CreateRequestContext.d.ts +0 -3
  170. package/decorators/CreateRequestContext.js +0 -29
  171. package/decorators/Embeddable.d.ts +0 -8
  172. package/decorators/Embeddable.js +0 -11
  173. package/decorators/Embedded.d.ts +0 -18
  174. package/decorators/Embedded.js +0 -18
  175. package/decorators/Entity.d.ts +0 -18
  176. package/decorators/Entity.js +0 -13
  177. package/decorators/Enum.d.ts +0 -9
  178. package/decorators/Enum.js +0 -16
  179. package/decorators/Filter.d.ts +0 -2
  180. package/decorators/Filter.js +0 -8
  181. package/decorators/Formula.d.ts +0 -5
  182. package/decorators/Formula.js +0 -15
  183. package/decorators/Indexed.d.ts +0 -17
  184. package/decorators/Indexed.js +0 -20
  185. package/decorators/ManyToMany.d.ts +0 -40
  186. package/decorators/ManyToMany.js +0 -14
  187. package/decorators/ManyToOne.d.ts +0 -30
  188. package/decorators/ManyToOne.js +0 -14
  189. package/decorators/OneToMany.d.ts +0 -28
  190. package/decorators/OneToMany.js +0 -17
  191. package/decorators/OneToOne.d.ts +0 -24
  192. package/decorators/OneToOne.js +0 -7
  193. package/decorators/PrimaryKey.d.ts +0 -9
  194. package/decorators/PrimaryKey.js +0 -20
  195. package/decorators/Property.d.ts +0 -250
  196. package/decorators/Property.js +0 -32
  197. package/decorators/Transactional.d.ts +0 -13
  198. package/decorators/Transactional.js +0 -28
  199. package/decorators/hooks.d.ts +0 -16
  200. package/decorators/hooks.js +0 -47
  201. package/decorators/index.d.ts +0 -17
  202. package/decorators/index.js +0 -17
  203. package/entity/ArrayCollection.d.ts +0 -116
  204. package/entity/ArrayCollection.js +0 -395
  205. package/entity/EntityValidator.d.ts +0 -19
  206. package/entity/EntityValidator.js +0 -150
  207. package/metadata/ReflectMetadataProvider.d.ts +0 -8
  208. package/metadata/ReflectMetadataProvider.js +0 -44
  209. package/utils/resolveContextProvider.d.ts +0 -10
  210. package/utils/resolveContextProvider.js +0 -28
@@ -3,25 +3,25 @@ import { helper } from '../entity/wrap.js';
3
3
  import { ChangeSetType } from './ChangeSet.js';
4
4
  import { isRaw } from '../utils/RawQueryFragment.js';
5
5
  import { Utils } from '../utils/Utils.js';
6
- import { OptimisticLockError } from '../errors.js';
6
+ import { OptimisticLockError, ValidationError } from '../errors.js';
7
7
  import { ReferenceKind } from '../enums.js';
8
8
  export class ChangeSetPersister {
9
9
  driver;
10
10
  metadata;
11
11
  hydrator;
12
12
  factory;
13
- validator;
14
13
  config;
14
+ em;
15
15
  platform;
16
16
  comparator;
17
17
  usesReturningStatement;
18
- constructor(driver, metadata, hydrator, factory, validator, config) {
18
+ constructor(driver, metadata, hydrator, factory, config, em) {
19
19
  this.driver = driver;
20
20
  this.metadata = metadata;
21
21
  this.hydrator = hydrator;
22
22
  this.factory = factory;
23
- this.validator = validator;
24
23
  this.config = config;
24
+ this.em = em;
25
25
  this.platform = this.driver.getPlatform();
26
26
  this.comparator = this.config.getComparator(this.metadata);
27
27
  this.usesReturningStatement = this.platform.usesReturningStatement() || this.platform.usesOutputStatement();
@@ -30,7 +30,7 @@ export class ChangeSetPersister {
30
30
  if (!withSchema) {
31
31
  return this.runForEachSchema(changeSets, 'executeInserts', options);
32
32
  }
33
- const meta = this.metadata.find(changeSets[0].name);
33
+ const meta = changeSets[0].meta;
34
34
  changeSets.forEach(changeSet => this.processProperties(changeSet));
35
35
  if (changeSets.length > 1 && this.config.get('useBatchInserts', this.platform.usesBatchInserts())) {
36
36
  return this.persistNewEntities(meta, changeSets, options);
@@ -43,9 +43,12 @@ export class ChangeSetPersister {
43
43
  if (!withSchema) {
44
44
  return this.runForEachSchema(changeSets, 'executeUpdates', options, batched);
45
45
  }
46
- const meta = this.metadata.find(changeSets[0].name);
46
+ const meta = changeSets[0].meta;
47
47
  changeSets.forEach(changeSet => this.processProperties(changeSet));
48
- if (batched && changeSets.length > 1 && this.config.get('useBatchUpdates', this.platform.usesBatchUpdates())) {
48
+ // For STI with conflicting fieldNames (renamedFrom properties), we can't batch mixed child types
49
+ const hasSTIConflicts = meta.root.props.some(p => p.renamedFrom);
50
+ const hasMixedTypes = hasSTIConflicts && changeSets.some(cs => cs.meta.class !== meta.class);
51
+ if (batched && changeSets.length > 1 && !hasMixedTypes && this.config.get('useBatchUpdates', this.platform.usesBatchUpdates())) {
49
52
  return this.persistManagedEntities(meta, changeSets, options);
50
53
  }
51
54
  for (const changeSet of changeSets) {
@@ -62,8 +65,8 @@ export class ChangeSetPersister {
62
65
  for (let i = 0; i < changeSets.length; i += size) {
63
66
  const chunk = changeSets.slice(i, i + size);
64
67
  const pks = chunk.map(cs => cs.getPrimaryKey());
65
- options = this.propagateSchemaFromMetadata(meta, options);
66
- await this.driver.nativeDelete(meta.root.className, { [pk]: { $in: pks } }, options);
68
+ options = this.prepareOptions(meta, options);
69
+ await this.driver.nativeDelete(meta.root.class, { [pk]: { $in: pks } }, options);
67
70
  }
68
71
  }
69
72
  async runForEachSchema(changeSets, method, options, ...args) {
@@ -79,21 +82,41 @@ export class ChangeSetPersister {
79
82
  await this[method](group, ...args, options, true);
80
83
  }
81
84
  }
85
+ validateRequired(entity) {
86
+ const wrapped = helper(entity);
87
+ for (const prop of wrapped.__meta.props) {
88
+ if (!prop.nullable &&
89
+ !prop.autoincrement &&
90
+ !prop.default &&
91
+ !prop.defaultRaw &&
92
+ !prop.onCreate &&
93
+ !prop.generated &&
94
+ !prop.embedded &&
95
+ ![ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind) &&
96
+ prop.name !== wrapped.__meta.root.discriminatorColumn &&
97
+ prop.type !== 'ObjectId' &&
98
+ prop.persist !== false &&
99
+ entity[prop.name] == null) {
100
+ throw ValidationError.propertyRequired(entity, prop);
101
+ }
102
+ }
103
+ }
82
104
  processProperties(changeSet) {
83
- const meta = this.metadata.find(changeSet.name);
105
+ const meta = changeSet.meta;
84
106
  for (const prop of meta.relations) {
85
107
  this.processProperty(changeSet, prop);
86
108
  }
87
109
  if (changeSet.type === ChangeSetType.CREATE && this.config.get('validateRequired')) {
88
- this.validator.validateRequired(changeSet.entity);
110
+ this.validateRequired(changeSet.entity);
89
111
  }
90
112
  }
91
113
  async persistNewEntity(meta, changeSet, options) {
92
114
  const wrapped = helper(changeSet.entity);
93
- options = this.propagateSchemaFromMetadata(meta, options, {
115
+ options = this.prepareOptions(meta, options, {
94
116
  convertCustomTypes: false,
95
117
  });
96
- const res = await this.driver.nativeInsertMany(meta.className, [changeSet.payload], options);
118
+ // Use changeSet's own meta for STI entities to get correct field mappings
119
+ const res = await this.driver.nativeInsertMany(changeSet.meta.class, [changeSet.payload], options);
97
120
  if (!wrapped.hasPrimaryKey()) {
98
121
  this.mapPrimaryKey(meta, res.insertId, changeSet);
99
122
  }
@@ -116,19 +139,21 @@ export class ChangeSetPersister {
116
139
  }
117
140
  }
118
141
  }
119
- propagateSchemaFromMetadata(meta, options, additionalOptions) {
142
+ prepareOptions(meta, options, additionalOptions) {
143
+ const loggerContext = Utils.merge({ id: this.em._id }, this.em.getLoggerContext({ disableContextResolution: true }));
120
144
  return {
121
145
  ...options,
122
146
  ...additionalOptions,
123
147
  schema: options?.schema ?? meta.schema,
148
+ loggerContext,
124
149
  };
125
150
  }
126
151
  async persistNewEntitiesBatch(meta, changeSets, options) {
127
- options = this.propagateSchemaFromMetadata(meta, options, {
152
+ options = this.prepareOptions(meta, options, {
128
153
  convertCustomTypes: false,
129
154
  processCollections: false,
130
155
  });
131
- const res = await this.driver.nativeInsertMany(meta.className, changeSets.map(cs => cs.payload), options);
156
+ const res = await this.driver.nativeInsertMany(meta.class, changeSets.map(cs => cs.payload), options);
132
157
  for (let i = 0; i < changeSets.length; i++) {
133
158
  const changeSet = changeSets[i];
134
159
  const wrapped = helper(changeSet.entity);
@@ -146,7 +171,7 @@ export class ChangeSetPersister {
146
171
  }
147
172
  }
148
173
  async persistManagedEntity(changeSet, options) {
149
- const meta = this.metadata.find(changeSet.name);
174
+ const meta = changeSet.meta;
150
175
  const res = await this.updateEntity(meta, changeSet, options);
151
176
  this.checkOptimisticLock(meta, changeSet, res);
152
177
  this.mapReturnedValues(changeSet.entity, changeSet.payload, res.row, meta);
@@ -175,7 +200,7 @@ export class ChangeSetPersister {
175
200
  }
176
201
  async persistManagedEntitiesBatch(meta, changeSets, options) {
177
202
  await this.checkOptimisticLocks(meta, changeSets, options);
178
- options = this.propagateSchemaFromMetadata(meta, options, {
203
+ options = this.prepareOptions(meta, options, {
179
204
  convertCustomTypes: false,
180
205
  processCollections: false,
181
206
  });
@@ -187,7 +212,7 @@ export class ChangeSetPersister {
187
212
  cond.push(where);
188
213
  payload.push(changeSet.payload);
189
214
  }
190
- const res = await this.driver.nativeUpdateMany(meta.className, cond, payload, options);
215
+ const res = await this.driver.nativeUpdateMany(meta.class, cond, payload, options);
191
216
  const map = new Map();
192
217
  res.rows?.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, true, this.platform, true), item));
193
218
  for (const changeSet of changeSets) {
@@ -210,7 +235,9 @@ export class ChangeSetPersister {
210
235
  // of using the raw value from db, we convert it back to the db value explicitly
211
236
  value = prop.customType ? prop.customType.convertToDatabaseValue(insertId, this.platform, { mode: 'serialization' }) : value;
212
237
  changeSet.payload[wrapped.__meta.primaryKeys[0]] = value;
213
- wrapped.__identifier?.setValue(value);
238
+ if (wrapped.__identifier && !Array.isArray(wrapped.__identifier)) {
239
+ wrapped.__identifier.setValue(value);
240
+ }
214
241
  }
215
242
  /**
216
243
  * Sets populate flag to new entities so they are serialized like if they were loaded from the db
@@ -233,17 +260,17 @@ export class ChangeSetPersister {
233
260
  }
234
261
  async updateEntity(meta, changeSet, options) {
235
262
  const cond = changeSet.getPrimaryKey(true);
236
- options = this.propagateSchemaFromMetadata(meta, options, {
263
+ options = this.prepareOptions(meta, options, {
237
264
  convertCustomTypes: false,
238
265
  });
239
266
  if (meta.concurrencyCheckKeys.size === 0 && (!meta.versionProperty || changeSet.entity[meta.versionProperty] == null)) {
240
- return this.driver.nativeUpdate(changeSet.name, cond, changeSet.payload, options);
267
+ return this.driver.nativeUpdate(changeSet.meta.class, cond, changeSet.payload, options);
241
268
  }
242
269
  if (meta.versionProperty) {
243
270
  cond[meta.versionProperty] = this.platform.quoteVersionValue(changeSet.entity[meta.versionProperty], meta.properties[meta.versionProperty]);
244
271
  }
245
272
  this.checkConcurrencyKeys(meta, changeSet, cond);
246
- return this.driver.nativeUpdate(changeSet.name, cond, changeSet.payload, options);
273
+ return this.driver.nativeUpdate(changeSet.meta.class, cond, changeSet.payload, options);
247
274
  }
248
275
  async checkOptimisticLocks(meta, changeSets, options) {
249
276
  if (meta.concurrencyCheckKeys.size === 0 && (!meta.versionProperty || changeSets.every(cs => cs.entity[meta.versionProperty] == null))) {
@@ -260,10 +287,10 @@ export class ChangeSetPersister {
260
287
  return cond;
261
288
  });
262
289
  const primaryKeys = meta.primaryKeys.concat(...meta.concurrencyCheckKeys);
263
- options = this.propagateSchemaFromMetadata(meta, options, {
290
+ options = this.prepareOptions(meta, options, {
264
291
  fields: primaryKeys,
265
292
  });
266
- const res = await this.driver.find(meta.root.className, { $or }, options);
293
+ const res = await this.driver.find(meta.root.class, { $or }, options);
267
294
  if (res.length !== changeSets.length) {
268
295
  const compare = (a, b, keys) => keys.every(k => a[k] === b[k]);
269
296
  const entity = changeSets.find(cs => {
@@ -284,11 +311,22 @@ export class ChangeSetPersister {
284
311
  async reloadVersionValues(meta, changeSets, options) {
285
312
  const reloadProps = meta.versionProperty && !this.usesReturningStatement ? [meta.properties[meta.versionProperty]] : [];
286
313
  if (changeSets[0].type === ChangeSetType.CREATE) {
287
- // do not reload things that already had a runtime value
288
- meta.props
289
- .filter(prop => prop.persist !== false && (prop.autoincrement || prop.generated || prop.defaultRaw))
290
- .filter(prop => (changeSets[0].entity[prop.name] == null && prop.defaultRaw !== 'null') || isRaw(changeSets[0].entity[prop.name]))
291
- .forEach(prop => reloadProps.push(prop));
314
+ for (const prop of meta.props) {
315
+ if (prop.persist === false) {
316
+ continue;
317
+ }
318
+ if (isRaw(changeSets[0].entity[prop.name])) {
319
+ reloadProps.push(prop);
320
+ continue;
321
+ }
322
+ // do not reload things that already had a runtime value
323
+ if (changeSets[0].entity[prop.name] != null || prop.defaultRaw === 'null') {
324
+ continue;
325
+ }
326
+ if (prop.autoincrement || prop.generated || prop.defaultRaw) {
327
+ reloadProps.push(prop);
328
+ }
329
+ }
292
330
  }
293
331
  if (changeSets[0].type === ChangeSetType.UPDATE) {
294
332
  const returning = new Set();
@@ -319,12 +357,12 @@ export class ChangeSetPersister {
319
357
  }
320
358
  return val;
321
359
  });
322
- options = this.propagateSchemaFromMetadata(meta, options, {
360
+ options = this.prepareOptions(meta, options, {
323
361
  fields: Utils.unique(reloadProps.map(prop => prop.name)),
324
362
  });
325
- const data = await this.driver.find(meta.className, { [pk]: { $in: pks } }, options);
363
+ const data = await this.driver.find(meta.class, { [pk]: { $in: pks } }, options);
326
364
  const map = new Map();
327
- data.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, true, this.platform, true), item));
365
+ data.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, false, this.platform, true), item));
328
366
  for (const changeSet of changeSets) {
329
367
  const data = map.get(helper(changeSet.entity).getSerializedPrimaryKey());
330
368
  this.hydrator.hydrate(changeSet.entity, meta, data, this.factory, 'full', false, true);
@@ -332,12 +370,16 @@ export class ChangeSetPersister {
332
370
  }
333
371
  }
334
372
  processProperty(changeSet, prop) {
335
- const meta = this.metadata.find(changeSet.name);
373
+ const meta = changeSet.meta;
336
374
  const value = changeSet.payload[prop.name]; // for inline embeddables
337
375
  if (value instanceof EntityIdentifier) {
338
376
  changeSet.payload[prop.name] = value.getValue();
339
377
  return;
340
378
  }
379
+ if (Array.isArray(value) && value.every(item => item instanceof EntityIdentifier)) {
380
+ changeSet.payload[prop.name] = value.map(item => item.getValue());
381
+ return;
382
+ }
341
383
  if (prop.kind === ReferenceKind.MANY_TO_MANY && Array.isArray(value)) {
342
384
  changeSet.payload[prop.name] = value.map(val => val instanceof EntityIdentifier ? val.getValue() : val);
343
385
  return;
@@ -361,7 +403,7 @@ export class ChangeSetPersister {
361
403
  if ((!this.usesReturningStatement && !upsert) || !row || !Utils.hasObjectKeys(row)) {
362
404
  return;
363
405
  }
364
- const mapped = this.comparator.mapResult(meta.className, row);
406
+ const mapped = this.comparator.mapResult(meta, row);
365
407
  if (entity) {
366
408
  this.hydrator.hydrate(entity, meta, mapped, this.factory, 'full', false, true);
367
409
  }
@@ -1,17 +1,18 @@
1
- import type { Dictionary, EntityProperty } from '../typings.js';
1
+ import type { EntityProperty } from '../typings.js';
2
2
  export declare const enum NodeState {
3
3
  NOT_VISITED = 0,
4
4
  IN_PROGRESS = 1,
5
5
  VISITED = 2
6
6
  }
7
+ type Hash = number;
7
8
  export interface Node {
8
- hash: string;
9
+ hash: Hash;
9
10
  state: NodeState;
10
- dependencies: Dictionary<Edge>;
11
+ dependencies: Map<Hash, Edge>;
11
12
  }
12
13
  export interface Edge {
13
- from: string;
14
- to: string;
14
+ from: Hash;
15
+ to: Hash;
15
16
  weight: number;
16
17
  }
17
18
  /**
@@ -32,23 +33,23 @@ export declare class CommitOrderCalculator {
32
33
  /**
33
34
  * Checks for node existence in graph.
34
35
  */
35
- hasNode(hash: string): boolean;
36
+ hasNode(hash: Hash): boolean;
36
37
  /**
37
38
  * Adds a new node to the graph, assigning its hash.
38
39
  */
39
- addNode(hash: string): void;
40
+ addNode(hash: Hash): void;
40
41
  /**
41
42
  * Adds a new dependency (edge) to the graph using their hashes.
42
43
  */
43
- addDependency(from: string, to: string, weight: number): void;
44
- discoverProperty(prop: EntityProperty, entityName: string): void;
44
+ addDependency(from: Hash, to: Hash, weight: number): void;
45
+ discoverProperty(prop: EntityProperty, entityName: Hash): void;
45
46
  /**
46
47
  * Return a valid order list of all current nodes.
47
48
  * The desired topological sorting is the reverse post order of these searches.
48
49
  *
49
50
  * @internal Highly performance-sensitive method.
50
51
  */
51
- sort(): string[];
52
+ sort(): Hash[];
52
53
  /**
53
54
  * Visit a given node definition for reordering.
54
55
  *
@@ -60,3 +61,4 @@ export declare class CommitOrderCalculator {
60
61
  */
61
62
  private visitOpenNode;
62
63
  }
64
+ export {};
@@ -17,26 +17,26 @@ export var NodeState;
17
17
  */
18
18
  export class CommitOrderCalculator {
19
19
  /** Matrix of nodes, keys are provided hashes and values are the node definition objects. */
20
- nodes = {};
20
+ nodes = new Map();
21
21
  /** Volatile variable holding calculated nodes during sorting process. */
22
22
  sortedNodeList = [];
23
23
  /**
24
24
  * Checks for node existence in graph.
25
25
  */
26
26
  hasNode(hash) {
27
- return hash in this.nodes;
27
+ return this.nodes.has(hash);
28
28
  }
29
29
  /**
30
30
  * Adds a new node to the graph, assigning its hash.
31
31
  */
32
32
  addNode(hash) {
33
- this.nodes[hash] = { hash, state: 0 /* NodeState.NOT_VISITED */, dependencies: {} };
33
+ this.nodes.set(hash, { hash, state: 0 /* NodeState.NOT_VISITED */, dependencies: new Map() });
34
34
  }
35
35
  /**
36
36
  * Adds a new dependency (edge) to the graph using their hashes.
37
37
  */
38
38
  addDependency(from, to, weight) {
39
- this.nodes[from].dependencies[to] = { from, to, weight };
39
+ this.nodes.get(from).dependencies.set(to, { from, to, weight });
40
40
  }
41
41
  discoverProperty(prop, entityName) {
42
42
  const toOneOwner = (prop.kind === ReferenceKind.ONE_TO_ONE && prop.owner) || prop.kind === ReferenceKind.MANY_TO_ONE;
@@ -44,8 +44,8 @@ export class CommitOrderCalculator {
44
44
  if (!toOneOwner && !toManyOwner) {
45
45
  return;
46
46
  }
47
- const propertyType = prop.targetMeta?.root.className;
48
- if (!propertyType || !this.hasNode(propertyType)) {
47
+ const propertyType = prop.targetMeta?.root._id;
48
+ if (propertyType == null || !this.hasNode(propertyType)) {
49
49
  return;
50
50
  }
51
51
  this.addDependency(propertyType, entityName, prop.nullable || prop.persist === false ? 0 : 1);
@@ -57,14 +57,14 @@ export class CommitOrderCalculator {
57
57
  * @internal Highly performance-sensitive method.
58
58
  */
59
59
  sort() {
60
- for (const vertex of Object.values(this.nodes)) {
60
+ for (const vertex of this.nodes.values()) {
61
61
  if (vertex.state !== 0 /* NodeState.NOT_VISITED */) {
62
62
  continue;
63
63
  }
64
64
  this.visit(vertex);
65
65
  }
66
66
  const sortedList = this.sortedNodeList.reverse();
67
- this.nodes = {};
67
+ this.nodes = new Map();
68
68
  this.sortedNodeList = [];
69
69
  return sortedList;
70
70
  }
@@ -75,8 +75,8 @@ export class CommitOrderCalculator {
75
75
  */
76
76
  visit(node) {
77
77
  node.state = 1 /* NodeState.IN_PROGRESS */;
78
- for (const edge of Object.values(node.dependencies)) {
79
- const target = this.nodes[edge.to];
78
+ for (const edge of node.dependencies.values()) {
79
+ const target = this.nodes.get(edge.to);
80
80
  switch (target.state) {
81
81
  case 2 /* NodeState.VISITED */: break; // Do nothing, since node was already visited
82
82
  case 1 /* NodeState.IN_PROGRESS */:
@@ -94,11 +94,11 @@ export class CommitOrderCalculator {
94
94
  * Visits all target's dependencies if in cycle with given node
95
95
  */
96
96
  visitOpenNode(node, target, edge) {
97
- if (!target.dependencies[node.hash] || target.dependencies[node.hash].weight >= edge.weight) {
97
+ if (!target.dependencies.has(node.hash) || target.dependencies.get(node.hash).weight >= edge.weight) {
98
98
  return;
99
99
  }
100
- for (const edge of Object.values(target.dependencies)) {
101
- const targetNode = this.nodes[edge.to];
100
+ for (const edge of target.dependencies.values()) {
101
+ const targetNode = this.nodes.get(edge.to);
102
102
  if (targetNode.state === 0 /* NodeState.NOT_VISITED */) {
103
103
  this.visit(targetNode);
104
104
  }
@@ -3,7 +3,14 @@ export declare class IdentityMap {
3
3
  private readonly defaultSchema?;
4
4
  constructor(defaultSchema?: string | undefined);
5
5
  private readonly registry;
6
+ /** Tracks alternate key hashes for each entity so we can clean them up on delete */
7
+ private readonly alternateKeys;
6
8
  store<T>(item: T): void;
9
+ /**
10
+ * Stores an entity under an alternate key (non-PK property).
11
+ * This allows looking up entities by unique properties that are not the primary key.
12
+ */
13
+ storeByKey<T>(item: T, key: string, value: string, schema?: string): void;
7
14
  delete<T>(item: T): void;
8
15
  getByHash<T>(meta: EntityMetadata<T>, hash: string): T | undefined;
9
16
  getStore<T>(meta: EntityMetadata<T>): Map<string, T>;
@@ -16,4 +23,9 @@ export declare class IdentityMap {
16
23
  */
17
24
  get<T>(hash: string): T | undefined;
18
25
  private getPkHash;
26
+ /**
27
+ * Creates a hash for an alternate key lookup.
28
+ * Format: `[key]value` or `schema:[key]value`
29
+ */
30
+ getKeyHash(key: string, value: string, schema?: string): string;
19
31
  }
@@ -4,11 +4,38 @@ export class IdentityMap {
4
4
  this.defaultSchema = defaultSchema;
5
5
  }
6
6
  registry = new Map();
7
+ /** Tracks alternate key hashes for each entity so we can clean them up on delete */
8
+ alternateKeys = new WeakMap();
7
9
  store(item) {
8
10
  this.getStore(item.__meta.root).set(this.getPkHash(item), item);
9
11
  }
12
+ /**
13
+ * Stores an entity under an alternate key (non-PK property).
14
+ * This allows looking up entities by unique properties that are not the primary key.
15
+ */
16
+ storeByKey(item, key, value, schema) {
17
+ const hash = this.getKeyHash(key, value, schema);
18
+ this.getStore(item.__meta.root).set(hash, item);
19
+ // Track this alternate key so we can clean it up when the entity is deleted
20
+ let keys = this.alternateKeys.get(item);
21
+ if (!keys) {
22
+ keys = new Set();
23
+ this.alternateKeys.set(item, keys);
24
+ }
25
+ keys.add(hash);
26
+ }
10
27
  delete(item) {
11
- this.getStore(item.__meta.root).delete(this.getPkHash(item));
28
+ const meta = item.__meta.root;
29
+ const store = this.getStore(meta);
30
+ store.delete(this.getPkHash(item));
31
+ // Also delete any alternate key entries for this entity
32
+ const altKeys = this.alternateKeys.get(item);
33
+ if (altKeys) {
34
+ for (const hash of altKeys) {
35
+ store.delete(hash);
36
+ }
37
+ this.alternateKeys.delete(item);
38
+ }
12
39
  }
13
40
  getByHash(meta, hash) {
14
41
  const store = this.getStore(meta);
@@ -69,4 +96,15 @@ export class IdentityMap {
69
96
  }
70
97
  return hash;
71
98
  }
99
+ /**
100
+ * Creates a hash for an alternate key lookup.
101
+ * Format: `[key]value` or `schema:[key]value`
102
+ */
103
+ getKeyHash(key, value, schema) {
104
+ const hash = `[${key}]${value}`;
105
+ if (schema) {
106
+ return schema + ':' + hash;
107
+ }
108
+ return hash;
109
+ }
72
110
  }
@@ -1,4 +1,4 @@
1
- import type { AnyEntity, EntityData, EntityMetadata, EntityProperty, FilterQuery, Primary } from '../typings.js';
1
+ import type { AnyEntity, EntityData, EntityMetadata, EntityName, EntityProperty, FilterQuery, Primary } from '../typings.js';
2
2
  import { Collection } from '../entity/Collection.js';
3
3
  import { Reference } from '../entity/Reference.js';
4
4
  import { ChangeSet, ChangeSetType } from './ChangeSet.js';
@@ -28,6 +28,12 @@ export declare class UnitOfWork {
28
28
  private working;
29
29
  constructor(em: EntityManager);
30
30
  merge<T extends object>(entity: T, visited?: Set<AnyEntity>): void;
31
+ /**
32
+ * Entity data can wary in its shape, e.g. we might get a deep relation graph with joined strategy, but for diffing,
33
+ * we need to normalize the shape, so relation values are only raw FKs. This method handles that.
34
+ * @internal
35
+ */
36
+ normalizeEntityData<T extends object>(meta: EntityMetadata<T>, data: EntityData<T>): void;
31
37
  /**
32
38
  * @internal
33
39
  */
@@ -39,8 +45,21 @@ export declare class UnitOfWork {
39
45
  /**
40
46
  * Returns entity from the identity map. For composite keys, you need to pass an array of PKs in the same order as they are defined in `meta.primaryKeys`.
41
47
  */
42
- getById<T extends object>(entityName: string, id: Primary<T> | Primary<T>[], schema?: string): T | undefined;
43
- tryGetById<T extends object>(entityName: string, where: FilterQuery<T>, schema?: string, strict?: boolean): T | null;
48
+ getById<T extends object>(entityName: EntityName<T>, id: Primary<T> | Primary<T>[], schema?: string, convertCustomTypes?: boolean): T | undefined;
49
+ /**
50
+ * Returns entity from the identity map by an alternate key (non-PK property).
51
+ * @param convertCustomTypes - If true, the value is in database format and will be converted to JS format for lookup.
52
+ * If false (default), the value is assumed to be in JS format already.
53
+ */
54
+ getByKey<T extends object>(entityName: EntityName<T>, key: string, value: unknown, schema?: string, convertCustomTypes?: boolean): T | undefined;
55
+ /**
56
+ * Stores an entity in the identity map under an alternate key (non-PK property).
57
+ * Also sets the property value on the entity.
58
+ * @param convertCustomTypes - If true, the value is in database format and will be converted to JS format.
59
+ * If false (default), the value is assumed to be in JS format already.
60
+ */
61
+ storeByKey<T extends object>(entity: T, key: string, value: unknown, schema?: string, convertCustomTypes?: boolean): void;
62
+ tryGetById<T extends object>(entityName: EntityName<T>, where: FilterQuery<T>, schema?: string, strict?: boolean): T | null;
44
63
  /**
45
64
  * Returns map of all managed entities.
46
65
  */
@@ -103,6 +122,7 @@ export declare class UnitOfWork {
103
122
  private commitDeleteChangeSets;
104
123
  private commitExtraUpdates;
105
124
  private commitCollectionUpdates;
125
+ private filterCollectionUpdates;
106
126
  /**
107
127
  * Orders change sets so FK constrains are maintained, ensures stable order (needed for node < 11)
108
128
  */