@mikro-orm/core 7.0.0-dev.114 → 7.0.0-dev.115
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 +8 -8
- package/EntityManager.js +40 -60
- package/MikroORM.d.ts +1 -1
- package/MikroORM.js +2 -3
- package/drivers/DatabaseDriver.d.ts +11 -11
- package/drivers/DatabaseDriver.js +7 -8
- package/drivers/IDatabaseDriver.d.ts +10 -10
- package/entity/Collection.js +5 -5
- package/entity/EntityAssigner.js +9 -9
- package/entity/EntityFactory.js +14 -17
- package/entity/EntityHelper.d.ts +2 -2
- package/entity/EntityHelper.js +2 -2
- package/entity/EntityLoader.d.ts +3 -3
- package/entity/EntityLoader.js +17 -16
- package/entity/WrappedEntity.js +1 -1
- package/entity/defineEntity.d.ts +11 -11
- package/errors.d.ts +8 -8
- package/errors.js +14 -13
- package/hydration/ObjectHydrator.js +23 -16
- package/metadata/EntitySchema.d.ts +5 -5
- package/metadata/EntitySchema.js +23 -21
- package/metadata/MetadataDiscovery.d.ts +2 -3
- package/metadata/MetadataDiscovery.js +117 -90
- package/metadata/MetadataProvider.js +2 -0
- package/metadata/MetadataStorage.d.ts +13 -6
- package/metadata/MetadataStorage.js +64 -19
- package/metadata/MetadataValidator.d.ts +2 -2
- package/metadata/MetadataValidator.js +22 -28
- package/metadata/types.d.ts +3 -3
- package/package.json +1 -1
- package/serialization/EntitySerializer.js +2 -2
- package/serialization/EntityTransformer.js +6 -6
- package/serialization/SerializationContext.d.ts +6 -6
- package/typings.d.ts +16 -14
- package/typings.js +15 -10
- package/unit-of-work/ChangeSet.d.ts +2 -3
- package/unit-of-work/ChangeSet.js +2 -3
- package/unit-of-work/ChangeSetComputer.js +3 -3
- package/unit-of-work/ChangeSetPersister.js +14 -14
- package/unit-of-work/CommitOrderCalculator.d.ts +12 -10
- package/unit-of-work/CommitOrderCalculator.js +13 -13
- package/unit-of-work/UnitOfWork.d.ts +3 -3
- package/unit-of-work/UnitOfWork.js +46 -45
- package/utils/AbstractSchemaGenerator.js +7 -7
- package/utils/Configuration.d.ts +0 -5
- package/utils/DataloaderUtils.js +13 -11
- package/utils/EntityComparator.d.ts +6 -6
- package/utils/EntityComparator.js +22 -24
- package/utils/QueryHelper.d.ts +5 -5
- package/utils/QueryHelper.js +7 -7
- package/utils/TransactionManager.js +1 -1
- package/utils/Utils.d.ts +1 -1
- package/utils/Utils.js +1 -2
- package/utils/env-vars.js +0 -1
|
@@ -30,7 +30,7 @@ export class ChangeSetPersister {
|
|
|
30
30
|
if (!withSchema) {
|
|
31
31
|
return this.runForEachSchema(changeSets, 'executeInserts', options);
|
|
32
32
|
}
|
|
33
|
-
const meta =
|
|
33
|
+
const meta = changeSets[0].meta;
|
|
34
34
|
changeSets.forEach(changeSet => this.processProperties(changeSet));
|
|
35
35
|
if (changeSets.length > 1 && this.config.get('useBatchInserts', this.platform.usesBatchInserts())) {
|
|
36
36
|
return this.persistNewEntities(meta, changeSets, options);
|
|
@@ -43,7 +43,7 @@ export class ChangeSetPersister {
|
|
|
43
43
|
if (!withSchema) {
|
|
44
44
|
return this.runForEachSchema(changeSets, 'executeUpdates', options, batched);
|
|
45
45
|
}
|
|
46
|
-
const meta =
|
|
46
|
+
const meta = changeSets[0].meta;
|
|
47
47
|
changeSets.forEach(changeSet => this.processProperties(changeSet));
|
|
48
48
|
if (batched && changeSets.length > 1 && this.config.get('useBatchUpdates', this.platform.usesBatchUpdates())) {
|
|
49
49
|
return this.persistManagedEntities(meta, changeSets, options);
|
|
@@ -63,7 +63,7 @@ export class ChangeSetPersister {
|
|
|
63
63
|
const chunk = changeSets.slice(i, i + size);
|
|
64
64
|
const pks = chunk.map(cs => cs.getPrimaryKey());
|
|
65
65
|
options = this.prepareOptions(meta, options);
|
|
66
|
-
await this.driver.nativeDelete(meta.root.
|
|
66
|
+
await this.driver.nativeDelete(meta.root.class, { [pk]: { $in: pks } }, options);
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
async runForEachSchema(changeSets, method, options, ...args) {
|
|
@@ -99,7 +99,7 @@ export class ChangeSetPersister {
|
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
processProperties(changeSet) {
|
|
102
|
-
const meta =
|
|
102
|
+
const meta = changeSet.meta;
|
|
103
103
|
for (const prop of meta.relations) {
|
|
104
104
|
this.processProperty(changeSet, prop);
|
|
105
105
|
}
|
|
@@ -112,7 +112,7 @@ export class ChangeSetPersister {
|
|
|
112
112
|
options = this.prepareOptions(meta, options, {
|
|
113
113
|
convertCustomTypes: false,
|
|
114
114
|
});
|
|
115
|
-
const res = await this.driver.nativeInsertMany(meta.
|
|
115
|
+
const res = await this.driver.nativeInsertMany(meta.class, [changeSet.payload], options);
|
|
116
116
|
if (!wrapped.hasPrimaryKey()) {
|
|
117
117
|
this.mapPrimaryKey(meta, res.insertId, changeSet);
|
|
118
118
|
}
|
|
@@ -149,7 +149,7 @@ export class ChangeSetPersister {
|
|
|
149
149
|
convertCustomTypes: false,
|
|
150
150
|
processCollections: false,
|
|
151
151
|
});
|
|
152
|
-
const res = await this.driver.nativeInsertMany(meta.
|
|
152
|
+
const res = await this.driver.nativeInsertMany(meta.class, changeSets.map(cs => cs.payload), options);
|
|
153
153
|
for (let i = 0; i < changeSets.length; i++) {
|
|
154
154
|
const changeSet = changeSets[i];
|
|
155
155
|
const wrapped = helper(changeSet.entity);
|
|
@@ -167,7 +167,7 @@ export class ChangeSetPersister {
|
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
169
|
async persistManagedEntity(changeSet, options) {
|
|
170
|
-
const meta =
|
|
170
|
+
const meta = changeSet.meta;
|
|
171
171
|
const res = await this.updateEntity(meta, changeSet, options);
|
|
172
172
|
this.checkOptimisticLock(meta, changeSet, res);
|
|
173
173
|
this.mapReturnedValues(changeSet.entity, changeSet.payload, res.row, meta);
|
|
@@ -208,7 +208,7 @@ export class ChangeSetPersister {
|
|
|
208
208
|
cond.push(where);
|
|
209
209
|
payload.push(changeSet.payload);
|
|
210
210
|
}
|
|
211
|
-
const res = await this.driver.nativeUpdateMany(meta.
|
|
211
|
+
const res = await this.driver.nativeUpdateMany(meta.class, cond, payload, options);
|
|
212
212
|
const map = new Map();
|
|
213
213
|
res.rows?.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, true, this.platform, true), item));
|
|
214
214
|
for (const changeSet of changeSets) {
|
|
@@ -260,13 +260,13 @@ export class ChangeSetPersister {
|
|
|
260
260
|
convertCustomTypes: false,
|
|
261
261
|
});
|
|
262
262
|
if (meta.concurrencyCheckKeys.size === 0 && (!meta.versionProperty || changeSet.entity[meta.versionProperty] == null)) {
|
|
263
|
-
return this.driver.nativeUpdate(changeSet.
|
|
263
|
+
return this.driver.nativeUpdate(changeSet.meta.class, cond, changeSet.payload, options);
|
|
264
264
|
}
|
|
265
265
|
if (meta.versionProperty) {
|
|
266
266
|
cond[meta.versionProperty] = this.platform.quoteVersionValue(changeSet.entity[meta.versionProperty], meta.properties[meta.versionProperty]);
|
|
267
267
|
}
|
|
268
268
|
this.checkConcurrencyKeys(meta, changeSet, cond);
|
|
269
|
-
return this.driver.nativeUpdate(changeSet.
|
|
269
|
+
return this.driver.nativeUpdate(changeSet.meta.class, cond, changeSet.payload, options);
|
|
270
270
|
}
|
|
271
271
|
async checkOptimisticLocks(meta, changeSets, options) {
|
|
272
272
|
if (meta.concurrencyCheckKeys.size === 0 && (!meta.versionProperty || changeSets.every(cs => cs.entity[meta.versionProperty] == null))) {
|
|
@@ -286,7 +286,7 @@ export class ChangeSetPersister {
|
|
|
286
286
|
options = this.prepareOptions(meta, options, {
|
|
287
287
|
fields: primaryKeys,
|
|
288
288
|
});
|
|
289
|
-
const res = await this.driver.find(meta.root.
|
|
289
|
+
const res = await this.driver.find(meta.root.class, { $or }, options);
|
|
290
290
|
if (res.length !== changeSets.length) {
|
|
291
291
|
const compare = (a, b, keys) => keys.every(k => a[k] === b[k]);
|
|
292
292
|
const entity = changeSets.find(cs => {
|
|
@@ -356,7 +356,7 @@ export class ChangeSetPersister {
|
|
|
356
356
|
options = this.prepareOptions(meta, options, {
|
|
357
357
|
fields: Utils.unique(reloadProps.map(prop => prop.name)),
|
|
358
358
|
});
|
|
359
|
-
const data = await this.driver.find(meta.
|
|
359
|
+
const data = await this.driver.find(meta.class, { [pk]: { $in: pks } }, options);
|
|
360
360
|
const map = new Map();
|
|
361
361
|
data.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, false, this.platform, true), item));
|
|
362
362
|
for (const changeSet of changeSets) {
|
|
@@ -366,7 +366,7 @@ export class ChangeSetPersister {
|
|
|
366
366
|
}
|
|
367
367
|
}
|
|
368
368
|
processProperty(changeSet, prop) {
|
|
369
|
-
const meta =
|
|
369
|
+
const meta = changeSet.meta;
|
|
370
370
|
const value = changeSet.payload[prop.name]; // for inline embeddables
|
|
371
371
|
if (value instanceof EntityIdentifier) {
|
|
372
372
|
changeSet.payload[prop.name] = value.getValue();
|
|
@@ -399,7 +399,7 @@ export class ChangeSetPersister {
|
|
|
399
399
|
if ((!this.usesReturningStatement && !upsert) || !row || !Utils.hasObjectKeys(row)) {
|
|
400
400
|
return;
|
|
401
401
|
}
|
|
402
|
-
const mapped = this.comparator.mapResult(meta
|
|
402
|
+
const mapped = this.comparator.mapResult(meta, row);
|
|
403
403
|
if (entity) {
|
|
404
404
|
this.hydrator.hydrate(entity, meta, mapped, this.factory, 'full', false, true);
|
|
405
405
|
}
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { EntityProperty } from '../typings.js';
|
|
2
2
|
export declare const enum NodeState {
|
|
3
3
|
NOT_VISITED = 0,
|
|
4
4
|
IN_PROGRESS = 1,
|
|
5
5
|
VISITED = 2
|
|
6
6
|
}
|
|
7
|
+
type Hash = number;
|
|
7
8
|
export interface Node {
|
|
8
|
-
hash:
|
|
9
|
+
hash: Hash;
|
|
9
10
|
state: NodeState;
|
|
10
|
-
dependencies:
|
|
11
|
+
dependencies: Map<Hash, Edge>;
|
|
11
12
|
}
|
|
12
13
|
export interface Edge {
|
|
13
|
-
from:
|
|
14
|
-
to:
|
|
14
|
+
from: Hash;
|
|
15
|
+
to: Hash;
|
|
15
16
|
weight: number;
|
|
16
17
|
}
|
|
17
18
|
/**
|
|
@@ -32,23 +33,23 @@ export declare class CommitOrderCalculator {
|
|
|
32
33
|
/**
|
|
33
34
|
* Checks for node existence in graph.
|
|
34
35
|
*/
|
|
35
|
-
hasNode(hash:
|
|
36
|
+
hasNode(hash: Hash): boolean;
|
|
36
37
|
/**
|
|
37
38
|
* Adds a new node to the graph, assigning its hash.
|
|
38
39
|
*/
|
|
39
|
-
addNode(hash:
|
|
40
|
+
addNode(hash: Hash): void;
|
|
40
41
|
/**
|
|
41
42
|
* Adds a new dependency (edge) to the graph using their hashes.
|
|
42
43
|
*/
|
|
43
|
-
addDependency(from:
|
|
44
|
-
discoverProperty(prop: EntityProperty, entityName:
|
|
44
|
+
addDependency(from: Hash, to: Hash, weight: number): void;
|
|
45
|
+
discoverProperty(prop: EntityProperty, entityName: Hash): void;
|
|
45
46
|
/**
|
|
46
47
|
* Return a valid order list of all current nodes.
|
|
47
48
|
* The desired topological sorting is the reverse post order of these searches.
|
|
48
49
|
*
|
|
49
50
|
* @internal Highly performance-sensitive method.
|
|
50
51
|
*/
|
|
51
|
-
sort():
|
|
52
|
+
sort(): Hash[];
|
|
52
53
|
/**
|
|
53
54
|
* Visit a given node definition for reordering.
|
|
54
55
|
*
|
|
@@ -60,3 +61,4 @@ export declare class CommitOrderCalculator {
|
|
|
60
61
|
*/
|
|
61
62
|
private visitOpenNode;
|
|
62
63
|
}
|
|
64
|
+
export {};
|
|
@@ -17,26 +17,26 @@ export var NodeState;
|
|
|
17
17
|
*/
|
|
18
18
|
export class CommitOrderCalculator {
|
|
19
19
|
/** Matrix of nodes, keys are provided hashes and values are the node definition objects. */
|
|
20
|
-
nodes =
|
|
20
|
+
nodes = new Map();
|
|
21
21
|
/** Volatile variable holding calculated nodes during sorting process. */
|
|
22
22
|
sortedNodeList = [];
|
|
23
23
|
/**
|
|
24
24
|
* Checks for node existence in graph.
|
|
25
25
|
*/
|
|
26
26
|
hasNode(hash) {
|
|
27
|
-
return
|
|
27
|
+
return this.nodes.has(hash);
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
30
|
* Adds a new node to the graph, assigning its hash.
|
|
31
31
|
*/
|
|
32
32
|
addNode(hash) {
|
|
33
|
-
this.nodes
|
|
33
|
+
this.nodes.set(hash, { hash, state: 0 /* NodeState.NOT_VISITED */, dependencies: new Map() });
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
36
|
* Adds a new dependency (edge) to the graph using their hashes.
|
|
37
37
|
*/
|
|
38
38
|
addDependency(from, to, weight) {
|
|
39
|
-
this.nodes
|
|
39
|
+
this.nodes.get(from).dependencies.set(to, { from, to, weight });
|
|
40
40
|
}
|
|
41
41
|
discoverProperty(prop, entityName) {
|
|
42
42
|
const toOneOwner = (prop.kind === ReferenceKind.ONE_TO_ONE && prop.owner) || prop.kind === ReferenceKind.MANY_TO_ONE;
|
|
@@ -44,8 +44,8 @@ export class CommitOrderCalculator {
|
|
|
44
44
|
if (!toOneOwner && !toManyOwner) {
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
|
-
const propertyType = prop.targetMeta?.root.
|
|
48
|
-
if (
|
|
47
|
+
const propertyType = prop.targetMeta?.root._id;
|
|
48
|
+
if (propertyType == null || !this.hasNode(propertyType)) {
|
|
49
49
|
return;
|
|
50
50
|
}
|
|
51
51
|
this.addDependency(propertyType, entityName, prop.nullable || prop.persist === false ? 0 : 1);
|
|
@@ -57,14 +57,14 @@ export class CommitOrderCalculator {
|
|
|
57
57
|
* @internal Highly performance-sensitive method.
|
|
58
58
|
*/
|
|
59
59
|
sort() {
|
|
60
|
-
for (const vertex of
|
|
60
|
+
for (const vertex of this.nodes.values()) {
|
|
61
61
|
if (vertex.state !== 0 /* NodeState.NOT_VISITED */) {
|
|
62
62
|
continue;
|
|
63
63
|
}
|
|
64
64
|
this.visit(vertex);
|
|
65
65
|
}
|
|
66
66
|
const sortedList = this.sortedNodeList.reverse();
|
|
67
|
-
this.nodes =
|
|
67
|
+
this.nodes = new Map();
|
|
68
68
|
this.sortedNodeList = [];
|
|
69
69
|
return sortedList;
|
|
70
70
|
}
|
|
@@ -75,8 +75,8 @@ export class CommitOrderCalculator {
|
|
|
75
75
|
*/
|
|
76
76
|
visit(node) {
|
|
77
77
|
node.state = 1 /* NodeState.IN_PROGRESS */;
|
|
78
|
-
for (const edge of
|
|
79
|
-
const target = this.nodes
|
|
78
|
+
for (const edge of node.dependencies.values()) {
|
|
79
|
+
const target = this.nodes.get(edge.to);
|
|
80
80
|
switch (target.state) {
|
|
81
81
|
case 2 /* NodeState.VISITED */: break; // Do nothing, since node was already visited
|
|
82
82
|
case 1 /* NodeState.IN_PROGRESS */:
|
|
@@ -94,11 +94,11 @@ export class CommitOrderCalculator {
|
|
|
94
94
|
* Visits all target's dependencies if in cycle with given node
|
|
95
95
|
*/
|
|
96
96
|
visitOpenNode(node, target, edge) {
|
|
97
|
-
if (!target.dependencies
|
|
97
|
+
if (!target.dependencies.has(node.hash) || target.dependencies.get(node.hash).weight >= edge.weight) {
|
|
98
98
|
return;
|
|
99
99
|
}
|
|
100
|
-
for (const edge of
|
|
101
|
-
const targetNode = this.nodes
|
|
100
|
+
for (const edge of target.dependencies.values()) {
|
|
101
|
+
const targetNode = this.nodes.get(edge.to);
|
|
102
102
|
if (targetNode.state === 0 /* NodeState.NOT_VISITED */) {
|
|
103
103
|
this.visit(targetNode);
|
|
104
104
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AnyEntity, EntityData, EntityMetadata, EntityProperty, FilterQuery, Primary } from '../typings.js';
|
|
1
|
+
import type { AnyEntity, EntityData, EntityMetadata, EntityName, EntityProperty, FilterQuery, Primary } from '../typings.js';
|
|
2
2
|
import { Collection } from '../entity/Collection.js';
|
|
3
3
|
import { Reference } from '../entity/Reference.js';
|
|
4
4
|
import { ChangeSet, ChangeSetType } from './ChangeSet.js';
|
|
@@ -45,8 +45,8 @@ export declare class UnitOfWork {
|
|
|
45
45
|
/**
|
|
46
46
|
* Returns entity from the identity map. For composite keys, you need to pass an array of PKs in the same order as they are defined in `meta.primaryKeys`.
|
|
47
47
|
*/
|
|
48
|
-
getById<T extends object>(entityName:
|
|
49
|
-
tryGetById<T extends object>(entityName:
|
|
48
|
+
getById<T extends object>(entityName: EntityName<T>, id: Primary<T> | Primary<T>[], schema?: string, convertCustomTypes?: boolean): T | undefined;
|
|
49
|
+
tryGetById<T extends object>(entityName: EntityName<T>, where: FilterQuery<T>, schema?: string, strict?: boolean): T | null;
|
|
50
50
|
/**
|
|
51
51
|
* Returns map of all managed entities.
|
|
52
52
|
*/
|
|
@@ -207,7 +207,7 @@ export class UnitOfWork {
|
|
|
207
207
|
if (insideFlush.getStore()) {
|
|
208
208
|
return false;
|
|
209
209
|
}
|
|
210
|
-
if (this.queuedActions.has(meta.
|
|
210
|
+
if (this.queuedActions.has(meta.class) || this.queuedActions.has(meta.root.class)) {
|
|
211
211
|
return true;
|
|
212
212
|
}
|
|
213
213
|
if (meta.discriminatorMap && Object.values(meta.discriminatorMap).some(v => this.queuedActions.has(v))) {
|
|
@@ -251,7 +251,7 @@ export class UnitOfWork {
|
|
|
251
251
|
}
|
|
252
252
|
const wrapped = helper(entity);
|
|
253
253
|
this.persistStack.add(entity);
|
|
254
|
-
this.queuedActions.add(wrapped.__meta.
|
|
254
|
+
this.queuedActions.add(wrapped.__meta.class);
|
|
255
255
|
this.removeStack.delete(entity);
|
|
256
256
|
if (!wrapped.__managed && wrapped.hasPrimaryKey()) {
|
|
257
257
|
this.identityMap.store(entity);
|
|
@@ -264,7 +264,7 @@ export class UnitOfWork {
|
|
|
264
264
|
// allow removing not managed entities if they are not part of the persist stack
|
|
265
265
|
if (helper(entity).__managed || !this.persistStack.has(entity)) {
|
|
266
266
|
this.removeStack.add(entity);
|
|
267
|
-
this.queuedActions.add(helper(entity).__meta.
|
|
267
|
+
this.queuedActions.add(helper(entity).__meta.class);
|
|
268
268
|
}
|
|
269
269
|
else {
|
|
270
270
|
this.persistStack.delete(entity);
|
|
@@ -355,10 +355,10 @@ export class UnitOfWork {
|
|
|
355
355
|
}
|
|
356
356
|
}
|
|
357
357
|
async lock(entity, options) {
|
|
358
|
-
if (!this.getById(entity.constructor
|
|
358
|
+
if (!this.getById(entity.constructor, helper(entity).__primaryKeys, helper(entity).__schema)) {
|
|
359
359
|
throw ValidationError.entityNotManaged(entity);
|
|
360
360
|
}
|
|
361
|
-
const meta = this.metadata.find(entity.constructor
|
|
361
|
+
const meta = this.metadata.find(entity.constructor);
|
|
362
362
|
if (options.lockMode === LockMode.OPTIMISTIC) {
|
|
363
363
|
await this.lockOptimistic(entity, meta, options.lockVersion);
|
|
364
364
|
}
|
|
@@ -382,7 +382,7 @@ export class UnitOfWork {
|
|
|
382
382
|
if (Utils.isCollection(rel)) {
|
|
383
383
|
rel.removeWithoutPropagation(entity);
|
|
384
384
|
}
|
|
385
|
-
else if (rel && (prop.mapToPk ? helper(this.em.getReference(prop.
|
|
385
|
+
else if (rel && (prop.mapToPk ? helper(this.em.getReference(prop.targetMeta.class, rel)).getSerializedPrimaryKey() === serializedPK : rel === entity)) {
|
|
386
386
|
if (prop.formula) {
|
|
387
387
|
delete referrer[prop.name];
|
|
388
388
|
}
|
|
@@ -424,13 +424,13 @@ export class UnitOfWork {
|
|
|
424
424
|
const inserts = {};
|
|
425
425
|
for (const cs of this.changeSets.values()) {
|
|
426
426
|
if (cs.type === ChangeSetType.CREATE) {
|
|
427
|
-
inserts[cs.meta.
|
|
428
|
-
inserts[cs.meta.
|
|
427
|
+
inserts[cs.meta.uniqueName] ??= [];
|
|
428
|
+
inserts[cs.meta.uniqueName].push(cs);
|
|
429
429
|
}
|
|
430
430
|
}
|
|
431
431
|
for (const cs of this.changeSets.values()) {
|
|
432
432
|
if (cs.type === ChangeSetType.UPDATE) {
|
|
433
|
-
this.findEarlyUpdates(cs, inserts[cs.meta.
|
|
433
|
+
this.findEarlyUpdates(cs, inserts[cs.meta.uniqueName]);
|
|
434
434
|
}
|
|
435
435
|
}
|
|
436
436
|
for (const entity of this.removeStack) {
|
|
@@ -441,7 +441,7 @@ export class UnitOfWork {
|
|
|
441
441
|
}
|
|
442
442
|
const deletePkHash = [wrapped.getSerializedPrimaryKey(), ...this.expandUniqueProps(entity)];
|
|
443
443
|
let type = ChangeSetType.DELETE;
|
|
444
|
-
for (const cs of inserts[wrapped.__meta.
|
|
444
|
+
for (const cs of inserts[wrapped.__meta.uniqueName] ?? []) {
|
|
445
445
|
if (deletePkHash.some(hash => hash === cs.getSerializedPrimaryKey() || this.expandUniqueProps(cs.entity).find(child => hash === child))) {
|
|
446
446
|
type = ChangeSetType.DELETE_EARLY;
|
|
447
447
|
}
|
|
@@ -460,7 +460,7 @@ export class UnitOfWork {
|
|
|
460
460
|
}
|
|
461
461
|
for (const cs of this.changeSets.values()) {
|
|
462
462
|
for (const prop of props) {
|
|
463
|
-
if (prop.name in cs.payload && cs.
|
|
463
|
+
if (prop.name in cs.payload && cs.rootMeta === changeSet.rootMeta && cs.type === changeSet.type) {
|
|
464
464
|
conflicts = true;
|
|
465
465
|
if (changeSet.payload[prop.name] == null) {
|
|
466
466
|
type = ChangeSetType.UPDATE_EARLY;
|
|
@@ -479,9 +479,10 @@ export class UnitOfWork {
|
|
|
479
479
|
}
|
|
480
480
|
scheduleOrphanRemoval(entity, visited) {
|
|
481
481
|
if (entity) {
|
|
482
|
-
helper(entity)
|
|
482
|
+
const wrapped = helper(entity);
|
|
483
|
+
wrapped.__em = this.em;
|
|
483
484
|
this.orphanRemoveStack.add(entity);
|
|
484
|
-
this.queuedActions.add(
|
|
485
|
+
this.queuedActions.add(wrapped.__meta.class);
|
|
485
486
|
this.cascade(entity, Cascade.SCHEDULE_ORPHAN_REMOVAL, visited);
|
|
486
487
|
}
|
|
487
488
|
}
|
|
@@ -630,7 +631,7 @@ export class UnitOfWork {
|
|
|
630
631
|
const copy = this.comparator.prepareEntity(changeSet.entity);
|
|
631
632
|
await this.eventManager.dispatchEvent(type, { entity: changeSet.entity, meta, em: this.em, changeSet });
|
|
632
633
|
const current = this.comparator.prepareEntity(changeSet.entity);
|
|
633
|
-
const diff = this.comparator.diffEntities(changeSet.
|
|
634
|
+
const diff = this.comparator.diffEntities(changeSet.meta.class, copy, current);
|
|
634
635
|
Object.assign(changeSet.payload, diff);
|
|
635
636
|
const wrapped = helper(changeSet.entity);
|
|
636
637
|
if (wrapped.__identifier) {
|
|
@@ -739,26 +740,26 @@ export class UnitOfWork {
|
|
|
739
740
|
}
|
|
740
741
|
fixMissingReference(entity, prop) {
|
|
741
742
|
const reference = entity[prop.name];
|
|
742
|
-
const
|
|
743
|
-
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
744
|
-
if (!Utils.isEntity(
|
|
745
|
-
entity[prop.name] = this.em.getReference(prop.
|
|
743
|
+
const target = Reference.unwrapReference(reference);
|
|
744
|
+
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && target && !prop.mapToPk) {
|
|
745
|
+
if (!Utils.isEntity(target)) {
|
|
746
|
+
entity[prop.name] = this.em.getReference(prop.targetMeta.class, target, { wrapped: !!prop.ref });
|
|
746
747
|
}
|
|
747
|
-
else if (!helper(
|
|
748
|
-
const pk = helper(
|
|
749
|
-
entity[prop.name] = this.em.getReference(prop.
|
|
748
|
+
else if (!helper(target).__initialized && !helper(target).__em) {
|
|
749
|
+
const pk = helper(target).getPrimaryKey();
|
|
750
|
+
entity[prop.name] = this.em.getReference(prop.targetMeta.class, pk, { wrapped: !!prop.ref });
|
|
750
751
|
}
|
|
751
752
|
}
|
|
752
|
-
// perf: set the `Collection._property` to skip the getter, as it can be slow when there
|
|
753
|
-
if (Utils.isCollection(
|
|
754
|
-
|
|
753
|
+
// perf: set the `Collection._property` to skip the getter, as it can be slow when there are a lot of relations
|
|
754
|
+
if (Utils.isCollection(target)) {
|
|
755
|
+
target.property = prop;
|
|
755
756
|
}
|
|
756
757
|
const isCollection = [ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind);
|
|
757
|
-
if (isCollection && Array.isArray(
|
|
758
|
+
if (isCollection && Array.isArray(target)) {
|
|
758
759
|
const collection = new Collection(entity);
|
|
759
760
|
collection.property = prop;
|
|
760
761
|
entity[prop.name] = collection;
|
|
761
|
-
collection.set(
|
|
762
|
+
collection.set(target);
|
|
762
763
|
}
|
|
763
764
|
}
|
|
764
765
|
async persistToDatabase(groups, ctx) {
|
|
@@ -768,30 +769,30 @@ export class UnitOfWork {
|
|
|
768
769
|
const commitOrder = this.getCommitOrder();
|
|
769
770
|
const commitOrderReversed = [...commitOrder].reverse();
|
|
770
771
|
// early delete - when we recreate entity in the same UoW, we need to issue those delete queries before inserts
|
|
771
|
-
for (const
|
|
772
|
-
await this.commitDeleteChangeSets(groups[ChangeSetType.DELETE_EARLY].get(
|
|
772
|
+
for (const meta of commitOrderReversed) {
|
|
773
|
+
await this.commitDeleteChangeSets(groups[ChangeSetType.DELETE_EARLY].get(meta) ?? [], ctx);
|
|
773
774
|
}
|
|
774
775
|
// early update - when we recreate entity in the same UoW, we need to issue those delete queries before inserts
|
|
775
|
-
for (const
|
|
776
|
-
await this.commitUpdateChangeSets(groups[ChangeSetType.UPDATE_EARLY].get(
|
|
776
|
+
for (const meta of commitOrder) {
|
|
777
|
+
await this.commitUpdateChangeSets(groups[ChangeSetType.UPDATE_EARLY].get(meta) ?? [], ctx);
|
|
777
778
|
}
|
|
778
779
|
// extra updates
|
|
779
780
|
await this.commitExtraUpdates(ChangeSetType.UPDATE_EARLY, ctx);
|
|
780
781
|
// create
|
|
781
|
-
for (const
|
|
782
|
-
await this.commitCreateChangeSets(groups[ChangeSetType.CREATE].get(
|
|
782
|
+
for (const meta of commitOrder) {
|
|
783
|
+
await this.commitCreateChangeSets(groups[ChangeSetType.CREATE].get(meta) ?? [], ctx);
|
|
783
784
|
}
|
|
784
785
|
// update
|
|
785
|
-
for (const
|
|
786
|
-
await this.commitUpdateChangeSets(groups[ChangeSetType.UPDATE].get(
|
|
786
|
+
for (const meta of commitOrder) {
|
|
787
|
+
await this.commitUpdateChangeSets(groups[ChangeSetType.UPDATE].get(meta) ?? [], ctx);
|
|
787
788
|
}
|
|
788
789
|
// extra updates
|
|
789
790
|
await this.commitExtraUpdates(ChangeSetType.UPDATE, ctx);
|
|
790
791
|
// collection updates
|
|
791
792
|
await this.commitCollectionUpdates(ctx);
|
|
792
793
|
// delete - entity deletions need to be in reverse commit order
|
|
793
|
-
for (const
|
|
794
|
-
await this.commitDeleteChangeSets(groups[ChangeSetType.DELETE].get(
|
|
794
|
+
for (const meta of commitOrderReversed) {
|
|
795
|
+
await this.commitDeleteChangeSets(groups[ChangeSetType.DELETE].get(meta) ?? [], ctx);
|
|
795
796
|
}
|
|
796
797
|
// take snapshots of all persisted collections
|
|
797
798
|
const visited = new Set();
|
|
@@ -960,10 +961,10 @@ export class UnitOfWork {
|
|
|
960
961
|
};
|
|
961
962
|
for (const cs of this.changeSets.values()) {
|
|
962
963
|
const group = groups[cs.type];
|
|
963
|
-
const classGroup = group.get(cs.
|
|
964
|
+
const classGroup = group.get(cs.rootMeta) ?? [];
|
|
964
965
|
classGroup.push(cs);
|
|
965
|
-
if (!group.has(cs.
|
|
966
|
-
group.set(cs.
|
|
966
|
+
if (!group.has(cs.rootMeta)) {
|
|
967
|
+
group.set(cs.rootMeta, classGroup);
|
|
967
968
|
}
|
|
968
969
|
}
|
|
969
970
|
return groups;
|
|
@@ -971,14 +972,14 @@ export class UnitOfWork {
|
|
|
971
972
|
getCommitOrder() {
|
|
972
973
|
const calc = new CommitOrderCalculator();
|
|
973
974
|
const set = new Set();
|
|
974
|
-
this.changeSets.forEach(cs => set.add(cs.
|
|
975
|
-
set.forEach(
|
|
976
|
-
for (const
|
|
977
|
-
for (const prop of
|
|
978
|
-
calc.discoverProperty(prop,
|
|
975
|
+
this.changeSets.forEach(cs => set.add(cs.rootMeta));
|
|
976
|
+
set.forEach(meta => calc.addNode(meta._id));
|
|
977
|
+
for (const meta of set) {
|
|
978
|
+
for (const prop of meta.relations) {
|
|
979
|
+
calc.discoverProperty(prop, meta._id);
|
|
979
980
|
}
|
|
980
981
|
}
|
|
981
|
-
return calc.sort();
|
|
982
|
+
return calc.sort().map(id => this.metadata.getById(id));
|
|
982
983
|
}
|
|
983
984
|
resetTransaction(oldTx) {
|
|
984
985
|
if (oldTx) {
|
|
@@ -40,7 +40,7 @@ export class AbstractSchemaGenerator {
|
|
|
40
40
|
}
|
|
41
41
|
async clear(options) {
|
|
42
42
|
for (const meta of this.getOrderedMetadata(options?.schema).reverse()) {
|
|
43
|
-
await this.driver.nativeDelete(meta.
|
|
43
|
+
await this.driver.nativeDelete(meta.class, {}, options);
|
|
44
44
|
}
|
|
45
45
|
if (options?.clearIdentityMap ?? true) {
|
|
46
46
|
this.clearIdentityMap();
|
|
@@ -90,21 +90,21 @@ export class AbstractSchemaGenerator {
|
|
|
90
90
|
this.notImplemented();
|
|
91
91
|
}
|
|
92
92
|
getOrderedMetadata(schema) {
|
|
93
|
-
const metadata =
|
|
94
|
-
const isRootEntity = meta.root.
|
|
93
|
+
const metadata = [...this.metadata.getAll().values()].filter(meta => {
|
|
94
|
+
const isRootEntity = meta.root.class === meta.class;
|
|
95
95
|
return isRootEntity && !meta.embeddable && !meta.virtual;
|
|
96
96
|
});
|
|
97
97
|
const calc = new CommitOrderCalculator();
|
|
98
|
-
metadata.forEach(meta => calc.addNode(meta.root.
|
|
98
|
+
metadata.forEach(meta => calc.addNode(meta.root._id));
|
|
99
99
|
let meta = metadata.pop();
|
|
100
100
|
while (meta) {
|
|
101
|
-
for (const prop of meta.
|
|
102
|
-
calc.discoverProperty(prop, meta.root.
|
|
101
|
+
for (const prop of meta.relations) {
|
|
102
|
+
calc.discoverProperty(prop, meta.root._id);
|
|
103
103
|
}
|
|
104
104
|
meta = metadata.pop();
|
|
105
105
|
}
|
|
106
106
|
return calc.sort()
|
|
107
|
-
.map(cls => this.metadata.
|
|
107
|
+
.map(cls => this.metadata.getById(cls))
|
|
108
108
|
.filter(meta => {
|
|
109
109
|
const targetSchema = meta.schema ?? this.config.get('schema', this.platform.getDefaultSchemaName());
|
|
110
110
|
return schema ? [schema, '*'].includes(targetSchema) : meta.schema !== '*';
|
package/utils/Configuration.d.ts
CHANGED
|
@@ -402,11 +402,6 @@ export interface MetadataDiscoveryOptions {
|
|
|
402
402
|
* @default true
|
|
403
403
|
*/
|
|
404
404
|
checkDuplicateFieldNames?: boolean;
|
|
405
|
-
/**
|
|
406
|
-
* Check for duplicate entities and throw an error if found.
|
|
407
|
-
* @default true
|
|
408
|
-
*/
|
|
409
|
-
checkDuplicateEntities?: boolean;
|
|
410
405
|
/**
|
|
411
406
|
* Check for composite primary keys marked as `persist: false` and throw an error if found.
|
|
412
407
|
* @default true
|
package/utils/DataloaderUtils.js
CHANGED
|
@@ -11,7 +11,7 @@ export class DataloaderUtils {
|
|
|
11
11
|
static groupPrimaryKeysByEntityAndOpts(refsWithOpts) {
|
|
12
12
|
const map = new Map();
|
|
13
13
|
for (const [ref, opts] of refsWithOpts) {
|
|
14
|
-
/* The key is a combination of the
|
|
14
|
+
/* The key is a combination of the uniqueName (a unique table name based identifier) and a stringified version if the load options because we want
|
|
15
15
|
to map each combination of entities/options into separate find queries in order to return accurate results.
|
|
16
16
|
This could be further optimized finding the "lowest common denominator" among the different options
|
|
17
17
|
for each Entity and firing a single query for each Entity instead of Entity+options combination.
|
|
@@ -24,7 +24,7 @@ export class DataloaderUtils {
|
|
|
24
24
|
Thus such approach should probably be configurable, if not opt-in.
|
|
25
25
|
NOTE: meta + opts multi maps (https://github.com/martian17/ds-js) might be a more elegant way
|
|
26
26
|
to implement this but not necessarily faster. */
|
|
27
|
-
const key = `${helper(ref).__meta.
|
|
27
|
+
const key = `${helper(ref).__meta.uniqueName}|${JSON.stringify(opts ?? {})}`;
|
|
28
28
|
let primaryKeysSet = map.get(key);
|
|
29
29
|
if (primaryKeysSet == null) {
|
|
30
30
|
primaryKeysSet = new Set();
|
|
@@ -42,9 +42,10 @@ export class DataloaderUtils {
|
|
|
42
42
|
return async (refsWithOpts) => {
|
|
43
43
|
const groupedIdsMap = DataloaderUtils.groupPrimaryKeysByEntityAndOpts(refsWithOpts);
|
|
44
44
|
const promises = Array.from(groupedIdsMap).map(([key, idsSet]) => {
|
|
45
|
-
const
|
|
45
|
+
const uniqueName = key.substring(0, key.indexOf('|'));
|
|
46
46
|
const opts = JSON.parse(key.substring(key.indexOf('|') + 1));
|
|
47
|
-
|
|
47
|
+
const meta = em.getMetadata().getByUniqueName(uniqueName);
|
|
48
|
+
return em.find(meta.class, Array.from(idsSet), opts);
|
|
48
49
|
});
|
|
49
50
|
await Promise.all(promises);
|
|
50
51
|
/* Instead of assigning each find result to the original reference we use a shortcut
|
|
@@ -70,7 +71,7 @@ export class DataloaderUtils {
|
|
|
70
71
|
The value is another Map which we can use to filter the find query to get results pertaining to the collections that have been dataloaded:
|
|
71
72
|
its keys are the props we are going to filter to and its values are the corresponding PKs.
|
|
72
73
|
*/
|
|
73
|
-
const key = `${col.property.targetMeta.
|
|
74
|
+
const key = `${col.property.targetMeta.uniqueName}|${JSON.stringify(opts ?? {})}`;
|
|
74
75
|
let filterMap = entitiesMap.get(key); // We are going to use this map to filter the entities pertaining to the collections that have been dataloaded.
|
|
75
76
|
if (filterMap == null) {
|
|
76
77
|
filterMap = new Map();
|
|
@@ -97,9 +98,10 @@ export class DataloaderUtils {
|
|
|
97
98
|
*/
|
|
98
99
|
static entitiesAndOptsMapToQueries(entitiesAndOptsMap, em) {
|
|
99
100
|
return Array.from(entitiesAndOptsMap, async ([key, filterMap]) => {
|
|
100
|
-
const
|
|
101
|
+
const uniqueName = key.substring(0, key.indexOf('|'));
|
|
101
102
|
const opts = JSON.parse(key.substring(key.indexOf('|') + 1));
|
|
102
|
-
const
|
|
103
|
+
const meta = em.getMetadata().getByUniqueName(uniqueName);
|
|
104
|
+
const res = await em.find(meta.class, opts?.where != null && Object.keys(opts.where).length > 0 ?
|
|
103
105
|
{
|
|
104
106
|
$and: [
|
|
105
107
|
{
|
|
@@ -121,7 +123,7 @@ export class DataloaderUtils {
|
|
|
121
123
|
...(opts.populate === false ? [] : opts.populate ?? []),
|
|
122
124
|
...Array.from(filterMap.keys()).filter(
|
|
123
125
|
// 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
|
|
124
|
-
prop =>
|
|
126
|
+
prop => meta.properties[prop]?.ref !== true),
|
|
125
127
|
],
|
|
126
128
|
});
|
|
127
129
|
return [key, res];
|
|
@@ -164,7 +166,7 @@ export class DataloaderUtils {
|
|
|
164
166
|
// We need to filter the results in order to map each input collection
|
|
165
167
|
// to a subset of each query matching the collection items.
|
|
166
168
|
return collsWithOpts.map(([col, opts]) => {
|
|
167
|
-
const key = `${col.property.targetMeta.
|
|
169
|
+
const key = `${col.property.targetMeta.uniqueName}|${JSON.stringify(opts ?? {})}`;
|
|
168
170
|
const entities = resultsMap.get(key);
|
|
169
171
|
if (entities == null) {
|
|
170
172
|
// Should never happen
|
|
@@ -183,7 +185,7 @@ export class DataloaderUtils {
|
|
|
183
185
|
return async (collsWithOpts) => {
|
|
184
186
|
const groups = new Map();
|
|
185
187
|
for (const [col, opts] of collsWithOpts) {
|
|
186
|
-
const key = `${col.property.targetMeta.
|
|
188
|
+
const key = `${col.property.targetMeta.uniqueName}.${col.property.name}|${JSON.stringify(opts ?? {})}`;
|
|
187
189
|
const value = groups.get(key) ?? [];
|
|
188
190
|
value.push([col, opts ?? {}]);
|
|
189
191
|
groups.set(key, value);
|
|
@@ -198,7 +200,7 @@ export class DataloaderUtils {
|
|
|
198
200
|
const owners = group.map(c => c[0].owner);
|
|
199
201
|
const $or = [];
|
|
200
202
|
// a bit of a hack, but we need to prefix the key, since we have only a column name, not a property name
|
|
201
|
-
const alias = em.config.getNamingStrategy().aliasName(prop.pivotEntity, 0);
|
|
203
|
+
const alias = em.config.getNamingStrategy().aliasName(Utils.className(prop.pivotEntity), 0);
|
|
202
204
|
const fk = `${alias}.${Utils.getPrimaryKeyHash(prop.joinColumns)}`;
|
|
203
205
|
for (const c of group) {
|
|
204
206
|
$or.push({ $and: [c[1]?.where ?? {}, { [fk]: c[0].owner }] });
|