@mikro-orm/core 6.4.14 → 6.4.15-dev.0

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.
@@ -1,7 +1,7 @@
1
1
  import { inspect } from 'node:util';
2
2
  import DataLoader from 'dataloader';
3
3
  import { type Configuration, Cursor } from './utils';
4
- import { type AssignOptions, EntityFactory, type EntityLoaderOptions, type EntityRepository, EntityValidator, Reference } from './entity';
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
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';
@@ -430,6 +430,10 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
430
430
  * Gets the EntityFactory used by the EntityManager.
431
431
  */
432
432
  getEntityFactory(): EntityFactory;
433
+ /**
434
+ * @internal use `em.populate()` as the user facing API, this is exposed only for internal usage
435
+ */
436
+ getEntityLoader(): EntityLoader;
433
437
  /**
434
438
  * Gets the Hydrator used by the EntityManager.
435
439
  */
package/EntityManager.js CHANGED
@@ -1449,6 +1449,12 @@ class EntityManager {
1449
1449
  getEntityFactory() {
1450
1450
  return this.getContext().entityFactory;
1451
1451
  }
1452
+ /**
1453
+ * @internal use `em.populate()` as the user facing API, this is exposed only for internal usage
1454
+ */
1455
+ getEntityLoader() {
1456
+ return this.getContext().entityLoader;
1457
+ }
1452
1458
  /**
1453
1459
  * Gets the Hydrator used by the EntityManager.
1454
1460
  */
@@ -2,7 +2,7 @@ import type { EntityDTO, EntityKey, FilterQuery, Loaded, LoadedCollection, Popul
2
2
  import { ArrayCollection } from './ArrayCollection';
3
3
  import { Reference } from './Reference';
4
4
  import type { Transaction } from '../connections/Connection';
5
- import type { FindOptions, CountOptions } from '../drivers/IDatabaseDriver';
5
+ import type { CountOptions, FindOptions } from '../drivers/IDatabaseDriver';
6
6
  import type { EntityLoaderOptions } from './EntityLoader';
7
7
  export interface MatchingOptions<T extends object, P extends string = never> extends FindOptions<T, P> {
8
8
  where?: FilterQuery<T>;
@@ -229,8 +229,13 @@ class Collection extends ArrayCollection_1.ArrayCollection {
229
229
  this,
230
230
  { ...options, orderBy },
231
231
  ]);
232
- if (!customOrder) {
233
- this.reorderItems(items, order);
232
+ if (this.property.kind === enums_1.ReferenceKind.MANY_TO_MANY) {
233
+ this.initialized = true;
234
+ this.dirty = false;
235
+ if (!customOrder) {
236
+ this.reorderItems(items, order);
237
+ }
238
+ return this;
234
239
  }
235
240
  this.items.clear();
236
241
  let i = 0;
@@ -1,4 +1,4 @@
1
- import type { ConnectionType, Dictionary, FilterQuery, PopulateOptions } from '../typings';
1
+ import type { AnyEntity, ConnectionType, Dictionary, 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
4
  import type { EntityField } from '../drivers/IDatabaseDriver';
@@ -49,7 +49,8 @@ export declare class EntityLoader {
49
49
  private findChildren;
50
50
  private mergePrimaryCondition;
51
51
  private populateField;
52
- private findChildrenFromPivotTable;
52
+ /** @internal */
53
+ findChildrenFromPivotTable<Entity extends object>(filtered: Entity[], prop: EntityProperty<Entity>, options: Required<EntityLoaderOptions<Entity>>, orderBy?: QueryOrderMap<Entity>[], populate?: PopulateOptions<Entity>, pivotJoin?: boolean): Promise<AnyEntity[][]>;
53
54
  private extractChildCondition;
54
55
  private buildFields;
55
56
  private getChildReferences;
@@ -144,7 +144,8 @@ class EntityLoader {
144
144
  .filter(orderBy => (Array.isArray(orderBy[prop.name]) && orderBy[prop.name].length > 0) || Utils_1.Utils.isObject(orderBy[prop.name]))
145
145
  .flatMap(orderBy => orderBy[prop.name]);
146
146
  if (prop.kind === enums_1.ReferenceKind.MANY_TO_MANY && this.driver.getPlatform().usesPivotTable()) {
147
- return this.findChildrenFromPivotTable(filtered, prop, options, innerOrderBy, populate, !!ref);
147
+ const res = await this.findChildrenFromPivotTable(filtered, prop, options, innerOrderBy, populate, !!ref);
148
+ return Utils_1.Utils.flatten(res);
148
149
  }
149
150
  const where = await this.extractChildCondition(options, prop);
150
151
  const data = await this.findChildren(entities, prop, populate, { ...options, where, orderBy: innerOrderBy }, !!(ref || prop.mapToPk));
@@ -342,6 +343,7 @@ class EntityLoader {
342
343
  visited: options.visited,
343
344
  });
344
345
  }
346
+ /** @internal */
345
347
  async findChildrenFromPivotTable(filtered, prop, options, orderBy, populate, pivotJoin) {
346
348
  const ids = filtered.map(e => e.__helper.__primaryKeys);
347
349
  const refresh = options.refresh;
@@ -379,7 +381,7 @@ class EntityLoader {
379
381
  return this.em.getUnitOfWork().register(entity, item, { refresh, loaded: true });
380
382
  });
381
383
  entity[prop.name].hydrate(items, true);
382
- children.push(...items);
384
+ children.push(items);
383
385
  }
384
386
  return children;
385
387
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/core",
3
- "version": "6.4.14",
3
+ "version": "6.4.15-dev.0",
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.0",
66
66
  "globby": "11.1.0",
67
- "mikro-orm": "6.4.14",
67
+ "mikro-orm": "6.4.15-dev.0",
68
68
  "reflect-metadata": "0.2.2"
69
69
  }
70
70
  }
@@ -5,6 +5,7 @@ const Collection_1 = require("../entity/Collection");
5
5
  const wrap_1 = require("../entity/wrap");
6
6
  const enums_1 = require("../enums");
7
7
  const Reference_1 = require("../entity/Reference");
8
+ const Utils_1 = require("./Utils");
8
9
  class DataloaderUtils {
9
10
  /**
10
11
  * Groups identified references by entity and returns a Map with the
@@ -121,7 +122,6 @@ class DataloaderUtils {
121
122
  // We need to populate the inverse side of the relationship in order to be able to later retrieve the PK(s) from its item(s)
122
123
  populate: [
123
124
  ...(opts.populate === false ? [] : opts.populate ?? []),
124
- ...(opts.ref ? [':ref'] : []),
125
125
  ...Array.from(filterMap.keys()).filter(
126
126
  // We need to do so only if the inverse side is a collection, because we can already retrieve the PK from a reference without having to load it
127
127
  prop => em.getMetadata(className).properties[prop]?.ref !== true),
@@ -152,10 +152,6 @@ class DataloaderUtils {
152
152
  else if (target) {
153
153
  return target === collection.owner;
154
154
  }
155
- // FIXME https://github.com/mikro-orm/mikro-orm/issues/6031
156
- if (!target && collection.property.kind === enums_1.ReferenceKind.MANY_TO_MANY) {
157
- throw new Error(`Inverse side is required for M:N relations with dataloader: ${collection.owner.constructor.name}.${collection.property.name}`);
158
- }
159
155
  return false;
160
156
  };
161
157
  }
@@ -165,6 +161,24 @@ class DataloaderUtils {
165
161
  */
166
162
  static getColBatchLoadFn(em) {
167
163
  return async (collsWithOpts) => {
164
+ const prop = collsWithOpts[0][0].property;
165
+ if (prop.kind === enums_1.ReferenceKind.MANY_TO_MANY && em.getPlatform().usesPivotTable()) {
166
+ const options = {};
167
+ const wrap = (cond) => ({ [prop.name]: cond });
168
+ const orderBy = Utils_1.Utils.asArray(collsWithOpts[0][1]?.orderBy).map(o => wrap(o));
169
+ const populate = wrap(collsWithOpts[0][1]?.populate);
170
+ const owners = collsWithOpts.map(c => c[0].owner);
171
+ const $or = [];
172
+ // a bit of a hack, but we need to prefix the key, since we have only a column name, not a property name
173
+ const alias = em.config.getNamingStrategy().aliasName(prop.pivotEntity, 0);
174
+ const fk = `${alias}.${Utils_1.Utils.getPrimaryKeyHash(prop.joinColumns)}`;
175
+ for (const c of collsWithOpts) {
176
+ $or.push({ $and: [c[1]?.where ?? {}, { [fk]: c[0].owner }] });
177
+ options.refresh ??= c[1]?.refresh;
178
+ }
179
+ options.where = wrap({ $or });
180
+ return em.getEntityLoader().findChildrenFromPivotTable(owners, prop, options, orderBy, populate, collsWithOpts[0][1]?.ref);
181
+ }
168
182
  const entitiesAndOptsMap = DataloaderUtils.groupInversedOrMappedKeysByEntityAndOpts(collsWithOpts);
169
183
  const promises = DataloaderUtils.entitiesAndOptsMapToQueries(entitiesAndOptsMap, em);
170
184
  const resultsMap = new Map(await Promise.all(promises));