@mikro-orm/core 6.5.10-dev.23 → 6.5.10-dev.25

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.
@@ -3,7 +3,7 @@ import DataLoader from 'dataloader';
3
3
  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
- import type { CountOptions, DeleteOptions, FindAllOptions, FindByCursorOptions, FindOneOptions, FindOneOrFailOptions, FindOptions, GetReferenceOptions, IDatabaseDriver, LockOptions, NativeInsertUpdateOptions, UpdateOptions, UpsertManyOptions, UpsertOptions } from './drivers';
6
+ import type { CountOptions, DeleteOptions, FilterOptions, FindAllOptions, FindByCursorOptions, FindOneOptions, FindOneOrFailOptions, FindOptions, GetReferenceOptions, IDatabaseDriver, LockOptions, NativeInsertUpdateOptions, UpdateOptions, UpsertManyOptions, UpsertOptions } from './drivers';
7
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';
@@ -130,7 +130,7 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
130
130
  /**
131
131
  * @internal
132
132
  */
133
- applyFilters<Entity extends object>(entityName: string, where: FilterQuery<Entity> | undefined, options: Dictionary<boolean | Dictionary> | string[] | boolean, type: 'read' | 'update' | 'delete', findOptions?: FindOptions<any, any, any, any> | FindOneOptions<any, any, any, any>): Promise<FilterQuery<Entity> | undefined>;
133
+ applyFilters<Entity extends object>(entityName: string, where: FilterQuery<Entity> | undefined, options: FilterOptions | undefined, type: 'read' | 'update' | 'delete', findOptions?: FindOptions<any, any, any, any> | FindOneOptions<any, any, any, any>): Promise<FilterQuery<Entity> | undefined>;
134
134
  /**
135
135
  * Calls `em.find()` and `em.count()` with the same arguments (where applicable) and returns the results as tuple
136
136
  * where the first element is the array of entities, and the second is the count.
package/EntityManager.js CHANGED
@@ -290,12 +290,14 @@ class EntityManager {
290
290
  if (!joined && !hint.filter) {
291
291
  continue;
292
292
  }
293
- const where = await this.applyFilters(prop.type, {}, options.filters ?? {}, 'read', {
293
+ const filters = utils_1.QueryHelper.mergePropertyFilters(prop.filters, options.filters);
294
+ const where = await this.applyFilters(prop.type, {}, filters, 'read', {
294
295
  ...options,
295
296
  populate: hint.children,
296
297
  });
297
298
  const where2 = await this.getJoinedFilters(prop.targetMeta, {
298
299
  ...options,
300
+ filters,
299
301
  populate: hint.children,
300
302
  populateWhere: enums_1.PopulateHint.ALL,
301
303
  });
@@ -328,7 +330,8 @@ class EntityManager {
328
330
  || (parent?.className === prop.targetMeta.root.className && parent.propName === prop.inversedBy)) {
329
331
  continue;
330
332
  }
331
- const cond = await this.applyFilters(prop.type, {}, options.filters ?? {}, 'read', options);
333
+ options = { ...options, filters: utils_1.QueryHelper.mergePropertyFilters(prop.filters, options.filters) };
334
+ const cond = await this.applyFilters(prop.type, {}, options.filters, 'read', options);
332
335
  if (!utils_1.Utils.isEmpty(cond)) {
333
336
  const populated = options.populate.filter(({ field }) => field.split(':')[0] === prop.name);
334
337
  let found = false;
@@ -383,7 +386,7 @@ class EntityManager {
383
386
  let cond;
384
387
  if (filter.cond instanceof Function) {
385
388
  // @ts-ignore
386
- const args = utils_1.Utils.isPlainObject(options[filter.name]) ? options[filter.name] : this.getContext().filterParams[filter.name];
389
+ const args = utils_1.Utils.isPlainObject(options?.[filter.name]) ? options[filter.name] : this.getContext().filterParams[filter.name];
387
390
  if (!args && filter.cond.length > 0 && filter.args !== false) {
388
391
  throw new Error(`No arguments provided for filter '${filter.name}'`);
389
392
  }
@@ -3,6 +3,7 @@ import type { EntityName, Constructor, CheckCallback, GeneratedColumnCallback, A
3
3
  import type { Type, types } from '../types';
4
4
  import type { EntityManager } from '../EntityManager';
5
5
  import type { SerializeOptions } from '../serialization/EntitySerializer';
6
+ import type { FilterOptions } from '../drivers/IDatabaseDriver';
6
7
  export declare function Property<T extends object>(options?: PropertyOptions<T>): (target: any, propertyName: string) => any;
7
8
  export interface PropertyOptions<Owner> {
8
9
  /**
@@ -242,6 +243,8 @@ export interface ReferenceOptions<Owner, Target> extends PropertyOptions<Owner>
242
243
  eager?: boolean;
243
244
  /** Override the default loading strategy for this property. This option has precedence over the global `loadStrategy`, but can be overridden by `FindOptions.strategy`. */
244
245
  strategy?: LoadStrategy | `${LoadStrategy}`;
246
+ /** Control filter parameters for the relation. This will serve as a default value when processing filters on this relation. It's value can be overridden via `em.fork()` or `FindOptions`. */
247
+ filters?: FilterOptions;
245
248
  }
246
249
  /**
247
250
  * Inspired by https://github.com/typeorm/typeorm/blob/941b584ba135617e55d6685caef671172ec1dc03/src/driver/types/ColumnTypes.ts
@@ -35,6 +35,7 @@ class Collection extends ArrayCollection_1.ArrayCollection {
35
35
  async load(options = {}) {
36
36
  if (this.isInitialized(true) && !options.refresh) {
37
37
  const em = this.getEntityManager(this.items, false);
38
+ options = { ...options, filters: utils_1.QueryHelper.mergePropertyFilters(this.property.filters, options.filters) };
38
39
  await em?.populate(this.items, options.populate, options);
39
40
  this.setSerializationContext(options);
40
41
  }
@@ -220,6 +221,7 @@ class Collection extends ArrayCollection_1.ArrayCollection {
220
221
  return this;
221
222
  }
222
223
  const em = this.getEntityManager();
224
+ options = { ...options, filters: utils_1.QueryHelper.mergePropertyFilters(this.property.filters, options.filters) };
223
225
  if (options.dataloader ?? [enums_1.DataloaderType.ALL, enums_1.DataloaderType.COLLECTION].includes(utils_1.DataloaderUtils.getDataloaderType(em.config.get('dataloader')))) {
224
226
  const order = [...this.items]; // copy order of references
225
227
  const orderBy = this.createOrderBy(options.orderBy);
@@ -175,6 +175,10 @@ class EntityHelper {
175
175
  continue;
176
176
  }
177
177
  const inverse = value?.[prop2.name];
178
+ if (prop.ref && owner[prop.name]) {
179
+ // eslint-disable-next-line dot-notation
180
+ owner[prop.name]['property'] = prop;
181
+ }
178
182
  if (Utils_1.Utils.isCollection(inverse) && inverse.isPartial()) {
179
183
  continue;
180
184
  }
@@ -1,7 +1,7 @@
1
- import type { AnyEntity, ConnectionType, Dictionary, EntityProperty, FilterQuery, PopulateOptions } from '../typings';
1
+ import type { AnyEntity, ConnectionType, EntityProperty, FilterQuery, PopulateOptions } from '../typings';
2
2
  import type { EntityManager } from '../EntityManager';
3
3
  import { LoadStrategy, type LockMode, type PopulateHint, PopulatePath, type QueryOrderMap } from '../enums';
4
- import type { EntityField } from '../drivers/IDatabaseDriver';
4
+ import type { EntityField, FilterOptions } from '../drivers/IDatabaseDriver';
5
5
  import type { LoggingOptions } from '../logging/Logger';
6
6
  export type EntityLoaderOptions<Entity, Fields extends string = PopulatePath.ALL, Excludes extends string = never> = {
7
7
  where?: FilterQuery<Entity>;
@@ -14,7 +14,7 @@ export type EntityLoaderOptions<Entity, Fields extends string = PopulatePath.ALL
14
14
  lookup?: boolean;
15
15
  convertCustomTypes?: boolean;
16
16
  ignoreLazyScalarProperties?: boolean;
17
- filters?: Dictionary<boolean | Dictionary> | string[] | boolean;
17
+ filters?: FilterOptions;
18
18
  strategy?: LoadStrategy;
19
19
  lockMode?: Exclude<LockMode, LockMode.OPTIMISTIC>;
20
20
  schema?: string;
@@ -35,7 +35,6 @@ class EntityLoader {
35
35
  const visited = options.visited ??= new Set();
36
36
  options.where ??= {};
37
37
  options.orderBy ??= {};
38
- options.filters ??= {};
39
38
  options.lookup ??= true;
40
39
  options.validate ??= true;
41
40
  options.refresh ??= false;
@@ -310,6 +309,7 @@ class EntityLoader {
310
309
  if (prop.kind === enums_1.ReferenceKind.SCALAR && !prop.lazy) {
311
310
  return;
312
311
  }
312
+ options = { ...options, filters: QueryHelper_1.QueryHelper.mergePropertyFilters(prop.filters, options.filters) };
313
313
  const populated = await this.populateMany(entityName, entities, populate, options);
314
314
  if (!populate.children && !populate.all) {
315
315
  return;
@@ -3,6 +3,7 @@ import type { AddEager, AddOptional, Dictionary, EntityClass, EntityKey, EntityP
3
3
  import type { FindOneOptions, FindOneOrFailOptions } from '../drivers/IDatabaseDriver';
4
4
  export declare class Reference<T extends object> {
5
5
  private entity;
6
+ private property?;
6
7
  constructor(entity: T);
7
8
  static create<T extends object>(entity: T | Ref<T>): Ref<T>;
8
9
  static createFromPK<T extends object>(entityType: EntityClass<T>, pk: Primary<T>, options?: {
@@ -10,6 +10,7 @@ const utils_1 = require("../utils");
10
10
  const errors_1 = require("../errors");
11
11
  class Reference {
12
12
  entity;
13
+ property;
13
14
  constructor(entity) {
14
15
  this.entity = entity;
15
16
  this.set(entity);
@@ -69,7 +70,9 @@ class Reference {
69
70
  */
70
71
  static wrapReference(entity, prop) {
71
72
  if (entity && prop.ref && !Reference.isReference(entity)) {
72
- return Reference.create(entity);
73
+ const ref = Reference.create(entity);
74
+ ref.property = prop;
75
+ return ref;
73
76
  }
74
77
  return entity;
75
78
  }
@@ -89,6 +92,7 @@ class Reference {
89
92
  if (!wrapped.__em) {
90
93
  return this.entity;
91
94
  }
95
+ options = { ...options, filters: utils_1.QueryHelper.mergePropertyFilters(this.property?.filters, options.filters) };
92
96
  if (this.isInitialized() && !options.refresh && options.populate) {
93
97
  await wrapped.__em.populate(this.entity, options.populate, options);
94
98
  }
@@ -149,7 +153,7 @@ class Reference {
149
153
  /** @ignore */
150
154
  [node_util_1.inspect.custom](depth = 2) {
151
155
  const object = { ...this };
152
- const hidden = ['meta'];
156
+ const hidden = ['meta', 'property'];
153
157
  hidden.forEach(k => delete object[k]);
154
158
  const ret = (0, node_util_1.inspect)(object, { depth });
155
159
  const wrapped = (0, wrap_1.helper)(this.entity);
@@ -6,8 +6,8 @@ import type { ManyToOneOptions } from '../decorators/ManyToOne';
6
6
  import type { OneToManyOptions } from '../decorators/OneToMany';
7
7
  import type { OneToOneOptions } from '../decorators/OneToOne';
8
8
  import type { ManyToManyOptions } from '../decorators/ManyToMany';
9
- import type { AnyString, GeneratedColumnCallback, Constructor, CheckCallback, FilterQuery, EntityName, Dictionary, EntityMetadata, PrimaryKeyProp, Hidden, Opt, Primary, EntityClass } from '../typings';
10
- import type { Reference, ScalarReference } from './Reference';
9
+ import type { AnyString, GeneratedColumnCallback, Constructor, CheckCallback, FilterQuery, EntityName, Dictionary, EntityMetadata, PrimaryKeyProp, Hidden, Opt, Primary, EntityClass, Ref } from '../typings';
10
+ import type { ScalarReference } from './Reference';
11
11
  import type { SerializeOptions } from '../serialization/EntitySerializer';
12
12
  import type { Cascade, DeferMode, EventType, LoadStrategy, QueryOrderMap } from '../enums';
13
13
  import type { IType, Type } from '../types/Type';
@@ -15,6 +15,7 @@ import { types } from '../types';
15
15
  import { EntitySchema } from '../metadata/EntitySchema';
16
16
  import type { Collection } from './Collection';
17
17
  import type { EventSubscriber } from '../events';
18
+ import type { FilterOptions } from '../drivers/IDatabaseDriver';
18
19
  export type UniversalPropertyKeys = keyof PropertyOptions<any> | keyof EnumOptions<any> | keyof EmbeddedOptions<any, any> | keyof ReferenceOptions<any, any> | keyof ManyToOneOptions<any, any> | keyof OneToManyOptions<any, any> | keyof OneToOneOptions<any, any> | keyof ManyToManyOptions<any, any>;
19
20
  type BuilderExtraKeys = '~options' | '~type' | '$type';
20
21
  type ExcludeKeys = 'entity' | 'items';
@@ -134,6 +135,10 @@ export declare class UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys
134
135
  defaultRaw(defaultRaw: string): Pick<UniversalPropertyOptionsBuilder<Value, Options & {
135
136
  defaultRaw: string;
136
137
  }, IncludeKeys>, IncludeKeys>;
138
+ /**
139
+ * Allow controlling `filters` option. This will be overridden with `em.fork` or `FindOptions` if provided.
140
+ */
141
+ filters(filters: FilterOptions): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
137
142
  /**
138
143
  * Set to map some SQL snippet for the entity.
139
144
  *
@@ -511,10 +516,10 @@ type MaybeRelationRef<Value, Options> = Options extends {
511
516
  } ? Value : Options extends {
512
517
  ref: true;
513
518
  kind: '1:1';
514
- } ? Value extends object ? Reference<Value> : never : Options extends {
519
+ } ? Value extends object ? Ref<Value> : never : Options extends {
515
520
  ref: true;
516
521
  kind: 'm:1';
517
- } ? Value extends object ? Reference<Value> : never : Options extends {
522
+ } ? Value extends object ? Ref<Value> : never : Options extends {
518
523
  kind: '1:m';
519
524
  } ? Value extends object ? Collection<Value> : never : Options extends {
520
525
  kind: 'm:n';
@@ -126,6 +126,12 @@ class UniversalPropertyOptionsBuilder {
126
126
  defaultRaw(defaultRaw) {
127
127
  return this.assignOptions({ defaultRaw });
128
128
  }
129
+ /**
130
+ * Allow controlling `filters` option. This will be overridden with `em.fork` or `FindOptions` if provided.
131
+ */
132
+ filters(filters) {
133
+ return this.assignOptions({ filters });
134
+ }
129
135
  /**
130
136
  * Set to map some SQL snippet for the entity.
131
137
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/core",
3
- "version": "6.5.10-dev.23",
3
+ "version": "6.5.10-dev.25",
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.23",
67
+ "mikro-orm": "6.5.10-dev.25",
68
68
  "reflect-metadata": "0.2.2"
69
69
  }
70
70
  }
package/typings.d.ts CHANGED
@@ -10,7 +10,7 @@ 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';
13
- import type { FindOneOptions, FindOptions, LoadHint } from './drivers';
13
+ import type { FilterOptions, FindOneOptions, FindOptions, LoadHint } from './drivers';
14
14
  export type Constructor<T = unknown> = new (...args: any[]) => T;
15
15
  export type Dictionary<T = any> = {
16
16
  [k: string]: T;
@@ -351,6 +351,7 @@ export interface EntityProperty<Owner = any, Target = any> {
351
351
  default?: string | number | boolean | null;
352
352
  defaultRaw?: string;
353
353
  formula?: (alias: string) => string;
354
+ filters?: FilterOptions;
354
355
  prefix?: string | boolean;
355
356
  prefixMode?: EmbeddedPrefixMode;
356
357
  embedded?: [EntityKey<Owner>, EntityKey<Owner>];
@@ -1,6 +1,7 @@
1
1
  import type { Dictionary, EntityMetadata, EntityProperty, FilterDef, FilterQuery } from '../typings';
2
2
  import type { Platform } from '../platforms';
3
3
  import type { MetadataStorage } from '../metadata/MetadataStorage';
4
+ import type { FilterOptions } from '../drivers/IDatabaseDriver';
4
5
  /** @internal */
5
6
  export declare class QueryHelper {
6
7
  static readonly SUPPORTED_OPERATORS: string[];
@@ -13,7 +14,8 @@ export declare class QueryHelper {
13
14
  static liftGroupOperators<T extends object>(where: Dictionary, meta: EntityMetadata<T>, metadata: MetadataStorage, key?: string): string | undefined;
14
15
  static inlinePrimaryKeyObjects<T extends object>(where: Dictionary, meta: EntityMetadata<T>, metadata: MetadataStorage, key?: string): boolean;
15
16
  static processWhere<T extends object>(options: ProcessWhereOptions<T>): FilterQuery<T>;
16
- static getActiveFilters(entityName: string, options: Dictionary<boolean | Dictionary> | string[] | boolean, filters: Dictionary<FilterDef>): FilterDef[];
17
+ static getActiveFilters(entityName: string, options: FilterOptions | undefined, filters: Dictionary<FilterDef>): FilterDef[];
18
+ static mergePropertyFilters(propFilters: FilterOptions | undefined, options: FilterOptions | undefined): FilterOptions | undefined;
17
19
  static isFilterActive(entityName: string, filterName: string, filter: FilterDef, options: Dictionary<boolean | Dictionary>): boolean;
18
20
  static processCustomType<T extends object>(prop: EntityProperty<T>, cond: FilterQuery<T>, platform: Platform, key?: string, fromQuery?: boolean): FilterQuery<T>;
19
21
  private static isSupportedOperator;
@@ -205,6 +205,24 @@ class QueryHelper {
205
205
  return filters[f];
206
206
  });
207
207
  }
208
+ static mergePropertyFilters(propFilters, options) {
209
+ if (!options || !propFilters || options === true || propFilters === true) {
210
+ return options ?? propFilters;
211
+ }
212
+ if (Array.isArray(propFilters)) {
213
+ propFilters = propFilters.reduce((o, item) => {
214
+ o[item] = true;
215
+ return o;
216
+ }, {});
217
+ }
218
+ if (Array.isArray(options)) {
219
+ options = options.reduce((o, item) => {
220
+ o[item] = true;
221
+ return o;
222
+ }, {});
223
+ }
224
+ return Utils_1.Utils.mergeConfig({}, propFilters, options);
225
+ }
208
226
  static isFilterActive(entityName, filterName, filter, options) {
209
227
  if (filter.entity && !filter.entity.includes(entityName)) {
210
228
  return false;