@mikro-orm/core 7.0.0-dev.3 → 7.0.0-dev.300
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 +114 -63
- package/EntityManager.js +385 -310
- package/MikroORM.d.ts +44 -35
- package/MikroORM.js +109 -143
- package/README.md +3 -2
- package/cache/FileCacheAdapter.d.ts +1 -1
- package/cache/FileCacheAdapter.js +17 -8
- package/cache/GeneratedCacheAdapter.d.ts +0 -1
- package/cache/GeneratedCacheAdapter.js +0 -2
- package/cache/index.d.ts +0 -1
- package/cache/index.js +0 -1
- package/connections/Connection.d.ts +16 -7
- package/connections/Connection.js +23 -14
- package/drivers/DatabaseDriver.d.ts +25 -16
- package/drivers/DatabaseDriver.js +119 -36
- package/drivers/IDatabaseDriver.d.ts +125 -23
- package/entity/BaseEntity.d.ts +63 -4
- package/entity/BaseEntity.js +0 -3
- package/entity/Collection.d.ts +102 -31
- package/entity/Collection.js +446 -108
- package/entity/EntityAssigner.d.ts +1 -1
- package/entity/EntityAssigner.js +26 -18
- package/entity/EntityFactory.d.ts +13 -1
- package/entity/EntityFactory.js +106 -60
- package/entity/EntityHelper.d.ts +2 -2
- package/entity/EntityHelper.js +65 -20
- package/entity/EntityLoader.d.ts +13 -11
- package/entity/EntityLoader.js +257 -107
- package/entity/EntityRepository.d.ts +28 -8
- package/entity/EntityRepository.js +8 -2
- package/entity/PolymorphicRef.d.ts +12 -0
- package/entity/PolymorphicRef.js +18 -0
- package/entity/Reference.d.ts +9 -12
- package/entity/Reference.js +34 -9
- package/entity/WrappedEntity.d.ts +3 -8
- package/entity/WrappedEntity.js +3 -8
- package/entity/defineEntity.d.ts +753 -0
- package/entity/defineEntity.js +537 -0
- package/entity/index.d.ts +4 -2
- package/entity/index.js +4 -2
- package/entity/utils.d.ts +13 -1
- package/entity/utils.js +49 -4
- package/entity/validators.d.ts +11 -0
- package/entity/validators.js +65 -0
- package/enums.d.ts +23 -8
- package/enums.js +15 -1
- package/errors.d.ts +25 -9
- package/errors.js +67 -21
- package/events/EventManager.d.ts +2 -1
- package/events/EventManager.js +19 -11
- package/events/EventSubscriber.d.ts +3 -1
- package/hydration/Hydrator.js +1 -2
- package/hydration/ObjectHydrator.d.ts +4 -4
- package/hydration/ObjectHydrator.js +89 -36
- package/index.d.ts +2 -2
- package/index.js +1 -2
- package/logging/DefaultLogger.d.ts +1 -1
- package/logging/DefaultLogger.js +1 -0
- package/logging/SimpleLogger.d.ts +1 -1
- package/logging/colors.d.ts +1 -1
- package/logging/colors.js +7 -6
- package/logging/index.d.ts +1 -0
- package/logging/index.js +1 -0
- package/logging/inspect.d.ts +2 -0
- package/logging/inspect.js +11 -0
- package/metadata/EntitySchema.d.ts +53 -27
- package/metadata/EntitySchema.js +125 -52
- package/metadata/MetadataDiscovery.d.ts +64 -10
- package/metadata/MetadataDiscovery.js +823 -344
- package/metadata/MetadataProvider.d.ts +11 -2
- package/metadata/MetadataProvider.js +66 -2
- package/metadata/MetadataStorage.d.ts +13 -11
- package/metadata/MetadataStorage.js +71 -38
- package/metadata/MetadataValidator.d.ts +32 -9
- package/metadata/MetadataValidator.js +198 -42
- package/metadata/discover-entities.d.ts +5 -0
- package/metadata/discover-entities.js +40 -0
- package/metadata/index.d.ts +1 -1
- package/metadata/index.js +1 -1
- package/metadata/types.d.ts +577 -0
- package/metadata/types.js +1 -0
- package/naming-strategy/AbstractNamingStrategy.d.ts +16 -4
- package/naming-strategy/AbstractNamingStrategy.js +20 -2
- package/naming-strategy/EntityCaseNamingStrategy.d.ts +3 -3
- package/naming-strategy/EntityCaseNamingStrategy.js +6 -5
- package/naming-strategy/MongoNamingStrategy.d.ts +3 -3
- package/naming-strategy/MongoNamingStrategy.js +6 -6
- package/naming-strategy/NamingStrategy.d.ts +28 -4
- package/naming-strategy/UnderscoreNamingStrategy.d.ts +3 -3
- package/naming-strategy/UnderscoreNamingStrategy.js +6 -6
- package/not-supported.d.ts +2 -0
- package/not-supported.js +4 -0
- package/package.json +22 -11
- package/platforms/ExceptionConverter.js +1 -1
- package/platforms/Platform.d.ts +14 -16
- package/platforms/Platform.js +24 -44
- package/serialization/EntitySerializer.d.ts +8 -3
- package/serialization/EntitySerializer.js +47 -27
- package/serialization/EntityTransformer.js +33 -21
- package/serialization/SerializationContext.d.ts +6 -6
- package/serialization/SerializationContext.js +16 -13
- package/types/ArrayType.d.ts +1 -1
- package/types/ArrayType.js +2 -3
- package/types/BigIntType.d.ts +9 -6
- package/types/BigIntType.js +4 -1
- package/types/BlobType.d.ts +0 -1
- package/types/BlobType.js +0 -3
- package/types/BooleanType.d.ts +2 -1
- package/types/BooleanType.js +3 -0
- package/types/DecimalType.d.ts +6 -4
- package/types/DecimalType.js +3 -3
- package/types/DoubleType.js +2 -2
- package/types/EnumArrayType.js +1 -2
- package/types/JsonType.d.ts +1 -1
- package/types/JsonType.js +7 -2
- package/types/TinyIntType.js +1 -1
- package/types/Type.d.ts +2 -4
- package/types/Type.js +3 -3
- package/types/Uint8ArrayType.d.ts +0 -1
- package/types/Uint8ArrayType.js +1 -4
- package/types/index.d.ts +1 -1
- package/typings.d.ts +469 -175
- package/typings.js +120 -45
- package/unit-of-work/ChangeSet.d.ts +4 -6
- package/unit-of-work/ChangeSet.js +4 -5
- package/unit-of-work/ChangeSetComputer.d.ts +3 -8
- package/unit-of-work/ChangeSetComputer.js +44 -21
- package/unit-of-work/ChangeSetPersister.d.ts +15 -12
- package/unit-of-work/ChangeSetPersister.js +113 -45
- package/unit-of-work/CommitOrderCalculator.d.ts +12 -10
- package/unit-of-work/CommitOrderCalculator.js +13 -13
- package/unit-of-work/IdentityMap.d.ts +12 -0
- package/unit-of-work/IdentityMap.js +39 -1
- package/unit-of-work/UnitOfWork.d.ts +28 -3
- package/unit-of-work/UnitOfWork.js +315 -110
- package/utils/AbstractMigrator.d.ts +101 -0
- package/utils/AbstractMigrator.js +305 -0
- package/utils/AbstractSchemaGenerator.d.ts +5 -5
- package/utils/AbstractSchemaGenerator.js +32 -18
- package/utils/AsyncContext.d.ts +6 -0
- package/utils/AsyncContext.js +42 -0
- package/utils/Configuration.d.ts +801 -207
- package/utils/Configuration.js +150 -191
- package/utils/ConfigurationLoader.d.ts +1 -54
- package/utils/ConfigurationLoader.js +1 -352
- package/utils/Cursor.d.ts +3 -6
- package/utils/Cursor.js +27 -11
- package/utils/DataloaderUtils.d.ts +15 -5
- package/utils/DataloaderUtils.js +65 -17
- package/utils/EntityComparator.d.ts +21 -10
- package/utils/EntityComparator.js +243 -106
- package/utils/QueryHelper.d.ts +24 -6
- package/utils/QueryHelper.js +122 -26
- package/utils/RawQueryFragment.d.ts +60 -32
- package/utils/RawQueryFragment.js +69 -66
- package/utils/RequestContext.js +2 -2
- package/utils/TransactionContext.js +2 -2
- package/utils/TransactionManager.d.ts +65 -0
- package/utils/TransactionManager.js +223 -0
- package/utils/Utils.d.ts +15 -122
- package/utils/Utils.js +108 -376
- package/utils/clone.js +8 -23
- package/utils/env-vars.d.ts +7 -0
- package/utils/env-vars.js +97 -0
- package/utils/fs-utils.d.ts +34 -0
- package/utils/fs-utils.js +196 -0
- package/utils/index.d.ts +2 -3
- package/utils/index.js +2 -3
- package/utils/upsert-utils.d.ts +9 -4
- package/utils/upsert-utils.js +55 -4
- package/decorators/Check.d.ts +0 -3
- package/decorators/Check.js +0 -13
- package/decorators/CreateRequestContext.d.ts +0 -3
- package/decorators/CreateRequestContext.js +0 -32
- package/decorators/Embeddable.d.ts +0 -8
- package/decorators/Embeddable.js +0 -11
- package/decorators/Embedded.d.ts +0 -18
- package/decorators/Embedded.js +0 -18
- package/decorators/Entity.d.ts +0 -18
- package/decorators/Entity.js +0 -13
- package/decorators/Enum.d.ts +0 -9
- package/decorators/Enum.js +0 -16
- package/decorators/Filter.d.ts +0 -2
- package/decorators/Filter.js +0 -8
- package/decorators/Formula.d.ts +0 -5
- package/decorators/Formula.js +0 -15
- package/decorators/Indexed.d.ts +0 -17
- package/decorators/Indexed.js +0 -20
- package/decorators/ManyToMany.d.ts +0 -40
- package/decorators/ManyToMany.js +0 -14
- package/decorators/ManyToOne.d.ts +0 -30
- package/decorators/ManyToOne.js +0 -14
- package/decorators/OneToMany.d.ts +0 -28
- package/decorators/OneToMany.js +0 -17
- package/decorators/OneToOne.d.ts +0 -24
- package/decorators/OneToOne.js +0 -7
- package/decorators/PrimaryKey.d.ts +0 -9
- package/decorators/PrimaryKey.js +0 -20
- package/decorators/Property.d.ts +0 -250
- package/decorators/Property.js +0 -32
- package/decorators/Transactional.d.ts +0 -13
- package/decorators/Transactional.js +0 -28
- package/decorators/hooks.d.ts +0 -16
- package/decorators/hooks.js +0 -47
- package/decorators/index.d.ts +0 -17
- package/decorators/index.js +0 -17
- package/entity/ArrayCollection.d.ts +0 -116
- package/entity/ArrayCollection.js +0 -395
- package/entity/EntityValidator.d.ts +0 -19
- package/entity/EntityValidator.js +0 -150
- package/metadata/ReflectMetadataProvider.d.ts +0 -8
- package/metadata/ReflectMetadataProvider.js +0 -44
- package/utils/resolveContextProvider.d.ts +0 -10
- package/utils/resolveContextProvider.js +0 -28
package/entity/EntityLoader.js
CHANGED
|
@@ -4,8 +4,8 @@ import { ValidationError } from '../errors.js';
|
|
|
4
4
|
import { LoadStrategy, PopulatePath, ReferenceKind, } from '../enums.js';
|
|
5
5
|
import { Reference } from './Reference.js';
|
|
6
6
|
import { helper } from './wrap.js';
|
|
7
|
-
import { raw, RawQueryFragment } from '../utils/RawQueryFragment.js';
|
|
8
7
|
import { expandDotPaths } from './utils.js';
|
|
8
|
+
import { Raw } from '../utils/RawQueryFragment.js';
|
|
9
9
|
export class EntityLoader {
|
|
10
10
|
em;
|
|
11
11
|
metadata;
|
|
@@ -32,17 +32,16 @@ export class EntityLoader {
|
|
|
32
32
|
const visited = options.visited ??= new Set();
|
|
33
33
|
options.where ??= {};
|
|
34
34
|
options.orderBy ??= {};
|
|
35
|
-
options.filters ??= {};
|
|
36
35
|
options.lookup ??= true;
|
|
37
36
|
options.validate ??= true;
|
|
38
37
|
options.refresh ??= false;
|
|
39
38
|
options.convertCustomTypes ??= true;
|
|
40
39
|
if (references.length > 0) {
|
|
41
|
-
await this.populateScalar(meta, references, options);
|
|
40
|
+
await this.populateScalar(meta, references, { ...options, populateWhere: undefined });
|
|
42
41
|
}
|
|
43
|
-
populate = this.normalizePopulate(entityName, populate, options.strategy, options.lookup);
|
|
42
|
+
populate = this.normalizePopulate(entityName, populate, options.strategy, options.lookup, options.exclude);
|
|
44
43
|
const invalid = populate.find(({ field }) => !this.em.canPopulate(entityName, field));
|
|
45
|
-
/* v8 ignore next
|
|
44
|
+
/* v8 ignore next */
|
|
46
45
|
if (options.validate && invalid) {
|
|
47
46
|
throw ValidationError.invalidPropertyName(entityName, invalid.field);
|
|
48
47
|
}
|
|
@@ -57,7 +56,7 @@ export class EntityLoader {
|
|
|
57
56
|
visited.delete(entity);
|
|
58
57
|
}
|
|
59
58
|
}
|
|
60
|
-
normalizePopulate(entityName, populate, strategy, lookup = true) {
|
|
59
|
+
normalizePopulate(entityName, populate, strategy, lookup = true, exclude) {
|
|
61
60
|
const meta = this.metadata.find(entityName);
|
|
62
61
|
let normalized = Utils.asArray(populate).map(field => {
|
|
63
62
|
return typeof field === 'boolean' || field.field === PopulatePath.ALL ? { all: !!field, field: meta.primaryKeys[0] } : field;
|
|
@@ -68,7 +67,7 @@ export class EntityLoader {
|
|
|
68
67
|
// convert nested `field` with dot syntax to PopulateOptions with `children` array
|
|
69
68
|
expandDotPaths(meta, normalized, true);
|
|
70
69
|
if (lookup && populate !== false) {
|
|
71
|
-
normalized = this.lookupEagerLoadedRelationships(entityName, normalized, strategy);
|
|
70
|
+
normalized = this.lookupEagerLoadedRelationships(entityName, normalized, strategy, '', [], exclude);
|
|
72
71
|
// convert nested `field` with dot syntax produced by eager relations
|
|
73
72
|
expandDotPaths(meta, normalized, true);
|
|
74
73
|
}
|
|
@@ -140,34 +139,102 @@ export class EntityLoader {
|
|
|
140
139
|
const innerOrderBy = Utils.asArray(options.orderBy)
|
|
141
140
|
.filter(orderBy => (Array.isArray(orderBy[prop.name]) && orderBy[prop.name].length > 0) || Utils.isObject(orderBy[prop.name]))
|
|
142
141
|
.flatMap(orderBy => orderBy[prop.name]);
|
|
142
|
+
const where = await this.extractChildCondition(options, prop);
|
|
143
143
|
if (prop.kind === ReferenceKind.MANY_TO_MANY && this.driver.getPlatform().usesPivotTable()) {
|
|
144
|
-
|
|
144
|
+
const pivotOrderBy = QueryHelper.mergeOrderBy(innerOrderBy, prop.orderBy, prop.targetMeta?.orderBy);
|
|
145
|
+
const res = await this.findChildrenFromPivotTable(filtered, prop, options, pivotOrderBy, populate, !!ref);
|
|
146
|
+
return Utils.flatten(res);
|
|
145
147
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
148
|
+
if (prop.polymorphic && prop.polymorphTargets) {
|
|
149
|
+
return this.populatePolymorphic(entities, prop, options, !!ref);
|
|
150
|
+
}
|
|
151
|
+
const { items, partial } = await this.findChildren(options.filtered ?? entities, prop, populate, {
|
|
152
|
+
...options,
|
|
153
|
+
where,
|
|
154
|
+
orderBy: innerOrderBy,
|
|
155
|
+
}, !!(ref || prop.mapToPk));
|
|
156
|
+
const customOrder = innerOrderBy.length > 0 || !!prop.orderBy || !!prop.targetMeta?.orderBy;
|
|
157
|
+
this.initializeCollections(filtered, prop, field, items, customOrder, partial);
|
|
158
|
+
return items;
|
|
150
159
|
}
|
|
151
160
|
async populateScalar(meta, filtered, options) {
|
|
152
161
|
const pk = Utils.getPrimaryKeyHash(meta.primaryKeys);
|
|
153
|
-
const ids = Utils.unique(filtered.map(e => Utils.getPrimaryKeyValues(e, meta
|
|
162
|
+
const ids = Utils.unique(filtered.map(e => Utils.getPrimaryKeyValues(e, meta, true)));
|
|
154
163
|
const where = this.mergePrimaryCondition(ids, pk, options, meta, this.metadata, this.driver.getPlatform());
|
|
155
164
|
const { filters, convertCustomTypes, lockMode, strategy, populateWhere, connectionType, logging, fields } = options;
|
|
156
|
-
await this.em.find(meta.
|
|
165
|
+
await this.em.find(meta.class, where, {
|
|
157
166
|
filters, convertCustomTypes, lockMode, strategy, populateWhere, connectionType, logging,
|
|
158
167
|
fields: fields,
|
|
159
168
|
populate: [],
|
|
160
169
|
});
|
|
161
170
|
}
|
|
162
|
-
|
|
171
|
+
async populatePolymorphic(entities, prop, options, ref) {
|
|
172
|
+
const ownerMeta = this.metadata.get(entities[0].constructor);
|
|
173
|
+
// Separate entities: those with loaded refs vs those needing FK load
|
|
174
|
+
const toPopulate = [];
|
|
175
|
+
const needsFkLoad = [];
|
|
176
|
+
for (const entity of entities) {
|
|
177
|
+
const refValue = entity[prop.name];
|
|
178
|
+
if (refValue && helper(refValue).hasPrimaryKey()) {
|
|
179
|
+
if ((ref && !options.refresh) || // :ref hint - already have reference
|
|
180
|
+
(!ref && helper(refValue).__initialized && !options.refresh) // already loaded
|
|
181
|
+
) {
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
toPopulate.push(entity);
|
|
185
|
+
}
|
|
186
|
+
else if (refValue == null && !helper(entity).__loadedProperties.has(prop.name)) {
|
|
187
|
+
// FK columns weren't loaded (partial loading) — need to re-fetch them.
|
|
188
|
+
// If the property IS in __loadedProperties, the FK was loaded and is genuinely null.
|
|
189
|
+
needsFkLoad.push(entity);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// Load FK columns using populateScalar pattern
|
|
193
|
+
if (needsFkLoad.length > 0) {
|
|
194
|
+
await this.populateScalar(ownerMeta, needsFkLoad, {
|
|
195
|
+
...options,
|
|
196
|
+
fields: [...ownerMeta.primaryKeys, prop.name],
|
|
197
|
+
});
|
|
198
|
+
// After loading FKs, add to toPopulate if not using :ref hint
|
|
199
|
+
if (!ref) {
|
|
200
|
+
for (const entity of needsFkLoad) {
|
|
201
|
+
const refValue = entity[prop.name];
|
|
202
|
+
if (refValue && helper(refValue).hasPrimaryKey()) {
|
|
203
|
+
toPopulate.push(entity);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (toPopulate.length === 0) {
|
|
209
|
+
return [];
|
|
210
|
+
}
|
|
211
|
+
// Group references by target class for batch loading
|
|
212
|
+
const groups = new Map();
|
|
213
|
+
for (const entity of toPopulate) {
|
|
214
|
+
const refValue = Reference.unwrapReference(entity[prop.name]);
|
|
215
|
+
const discriminator = QueryHelper.findDiscriminatorValue(prop.discriminatorMap, helper(refValue).__meta.class);
|
|
216
|
+
const group = groups.get(discriminator) ?? [];
|
|
217
|
+
group.push(refValue);
|
|
218
|
+
groups.set(discriminator, group);
|
|
219
|
+
}
|
|
220
|
+
// Load each group concurrently - identity map handles merging with existing references
|
|
221
|
+
const allItems = [];
|
|
222
|
+
await Promise.all([...groups].map(async ([discriminator, children]) => {
|
|
223
|
+
const targetMeta = this.metadata.find(prop.discriminatorMap[discriminator]);
|
|
224
|
+
await this.populateScalar(targetMeta, children, options);
|
|
225
|
+
allItems.push(...children);
|
|
226
|
+
}));
|
|
227
|
+
return allItems;
|
|
228
|
+
}
|
|
229
|
+
initializeCollections(filtered, prop, field, children, customOrder, partial) {
|
|
163
230
|
if (prop.kind === ReferenceKind.ONE_TO_MANY) {
|
|
164
|
-
this.initializeOneToMany(filtered, children, prop, field);
|
|
231
|
+
this.initializeOneToMany(filtered, children, prop, field, partial);
|
|
165
232
|
}
|
|
166
233
|
if (prop.kind === ReferenceKind.MANY_TO_MANY && !this.driver.getPlatform().usesPivotTable()) {
|
|
167
|
-
this.initializeManyToMany(filtered, children, prop, field, customOrder);
|
|
234
|
+
this.initializeManyToMany(filtered, children, prop, field, customOrder, partial);
|
|
168
235
|
}
|
|
169
236
|
}
|
|
170
|
-
initializeOneToMany(filtered, children, prop, field) {
|
|
237
|
+
initializeOneToMany(filtered, children, prop, field, partial) {
|
|
171
238
|
const mapToPk = prop.targetMeta.properties[prop.mappedBy].mapToPk;
|
|
172
239
|
const map = {};
|
|
173
240
|
for (const entity of filtered) {
|
|
@@ -177,20 +244,20 @@ export class EntityLoader {
|
|
|
177
244
|
for (const child of children) {
|
|
178
245
|
const pk = child.__helper.__data[prop.mappedBy] ?? child[prop.mappedBy];
|
|
179
246
|
if (pk) {
|
|
180
|
-
const key = helper(mapToPk ? this.em.getReference(prop.
|
|
247
|
+
const key = helper(mapToPk ? this.em.getReference(prop.targetMeta.class, pk) : pk).getSerializedPrimaryKey();
|
|
181
248
|
map[key]?.push(child);
|
|
182
249
|
}
|
|
183
250
|
}
|
|
184
251
|
for (const entity of filtered) {
|
|
185
252
|
const key = helper(entity).getSerializedPrimaryKey();
|
|
186
|
-
entity[field].hydrate(map[key]);
|
|
253
|
+
entity[field].hydrate(map[key], undefined, partial);
|
|
187
254
|
}
|
|
188
255
|
}
|
|
189
|
-
initializeManyToMany(filtered, children, prop, field, customOrder) {
|
|
256
|
+
initializeManyToMany(filtered, children, prop, field, customOrder, partial) {
|
|
190
257
|
if (prop.mappedBy) {
|
|
191
258
|
for (const entity of filtered) {
|
|
192
259
|
const items = children.filter(child => child[prop.mappedBy].contains(entity, false));
|
|
193
|
-
entity[field].hydrate(items, true);
|
|
260
|
+
entity[field].hydrate(items, true, partial);
|
|
194
261
|
}
|
|
195
262
|
}
|
|
196
263
|
else { // owning side of M:N without pivot table needs to be reordered
|
|
@@ -200,17 +267,28 @@ export class EntityLoader {
|
|
|
200
267
|
if (!customOrder) {
|
|
201
268
|
items.sort((a, b) => order.indexOf(a) - order.indexOf(b));
|
|
202
269
|
}
|
|
203
|
-
entity[field].hydrate(items, true);
|
|
270
|
+
entity[field].hydrate(items, true, partial);
|
|
204
271
|
}
|
|
205
272
|
}
|
|
206
273
|
}
|
|
207
274
|
async findChildren(entities, prop, populate, options, ref) {
|
|
208
|
-
const children = this.getChildReferences(entities, prop, options, ref);
|
|
275
|
+
const children = Utils.unique(this.getChildReferences(entities, prop, options, ref));
|
|
209
276
|
const meta = prop.targetMeta;
|
|
210
|
-
|
|
277
|
+
// When targetKey is set, use it for FK lookup instead of the PK
|
|
278
|
+
let fk = prop.targetKey ?? Utils.getPrimaryKeyHash(meta.primaryKeys);
|
|
211
279
|
let schema = options.schema;
|
|
280
|
+
const partial = !Utils.isEmpty(prop.where) || !Utils.isEmpty(options.where);
|
|
281
|
+
let polymorphicOwnerProp;
|
|
212
282
|
if (prop.kind === ReferenceKind.ONE_TO_MANY || (prop.kind === ReferenceKind.MANY_TO_MANY && !prop.owner)) {
|
|
213
|
-
|
|
283
|
+
const ownerProp = meta.properties[prop.mappedBy];
|
|
284
|
+
if (ownerProp.polymorphic && ownerProp.fieldNames.length >= 2) {
|
|
285
|
+
const idColumns = ownerProp.fieldNames.slice(1);
|
|
286
|
+
fk = idColumns.length === 1 ? idColumns[0] : idColumns;
|
|
287
|
+
polymorphicOwnerProp = ownerProp;
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
fk = ownerProp.name;
|
|
291
|
+
}
|
|
214
292
|
}
|
|
215
293
|
if (prop.kind === ReferenceKind.ONE_TO_ONE && !prop.owner && !ref) {
|
|
216
294
|
children.length = 0;
|
|
@@ -218,40 +296,43 @@ export class EntityLoader {
|
|
|
218
296
|
children.push(...this.filterByReferences(entities, prop.name, options.refresh));
|
|
219
297
|
}
|
|
220
298
|
if (children.length === 0) {
|
|
221
|
-
return [];
|
|
299
|
+
return { items: [], partial };
|
|
222
300
|
}
|
|
223
301
|
if (!schema && [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind)) {
|
|
224
302
|
schema = children.find(e => e.__helper.__schema)?.__helper.__schema;
|
|
225
303
|
}
|
|
226
|
-
const ids = Utils.unique(children.map(e => e.__helper.getPrimaryKey()));
|
|
227
|
-
let where
|
|
304
|
+
const ids = Utils.unique(children.map(e => prop.targetKey ? e[prop.targetKey] : e.__helper.getPrimaryKey()));
|
|
305
|
+
let where;
|
|
306
|
+
if (polymorphicOwnerProp && Array.isArray(fk)) {
|
|
307
|
+
const conditions = ids.map(id => {
|
|
308
|
+
const pkValues = Object.values(id);
|
|
309
|
+
return Object.fromEntries(fk.map((col, idx) => [col, pkValues[idx]]));
|
|
310
|
+
});
|
|
311
|
+
where = (conditions.length === 1 ? conditions[0] : { $or: conditions });
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
where = this.mergePrimaryCondition(ids, fk, options, meta, this.metadata, this.driver.getPlatform());
|
|
315
|
+
}
|
|
316
|
+
if (polymorphicOwnerProp) {
|
|
317
|
+
const parentMeta = this.metadata.find(entities[0].constructor);
|
|
318
|
+
const discriminatorValue = QueryHelper.findDiscriminatorValue(polymorphicOwnerProp.discriminatorMap, parentMeta.class) ?? parentMeta.tableName;
|
|
319
|
+
const discriminatorColumn = polymorphicOwnerProp.fieldNames[0];
|
|
320
|
+
where = { $and: [where, { [discriminatorColumn]: discriminatorValue }] };
|
|
321
|
+
}
|
|
228
322
|
const fields = this.buildFields(options.fields, prop, ref);
|
|
229
323
|
/* eslint-disable prefer-const */
|
|
230
|
-
let { refresh, filters, convertCustomTypes, lockMode, strategy, populateWhere, connectionType, logging, } = options;
|
|
324
|
+
let { refresh, filters, convertCustomTypes, lockMode, strategy, populateWhere = 'infer', connectionType, logging, } = options;
|
|
231
325
|
/* eslint-enable prefer-const */
|
|
232
326
|
if (typeof populateWhere === 'object') {
|
|
233
327
|
populateWhere = await this.extractChildCondition({ where: populateWhere }, prop);
|
|
234
328
|
}
|
|
235
|
-
if (!Utils.isEmpty(prop.where)) {
|
|
329
|
+
if (!Utils.isEmpty(prop.where) || Raw.hasObjectFragments(prop.where)) {
|
|
236
330
|
where = { $and: [where, prop.where] };
|
|
237
331
|
}
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
for (const item of Utils.asArray(prop.orderBy)) {
|
|
241
|
-
for (const field of Utils.keys(item)) {
|
|
242
|
-
const rawField = RawQueryFragment.getKnownFragment(field, false);
|
|
243
|
-
if (rawField) {
|
|
244
|
-
const raw2 = raw(rawField.sql, rawField.params);
|
|
245
|
-
propOrderBy.push({ [raw2.toString()]: item[field] });
|
|
246
|
-
continue;
|
|
247
|
-
}
|
|
248
|
-
propOrderBy.push({ [field]: item[field] });
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
const items = await this.em.find(prop.type, where, {
|
|
332
|
+
const orderBy = QueryHelper.mergeOrderBy(options.orderBy, prop.orderBy);
|
|
333
|
+
const items = await this.em.find(meta.class, where, {
|
|
253
334
|
filters, convertCustomTypes, lockMode, populateWhere, logging,
|
|
254
|
-
orderBy
|
|
335
|
+
orderBy,
|
|
255
336
|
populate: populate.children ?? populate.all ?? [],
|
|
256
337
|
exclude: Array.isArray(options.exclude) ? Utils.extractChildElements(options.exclude, prop.name) : options.exclude,
|
|
257
338
|
strategy, fields, schema, connectionType,
|
|
@@ -260,6 +341,49 @@ export class EntityLoader {
|
|
|
260
341
|
// @ts-ignore not a public option, will be propagated to the populate call
|
|
261
342
|
visited: options.visited,
|
|
262
343
|
});
|
|
344
|
+
// For targetKey relations, wire up loaded entities to parent references
|
|
345
|
+
// This is needed because the references were created under alternate key,
|
|
346
|
+
// but loaded entities are stored under PK, so they don't automatically merge
|
|
347
|
+
if (prop.targetKey && [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind)) {
|
|
348
|
+
const itemsByKey = new Map();
|
|
349
|
+
for (const item of items) {
|
|
350
|
+
itemsByKey.set('' + item[prop.targetKey], item);
|
|
351
|
+
}
|
|
352
|
+
for (const entity of entities) {
|
|
353
|
+
const ref = entity[prop.name];
|
|
354
|
+
/* v8 ignore next */
|
|
355
|
+
if (!ref) {
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
const keyValue = '' + (Reference.isReference(ref) ? ref.unwrap()[prop.targetKey] : ref[prop.targetKey]);
|
|
359
|
+
const loadedItem = itemsByKey.get(keyValue);
|
|
360
|
+
if (loadedItem) {
|
|
361
|
+
entity[prop.name] = (Reference.isReference(ref) ? Reference.create(loadedItem) : loadedItem);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
if ([ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind) && items.length !== children.length) {
|
|
366
|
+
const nullVal = this.em.config.get('forceUndefined') ? undefined : null;
|
|
367
|
+
const itemsMap = new Set();
|
|
368
|
+
const childrenMap = new Set();
|
|
369
|
+
// Use targetKey value if set, otherwise use serialized PK
|
|
370
|
+
const getKey = (e) => prop.targetKey ? '' + e[prop.targetKey] : helper(e).getSerializedPrimaryKey();
|
|
371
|
+
for (const item of items) {
|
|
372
|
+
/* v8 ignore next */
|
|
373
|
+
itemsMap.add(getKey(item));
|
|
374
|
+
}
|
|
375
|
+
for (const child of children) {
|
|
376
|
+
childrenMap.add(getKey(child));
|
|
377
|
+
}
|
|
378
|
+
for (const entity of entities) {
|
|
379
|
+
const ref = entity[prop.name] ?? {};
|
|
380
|
+
const key = helper(ref) ? getKey(ref) : undefined;
|
|
381
|
+
if (key && childrenMap.has(key) && !itemsMap.has(key)) {
|
|
382
|
+
entity[prop.name] = nullVal;
|
|
383
|
+
helper(entity).__originalEntityData[prop.name] = null;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
263
387
|
for (const item of items) {
|
|
264
388
|
if (ref && !helper(item).__onLoadFired) {
|
|
265
389
|
helper(item).__initialized = false;
|
|
@@ -267,10 +391,10 @@ export class EntityLoader {
|
|
|
267
391
|
this.em.getUnitOfWork()['loadedEntities'].delete(item);
|
|
268
392
|
}
|
|
269
393
|
}
|
|
270
|
-
return items;
|
|
394
|
+
return { items, partial };
|
|
271
395
|
}
|
|
272
396
|
mergePrimaryCondition(ids, pk, options, meta, metadata, platform) {
|
|
273
|
-
const cond1 = QueryHelper.processWhere({ where: { [pk]: { $in: ids } }, entityName: meta.
|
|
397
|
+
const cond1 = QueryHelper.processWhere({ where: { [pk]: { $in: ids } }, entityName: meta.class, metadata, platform, convertCustomTypes: !options.convertCustomTypes });
|
|
274
398
|
const where = { ...options.where };
|
|
275
399
|
Utils.dropUndefinedProperties(where);
|
|
276
400
|
return where[pk]
|
|
@@ -283,6 +407,7 @@ export class EntityLoader {
|
|
|
283
407
|
if (prop.kind === ReferenceKind.SCALAR && !prop.lazy) {
|
|
284
408
|
return;
|
|
285
409
|
}
|
|
410
|
+
options = { ...options, filters: QueryHelper.mergePropertyFilters(prop.filters, options.filters) };
|
|
286
411
|
const populated = await this.populateMany(entityName, entities, populate, options);
|
|
287
412
|
if (!populate.children && !populate.all) {
|
|
288
413
|
return;
|
|
@@ -310,38 +435,64 @@ export class EntityLoader {
|
|
|
310
435
|
const innerOrderBy = Utils.asArray(options.orderBy)
|
|
311
436
|
.filter(orderBy => Utils.isObject(orderBy[prop.name]))
|
|
312
437
|
.map(orderBy => orderBy[prop.name]);
|
|
313
|
-
const { refresh, filters, ignoreLazyScalarProperties, populateWhere, connectionType, logging } = options;
|
|
438
|
+
const { refresh, filters, ignoreLazyScalarProperties, populateWhere, connectionType, logging, schema } = options;
|
|
314
439
|
const exclude = Array.isArray(options.exclude) ? Utils.extractChildElements(options.exclude, prop.name) : options.exclude;
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
440
|
+
const visited = options.visited;
|
|
441
|
+
for (const entity of entities) {
|
|
442
|
+
visited.delete(entity);
|
|
443
|
+
}
|
|
444
|
+
const unique = Utils.unique(children);
|
|
445
|
+
const filtered = unique.filter(e => !visited.has(e));
|
|
446
|
+
for (const entity of entities) {
|
|
447
|
+
visited.add(entity);
|
|
448
|
+
}
|
|
449
|
+
if (!prop.targetMeta) {
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
const populateChildren = async (targetMeta, items) => {
|
|
453
|
+
await this.populate(targetMeta.class, items, populate.children ?? populate.all, {
|
|
454
|
+
where: await this.extractChildCondition(options, prop, false),
|
|
455
|
+
orderBy: innerOrderBy,
|
|
456
|
+
fields,
|
|
457
|
+
exclude,
|
|
458
|
+
validate: false,
|
|
459
|
+
lookup: false,
|
|
460
|
+
filters,
|
|
461
|
+
ignoreLazyScalarProperties,
|
|
462
|
+
populateWhere,
|
|
463
|
+
connectionType,
|
|
464
|
+
logging,
|
|
465
|
+
schema,
|
|
466
|
+
// @ts-ignore not a public option, will be propagated to the populate call
|
|
467
|
+
refresh: refresh && !filtered.every(item => options.visited.has(item)),
|
|
468
|
+
// @ts-ignore not a public option, will be propagated to the populate call
|
|
469
|
+
visited: options.visited,
|
|
470
|
+
// @ts-ignore not a public option
|
|
471
|
+
filtered,
|
|
472
|
+
});
|
|
473
|
+
};
|
|
474
|
+
if (prop.polymorphic && prop.polymorphTargets) {
|
|
475
|
+
await Promise.all(prop.polymorphTargets.map(async (targetMeta) => {
|
|
476
|
+
const targetChildren = unique.filter(child => helper(child).__meta.className === targetMeta.className);
|
|
477
|
+
if (targetChildren.length > 0) {
|
|
478
|
+
await populateChildren(targetMeta, targetChildren);
|
|
479
|
+
}
|
|
480
|
+
}));
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
await populateChildren(prop.targetMeta, unique);
|
|
484
|
+
}
|
|
333
485
|
}
|
|
486
|
+
/** @internal */
|
|
334
487
|
async findChildrenFromPivotTable(filtered, prop, options, orderBy, populate, pivotJoin) {
|
|
335
488
|
const ids = filtered.map(e => e.__helper.__primaryKeys);
|
|
336
489
|
const refresh = options.refresh;
|
|
337
490
|
let where = await this.extractChildCondition(options, prop, true);
|
|
338
491
|
const fields = this.buildFields(options.fields, prop);
|
|
339
492
|
const exclude = Array.isArray(options.exclude) ? Utils.extractChildElements(options.exclude, prop.name) : options.exclude;
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
delete options2
|
|
343
|
-
options2.fields = fields;
|
|
344
|
-
options2.exclude = exclude;
|
|
493
|
+
const populateFilter = options.populateFilter?.[prop.name];
|
|
494
|
+
const options2 = { ...options, fields, exclude, populateFilter };
|
|
495
|
+
['limit', 'offset', 'first', 'last', 'before', 'after', 'overfetch'].forEach(prop => delete options2[prop]);
|
|
345
496
|
options2.populate = (populate?.children ?? []);
|
|
346
497
|
if (prop.customType) {
|
|
347
498
|
ids.forEach((id, idx) => ids[idx] = QueryHelper.processCustomType(prop, id, this.driver.getPlatform()));
|
|
@@ -354,12 +505,12 @@ export class EntityLoader {
|
|
|
354
505
|
for (const entity of filtered) {
|
|
355
506
|
const items = map[entity.__helper.getSerializedPrimaryKey()].map(item => {
|
|
356
507
|
if (pivotJoin) {
|
|
357
|
-
return this.em.getReference(prop.
|
|
508
|
+
return this.em.getReference(prop.targetMeta.class, item, {
|
|
358
509
|
convertCustomTypes: true,
|
|
359
510
|
schema: options.schema ?? this.em.config.get('schema'),
|
|
360
511
|
});
|
|
361
512
|
}
|
|
362
|
-
const entity = this.em.getEntityFactory().create(prop.
|
|
513
|
+
const entity = this.em.getEntityFactory().create(prop.targetMeta.class, item, {
|
|
363
514
|
refresh,
|
|
364
515
|
merge: true,
|
|
365
516
|
convertCustomTypes: true,
|
|
@@ -368,23 +519,20 @@ export class EntityLoader {
|
|
|
368
519
|
return this.em.getUnitOfWork().register(entity, item, { refresh, loaded: true });
|
|
369
520
|
});
|
|
370
521
|
entity[prop.name].hydrate(items, true);
|
|
371
|
-
children.push(
|
|
522
|
+
children.push(items);
|
|
372
523
|
}
|
|
373
524
|
return children;
|
|
374
525
|
}
|
|
375
526
|
async extractChildCondition(options, prop, filters = false) {
|
|
376
527
|
const where = options.where;
|
|
377
528
|
const subCond = Utils.isPlainObject(where[prop.name]) ? where[prop.name] : {};
|
|
378
|
-
const meta2 =
|
|
379
|
-
if (!meta2) {
|
|
380
|
-
return {};
|
|
381
|
-
}
|
|
529
|
+
const meta2 = prop.targetMeta;
|
|
382
530
|
const pk = Utils.getPrimaryKeyHash(meta2.primaryKeys);
|
|
383
531
|
['$and', '$or'].forEach(op => {
|
|
384
532
|
if (where[op]) {
|
|
385
533
|
const child = where[op]
|
|
386
534
|
.map((cond) => cond[prop.name])
|
|
387
|
-
.filter((sub) => sub != null && !(Utils.isPlainObject(sub) &&
|
|
535
|
+
.filter((sub) => sub != null && !(Utils.isPlainObject(sub) && Utils.getObjectQueryKeys(sub).every(key => Utils.isOperator(key, false))))
|
|
388
536
|
.map((cond) => {
|
|
389
537
|
if (Utils.isPrimaryKey(cond)) {
|
|
390
538
|
return { [pk]: cond };
|
|
@@ -405,7 +553,7 @@ export class EntityLoader {
|
|
|
405
553
|
});
|
|
406
554
|
}
|
|
407
555
|
if (filters) {
|
|
408
|
-
return this.em.applyFilters(
|
|
556
|
+
return this.em.applyFilters(meta2.class, subCond, options.filters, 'read', options);
|
|
409
557
|
}
|
|
410
558
|
return subCond;
|
|
411
559
|
}
|
|
@@ -423,44 +571,43 @@ export class EntityLoader {
|
|
|
423
571
|
const parts = f.toString().split('.');
|
|
424
572
|
const propName = parts.shift();
|
|
425
573
|
const childPropName = parts.join('.');
|
|
426
|
-
/* v8 ignore next
|
|
574
|
+
/* v8 ignore next */
|
|
427
575
|
if (propName === prop.name) {
|
|
428
576
|
ret.push(childPropName);
|
|
429
577
|
}
|
|
430
578
|
}
|
|
431
579
|
return ret;
|
|
432
580
|
}, []);
|
|
433
|
-
if (ret.length === 0) {
|
|
434
|
-
return undefined;
|
|
435
|
-
}
|
|
436
581
|
// we need to automatically select the FKs too, e.g. for 1:m relations to be able to wire them with the items
|
|
437
582
|
if (prop.kind === ReferenceKind.ONE_TO_MANY || prop.kind === ReferenceKind.MANY_TO_MANY) {
|
|
438
583
|
const owner = prop.targetMeta.properties[prop.mappedBy];
|
|
439
|
-
|
|
584
|
+
// when the owning FK is lazy, we need to explicitly select it even without user-provided fields,
|
|
585
|
+
// otherwise the driver will exclude it and we won't be able to map children to their parent collections
|
|
586
|
+
if (owner && !ret.includes(owner.name) && (ret.length > 0 || owner.lazy)) {
|
|
440
587
|
ret.push(owner.name);
|
|
441
588
|
}
|
|
442
589
|
}
|
|
590
|
+
if (ret.length === 0) {
|
|
591
|
+
return undefined;
|
|
592
|
+
}
|
|
443
593
|
return ret;
|
|
444
594
|
}
|
|
445
595
|
getChildReferences(entities, prop, options, ref) {
|
|
446
596
|
const filtered = this.filterCollections(entities, prop.name, options, ref);
|
|
447
|
-
const children = [];
|
|
448
597
|
if (prop.kind === ReferenceKind.ONE_TO_MANY) {
|
|
449
|
-
|
|
598
|
+
return filtered.map(e => e[prop.name].owner);
|
|
450
599
|
}
|
|
451
|
-
|
|
452
|
-
|
|
600
|
+
if (prop.kind === ReferenceKind.MANY_TO_MANY && prop.owner) {
|
|
601
|
+
return filtered.reduce((a, b) => {
|
|
453
602
|
a.push(...b[prop.name].getItems());
|
|
454
603
|
return a;
|
|
455
|
-
}, [])
|
|
604
|
+
}, []);
|
|
456
605
|
}
|
|
457
|
-
|
|
458
|
-
|
|
606
|
+
if (prop.kind === ReferenceKind.MANY_TO_MANY) { // inverse side
|
|
607
|
+
return filtered;
|
|
459
608
|
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
}
|
|
463
|
-
return children;
|
|
609
|
+
// MANY_TO_ONE or ONE_TO_ONE
|
|
610
|
+
return this.filterReferences(entities, prop.name, options, ref);
|
|
464
611
|
}
|
|
465
612
|
filterCollections(entities, field, options, ref) {
|
|
466
613
|
if (options.refresh) {
|
|
@@ -477,7 +624,7 @@ export class EntityLoader {
|
|
|
477
624
|
return wrapped.__loadedProperties.has(field);
|
|
478
625
|
}
|
|
479
626
|
const [f, ...r] = field.split('.');
|
|
480
|
-
/* v8 ignore next
|
|
627
|
+
/* v8 ignore next */
|
|
481
628
|
if (!wrapped.__loadedProperties.has(f) || !wrapped.__meta.properties[f]?.targetMeta) {
|
|
482
629
|
return false;
|
|
483
630
|
}
|
|
@@ -510,11 +657,11 @@ export class EntityLoader {
|
|
|
510
657
|
.map(e => Reference.unwrapReference(e[field]));
|
|
511
658
|
}
|
|
512
659
|
filterByReferences(entities, field, refresh) {
|
|
513
|
-
/* v8 ignore next
|
|
660
|
+
/* v8 ignore next */
|
|
514
661
|
if (refresh) {
|
|
515
662
|
return entities;
|
|
516
663
|
}
|
|
517
|
-
return entities.filter(e => !e[field]?.__helper?.__initialized);
|
|
664
|
+
return entities.filter(e => e[field] !== null && !e[field]?.__helper?.__initialized);
|
|
518
665
|
}
|
|
519
666
|
lookupAllRelationships(entityName) {
|
|
520
667
|
const ret = [];
|
|
@@ -536,33 +683,36 @@ export class EntityLoader {
|
|
|
536
683
|
}
|
|
537
684
|
return `${this.getRelationName(meta, meta.properties[prop.embedded[0]])}.${prop.embedded[1]}`;
|
|
538
685
|
}
|
|
539
|
-
lookupEagerLoadedRelationships(entityName, populate, strategy, prefix = '', visited = []) {
|
|
686
|
+
lookupEagerLoadedRelationships(entityName, populate, strategy, prefix = '', visited = [], exclude) {
|
|
540
687
|
const meta = this.metadata.find(entityName);
|
|
541
688
|
if (!meta && !prefix) {
|
|
542
689
|
return populate;
|
|
543
690
|
}
|
|
544
|
-
if (visited.includes(
|
|
691
|
+
if (!meta || visited.includes(meta)) {
|
|
545
692
|
return [];
|
|
546
693
|
}
|
|
547
|
-
visited.push(
|
|
694
|
+
visited.push(meta);
|
|
548
695
|
const ret = prefix === '' ? [...populate] : [];
|
|
549
696
|
meta.relations
|
|
550
697
|
.filter(prop => {
|
|
698
|
+
const field = this.getRelationName(meta, prop);
|
|
699
|
+
const prefixed = prefix ? `${prefix}.${field}` : field;
|
|
700
|
+
const isExcluded = exclude?.includes(prefixed);
|
|
551
701
|
const eager = prop.eager && !populate.some(p => p.field === `${prop.name}:ref`);
|
|
552
702
|
const populated = populate.some(p => p.field === prop.name);
|
|
553
703
|
const disabled = populate.some(p => p.field === prop.name && p.all === false);
|
|
554
|
-
return !disabled && (eager || populated);
|
|
704
|
+
return !disabled && !isExcluded && (eager || populated);
|
|
555
705
|
})
|
|
556
706
|
.forEach(prop => {
|
|
557
707
|
const field = this.getRelationName(meta, prop);
|
|
558
708
|
const prefixed = prefix ? `${prefix}.${field}` : field;
|
|
559
709
|
const nestedPopulate = populate.filter(p => p.field === prop.name).flatMap(p => p.children).filter(Boolean);
|
|
560
|
-
const nested = this.lookupEagerLoadedRelationships(prop.
|
|
710
|
+
const nested = this.lookupEagerLoadedRelationships(prop.targetMeta.class, nestedPopulate, strategy, prefixed, visited.slice(), exclude);
|
|
561
711
|
if (nested.length > 0) {
|
|
562
712
|
ret.push(...nested);
|
|
563
713
|
}
|
|
564
714
|
else {
|
|
565
|
-
const selfReferencing = [meta.
|
|
715
|
+
const selfReferencing = [meta.tableName, ...visited.map(m => m.tableName)].includes(prop.targetMeta.tableName) && prop.eager;
|
|
566
716
|
ret.push({
|
|
567
717
|
field: prefixed,
|
|
568
718
|
// enforce select-in strategy for self-referencing relations
|