@mikro-orm/core 7.0.0-rc.0 → 7.0.0-rc.2

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.
@@ -456,7 +456,7 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
456
456
  /**
457
457
  * Loads specified relations in batch. This will execute one query for each relation, that will populate it on all the specified entities.
458
458
  */
459
- populate<Entity extends object, Naked extends FromEntityType<UnboxArray<Entity>> = FromEntityType<UnboxArray<Entity>>, Hint extends string = never, Fields extends string = '*', Excludes extends string = never>(entities: Entity, populate: readonly AutoPath<Naked, Hint, PopulatePath.ALL>[] | false, options?: EntityLoaderOptions<Naked, Fields, Excludes>): Promise<Entity extends object[] ? MergeLoaded<ArrayElement<Entity>, Naked, Hint, Fields, Excludes>[] : MergeLoaded<Entity, Naked, Hint, Fields, Excludes>>;
459
+ populate<Entity extends object, Naked extends FromEntityType<UnboxArray<Entity>> = FromEntityType<UnboxArray<Entity>>, Hint extends string = never, Fields extends string = never, Excludes extends string = never>(entities: Entity, populate: readonly AutoPath<Naked, Hint, PopulatePath.ALL>[] | false, options?: EntityLoaderOptions<Naked, Fields, Excludes>): Promise<Entity extends object[] ? MergeLoaded<ArrayElement<Entity>, Naked, Hint, Fields, Excludes>[] : MergeLoaded<Entity, Naked, Hint, Fields, Excludes>>;
460
460
  /**
461
461
  * Returns new EntityManager instance with its own identity map
462
462
  */
@@ -515,7 +515,7 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
515
515
  private lockAndPopulate;
516
516
  private buildFields;
517
517
  /** @internal */
518
- preparePopulate<Entity extends object>(entityName: EntityName<Entity>, options: Pick<FindOptions<Entity, any, any, any>, 'populate' | 'strategy' | 'fields' | 'flags' | 'filters' | 'exclude'>, validate?: boolean): Promise<PopulateOptions<Entity>[]>;
518
+ preparePopulate<Entity extends object>(entityName: EntityName<Entity>, options: Pick<FindOptions<Entity, any, any, any>, 'populate' | 'strategy' | 'fields' | 'flags' | 'filters' | 'exclude' | 'populateHints'>, validate?: boolean): Promise<PopulateOptions<Entity>[]>;
519
519
  /**
520
520
  * when the entity is found in identity map, we check if it was partially loaded or we are trying to populate
521
521
  * some additional lazy properties, if so, we reload and merge the data from database
package/EntityManager.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import { getOnConflictReturningFields, getWhereCondition } from './utils/upsert-utils.js';
2
2
  import { Utils } from './utils/Utils.js';
3
3
  import { Cursor } from './utils/Cursor.js';
4
- import { DataloaderUtils } from './utils/DataloaderUtils.js';
5
4
  import { QueryHelper } from './utils/QueryHelper.js';
6
5
  import { TransactionContext } from './utils/TransactionContext.js';
7
6
  import { isRaw, Raw } from './utils/RawQueryFragment.js';
@@ -17,7 +16,7 @@ import { EventType, FlushMode, LoadStrategy, LockMode, PopulateHint, PopulatePat
17
16
  import { EventManager } from './events/EventManager.js';
18
17
  import { TransactionEventBroadcaster } from './events/TransactionEventBroadcaster.js';
19
18
  import { OptimisticLockError, ValidationError } from './errors.js';
20
- import { getLoadingStrategy } from './entity/utils.js';
19
+ import { applyPopulateHints, getLoadingStrategy } from './entity/utils.js';
21
20
  import { TransactionManager } from './utils/TransactionManager.js';
22
21
  /**
23
22
  * The EntityManager is the central access point to ORM functionality. It is a facade to all different ORM subsystems
@@ -1683,12 +1682,15 @@ export class EntityManager {
1683
1682
  throw ValidationError.invalidPropertyName(entityName, invalid.field);
1684
1683
  }
1685
1684
  await this.autoJoinRefsForFilters(meta, { ...options, populate });
1686
- return populate.map(field => {
1685
+ for (const field of populate) {
1687
1686
  // force select-in strategy when populating all relations as otherwise we could cause infinite loops when self-referencing
1688
1687
  const all = field.all ?? (Array.isArray(options.populate) && options.populate.includes('*'));
1689
1688
  field.strategy = all ? LoadStrategy.SELECT_IN : (options.strategy ?? field.strategy);
1690
- return field;
1691
- });
1689
+ }
1690
+ if (options.populateHints) {
1691
+ applyPopulateHints(populate, options.populateHints);
1692
+ }
1693
+ return populate;
1692
1694
  }
1693
1695
  /**
1694
1696
  * when the entity is found in identity map, we check if it was partially loaded or we are trying to populate
@@ -1813,6 +1815,7 @@ export class EntityManager {
1813
1815
  if (em.loaders[type]) {
1814
1816
  return em.loaders[type];
1815
1817
  }
1818
+ const { DataloaderUtils } = await import('@mikro-orm/core/dataloader');
1816
1819
  const DataLoader = await DataloaderUtils.getDataLoader();
1817
1820
  switch (type) {
1818
1821
  case 'ref': return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getRefBatchLoadFn(em)));
@@ -1,4 +1,4 @@
1
- import type { ConnectionType, EntityData, EntityMetadata, EntityProperty, FilterQuery, Primary, Dictionary, IPrimaryKey, PopulateOptions, EntityDictionary, AutoPath, ObjectQuery, FilterObject, Populate, EntityName } from '../typings.js';
1
+ import type { ConnectionType, Constructor, EntityData, EntityMetadata, EntityProperty, FilterQuery, Primary, Dictionary, IPrimaryKey, PopulateOptions, EntityDictionary, AutoPath, ObjectQuery, FilterObject, Populate, EntityName, PopulateHintOptions, Prefixes } from '../typings.js';
2
2
  import type { Connection, QueryResult, Transaction } from '../connections/Connection.js';
3
3
  import type { FlushMode, LockMode, QueryOrderMap, QueryFlag, LoadStrategy, PopulateHint, PopulatePath } from '../enums.js';
4
4
  import type { Platform } from '../platforms/Platform.js';
@@ -7,6 +7,7 @@ import type { Collection } from '../entity/Collection.js';
7
7
  import type { EntityManager } from '../EntityManager.js';
8
8
  import type { DriverException } from '../exceptions.js';
9
9
  import type { Configuration } from '../utils/Configuration.js';
10
+ import type { MikroORM } from '../MikroORM.js';
10
11
  import type { LoggingOptions, LogContext } from '../logging/Logger.js';
11
12
  import type { Raw } from '../utils/RawQueryFragment.js';
12
13
  export declare const EntityManagerType: unique symbol;
@@ -65,6 +66,10 @@ export interface IDatabaseDriver<C extends Connection = Connection> {
65
66
  schema?: string;
66
67
  parentSchema?: string;
67
68
  }): string | undefined;
69
+ /**
70
+ * @internal
71
+ */
72
+ getORMClass(): Constructor<MikroORM>;
68
73
  }
69
74
  export type EntityField<T, P extends string = PopulatePath.ALL> = keyof T | PopulatePath.ALL | AutoPath<T, P, `${PopulatePath.ALL}`>;
70
75
  export type OrderDefinition<T> = (QueryOrderMap<T> & {
@@ -107,6 +112,10 @@ export interface FindOptions<Entity, Hint extends string = never, Fields extends
107
112
  populateFilter?: ObjectQuery<Entity>;
108
113
  /** Used for ordering of the populate queries. If not specified, the value of `options.orderBy` is used. */
109
114
  populateOrderBy?: OrderDefinition<Entity>;
115
+ /** Per-relation overrides for populate loading behavior. Keys are populate paths (same as used in `populate`). */
116
+ populateHints?: [Hint] extends [never] ? never : {
117
+ [K in Prefixes<Hint>]?: PopulateHintOptions;
118
+ };
110
119
  /** Ordering of the results.Can be an object or array of objects, keys are property names, values are ordering (asc/desc) */
111
120
  orderBy?: OrderDefinition<Entity>;
112
121
  /** Control result caching for this query. Result cache is by default disabled, not to be confused with the identity map. */
@@ -155,12 +164,18 @@ export interface FindOptions<Entity, Hint extends string = never, Fields extends
155
164
  lockTableAliases?: string[];
156
165
  ctx?: Transaction;
157
166
  connectionType?: ConnectionType;
158
- /** sql only */
159
- indexHint?: string;
167
+ /** SQL: appended to FROM clause (e.g. `'force index(my_index)'`); MongoDB: index name or spec passed as `hint`. */
168
+ indexHint?: string | Dictionary;
160
169
  /** sql only */
161
170
  comments?: string | string[];
162
171
  /** sql only */
163
172
  hintComments?: string | string[];
173
+ /** SQL: collation name string applied as COLLATE to ORDER BY; MongoDB: CollationOptions object. */
174
+ collation?: CollationOptions | string;
175
+ /** mongodb only */
176
+ maxTimeMS?: number;
177
+ /** mongodb only */
178
+ allowDiskUse?: boolean;
164
179
  loggerContext?: LogContext;
165
180
  logging?: LoggingOptions;
166
181
  /** @internal used to apply filters to the auto-joined relations */
@@ -211,12 +226,16 @@ export interface CountOptions<T extends object, P extends string = never> {
211
226
  ctx?: Transaction;
212
227
  connectionType?: ConnectionType;
213
228
  flushMode?: FlushMode | `${FlushMode}`;
214
- /** sql only */
215
- indexHint?: string;
229
+ /** SQL: appended to FROM clause (e.g. `'force index(my_index)'`); MongoDB: index name or spec passed as `hint`. */
230
+ indexHint?: string | Dictionary;
216
231
  /** sql only */
217
232
  comments?: string | string[];
218
233
  /** sql only */
219
234
  hintComments?: string | string[];
235
+ /** SQL: collation name string applied as COLLATE; MongoDB: CollationOptions object. */
236
+ collation?: CollationOptions | string;
237
+ /** mongodb only */
238
+ maxTimeMS?: number;
220
239
  loggerContext?: LogContext;
221
240
  logging?: LoggingOptions;
222
241
  /** @internal used to apply filters to the auto-joined relations */
@@ -244,6 +263,16 @@ export interface DriverMethodOptions {
244
263
  schema?: string;
245
264
  loggerContext?: LogContext;
246
265
  }
266
+ export interface CollationOptions {
267
+ locale: string;
268
+ caseLevel?: boolean;
269
+ caseFirst?: string;
270
+ strength?: number;
271
+ numericOrdering?: boolean;
272
+ alternate?: string;
273
+ maxVariable?: string;
274
+ backwards?: boolean;
275
+ }
247
276
  export interface GetReferenceOptions {
248
277
  wrapped?: boolean;
249
278
  convertCustomTypes?: boolean;
@@ -1,5 +1,5 @@
1
1
  import { type Ref } from './Reference.js';
2
- import type { AutoPath, EntityData, EntityDTO, Loaded, LoadedReference, AddEager, EntityKey, FromEntityType, IsSubset, MergeSelected } from '../typings.js';
2
+ import type { AutoPath, EntityData, EntityDTO, Loaded, LoadedReference, AddEager, EntityKey, FromEntityType, IsSubset, MergeSelected, SerializeDTO } from '../typings.js';
3
3
  import { type AssignOptions } from './EntityAssigner.js';
4
4
  import type { EntityLoaderOptions } from './EntityLoader.js';
5
5
  import { type SerializeOptions } from '../serialization/EntitySerializer.js';
@@ -8,7 +8,7 @@ import type { PopulatePath } from '../enums.js';
8
8
  export declare abstract class BaseEntity {
9
9
  isInitialized(): boolean;
10
10
  populated(populated?: boolean): void;
11
- populate<Entity extends this = this, Hint extends string = never>(populate: AutoPath<Entity, Hint, PopulatePath.ALL>[] | false, options?: EntityLoaderOptions<Entity>): Promise<Loaded<Entity, Hint>>;
11
+ populate<Entity extends this = this, Hint extends string = never, Fields extends string = never>(populate: AutoPath<Entity, Hint, PopulatePath.ALL>[] | false, options?: EntityLoaderOptions<Entity, Fields>): Promise<Loaded<Entity, Hint>>;
12
12
  toReference<Entity extends this = this>(): Ref<Entity> & LoadedReference<Loaded<Entity, AddEager<Entity>>>;
13
13
  /**
14
14
  * Converts the entity to a plain object representation.
@@ -73,7 +73,7 @@ export declare abstract class BaseEntity {
73
73
  */
74
74
  toObject<Entity extends this = this, Ignored extends EntityKey<Entity> = never>(ignoreFields: Ignored[]): Omit<EntityDTO<Entity>, Ignored>;
75
75
  toPOJO<Entity extends this = this>(): EntityDTO<Entity>;
76
- serialize<Entity extends this = this, Naked extends FromEntityType<Entity> = FromEntityType<Entity>, Hint extends string = never, Exclude extends string = never>(options?: SerializeOptions<Naked, Hint, Exclude>): EntityDTO<Loaded<Naked, Hint>>;
76
+ serialize<Entity extends this = this, Naked extends FromEntityType<Entity> = FromEntityType<Entity>, Hint extends string = never, Exclude extends string = never>(options?: SerializeOptions<Naked, Hint, Exclude>): SerializeDTO<Naked, Hint, Exclude>;
77
77
  assign<Entity extends this, Naked extends FromEntityType<Entity> = FromEntityType<Entity>, Convert extends boolean = false, Data extends EntityData<Naked, Convert> | Partial<EntityDTO<Naked>> = EntityData<Naked, Convert> | Partial<EntityDTO<Naked>>>(data: Data & IsSubset<EntityData<Naked>, Data>, options?: AssignOptions<Convert>): MergeSelected<Entity, Naked, keyof Data & string>;
78
78
  init<Entity extends this = this, Hint extends string = never, Fields extends string = '*', Excludes extends string = never>(options?: FindOneOptions<Entity, Hint, Fields, Excludes>): Promise<Loaded<Entity, Hint, Fields, Excludes> | null>;
79
79
  getSchema(): string | undefined;
@@ -129,7 +129,10 @@ export class EntityFactory {
129
129
  .filter(key => meta.properties[key]?.formula || [ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(meta.properties[key]?.kind))
130
130
  .forEach(key => diff2[key] = data[key]);
131
131
  // rehydrated with the new values, skip those changed by user
132
- this.hydrate(entity, meta, diff2, options);
132
+ // use full hydration if the entity is already initialized, even if the caller used `initialized: false`
133
+ // (e.g. from createReference), otherwise scalar properties in diff2 won't be applied
134
+ const initialized = options.initialized || helper(entity).__initialized;
135
+ this.hydrate(entity, meta, diff2, initialized ? { ...options, initialized } : options);
133
136
  // we need to update the entity data only with keys that were not present before
134
137
  const nullVal = this.config.get('forceUndefined') ? undefined : null;
135
138
  Utils.keys(diff2).forEach(key => {
@@ -137,7 +140,7 @@ export class EntityFactory {
137
140
  if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && Utils.isPlainObject(data[prop.name])) {
138
141
  diff2[key] = entity[prop.name] ? helper(entity[prop.name]).getPrimaryKey(options.convertCustomTypes) : null;
139
142
  }
140
- if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE, ReferenceKind.SCALAR].includes(prop.kind) && prop.customType?.ensureComparable(meta, prop) && diff2[key] != null) {
143
+ if (!options.convertCustomTypes && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE, ReferenceKind.SCALAR].includes(prop.kind) && prop.customType?.ensureComparable(meta, prop) && diff2[key] != null) {
141
144
  const converted = prop.customType.convertToJSValue(diff2[key], this.platform, { force: true });
142
145
  diff2[key] = prop.customType.convertToDatabaseValue(converted, this.platform, { fromQuery: true });
143
146
  }
@@ -1,4 +1,4 @@
1
- import { EagerProps, EntityRepositoryType, HiddenProps, OptionalProps, PrimaryKeyProp, } from '../typings.js';
1
+ import { EagerProps, EntityName, EntityRepositoryType, HiddenProps, OptionalProps, PrimaryKeyProp, } from '../typings.js';
2
2
  import { EntityTransformer } from '../serialization/EntityTransformer.js';
3
3
  import { Reference } from './Reference.js';
4
4
  import { Utils } from '../utils/Utils.js';
@@ -130,7 +130,7 @@ export class EntityHelper {
130
130
  }
131
131
  }
132
132
  // ensure we dont have internal symbols in the POJO
133
- [OptionalProps, EntityRepositoryType, PrimaryKeyProp, EagerProps, HiddenProps].forEach(sym => delete object[sym]);
133
+ [OptionalProps, EntityRepositoryType, PrimaryKeyProp, EagerProps, HiddenProps, EntityName].forEach(sym => delete object[sym]);
134
134
  meta.props
135
135
  .filter(prop => object[prop.name] === undefined)
136
136
  .forEach(prop => delete object[prop.name]);
@@ -1,13 +1,13 @@
1
- import type { AnyEntity, ConnectionType, EntityName, EntityProperty, FilterQuery, PopulateOptions } from '../typings.js';
1
+ import type { AnyEntity, AutoPath, ConnectionType, EntityName, EntityProperty, FilterQuery, PopulateOptions } from '../typings.js';
2
2
  import type { EntityManager } from '../EntityManager.js';
3
3
  import { LoadStrategy, type LockMode, type PopulateHint, PopulatePath, type QueryOrderMap } from '../enums.js';
4
- import type { EntityField, FilterOptions } from '../drivers/IDatabaseDriver.js';
4
+ import type { FilterOptions } from '../drivers/IDatabaseDriver.js';
5
5
  import type { LoggingOptions } from '../logging/Logger.js';
6
- export type EntityLoaderOptions<Entity, Fields extends string = PopulatePath.ALL, Excludes extends string = never> = {
6
+ export interface EntityLoaderOptions<Entity, Fields extends string = PopulatePath.ALL, Excludes extends string = never> {
7
+ fields?: readonly AutoPath<Entity, Fields, `${PopulatePath.ALL}`>[];
8
+ exclude?: readonly AutoPath<Entity, Excludes>[];
7
9
  where?: FilterQuery<Entity>;
8
10
  populateWhere?: PopulateHint | `${PopulateHint}`;
9
- fields?: readonly EntityField<Entity, Fields>[];
10
- exclude?: readonly EntityField<Entity, Excludes>[];
11
11
  orderBy?: QueryOrderMap<Entity> | QueryOrderMap<Entity>[];
12
12
  refresh?: boolean;
13
13
  validate?: boolean;
@@ -20,7 +20,7 @@ export type EntityLoaderOptions<Entity, Fields extends string = PopulatePath.ALL
20
20
  schema?: string;
21
21
  connectionType?: ConnectionType;
22
22
  logging?: LoggingOptions;
23
- };
23
+ }
24
24
  export declare class EntityLoader {
25
25
  private readonly em;
26
26
  private readonly metadata;
@@ -150,7 +150,7 @@ export declare class EntityRepository<Entity extends object> {
150
150
  /**
151
151
  * Loads specified relations in batch. This will execute one query for each relation, that will populate it on all the specified entities.
152
152
  */
153
- populate<Ent extends Entity | Entity[], Hint extends string = never, Naked extends FromEntityType<Entity> = FromEntityType<Entity>, Fields extends string = '*', Excludes extends string = never>(entities: Ent, populate: AutoPath<Naked, Hint, PopulatePath.ALL>[] | false, options?: EntityLoaderOptions<Naked, Fields, Excludes>): Promise<Ent extends object[] ? MergeLoaded<ArrayElement<Ent>, Naked, Hint, Fields, Excludes>[] : MergeLoaded<Ent, Naked, Hint, Fields, Excludes>>;
153
+ populate<Ent extends Entity | Entity[], Hint extends string = never, Naked extends FromEntityType<Entity> = FromEntityType<Entity>, Fields extends string = never, Excludes extends string = never>(entities: Ent, populate: AutoPath<Naked, Hint, PopulatePath.ALL>[] | false, options?: EntityLoaderOptions<Naked, Fields, Excludes>): Promise<Ent extends object[] ? MergeLoaded<ArrayElement<Ent>, Naked, Hint, Fields, Excludes>[] : MergeLoaded<Ent, Naked, Hint, Fields, Excludes>>;
154
154
  /**
155
155
  * Creates new instance of given entity and populates it with given data.
156
156
  * The entity constructor will be used unless you provide `{ managed: true }` in the `options` parameter.
@@ -1,6 +1,6 @@
1
1
  import type { PopulatePath } from '../enums.js';
2
2
  import type { EntityManager } from '../EntityManager.js';
3
- import type { Dictionary, EntityData, EntityDictionary, EntityMetadata, IHydrator, EntityKey, PopulateOptions, Primary, AutoPath, Ref, AddEager, LoadedReference, EntityDTO, Loaded, FromEntityType, IsSubset, MergeSelected } from '../typings.js';
3
+ import type { Dictionary, EntityData, EntityDictionary, EntityMetadata, IHydrator, EntityKey, PopulateOptions, Primary, AutoPath, Ref, AddEager, LoadedReference, EntityDTO, Loaded, SerializeDTO, FromEntityType, IsSubset, MergeSelected } from '../typings.js';
4
4
  import { Reference } from './Reference.js';
5
5
  import { type AssignOptions } from './EntityAssigner.js';
6
6
  import type { EntityLoaderOptions } from './EntityLoader.js';
@@ -44,12 +44,12 @@ export declare class WrappedEntity<Entity extends object> {
44
44
  setSerializationContext<Hint extends string = never, Fields extends string = '*', Exclude extends string = never>(options: LoadHint<Entity, Hint, Fields, Exclude>): void;
45
45
  toReference(): Ref<Entity> & LoadedReference<Loaded<Entity, AddEager<Entity>>>;
46
46
  toObject<Ignored extends EntityKey<Entity> = never>(ignoreFields?: Ignored[]): Omit<EntityDTO<Entity>, Ignored>;
47
- serialize<Hint extends string = never, Exclude extends string = never>(options?: SerializeOptions<Entity, Hint, Exclude>): EntityDTO<Loaded<Entity, Hint>>;
47
+ serialize<Hint extends string = never, Exclude extends string = never>(options?: SerializeOptions<Entity, Hint, Exclude>): SerializeDTO<Entity, Hint, Exclude>;
48
48
  toPOJO(): EntityDTO<Entity>;
49
49
  toJSON(...args: any[]): EntityDictionary<Entity>;
50
50
  assign<Naked extends FromEntityType<Entity> = FromEntityType<Entity>, Convert extends boolean = false, Data extends EntityData<Naked, Convert> | Partial<EntityDTO<Naked>> = EntityData<Naked, Convert> | Partial<EntityDTO<Naked>>>(data: Data & IsSubset<EntityData<Naked>, Data>, options?: AssignOptions<Convert>): MergeSelected<Entity, Naked, keyof Data & string>;
51
51
  init<Hint extends string = never, Fields extends string = '*', Excludes extends string = never>(options?: FindOneOptions<Entity, Hint, Fields, Excludes>): Promise<Loaded<Entity, Hint, Fields, Excludes> | null>;
52
- populate<Hint extends string = never>(populate: AutoPath<Entity, Hint, PopulatePath.ALL>[] | false, options?: EntityLoaderOptions<Entity>): Promise<Loaded<Entity, Hint>>;
52
+ populate<Hint extends string = never, Fields extends string = never>(populate: AutoPath<Entity, Hint, PopulatePath.ALL>[] | false, options?: EntityLoaderOptions<Entity, Fields>): Promise<Loaded<Entity, Hint>>;
53
53
  hasPrimaryKey(): boolean;
54
54
  getPrimaryKey(convertCustomTypes?: boolean): Primary<Entity> | null;
55
55
  getPrimaryKeys(convertCustomTypes?: boolean): Primary<Entity>[] | null;