@mikro-orm/core 6.4.17-dev.6 → 6.4.17-dev.61

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 (48) hide show
  1. package/EntityManager.d.ts +11 -2
  2. package/EntityManager.js +23 -15
  3. package/README.md +1 -1
  4. package/connections/Connection.d.ts +4 -2
  5. package/connections/Connection.js +2 -2
  6. package/decorators/Entity.d.ts +14 -0
  7. package/decorators/Indexed.d.ts +2 -2
  8. package/decorators/Transactional.d.ts +1 -0
  9. package/decorators/Transactional.js +3 -3
  10. package/drivers/IDatabaseDriver.d.ts +4 -0
  11. package/entity/ArrayCollection.d.ts +3 -1
  12. package/entity/ArrayCollection.js +7 -2
  13. package/entity/EntityFactory.d.ts +6 -0
  14. package/entity/EntityFactory.js +12 -1
  15. package/entity/EntityHelper.js +4 -1
  16. package/entity/EntityLoader.js +16 -14
  17. package/entity/Reference.d.ts +5 -0
  18. package/entity/Reference.js +16 -0
  19. package/entity/WrappedEntity.js +1 -1
  20. package/entity/defineEntity.d.ts +528 -0
  21. package/entity/defineEntity.js +684 -0
  22. package/entity/index.d.ts +1 -0
  23. package/entity/index.js +1 -0
  24. package/hydration/ObjectHydrator.js +1 -1
  25. package/index.d.ts +1 -1
  26. package/index.mjs +2 -0
  27. package/metadata/MetadataDiscovery.d.ts +0 -1
  28. package/metadata/MetadataDiscovery.js +6 -11
  29. package/package.json +3 -3
  30. package/platforms/Platform.d.ts +3 -1
  31. package/types/BooleanType.d.ts +1 -1
  32. package/typings.d.ts +16 -8
  33. package/typings.js +10 -2
  34. package/unit-of-work/ChangeSetComputer.js +3 -1
  35. package/unit-of-work/ChangeSetPersister.d.ts +4 -2
  36. package/unit-of-work/ChangeSetPersister.js +13 -9
  37. package/unit-of-work/UnitOfWork.d.ts +1 -0
  38. package/unit-of-work/UnitOfWork.js +25 -11
  39. package/utils/Configuration.d.ts +6 -0
  40. package/utils/Configuration.js +1 -0
  41. package/utils/ConfigurationLoader.js +1 -1
  42. package/utils/EntityComparator.js +6 -3
  43. package/utils/QueryHelper.js +3 -3
  44. package/utils/RawQueryFragment.d.ts +34 -0
  45. package/utils/RawQueryFragment.js +35 -0
  46. package/utils/Utils.d.ts +2 -2
  47. package/utils/Utils.js +24 -5
  48. package/utils/upsert-utils.js +9 -1
package/entity/index.js CHANGED
@@ -27,3 +27,4 @@ __exportStar(require("./Reference"), exports);
27
27
  __exportStar(require("./BaseEntity"), exports);
28
28
  __exportStar(require("./WrappedEntity"), exports);
29
29
  __exportStar(require("./wrap"), exports);
30
+ __exportStar(require("./defineEntity"), exports);
@@ -65,7 +65,7 @@ class ObjectHydrator extends Hydrator_1.Hydrator {
65
65
  const ret = [];
66
66
  const idx = this.tmpIndex++;
67
67
  const nullVal = this.config.get('forceUndefined') ? 'undefined' : 'null';
68
- if (prop.getter && !prop.setter) {
68
+ if (prop.getter && !prop.setter && prop.persist === false) {
69
69
  return [];
70
70
  }
71
71
  if (prop.ref) {
package/index.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * @packageDocumentation
3
3
  * @module core
4
4
  */
5
- export { Constructor, ConnectionType, Dictionary, PrimaryKeyProp, Primary, IPrimaryKey, ObjectQuery, FilterQuery, IWrappedEntity, EntityName, EntityData, Highlighter, MaybePromise, AnyEntity, EntityClass, EntityProperty, EntityMetadata, QBFilterQuery, PopulateOptions, Populate, Loaded, New, LoadedReference, LoadedCollection, IMigrator, IMigrationGenerator, MigratorEvent, GetRepository, EntityRepositoryType, MigrationObject, DeepPartial, PrimaryProperty, Cast, IsUnknown, EntityDictionary, EntityDTO, MigrationDiff, GenerateOptions, FilterObject, IEntityGenerator, ISeedManager, EntityClassGroup, OptionalProps, EagerProps, HiddenProps, RequiredEntityData, CheckCallback, SimpleColumnMeta, Rel, Ref, ScalarRef, EntityRef, ISchemaGenerator, UmzugMigration, MigrateOptions, MigrationResult, MigrationRow, EntityKey, EntityValue, EntityDataValue, FilterKey, Opt, EntityType, FromEntityType, Selected, IsSubset, NoInfer, EntityProps, ExpandProperty, ExpandScalar, FilterItemValue, ExpandQuery, Scalar, ExpandHint, Hidden, FilterValue, MergeLoaded, MergeSelected, Config, DefineConfig, TypeConfig, ClearDatabaseOptions, CreateSchemaOptions, EnsureDatabaseOptions, UpdateSchemaOptions, DropSchemaOptions, RefreshDatabaseOptions, AutoPath, UnboxArray, MetadataProcessor, ImportsResolver, } from './typings';
5
+ export { Constructor, ConnectionType, Dictionary, PrimaryKeyProp, Primary, IPrimaryKey, ObjectQuery, FilterQuery, IWrappedEntity, EntityName, EntityData, Highlighter, MaybePromise, AnyEntity, EntityClass, EntityProperty, EntityMetadata, QBFilterQuery, PopulateOptions, Populate, Loaded, New, LoadedReference, LoadedCollection, IMigrator, IMigrationGenerator, MigratorEvent, GetRepository, EntityRepositoryType, MigrationObject, DeepPartial, PrimaryProperty, Cast, IsUnknown, EntityDictionary, EntityDTO, MigrationDiff, GenerateOptions, FilterObject, IEntityGenerator, ISeedManager, EntityClassGroup, OptionalProps, EagerProps, HiddenProps, RequiredEntityData, CheckCallback, IndexCallback, SimpleColumnMeta, Rel, Ref, ScalarRef, EntityRef, ISchemaGenerator, UmzugMigration, MigrateOptions, MigrationResult, MigrationRow, EntityKey, EntityValue, EntityDataValue, FilterKey, Opt, EntityType, FromEntityType, Selected, IsSubset, NoInfer, EntityProps, ExpandProperty, ExpandScalar, FilterItemValue, ExpandQuery, Scalar, ExpandHint, Hidden, FilterValue, MergeLoaded, MergeSelected, Config, DefineConfig, TypeConfig, ClearDatabaseOptions, CreateSchemaOptions, EnsureDatabaseOptions, UpdateSchemaOptions, DropSchemaOptions, RefreshDatabaseOptions, AutoPath, UnboxArray, MetadataProcessor, ImportsResolver, } from './typings';
6
6
  export * from './enums';
7
7
  export * from './errors';
8
8
  export * from './exceptions';
package/index.mjs CHANGED
@@ -182,11 +182,13 @@ export const compareBuffers = mod.compareBuffers;
182
182
  export const compareObjects = mod.compareObjects;
183
183
  export const createSqlFunction = mod.createSqlFunction;
184
184
  export const defineConfig = mod.defineConfig;
185
+ export const defineEntity = mod.defineEntity;
185
186
  export const equals = mod.equals;
186
187
  export const getOnConflictFields = mod.getOnConflictFields;
187
188
  export const getOnConflictReturningFields = mod.getOnConflictReturningFields;
188
189
  export const helper = mod.helper;
189
190
  export const parseJsonSafe = mod.parseJsonSafe;
191
+ export const quote = mod.quote;
190
192
  export const raw = mod.raw;
191
193
  export const ref = mod.ref;
192
194
  export const rel = mod.rel;
@@ -54,7 +54,6 @@ export declare class MetadataDiscovery {
54
54
  private initAutoincrement;
55
55
  private initCheckConstraints;
56
56
  private initGeneratedColumn;
57
- private createColumnMappingObject;
58
57
  private getDefaultVersionValue;
59
58
  private inferDefaultValue;
60
59
  private initDefaultValue;
@@ -969,7 +969,7 @@ class MetadataDiscovery {
969
969
  }
970
970
  }
971
971
  initCheckConstraints(meta) {
972
- const map = this.createColumnMappingObject(meta);
972
+ const map = meta.createColumnMappingObject();
973
973
  for (const check of meta.checks) {
974
974
  const columns = check.property ? meta.properties[check.property].fieldNames : [];
975
975
  check.name ??= this.namingStrategy.indexName(meta.tableName, columns, 'check');
@@ -992,19 +992,11 @@ class MetadataDiscovery {
992
992
  }
993
993
  return;
994
994
  }
995
- const map = this.createColumnMappingObject(meta);
995
+ const map = meta.createColumnMappingObject();
996
996
  if (prop.generated instanceof Function) {
997
997
  prop.generated = prop.generated(map);
998
998
  }
999
999
  }
1000
- createColumnMappingObject(meta) {
1001
- return Object.values(meta.properties).reduce((o, prop) => {
1002
- if (prop.fieldNames) {
1003
- o[prop.name] = prop.fieldNames[0];
1004
- }
1005
- return o;
1006
- }, {});
1007
- }
1008
1000
  getDefaultVersionValue(meta, prop) {
1009
1001
  if (typeof prop.defaultRaw !== 'undefined') {
1010
1002
  return prop.defaultRaw;
@@ -1058,7 +1050,7 @@ class MetadataDiscovery {
1058
1050
  prop.defaultRaw = this.platform.formatQuery(raw.sql, raw.params);
1059
1051
  return;
1060
1052
  }
1061
- if (prop.customType instanceof types_1.ArrayType && Array.isArray(prop.default)) {
1053
+ if (Array.isArray(prop.default) && prop.customType) {
1062
1054
  val = prop.customType.convertToDatabaseValue(prop.default, this.platform);
1063
1055
  }
1064
1056
  prop.defaultRaw = typeof val === 'string' ? `'${val}'` : '' + val;
@@ -1112,6 +1104,9 @@ class MetadataDiscovery {
1112
1104
  if (prop.kind === enums_1.ReferenceKind.SCALAR && !prop.customType && prop.columnTypes && ['json', 'jsonb'].includes(prop.columnTypes[0])) {
1113
1105
  prop.customType = new types_1.JsonType();
1114
1106
  }
1107
+ if (prop.kind === enums_1.ReferenceKind.EMBEDDED && !prop.customType && (prop.object || prop.array)) {
1108
+ prop.customType = new types_1.JsonType();
1109
+ }
1115
1110
  if (!prop.customType && prop.array && prop.items) {
1116
1111
  prop.customType = new types_1.EnumArrayType(`${meta.className}.${prop.name}`, prop.items);
1117
1112
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/core",
3
- "version": "6.4.17-dev.6",
3
+ "version": "6.4.17-dev.61",
4
4
  "description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
5
5
  "main": "index.js",
6
6
  "module": "index.mjs",
@@ -60,11 +60,11 @@
60
60
  },
61
61
  "dependencies": {
62
62
  "dataloader": "2.2.3",
63
- "dotenv": "16.5.0",
63
+ "dotenv": "17.2.1",
64
64
  "esprima": "4.0.1",
65
65
  "fs-extra": "11.3.0",
66
66
  "globby": "11.1.0",
67
- "mikro-orm": "6.4.17-dev.6",
67
+ "mikro-orm": "6.4.17-dev.61",
68
68
  "reflect-metadata": "0.2.2"
69
69
  }
70
70
  }
@@ -173,7 +173,9 @@ export declare abstract class Platform {
173
173
  getExtension<T>(extensionName: string, extensionKey: string, moduleName: string, em: EntityManager): T;
174
174
  getSchemaGenerator(driver: IDatabaseDriver, em?: EntityManager): ISchemaGenerator;
175
175
  processDateProperty(value: unknown): string | number | Date;
176
- quoteIdentifier(id: string, quote?: string): string;
176
+ quoteIdentifier(id: string | {
177
+ toString: () => string;
178
+ }, quote?: string): string;
177
179
  quoteValue(value: any): string;
178
180
  escape(value: any): string;
179
181
  formatQuery(sql: string, params: readonly any[]): string;
@@ -1,7 +1,7 @@
1
1
  import { Type } from './Type';
2
2
  import type { Platform } from '../platforms';
3
3
  import type { EntityProperty } from '../typings';
4
- export declare class BooleanType extends Type<number | null | undefined, number | null | undefined> {
4
+ export declare class BooleanType extends Type<boolean | null | undefined, boolean | null | undefined> {
5
5
  getColumnType(prop: EntityProperty, platform: Platform): string;
6
6
  compareAsType(): string;
7
7
  ensureComparable(): boolean;
package/typings.d.ts CHANGED
@@ -6,7 +6,7 @@ import type { SerializationContext, SerializeOptions } from './serialization';
6
6
  import type { EntitySchema, MetadataStorage } from './metadata';
7
7
  import type { Type, types } from './types';
8
8
  import type { Platform } from './platforms';
9
- import type { Configuration } from './utils';
9
+ import type { Configuration, RawQueryFragment } from './utils';
10
10
  import type { EntityManager } from './EntityManager';
11
11
  import type { EmbeddedPrefixMode } from './decorators/Embedded';
12
12
  import type { EventSubscriber } from './events';
@@ -73,6 +73,7 @@ export type Primary<T> = IsAny<T> extends true ? any : T extends {
73
73
  } ? ReadonlyPrimary<PK> : T extends {
74
74
  id?: infer PK;
75
75
  } ? ReadonlyPrimary<PK> : T;
76
+ /** @internal */
76
77
  export type PrimaryProperty<T> = T extends {
77
78
  [PrimaryKeyProp]?: infer PK;
78
79
  } ? (PK extends keyof T ? PK : (PK extends any[] ? PK[number] : never)) : T extends {
@@ -287,8 +288,14 @@ export type EntityDTO<T, C extends TypeConfig = never> = {
287
288
  } & {
288
289
  [K in keyof T as DTOOptionalKeys<T, K>]?: EntityDTOProp<T, T[K], C> | AddOptional<T[K]>;
289
290
  };
290
- type CheckKey<T> = IsUnknown<T> extends false ? keyof T : string;
291
- export type CheckCallback<T> = (columns: Record<CheckKey<T>, string>) => string;
291
+ type PropertyName<T> = IsUnknown<T> extends false ? keyof T : string;
292
+ type TableName = {
293
+ name: string;
294
+ schema?: string;
295
+ toString: () => string;
296
+ };
297
+ export type IndexCallback<T> = (table: TableName, columns: Record<PropertyName<T>, string>) => string | RawQueryFragment;
298
+ export type CheckCallback<T> = (columns: Record<PropertyName<T>, string>) => string;
292
299
  export type GeneratedColumnCallback<T> = (columns: Record<keyof T, string>) => string;
293
300
  export interface CheckConstraint<T = any> {
294
301
  name?: string;
@@ -396,6 +403,7 @@ export declare class EntityMetadata<T = any> {
396
403
  removeProperty(name: string, sync?: boolean): void;
397
404
  getPrimaryProps(): EntityProperty<T>[];
398
405
  getPrimaryProp(): EntityProperty<T>;
406
+ createColumnMappingObject(): Dictionary<any>;
399
407
  get tableName(): string;
400
408
  set tableName(name: string);
401
409
  sync(initIndexes?: boolean): void;
@@ -447,18 +455,18 @@ export interface EntityMetadata<T = any> {
447
455
  uniqueProps: EntityProperty<T>[];
448
456
  getterProps: EntityProperty<T>[];
449
457
  indexes: {
450
- properties: EntityKey<T> | EntityKey<T>[];
458
+ properties?: EntityKey<T> | EntityKey<T>[];
451
459
  name?: string;
452
460
  type?: string;
453
461
  options?: Dictionary;
454
- expression?: string;
462
+ expression?: string | IndexCallback<T>;
455
463
  }[];
456
464
  uniques: {
457
- properties: EntityKey<T> | EntityKey<T>[];
465
+ properties?: EntityKey<T> | EntityKey<T>[];
458
466
  name?: string;
459
467
  options?: Dictionary;
460
- expression?: string;
461
- deferMode?: DeferMode;
468
+ expression?: string | IndexCallback<T>;
469
+ deferMode?: DeferMode | `${DeferMode}`;
462
470
  }[];
463
471
  checks: CheckConstraint<T>[];
464
472
  repositoryClass?: string;
package/typings.js CHANGED
@@ -53,6 +53,14 @@ class EntityMetadata {
53
53
  getPrimaryProp() {
54
54
  return this.properties[this.primaryKeys[0]];
55
55
  }
56
+ createColumnMappingObject() {
57
+ return Object.values(this.properties).reduce((o, prop) => {
58
+ if (prop.fieldNames) {
59
+ o[prop.name] = prop.fieldNames[0];
60
+ }
61
+ return o;
62
+ }, {});
63
+ }
56
64
  get tableName() {
57
65
  return this.collection;
58
66
  }
@@ -72,7 +80,7 @@ class EntityMetadata {
72
80
  // `prop.userDefined` is either `undefined` or `false`
73
81
  const discriminator = this.root.discriminatorColumn === prop.name && prop.userDefined === false;
74
82
  // even if we don't have a setter, do not ignore value from database!
75
- const onlyGetter = prop.getter && !prop.setter;
83
+ const onlyGetter = prop.getter && !prop.setter && prop.persist === false;
76
84
  return !prop.inherited && prop.hydrate !== false && !discriminator && !prop.embedded && !onlyGetter;
77
85
  });
78
86
  this.trackingProps = this.hydrateProps
@@ -115,7 +123,7 @@ class EntityMetadata {
115
123
  wrapped.__data[prop.name] = entity_1.Reference.wrapReference(val, prop);
116
124
  // when propagation from inside hydration, we set the FK to the entity data immediately
117
125
  if (val && hydrator.isRunning() && wrapped.__originalEntityData && prop.owner) {
118
- wrapped.__originalEntityData[prop.name] = Utils_1.Utils.getPrimaryKeyValues(val, prop.targetMeta.primaryKeys, true);
126
+ wrapped.__originalEntityData[prop.name] = Utils_1.Utils.getPrimaryKeyValues(val, prop.targetMeta, true);
119
127
  }
120
128
  else {
121
129
  wrapped.__touched = !hydrator.isRunning();
@@ -144,7 +144,9 @@ class ChangeSetComputer {
144
144
  if (!target.isDirty() && changeSet.type !== ChangeSet_1.ChangeSetType.CREATE) {
145
145
  return;
146
146
  }
147
- this.collectionUpdates.add(target);
147
+ if (target.isDirty()) {
148
+ this.collectionUpdates.add(target);
149
+ }
148
150
  if (prop.owner && !this.platform.usesPivotTable()) {
149
151
  changeSet.payload[prop.name] = target.getItems(false).map((item) => item.__helper.__identifier ?? item.__helper.getPrimaryKey());
150
152
  }
@@ -4,6 +4,7 @@ import { type EntityFactory, type EntityValidator } from '../entity';
4
4
  import { type ChangeSet } from './ChangeSet';
5
5
  import { type Configuration } from '../utils';
6
6
  import type { DriverMethodOptions, IDatabaseDriver } from '../drivers';
7
+ import type { EntityManager } from '../EntityManager';
7
8
  export declare class ChangeSetPersister {
8
9
  private readonly driver;
9
10
  private readonly metadata;
@@ -11,10 +12,11 @@ export declare class ChangeSetPersister {
11
12
  private readonly factory;
12
13
  private readonly validator;
13
14
  private readonly config;
15
+ private readonly em;
14
16
  private readonly platform;
15
17
  private readonly comparator;
16
18
  private readonly usesReturningStatement;
17
- constructor(driver: IDatabaseDriver, metadata: MetadataStorage, hydrator: IHydrator, factory: EntityFactory, validator: EntityValidator, config: Configuration);
19
+ constructor(driver: IDatabaseDriver, metadata: MetadataStorage, hydrator: IHydrator, factory: EntityFactory, validator: EntityValidator, config: Configuration, em: EntityManager);
18
20
  executeInserts<T extends object>(changeSets: ChangeSet<T>[], options?: DriverMethodOptions, withSchema?: boolean): Promise<void>;
19
21
  executeUpdates<T extends object>(changeSets: ChangeSet<T>[], batched: boolean, options?: DriverMethodOptions, withSchema?: boolean): Promise<void>;
20
22
  executeDeletes<T extends object>(changeSets: ChangeSet<T>[], options?: DriverMethodOptions, withSchema?: boolean): Promise<void>;
@@ -22,7 +24,7 @@ export declare class ChangeSetPersister {
22
24
  private processProperties;
23
25
  private persistNewEntity;
24
26
  private persistNewEntities;
25
- private propagateSchemaFromMetadata;
27
+ private prepareOptions;
26
28
  private persistNewEntitiesBatch;
27
29
  private persistManagedEntity;
28
30
  private persistManagedEntities;
@@ -13,16 +13,18 @@ class ChangeSetPersister {
13
13
  factory;
14
14
  validator;
15
15
  config;
16
+ em;
16
17
  platform;
17
18
  comparator;
18
19
  usesReturningStatement;
19
- constructor(driver, metadata, hydrator, factory, validator, config) {
20
+ constructor(driver, metadata, hydrator, factory, validator, config, em) {
20
21
  this.driver = driver;
21
22
  this.metadata = metadata;
22
23
  this.hydrator = hydrator;
23
24
  this.factory = factory;
24
25
  this.validator = validator;
25
26
  this.config = config;
27
+ this.em = em;
26
28
  this.platform = this.driver.getPlatform();
27
29
  this.comparator = this.config.getComparator(this.metadata);
28
30
  this.usesReturningStatement = this.platform.usesReturningStatement() || this.platform.usesOutputStatement();
@@ -63,7 +65,7 @@ class ChangeSetPersister {
63
65
  for (let i = 0; i < changeSets.length; i += size) {
64
66
  const chunk = changeSets.slice(i, i + size);
65
67
  const pks = chunk.map(cs => cs.getPrimaryKey());
66
- options = this.propagateSchemaFromMetadata(meta, options);
68
+ options = this.prepareOptions(meta, options);
67
69
  await this.driver.nativeDelete(meta.root.className, { [pk]: { $in: pks } }, options);
68
70
  }
69
71
  }
@@ -91,7 +93,7 @@ class ChangeSetPersister {
91
93
  }
92
94
  async persistNewEntity(meta, changeSet, options) {
93
95
  const wrapped = (0, entity_1.helper)(changeSet.entity);
94
- options = this.propagateSchemaFromMetadata(meta, options, {
96
+ options = this.prepareOptions(meta, options, {
95
97
  convertCustomTypes: false,
96
98
  });
97
99
  const res = await this.driver.nativeInsertMany(meta.className, [changeSet.payload], options);
@@ -117,15 +119,17 @@ class ChangeSetPersister {
117
119
  }
118
120
  }
119
121
  }
120
- propagateSchemaFromMetadata(meta, options, additionalOptions) {
122
+ prepareOptions(meta, options, additionalOptions) {
123
+ const loggerContext = utils_1.Utils.merge({ id: this.em._id }, this.em.getLoggerContext({ disableContextResolution: true }));
121
124
  return {
122
125
  ...options,
123
126
  ...additionalOptions,
124
127
  schema: options?.schema ?? meta.schema,
128
+ loggerContext,
125
129
  };
126
130
  }
127
131
  async persistNewEntitiesBatch(meta, changeSets, options) {
128
- options = this.propagateSchemaFromMetadata(meta, options, {
132
+ options = this.prepareOptions(meta, options, {
129
133
  convertCustomTypes: false,
130
134
  processCollections: false,
131
135
  });
@@ -176,7 +180,7 @@ class ChangeSetPersister {
176
180
  }
177
181
  async persistManagedEntitiesBatch(meta, changeSets, options) {
178
182
  await this.checkOptimisticLocks(meta, changeSets, options);
179
- options = this.propagateSchemaFromMetadata(meta, options, {
183
+ options = this.prepareOptions(meta, options, {
180
184
  convertCustomTypes: false,
181
185
  processCollections: false,
182
186
  });
@@ -236,7 +240,7 @@ class ChangeSetPersister {
236
240
  }
237
241
  async updateEntity(meta, changeSet, options) {
238
242
  const cond = changeSet.getPrimaryKey(true);
239
- options = this.propagateSchemaFromMetadata(meta, options, {
243
+ options = this.prepareOptions(meta, options, {
240
244
  convertCustomTypes: false,
241
245
  });
242
246
  if (meta.concurrencyCheckKeys.size === 0 && (!meta.versionProperty || changeSet.entity[meta.versionProperty] == null)) {
@@ -263,7 +267,7 @@ class ChangeSetPersister {
263
267
  return cond;
264
268
  });
265
269
  const primaryKeys = meta.primaryKeys.concat(...meta.concurrencyCheckKeys);
266
- options = this.propagateSchemaFromMetadata(meta, options, {
270
+ options = this.prepareOptions(meta, options, {
267
271
  fields: primaryKeys,
268
272
  });
269
273
  const res = await this.driver.find(meta.root.className, { $or }, options);
@@ -322,7 +326,7 @@ class ChangeSetPersister {
322
326
  }
323
327
  return val;
324
328
  });
325
- options = this.propagateSchemaFromMetadata(meta, options, {
329
+ options = this.prepareOptions(meta, options, {
326
330
  fields: utils_1.Utils.unique(reloadProps.map(prop => prop.name)),
327
331
  });
328
332
  const data = await this.driver.find(meta.className, { [pk]: { $in: pks } }, options);
@@ -102,6 +102,7 @@ export declare class UnitOfWork {
102
102
  private commitDeleteChangeSets;
103
103
  private commitExtraUpdates;
104
104
  private commitCollectionUpdates;
105
+ private filterCollectionUpdates;
105
106
  /**
106
107
  * Orders change sets so FK constrains are maintained, ensures stable order (needed for node < 11)
107
108
  */
@@ -42,7 +42,7 @@ class UnitOfWork {
42
42
  this.eventManager = this.em.getEventManager();
43
43
  this.comparator = this.em.getComparator();
44
44
  this.changeSetComputer = new ChangeSetComputer_1.ChangeSetComputer(this.em.getValidator(), this.collectionUpdates, this.metadata, this.platform, this.em.config, this.em);
45
- this.changeSetPersister = new ChangeSetPersister_1.ChangeSetPersister(this.em.getDriver(), this.metadata, this.em.config.getHydrator(this.metadata), this.em.getEntityFactory(), this.em.getValidator(), this.em.config);
45
+ this.changeSetPersister = new ChangeSetPersister_1.ChangeSetPersister(this.em.getDriver(), this.metadata, this.em.config.getHydrator(this.metadata), this.em.getEntityFactory(), this.em.getValidator(), this.em.config, this.em);
46
46
  }
47
47
  merge(entity, visited) {
48
48
  const wrapped = (0, entity_1.helper)(entity);
@@ -88,7 +88,7 @@ class UnitOfWork {
88
88
  }
89
89
  wrapped.__loadedProperties.add(key);
90
90
  if ([enums_1.ReferenceKind.MANY_TO_ONE, enums_1.ReferenceKind.ONE_TO_ONE].includes(prop.kind) && Utils_1.Utils.isPlainObject(data[prop.name])) {
91
- data[prop.name] = Utils_1.Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta.primaryKeys, true);
91
+ data[prop.name] = Utils_1.Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
92
92
  }
93
93
  else if (prop.kind === enums_1.ReferenceKind.EMBEDDED && !prop.object && Utils_1.Utils.isPlainObject(data[prop.name])) {
94
94
  for (const p of prop.targetMeta.props) {
@@ -96,7 +96,7 @@ class UnitOfWork {
96
96
  const prefix = prop.prefix === false ? '' : prop.prefix === true ? prop.name + '_' : prop.prefix;
97
97
  data[prefix + p.name] = data[prop.name][p.name];
98
98
  }
99
- data[prop.name] = Utils_1.Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta.primaryKeys, true);
99
+ data[prop.name] = Utils_1.Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
100
100
  }
101
101
  if (forceUndefined) {
102
102
  if (data[key] === null) {
@@ -303,6 +303,7 @@ class UnitOfWork {
303
303
  cs.entity.__helper.__processing = true;
304
304
  }
305
305
  await this.eventManager.dispatchEvent(enums_1.EventType.onFlush, { em: this.em, uow: this });
306
+ this.filterCollectionUpdates();
306
307
  // nothing to do, do not start transaction
307
308
  if (this.changeSets.size === 0 && this.collectionUpdates.size === 0 && this.extraUpdates.size === 0) {
308
309
  return void await this.eventManager.dispatchEvent(enums_1.EventType.afterFlush, { em: this.em, uow: this });
@@ -311,9 +312,11 @@ class UnitOfWork {
311
312
  const platform = this.em.getPlatform();
312
313
  const runInTransaction = !this.em.isInTransaction() && platform.supportsTransactions() && this.em.config.get('implicitTransactions');
313
314
  if (runInTransaction) {
315
+ const loggerContext = Utils_1.Utils.merge({ id: this.em._id }, this.em.getLoggerContext({ disableContextResolution: true }));
314
316
  await this.em.getConnection('write').transactional(trx => this.persistToDatabase(groups, trx), {
315
317
  ctx: oldTx,
316
318
  eventBroadcaster: new events_1.TransactionEventBroadcaster(this.em, this),
319
+ loggerContext,
317
320
  });
318
321
  }
319
322
  else {
@@ -886,23 +889,34 @@ class UnitOfWork {
886
889
  }
887
890
  }
888
891
  async commitCollectionUpdates(ctx) {
889
- const collectionUpdates = [];
892
+ this.filterCollectionUpdates();
893
+ const loggerContext = Utils_1.Utils.merge({ id: this.em._id }, this.em.getLoggerContext({ disableContextResolution: true }));
894
+ await this.em.getDriver().syncCollections(this.collectionUpdates, {
895
+ ctx,
896
+ schema: this.em.schema,
897
+ loggerContext,
898
+ });
899
+ for (const coll of this.collectionUpdates) {
900
+ coll.takeSnapshot();
901
+ }
902
+ }
903
+ filterCollectionUpdates() {
890
904
  for (const coll of this.collectionUpdates) {
905
+ let skip = true;
891
906
  if (coll.property.owner || coll.getItems(false).filter(item => !item.__helper.__initialized).length > 0) {
892
907
  if (this.platform.usesPivotTable()) {
893
- collectionUpdates.push(coll);
908
+ skip = false;
894
909
  }
895
910
  }
896
911
  else if (coll.property.kind === enums_1.ReferenceKind.ONE_TO_MANY && coll.getSnapshot() === undefined) {
897
- collectionUpdates.push(coll);
912
+ skip = false;
898
913
  }
899
914
  else if (coll.property.kind === enums_1.ReferenceKind.MANY_TO_MANY && !coll.property.owner) {
900
- collectionUpdates.push(coll);
915
+ skip = false;
916
+ }
917
+ if (skip) {
918
+ this.collectionUpdates.delete(coll);
901
919
  }
902
- }
903
- await this.em.getDriver().syncCollections(collectionUpdates, { ctx, schema: this.em.schema });
904
- for (const coll of this.collectionUpdates) {
905
- coll.takeSnapshot();
906
920
  }
907
921
  }
908
922
  /**
@@ -77,6 +77,7 @@ export declare class Configuration<D extends IDatabaseDriver = IDatabaseDriver,
77
77
  upsertManaged: true;
78
78
  forceEntityConstructor: false;
79
79
  forceUndefined: false;
80
+ processOnCreateHooksEarly: false;
80
81
  ensureDatabase: true;
81
82
  ensureIndexes: false;
82
83
  batchSize: number;
@@ -345,6 +346,11 @@ export interface MikroORMOptions<D extends IDatabaseDriver = IDatabaseDriver, EM
345
346
  upsertManaged: boolean;
346
347
  forceEntityConstructor: boolean | (Constructor<AnyEntity> | string)[];
347
348
  forceUndefined: boolean;
349
+ /**
350
+ * Property `onCreate` hooks are normally executed during `flush` operation.
351
+ * With this option, they will be processed early inside `em.create()` method.
352
+ */
353
+ processOnCreateHooksEarly: boolean;
348
354
  forceUtcTimezone?: boolean;
349
355
  timezone?: string;
350
356
  ensureDatabase: boolean | EnsureDatabaseOptions;
@@ -71,6 +71,7 @@ class Configuration {
71
71
  upsertManaged: true,
72
72
  forceEntityConstructor: false,
73
73
  forceUndefined: false,
74
+ processOnCreateHooksEarly: false,
74
75
  ensureDatabase: true,
75
76
  ensureIndexes: false,
76
77
  batchSize: 300,
@@ -213,7 +213,7 @@ class ConfigurationLoader {
213
213
  const baseDir = options instanceof Configuration_1.Configuration ? options.get('baseDir') : options?.baseDir;
214
214
  const path = process.env.MIKRO_ORM_ENV ?? ((baseDir ?? process.cwd()) + '/.env');
215
215
  const env = {};
216
- dotenv_1.default.config({ path, processEnv: env });
216
+ dotenv_1.default.config({ path, processEnv: env, quiet: true });
217
217
  // only propagate known env vars
218
218
  for (const key of Object.keys(env)) {
219
219
  if (key.startsWith('MIKRO_ORM_')) {
@@ -534,9 +534,12 @@ class EntityComparator {
534
534
  context.set('compareBuffers', Utils_1.compareBuffers);
535
535
  context.set('compareObjects', Utils_1.compareObjects);
536
536
  context.set('equals', Utils_1.equals);
537
- meta.comparableProps.forEach(prop => {
538
- lines.push(this.getPropertyComparator(prop, context));
539
- });
537
+ for (const prop of meta.comparableProps) {
538
+ // skip properties that are not hydrated
539
+ if (prop.hydrate !== false) {
540
+ lines.push(this.getPropertyComparator(prop, context));
541
+ }
542
+ }
540
543
  const code = `// compiled comparator for entity ${meta.className}\n`
541
544
  + `return function(last, current) {\n const diff = {};\n${lines.join('\n')}\n return diff;\n}`;
542
545
  const comparator = Utils_1.Utils.createFunction(context, code);
@@ -40,7 +40,7 @@ class QueryHelper {
40
40
  if (Array.isArray(where)) {
41
41
  where.forEach((item, i) => {
42
42
  if (this.inlinePrimaryKeyObjects(item, meta, metadata, key)) {
43
- where[i] = Utils_1.Utils.getPrimaryKeyValues(item, meta.primaryKeys, false);
43
+ where[i] = Utils_1.Utils.getPrimaryKeyValues(item, meta, false);
44
44
  }
45
45
  });
46
46
  }
@@ -61,7 +61,7 @@ class QueryHelper {
61
61
  Object.keys(where).forEach(k => {
62
62
  const meta2 = metadata.find(meta.properties[k]?.type) || meta;
63
63
  if (this.inlinePrimaryKeyObjects(where[k], meta2, metadata, k)) {
64
- where[k] = Utils_1.Utils.getPrimaryKeyValues(where[k], meta2.primaryKeys, true);
64
+ where[k] = Utils_1.Utils.getPrimaryKeyValues(where[k], meta2, true);
65
65
  }
66
66
  });
67
67
  return false;
@@ -121,7 +121,7 @@ class QueryHelper {
121
121
  value = QueryHelper.processCustomType(prop, value, platform, undefined, true);
122
122
  }
123
123
  const isJsonProperty = prop?.customType instanceof JsonType_1.JsonType && Utils_1.Utils.isPlainObject(value) && !platform.isRaw(value) && Object.keys(value)[0] !== '$eq';
124
- if (isJsonProperty) {
124
+ if (isJsonProperty && prop?.kind !== enums_1.ReferenceKind.EMBEDDED) {
125
125
  return this.processJsonCondition(o, value, [prop.fieldNames[0]], platform, aliased);
126
126
  }
127
127
  if (Array.isArray(value) && !Utils_1.Utils.isOperator(key) && !QueryHelper.isSupportedOperator(key) && !key.includes('?') && options.type !== 'orderBy') {
@@ -70,6 +70,24 @@ export declare const ALIAS_REPLACEMENT_RE = "\\[::alias::\\]";
70
70
  * ```ts
71
71
  * @Filter({ name: 'long', cond: () => ({ [raw('length(perex)')]: { $gt: 10000 } }) })
72
72
  * ```
73
+ *
74
+ * The `raw` helper can be used within indexes and uniques to write database-agnostic SQL expressions. In that case, you can use `'??'` to tag your database identifiers (table name, column names, index name, ...) inside your expression, and pass those identifiers as a second parameter to the `raw` helper. Internally, those will automatically be quoted according to the database in use:
75
+ *
76
+ * ```ts
77
+ * // On postgres, will produce: create index "index custom_idx_on_name" on "library.author" ("country")
78
+ * // On mysql, will produce: create index `index custom_idx_on_name` on `library.author` (`country`)
79
+ * @Index({ name: 'custom_idx_on_name', expression: (table, columns) => raw(`create index ?? on ?? (??)`, ['custom_idx_on_name', table, columns.name]) })
80
+ * @Entity({ schema: 'library' })
81
+ * export class Author { ... }
82
+ * ```
83
+ *
84
+ * You can also use the `quote` tag function to write database-agnostic SQL expressions. The end-result is the same as using the `raw` function regarding database identifiers quoting, only to have a more elegant expression syntax:
85
+ *
86
+ * ```ts
87
+ * @Index({ name: 'custom_idx_on_name', expression: (table, columns) => quote`create index ${'custom_idx_on_name'} on ${table} (${columns.name})` })
88
+ * @Entity({ schema: 'library' })
89
+ * export class Author { ... }
90
+ * ```
73
91
  */
74
92
  export declare function raw<T extends object = any, R = any>(sql: EntityKey<T> | EntityKey<T>[] | AnyString | ((alias: string) => string) | RawQueryFragment, params?: readonly unknown[] | Dictionary<unknown>): R;
75
93
  /**
@@ -94,3 +112,19 @@ export declare namespace sql {
94
112
  var upper: <T extends object>(key: string | ((alias: string) => string)) => string;
95
113
  }
96
114
  export declare function createSqlFunction<T extends object, R = string>(func: string, key: string | ((alias: string) => string)): R;
115
+ /**
116
+ * Tag function providing quoting of db identifiers (table name, columns names, index names, ...).
117
+ *
118
+ * Within the template literal on which the tag function is applied, all placeholders are considered to be database identifiers, and will thus be quoted as so according to the database in use.
119
+ *
120
+ * ```ts
121
+ * // On postgres, will produce: create index "index custom_idx_on_name" on "library.author" ("name")
122
+ * // On mysql, will produce: create index `index custom_idx_on_name` on `library.author` (`name`)
123
+ * @Index({ name: 'custom_idx_on_name', expression: (table, columns) => quote`create index ${'custom_idx_on_name'} on ${table} (${columns.name})` })
124
+ * @Entity({ schema: 'library' })
125
+ * export class Author { ... }
126
+ * ```
127
+ */
128
+ export declare function quote(expParts: readonly string[], ...values: (string | {
129
+ toString(): string;
130
+ })[]): any;