@mikro-orm/core 7.0.0-dev.21 → 7.0.0-dev.23

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 (72) hide show
  1. package/EntityManager.d.ts +12 -2
  2. package/EntityManager.js +40 -53
  3. package/README.md +1 -2
  4. package/connections/Connection.d.ts +4 -2
  5. package/connections/Connection.js +2 -2
  6. package/decorators/Entity.d.ts +15 -0
  7. package/decorators/Indexed.d.ts +2 -2
  8. package/decorators/ManyToMany.d.ts +2 -0
  9. package/decorators/ManyToOne.d.ts +2 -0
  10. package/decorators/OneToOne.d.ts +2 -0
  11. package/decorators/Transactional.d.ts +1 -0
  12. package/decorators/Transactional.js +3 -3
  13. package/drivers/IDatabaseDriver.d.ts +4 -0
  14. package/entity/ArrayCollection.d.ts +3 -1
  15. package/entity/ArrayCollection.js +7 -2
  16. package/entity/Collection.js +3 -2
  17. package/entity/EntityFactory.d.ts +6 -0
  18. package/entity/EntityFactory.js +17 -6
  19. package/entity/EntityHelper.js +5 -1
  20. package/entity/EntityLoader.js +27 -19
  21. package/entity/Reference.d.ts +5 -0
  22. package/entity/Reference.js +16 -0
  23. package/entity/WrappedEntity.js +1 -1
  24. package/entity/defineEntity.d.ts +537 -0
  25. package/entity/defineEntity.js +690 -0
  26. package/entity/index.d.ts +2 -0
  27. package/entity/index.js +2 -0
  28. package/entity/utils.d.ts +7 -0
  29. package/entity/utils.js +15 -3
  30. package/enums.d.ts +15 -2
  31. package/enums.js +13 -0
  32. package/errors.d.ts +6 -0
  33. package/errors.js +14 -0
  34. package/hydration/ObjectHydrator.js +1 -1
  35. package/index.d.ts +1 -1
  36. package/metadata/EntitySchema.js +10 -2
  37. package/metadata/MetadataDiscovery.d.ts +0 -1
  38. package/metadata/MetadataDiscovery.js +27 -18
  39. package/package.json +4 -5
  40. package/platforms/Platform.d.ts +3 -1
  41. package/serialization/SerializationContext.js +13 -10
  42. package/types/BooleanType.d.ts +1 -1
  43. package/types/DecimalType.js +1 -1
  44. package/types/DoubleType.js +1 -1
  45. package/typings.d.ts +32 -10
  46. package/typings.js +21 -4
  47. package/unit-of-work/ChangeSetComputer.js +3 -1
  48. package/unit-of-work/ChangeSetPersister.d.ts +4 -2
  49. package/unit-of-work/ChangeSetPersister.js +14 -10
  50. package/unit-of-work/UnitOfWork.d.ts +2 -1
  51. package/unit-of-work/UnitOfWork.js +36 -13
  52. package/utils/Configuration.d.ts +7 -1
  53. package/utils/Configuration.js +1 -0
  54. package/utils/ConfigurationLoader.js +2 -2
  55. package/utils/Cursor.js +3 -0
  56. package/utils/DataloaderUtils.d.ts +6 -1
  57. package/utils/DataloaderUtils.js +37 -20
  58. package/utils/EntityComparator.d.ts +6 -2
  59. package/utils/EntityComparator.js +29 -8
  60. package/utils/QueryHelper.d.ts +6 -0
  61. package/utils/QueryHelper.js +47 -4
  62. package/utils/RawQueryFragment.d.ts +34 -0
  63. package/utils/RawQueryFragment.js +34 -0
  64. package/utils/TransactionManager.d.ts +65 -0
  65. package/utils/TransactionManager.js +199 -0
  66. package/utils/Utils.d.ts +2 -2
  67. package/utils/Utils.js +31 -7
  68. package/utils/index.d.ts +1 -0
  69. package/utils/index.js +1 -0
  70. package/utils/upsert-utils.js +9 -1
  71. package/exports.d.ts +0 -24
  72. package/exports.js +0 -23
package/typings.js CHANGED
@@ -45,12 +45,29 @@ export class EntityMetadata {
45
45
  this.sync();
46
46
  }
47
47
  }
48
- getPrimaryProps() {
49
- return this.primaryKeys.map(pk => this.properties[pk]);
48
+ getPrimaryProps(flatten = false) {
49
+ const pks = this.primaryKeys.map(pk => this.properties[pk]);
50
+ if (flatten) {
51
+ return pks.flatMap(pk => {
52
+ if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(pk.kind)) {
53
+ return pk.targetMeta.getPrimaryProps(true);
54
+ }
55
+ return [pk];
56
+ });
57
+ }
58
+ return pks;
50
59
  }
51
60
  getPrimaryProp() {
52
61
  return this.properties[this.primaryKeys[0]];
53
62
  }
63
+ createColumnMappingObject() {
64
+ return Object.values(this.properties).reduce((o, prop) => {
65
+ if (prop.fieldNames) {
66
+ o[prop.name] = prop.fieldNames[0];
67
+ }
68
+ return o;
69
+ }, {});
70
+ }
54
71
  get tableName() {
55
72
  return this.collection;
56
73
  }
@@ -70,7 +87,7 @@ export class EntityMetadata {
70
87
  // `prop.userDefined` is either `undefined` or `false`
71
88
  const discriminator = this.root.discriminatorColumn === prop.name && prop.userDefined === false;
72
89
  // even if we don't have a setter, do not ignore value from database!
73
- const onlyGetter = prop.getter && !prop.setter;
90
+ const onlyGetter = prop.getter && !prop.setter && prop.persist === false;
74
91
  return !prop.inherited && prop.hydrate !== false && !discriminator && !prop.embedded && !onlyGetter;
75
92
  });
76
93
  this.trackingProps = this.hydrateProps
@@ -129,7 +146,7 @@ export class EntityMetadata {
129
146
  wrapped.__data[prop.name] = Reference.wrapReference(val, prop);
130
147
  // when propagation from inside hydration, we set the FK to the entity data immediately
131
148
  if (val && hydrator.isRunning() && wrapped.__originalEntityData && prop.owner) {
132
- wrapped.__originalEntityData[prop.name] = Utils.getPrimaryKeyValues(val, prop.targetMeta.primaryKeys, true);
149
+ wrapped.__originalEntityData[prop.name] = Utils.getPrimaryKeyValues(val, prop.targetMeta, true);
133
150
  }
134
151
  else {
135
152
  wrapped.__touched = !hydrator.isRunning();
@@ -141,7 +141,9 @@ export class ChangeSetComputer {
141
141
  if (!target.isDirty() && changeSet.type !== ChangeSetType.CREATE) {
142
142
  return;
143
143
  }
144
- this.collectionUpdates.add(target);
144
+ if (target.isDirty()) {
145
+ this.collectionUpdates.add(target);
146
+ }
145
147
  if (prop.owner && !this.platform.usesPivotTable()) {
146
148
  changeSet.payload[prop.name] = target.getItems(false).map((item) => item.__helper.__identifier ?? item.__helper.getPrimaryKey());
147
149
  }
@@ -5,6 +5,7 @@ import { type EntityValidator } from '../entity/EntityValidator.js';
5
5
  import { type ChangeSet } from './ChangeSet.js';
6
6
  import { type Configuration } from '../utils/Configuration.js';
7
7
  import type { DriverMethodOptions, IDatabaseDriver } from '../drivers/IDatabaseDriver.js';
8
+ import type { EntityManager } from '../EntityManager.js';
8
9
  export declare class ChangeSetPersister {
9
10
  private readonly driver;
10
11
  private readonly metadata;
@@ -12,10 +13,11 @@ export declare class ChangeSetPersister {
12
13
  private readonly factory;
13
14
  private readonly validator;
14
15
  private readonly config;
16
+ private readonly em;
15
17
  private readonly platform;
16
18
  private readonly comparator;
17
19
  private readonly usesReturningStatement;
18
- constructor(driver: IDatabaseDriver, metadata: MetadataStorage, hydrator: IHydrator, factory: EntityFactory, validator: EntityValidator, config: Configuration);
20
+ constructor(driver: IDatabaseDriver, metadata: MetadataStorage, hydrator: IHydrator, factory: EntityFactory, validator: EntityValidator, config: Configuration, em: EntityManager);
19
21
  executeInserts<T extends object>(changeSets: ChangeSet<T>[], options?: DriverMethodOptions, withSchema?: boolean): Promise<void>;
20
22
  executeUpdates<T extends object>(changeSets: ChangeSet<T>[], batched: boolean, options?: DriverMethodOptions, withSchema?: boolean): Promise<void>;
21
23
  executeDeletes<T extends object>(changeSets: ChangeSet<T>[], options?: DriverMethodOptions, withSchema?: boolean): Promise<void>;
@@ -23,7 +25,7 @@ export declare class ChangeSetPersister {
23
25
  private processProperties;
24
26
  private persistNewEntity;
25
27
  private persistNewEntities;
26
- private propagateSchemaFromMetadata;
28
+ private prepareOptions;
27
29
  private persistNewEntitiesBatch;
28
30
  private persistManagedEntity;
29
31
  private persistManagedEntities;
@@ -12,16 +12,18 @@ export class ChangeSetPersister {
12
12
  factory;
13
13
  validator;
14
14
  config;
15
+ em;
15
16
  platform;
16
17
  comparator;
17
18
  usesReturningStatement;
18
- constructor(driver, metadata, hydrator, factory, validator, config) {
19
+ constructor(driver, metadata, hydrator, factory, validator, config, em) {
19
20
  this.driver = driver;
20
21
  this.metadata = metadata;
21
22
  this.hydrator = hydrator;
22
23
  this.factory = factory;
23
24
  this.validator = validator;
24
25
  this.config = config;
26
+ this.em = em;
25
27
  this.platform = this.driver.getPlatform();
26
28
  this.comparator = this.config.getComparator(this.metadata);
27
29
  this.usesReturningStatement = this.platform.usesReturningStatement() || this.platform.usesOutputStatement();
@@ -62,7 +64,7 @@ export class ChangeSetPersister {
62
64
  for (let i = 0; i < changeSets.length; i += size) {
63
65
  const chunk = changeSets.slice(i, i + size);
64
66
  const pks = chunk.map(cs => cs.getPrimaryKey());
65
- options = this.propagateSchemaFromMetadata(meta, options);
67
+ options = this.prepareOptions(meta, options);
66
68
  await this.driver.nativeDelete(meta.root.className, { [pk]: { $in: pks } }, options);
67
69
  }
68
70
  }
@@ -90,7 +92,7 @@ export class ChangeSetPersister {
90
92
  }
91
93
  async persistNewEntity(meta, changeSet, options) {
92
94
  const wrapped = helper(changeSet.entity);
93
- options = this.propagateSchemaFromMetadata(meta, options, {
95
+ options = this.prepareOptions(meta, options, {
94
96
  convertCustomTypes: false,
95
97
  });
96
98
  const res = await this.driver.nativeInsertMany(meta.className, [changeSet.payload], options);
@@ -116,15 +118,17 @@ export class ChangeSetPersister {
116
118
  }
117
119
  }
118
120
  }
119
- propagateSchemaFromMetadata(meta, options, additionalOptions) {
121
+ prepareOptions(meta, options, additionalOptions) {
122
+ const loggerContext = Utils.merge({ id: this.em._id }, this.em.getLoggerContext({ disableContextResolution: true }));
120
123
  return {
121
124
  ...options,
122
125
  ...additionalOptions,
123
126
  schema: options?.schema ?? meta.schema,
127
+ loggerContext,
124
128
  };
125
129
  }
126
130
  async persistNewEntitiesBatch(meta, changeSets, options) {
127
- options = this.propagateSchemaFromMetadata(meta, options, {
131
+ options = this.prepareOptions(meta, options, {
128
132
  convertCustomTypes: false,
129
133
  processCollections: false,
130
134
  });
@@ -175,7 +179,7 @@ export class ChangeSetPersister {
175
179
  }
176
180
  async persistManagedEntitiesBatch(meta, changeSets, options) {
177
181
  await this.checkOptimisticLocks(meta, changeSets, options);
178
- options = this.propagateSchemaFromMetadata(meta, options, {
182
+ options = this.prepareOptions(meta, options, {
179
183
  convertCustomTypes: false,
180
184
  processCollections: false,
181
185
  });
@@ -235,7 +239,7 @@ export class ChangeSetPersister {
235
239
  }
236
240
  async updateEntity(meta, changeSet, options) {
237
241
  const cond = changeSet.getPrimaryKey(true);
238
- options = this.propagateSchemaFromMetadata(meta, options, {
242
+ options = this.prepareOptions(meta, options, {
239
243
  convertCustomTypes: false,
240
244
  });
241
245
  if (meta.concurrencyCheckKeys.size === 0 && (!meta.versionProperty || changeSet.entity[meta.versionProperty] == null)) {
@@ -262,7 +266,7 @@ export class ChangeSetPersister {
262
266
  return cond;
263
267
  });
264
268
  const primaryKeys = meta.primaryKeys.concat(...meta.concurrencyCheckKeys);
265
- options = this.propagateSchemaFromMetadata(meta, options, {
269
+ options = this.prepareOptions(meta, options, {
266
270
  fields: primaryKeys,
267
271
  });
268
272
  const res = await this.driver.find(meta.root.className, { $or }, options);
@@ -321,12 +325,12 @@ export class ChangeSetPersister {
321
325
  }
322
326
  return val;
323
327
  });
324
- options = this.propagateSchemaFromMetadata(meta, options, {
328
+ options = this.prepareOptions(meta, options, {
325
329
  fields: Utils.unique(reloadProps.map(prop => prop.name)),
326
330
  });
327
331
  const data = await this.driver.find(meta.className, { [pk]: { $in: pks } }, options);
328
332
  const map = new Map();
329
- data.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, true, this.platform, true), item));
333
+ data.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, false, this.platform, true), item));
330
334
  for (const changeSet of changeSets) {
331
335
  const data = map.get(helper(changeSet.entity).getSerializedPrimaryKey());
332
336
  this.hydrator.hydrate(changeSet.entity, meta, data, this.factory, 'full', false, true);
@@ -39,7 +39,7 @@ export declare class UnitOfWork {
39
39
  /**
40
40
  * 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
41
  */
42
- getById<T extends object>(entityName: string, id: Primary<T> | Primary<T>[], schema?: string): T | undefined;
42
+ getById<T extends object>(entityName: string, id: Primary<T> | Primary<T>[], schema?: string, convertCustomTypes?: boolean): T | undefined;
43
43
  tryGetById<T extends object>(entityName: string, where: FilterQuery<T>, schema?: string, strict?: boolean): T | null;
44
44
  /**
45
45
  * Returns map of all managed entities.
@@ -103,6 +103,7 @@ export declare class UnitOfWork {
103
103
  private commitDeleteChangeSets;
104
104
  private commitExtraUpdates;
105
105
  private commitCollectionUpdates;
106
+ private filterCollectionUpdates;
106
107
  /**
107
108
  * Orders change sets so FK constrains are maintained, ensures stable order (needed for node < 11)
108
109
  */
@@ -43,7 +43,7 @@ export class UnitOfWork {
43
43
  this.eventManager = this.em.getEventManager();
44
44
  this.comparator = this.em.getComparator();
45
45
  this.changeSetComputer = new ChangeSetComputer(this.em.getValidator(), this.collectionUpdates, this.metadata, this.platform, this.em.config, this.em);
46
- this.changeSetPersister = new ChangeSetPersister(this.em.getDriver(), this.metadata, this.em.config.getHydrator(this.metadata), this.em.getEntityFactory(), this.em.getValidator(), this.em.config);
46
+ this.changeSetPersister = new ChangeSetPersister(this.em.getDriver(), this.metadata, this.em.config.getHydrator(this.metadata), this.em.getEntityFactory(), this.em.getValidator(), this.em.config, this.em);
47
47
  }
48
48
  merge(entity, visited) {
49
49
  const wrapped = helper(entity);
@@ -89,7 +89,7 @@ export class UnitOfWork {
89
89
  }
90
90
  wrapped.__loadedProperties.add(key);
91
91
  if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && Utils.isPlainObject(data[prop.name])) {
92
- data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta.primaryKeys, true);
92
+ data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
93
93
  }
94
94
  else if (prop.kind === ReferenceKind.EMBEDDED && !prop.object && Utils.isPlainObject(data[prop.name])) {
95
95
  for (const p of prop.targetMeta.props) {
@@ -97,7 +97,7 @@ export class UnitOfWork {
97
97
  const prefix = prop.prefix === false ? '' : prop.prefix === true ? prop.name + '_' : prop.prefix;
98
98
  data[prefix + p.name] = data[prop.name][p.name];
99
99
  }
100
- data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta.primaryKeys, true);
100
+ data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
101
101
  }
102
102
  if (forceUndefined) {
103
103
  if (data[key] === null) {
@@ -125,7 +125,7 @@ export class UnitOfWork {
125
125
  /**
126
126
  * 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`.
127
127
  */
128
- getById(entityName, id, schema) {
128
+ getById(entityName, id, schema, convertCustomTypes) {
129
129
  if (id == null || (Array.isArray(id) && id.length === 0)) {
130
130
  return undefined;
131
131
  }
@@ -135,7 +135,16 @@ export class UnitOfWork {
135
135
  hash = '' + id;
136
136
  }
137
137
  else {
138
- const keys = Array.isArray(id) ? Utils.flatten(id) : [id];
138
+ let keys = Array.isArray(id) ? Utils.flatten(id) : [id];
139
+ keys = meta.getPrimaryProps(true).map((p, i) => {
140
+ if (!convertCustomTypes && p.customType) {
141
+ return p.customType.convertToDatabaseValue(keys[i], this.platform, {
142
+ key: p.name,
143
+ mode: 'hydration',
144
+ });
145
+ }
146
+ return keys[i];
147
+ });
139
148
  hash = Utils.getPrimaryKeyHash(keys);
140
149
  }
141
150
  schema ??= meta.schema ?? this.em.config.getSchema();
@@ -303,6 +312,7 @@ export class UnitOfWork {
303
312
  cs.entity.__helper.__processing = true;
304
313
  }
305
314
  await this.eventManager.dispatchEvent(EventType.onFlush, { em: this.em, uow: this });
315
+ this.filterCollectionUpdates();
306
316
  // nothing to do, do not start transaction
307
317
  if (this.changeSets.size === 0 && this.collectionUpdates.size === 0 && this.extraUpdates.size === 0) {
308
318
  return void (await this.eventManager.dispatchEvent(EventType.afterFlush, { em: this.em, uow: this }));
@@ -311,9 +321,11 @@ export class UnitOfWork {
311
321
  const platform = this.em.getPlatform();
312
322
  const runInTransaction = !this.em.isInTransaction() && platform.supportsTransactions() && this.em.config.get('implicitTransactions');
313
323
  if (runInTransaction) {
324
+ const loggerContext = Utils.merge({ id: this.em._id }, this.em.getLoggerContext({ disableContextResolution: true }));
314
325
  await this.em.getConnection('write').transactional(trx => this.persistToDatabase(groups, trx), {
315
326
  ctx: oldTx,
316
327
  eventBroadcaster: new TransactionEventBroadcaster(this.em),
328
+ loggerContext,
317
329
  });
318
330
  }
319
331
  else {
@@ -886,23 +898,34 @@ export class UnitOfWork {
886
898
  }
887
899
  }
888
900
  async commitCollectionUpdates(ctx) {
889
- const collectionUpdates = [];
901
+ this.filterCollectionUpdates();
902
+ const loggerContext = Utils.merge({ id: this.em._id }, this.em.getLoggerContext({ disableContextResolution: true }));
903
+ await this.em.getDriver().syncCollections(this.collectionUpdates, {
904
+ ctx,
905
+ schema: this.em.schema,
906
+ loggerContext,
907
+ });
908
+ for (const coll of this.collectionUpdates) {
909
+ coll.takeSnapshot();
910
+ }
911
+ }
912
+ filterCollectionUpdates() {
890
913
  for (const coll of this.collectionUpdates) {
914
+ let skip = true;
891
915
  if (coll.property.owner || coll.getItems(false).filter(item => !item.__helper.__initialized).length > 0) {
892
916
  if (this.platform.usesPivotTable()) {
893
- collectionUpdates.push(coll);
917
+ skip = false;
894
918
  }
895
919
  }
896
920
  else if (coll.property.kind === ReferenceKind.ONE_TO_MANY && coll.getSnapshot() === undefined) {
897
- collectionUpdates.push(coll);
921
+ skip = false;
898
922
  }
899
923
  else if (coll.property.kind === ReferenceKind.MANY_TO_MANY && !coll.property.owner) {
900
- collectionUpdates.push(coll);
924
+ skip = false;
925
+ }
926
+ if (skip) {
927
+ this.collectionUpdates.delete(coll);
901
928
  }
902
- }
903
- await this.em.getDriver().syncCollections(collectionUpdates, { ctx, schema: this.em.schema });
904
- for (const coll of this.collectionUpdates) {
905
- coll.takeSnapshot();
906
929
  }
907
930
  }
908
931
  /**
@@ -78,6 +78,7 @@ export declare class Configuration<D extends IDatabaseDriver = IDatabaseDriver,
78
78
  upsertManaged: true;
79
79
  forceEntityConstructor: false;
80
80
  forceUndefined: false;
81
+ processOnCreateHooksEarly: false;
81
82
  ensureDatabase: true;
82
83
  ensureIndexes: false;
83
84
  batchSize: number;
@@ -332,6 +333,11 @@ export interface MikroORMOptions<D extends IDatabaseDriver = IDatabaseDriver, EM
332
333
  upsertManaged: boolean;
333
334
  forceEntityConstructor: boolean | (Constructor<AnyEntity> | string)[];
334
335
  forceUndefined: boolean;
336
+ /**
337
+ * Property `onCreate` hooks are normally executed during `flush` operation.
338
+ * With this option, they will be processed early inside `em.create()` method.
339
+ */
340
+ processOnCreateHooksEarly: boolean;
335
341
  forceUtcTimezone?: boolean;
336
342
  timezone?: string;
337
343
  ensureDatabase: boolean | EnsureDatabaseOptions;
@@ -340,7 +346,7 @@ export interface MikroORMOptions<D extends IDatabaseDriver = IDatabaseDriver, EM
340
346
  useBatchUpdates?: boolean;
341
347
  batchSize: number;
342
348
  hydrator: HydratorConstructor;
343
- loadStrategy: LoadStrategy | 'select-in' | 'joined';
349
+ loadStrategy: LoadStrategy | `${LoadStrategy}`;
344
350
  dataloader: DataloaderType | boolean;
345
351
  populateWhere?: PopulateHint | `${PopulateHint}`;
346
352
  flushMode: FlushMode | 'commit' | 'auto' | 'always';
@@ -68,6 +68,7 @@ export class Configuration {
68
68
  upsertManaged: true,
69
69
  forceEntityConstructor: false,
70
70
  forceUndefined: false,
71
+ processOnCreateHooksEarly: false,
71
72
  ensureDatabase: true,
72
73
  ensureIndexes: false,
73
74
  batchSize: 300,
@@ -192,7 +192,7 @@ export class ConfigurationLoader {
192
192
  static registerDotenv(options) {
193
193
  const path = process.env.MIKRO_ORM_ENV ?? ((options?.baseDir ?? process.cwd()) + '/.env');
194
194
  const env = {};
195
- dotenv.config({ path, processEnv: env });
195
+ dotenv.config({ path, processEnv: env, quiet: true });
196
196
  // only propagate known env vars
197
197
  for (const key of Object.keys(env)) {
198
198
  if (key.startsWith('MIKRO_ORM_')) {
@@ -332,7 +332,7 @@ export class ConfigurationLoader {
332
332
  // inspired by https://github.com/facebook/docusaurus/pull/3386
333
333
  static checkPackageVersion() {
334
334
  const coreVersion = Utils.getORMVersion();
335
- if (process.env.MIKRO_ORM_ALLOW_VERSION_MISMATCH) {
335
+ if (process.env.MIKRO_ORM_ALLOW_VERSION_MISMATCH || coreVersion === 'N/A') {
336
336
  return coreVersion;
337
337
  }
338
338
  const deps = this.getORMPackages();
package/utils/Cursor.js CHANGED
@@ -107,6 +107,9 @@ export class Cursor {
107
107
  if (Utils.isEntity(value, true)) {
108
108
  value = helper(value).getPrimaryKey();
109
109
  }
110
+ if (Utils.isScalarReference(value)) {
111
+ value = value.unwrap();
112
+ }
110
113
  if (object) {
111
114
  return ({ [prop]: value });
112
115
  }
@@ -34,8 +34,13 @@ export declare class DataloaderUtils {
34
34
  */
35
35
  static getColFilter<T, S extends T>(collection: Collection<any>): (result: T) => result is S;
36
36
  /**
37
- * Returns the collection dataloader batchLoadFn, which aggregates collections by entity,
37
+ * Returns the 1:M collection dataloader batchLoadFn, which aggregates collections by entity,
38
38
  * makes one query per entity and maps each input collection to the corresponding result.
39
39
  */
40
40
  static getColBatchLoadFn(em: EntityManager): DataLoader.BatchLoadFn<[Collection<any>, Omit<InitCollectionOptions<any, any>, 'dataloader'>?], any>;
41
+ /**
42
+ * Returns the M:N collection dataloader batchLoadFn, which aggregates collections by entity,
43
+ * makes one query per entity and maps each input collection to the corresponding result.
44
+ */
45
+ static getManyToManyColBatchLoadFn(em: EntityManager): DataLoader.BatchLoadFn<[Collection<any>, Omit<InitCollectionOptions<any, any>, 'dataloader'>?], any>;
41
46
  }
@@ -1,6 +1,5 @@
1
1
  import { Collection } from '../entity/Collection.js';
2
2
  import { helper } from '../entity/wrap.js';
3
- import { ReferenceKind } from '../enums.js';
4
3
  import { Reference } from '../entity/Reference.js';
5
4
  import { Utils } from './Utils.js';
6
5
  export class DataloaderUtils {
@@ -153,29 +152,11 @@ export class DataloaderUtils {
153
152
  };
154
153
  }
155
154
  /**
156
- * Returns the collection dataloader batchLoadFn, which aggregates collections by entity,
155
+ * Returns the 1:M collection dataloader batchLoadFn, which aggregates collections by entity,
157
156
  * makes one query per entity and maps each input collection to the corresponding result.
158
157
  */
159
158
  static getColBatchLoadFn(em) {
160
159
  return async (collsWithOpts) => {
161
- const prop = collsWithOpts[0][0].property;
162
- if (prop.kind === ReferenceKind.MANY_TO_MANY && em.getPlatform().usesPivotTable()) {
163
- const options = {};
164
- const wrap = (cond) => ({ [prop.name]: cond });
165
- const orderBy = Utils.asArray(collsWithOpts[0][1]?.orderBy).map(o => wrap(o));
166
- const populate = wrap(collsWithOpts[0][1]?.populate);
167
- const owners = collsWithOpts.map(c => c[0].owner);
168
- const $or = [];
169
- // a bit of a hack, but we need to prefix the key, since we have only a column name, not a property name
170
- const alias = em.config.getNamingStrategy().aliasName(prop.pivotEntity, 0);
171
- const fk = `${alias}.${Utils.getPrimaryKeyHash(prop.joinColumns)}`;
172
- for (const c of collsWithOpts) {
173
- $or.push({ $and: [c[1]?.where ?? {}, { [fk]: c[0].owner }] });
174
- options.refresh ??= c[1]?.refresh;
175
- }
176
- options.where = wrap({ $or });
177
- return em.getEntityLoader().findChildrenFromPivotTable(owners, prop, options, orderBy, populate, collsWithOpts[0][1]?.ref);
178
- }
179
160
  const entitiesAndOptsMap = DataloaderUtils.groupInversedOrMappedKeysByEntityAndOpts(collsWithOpts);
180
161
  const promises = DataloaderUtils.entitiesAndOptsMapToQueries(entitiesAndOptsMap, em);
181
162
  const resultsMap = new Map(await Promise.all(promises));
@@ -193,4 +174,40 @@ export class DataloaderUtils {
193
174
  });
194
175
  };
195
176
  }
177
+ /**
178
+ * Returns the M:N collection dataloader batchLoadFn, which aggregates collections by entity,
179
+ * makes one query per entity and maps each input collection to the corresponding result.
180
+ */
181
+ static getManyToManyColBatchLoadFn(em) {
182
+ return async (collsWithOpts) => {
183
+ const groups = new Map();
184
+ for (const [col, opts] of collsWithOpts) {
185
+ const key = `${col.property.targetMeta.className}.${col.property.name}|${JSON.stringify(opts ?? {})}`;
186
+ const value = groups.get(key) ?? [];
187
+ value.push([col, opts ?? {}]);
188
+ groups.set(key, value);
189
+ }
190
+ const ret = [];
191
+ for (const group of groups.values()) {
192
+ const prop = group[0][0].property;
193
+ const options = {};
194
+ const wrap = (cond) => ({ [prop.name]: cond });
195
+ const orderBy = Utils.asArray(group[0][1]?.orderBy).map(o => wrap(o));
196
+ const populate = wrap(group[0][1]?.populate);
197
+ const owners = group.map(c => c[0].owner);
198
+ const $or = [];
199
+ // a bit of a hack, but we need to prefix the key, since we have only a column name, not a property name
200
+ const alias = em.config.getNamingStrategy().aliasName(prop.pivotEntity, 0);
201
+ const fk = `${alias}.${Utils.getPrimaryKeyHash(prop.joinColumns)}`;
202
+ for (const c of group) {
203
+ $or.push({ $and: [c[1]?.where ?? {}, { [fk]: c[0].owner }] });
204
+ options.refresh ??= c[1]?.refresh;
205
+ }
206
+ options.where = wrap({ $or });
207
+ const r = await em.getEntityLoader().findChildrenFromPivotTable(owners, prop, options, orderBy, populate, group[0][1]?.ref);
208
+ ret.push(...r);
209
+ }
210
+ return ret;
211
+ };
212
+ }
196
213
  }
@@ -1,6 +1,8 @@
1
1
  import type { EntityData, EntityDictionary, EntityMetadata, EntityProperty, IMetadataStorage } from '../typings.js';
2
2
  import type { Platform } from '../platforms/Platform.js';
3
- type Comparator<T> = (a: T, b: T) => EntityData<T>;
3
+ type Comparator<T> = (a: T, b: T, options?: {
4
+ includeInverseSides?: boolean;
5
+ }) => EntityData<T>;
4
6
  type ResultMapper<T> = (result: EntityData<T>) => EntityData<T> | null;
5
7
  type SnapshotGenerator<T> = (entity: T) => EntityData<T>;
6
8
  type CompositeKeyPart = string | CompositeKeyPart[];
@@ -18,7 +20,9 @@ export declare class EntityComparator {
18
20
  /**
19
21
  * Computes difference between two entities.
20
22
  */
21
- diffEntities<T>(entityName: string, a: EntityData<T>, b: EntityData<T>): EntityData<T>;
23
+ diffEntities<T>(entityName: string, a: EntityData<T>, b: EntityData<T>, options?: {
24
+ includeInverseSides?: boolean;
25
+ }): EntityData<T>;
22
26
  matching<T>(entityName: string, a: EntityData<T>, b: EntityData<T>): boolean;
23
27
  /**
24
28
  * Removes ORM specific code from entities and prepares it for serializing. Used before change set computation.
@@ -20,9 +20,9 @@ export class EntityComparator {
20
20
  /**
21
21
  * Computes difference between two entities.
22
22
  */
23
- diffEntities(entityName, a, b) {
23
+ diffEntities(entityName, a, b, options) {
24
24
  const comparator = this.getEntityComparator(entityName);
25
- return Utils.callCompiledFunction(comparator, a, b);
25
+ return Utils.callCompiledFunction(comparator, a, b, options);
26
26
  }
27
27
  matching(entityName, a, b) {
28
28
  const diff = this.diffEntities(entityName, a, b);
@@ -155,6 +155,7 @@ export class EntityComparator {
155
155
  const lines = [];
156
156
  const context = new Map();
157
157
  context.set('getCompositeKeyValue', (val) => Utils.flatten(Utils.getCompositeKeyValue(val, meta, 'convertToDatabaseValue', this.platform)));
158
+ context.set('getPrimaryKeyHash', (val) => Utils.getPrimaryKeyHash(Utils.asArray(val)));
158
159
  if (meta.primaryKeys.length > 1) {
159
160
  lines.push(` const pks = entity.__helper.__pk ? getCompositeKeyValue(entity.__helper.__pk) : [`);
160
161
  meta.primaryKeys.forEach(pk => {
@@ -170,14 +171,23 @@ export class EntityComparator {
170
171
  }
171
172
  else {
172
173
  const pk = meta.primaryKeys[0];
173
- if (meta.properties[pk].kind !== ReferenceKind.SCALAR) {
174
+ const prop = meta.properties[pk];
175
+ if (prop.kind !== ReferenceKind.SCALAR) {
174
176
  lines.push(` if (entity${this.wrap(pk)} != null && (entity${this.wrap(pk)}.__entity || entity${this.wrap(pk)}.__reference)) return entity${this.wrap(pk)}.__helper.getSerializedPrimaryKey();`);
175
177
  }
176
178
  const serializedPrimaryKey = meta.props.find(p => p.serializedPrimaryKey);
177
179
  if (serializedPrimaryKey) {
178
180
  lines.push(` return '' + entity.${serializedPrimaryKey.name};`);
179
181
  }
180
- lines.push(` return '' + entity.${meta.primaryKeys[0]};`);
182
+ else if (prop.customType) {
183
+ const convertorKey = this.registerCustomType(meta.properties[pk], context);
184
+ const idx = this.tmpIndex++;
185
+ lines.push(` const val_${idx} = convertToDatabaseValue_${convertorKey}(entity${this.wrap(pk)});`);
186
+ lines.push(` return getPrimaryKeyHash(val_${idx});`);
187
+ }
188
+ else {
189
+ lines.push(` return '' + entity${this.wrap(pk)};`);
190
+ }
181
191
  }
182
192
  const code = `// compiled pk serializer for entity ${meta.className}\n`
183
193
  + `return function(entity) {\n${lines.join('\n')}\n}`;
@@ -531,11 +541,22 @@ export class EntityComparator {
531
541
  context.set('compareBuffers', compareBuffers);
532
542
  context.set('compareObjects', compareObjects);
533
543
  context.set('equals', equals);
534
- meta.comparableProps.forEach(prop => {
535
- lines.push(this.getPropertyComparator(prop, context));
536
- });
544
+ for (const prop of meta.comparableProps) {
545
+ // skip properties that are not hydrated
546
+ if (prop.hydrate !== false) {
547
+ lines.push(this.getPropertyComparator(prop, context));
548
+ }
549
+ }
550
+ // also compare 1:1 inverse sides, important for `factory.mergeData`
551
+ lines.push(`if (options?.includeInverseSides) {`);
552
+ for (const prop of meta.bidirectionalRelations) {
553
+ if (prop.kind === ReferenceKind.ONE_TO_ONE && !prop.owner && prop.hydrate !== false) {
554
+ lines.push(this.getPropertyComparator(prop, context));
555
+ }
556
+ }
557
+ lines.push(`}`);
537
558
  const code = `// compiled comparator for entity ${meta.className}\n`
538
- + `return function(last, current) {\n const diff = {};\n${lines.join('\n')}\n return diff;\n}`;
559
+ + `return function(last, current, options) {\n const diff = {};\n${lines.join('\n')}\n return diff;\n}`;
539
560
  const comparator = Utils.createFunction(context, code);
540
561
  this.comparators.set(entityName, comparator);
541
562
  return comparator;
@@ -1,10 +1,16 @@
1
1
  import type { Dictionary, EntityMetadata, EntityProperty, FilterDef, FilterQuery } from '../typings.js';
2
2
  import type { Platform } from '../platforms/Platform.js';
3
3
  import type { MetadataStorage } from '../metadata/MetadataStorage.js';
4
+ /** @internal */
4
5
  export declare class QueryHelper {
5
6
  static readonly SUPPORTED_OPERATORS: string[];
6
7
  static processParams(params: unknown): any;
7
8
  static processObjectParams<T extends object>(params?: T): T;
9
+ /**
10
+ * converts `{ account: { $or: [ [Object], [Object] ] } }`
11
+ * to `{ $or: [ { account: [Object] }, { account: [Object] } ] }`
12
+ */
13
+ static liftGroupOperators<T extends object>(where: Dictionary, meta: EntityMetadata<T>, metadata: MetadataStorage, key?: string): string | undefined;
8
14
  static inlinePrimaryKeyObjects<T extends object>(where: Dictionary, meta: EntityMetadata<T>, metadata: MetadataStorage, key?: string): boolean;
9
15
  static processWhere<T extends object>(options: ProcessWhereOptions<T>): FilterQuery<T>;
10
16
  static getActiveFilters(entityName: string, options: Dictionary<boolean | Dictionary> | string[] | boolean, filters: Dictionary<FilterDef>): FilterDef[];