@mikro-orm/core 6.3.9-dev.1 → 6.3.9-dev.3

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.
@@ -115,7 +115,8 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
115
115
  setFlushMode(flushMode?: FlushMode): void;
116
116
  protected processWhere<Entity extends object, Hint extends string = never, Fields extends string = '*', Excludes extends string = never>(entityName: string, where: FilterQuery<Entity>, options: FindOptions<Entity, Hint, Fields, Excludes> | FindOneOptions<Entity, Hint, Fields, Excludes>, type: 'read' | 'update' | 'delete'): Promise<FilterQuery<Entity>>;
117
117
  protected applyDiscriminatorCondition<Entity extends object>(entityName: string, where: FilterQuery<Entity>): FilterQuery<Entity>;
118
- protected applyJoinedFilters<Entity extends object>(meta: EntityMetadata<Entity>, cond: ObjectQuery<Entity>, options: FindOptions<Entity, any, any, any> | FindOneOptions<Entity, any, any, any>): Promise<ObjectQuery<Entity>>;
118
+ protected createPopulateWhere<Entity extends object>(cond: ObjectQuery<Entity>, options: FindOptions<Entity, any, any, any> | FindOneOptions<Entity, any, any, any>): ObjectQuery<Entity>;
119
+ protected getJoinedFilters<Entity extends object>(meta: EntityMetadata<Entity>, cond: ObjectQuery<Entity>, options: FindOptions<Entity, any, any, any> | FindOneOptions<Entity, any, any, any>): Promise<ObjectQuery<Entity>>;
119
120
  /**
120
121
  * 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.
121
122
  */
package/EntityManager.js CHANGED
@@ -140,7 +140,8 @@ class EntityManager {
140
140
  options = { ...options };
141
141
  // save the original hint value so we know it was infer/all
142
142
  options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
143
- options.populateWhere = await this.applyJoinedFilters(meta, { ...where }, options);
143
+ options.populateWhere = this.createPopulateWhere({ ...where }, options);
144
+ options.populateFilter = await this.getJoinedFilters(meta, { ...where }, options);
144
145
  const results = await em.driver.find(entityName, where, { ctx: em.transactionContext, ...options });
145
146
  if (results.length === 0) {
146
147
  await em.storeCache(options.cache, cached, []);
@@ -264,7 +265,7 @@ class EntityManager {
264
265
  where[meta.root.discriminatorColumn] = children.length > 0 ? { $in: [meta.discriminatorValue, ...children.map(c => c.discriminatorValue)] } : meta.discriminatorValue;
265
266
  return where;
266
267
  }
267
- async applyJoinedFilters(meta, cond, options) {
268
+ createPopulateWhere(cond, options) {
268
269
  const ret = {};
269
270
  const populateWhere = options.populateWhere ?? this.config.get('populateWhere');
270
271
  if (populateWhere === enums_1.PopulateHint.INFER) {
@@ -273,6 +274,10 @@ class EntityManager {
273
274
  else if (typeof populateWhere === 'object') {
274
275
  utils_1.Utils.merge(ret, populateWhere);
275
276
  }
277
+ return ret;
278
+ }
279
+ async getJoinedFilters(meta, cond, options) {
280
+ const ret = {};
276
281
  if (options.populate) {
277
282
  for (const hint of options.populate) {
278
283
  const field = hint.field.split(':')[0];
@@ -282,7 +287,7 @@ class EntityManager {
282
287
  continue;
283
288
  }
284
289
  const where = await this.applyFilters(prop.type, {}, options.filters ?? {}, 'read', { ...options, populate: hint.children });
285
- const where2 = await this.applyJoinedFilters(prop.targetMeta, {}, { ...options, populate: hint.children, populateWhere: enums_1.PopulateHint.ALL });
290
+ const where2 = await this.getJoinedFilters(prop.targetMeta, {}, { ...options, populate: hint.children, populateWhere: enums_1.PopulateHint.ALL });
286
291
  if (utils_1.Utils.hasObjectKeys(where)) {
287
292
  ret[field] = ret[field] ? { $and: [where, ret[field]] } : where;
288
293
  }
@@ -531,7 +536,8 @@ class EntityManager {
531
536
  options = { ...options };
532
537
  // save the original hint value so we know it was infer/all
533
538
  options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
534
- options.populateWhere = await this.applyJoinedFilters(meta, { ...where }, options);
539
+ options.populateWhere = this.createPopulateWhere({ ...where }, options);
540
+ options.populateFilter = await this.getJoinedFilters(meta, { ...where }, options);
535
541
  const data = await em.driver.findOne(entityName, where, {
536
542
  ctx: em.transactionContext,
537
543
  ...options,
@@ -74,11 +74,36 @@ export interface LoadHint<Entity, Hint extends string = never, Fields extends st
74
74
  exclude?: readonly AutoPath<Entity, Excludes>[];
75
75
  }
76
76
  export interface FindOptions<Entity, Hint extends string = never, Fields extends string = PopulatePath.ALL, Excludes extends string = never> extends LoadHint<Entity, Hint, Fields, Excludes> {
77
+ /**
78
+ * Where condition for populated relations. This will have no effect on the root entity.
79
+ * With `select-in` strategy, this is applied only to the populate queries.
80
+ * With `joined` strategy, those are applied as `join on` conditions.
81
+ * When you use a nested condition on a to-many relation, it will produce a nested inner join,
82
+ * discarding the collection items based on the child condition.
83
+ */
77
84
  populateWhere?: ObjectQuery<Entity> | PopulateHint | `${PopulateHint}`;
85
+ /**
86
+ * Filter condition for populated relations. This is similar to `populateWhere`, but will produce a `left join`
87
+ * when nesting the condition. This is used for implementation of joined filters.
88
+ */
89
+ populateFilter?: ObjectQuery<Entity> | PopulateHint | `${PopulateHint}`;
90
+ /** Used for ordering of the populate queries. If not specified, the value of `options.orderBy` is used. */
78
91
  populateOrderBy?: OrderDefinition<Entity>;
92
+ /** Ordering of the results.Can be an object or array of objects, keys are property names, values are ordering (asc/desc) */
79
93
  orderBy?: OrderDefinition<Entity>;
94
+ /** Control result caching for this query. Result cache is by default disabled, not to be confused with the identity map. */
80
95
  cache?: boolean | number | [string, number];
96
+ /**
97
+ * Limit the number of returned results. If you try to use limit/offset on a query that joins a to-many relation, pagination mechanism
98
+ * will be triggered, resulting in a subquery condition, to apply this limit only to the root entities
99
+ * instead of the cartesian product you get from a database in this case.
100
+ */
81
101
  limit?: number;
102
+ /**
103
+ * Sets the offset. If you try to use limit/offset on a query that joins a to-many relation, pagination mechanism
104
+ * will be triggered, resulting in a subquery condition, to apply this limit only to the root entities
105
+ * instead of the cartesian product you get from a database in this case.
106
+ */
82
107
  offset?: number;
83
108
  /** Fetch items `before` this cursor. */
84
109
  before?: string | {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/core",
3
- "version": "6.3.9-dev.1",
3
+ "version": "6.3.9-dev.3",
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.2.0",
66
66
  "globby": "11.1.0",
67
- "mikro-orm": "6.3.9-dev.1",
67
+ "mikro-orm": "6.3.9-dev.3",
68
68
  "reflect-metadata": "0.2.2"
69
69
  }
70
70
  }
@@ -12,6 +12,7 @@ export declare class QueryHelper {
12
12
  static processCustomType<T extends object>(prop: EntityProperty<T>, cond: FilterQuery<T>, platform: Platform, key?: string, fromQuery?: boolean): FilterQuery<T>;
13
13
  private static isSupportedOperator;
14
14
  private static processJsonCondition;
15
+ private static getValueType;
15
16
  static findProperty<T>(fieldName: string, options: ProcessWhereOptions<T>): EntityProperty<T> | undefined;
16
17
  }
17
18
  interface ProcessWhereOptions<T> {
@@ -197,7 +197,7 @@ class QueryHelper {
197
197
  return prop.customType.convertToDatabaseValue(cond, platform, { fromQuery, key, mode: 'query' });
198
198
  }
199
199
  static isSupportedOperator(key) {
200
- return !!QueryHelper.SUPPORTED_OPERATORS.find(op => key.includes(op));
200
+ return !!QueryHelper.SUPPORTED_OPERATORS.find(op => key === op);
201
201
  }
202
202
  static processJsonCondition(o, value, path, platform, alias) {
203
203
  if (Utils_1.Utils.isPlainObject(value) && !Object.keys(value).some(k => Utils_1.Utils.isOperator(k))) {
@@ -210,12 +210,20 @@ class QueryHelper {
210
210
  o[path[0]] = value;
211
211
  return o;
212
212
  }
213
- const operatorObject = Utils_1.Utils.isPlainObject(value) && Object.keys(value).every(k => Utils_1.Utils.isOperator(k));
214
- const type = operatorObject ? typeof Object.values(value)[0] : typeof value;
213
+ const type = this.getValueType(value);
215
214
  const k = platform.getSearchJsonPropertyKey(path, type, alias, value);
216
215
  o[k] = value;
217
216
  return o;
218
217
  }
218
+ static getValueType(value) {
219
+ if (Array.isArray(value)) {
220
+ return typeof value[0];
221
+ }
222
+ if (Utils_1.Utils.isPlainObject(value) && Object.keys(value).every(k => Utils_1.Utils.isOperator(k))) {
223
+ return this.getValueType(Object.values(value)[0]);
224
+ }
225
+ return typeof value;
226
+ }
219
227
  static findProperty(fieldName, options) {
220
228
  const parts = fieldName.split('.');
221
229
  const propName = parts.pop();