@mikro-orm/core 7.0.0-rc.2 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/EntityManager.d.ts +4 -16
- package/EntityManager.js +248 -181
- package/MikroORM.d.ts +4 -6
- package/MikroORM.js +24 -24
- package/README.md +5 -4
- package/cache/FileCacheAdapter.d.ts +1 -5
- package/cache/FileCacheAdapter.js +22 -24
- package/cache/GeneratedCacheAdapter.d.ts +1 -1
- package/cache/GeneratedCacheAdapter.js +6 -6
- package/cache/MemoryCacheAdapter.d.ts +1 -2
- package/cache/MemoryCacheAdapter.js +8 -8
- package/cache/index.d.ts +1 -1
- package/cache/index.js +0 -1
- package/connections/Connection.d.ts +1 -0
- package/connections/Connection.js +43 -14
- package/drivers/DatabaseDriver.d.ts +0 -2
- package/drivers/DatabaseDriver.js +28 -12
- package/drivers/IDatabaseDriver.d.ts +43 -0
- package/entity/Collection.d.ts +1 -9
- package/entity/Collection.js +124 -108
- package/entity/EntityAssigner.js +23 -11
- package/entity/EntityFactory.d.ts +1 -8
- package/entity/EntityFactory.js +79 -59
- package/entity/EntityHelper.js +25 -16
- package/entity/EntityLoader.d.ts +1 -3
- package/entity/EntityLoader.js +90 -60
- package/entity/Reference.d.ts +2 -3
- package/entity/Reference.js +48 -19
- package/entity/WrappedEntity.d.ts +4 -2
- package/entity/WrappedEntity.js +5 -1
- package/entity/defineEntity.d.ts +42 -85
- package/entity/utils.js +28 -26
- package/entity/validators.js +2 -1
- package/enums.d.ts +2 -1
- package/enums.js +13 -17
- package/errors.d.ts +11 -11
- package/errors.js +8 -8
- package/events/EventManager.d.ts +1 -4
- package/events/EventManager.js +26 -23
- package/events/index.d.ts +1 -1
- package/events/index.js +0 -1
- package/exceptions.js +9 -2
- package/hydration/ObjectHydrator.d.ts +1 -2
- package/hydration/ObjectHydrator.js +41 -27
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/logging/DefaultLogger.js +6 -7
- package/logging/Logger.d.ts +2 -1
- package/logging/colors.js +2 -5
- package/logging/index.d.ts +1 -1
- package/logging/index.js +0 -1
- package/metadata/EntitySchema.d.ts +3 -3
- package/metadata/EntitySchema.js +12 -2
- package/metadata/MetadataDiscovery.d.ts +1 -9
- package/metadata/MetadataDiscovery.js +251 -179
- package/metadata/MetadataProvider.js +26 -1
- package/metadata/MetadataStorage.d.ts +1 -5
- package/metadata/MetadataStorage.js +37 -39
- package/metadata/MetadataValidator.js +20 -5
- package/metadata/discover-entities.js +1 -1
- package/metadata/index.d.ts +1 -1
- package/metadata/index.js +0 -1
- package/metadata/types.d.ts +2 -2
- package/naming-strategy/AbstractNamingStrategy.js +6 -3
- package/naming-strategy/EntityCaseNamingStrategy.js +1 -1
- package/naming-strategy/index.d.ts +1 -1
- package/naming-strategy/index.js +0 -1
- package/not-supported.js +5 -1
- package/package.json +38 -38
- package/platforms/Platform.d.ts +24 -1
- package/platforms/Platform.js +106 -27
- package/serialization/EntitySerializer.js +8 -4
- package/serialization/EntityTransformer.js +4 -1
- package/serialization/SerializationContext.d.ts +4 -8
- package/serialization/SerializationContext.js +21 -16
- package/types/UuidType.d.ts +2 -0
- package/types/UuidType.js +14 -2
- package/types/index.d.ts +2 -1
- package/typings.d.ts +35 -24
- package/typings.js +9 -9
- package/unit-of-work/ChangeSet.js +4 -4
- package/unit-of-work/ChangeSetComputer.d.ts +1 -6
- package/unit-of-work/ChangeSetComputer.js +29 -27
- package/unit-of-work/ChangeSetPersister.d.ts +1 -9
- package/unit-of-work/ChangeSetPersister.js +63 -58
- package/unit-of-work/CommitOrderCalculator.d.ts +1 -4
- package/unit-of-work/CommitOrderCalculator.js +17 -15
- package/unit-of-work/IdentityMap.d.ts +2 -5
- package/unit-of-work/IdentityMap.js +18 -18
- package/unit-of-work/UnitOfWork.d.ts +12 -20
- package/unit-of-work/UnitOfWork.js +228 -191
- package/utils/AbstractMigrator.d.ts +2 -2
- package/utils/AbstractMigrator.js +10 -12
- package/utils/AbstractSchemaGenerator.js +2 -1
- package/utils/AsyncContext.js +1 -1
- package/utils/Configuration.d.ts +90 -189
- package/utils/Configuration.js +97 -77
- package/utils/Cursor.d.ts +3 -3
- package/utils/Cursor.js +8 -6
- package/utils/DataloaderUtils.js +15 -12
- package/utils/EntityComparator.d.ts +8 -15
- package/utils/EntityComparator.js +100 -92
- package/utils/QueryHelper.d.ts +16 -1
- package/utils/QueryHelper.js +108 -50
- package/utils/RawQueryFragment.d.ts +4 -4
- package/utils/RawQueryFragment.js +3 -2
- package/utils/TransactionManager.js +3 -3
- package/utils/Utils.d.ts +2 -2
- package/utils/Utils.js +39 -32
- package/utils/clone.js +5 -0
- package/utils/env-vars.js +6 -5
- package/utils/fs-utils.d.ts +3 -17
- package/utils/fs-utils.js +2 -5
- package/utils/upsert-utils.js +7 -4
package/entity/EntityLoader.js
CHANGED
|
@@ -7,13 +7,13 @@ import { helper } from './wrap.js';
|
|
|
7
7
|
import { expandDotPaths } from './utils.js';
|
|
8
8
|
import { Raw } from '../utils/RawQueryFragment.js';
|
|
9
9
|
export class EntityLoader {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
#metadata;
|
|
11
|
+
#driver;
|
|
12
|
+
#em;
|
|
13
13
|
constructor(em) {
|
|
14
|
-
this
|
|
15
|
-
this
|
|
16
|
-
this
|
|
14
|
+
this.#em = em;
|
|
15
|
+
this.#metadata = this.#em.getMetadata();
|
|
16
|
+
this.#driver = this.#em.getDriver();
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
19
|
* Loads specified relations in batch.
|
|
@@ -23,13 +23,13 @@ export class EntityLoader {
|
|
|
23
23
|
if (entities.length === 0 || Utils.isEmpty(populate)) {
|
|
24
24
|
return this.setSerializationContext(entities, populate, options);
|
|
25
25
|
}
|
|
26
|
-
const meta = this
|
|
26
|
+
const meta = this.#metadata.find(entityName);
|
|
27
27
|
if (entities.some(e => !e.__helper)) {
|
|
28
28
|
const entity = entities.find(e => !Utils.isEntity(e));
|
|
29
29
|
throw ValidationError.notDiscoveredEntity(entity, meta, 'populate');
|
|
30
30
|
}
|
|
31
31
|
const references = entities.filter(e => !helper(e).isInitialized());
|
|
32
|
-
const visited = options.visited ??= new Set();
|
|
32
|
+
const visited = (options.visited ??= new Set());
|
|
33
33
|
options.where ??= {};
|
|
34
34
|
options.orderBy ??= {};
|
|
35
35
|
options.lookup ??= true;
|
|
@@ -40,7 +40,7 @@ export class EntityLoader {
|
|
|
40
40
|
await this.populateScalar(meta, references, { ...options, populateWhere: undefined });
|
|
41
41
|
}
|
|
42
42
|
populate = this.normalizePopulate(entityName, populate, options.strategy, options.lookup, options.exclude);
|
|
43
|
-
const invalid = populate.find(({ field }) => !this
|
|
43
|
+
const invalid = populate.find(({ field }) => !this.#em.canPopulate(entityName, field));
|
|
44
44
|
/* v8 ignore next */
|
|
45
45
|
if (options.validate && invalid) {
|
|
46
46
|
throw ValidationError.invalidPropertyName(entityName, invalid.field);
|
|
@@ -57,8 +57,9 @@ export class EntityLoader {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
normalizePopulate(entityName, populate, strategy, lookup = true, exclude) {
|
|
60
|
-
const meta = this
|
|
60
|
+
const meta = this.#metadata.find(entityName);
|
|
61
61
|
let normalized = Utils.asArray(populate).map(field => {
|
|
62
|
+
// oxfmt-ignore
|
|
62
63
|
return typeof field === 'boolean' || field.field === PopulatePath.ALL ? { all: !!field, field: meta.primaryKeys[0] } : field;
|
|
63
64
|
});
|
|
64
65
|
if (normalized.some(p => p.all)) {
|
|
@@ -89,6 +90,7 @@ export class EntityLoader {
|
|
|
89
90
|
*/
|
|
90
91
|
mergeNestedPopulate(populate) {
|
|
91
92
|
const tmp = populate.reduce((ret, item) => {
|
|
93
|
+
/* v8 ignore next */
|
|
92
94
|
if (item.field === PopulatePath.ALL) {
|
|
93
95
|
return ret;
|
|
94
96
|
}
|
|
@@ -116,16 +118,17 @@ export class EntityLoader {
|
|
|
116
118
|
*/
|
|
117
119
|
async populateMany(entityName, entities, populate, options) {
|
|
118
120
|
const [field, ref] = populate.field.split(':', 2);
|
|
119
|
-
const meta = this
|
|
121
|
+
const meta = this.#metadata.find(entityName);
|
|
120
122
|
const prop = meta.properties[field];
|
|
121
|
-
if (prop.kind === ReferenceKind.MANY_TO_MANY && prop.owner && !this
|
|
123
|
+
if (prop.kind === ReferenceKind.MANY_TO_MANY && prop.owner && !this.#driver.getPlatform().usesPivotTable()) {
|
|
122
124
|
const filtered = entities.filter(e => !e[prop.name]?.isInitialized());
|
|
123
125
|
if (filtered.length > 0) {
|
|
124
126
|
await this.populateScalar(meta, filtered, { ...options, fields: [prop.name] });
|
|
125
127
|
}
|
|
126
128
|
}
|
|
127
129
|
if (prop.kind === ReferenceKind.SCALAR && prop.lazy) {
|
|
128
|
-
const filtered = entities.filter(e => options.refresh ||
|
|
130
|
+
const filtered = entities.filter(e => options.refresh ||
|
|
131
|
+
(prop.ref ? !e[prop.name]?.isInitialized() : e[prop.name] === undefined));
|
|
129
132
|
if (options.ignoreLazyScalarProperties || filtered.length === 0) {
|
|
130
133
|
return entities;
|
|
131
134
|
}
|
|
@@ -137,10 +140,11 @@ export class EntityLoader {
|
|
|
137
140
|
}
|
|
138
141
|
const filtered = this.filterCollections(entities, field, options, ref);
|
|
139
142
|
const innerOrderBy = Utils.asArray(options.orderBy)
|
|
140
|
-
.filter(orderBy => (Array.isArray(orderBy[prop.name]) && orderBy[prop.name].length > 0) ||
|
|
143
|
+
.filter(orderBy => (Array.isArray(orderBy[prop.name]) && orderBy[prop.name].length > 0) ||
|
|
144
|
+
Utils.isObject(orderBy[prop.name]))
|
|
141
145
|
.flatMap(orderBy => orderBy[prop.name]);
|
|
142
146
|
const where = await this.extractChildCondition(options, prop);
|
|
143
|
-
if (prop.kind === ReferenceKind.MANY_TO_MANY && this
|
|
147
|
+
if (prop.kind === ReferenceKind.MANY_TO_MANY && this.#driver.getPlatform().usesPivotTable()) {
|
|
144
148
|
const pivotOrderBy = QueryHelper.mergeOrderBy(innerOrderBy, prop.orderBy, prop.targetMeta?.orderBy);
|
|
145
149
|
const res = await this.findChildrenFromPivotTable(filtered, prop, options, pivotOrderBy, populate, !!ref);
|
|
146
150
|
return Utils.flatten(res);
|
|
@@ -160,16 +164,22 @@ export class EntityLoader {
|
|
|
160
164
|
async populateScalar(meta, filtered, options) {
|
|
161
165
|
const pk = Utils.getPrimaryKeyHash(meta.primaryKeys);
|
|
162
166
|
const ids = Utils.unique(filtered.map(e => Utils.getPrimaryKeyValues(e, meta, true)));
|
|
163
|
-
const where = this.mergePrimaryCondition(ids, pk, options, meta, this
|
|
167
|
+
const where = this.mergePrimaryCondition(ids, pk, options, meta, this.#metadata, this.#driver.getPlatform());
|
|
164
168
|
const { filters, convertCustomTypes, lockMode, strategy, populateWhere, connectionType, logging, fields } = options;
|
|
165
|
-
await this
|
|
166
|
-
filters,
|
|
169
|
+
await this.#em.find(meta.class, where, {
|
|
170
|
+
filters,
|
|
171
|
+
convertCustomTypes,
|
|
172
|
+
lockMode,
|
|
173
|
+
strategy,
|
|
174
|
+
populateWhere,
|
|
175
|
+
connectionType,
|
|
176
|
+
logging,
|
|
167
177
|
fields: fields,
|
|
168
178
|
populate: [],
|
|
169
179
|
});
|
|
170
180
|
}
|
|
171
181
|
async populatePolymorphic(entities, prop, options, ref) {
|
|
172
|
-
const ownerMeta = this
|
|
182
|
+
const ownerMeta = this.#metadata.get(entities[0].constructor);
|
|
173
183
|
// Separate entities: those with loaded refs vs those needing FK load
|
|
174
184
|
const toPopulate = [];
|
|
175
185
|
const needsFkLoad = [];
|
|
@@ -220,7 +230,7 @@ export class EntityLoader {
|
|
|
220
230
|
// Load each group concurrently - identity map handles merging with existing references
|
|
221
231
|
const allItems = [];
|
|
222
232
|
await Promise.all([...groups].map(async ([discriminator, children]) => {
|
|
223
|
-
const targetMeta = this
|
|
233
|
+
const targetMeta = this.#metadata.find(prop.discriminatorMap[discriminator]);
|
|
224
234
|
await this.populateScalar(targetMeta, children, options);
|
|
225
235
|
allItems.push(...children);
|
|
226
236
|
}));
|
|
@@ -230,7 +240,7 @@ export class EntityLoader {
|
|
|
230
240
|
if (prop.kind === ReferenceKind.ONE_TO_MANY) {
|
|
231
241
|
this.initializeOneToMany(filtered, children, prop, field, partial);
|
|
232
242
|
}
|
|
233
|
-
if (prop.kind === ReferenceKind.MANY_TO_MANY && !this
|
|
243
|
+
if (prop.kind === ReferenceKind.MANY_TO_MANY && !this.#driver.getPlatform().usesPivotTable()) {
|
|
234
244
|
this.initializeManyToMany(filtered, children, prop, field, customOrder, partial);
|
|
235
245
|
}
|
|
236
246
|
}
|
|
@@ -244,7 +254,7 @@ export class EntityLoader {
|
|
|
244
254
|
for (const child of children) {
|
|
245
255
|
const pk = child.__helper.__data[prop.mappedBy] ?? child[prop.mappedBy];
|
|
246
256
|
if (pk) {
|
|
247
|
-
const key = helper(mapToPk ? this
|
|
257
|
+
const key = helper(mapToPk ? this.#em.getReference(prop.targetMeta.class, pk) : pk).getSerializedPrimaryKey();
|
|
248
258
|
map[key]?.push(child);
|
|
249
259
|
}
|
|
250
260
|
}
|
|
@@ -260,7 +270,8 @@ export class EntityLoader {
|
|
|
260
270
|
entity[field].hydrate(items, true, partial);
|
|
261
271
|
}
|
|
262
272
|
}
|
|
263
|
-
else {
|
|
273
|
+
else {
|
|
274
|
+
// owning side of M:N without pivot table needs to be reordered
|
|
264
275
|
for (const entity of filtered) {
|
|
265
276
|
const order = !customOrder ? [...entity[prop.name].getItems(false)] : []; // copy order of references
|
|
266
277
|
const items = children.filter(child => entity[prop.name].contains(child, false));
|
|
@@ -301,7 +312,7 @@ export class EntityLoader {
|
|
|
301
312
|
if (!schema && [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind)) {
|
|
302
313
|
schema = children.find(e => e.__helper.__schema)?.__helper.__schema;
|
|
303
314
|
}
|
|
304
|
-
const ids = Utils.unique(children.map(e => prop.targetKey ? e[prop.targetKey] : e.__helper.getPrimaryKey()));
|
|
315
|
+
const ids = Utils.unique(children.map(e => (prop.targetKey ? e[prop.targetKey] : e.__helper.getPrimaryKey())));
|
|
305
316
|
let where;
|
|
306
317
|
if (polymorphicOwnerProp && Array.isArray(fk)) {
|
|
307
318
|
const conditions = ids.map(id => {
|
|
@@ -311,11 +322,12 @@ export class EntityLoader {
|
|
|
311
322
|
where = (conditions.length === 1 ? conditions[0] : { $or: conditions });
|
|
312
323
|
}
|
|
313
324
|
else {
|
|
314
|
-
where = this.mergePrimaryCondition(ids, fk, options, meta, this
|
|
325
|
+
where = this.mergePrimaryCondition(ids, fk, options, meta, this.#metadata, this.#driver.getPlatform());
|
|
315
326
|
}
|
|
316
327
|
if (polymorphicOwnerProp) {
|
|
317
|
-
const parentMeta = this
|
|
318
|
-
const discriminatorValue = QueryHelper.findDiscriminatorValue(polymorphicOwnerProp.discriminatorMap, parentMeta.class) ??
|
|
328
|
+
const parentMeta = this.#metadata.find(entities[0].constructor);
|
|
329
|
+
const discriminatorValue = QueryHelper.findDiscriminatorValue(polymorphicOwnerProp.discriminatorMap, parentMeta.class) ??
|
|
330
|
+
parentMeta.tableName;
|
|
319
331
|
const discriminatorColumn = polymorphicOwnerProp.fieldNames[0];
|
|
320
332
|
where = { $and: [where, { [discriminatorColumn]: discriminatorValue }] };
|
|
321
333
|
}
|
|
@@ -330,12 +342,21 @@ export class EntityLoader {
|
|
|
330
342
|
where = { $and: [where, prop.where] };
|
|
331
343
|
}
|
|
332
344
|
const orderBy = QueryHelper.mergeOrderBy(options.orderBy, prop.orderBy);
|
|
333
|
-
const items = await this
|
|
334
|
-
filters,
|
|
345
|
+
const items = await this.#em.find(meta.class, where, {
|
|
346
|
+
filters,
|
|
347
|
+
convertCustomTypes,
|
|
348
|
+
lockMode,
|
|
349
|
+
populateWhere,
|
|
350
|
+
logging,
|
|
335
351
|
orderBy,
|
|
336
352
|
populate: populate.children ?? populate.all ?? [],
|
|
337
|
-
exclude: Array.isArray(options.exclude)
|
|
338
|
-
|
|
353
|
+
exclude: Array.isArray(options.exclude)
|
|
354
|
+
? Utils.extractChildElements(options.exclude, prop.name)
|
|
355
|
+
: options.exclude,
|
|
356
|
+
strategy,
|
|
357
|
+
fields,
|
|
358
|
+
schema,
|
|
359
|
+
connectionType,
|
|
339
360
|
// @ts-ignore not a public option, will be propagated to the populate call
|
|
340
361
|
refresh: refresh && !children.every(item => options.visited.has(item)),
|
|
341
362
|
// @ts-ignore not a public option, will be propagated to the populate call
|
|
@@ -355,6 +376,7 @@ export class EntityLoader {
|
|
|
355
376
|
if (!ref) {
|
|
356
377
|
continue;
|
|
357
378
|
}
|
|
379
|
+
// oxfmt-ignore
|
|
358
380
|
const keyValue = '' + (Reference.isReference(ref) ? ref.unwrap()[prop.targetKey] : ref[prop.targetKey]);
|
|
359
381
|
const loadedItem = itemsByKey.get(keyValue);
|
|
360
382
|
if (loadedItem) {
|
|
@@ -363,11 +385,11 @@ export class EntityLoader {
|
|
|
363
385
|
}
|
|
364
386
|
}
|
|
365
387
|
if ([ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind) && items.length !== children.length) {
|
|
366
|
-
const nullVal = this
|
|
388
|
+
const nullVal = this.#em.config.get('forceUndefined') ? undefined : null;
|
|
367
389
|
const itemsMap = new Set();
|
|
368
390
|
const childrenMap = new Set();
|
|
369
391
|
// Use targetKey value if set, otherwise use serialized PK
|
|
370
|
-
const getKey = (e) => prop.targetKey ? '' + e[prop.targetKey] : helper(e).getSerializedPrimaryKey();
|
|
392
|
+
const getKey = (e) => (prop.targetKey ? '' + e[prop.targetKey] : helper(e).getSerializedPrimaryKey());
|
|
371
393
|
for (const item of items) {
|
|
372
394
|
/* v8 ignore next */
|
|
373
395
|
itemsMap.add(getKey(item));
|
|
@@ -387,23 +409,26 @@ export class EntityLoader {
|
|
|
387
409
|
for (const item of items) {
|
|
388
410
|
if (ref && !helper(item).__onLoadFired) {
|
|
389
411
|
helper(item).__initialized = false;
|
|
390
|
-
|
|
391
|
-
this.em.getUnitOfWork()['loadedEntities'].delete(item);
|
|
412
|
+
this.#em.getUnitOfWork().unmarkAsLoaded(item);
|
|
392
413
|
}
|
|
393
414
|
}
|
|
394
415
|
return { items, partial };
|
|
395
416
|
}
|
|
396
417
|
mergePrimaryCondition(ids, pk, options, meta, metadata, platform) {
|
|
397
|
-
const cond1 = QueryHelper.processWhere({
|
|
418
|
+
const cond1 = QueryHelper.processWhere({
|
|
419
|
+
where: { [pk]: { $in: ids } },
|
|
420
|
+
entityName: meta.class,
|
|
421
|
+
metadata,
|
|
422
|
+
platform,
|
|
423
|
+
convertCustomTypes: !options.convertCustomTypes,
|
|
424
|
+
});
|
|
398
425
|
const where = { ...options.where };
|
|
399
426
|
Utils.dropUndefinedProperties(where);
|
|
400
|
-
return where[pk]
|
|
401
|
-
? { $and: [cond1, where] }
|
|
402
|
-
: { ...cond1, ...where };
|
|
427
|
+
return where[pk] ? { $and: [cond1, where] } : { ...cond1, ...where };
|
|
403
428
|
}
|
|
404
429
|
async populateField(entityName, entities, populate, options) {
|
|
405
430
|
const field = populate.field.split(':')[0];
|
|
406
|
-
const prop = this
|
|
431
|
+
const prop = this.#metadata.find(entityName).properties[field];
|
|
407
432
|
if (prop.kind === ReferenceKind.SCALAR && !prop.lazy) {
|
|
408
433
|
return;
|
|
409
434
|
}
|
|
@@ -436,6 +461,7 @@ export class EntityLoader {
|
|
|
436
461
|
.filter(orderBy => Utils.isObject(orderBy[prop.name]))
|
|
437
462
|
.map(orderBy => orderBy[prop.name]);
|
|
438
463
|
const { refresh, filters, ignoreLazyScalarProperties, populateWhere, connectionType, logging, schema } = options;
|
|
464
|
+
// oxfmt-ignore
|
|
439
465
|
const exclude = Array.isArray(options.exclude) ? Utils.extractChildElements(options.exclude, prop.name) : options.exclude;
|
|
440
466
|
const visited = options.visited;
|
|
441
467
|
for (const entity of entities) {
|
|
@@ -451,7 +477,7 @@ export class EntityLoader {
|
|
|
451
477
|
}
|
|
452
478
|
const populateChildren = async (targetMeta, items) => {
|
|
453
479
|
await this.populate(targetMeta.class, items, populate.children ?? populate.all, {
|
|
454
|
-
where: await this.extractChildCondition(options, prop, false),
|
|
480
|
+
where: (await this.extractChildCondition(options, prop, false)),
|
|
455
481
|
orderBy: innerOrderBy,
|
|
456
482
|
fields,
|
|
457
483
|
exclude,
|
|
@@ -489,34 +515,33 @@ export class EntityLoader {
|
|
|
489
515
|
const refresh = options.refresh;
|
|
490
516
|
let where = await this.extractChildCondition(options, prop, true);
|
|
491
517
|
const fields = this.buildFields(options.fields, prop);
|
|
518
|
+
// oxfmt-ignore
|
|
492
519
|
const exclude = Array.isArray(options.exclude) ? Utils.extractChildElements(options.exclude, prop.name) : options.exclude;
|
|
493
520
|
const populateFilter = options.populateFilter?.[prop.name];
|
|
494
521
|
const options2 = { ...options, fields, exclude, populateFilter };
|
|
495
522
|
['limit', 'offset', 'first', 'last', 'before', 'after', 'overfetch'].forEach(prop => delete options2[prop]);
|
|
496
|
-
options2.populate =
|
|
497
|
-
if (prop.customType) {
|
|
498
|
-
ids.forEach((id, idx) => ids[idx] = QueryHelper.processCustomType(prop, id, this.driver.getPlatform()));
|
|
499
|
-
}
|
|
523
|
+
options2.populate = populate?.children ?? [];
|
|
500
524
|
if (!Utils.isEmpty(prop.where)) {
|
|
501
525
|
where = { $and: [where, prop.where] };
|
|
502
526
|
}
|
|
503
|
-
const map = await this
|
|
527
|
+
const map = await this.#driver.loadFromPivotTable(prop, ids, where, orderBy, this.#em.getTransactionContext(), options2, pivotJoin);
|
|
504
528
|
const children = [];
|
|
505
|
-
for (
|
|
506
|
-
const
|
|
529
|
+
for (let i = 0; i < filtered.length; i++) {
|
|
530
|
+
const entity = filtered[i];
|
|
531
|
+
const items = map[Utils.getPrimaryKeyHash(ids[i])].map(item => {
|
|
507
532
|
if (pivotJoin) {
|
|
508
|
-
return this
|
|
533
|
+
return this.#em.getReference(prop.targetMeta.class, item, {
|
|
509
534
|
convertCustomTypes: true,
|
|
510
|
-
schema: options.schema ?? this
|
|
535
|
+
schema: options.schema ?? this.#em.config.get('schema'),
|
|
511
536
|
});
|
|
512
537
|
}
|
|
513
|
-
const entity = this
|
|
538
|
+
const entity = this.#em.getEntityFactory().create(prop.targetMeta.class, item, {
|
|
514
539
|
refresh,
|
|
515
540
|
merge: true,
|
|
516
541
|
convertCustomTypes: true,
|
|
517
|
-
schema: options.schema ?? this
|
|
542
|
+
schema: options.schema ?? this.#em.config.get('schema'),
|
|
518
543
|
});
|
|
519
|
-
return this
|
|
544
|
+
return this.#em.getUnitOfWork().register(entity, item, { refresh, loaded: true });
|
|
520
545
|
});
|
|
521
546
|
entity[prop.name].hydrate(items, true);
|
|
522
547
|
children.push(items);
|
|
@@ -532,7 +557,8 @@ export class EntityLoader {
|
|
|
532
557
|
if (where[op]) {
|
|
533
558
|
const child = where[op]
|
|
534
559
|
.map((cond) => cond[prop.name])
|
|
535
|
-
.filter((sub) => sub != null &&
|
|
560
|
+
.filter((sub) => sub != null &&
|
|
561
|
+
!(Utils.isPlainObject(sub) && Utils.getObjectQueryKeys(sub).every(key => Utils.isOperator(key, false))))
|
|
536
562
|
.map((cond) => {
|
|
537
563
|
if (Utils.isPrimaryKey(cond)) {
|
|
538
564
|
return { [pk]: cond };
|
|
@@ -553,7 +579,7 @@ export class EntityLoader {
|
|
|
553
579
|
});
|
|
554
580
|
}
|
|
555
581
|
if (filters) {
|
|
556
|
-
return this
|
|
582
|
+
return this.#em.applyFilters(meta2.class, subCond, options.filters, 'read', options);
|
|
557
583
|
}
|
|
558
584
|
return subCond;
|
|
559
585
|
}
|
|
@@ -603,7 +629,8 @@ export class EntityLoader {
|
|
|
603
629
|
return a;
|
|
604
630
|
}, []);
|
|
605
631
|
}
|
|
606
|
-
if (prop.kind === ReferenceKind.MANY_TO_MANY) {
|
|
632
|
+
if (prop.kind === ReferenceKind.MANY_TO_MANY) {
|
|
633
|
+
// inverse side
|
|
607
634
|
return filtered;
|
|
608
635
|
}
|
|
609
636
|
// MANY_TO_ONE or ONE_TO_ONE
|
|
@@ -665,7 +692,7 @@ export class EntityLoader {
|
|
|
665
692
|
}
|
|
666
693
|
lookupAllRelationships(entityName) {
|
|
667
694
|
const ret = [];
|
|
668
|
-
const meta = this
|
|
695
|
+
const meta = this.#metadata.find(entityName);
|
|
669
696
|
meta.relations.forEach(prop => {
|
|
670
697
|
ret.push({
|
|
671
698
|
field: this.getRelationName(meta, prop),
|
|
@@ -684,7 +711,7 @@ export class EntityLoader {
|
|
|
684
711
|
return `${this.getRelationName(meta, meta.properties[prop.embedded[0]])}.${prop.embedded[1]}`;
|
|
685
712
|
}
|
|
686
713
|
lookupEagerLoadedRelationships(entityName, populate, strategy, prefix = '', visited = [], exclude) {
|
|
687
|
-
const meta = this
|
|
714
|
+
const meta = this.#metadata.find(entityName);
|
|
688
715
|
if (!meta && !prefix) {
|
|
689
716
|
return populate;
|
|
690
717
|
}
|
|
@@ -706,7 +733,10 @@ export class EntityLoader {
|
|
|
706
733
|
.forEach(prop => {
|
|
707
734
|
const field = this.getRelationName(meta, prop);
|
|
708
735
|
const prefixed = prefix ? `${prefix}.${field}` : field;
|
|
709
|
-
const nestedPopulate = populate
|
|
736
|
+
const nestedPopulate = populate
|
|
737
|
+
.filter(p => p.field === prop.name)
|
|
738
|
+
.flatMap(p => p.children)
|
|
739
|
+
.filter(Boolean);
|
|
710
740
|
const nested = this.lookupEagerLoadedRelationships(prop.targetMeta.class, nestedPopulate, strategy, prefixed, visited.slice(), exclude);
|
|
711
741
|
if (nested.length > 0) {
|
|
712
742
|
ret.push(...nested);
|
|
@@ -716,7 +746,7 @@ export class EntityLoader {
|
|
|
716
746
|
ret.push({
|
|
717
747
|
field: prefixed,
|
|
718
748
|
// enforce select-in strategy for self-referencing relations
|
|
719
|
-
strategy: selfReferencing ? LoadStrategy.SELECT_IN : strategy ?? prop.strategy,
|
|
749
|
+
strategy: selfReferencing ? LoadStrategy.SELECT_IN : (strategy ?? prop.strategy),
|
|
720
750
|
});
|
|
721
751
|
}
|
|
722
752
|
});
|
package/entity/Reference.d.ts
CHANGED
|
@@ -44,10 +44,9 @@ export declare class Reference<T extends object> {
|
|
|
44
44
|
toJSON(...args: any[]): Dictionary;
|
|
45
45
|
}
|
|
46
46
|
export declare class ScalarReference<Value> {
|
|
47
|
+
#private;
|
|
47
48
|
private value?;
|
|
48
|
-
private initialized;
|
|
49
49
|
private entity?;
|
|
50
|
-
private property?;
|
|
51
50
|
constructor(value?: Value | undefined, initialized?: boolean);
|
|
52
51
|
/**
|
|
53
52
|
* Ensures the underlying entity is loaded first (without reloading it if it already is loaded).
|
|
@@ -73,7 +72,7 @@ export interface LoadReferenceOrFailOptions<T extends object, P extends string =
|
|
|
73
72
|
/**
|
|
74
73
|
* shortcut for `wrap(entity).toReference()`
|
|
75
74
|
*/
|
|
76
|
-
export declare function ref<I extends unknown | Ref<unknown> | undefined | null, T extends I & {}>(entity: I): Ref<T> & LoadedReference<Loaded<T, AddEager<T>>> | AddOptional<typeof entity>;
|
|
75
|
+
export declare function ref<I extends unknown | Ref<unknown> | undefined | null, T extends I & {}>(entity: I): (Ref<T> & LoadedReference<Loaded<T, AddEager<T>>>) | AddOptional<typeof entity>;
|
|
77
76
|
/**
|
|
78
77
|
* shortcut for `Reference.createFromPK(entityType, pk)`
|
|
79
78
|
*/
|
package/entity/Reference.js
CHANGED
|
@@ -93,7 +93,8 @@ export class Reference {
|
|
|
93
93
|
await wrapped.__em.populate(this.entity, options.populate, options);
|
|
94
94
|
}
|
|
95
95
|
if (!this.isInitialized() || options.refresh) {
|
|
96
|
-
if (options.dataloader ??
|
|
96
|
+
if (options.dataloader ??
|
|
97
|
+
[DataloaderType.ALL, DataloaderType.REFERENCE].includes(wrapped.__em.config.getDataloaderType())) {
|
|
97
98
|
const dataLoader = await wrapped.__em.getDataLoader('ref');
|
|
98
99
|
return dataLoader.load([this, options]);
|
|
99
100
|
}
|
|
@@ -161,12 +162,12 @@ export class Reference {
|
|
|
161
162
|
}
|
|
162
163
|
export class ScalarReference {
|
|
163
164
|
value;
|
|
164
|
-
initialized;
|
|
165
165
|
entity;
|
|
166
|
-
property;
|
|
166
|
+
#property;
|
|
167
|
+
#initialized;
|
|
167
168
|
constructor(value, initialized = value != null) {
|
|
168
169
|
this.value = value;
|
|
169
|
-
this
|
|
170
|
+
this.#initialized = initialized;
|
|
170
171
|
}
|
|
171
172
|
/**
|
|
172
173
|
* Ensures the underlying entity is loaded first (without reloading it if it already is loaded).
|
|
@@ -174,11 +175,11 @@ export class ScalarReference {
|
|
|
174
175
|
*/
|
|
175
176
|
async load(options) {
|
|
176
177
|
const opts = typeof options === 'object' ? options : { prop: options };
|
|
177
|
-
if (!this
|
|
178
|
-
if (this.entity == null || this
|
|
178
|
+
if (!this.#initialized || opts.refresh) {
|
|
179
|
+
if (this.entity == null || this.#property == null) {
|
|
179
180
|
throw new Error('Cannot load scalar reference that is not bound to an entity property.');
|
|
180
181
|
}
|
|
181
|
-
await helper(this.entity).populate([this
|
|
182
|
+
await helper(this.entity).populate([this.#property], opts);
|
|
182
183
|
}
|
|
183
184
|
return this.value;
|
|
184
185
|
}
|
|
@@ -192,43 +193,71 @@ export class ScalarReference {
|
|
|
192
193
|
const wrapped = helper(this.entity);
|
|
193
194
|
options.failHandler ??= wrapped.__em.config.get('findOneOrFailHandler');
|
|
194
195
|
const entityName = this.entity.constructor.name;
|
|
195
|
-
throw NotFoundError.failedToLoadProperty(entityName, this
|
|
196
|
+
throw NotFoundError.failedToLoadProperty(entityName, this.#property, wrapped.getPrimaryKey());
|
|
196
197
|
}
|
|
197
198
|
return ret;
|
|
198
199
|
}
|
|
199
200
|
set(value) {
|
|
200
201
|
this.value = value;
|
|
201
|
-
this
|
|
202
|
+
this.#initialized = true;
|
|
202
203
|
}
|
|
203
204
|
bind(entity, property) {
|
|
204
205
|
this.entity = entity;
|
|
205
|
-
this
|
|
206
|
+
this.#property = property;
|
|
206
207
|
Object.defineProperty(this, 'entity', { enumerable: false, value: entity });
|
|
207
208
|
}
|
|
208
209
|
unwrap() {
|
|
209
210
|
return this.value;
|
|
210
211
|
}
|
|
211
212
|
isInitialized() {
|
|
212
|
-
return this
|
|
213
|
+
return this.#initialized;
|
|
213
214
|
}
|
|
214
215
|
/** @ignore */
|
|
215
216
|
/* v8 ignore next */
|
|
216
217
|
[Symbol.for('nodejs.util.inspect.custom')]() {
|
|
217
|
-
return this
|
|
218
|
+
return this.#initialized ? `Ref<${inspect(this.value)}>` : `Ref<?>`;
|
|
218
219
|
}
|
|
219
220
|
}
|
|
220
221
|
Object.defineProperties(Reference.prototype, {
|
|
221
222
|
__reference: { value: true, enumerable: false },
|
|
222
|
-
__meta: {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
223
|
+
__meta: {
|
|
224
|
+
get() {
|
|
225
|
+
return this.entity.__meta;
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
__platform: {
|
|
229
|
+
get() {
|
|
230
|
+
return this.entity.__platform;
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
__helper: {
|
|
234
|
+
get() {
|
|
235
|
+
return this.entity.__helper;
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
$: {
|
|
239
|
+
get() {
|
|
240
|
+
return this.entity;
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
get: {
|
|
244
|
+
get() {
|
|
245
|
+
return () => this.entity;
|
|
246
|
+
},
|
|
247
|
+
},
|
|
227
248
|
});
|
|
228
249
|
Object.defineProperties(ScalarReference.prototype, {
|
|
229
250
|
__scalarReference: { value: true, enumerable: false },
|
|
230
|
-
$: {
|
|
231
|
-
|
|
251
|
+
$: {
|
|
252
|
+
get() {
|
|
253
|
+
return this.value;
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
get: {
|
|
257
|
+
get() {
|
|
258
|
+
return () => this.value;
|
|
259
|
+
},
|
|
260
|
+
},
|
|
232
261
|
});
|
|
233
262
|
/**
|
|
234
263
|
* shortcut for `wrap(entity).toReference()`
|
|
@@ -8,6 +8,8 @@ import type { EntityIdentifier } from './EntityIdentifier.js';
|
|
|
8
8
|
import type { SerializationContext } from '../serialization/SerializationContext.js';
|
|
9
9
|
import { type SerializeOptions } from '../serialization/EntitySerializer.js';
|
|
10
10
|
import type { FindOneOptions, LoadHint } from '../drivers/IDatabaseDriver.js';
|
|
11
|
+
import type { Platform } from '../platforms/Platform.js';
|
|
12
|
+
import type { Configuration } from '../utils/Configuration.js';
|
|
11
13
|
export declare class WrappedEntity<Entity extends object> {
|
|
12
14
|
__initialized: boolean;
|
|
13
15
|
__populated?: boolean;
|
|
@@ -58,7 +60,7 @@ export declare class WrappedEntity<Entity extends object> {
|
|
|
58
60
|
setPrimaryKey(id: Primary<Entity> | null): void;
|
|
59
61
|
getSerializedPrimaryKey(): string;
|
|
60
62
|
get __meta(): EntityMetadata<Entity>;
|
|
61
|
-
get __platform():
|
|
62
|
-
get __config():
|
|
63
|
+
get __platform(): Platform;
|
|
64
|
+
get __config(): Configuration;
|
|
63
65
|
get __primaryKeys(): Primary<Entity>[];
|
|
64
66
|
}
|
package/entity/WrappedEntity.js
CHANGED
|
@@ -71,7 +71,11 @@ export class WrappedEntity {
|
|
|
71
71
|
if (!this.__em) {
|
|
72
72
|
throw ValidationError.entityNotManaged(this.entity);
|
|
73
73
|
}
|
|
74
|
-
return this.__em.findOne(this.entity.constructor, this.entity, {
|
|
74
|
+
return this.__em.findOne(this.entity.constructor, this.entity, {
|
|
75
|
+
...options,
|
|
76
|
+
refresh: true,
|
|
77
|
+
schema: this.__schema,
|
|
78
|
+
});
|
|
75
79
|
}
|
|
76
80
|
async populate(populate, options = {}) {
|
|
77
81
|
if (!this.__em) {
|