@mikro-orm/core 7.2.4-dev.10 → 7.2.4-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.
package/EntityManager.js CHANGED
@@ -1283,8 +1283,8 @@ export class EntityManager {
1283
1283
  connectionType: 'write',
1284
1284
  schema: options.schema,
1285
1285
  }));
1286
- for (const [entity, cond] of loadPK.entries()) {
1287
- const row = data2.find(row => {
1286
+ if (loadPK.size > 0) {
1287
+ const comparableRows = data2.map(row => {
1288
1288
  const tmp = {};
1289
1289
  add.forEach(k => {
1290
1290
  const prop = meta.properties[k];
@@ -1292,13 +1292,43 @@ export class EntityManager {
1292
1292
  tmp[k] = prop?.customType ? prop.customType.convertToDatabaseValue(row[k], this.getPlatform()) : row[k];
1293
1293
  }
1294
1294
  });
1295
- return this.#comparator.matching(entityName, cond, tmp);
1295
+ return tmp;
1296
1296
  });
1297
- /* v8 ignore next */
1298
- if (!row) {
1299
- throw new Error(`Cannot find matching entity for condition ${JSON.stringify(cond)}`);
1297
+ const isMatch = (cond, idx) => this.#comparator.matching(entityName, cond, comparableRows[idx]);
1298
+ // When every condition key is a comparable prop checked with `!==`, a matching row has the same primitive
1299
+ // values, so rows can be grouped by those values once instead of comparing every row for every entity.
1300
+ // Props outside `comparableProps` are skipped by the comparator, so they never qualify for grouping.
1301
+ const groupableProps = new Set(meta.comparableProps.filter(prop => this.#comparator.isStrictlyCompared(prop)));
1302
+ const groups = new Map();
1303
+ // `null` and `undefined` share a key, which can only make a group wider than what `matching` accepts
1304
+ const groupKey = (data, keys) => keys.map(k => (data[k] == null ? 'null' : `${typeof data[k]}:${data[k]}`)).join('\0');
1305
+ const findRowIndex = (cond) => {
1306
+ const keys = Utils.keys(cond);
1307
+ if (!keys.every(k => groupableProps.has(meta.properties[k]))) {
1308
+ return comparableRows.findIndex((_, idx) => isMatch(cond, idx));
1309
+ }
1310
+ const signature = keys.join('\0');
1311
+ if (!groups.has(signature)) {
1312
+ const rowsByKey = new Map();
1313
+ comparableRows.forEach((tmp, idx) => {
1314
+ const rowKey = groupKey(tmp, keys);
1315
+ const indexes = rowsByKey.get(rowKey) ?? [];
1316
+ indexes.push(idx);
1317
+ rowsByKey.set(rowKey, indexes);
1318
+ });
1319
+ groups.set(signature, rowsByKey);
1320
+ }
1321
+ const group = groups.get(signature);
1322
+ return group.get(groupKey(cond, keys))?.find(idx => isMatch(cond, idx)) ?? -1;
1323
+ };
1324
+ for (const [entity, cond] of loadPK.entries()) {
1325
+ const row = data2[findRowIndex(cond)];
1326
+ /* v8 ignore next */
1327
+ if (!row) {
1328
+ throw new Error(`Cannot find matching entity for condition ${JSON.stringify(cond)}`);
1329
+ }
1330
+ em.getHydrator().hydrate(entity, meta, row, em.#entityFactory, 'full', false, true);
1300
1331
  }
1301
- em.getHydrator().hydrate(entity, meta, row, em.#entityFactory, 'full', false, true);
1302
1332
  }
1303
1333
  if (loadPK.size !== data2.length && Array.isArray(uniqueFields)) {
1304
1334
  for (let i = 0; i < allData.length; i++) {
@@ -641,7 +641,7 @@ export interface EntityMetadataWithProperties<TName extends string, TTableName e
641
641
  '~discriminatorColumn'?: TBaseDiscriminatorColumn;
642
642
  });
643
643
  properties: TProperties | ((properties: PropertyBuilders) => TProperties);
644
- primaryKeys?: TPK & InferPrimaryKeyConstraint<TProperties>[];
644
+ primaryKeys?: TPK & NoInfer<InferPrimaryKeyConstraint<TProperties>>[];
645
645
  hooks?: DefineEntityHooks;
646
646
  repository?: () => TRepository;
647
647
  filters?: Dictionary<{
@@ -667,16 +667,16 @@ export interface EntityMetadataWithProperties<TName extends string, TTableName e
667
667
  /** Alias for `discriminator`. */
668
668
  discriminatorColumn?: TDiscriminatorColumn;
669
669
  discriminatorValue?: TDiscriminatorValue;
670
- versionProperty?: AllKeys<TProperties, TBase>;
671
- concurrencyCheckKeys?: Set<AllKeys<TProperties, TBase>>;
672
- serializedPrimaryKey?: AllKeys<TProperties, TBase>;
670
+ versionProperty?: NoInfer<AllKeys<TProperties, TBase>>;
671
+ concurrencyCheckKeys?: Set<NoInfer<AllKeys<TProperties, TBase>>>;
672
+ serializedPrimaryKey?: NoInfer<AllKeys<TProperties, TBase>>;
673
673
  indexes?: {
674
674
  properties?: NoInfer<AllKeys<TProperties, TBase>> | NoInfer<AllKeys<TProperties, TBase>>[];
675
675
  name?: string;
676
676
  type?: string;
677
677
  options?: Dictionary;
678
678
  expression?: string | IndexCallback<InferEntityFromProperties<TProperties, TPK, TBase>>;
679
- where?: PartialWhere<TProperties, TBase>;
679
+ where?: NoInfer<PartialWhere<TProperties, TBase>>;
680
680
  columns?: IndexColumnOptions[];
681
681
  include?: NoInfer<AllKeys<TProperties, TBase>> | NoInfer<AllKeys<TProperties, TBase>>[];
682
682
  fillFactor?: number;
@@ -689,7 +689,7 @@ export interface EntityMetadataWithProperties<TName extends string, TTableName e
689
689
  name?: string;
690
690
  options?: Dictionary;
691
691
  expression?: string | IndexCallback<InferEntityFromProperties<TProperties, TPK, TBase>>;
692
- where?: PartialWhere<TProperties, TBase>;
692
+ where?: NoInfer<PartialWhere<TProperties, TBase>>;
693
693
  deferMode?: DeferMode | `${DeferMode}`;
694
694
  columns?: IndexColumnOptions[];
695
695
  include?: NoInfer<AllKeys<TProperties, TBase>> | NoInfer<AllKeys<TProperties, TBase>>[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/core",
3
- "version": "7.2.4-dev.10",
3
+ "version": "7.2.4-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
  "keywords": [
6
6
  "data-mapper",
@@ -19,6 +19,11 @@ export declare class EntityComparator {
19
19
  diffEntities<T extends object>(entityName: EntityName<T>, a: EntityData<T>, b: EntityData<T>, options?: {
20
20
  includeInverseSides?: boolean;
21
21
  }): EntityData<T>;
22
+ /**
23
+ * Returns true if the comparator checks this property with `!==`, so matching values are identical primitives.
24
+ * @internal
25
+ */
26
+ isStrictlyCompared<T>(prop: EntityProperty<T>): boolean;
22
27
  /** Returns true if two entity snapshots are identical (no differences). */
23
28
  matching<T extends object>(entityName: EntityName<T>, a: EntityData<T>, b: EntityData<T>): boolean;
24
29
  /**
@@ -29,6 +29,17 @@ export class EntityComparator {
29
29
  const comparator = this.getEntityComparator(entityName);
30
30
  return Utils.callCompiledFunction(comparator, a, b, options);
31
31
  }
32
+ /**
33
+ * Returns true if the comparator checks this property with `!==`, so matching values are identical primitives.
34
+ * @internal
35
+ */
36
+ isStrictlyCompared(prop) {
37
+ if (prop.kind !== ReferenceKind.SCALAR || prop.customType?.compareValues) {
38
+ return false;
39
+ }
40
+ const type = (prop.customType ? prop.customType.compareAsType() : prop.type).toLowerCase();
41
+ return ['string', 'number', 'bigint'].includes(type);
42
+ }
32
43
  /** Returns true if two entity snapshots are identical (no differences). */
33
44
  matching(entityName, a, b) {
34
45
  const diff = this.diffEntities(entityName, a, b);
package/utils/Utils.js CHANGED
@@ -153,7 +153,7 @@ export function parseJsonSafe(value) {
153
153
  /** Collection of general-purpose utility methods used throughout the ORM. */
154
154
  export class Utils {
155
155
  static PK_SEPARATOR = '~~~';
156
- static #ORM_VERSION = '7.2.4-dev.10';
156
+ static #ORM_VERSION = '7.2.4-dev.12';
157
157
  /** Default session variable name backing an RLS filter argument (`current_setting('mikro.<filter>.<arg>')`). */
158
158
  static getRlsSettingName(filterName, argName) {
159
159
  return `mikro.${filterName}.${argName}`;