@mikro-orm/core 6.5.10-dev.10 → 6.5.10-dev.12

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.
@@ -4,7 +4,7 @@ import { type Configuration, Cursor } from './utils';
4
4
  import { type AssignOptions, EntityFactory, EntityLoader, type EntityLoaderOptions, type EntityRepository, EntityValidator, Reference } from './entity';
5
5
  import { UnitOfWork } from './unit-of-work';
6
6
  import type { CountOptions, DeleteOptions, FindAllOptions, FindByCursorOptions, FindOneOptions, FindOneOrFailOptions, FindOptions, GetReferenceOptions, IDatabaseDriver, LockOptions, NativeInsertUpdateOptions, UpdateOptions, UpsertManyOptions, UpsertOptions } from './drivers';
7
- import type { AnyEntity, AnyString, ArrayElement, AutoPath, ConnectionType, Dictionary, EntityData, EntityDictionary, EntityDTO, EntityMetadata, EntityName, FilterQuery, FromEntityType, GetRepository, IHydrator, IsSubset, Loaded, MaybePromise, MergeLoaded, MergeSelected, NoInfer, ObjectQuery, Primary, Ref, RequiredEntityData, UnboxArray } from './typings';
7
+ import type { AnyEntity, AnyString, ArrayElement, AutoPath, ConnectionType, Dictionary, EntityData, EntityDictionary, EntityDTO, EntityMetadata, EntityName, FilterDef, FilterQuery, FromEntityType, GetRepository, IHydrator, IsSubset, Loaded, MaybePromise, MergeLoaded, MergeSelected, NoInfer, ObjectQuery, Primary, Ref, RequiredEntityData, UnboxArray } from './typings';
8
8
  import { FlushMode, LockMode, PopulatePath, type TransactionOptions } from './enums';
9
9
  import type { MetadataStorage } from './metadata';
10
10
  import type { Transaction } from './connections';
@@ -83,19 +83,19 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
83
83
  /**
84
84
  * Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
85
85
  */
86
- addFilter<T1>(name: string, cond: FilterQuery<T1> | ((args: Dictionary) => MaybePromise<FilterQuery<T1>>), entityName?: EntityName<T1> | [EntityName<T1>], enabled?: boolean): void;
86
+ addFilter<T1>(name: string, cond: FilterQuery<T1> | ((args: Dictionary) => MaybePromise<FilterQuery<T1>>), entityName?: EntityName<T1> | [EntityName<T1>], options?: boolean | Partial<FilterDef>): void;
87
87
  /**
88
88
  * Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
89
89
  */
90
- addFilter<T1, T2>(name: string, cond: FilterQuery<T1 | T2> | ((args: Dictionary) => MaybePromise<FilterQuery<T1 | T2>>), entityName?: [EntityName<T1>, EntityName<T2>], enabled?: boolean): void;
90
+ addFilter<T1, T2>(name: string, cond: FilterQuery<T1 | T2> | ((args: Dictionary) => MaybePromise<FilterQuery<T1 | T2>>), entityName?: [EntityName<T1>, EntityName<T2>], options?: boolean | Partial<FilterDef>): void;
91
91
  /**
92
92
  * Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
93
93
  */
94
- addFilter<T1, T2, T3>(name: string, cond: FilterQuery<T1 | T2 | T3> | ((args: Dictionary) => MaybePromise<FilterQuery<T1 | T2 | T3>>), entityName?: [EntityName<T1>, EntityName<T2>, EntityName<T3>], enabled?: boolean): void;
94
+ addFilter<T1, T2, T3>(name: string, cond: FilterQuery<T1 | T2 | T3> | ((args: Dictionary) => MaybePromise<FilterQuery<T1 | T2 | T3>>), entityName?: [EntityName<T1>, EntityName<T2>, EntityName<T3>], options?: boolean | Partial<FilterDef>): void;
95
95
  /**
96
96
  * Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
97
97
  */
98
- addFilter(name: string, cond: Dictionary | ((args: Dictionary) => MaybePromise<FilterQuery<AnyEntity>>), entityName?: EntityName<AnyEntity> | EntityName<AnyEntity>[], enabled?: boolean): void;
98
+ addFilter(name: string, cond: Dictionary | ((args: Dictionary) => MaybePromise<FilterQuery<AnyEntity>>), entityName?: EntityName<AnyEntity> | EntityName<AnyEntity>[], options?: boolean | Partial<FilterDef>): void;
99
99
  /**
100
100
  * Sets filter parameter values globally inside context defined by this entity manager.
101
101
  * If you want to set shared value for all contexts, be sure to use the root entity manager.
@@ -123,7 +123,10 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
123
123
  /**
124
124
  * When filters are active on M:1 or 1:1 relations, we need to ref join them eagerly as they might affect the FK value.
125
125
  */
126
- protected autoJoinRefsForFilters<T extends object>(meta: EntityMetadata<T>, options: FindOptions<T, any, any, any> | FindOneOptions<T, any, any, any>): Promise<void>;
126
+ protected autoJoinRefsForFilters<T extends object>(meta: EntityMetadata<T>, options: FindOptions<T, any, any, any> | FindOneOptions<T, any, any, any>, parent?: {
127
+ className: string;
128
+ propName: string;
129
+ }): Promise<void>;
127
130
  /**
128
131
  * @internal
129
132
  */
package/EntityManager.js CHANGED
@@ -197,8 +197,8 @@ class EntityManager {
197
197
  /**
198
198
  * Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
199
199
  */
200
- addFilter(name, cond, entityName, enabled = true) {
201
- const options = { name, cond, default: enabled };
200
+ addFilter(name, cond, entityName, options = true) {
201
+ options = typeof options === 'object' ? { name, cond, default: true, ...options } : { name, cond, default: options };
202
202
  if (entityName) {
203
203
  options.entity = utils_1.Utils.asArray(entityName).map(n => utils_1.Utils.className(n));
204
204
  }
@@ -308,29 +308,29 @@ class EntityManager {
308
308
  /**
309
309
  * When filters are active on M:1 or 1:1 relations, we need to ref join them eagerly as they might affect the FK value.
310
310
  */
311
- async autoJoinRefsForFilters(meta, options) {
312
- if (!meta || !this.config.get('autoJoinRefsForFilters')) {
311
+ async autoJoinRefsForFilters(meta, options, parent) {
312
+ if (!meta || !this.config.get('autoJoinRefsForFilters') || options.filters === false) {
313
313
  return;
314
314
  }
315
- const props = meta.relations.filter(prop => {
316
- return !prop.object && [enums_1.ReferenceKind.MANY_TO_ONE, enums_1.ReferenceKind.ONE_TO_ONE].includes(prop.kind)
317
- && ((options.fields?.length ?? 0) === 0 || options.fields?.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)));
318
- });
319
315
  const ret = options.populate;
320
- for (const prop of props) {
316
+ for (const prop of meta.relations) {
317
+ if (prop.object
318
+ || ![enums_1.ReferenceKind.MANY_TO_ONE, enums_1.ReferenceKind.ONE_TO_ONE].includes(prop.kind)
319
+ || !((options.fields?.length ?? 0) === 0 || options.fields?.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
320
+ || (parent?.className === prop.targetMeta.root.className && parent.propName === prop.inversedBy)) {
321
+ continue;
322
+ }
321
323
  const cond = await this.applyFilters(prop.type, {}, options.filters ?? {}, 'read', options);
322
324
  if (!utils_1.Utils.isEmpty(cond)) {
323
325
  const populated = options.populate.filter(({ field }) => field.split(':')[0] === prop.name);
324
326
  let found = false;
325
- if (populated.length > 0) {
326
- for (const hint of populated) {
327
- if (!hint.all) {
328
- hint.filter = true;
329
- found = true;
330
- }
331
- else if (hint.field === `${prop.name}:ref`) {
332
- found = true;
333
- }
327
+ for (const hint of populated) {
328
+ if (!hint.all) {
329
+ hint.filter = true;
330
+ }
331
+ const strategy = (0, utils_2.getLoadingStrategy)(prop.strategy || hint.strategy || options.strategy || this.config.get('loadStrategy'), prop.kind);
332
+ if (hint.field === `${prop.name}:ref` || (hint.filter && strategy === enums_1.LoadStrategy.JOINED)) {
333
+ found = true;
334
334
  }
335
335
  }
336
336
  if (!found) {
@@ -338,6 +338,14 @@ class EntityManager {
338
338
  }
339
339
  }
340
340
  }
341
+ for (const hint of ret) {
342
+ const [field, ref] = hint.field.split(':');
343
+ const prop = meta?.properties[field];
344
+ if (prop && !ref) {
345
+ hint.children ??= [];
346
+ await this.autoJoinRefsForFilters(prop.targetMeta, { ...options, populate: hint.children }, { className: meta.root.className, propName: prop.name });
347
+ }
348
+ }
341
349
  }
342
350
  /**
343
351
  * @internal
@@ -376,13 +384,17 @@ class EntityManager {
376
384
  else {
377
385
  cond = filter.cond;
378
386
  }
379
- ret.push(utils_1.QueryHelper.processWhere({
387
+ cond = utils_1.QueryHelper.processWhere({
380
388
  where: cond,
381
389
  entityName,
382
390
  metadata: this.metadata,
383
391
  platform: this.driver.getPlatform(),
384
392
  aliased: type === 'read',
385
- }));
393
+ });
394
+ if (filter.strict) {
395
+ Object.defineProperty(cond, '__strict', { value: filter.strict, enumerable: false });
396
+ }
397
+ ret.push(cond);
386
398
  }
387
399
  const conds = [...ret, where].filter(c => utils_1.Utils.hasObjectKeys(c));
388
400
  return conds.length > 1 ? { $and: conds } : conds[0];
@@ -1387,7 +1399,7 @@ class EntityManager {
1387
1399
  const em = this.getContext();
1388
1400
  em.prepareOptions(options);
1389
1401
  const entityName = arr[0].constructor.name;
1390
- const preparedPopulate = await em.preparePopulate(entityName, { populate: populate }, options.validate);
1402
+ const preparedPopulate = await em.preparePopulate(entityName, { populate: populate, filters: options.filters }, options.validate);
1391
1403
  await em.entityLoader.populate(entityName, arr, preparedPopulate, options);
1392
1404
  return entities;
1393
1405
  }
@@ -213,7 +213,7 @@ class EntityLoader {
213
213
  }
214
214
  }
215
215
  async findChildren(entities, prop, populate, options, ref) {
216
- const children = this.getChildReferences(entities, prop, options, ref);
216
+ const children = Utils_1.Utils.unique(this.getChildReferences(entities, prop, options, ref));
217
217
  const meta = prop.targetMeta;
218
218
  let fk = Utils_1.Utils.getPrimaryKeyHash(meta.primaryKeys);
219
219
  let schema = options.schema;
@@ -269,6 +269,24 @@ class EntityLoader {
269
269
  // @ts-ignore not a public option, will be propagated to the populate call
270
270
  visited: options.visited,
271
271
  });
272
+ if ([enums_1.ReferenceKind.ONE_TO_ONE, enums_1.ReferenceKind.MANY_TO_ONE].includes(prop.kind) && items.length !== children.length) {
273
+ const nullVal = this.em.config.get('forceUndefined') ? undefined : null;
274
+ const itemsMap = {};
275
+ const childrenMap = {};
276
+ for (const item of items) {
277
+ itemsMap[(0, wrap_1.helper)(item).getSerializedPrimaryKey()] ??= true;
278
+ }
279
+ for (const child of children) {
280
+ childrenMap[(0, wrap_1.helper)(child).getSerializedPrimaryKey()] ??= true;
281
+ }
282
+ for (const entity of entities) {
283
+ const key = (0, wrap_1.helper)(entity[prop.name] ?? {})?.getSerializedPrimaryKey();
284
+ if (childrenMap[key] != null && itemsMap[key] == null) {
285
+ entity[prop.name] = nullVal;
286
+ (0, wrap_1.helper)(entity).__originalEntityData[prop.name] = null;
287
+ }
288
+ }
289
+ }
272
290
  for (const item of items) {
273
291
  if (ref && !(0, wrap_1.helper)(item).__onLoadFired) {
274
292
  (0, wrap_1.helper)(item).__initialized = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/core",
3
- "version": "6.5.10-dev.10",
3
+ "version": "6.5.10-dev.12",
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",
@@ -64,7 +64,7 @@
64
64
  "esprima": "4.0.1",
65
65
  "fs-extra": "11.3.2",
66
66
  "globby": "11.1.0",
67
- "mikro-orm": "6.5.10-dev.10",
67
+ "mikro-orm": "6.5.10-dev.12",
68
68
  "reflect-metadata": "0.2.2"
69
69
  }
70
70
  }
package/typings.d.ts CHANGED
@@ -710,6 +710,7 @@ export type FilterDef = {
710
710
  default?: boolean;
711
711
  entity?: string[];
712
712
  args?: boolean;
713
+ strict?: boolean;
713
714
  };
714
715
  export type Populate<T, P extends string = never> = readonly AutoPath<T, P, `${PopulatePath}`>[] | false;
715
716
  export type PopulateOptions<T> = {