@mikro-orm/core 7.0.0-rc.2 → 7.0.0-rc.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.
- package/EntityManager.d.ts +2 -1
- package/EntityManager.js +106 -42
- package/MikroORM.js +4 -4
- package/cache/FileCacheAdapter.js +1 -3
- package/connections/Connection.js +16 -3
- package/drivers/DatabaseDriver.js +26 -8
- package/drivers/IDatabaseDriver.d.ts +43 -0
- package/entity/Collection.js +43 -17
- package/entity/EntityAssigner.js +23 -11
- package/entity/EntityFactory.js +32 -12
- package/entity/EntityHelper.js +25 -16
- package/entity/EntityLoader.js +55 -22
- package/entity/Reference.d.ts +1 -1
- package/entity/Reference.js +37 -8
- package/entity/WrappedEntity.js +5 -1
- package/entity/defineEntity.d.ts +24 -12
- package/entity/utils.js +28 -26
- package/entity/validators.js +2 -1
- package/enums.js +12 -17
- package/errors.js +18 -8
- package/events/EventManager.js +1 -1
- package/exceptions.js +7 -2
- package/hydration/ObjectHydrator.js +27 -13
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/logging/DefaultLogger.js +3 -5
- package/logging/colors.js +3 -6
- package/metadata/EntitySchema.d.ts +2 -2
- package/metadata/EntitySchema.js +12 -2
- package/metadata/MetadataDiscovery.js +106 -47
- package/metadata/MetadataProvider.js +26 -1
- package/metadata/MetadataStorage.js +2 -4
- package/metadata/MetadataValidator.js +20 -5
- package/metadata/types.d.ts +2 -2
- package/naming-strategy/AbstractNamingStrategy.js +5 -2
- package/not-supported.js +5 -1
- package/package.json +38 -38
- package/platforms/Platform.d.ts +1 -0
- package/platforms/Platform.js +49 -23
- package/serialization/EntitySerializer.js +7 -3
- package/serialization/SerializationContext.js +1 -1
- package/typings.d.ts +23 -23
- package/typings.js +9 -9
- package/unit-of-work/ChangeSet.js +4 -4
- package/unit-of-work/ChangeSetComputer.js +8 -6
- package/unit-of-work/ChangeSetPersister.js +13 -8
- package/unit-of-work/CommitOrderCalculator.js +4 -2
- package/unit-of-work/UnitOfWork.d.ts +7 -1
- package/unit-of-work/UnitOfWork.js +51 -22
- package/utils/AbstractMigrator.d.ts +1 -1
- package/utils/AbstractMigrator.js +3 -5
- package/utils/AbstractSchemaGenerator.js +2 -1
- package/utils/AsyncContext.js +1 -1
- package/utils/Configuration.js +8 -4
- package/utils/Cursor.js +4 -2
- package/utils/DataloaderUtils.js +15 -12
- package/utils/EntityComparator.js +51 -43
- package/utils/QueryHelper.js +38 -26
- package/utils/RawQueryFragment.js +3 -2
- package/utils/TransactionManager.js +2 -1
- package/utils/Utils.d.ts +1 -1
- package/utils/Utils.js +36 -30
- package/utils/env-vars.js +6 -5
- package/utils/fs-utils.js +2 -5
- package/utils/upsert-utils.js +6 -3
|
@@ -76,7 +76,8 @@ export class UnitOfWork {
|
|
|
76
76
|
if (!prop) {
|
|
77
77
|
continue;
|
|
78
78
|
}
|
|
79
|
-
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
79
|
+
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
80
|
+
Utils.isPlainObject(data[prop.name])) {
|
|
80
81
|
// Skip polymorphic relations - they use PolymorphicRef wrapper
|
|
81
82
|
if (!prop.polymorphic) {
|
|
82
83
|
data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
|
|
@@ -86,12 +87,16 @@ export class UnitOfWork {
|
|
|
86
87
|
for (const p of prop.targetMeta.props) {
|
|
87
88
|
/* v8 ignore next */
|
|
88
89
|
const prefix = prop.prefix === false ? '' : prop.prefix === true ? prop.name + '_' : prop.prefix;
|
|
89
|
-
data[prefix + p.name] = data[prop.name][p.name];
|
|
90
|
+
data[(prefix + p.name)] = data[prop.name][p.name];
|
|
90
91
|
}
|
|
91
92
|
data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
|
|
92
93
|
}
|
|
93
94
|
if (prop.hydrate === false && prop.customType?.ensureComparable(meta, prop)) {
|
|
94
|
-
const converted = prop.customType.convertToJSValue(data[key], this.platform, {
|
|
95
|
+
const converted = prop.customType.convertToJSValue(data[key], this.platform, {
|
|
96
|
+
key,
|
|
97
|
+
mode: 'hydration',
|
|
98
|
+
force: true,
|
|
99
|
+
});
|
|
95
100
|
data[key] = prop.customType.convertToDatabaseValue(converted, this.platform, { key, mode: 'hydration' });
|
|
96
101
|
}
|
|
97
102
|
if (forceUndefined) {
|
|
@@ -423,7 +428,10 @@ export class UnitOfWork {
|
|
|
423
428
|
if (Utils.isCollection(rel)) {
|
|
424
429
|
rel.removeWithoutPropagation(entity);
|
|
425
430
|
}
|
|
426
|
-
else if (rel &&
|
|
431
|
+
else if (rel &&
|
|
432
|
+
(prop.mapToPk
|
|
433
|
+
? helper(this.em.getReference(prop.targetMeta.class, rel)).getSerializedPrimaryKey() === serializedPK
|
|
434
|
+
: rel === entity)) {
|
|
427
435
|
if (prop.formula) {
|
|
428
436
|
delete referrer[prop.name];
|
|
429
437
|
}
|
|
@@ -512,7 +520,13 @@ export class UnitOfWork {
|
|
|
512
520
|
if (!conflicts) {
|
|
513
521
|
return;
|
|
514
522
|
}
|
|
515
|
-
this.extraUpdates.add([
|
|
523
|
+
this.extraUpdates.add([
|
|
524
|
+
changeSet.entity,
|
|
525
|
+
props.map(p => p.name),
|
|
526
|
+
props.map(p => changeSet.entity[p.name]),
|
|
527
|
+
changeSet,
|
|
528
|
+
type,
|
|
529
|
+
]);
|
|
516
530
|
for (const p of props) {
|
|
517
531
|
delete changeSet.entity[p.name];
|
|
518
532
|
delete changeSet.payload[p.name];
|
|
@@ -633,7 +647,7 @@ export class UnitOfWork {
|
|
|
633
647
|
// when changing a unique nullable property (or a 1:1 relation), we can't do it in a single
|
|
634
648
|
// query as it would cause unique constraint violations
|
|
635
649
|
const uniqueProps = changeSet.meta.uniqueProps.filter(prop => {
|
|
636
|
-
return
|
|
650
|
+
return prop.nullable || changeSet.type !== ChangeSetType.CREATE;
|
|
637
651
|
});
|
|
638
652
|
this.scheduleExtraUpdate(changeSet, uniqueProps);
|
|
639
653
|
return changeSet.type === ChangeSetType.UPDATE && !Utils.hasObjectKeys(changeSet.payload);
|
|
@@ -643,25 +657,33 @@ export class UnitOfWork {
|
|
|
643
657
|
if (!wrapped.__meta.hasUniqueProps) {
|
|
644
658
|
return [];
|
|
645
659
|
}
|
|
646
|
-
const simpleUniqueHashes = wrapped.__meta.uniqueProps
|
|
660
|
+
const simpleUniqueHashes = wrapped.__meta.uniqueProps
|
|
661
|
+
.map(prop => {
|
|
647
662
|
if (entity[prop.name] != null) {
|
|
648
|
-
return prop.kind === ReferenceKind.SCALAR || prop.mapToPk
|
|
663
|
+
return prop.kind === ReferenceKind.SCALAR || prop.mapToPk
|
|
664
|
+
? entity[prop.name]
|
|
665
|
+
: helper(entity[prop.name]).getSerializedPrimaryKey();
|
|
649
666
|
}
|
|
650
667
|
if (wrapped.__originalEntityData?.[prop.name] != null) {
|
|
651
668
|
return Utils.getPrimaryKeyHash(Utils.asArray(wrapped.__originalEntityData[prop.name]));
|
|
652
669
|
}
|
|
653
670
|
return undefined;
|
|
654
|
-
})
|
|
655
|
-
|
|
671
|
+
})
|
|
672
|
+
.filter(i => i);
|
|
673
|
+
const compoundUniqueHashes = wrapped.__meta.uniques
|
|
674
|
+
.map(unique => {
|
|
656
675
|
const props = Utils.asArray(unique.properties);
|
|
657
676
|
if (props.every(prop => entity[prop] != null)) {
|
|
658
677
|
return Utils.getPrimaryKeyHash(props.map(p => {
|
|
659
678
|
const prop = wrapped.__meta.properties[p];
|
|
660
|
-
return prop.kind === ReferenceKind.SCALAR || prop.mapToPk
|
|
679
|
+
return prop.kind === ReferenceKind.SCALAR || prop.mapToPk
|
|
680
|
+
? entity[prop.name]
|
|
681
|
+
: helper(entity[prop.name]).getSerializedPrimaryKey();
|
|
661
682
|
}));
|
|
662
683
|
}
|
|
663
684
|
return undefined;
|
|
664
|
-
})
|
|
685
|
+
})
|
|
686
|
+
.filter(i => i);
|
|
665
687
|
return simpleUniqueHashes.concat(compoundUniqueHashes);
|
|
666
688
|
}
|
|
667
689
|
initIdentifier(entity) {
|
|
@@ -693,7 +715,8 @@ export class UnitOfWork {
|
|
|
693
715
|
return this.processToOneReference(kind, visited, processed, idx);
|
|
694
716
|
}
|
|
695
717
|
if (Utils.isCollection(kind)) {
|
|
696
|
-
kind
|
|
718
|
+
kind
|
|
719
|
+
.getItems(false)
|
|
697
720
|
.filter(item => !item.__helper.__originalEntityData)
|
|
698
721
|
.forEach(item => {
|
|
699
722
|
// propagate schema from parent
|
|
@@ -717,7 +740,8 @@ export class UnitOfWork {
|
|
|
717
740
|
parent[prop.name] = coll;
|
|
718
741
|
return;
|
|
719
742
|
}
|
|
720
|
-
collection
|
|
743
|
+
collection
|
|
744
|
+
.getItems(false)
|
|
721
745
|
.filter(item => !item.__helper.__originalEntityData)
|
|
722
746
|
.forEach(item => this.findNewEntities(item, visited, 0, processed));
|
|
723
747
|
}
|
|
@@ -809,7 +833,8 @@ export class UnitOfWork {
|
|
|
809
833
|
return filtered.some(items => processed.has(items));
|
|
810
834
|
}
|
|
811
835
|
shouldCascade(prop, type) {
|
|
812
|
-
if ([Cascade.REMOVE, Cascade.SCHEDULE_ORPHAN_REMOVAL, Cascade.CANCEL_ORPHAN_REMOVAL, Cascade.ALL].includes(type) &&
|
|
836
|
+
if ([Cascade.REMOVE, Cascade.SCHEDULE_ORPHAN_REMOVAL, Cascade.CANCEL_ORPHAN_REMOVAL, Cascade.ALL].includes(type) &&
|
|
837
|
+
prop.orphanRemoval) {
|
|
813
838
|
return true;
|
|
814
839
|
}
|
|
815
840
|
// ignore user settings for merge, it is kept only for back compatibility, this should have never been configurable
|
|
@@ -845,7 +870,9 @@ export class UnitOfWork {
|
|
|
845
870
|
const target = Reference.unwrapReference(reference);
|
|
846
871
|
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && target && !prop.mapToPk) {
|
|
847
872
|
if (!Utils.isEntity(target)) {
|
|
848
|
-
entity[prop.name] = this.em.getReference(prop.targetMeta.class, target, {
|
|
873
|
+
entity[prop.name] = this.em.getReference(prop.targetMeta.class, target, {
|
|
874
|
+
wrapped: !!prop.ref,
|
|
875
|
+
});
|
|
849
876
|
}
|
|
850
877
|
else if (!helper(target).__initialized && !helper(target).__em) {
|
|
851
878
|
const pk = helper(target).getPrimaryKey();
|
|
@@ -907,9 +934,9 @@ export class UnitOfWork {
|
|
|
907
934
|
return;
|
|
908
935
|
}
|
|
909
936
|
const props = changeSets[0].meta.root.relations.filter(prop => {
|
|
910
|
-
return (prop.kind === ReferenceKind.ONE_TO_ONE && prop.owner)
|
|
911
|
-
|
|
912
|
-
|
|
937
|
+
return ((prop.kind === ReferenceKind.ONE_TO_ONE && prop.owner) ||
|
|
938
|
+
prop.kind === ReferenceKind.MANY_TO_ONE ||
|
|
939
|
+
(prop.kind === ReferenceKind.MANY_TO_MANY && prop.owner && !this.platform.usesPivotTable()));
|
|
913
940
|
});
|
|
914
941
|
for (const changeSet of changeSets) {
|
|
915
942
|
this.findExtraUpdates(changeSet, props);
|
|
@@ -984,7 +1011,8 @@ export class UnitOfWork {
|
|
|
984
1011
|
wrapped.__originalEntityData = this.comparator.prepareEntity(changeSet.entity);
|
|
985
1012
|
if (!wrapped.__initialized) {
|
|
986
1013
|
for (const prop of changeSet.meta.relations) {
|
|
987
|
-
if ([ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(prop.kind) &&
|
|
1014
|
+
if ([ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(prop.kind) &&
|
|
1015
|
+
changeSet.entity[prop.name] == null) {
|
|
988
1016
|
changeSet.entity[prop.name] = Collection.create(changeSet.entity, prop.name, undefined, wrapped.isInitialized());
|
|
989
1017
|
}
|
|
990
1018
|
}
|
|
@@ -1013,7 +1041,7 @@ export class UnitOfWork {
|
|
|
1013
1041
|
continue;
|
|
1014
1042
|
}
|
|
1015
1043
|
if (Array.isArray(extraUpdate[1])) {
|
|
1016
|
-
extraUpdate[1].forEach((p, i) => extraUpdate[0][p] = extraUpdate[2][i]);
|
|
1044
|
+
extraUpdate[1].forEach((p, i) => (extraUpdate[0][p] = extraUpdate[2][i]));
|
|
1017
1045
|
}
|
|
1018
1046
|
else {
|
|
1019
1047
|
extraUpdate[0][extraUpdate[1]] = extraUpdate[2];
|
|
@@ -1075,7 +1103,8 @@ export class UnitOfWork {
|
|
|
1075
1103
|
};
|
|
1076
1104
|
const addToGroup = (cs) => {
|
|
1077
1105
|
// Skip stub TPT changesets with empty payload (e.g. leaf with no own-property changes on UPDATE)
|
|
1078
|
-
if ((cs.type === ChangeSetType.UPDATE || cs.type === ChangeSetType.UPDATE_EARLY) &&
|
|
1106
|
+
if ((cs.type === ChangeSetType.UPDATE || cs.type === ChangeSetType.UPDATE_EARLY) &&
|
|
1107
|
+
!Utils.hasObjectKeys(cs.payload)) {
|
|
1079
1108
|
return;
|
|
1080
1109
|
}
|
|
1081
1110
|
const group = groups[cs.type];
|
|
@@ -95,7 +95,7 @@ export declare abstract class AbstractMigrator<D extends IDatabaseDriver> implem
|
|
|
95
95
|
private filterDown;
|
|
96
96
|
private getMigrationFilename;
|
|
97
97
|
private prefix;
|
|
98
|
-
|
|
98
|
+
protected runMigrations(method: 'up' | 'down', options?: string | string[] | MigrateOptions): Promise<MigrationInfo[]>;
|
|
99
99
|
private runInTransaction;
|
|
100
100
|
}
|
|
101
101
|
export {};
|
|
@@ -49,9 +49,7 @@ export class AbstractMigrator {
|
|
|
49
49
|
await this.init();
|
|
50
50
|
const all = await this.discoverMigrations();
|
|
51
51
|
const executed = new Set(await this.storage.executed());
|
|
52
|
-
return all
|
|
53
|
-
.filter(m => !executed.has(m.name))
|
|
54
|
-
.map(m => ({ name: m.name, path: m.path }));
|
|
52
|
+
return all.filter(m => !executed.has(m.name)).map(m => ({ name: m.name, path: m.path }));
|
|
55
53
|
}
|
|
56
54
|
/**
|
|
57
55
|
* @inheritDoc
|
|
@@ -74,7 +72,7 @@ export class AbstractMigrator {
|
|
|
74
72
|
const { fs } = await import('@mikro-orm/core/fs-utils');
|
|
75
73
|
this.detectSourceFolder(fs);
|
|
76
74
|
/* v8 ignore next */
|
|
77
|
-
const key =
|
|
75
|
+
const key = this.config.get('preferTs', Utils.detectTypeScriptSupport()) && this.options.pathTs ? 'pathTs' : 'path';
|
|
78
76
|
this.absolutePath = fs.absolutePath(this.options[key], this.config.get('baseDir'));
|
|
79
77
|
fs.ensureDir(this.absolutePath);
|
|
80
78
|
}
|
|
@@ -132,7 +130,7 @@ export class AbstractMigrator {
|
|
|
132
130
|
const buildDir = fs.pathExists(baseDir + '/build');
|
|
133
131
|
// if neither `dist` nor `build` exist, we use the `src` folder as it might be a JS project without building, but with `src` folder
|
|
134
132
|
/* v8 ignore next */
|
|
135
|
-
const path = distDir ? './dist' :
|
|
133
|
+
const path = distDir ? './dist' : buildDir ? './build' : './src';
|
|
136
134
|
// only if the user did not provide any values and if the default path does not exist
|
|
137
135
|
if (!this.options.path && !this.options.pathTs && !exists) {
|
|
138
136
|
this.options.path = `${path}/migrations`;
|
|
@@ -112,7 +112,8 @@ export class AbstractSchemaGenerator {
|
|
|
112
112
|
}
|
|
113
113
|
meta = metadata.pop();
|
|
114
114
|
}
|
|
115
|
-
return calc
|
|
115
|
+
return calc
|
|
116
|
+
.sort()
|
|
116
117
|
.map(cls => this.metadata.getById(cls))
|
|
117
118
|
.filter(meta => {
|
|
118
119
|
const targetSchema = meta.schema ?? this.config.get('schema', this.platform.getDefaultSchemaName());
|
package/utils/AsyncContext.js
CHANGED
|
@@ -13,7 +13,7 @@ function createFallbackAsyncContext() {
|
|
|
13
13
|
console.warn('AsyncLocalStorage not available');
|
|
14
14
|
return {
|
|
15
15
|
getStore: () => store,
|
|
16
|
-
enterWith: value => store = value,
|
|
16
|
+
enterWith: value => (store = value),
|
|
17
17
|
run: (value, cb) => {
|
|
18
18
|
const prev = store;
|
|
19
19
|
store = value;
|
package/utils/Configuration.js
CHANGED
|
@@ -262,7 +262,10 @@ export class Configuration {
|
|
|
262
262
|
* Gets instance of CacheAdapter for result cache. (cached)
|
|
263
263
|
*/
|
|
264
264
|
getResultCacheAdapter() {
|
|
265
|
-
return this.getCachedService(this.options.resultCache.adapter, {
|
|
265
|
+
return this.getCachedService(this.options.resultCache.adapter, {
|
|
266
|
+
expiration: this.options.resultCache.expiration,
|
|
267
|
+
...this.options.resultCache.options,
|
|
268
|
+
});
|
|
266
269
|
}
|
|
267
270
|
/**
|
|
268
271
|
* Gets EntityRepository class to be instantiated.
|
|
@@ -338,10 +341,10 @@ export class Configuration {
|
|
|
338
341
|
validateOptions() {
|
|
339
342
|
/* v8 ignore next */
|
|
340
343
|
if ('type' in this.options) {
|
|
341
|
-
throw new Error(
|
|
344
|
+
throw new Error("The `type` option has been removed in v6, please fill in the `driver` option instead or use `defineConfig` helper (to define your ORM config) or `MikroORM` class (to call the `init` method) exported from the driver package (e.g. `import { defineConfig } from '@mikro-orm/mysql'; export default defineConfig({ ... })`).");
|
|
342
345
|
}
|
|
343
346
|
if (!this.options.driver) {
|
|
344
|
-
throw new Error(
|
|
347
|
+
throw new Error("No driver specified, please fill in the `driver` option or use `defineConfig` helper (to define your ORM config) or `MikroORM` class (to call the `init` method) exported from the driver package (e.g. `import { defineConfig } from '@mikro-orm/mysql'; export defineConfig({ ... })`).");
|
|
345
348
|
}
|
|
346
349
|
if (!this.options.dbName && !this.options.clientUrl) {
|
|
347
350
|
throw new Error('No database specified, please fill in `dbName` or `clientUrl` option');
|
|
@@ -349,7 +352,8 @@ export class Configuration {
|
|
|
349
352
|
if (this.options.entities.length === 0 && this.options.discovery.warnWhenNoEntities) {
|
|
350
353
|
throw new Error('No entities found, please use `entities` option');
|
|
351
354
|
}
|
|
352
|
-
if (typeof this.options.driverOptions === 'function' &&
|
|
355
|
+
if (typeof this.options.driverOptions === 'function' &&
|
|
356
|
+
this.options.driverOptions.constructor.name === 'AsyncFunction') {
|
|
353
357
|
throw new Error('`driverOptions` callback cannot be async');
|
|
354
358
|
}
|
|
355
359
|
}
|
package/utils/Cursor.js
CHANGED
|
@@ -118,7 +118,7 @@ export class Cursor {
|
|
|
118
118
|
value = value.unwrap();
|
|
119
119
|
}
|
|
120
120
|
if (object) {
|
|
121
|
-
return
|
|
121
|
+
return { [prop]: value };
|
|
122
122
|
}
|
|
123
123
|
return value;
|
|
124
124
|
};
|
|
@@ -167,7 +167,9 @@ export class Cursor {
|
|
|
167
167
|
}
|
|
168
168
|
const prop = meta.properties[key];
|
|
169
169
|
/* v8 ignore next */
|
|
170
|
-
if (!prop ||
|
|
170
|
+
if (!prop ||
|
|
171
|
+
!([ReferenceKind.SCALAR, ReferenceKind.EMBEDDED, ReferenceKind.MANY_TO_ONE].includes(prop.kind) ||
|
|
172
|
+
(prop.kind === ReferenceKind.ONE_TO_ONE && prop.owner))) {
|
|
171
173
|
continue;
|
|
172
174
|
}
|
|
173
175
|
ret.push([prop.name, order[prop.name]]);
|
package/utils/DataloaderUtils.js
CHANGED
|
@@ -101,26 +101,27 @@ export class DataloaderUtils {
|
|
|
101
101
|
const uniqueName = key.substring(0, key.indexOf('|'));
|
|
102
102
|
const opts = JSON.parse(key.substring(key.indexOf('|') + 1));
|
|
103
103
|
const meta = em.getMetadata().getByUniqueName(uniqueName);
|
|
104
|
-
const res = await em.find(meta.class, opts?.where != null && Object.keys(opts.where).length > 0
|
|
105
|
-
{
|
|
104
|
+
const res = await em.find(meta.class, opts?.where != null && Object.keys(opts.where).length > 0
|
|
105
|
+
? {
|
|
106
106
|
$and: [
|
|
107
107
|
{
|
|
108
108
|
$or: Array.from(filterMap.entries()).map(([prop, pks]) => {
|
|
109
|
-
return
|
|
109
|
+
return { [prop]: Array.from(pks) };
|
|
110
110
|
}),
|
|
111
111
|
},
|
|
112
112
|
opts.where,
|
|
113
113
|
],
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
114
|
+
}
|
|
115
|
+
: {
|
|
116
|
+
// The entries of the filter Map will be used as the values of the $or operator
|
|
117
|
+
$or: Array.from(filterMap.entries()).map(([prop, pks]) => {
|
|
118
|
+
return { [prop]: Array.from(pks) };
|
|
119
|
+
}),
|
|
120
|
+
}, {
|
|
120
121
|
...opts,
|
|
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
|
-
...(opts.populate === false ? [] : opts.populate ?? []),
|
|
124
|
+
...(opts.populate === false ? [] : (opts.populate ?? [])),
|
|
124
125
|
...Array.from(filterMap.keys()).filter(
|
|
125
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
|
|
126
127
|
prop => meta.properties[prop]?.ref !== true),
|
|
@@ -207,7 +208,9 @@ export class DataloaderUtils {
|
|
|
207
208
|
options.refresh ??= c[1]?.refresh;
|
|
208
209
|
}
|
|
209
210
|
options.where = wrap({ $or });
|
|
210
|
-
const r = await em
|
|
211
|
+
const r = await em
|
|
212
|
+
.getEntityLoader()
|
|
213
|
+
.findChildrenFromPivotTable(owners, prop, options, orderBy, populate, group[0][1]?.ref);
|
|
211
214
|
ret.push(...r);
|
|
212
215
|
}
|
|
213
216
|
return ret;
|
|
@@ -224,7 +227,7 @@ export class DataloaderUtils {
|
|
|
224
227
|
}
|
|
225
228
|
catch {
|
|
226
229
|
/* v8 ignore next */
|
|
227
|
-
throw new Error(
|
|
230
|
+
throw new Error("DataLoader is not found, make sure `dataloader` package is installed in your project's dependencies.");
|
|
228
231
|
}
|
|
229
232
|
}
|
|
230
233
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { clone } from './clone.js';
|
|
2
2
|
import { ReferenceKind } from '../enums.js';
|
|
3
|
-
import { compareArrays, compareBooleans, compareBuffers, compareObjects, equals, parseJsonSafe, Utils } from './Utils.js';
|
|
3
|
+
import { compareArrays, compareBooleans, compareBuffers, compareObjects, equals, parseJsonSafe, Utils, } from './Utils.js';
|
|
4
4
|
import { JsonType } from '../types/JsonType.js';
|
|
5
5
|
import { Raw } from './RawQueryFragment.js';
|
|
6
6
|
import { EntityIdentifier } from '../entity/EntityIdentifier.js';
|
|
@@ -91,8 +91,7 @@ export class EntityComparator {
|
|
|
91
91
|
}
|
|
92
92
|
lines.push(` return entity${this.wrap(pk)};`);
|
|
93
93
|
}
|
|
94
|
-
const code = `// compiled pk getter for entity ${meta.className}\n`
|
|
95
|
-
+ `return function(entity) {\n${lines.join('\n')}\n}`;
|
|
94
|
+
const code = `// compiled pk getter for entity ${meta.className}\n` + `return function(entity) {\n${lines.join('\n')}\n}`;
|
|
96
95
|
const fnKey = `pkGetter-${meta.uniqueName}`;
|
|
97
96
|
const pkSerializer = Utils.createFunction(context, code, this.config?.get('compiledFunctions'), fnKey);
|
|
98
97
|
this.pkGetters.set(meta, pkSerializer);
|
|
@@ -142,8 +141,8 @@ export class EntityComparator {
|
|
|
142
141
|
lines.push(` return entity${this.wrap(pk)};`);
|
|
143
142
|
}
|
|
144
143
|
}
|
|
145
|
-
const code = `// compiled pk getter (with converted custom types) for entity ${meta.className}\n`
|
|
146
|
-
|
|
144
|
+
const code = `// compiled pk getter (with converted custom types) for entity ${meta.className}\n` +
|
|
145
|
+
`return function(entity) {\n${lines.join('\n')}\n}`;
|
|
147
146
|
const fnKey = `pkGetterConverted-${meta.uniqueName}`;
|
|
148
147
|
const pkSerializer = Utils.createFunction(context, code, this.config?.get('compiledFunctions'), fnKey);
|
|
149
148
|
this.pkGettersConverted.set(meta, pkSerializer);
|
|
@@ -195,8 +194,7 @@ export class EntityComparator {
|
|
|
195
194
|
lines.push(` return '' + entity${this.wrap(pk)};`);
|
|
196
195
|
}
|
|
197
196
|
}
|
|
198
|
-
const code = `// compiled pk serializer for entity ${meta.className}\n`
|
|
199
|
-
+ `return function(entity) {\n${lines.join('\n')}\n}`;
|
|
197
|
+
const code = `// compiled pk serializer for entity ${meta.className}\n` + `return function(entity) {\n${lines.join('\n')}\n}`;
|
|
200
198
|
const fnKey = `pkSerializer-${meta.uniqueName}`;
|
|
201
199
|
const pkSerializer = Utils.createFunction(context, code, this.config?.get('compiledFunctions'), fnKey);
|
|
202
200
|
this.pkSerializers.set(meta, pkSerializer);
|
|
@@ -359,7 +357,7 @@ export class EntityComparator {
|
|
|
359
357
|
context.set(`mapEmbeddedResult_${idx}`, (data) => {
|
|
360
358
|
const item = parseJsonSafe(data);
|
|
361
359
|
if (Array.isArray(item)) {
|
|
362
|
-
return item.map(row => row == null ? row : this.getResultMapper(prop.targetMeta)(row));
|
|
360
|
+
return item.map(row => (row == null ? row : this.getResultMapper(prop.targetMeta)(row)));
|
|
363
361
|
}
|
|
364
362
|
return item == null ? item : this.getResultMapper(prop.targetMeta)(item);
|
|
365
363
|
});
|
|
@@ -391,8 +389,8 @@ export class EntityComparator {
|
|
|
391
389
|
mapEntityProperties(meta);
|
|
392
390
|
}
|
|
393
391
|
lines.push(` for (let k in result) { if (Object.hasOwn(result, k) && !mapped[k] && ret[k] === undefined) ret[k] = result[k]; }`);
|
|
394
|
-
const code = `// compiled mapper for entity ${meta.className}\n`
|
|
395
|
-
|
|
392
|
+
const code = `// compiled mapper for entity ${meta.className}\n` +
|
|
393
|
+
`return function(result) {\n const ret = {};\n${lines.join('\n')}\n return ret;\n}`;
|
|
396
394
|
const fnKey = `resultMapper-${meta.uniqueName}`;
|
|
397
395
|
const resultMapper = Utils.createFunction(context, code, this.config?.get('compiledFunctions'), fnKey);
|
|
398
396
|
this.mappers.set(meta, resultMapper);
|
|
@@ -411,7 +409,7 @@ export class EntityComparator {
|
|
|
411
409
|
return '';
|
|
412
410
|
}
|
|
413
411
|
const mapped = `typeof entity${tail ? '.' + tail : ''}${this.wrap(k)} !== 'undefined'`;
|
|
414
|
-
tail += tail ?
|
|
412
|
+
tail += tail ? '.' + k : k;
|
|
415
413
|
return mapped;
|
|
416
414
|
})
|
|
417
415
|
.filter(k => k)
|
|
@@ -453,12 +451,16 @@ export class EntityComparator {
|
|
|
453
451
|
}
|
|
454
452
|
else {
|
|
455
453
|
ret += `${padding}if (${nullCond}) {\n`;
|
|
456
|
-
ret +=
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
454
|
+
ret +=
|
|
455
|
+
meta.props
|
|
456
|
+
.filter(p => p.embedded?.[0] === prop.name &&
|
|
457
|
+
// object for JSON embeddable
|
|
458
|
+
(p.object || p.persist !== false))
|
|
459
|
+
.map(childProp => {
|
|
460
|
+
const childDataKey = meta.embeddable || prop.object ? dataKey + this.wrap(childProp.embedded[1]) : this.wrap(childProp.name);
|
|
461
|
+
return `${padding} ret${childDataKey} = null;`;
|
|
462
|
+
})
|
|
463
|
+
.join('\n') + `\n`;
|
|
462
464
|
ret += `${padding}}\n`;
|
|
463
465
|
}
|
|
464
466
|
const cond = `entity${path.map(k => this.wrap(k)).join('')} != null`;
|
|
@@ -475,28 +477,34 @@ export class EntityComparator {
|
|
|
475
477
|
}
|
|
476
478
|
return true;
|
|
477
479
|
}
|
|
478
|
-
ret +=
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
.split('\n').map(l => padding + l).join('\n');
|
|
490
|
-
}
|
|
491
|
-
if (shouldProcessCustomType(childProp)) {
|
|
492
|
-
const convertorKey = this.registerCustomType(childProp, context);
|
|
493
|
-
if (['number', 'string', 'boolean', 'bigint'].includes(childProp.customType.compareAsType().toLowerCase())) {
|
|
494
|
-
return `${padding} if (${childCond}) ret${childDataKey} = convertToDatabaseValue_${convertorKey}(entity${childEntityKey});`;
|
|
480
|
+
ret +=
|
|
481
|
+
meta.props
|
|
482
|
+
.filter(p => p.embedded?.[0] === prop.name &&
|
|
483
|
+
// object for JSON embeddable
|
|
484
|
+
(p.object || p.persist !== false))
|
|
485
|
+
.map(childProp => {
|
|
486
|
+
const childDataKey = meta.embeddable || prop.object ? dataKey + this.wrap(childProp.embedded[1]) : this.wrap(childProp.name);
|
|
487
|
+
const childEntityKey = [...path, childProp.embedded[1]].map(k => this.wrap(k)).join('');
|
|
488
|
+
const childCond = `typeof entity${childEntityKey} !== 'undefined'`;
|
|
489
|
+
if (childProp.kind === ReferenceKind.EMBEDDED) {
|
|
490
|
+
return this.getPropertySnapshot(meta, childProp, context, childDataKey, childEntityKey, [...path, childProp.embedded[1]], level + 1, prop.object);
|
|
495
491
|
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
492
|
+
if (childProp.kind !== ReferenceKind.SCALAR) {
|
|
493
|
+
return this.getPropertySnapshot(meta, childProp, context, childDataKey, childEntityKey, [...path, childProp.embedded[1]], level, prop.object)
|
|
494
|
+
.split('\n')
|
|
495
|
+
.map(l => padding + l)
|
|
496
|
+
.join('\n');
|
|
497
|
+
}
|
|
498
|
+
if (shouldProcessCustomType(childProp)) {
|
|
499
|
+
const convertorKey = this.registerCustomType(childProp, context);
|
|
500
|
+
if (['number', 'string', 'boolean', 'bigint'].includes(childProp.customType.compareAsType().toLowerCase())) {
|
|
501
|
+
return `${padding} if (${childCond}) ret${childDataKey} = convertToDatabaseValue_${convertorKey}(entity${childEntityKey});`;
|
|
502
|
+
}
|
|
503
|
+
return `${padding} if (${childCond}) ret${childDataKey} = clone(convertToDatabaseValue_${convertorKey}(entity${childEntityKey}));`;
|
|
504
|
+
}
|
|
505
|
+
return `${padding} if (${childCond}) ret${childDataKey} = clone(entity${childEntityKey});`;
|
|
506
|
+
})
|
|
507
|
+
.join('\n') + `\n`;
|
|
500
508
|
if (this.shouldSerialize(prop, dataKey)) {
|
|
501
509
|
return `${ret + padding} ret${dataKey} = cloneEmbeddable(ret${dataKey});\n${padding}}`;
|
|
502
510
|
}
|
|
@@ -591,7 +599,7 @@ export class EntityComparator {
|
|
|
591
599
|
if (['number', 'string', 'boolean', 'bigint'].includes(prop.customType.compareAsType().toLowerCase())) {
|
|
592
600
|
return ret + ` ret${dataKey} = convertToDatabaseValue_${convertorKey}(entity${entityKey}${unwrap});\n }\n`;
|
|
593
601
|
}
|
|
594
|
-
return ret + ` ret${dataKey} = clone(convertToDatabaseValue_${convertorKey}(entity${entityKey}${unwrap}));\n }\n
|
|
602
|
+
return (ret + ` ret${dataKey} = clone(convertToDatabaseValue_${convertorKey}(entity${entityKey}${unwrap}));\n }\n`);
|
|
595
603
|
}
|
|
596
604
|
if (prop.runtimeType === 'Date') {
|
|
597
605
|
context.set('processDateProperty', this.platform.processDateProperty.bind(this.platform));
|
|
@@ -626,22 +634,22 @@ export class EntityComparator {
|
|
|
626
634
|
}
|
|
627
635
|
}
|
|
628
636
|
lines.push(`}`);
|
|
629
|
-
const code = `// compiled comparator for entity ${meta.className}\n`
|
|
630
|
-
|
|
637
|
+
const code = `// compiled comparator for entity ${meta.className}\n` +
|
|
638
|
+
`return function(last, current, options) {\n const diff = {};\n${lines.join('\n')}\n return diff;\n}`;
|
|
631
639
|
const fnKey = `comparator-${meta.uniqueName}`;
|
|
632
640
|
const comparator = Utils.createFunction(context, code, this.config?.get('compiledFunctions'), fnKey);
|
|
633
641
|
this.comparators.set(meta, comparator);
|
|
634
642
|
return comparator;
|
|
635
643
|
}
|
|
636
644
|
getGenericComparator(prop, cond) {
|
|
637
|
-
return ` if (current${prop} === null && last${prop} === undefined) {\n` +
|
|
645
|
+
return (` if (current${prop} === null && last${prop} === undefined) {\n` +
|
|
638
646
|
` diff${prop} = current${prop};\n` +
|
|
639
647
|
` } else if (current${prop} == null && last${prop} == null) {\n\n` +
|
|
640
648
|
` } else if ((current${prop} != null && last${prop} == null) || (current${prop} == null && last${prop} != null)) {\n` +
|
|
641
649
|
` diff${prop} = current${prop};\n` +
|
|
642
650
|
` } else if (${cond}) {\n` +
|
|
643
651
|
` diff${prop} = current${prop};\n` +
|
|
644
|
-
` }\n
|
|
652
|
+
` }\n`);
|
|
645
653
|
}
|
|
646
654
|
getPropertyComparator(prop, context) {
|
|
647
655
|
let type = prop.type.toLowerCase();
|