@mikro-orm/core 7.0.0-dev.21 → 7.0.0-dev.23
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 +12 -2
- package/EntityManager.js +40 -53
- package/README.md +1 -2
- package/connections/Connection.d.ts +4 -2
- package/connections/Connection.js +2 -2
- package/decorators/Entity.d.ts +15 -0
- package/decorators/Indexed.d.ts +2 -2
- package/decorators/ManyToMany.d.ts +2 -0
- package/decorators/ManyToOne.d.ts +2 -0
- package/decorators/OneToOne.d.ts +2 -0
- package/decorators/Transactional.d.ts +1 -0
- package/decorators/Transactional.js +3 -3
- package/drivers/IDatabaseDriver.d.ts +4 -0
- package/entity/ArrayCollection.d.ts +3 -1
- package/entity/ArrayCollection.js +7 -2
- package/entity/Collection.js +3 -2
- package/entity/EntityFactory.d.ts +6 -0
- package/entity/EntityFactory.js +17 -6
- package/entity/EntityHelper.js +5 -1
- package/entity/EntityLoader.js +27 -19
- package/entity/Reference.d.ts +5 -0
- package/entity/Reference.js +16 -0
- package/entity/WrappedEntity.js +1 -1
- package/entity/defineEntity.d.ts +537 -0
- package/entity/defineEntity.js +690 -0
- package/entity/index.d.ts +2 -0
- package/entity/index.js +2 -0
- package/entity/utils.d.ts +7 -0
- package/entity/utils.js +15 -3
- package/enums.d.ts +15 -2
- package/enums.js +13 -0
- package/errors.d.ts +6 -0
- package/errors.js +14 -0
- package/hydration/ObjectHydrator.js +1 -1
- package/index.d.ts +1 -1
- package/metadata/EntitySchema.js +10 -2
- package/metadata/MetadataDiscovery.d.ts +0 -1
- package/metadata/MetadataDiscovery.js +27 -18
- package/package.json +4 -5
- package/platforms/Platform.d.ts +3 -1
- package/serialization/SerializationContext.js +13 -10
- package/types/BooleanType.d.ts +1 -1
- package/types/DecimalType.js +1 -1
- package/types/DoubleType.js +1 -1
- package/typings.d.ts +32 -10
- package/typings.js +21 -4
- package/unit-of-work/ChangeSetComputer.js +3 -1
- package/unit-of-work/ChangeSetPersister.d.ts +4 -2
- package/unit-of-work/ChangeSetPersister.js +14 -10
- package/unit-of-work/UnitOfWork.d.ts +2 -1
- package/unit-of-work/UnitOfWork.js +36 -13
- package/utils/Configuration.d.ts +7 -1
- package/utils/Configuration.js +1 -0
- package/utils/ConfigurationLoader.js +2 -2
- package/utils/Cursor.js +3 -0
- package/utils/DataloaderUtils.d.ts +6 -1
- package/utils/DataloaderUtils.js +37 -20
- package/utils/EntityComparator.d.ts +6 -2
- package/utils/EntityComparator.js +29 -8
- package/utils/QueryHelper.d.ts +6 -0
- package/utils/QueryHelper.js +47 -4
- package/utils/RawQueryFragment.d.ts +34 -0
- package/utils/RawQueryFragment.js +34 -0
- package/utils/TransactionManager.d.ts +65 -0
- package/utils/TransactionManager.js +199 -0
- package/utils/Utils.d.ts +2 -2
- package/utils/Utils.js +31 -7
- package/utils/index.d.ts +1 -0
- package/utils/index.js +1 -0
- package/utils/upsert-utils.js +9 -1
- package/exports.d.ts +0 -24
- package/exports.js +0 -23
package/typings.js
CHANGED
|
@@ -45,12 +45,29 @@ export class EntityMetadata {
|
|
|
45
45
|
this.sync();
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
|
-
getPrimaryProps() {
|
|
49
|
-
|
|
48
|
+
getPrimaryProps(flatten = false) {
|
|
49
|
+
const pks = this.primaryKeys.map(pk => this.properties[pk]);
|
|
50
|
+
if (flatten) {
|
|
51
|
+
return pks.flatMap(pk => {
|
|
52
|
+
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(pk.kind)) {
|
|
53
|
+
return pk.targetMeta.getPrimaryProps(true);
|
|
54
|
+
}
|
|
55
|
+
return [pk];
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return pks;
|
|
50
59
|
}
|
|
51
60
|
getPrimaryProp() {
|
|
52
61
|
return this.properties[this.primaryKeys[0]];
|
|
53
62
|
}
|
|
63
|
+
createColumnMappingObject() {
|
|
64
|
+
return Object.values(this.properties).reduce((o, prop) => {
|
|
65
|
+
if (prop.fieldNames) {
|
|
66
|
+
o[prop.name] = prop.fieldNames[0];
|
|
67
|
+
}
|
|
68
|
+
return o;
|
|
69
|
+
}, {});
|
|
70
|
+
}
|
|
54
71
|
get tableName() {
|
|
55
72
|
return this.collection;
|
|
56
73
|
}
|
|
@@ -70,7 +87,7 @@ export class EntityMetadata {
|
|
|
70
87
|
// `prop.userDefined` is either `undefined` or `false`
|
|
71
88
|
const discriminator = this.root.discriminatorColumn === prop.name && prop.userDefined === false;
|
|
72
89
|
// even if we don't have a setter, do not ignore value from database!
|
|
73
|
-
const onlyGetter = prop.getter && !prop.setter;
|
|
90
|
+
const onlyGetter = prop.getter && !prop.setter && prop.persist === false;
|
|
74
91
|
return !prop.inherited && prop.hydrate !== false && !discriminator && !prop.embedded && !onlyGetter;
|
|
75
92
|
});
|
|
76
93
|
this.trackingProps = this.hydrateProps
|
|
@@ -129,7 +146,7 @@ export class EntityMetadata {
|
|
|
129
146
|
wrapped.__data[prop.name] = Reference.wrapReference(val, prop);
|
|
130
147
|
// when propagation from inside hydration, we set the FK to the entity data immediately
|
|
131
148
|
if (val && hydrator.isRunning() && wrapped.__originalEntityData && prop.owner) {
|
|
132
|
-
wrapped.__originalEntityData[prop.name] = Utils.getPrimaryKeyValues(val, prop.targetMeta
|
|
149
|
+
wrapped.__originalEntityData[prop.name] = Utils.getPrimaryKeyValues(val, prop.targetMeta, true);
|
|
133
150
|
}
|
|
134
151
|
else {
|
|
135
152
|
wrapped.__touched = !hydrator.isRunning();
|
|
@@ -141,7 +141,9 @@ export class ChangeSetComputer {
|
|
|
141
141
|
if (!target.isDirty() && changeSet.type !== ChangeSetType.CREATE) {
|
|
142
142
|
return;
|
|
143
143
|
}
|
|
144
|
-
|
|
144
|
+
if (target.isDirty()) {
|
|
145
|
+
this.collectionUpdates.add(target);
|
|
146
|
+
}
|
|
145
147
|
if (prop.owner && !this.platform.usesPivotTable()) {
|
|
146
148
|
changeSet.payload[prop.name] = target.getItems(false).map((item) => item.__helper.__identifier ?? item.__helper.getPrimaryKey());
|
|
147
149
|
}
|
|
@@ -5,6 +5,7 @@ import { type EntityValidator } from '../entity/EntityValidator.js';
|
|
|
5
5
|
import { type ChangeSet } from './ChangeSet.js';
|
|
6
6
|
import { type Configuration } from '../utils/Configuration.js';
|
|
7
7
|
import type { DriverMethodOptions, IDatabaseDriver } from '../drivers/IDatabaseDriver.js';
|
|
8
|
+
import type { EntityManager } from '../EntityManager.js';
|
|
8
9
|
export declare class ChangeSetPersister {
|
|
9
10
|
private readonly driver;
|
|
10
11
|
private readonly metadata;
|
|
@@ -12,10 +13,11 @@ export declare class ChangeSetPersister {
|
|
|
12
13
|
private readonly factory;
|
|
13
14
|
private readonly validator;
|
|
14
15
|
private readonly config;
|
|
16
|
+
private readonly em;
|
|
15
17
|
private readonly platform;
|
|
16
18
|
private readonly comparator;
|
|
17
19
|
private readonly usesReturningStatement;
|
|
18
|
-
constructor(driver: IDatabaseDriver, metadata: MetadataStorage, hydrator: IHydrator, factory: EntityFactory, validator: EntityValidator, config: Configuration);
|
|
20
|
+
constructor(driver: IDatabaseDriver, metadata: MetadataStorage, hydrator: IHydrator, factory: EntityFactory, validator: EntityValidator, config: Configuration, em: EntityManager);
|
|
19
21
|
executeInserts<T extends object>(changeSets: ChangeSet<T>[], options?: DriverMethodOptions, withSchema?: boolean): Promise<void>;
|
|
20
22
|
executeUpdates<T extends object>(changeSets: ChangeSet<T>[], batched: boolean, options?: DriverMethodOptions, withSchema?: boolean): Promise<void>;
|
|
21
23
|
executeDeletes<T extends object>(changeSets: ChangeSet<T>[], options?: DriverMethodOptions, withSchema?: boolean): Promise<void>;
|
|
@@ -23,7 +25,7 @@ export declare class ChangeSetPersister {
|
|
|
23
25
|
private processProperties;
|
|
24
26
|
private persistNewEntity;
|
|
25
27
|
private persistNewEntities;
|
|
26
|
-
private
|
|
28
|
+
private prepareOptions;
|
|
27
29
|
private persistNewEntitiesBatch;
|
|
28
30
|
private persistManagedEntity;
|
|
29
31
|
private persistManagedEntities;
|
|
@@ -12,16 +12,18 @@ export class ChangeSetPersister {
|
|
|
12
12
|
factory;
|
|
13
13
|
validator;
|
|
14
14
|
config;
|
|
15
|
+
em;
|
|
15
16
|
platform;
|
|
16
17
|
comparator;
|
|
17
18
|
usesReturningStatement;
|
|
18
|
-
constructor(driver, metadata, hydrator, factory, validator, config) {
|
|
19
|
+
constructor(driver, metadata, hydrator, factory, validator, config, em) {
|
|
19
20
|
this.driver = driver;
|
|
20
21
|
this.metadata = metadata;
|
|
21
22
|
this.hydrator = hydrator;
|
|
22
23
|
this.factory = factory;
|
|
23
24
|
this.validator = validator;
|
|
24
25
|
this.config = config;
|
|
26
|
+
this.em = em;
|
|
25
27
|
this.platform = this.driver.getPlatform();
|
|
26
28
|
this.comparator = this.config.getComparator(this.metadata);
|
|
27
29
|
this.usesReturningStatement = this.platform.usesReturningStatement() || this.platform.usesOutputStatement();
|
|
@@ -62,7 +64,7 @@ export class ChangeSetPersister {
|
|
|
62
64
|
for (let i = 0; i < changeSets.length; i += size) {
|
|
63
65
|
const chunk = changeSets.slice(i, i + size);
|
|
64
66
|
const pks = chunk.map(cs => cs.getPrimaryKey());
|
|
65
|
-
options = this.
|
|
67
|
+
options = this.prepareOptions(meta, options);
|
|
66
68
|
await this.driver.nativeDelete(meta.root.className, { [pk]: { $in: pks } }, options);
|
|
67
69
|
}
|
|
68
70
|
}
|
|
@@ -90,7 +92,7 @@ export class ChangeSetPersister {
|
|
|
90
92
|
}
|
|
91
93
|
async persistNewEntity(meta, changeSet, options) {
|
|
92
94
|
const wrapped = helper(changeSet.entity);
|
|
93
|
-
options = this.
|
|
95
|
+
options = this.prepareOptions(meta, options, {
|
|
94
96
|
convertCustomTypes: false,
|
|
95
97
|
});
|
|
96
98
|
const res = await this.driver.nativeInsertMany(meta.className, [changeSet.payload], options);
|
|
@@ -116,15 +118,17 @@ export class ChangeSetPersister {
|
|
|
116
118
|
}
|
|
117
119
|
}
|
|
118
120
|
}
|
|
119
|
-
|
|
121
|
+
prepareOptions(meta, options, additionalOptions) {
|
|
122
|
+
const loggerContext = Utils.merge({ id: this.em._id }, this.em.getLoggerContext({ disableContextResolution: true }));
|
|
120
123
|
return {
|
|
121
124
|
...options,
|
|
122
125
|
...additionalOptions,
|
|
123
126
|
schema: options?.schema ?? meta.schema,
|
|
127
|
+
loggerContext,
|
|
124
128
|
};
|
|
125
129
|
}
|
|
126
130
|
async persistNewEntitiesBatch(meta, changeSets, options) {
|
|
127
|
-
options = this.
|
|
131
|
+
options = this.prepareOptions(meta, options, {
|
|
128
132
|
convertCustomTypes: false,
|
|
129
133
|
processCollections: false,
|
|
130
134
|
});
|
|
@@ -175,7 +179,7 @@ export class ChangeSetPersister {
|
|
|
175
179
|
}
|
|
176
180
|
async persistManagedEntitiesBatch(meta, changeSets, options) {
|
|
177
181
|
await this.checkOptimisticLocks(meta, changeSets, options);
|
|
178
|
-
options = this.
|
|
182
|
+
options = this.prepareOptions(meta, options, {
|
|
179
183
|
convertCustomTypes: false,
|
|
180
184
|
processCollections: false,
|
|
181
185
|
});
|
|
@@ -235,7 +239,7 @@ export class ChangeSetPersister {
|
|
|
235
239
|
}
|
|
236
240
|
async updateEntity(meta, changeSet, options) {
|
|
237
241
|
const cond = changeSet.getPrimaryKey(true);
|
|
238
|
-
options = this.
|
|
242
|
+
options = this.prepareOptions(meta, options, {
|
|
239
243
|
convertCustomTypes: false,
|
|
240
244
|
});
|
|
241
245
|
if (meta.concurrencyCheckKeys.size === 0 && (!meta.versionProperty || changeSet.entity[meta.versionProperty] == null)) {
|
|
@@ -262,7 +266,7 @@ export class ChangeSetPersister {
|
|
|
262
266
|
return cond;
|
|
263
267
|
});
|
|
264
268
|
const primaryKeys = meta.primaryKeys.concat(...meta.concurrencyCheckKeys);
|
|
265
|
-
options = this.
|
|
269
|
+
options = this.prepareOptions(meta, options, {
|
|
266
270
|
fields: primaryKeys,
|
|
267
271
|
});
|
|
268
272
|
const res = await this.driver.find(meta.root.className, { $or }, options);
|
|
@@ -321,12 +325,12 @@ export class ChangeSetPersister {
|
|
|
321
325
|
}
|
|
322
326
|
return val;
|
|
323
327
|
});
|
|
324
|
-
options = this.
|
|
328
|
+
options = this.prepareOptions(meta, options, {
|
|
325
329
|
fields: Utils.unique(reloadProps.map(prop => prop.name)),
|
|
326
330
|
});
|
|
327
331
|
const data = await this.driver.find(meta.className, { [pk]: { $in: pks } }, options);
|
|
328
332
|
const map = new Map();
|
|
329
|
-
data.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta,
|
|
333
|
+
data.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, false, this.platform, true), item));
|
|
330
334
|
for (const changeSet of changeSets) {
|
|
331
335
|
const data = map.get(helper(changeSet.entity).getSerializedPrimaryKey());
|
|
332
336
|
this.hydrator.hydrate(changeSet.entity, meta, data, this.factory, 'full', false, true);
|
|
@@ -39,7 +39,7 @@ export declare class UnitOfWork {
|
|
|
39
39
|
/**
|
|
40
40
|
* 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`.
|
|
41
41
|
*/
|
|
42
|
-
getById<T extends object>(entityName: string, id: Primary<T> | Primary<T>[], schema?: string): T | undefined;
|
|
42
|
+
getById<T extends object>(entityName: string, id: Primary<T> | Primary<T>[], schema?: string, convertCustomTypes?: boolean): T | undefined;
|
|
43
43
|
tryGetById<T extends object>(entityName: string, where: FilterQuery<T>, schema?: string, strict?: boolean): T | null;
|
|
44
44
|
/**
|
|
45
45
|
* Returns map of all managed entities.
|
|
@@ -103,6 +103,7 @@ export declare class UnitOfWork {
|
|
|
103
103
|
private commitDeleteChangeSets;
|
|
104
104
|
private commitExtraUpdates;
|
|
105
105
|
private commitCollectionUpdates;
|
|
106
|
+
private filterCollectionUpdates;
|
|
106
107
|
/**
|
|
107
108
|
* Orders change sets so FK constrains are maintained, ensures stable order (needed for node < 11)
|
|
108
109
|
*/
|
|
@@ -43,7 +43,7 @@ export class UnitOfWork {
|
|
|
43
43
|
this.eventManager = this.em.getEventManager();
|
|
44
44
|
this.comparator = this.em.getComparator();
|
|
45
45
|
this.changeSetComputer = new ChangeSetComputer(this.em.getValidator(), this.collectionUpdates, this.metadata, this.platform, this.em.config, this.em);
|
|
46
|
-
this.changeSetPersister = new ChangeSetPersister(this.em.getDriver(), this.metadata, this.em.config.getHydrator(this.metadata), this.em.getEntityFactory(), this.em.getValidator(), this.em.config);
|
|
46
|
+
this.changeSetPersister = new ChangeSetPersister(this.em.getDriver(), this.metadata, this.em.config.getHydrator(this.metadata), this.em.getEntityFactory(), this.em.getValidator(), this.em.config, this.em);
|
|
47
47
|
}
|
|
48
48
|
merge(entity, visited) {
|
|
49
49
|
const wrapped = helper(entity);
|
|
@@ -89,7 +89,7 @@ export class UnitOfWork {
|
|
|
89
89
|
}
|
|
90
90
|
wrapped.__loadedProperties.add(key);
|
|
91
91
|
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && Utils.isPlainObject(data[prop.name])) {
|
|
92
|
-
data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta
|
|
92
|
+
data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
|
|
93
93
|
}
|
|
94
94
|
else if (prop.kind === ReferenceKind.EMBEDDED && !prop.object && Utils.isPlainObject(data[prop.name])) {
|
|
95
95
|
for (const p of prop.targetMeta.props) {
|
|
@@ -97,7 +97,7 @@ export class UnitOfWork {
|
|
|
97
97
|
const prefix = prop.prefix === false ? '' : prop.prefix === true ? prop.name + '_' : prop.prefix;
|
|
98
98
|
data[prefix + p.name] = data[prop.name][p.name];
|
|
99
99
|
}
|
|
100
|
-
data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta
|
|
100
|
+
data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
|
|
101
101
|
}
|
|
102
102
|
if (forceUndefined) {
|
|
103
103
|
if (data[key] === null) {
|
|
@@ -125,7 +125,7 @@ export class UnitOfWork {
|
|
|
125
125
|
/**
|
|
126
126
|
* 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`.
|
|
127
127
|
*/
|
|
128
|
-
getById(entityName, id, schema) {
|
|
128
|
+
getById(entityName, id, schema, convertCustomTypes) {
|
|
129
129
|
if (id == null || (Array.isArray(id) && id.length === 0)) {
|
|
130
130
|
return undefined;
|
|
131
131
|
}
|
|
@@ -135,7 +135,16 @@ export class UnitOfWork {
|
|
|
135
135
|
hash = '' + id;
|
|
136
136
|
}
|
|
137
137
|
else {
|
|
138
|
-
|
|
138
|
+
let keys = Array.isArray(id) ? Utils.flatten(id) : [id];
|
|
139
|
+
keys = meta.getPrimaryProps(true).map((p, i) => {
|
|
140
|
+
if (!convertCustomTypes && p.customType) {
|
|
141
|
+
return p.customType.convertToDatabaseValue(keys[i], this.platform, {
|
|
142
|
+
key: p.name,
|
|
143
|
+
mode: 'hydration',
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
return keys[i];
|
|
147
|
+
});
|
|
139
148
|
hash = Utils.getPrimaryKeyHash(keys);
|
|
140
149
|
}
|
|
141
150
|
schema ??= meta.schema ?? this.em.config.getSchema();
|
|
@@ -303,6 +312,7 @@ export class UnitOfWork {
|
|
|
303
312
|
cs.entity.__helper.__processing = true;
|
|
304
313
|
}
|
|
305
314
|
await this.eventManager.dispatchEvent(EventType.onFlush, { em: this.em, uow: this });
|
|
315
|
+
this.filterCollectionUpdates();
|
|
306
316
|
// nothing to do, do not start transaction
|
|
307
317
|
if (this.changeSets.size === 0 && this.collectionUpdates.size === 0 && this.extraUpdates.size === 0) {
|
|
308
318
|
return void (await this.eventManager.dispatchEvent(EventType.afterFlush, { em: this.em, uow: this }));
|
|
@@ -311,9 +321,11 @@ export class UnitOfWork {
|
|
|
311
321
|
const platform = this.em.getPlatform();
|
|
312
322
|
const runInTransaction = !this.em.isInTransaction() && platform.supportsTransactions() && this.em.config.get('implicitTransactions');
|
|
313
323
|
if (runInTransaction) {
|
|
324
|
+
const loggerContext = Utils.merge({ id: this.em._id }, this.em.getLoggerContext({ disableContextResolution: true }));
|
|
314
325
|
await this.em.getConnection('write').transactional(trx => this.persistToDatabase(groups, trx), {
|
|
315
326
|
ctx: oldTx,
|
|
316
327
|
eventBroadcaster: new TransactionEventBroadcaster(this.em),
|
|
328
|
+
loggerContext,
|
|
317
329
|
});
|
|
318
330
|
}
|
|
319
331
|
else {
|
|
@@ -886,23 +898,34 @@ export class UnitOfWork {
|
|
|
886
898
|
}
|
|
887
899
|
}
|
|
888
900
|
async commitCollectionUpdates(ctx) {
|
|
889
|
-
|
|
901
|
+
this.filterCollectionUpdates();
|
|
902
|
+
const loggerContext = Utils.merge({ id: this.em._id }, this.em.getLoggerContext({ disableContextResolution: true }));
|
|
903
|
+
await this.em.getDriver().syncCollections(this.collectionUpdates, {
|
|
904
|
+
ctx,
|
|
905
|
+
schema: this.em.schema,
|
|
906
|
+
loggerContext,
|
|
907
|
+
});
|
|
908
|
+
for (const coll of this.collectionUpdates) {
|
|
909
|
+
coll.takeSnapshot();
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
filterCollectionUpdates() {
|
|
890
913
|
for (const coll of this.collectionUpdates) {
|
|
914
|
+
let skip = true;
|
|
891
915
|
if (coll.property.owner || coll.getItems(false).filter(item => !item.__helper.__initialized).length > 0) {
|
|
892
916
|
if (this.platform.usesPivotTable()) {
|
|
893
|
-
|
|
917
|
+
skip = false;
|
|
894
918
|
}
|
|
895
919
|
}
|
|
896
920
|
else if (coll.property.kind === ReferenceKind.ONE_TO_MANY && coll.getSnapshot() === undefined) {
|
|
897
|
-
|
|
921
|
+
skip = false;
|
|
898
922
|
}
|
|
899
923
|
else if (coll.property.kind === ReferenceKind.MANY_TO_MANY && !coll.property.owner) {
|
|
900
|
-
|
|
924
|
+
skip = false;
|
|
925
|
+
}
|
|
926
|
+
if (skip) {
|
|
927
|
+
this.collectionUpdates.delete(coll);
|
|
901
928
|
}
|
|
902
|
-
}
|
|
903
|
-
await this.em.getDriver().syncCollections(collectionUpdates, { ctx, schema: this.em.schema });
|
|
904
|
-
for (const coll of this.collectionUpdates) {
|
|
905
|
-
coll.takeSnapshot();
|
|
906
929
|
}
|
|
907
930
|
}
|
|
908
931
|
/**
|
package/utils/Configuration.d.ts
CHANGED
|
@@ -78,6 +78,7 @@ export declare class Configuration<D extends IDatabaseDriver = IDatabaseDriver,
|
|
|
78
78
|
upsertManaged: true;
|
|
79
79
|
forceEntityConstructor: false;
|
|
80
80
|
forceUndefined: false;
|
|
81
|
+
processOnCreateHooksEarly: false;
|
|
81
82
|
ensureDatabase: true;
|
|
82
83
|
ensureIndexes: false;
|
|
83
84
|
batchSize: number;
|
|
@@ -332,6 +333,11 @@ export interface MikroORMOptions<D extends IDatabaseDriver = IDatabaseDriver, EM
|
|
|
332
333
|
upsertManaged: boolean;
|
|
333
334
|
forceEntityConstructor: boolean | (Constructor<AnyEntity> | string)[];
|
|
334
335
|
forceUndefined: boolean;
|
|
336
|
+
/**
|
|
337
|
+
* Property `onCreate` hooks are normally executed during `flush` operation.
|
|
338
|
+
* With this option, they will be processed early inside `em.create()` method.
|
|
339
|
+
*/
|
|
340
|
+
processOnCreateHooksEarly: boolean;
|
|
335
341
|
forceUtcTimezone?: boolean;
|
|
336
342
|
timezone?: string;
|
|
337
343
|
ensureDatabase: boolean | EnsureDatabaseOptions;
|
|
@@ -340,7 +346,7 @@ export interface MikroORMOptions<D extends IDatabaseDriver = IDatabaseDriver, EM
|
|
|
340
346
|
useBatchUpdates?: boolean;
|
|
341
347
|
batchSize: number;
|
|
342
348
|
hydrator: HydratorConstructor;
|
|
343
|
-
loadStrategy: LoadStrategy |
|
|
349
|
+
loadStrategy: LoadStrategy | `${LoadStrategy}`;
|
|
344
350
|
dataloader: DataloaderType | boolean;
|
|
345
351
|
populateWhere?: PopulateHint | `${PopulateHint}`;
|
|
346
352
|
flushMode: FlushMode | 'commit' | 'auto' | 'always';
|
package/utils/Configuration.js
CHANGED
|
@@ -192,7 +192,7 @@ export class ConfigurationLoader {
|
|
|
192
192
|
static registerDotenv(options) {
|
|
193
193
|
const path = process.env.MIKRO_ORM_ENV ?? ((options?.baseDir ?? process.cwd()) + '/.env');
|
|
194
194
|
const env = {};
|
|
195
|
-
dotenv.config({ path, processEnv: env });
|
|
195
|
+
dotenv.config({ path, processEnv: env, quiet: true });
|
|
196
196
|
// only propagate known env vars
|
|
197
197
|
for (const key of Object.keys(env)) {
|
|
198
198
|
if (key.startsWith('MIKRO_ORM_')) {
|
|
@@ -332,7 +332,7 @@ export class ConfigurationLoader {
|
|
|
332
332
|
// inspired by https://github.com/facebook/docusaurus/pull/3386
|
|
333
333
|
static checkPackageVersion() {
|
|
334
334
|
const coreVersion = Utils.getORMVersion();
|
|
335
|
-
if (process.env.MIKRO_ORM_ALLOW_VERSION_MISMATCH) {
|
|
335
|
+
if (process.env.MIKRO_ORM_ALLOW_VERSION_MISMATCH || coreVersion === 'N/A') {
|
|
336
336
|
return coreVersion;
|
|
337
337
|
}
|
|
338
338
|
const deps = this.getORMPackages();
|
package/utils/Cursor.js
CHANGED
|
@@ -34,8 +34,13 @@ export declare class DataloaderUtils {
|
|
|
34
34
|
*/
|
|
35
35
|
static getColFilter<T, S extends T>(collection: Collection<any>): (result: T) => result is S;
|
|
36
36
|
/**
|
|
37
|
-
* Returns the collection dataloader batchLoadFn, which aggregates collections by entity,
|
|
37
|
+
* Returns the 1:M collection dataloader batchLoadFn, which aggregates collections by entity,
|
|
38
38
|
* makes one query per entity and maps each input collection to the corresponding result.
|
|
39
39
|
*/
|
|
40
40
|
static getColBatchLoadFn(em: EntityManager): DataLoader.BatchLoadFn<[Collection<any>, Omit<InitCollectionOptions<any, any>, 'dataloader'>?], any>;
|
|
41
|
+
/**
|
|
42
|
+
* Returns the M:N collection dataloader batchLoadFn, which aggregates collections by entity,
|
|
43
|
+
* makes one query per entity and maps each input collection to the corresponding result.
|
|
44
|
+
*/
|
|
45
|
+
static getManyToManyColBatchLoadFn(em: EntityManager): DataLoader.BatchLoadFn<[Collection<any>, Omit<InitCollectionOptions<any, any>, 'dataloader'>?], any>;
|
|
41
46
|
}
|
package/utils/DataloaderUtils.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Collection } from '../entity/Collection.js';
|
|
2
2
|
import { helper } from '../entity/wrap.js';
|
|
3
|
-
import { ReferenceKind } from '../enums.js';
|
|
4
3
|
import { Reference } from '../entity/Reference.js';
|
|
5
4
|
import { Utils } from './Utils.js';
|
|
6
5
|
export class DataloaderUtils {
|
|
@@ -153,29 +152,11 @@ export class DataloaderUtils {
|
|
|
153
152
|
};
|
|
154
153
|
}
|
|
155
154
|
/**
|
|
156
|
-
* Returns the collection dataloader batchLoadFn, which aggregates collections by entity,
|
|
155
|
+
* Returns the 1:M collection dataloader batchLoadFn, which aggregates collections by entity,
|
|
157
156
|
* makes one query per entity and maps each input collection to the corresponding result.
|
|
158
157
|
*/
|
|
159
158
|
static getColBatchLoadFn(em) {
|
|
160
159
|
return async (collsWithOpts) => {
|
|
161
|
-
const prop = collsWithOpts[0][0].property;
|
|
162
|
-
if (prop.kind === ReferenceKind.MANY_TO_MANY && em.getPlatform().usesPivotTable()) {
|
|
163
|
-
const options = {};
|
|
164
|
-
const wrap = (cond) => ({ [prop.name]: cond });
|
|
165
|
-
const orderBy = Utils.asArray(collsWithOpts[0][1]?.orderBy).map(o => wrap(o));
|
|
166
|
-
const populate = wrap(collsWithOpts[0][1]?.populate);
|
|
167
|
-
const owners = collsWithOpts.map(c => c[0].owner);
|
|
168
|
-
const $or = [];
|
|
169
|
-
// a bit of a hack, but we need to prefix the key, since we have only a column name, not a property name
|
|
170
|
-
const alias = em.config.getNamingStrategy().aliasName(prop.pivotEntity, 0);
|
|
171
|
-
const fk = `${alias}.${Utils.getPrimaryKeyHash(prop.joinColumns)}`;
|
|
172
|
-
for (const c of collsWithOpts) {
|
|
173
|
-
$or.push({ $and: [c[1]?.where ?? {}, { [fk]: c[0].owner }] });
|
|
174
|
-
options.refresh ??= c[1]?.refresh;
|
|
175
|
-
}
|
|
176
|
-
options.where = wrap({ $or });
|
|
177
|
-
return em.getEntityLoader().findChildrenFromPivotTable(owners, prop, options, orderBy, populate, collsWithOpts[0][1]?.ref);
|
|
178
|
-
}
|
|
179
160
|
const entitiesAndOptsMap = DataloaderUtils.groupInversedOrMappedKeysByEntityAndOpts(collsWithOpts);
|
|
180
161
|
const promises = DataloaderUtils.entitiesAndOptsMapToQueries(entitiesAndOptsMap, em);
|
|
181
162
|
const resultsMap = new Map(await Promise.all(promises));
|
|
@@ -193,4 +174,40 @@ export class DataloaderUtils {
|
|
|
193
174
|
});
|
|
194
175
|
};
|
|
195
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* Returns the M:N collection dataloader batchLoadFn, which aggregates collections by entity,
|
|
179
|
+
* makes one query per entity and maps each input collection to the corresponding result.
|
|
180
|
+
*/
|
|
181
|
+
static getManyToManyColBatchLoadFn(em) {
|
|
182
|
+
return async (collsWithOpts) => {
|
|
183
|
+
const groups = new Map();
|
|
184
|
+
for (const [col, opts] of collsWithOpts) {
|
|
185
|
+
const key = `${col.property.targetMeta.className}.${col.property.name}|${JSON.stringify(opts ?? {})}`;
|
|
186
|
+
const value = groups.get(key) ?? [];
|
|
187
|
+
value.push([col, opts ?? {}]);
|
|
188
|
+
groups.set(key, value);
|
|
189
|
+
}
|
|
190
|
+
const ret = [];
|
|
191
|
+
for (const group of groups.values()) {
|
|
192
|
+
const prop = group[0][0].property;
|
|
193
|
+
const options = {};
|
|
194
|
+
const wrap = (cond) => ({ [prop.name]: cond });
|
|
195
|
+
const orderBy = Utils.asArray(group[0][1]?.orderBy).map(o => wrap(o));
|
|
196
|
+
const populate = wrap(group[0][1]?.populate);
|
|
197
|
+
const owners = group.map(c => c[0].owner);
|
|
198
|
+
const $or = [];
|
|
199
|
+
// a bit of a hack, but we need to prefix the key, since we have only a column name, not a property name
|
|
200
|
+
const alias = em.config.getNamingStrategy().aliasName(prop.pivotEntity, 0);
|
|
201
|
+
const fk = `${alias}.${Utils.getPrimaryKeyHash(prop.joinColumns)}`;
|
|
202
|
+
for (const c of group) {
|
|
203
|
+
$or.push({ $and: [c[1]?.where ?? {}, { [fk]: c[0].owner }] });
|
|
204
|
+
options.refresh ??= c[1]?.refresh;
|
|
205
|
+
}
|
|
206
|
+
options.where = wrap({ $or });
|
|
207
|
+
const r = await em.getEntityLoader().findChildrenFromPivotTable(owners, prop, options, orderBy, populate, group[0][1]?.ref);
|
|
208
|
+
ret.push(...r);
|
|
209
|
+
}
|
|
210
|
+
return ret;
|
|
211
|
+
};
|
|
212
|
+
}
|
|
196
213
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { EntityData, EntityDictionary, EntityMetadata, EntityProperty, IMetadataStorage } from '../typings.js';
|
|
2
2
|
import type { Platform } from '../platforms/Platform.js';
|
|
3
|
-
type Comparator<T> = (a: T, b: T
|
|
3
|
+
type Comparator<T> = (a: T, b: T, options?: {
|
|
4
|
+
includeInverseSides?: boolean;
|
|
5
|
+
}) => EntityData<T>;
|
|
4
6
|
type ResultMapper<T> = (result: EntityData<T>) => EntityData<T> | null;
|
|
5
7
|
type SnapshotGenerator<T> = (entity: T) => EntityData<T>;
|
|
6
8
|
type CompositeKeyPart = string | CompositeKeyPart[];
|
|
@@ -18,7 +20,9 @@ export declare class EntityComparator {
|
|
|
18
20
|
/**
|
|
19
21
|
* Computes difference between two entities.
|
|
20
22
|
*/
|
|
21
|
-
diffEntities<T>(entityName: string, a: EntityData<T>, b: EntityData<T
|
|
23
|
+
diffEntities<T>(entityName: string, a: EntityData<T>, b: EntityData<T>, options?: {
|
|
24
|
+
includeInverseSides?: boolean;
|
|
25
|
+
}): EntityData<T>;
|
|
22
26
|
matching<T>(entityName: string, a: EntityData<T>, b: EntityData<T>): boolean;
|
|
23
27
|
/**
|
|
24
28
|
* Removes ORM specific code from entities and prepares it for serializing. Used before change set computation.
|
|
@@ -20,9 +20,9 @@ export class EntityComparator {
|
|
|
20
20
|
/**
|
|
21
21
|
* Computes difference between two entities.
|
|
22
22
|
*/
|
|
23
|
-
diffEntities(entityName, a, b) {
|
|
23
|
+
diffEntities(entityName, a, b, options) {
|
|
24
24
|
const comparator = this.getEntityComparator(entityName);
|
|
25
|
-
return Utils.callCompiledFunction(comparator, a, b);
|
|
25
|
+
return Utils.callCompiledFunction(comparator, a, b, options);
|
|
26
26
|
}
|
|
27
27
|
matching(entityName, a, b) {
|
|
28
28
|
const diff = this.diffEntities(entityName, a, b);
|
|
@@ -155,6 +155,7 @@ export class EntityComparator {
|
|
|
155
155
|
const lines = [];
|
|
156
156
|
const context = new Map();
|
|
157
157
|
context.set('getCompositeKeyValue', (val) => Utils.flatten(Utils.getCompositeKeyValue(val, meta, 'convertToDatabaseValue', this.platform)));
|
|
158
|
+
context.set('getPrimaryKeyHash', (val) => Utils.getPrimaryKeyHash(Utils.asArray(val)));
|
|
158
159
|
if (meta.primaryKeys.length > 1) {
|
|
159
160
|
lines.push(` const pks = entity.__helper.__pk ? getCompositeKeyValue(entity.__helper.__pk) : [`);
|
|
160
161
|
meta.primaryKeys.forEach(pk => {
|
|
@@ -170,14 +171,23 @@ export class EntityComparator {
|
|
|
170
171
|
}
|
|
171
172
|
else {
|
|
172
173
|
const pk = meta.primaryKeys[0];
|
|
173
|
-
|
|
174
|
+
const prop = meta.properties[pk];
|
|
175
|
+
if (prop.kind !== ReferenceKind.SCALAR) {
|
|
174
176
|
lines.push(` if (entity${this.wrap(pk)} != null && (entity${this.wrap(pk)}.__entity || entity${this.wrap(pk)}.__reference)) return entity${this.wrap(pk)}.__helper.getSerializedPrimaryKey();`);
|
|
175
177
|
}
|
|
176
178
|
const serializedPrimaryKey = meta.props.find(p => p.serializedPrimaryKey);
|
|
177
179
|
if (serializedPrimaryKey) {
|
|
178
180
|
lines.push(` return '' + entity.${serializedPrimaryKey.name};`);
|
|
179
181
|
}
|
|
180
|
-
|
|
182
|
+
else if (prop.customType) {
|
|
183
|
+
const convertorKey = this.registerCustomType(meta.properties[pk], context);
|
|
184
|
+
const idx = this.tmpIndex++;
|
|
185
|
+
lines.push(` const val_${idx} = convertToDatabaseValue_${convertorKey}(entity${this.wrap(pk)});`);
|
|
186
|
+
lines.push(` return getPrimaryKeyHash(val_${idx});`);
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
lines.push(` return '' + entity${this.wrap(pk)};`);
|
|
190
|
+
}
|
|
181
191
|
}
|
|
182
192
|
const code = `// compiled pk serializer for entity ${meta.className}\n`
|
|
183
193
|
+ `return function(entity) {\n${lines.join('\n')}\n}`;
|
|
@@ -531,11 +541,22 @@ export class EntityComparator {
|
|
|
531
541
|
context.set('compareBuffers', compareBuffers);
|
|
532
542
|
context.set('compareObjects', compareObjects);
|
|
533
543
|
context.set('equals', equals);
|
|
534
|
-
meta.comparableProps
|
|
535
|
-
|
|
536
|
-
|
|
544
|
+
for (const prop of meta.comparableProps) {
|
|
545
|
+
// skip properties that are not hydrated
|
|
546
|
+
if (prop.hydrate !== false) {
|
|
547
|
+
lines.push(this.getPropertyComparator(prop, context));
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
// also compare 1:1 inverse sides, important for `factory.mergeData`
|
|
551
|
+
lines.push(`if (options?.includeInverseSides) {`);
|
|
552
|
+
for (const prop of meta.bidirectionalRelations) {
|
|
553
|
+
if (prop.kind === ReferenceKind.ONE_TO_ONE && !prop.owner && prop.hydrate !== false) {
|
|
554
|
+
lines.push(this.getPropertyComparator(prop, context));
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
lines.push(`}`);
|
|
537
558
|
const code = `// compiled comparator for entity ${meta.className}\n`
|
|
538
|
-
+ `return function(last, current) {\n const diff = {};\n${lines.join('\n')}\n return diff;\n}`;
|
|
559
|
+
+ `return function(last, current, options) {\n const diff = {};\n${lines.join('\n')}\n return diff;\n}`;
|
|
539
560
|
const comparator = Utils.createFunction(context, code);
|
|
540
561
|
this.comparators.set(entityName, comparator);
|
|
541
562
|
return comparator;
|
package/utils/QueryHelper.d.ts
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import type { Dictionary, EntityMetadata, EntityProperty, FilterDef, FilterQuery } from '../typings.js';
|
|
2
2
|
import type { Platform } from '../platforms/Platform.js';
|
|
3
3
|
import type { MetadataStorage } from '../metadata/MetadataStorage.js';
|
|
4
|
+
/** @internal */
|
|
4
5
|
export declare class QueryHelper {
|
|
5
6
|
static readonly SUPPORTED_OPERATORS: string[];
|
|
6
7
|
static processParams(params: unknown): any;
|
|
7
8
|
static processObjectParams<T extends object>(params?: T): T;
|
|
9
|
+
/**
|
|
10
|
+
* converts `{ account: { $or: [ [Object], [Object] ] } }`
|
|
11
|
+
* to `{ $or: [ { account: [Object] }, { account: [Object] } ] }`
|
|
12
|
+
*/
|
|
13
|
+
static liftGroupOperators<T extends object>(where: Dictionary, meta: EntityMetadata<T>, metadata: MetadataStorage, key?: string): string | undefined;
|
|
8
14
|
static inlinePrimaryKeyObjects<T extends object>(where: Dictionary, meta: EntityMetadata<T>, metadata: MetadataStorage, key?: string): boolean;
|
|
9
15
|
static processWhere<T extends object>(options: ProcessWhereOptions<T>): FilterQuery<T>;
|
|
10
16
|
static getActiveFilters(entityName: string, options: Dictionary<boolean | Dictionary> | string[] | boolean, filters: Dictionary<FilterDef>): FilterDef[];
|