@mikro-orm/core 7.0.0-dev.32 → 7.0.0-dev.321
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 +71 -63
- package/EntityManager.js +365 -283
- package/MikroORM.d.ts +44 -35
- package/MikroORM.js +109 -142
- package/README.md +7 -4
- package/cache/FileCacheAdapter.d.ts +1 -2
- package/cache/FileCacheAdapter.js +19 -14
- package/cache/GeneratedCacheAdapter.d.ts +0 -1
- package/cache/GeneratedCacheAdapter.js +0 -2
- package/cache/index.d.ts +1 -2
- package/cache/index.js +0 -2
- package/connections/Connection.d.ts +12 -5
- package/connections/Connection.js +37 -15
- package/drivers/DatabaseDriver.d.ts +25 -18
- package/drivers/DatabaseDriver.js +144 -45
- package/drivers/IDatabaseDriver.d.ts +118 -23
- package/entity/BaseEntity.d.ts +63 -4
- package/entity/BaseEntity.js +0 -3
- package/entity/Collection.d.ts +101 -29
- package/entity/Collection.js +473 -115
- package/entity/EntityAssigner.js +37 -25
- package/entity/EntityFactory.d.ts +7 -1
- package/entity/EntityFactory.js +116 -64
- package/entity/EntityHelper.d.ts +2 -2
- package/entity/EntityHelper.js +69 -27
- package/entity/EntityLoader.d.ts +11 -10
- package/entity/EntityLoader.js +264 -102
- 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 +2 -6
- package/entity/Reference.js +52 -19
- package/entity/WrappedEntity.d.ts +3 -8
- package/entity/WrappedEntity.js +6 -7
- package/entity/defineEntity.d.ts +525 -311
- package/entity/defineEntity.js +134 -290
- package/entity/index.d.ts +2 -2
- package/entity/index.js +2 -2
- package/entity/utils.d.ts +6 -1
- package/entity/utils.js +46 -11
- package/entity/validators.d.ts +11 -0
- package/entity/validators.js +66 -0
- package/enums.d.ts +8 -6
- package/enums.js +13 -17
- package/errors.d.ts +20 -10
- package/errors.js +63 -31
- package/events/EventManager.d.ts +2 -1
- package/events/EventManager.js +24 -13
- package/events/index.d.ts +1 -1
- package/events/index.js +0 -1
- package/exceptions.js +9 -2
- package/hydration/Hydrator.js +1 -2
- package/hydration/ObjectHydrator.d.ts +4 -4
- package/hydration/ObjectHydrator.js +105 -46
- package/index.d.ts +2 -2
- package/index.js +1 -2
- package/logging/DefaultLogger.d.ts +1 -1
- package/logging/DefaultLogger.js +3 -4
- package/logging/SimpleLogger.d.ts +1 -1
- package/logging/colors.d.ts +1 -1
- package/logging/colors.js +5 -7
- package/logging/index.d.ts +2 -1
- package/logging/index.js +1 -1
- package/logging/inspect.d.ts +2 -0
- package/logging/inspect.js +11 -0
- package/metadata/EntitySchema.d.ts +47 -23
- package/metadata/EntitySchema.js +103 -34
- package/metadata/MetadataDiscovery.d.ts +64 -9
- package/metadata/MetadataDiscovery.js +867 -354
- package/metadata/MetadataProvider.d.ts +11 -2
- package/metadata/MetadataProvider.js +71 -2
- package/metadata/MetadataStorage.d.ts +13 -11
- package/metadata/MetadataStorage.js +72 -41
- package/metadata/MetadataValidator.d.ts +32 -9
- package/metadata/MetadataValidator.js +214 -44
- 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 +0 -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 +26 -5
- 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/naming-strategy/index.d.ts +1 -1
- package/naming-strategy/index.js +0 -1
- package/not-supported.d.ts +2 -0
- package/not-supported.js +8 -0
- package/package.json +47 -36
- package/platforms/ExceptionConverter.js +1 -1
- package/platforms/Platform.d.ts +33 -15
- package/platforms/Platform.js +125 -69
- package/serialization/EntitySerializer.d.ts +6 -3
- package/serialization/EntitySerializer.js +53 -29
- package/serialization/EntityTransformer.js +33 -21
- package/serialization/SerializationContext.d.ts +6 -6
- package/serialization/SerializationContext.js +4 -4
- package/types/ArrayType.d.ts +1 -1
- package/types/ArrayType.js +2 -3
- package/types/BigIntType.js +1 -1
- package/types/BlobType.d.ts +0 -1
- package/types/BlobType.js +0 -3
- package/types/BooleanType.d.ts +1 -0
- package/types/BooleanType.js +3 -0
- package/types/DecimalType.js +2 -2
- package/types/DoubleType.js +1 -1
- 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/UuidType.d.ts +2 -0
- package/types/UuidType.js +14 -2
- package/types/index.d.ts +3 -2
- package/typings.d.ts +427 -170
- package/typings.js +100 -45
- package/unit-of-work/ChangeSet.d.ts +4 -6
- package/unit-of-work/ChangeSet.js +8 -9
- package/unit-of-work/ChangeSetComputer.d.ts +3 -8
- package/unit-of-work/ChangeSetComputer.js +49 -26
- package/unit-of-work/ChangeSetPersister.d.ts +13 -12
- package/unit-of-work/ChangeSetPersister.js +107 -44
- package/unit-of-work/CommitOrderCalculator.d.ts +12 -10
- package/unit-of-work/CommitOrderCalculator.js +17 -15
- package/unit-of-work/IdentityMap.d.ts +12 -0
- package/unit-of-work/IdentityMap.js +39 -1
- package/unit-of-work/UnitOfWork.d.ts +34 -4
- package/unit-of-work/UnitOfWork.js +294 -107
- package/utils/AbstractMigrator.d.ts +101 -0
- package/utils/AbstractMigrator.js +303 -0
- package/utils/AbstractSchemaGenerator.d.ts +5 -5
- package/utils/AbstractSchemaGenerator.js +30 -18
- package/utils/AsyncContext.d.ts +6 -0
- package/utils/AsyncContext.js +42 -0
- package/utils/Configuration.d.ts +796 -211
- package/utils/Configuration.js +160 -197
- package/utils/ConfigurationLoader.d.ts +1 -52
- package/utils/ConfigurationLoader.js +1 -330
- package/utils/Cursor.d.ts +0 -3
- package/utils/Cursor.js +29 -14
- package/utils/DataloaderUtils.d.ts +10 -5
- package/utils/DataloaderUtils.js +42 -22
- package/utils/EntityComparator.d.ts +16 -9
- package/utils/EntityComparator.js +202 -96
- package/utils/QueryHelper.d.ts +34 -7
- package/utils/QueryHelper.js +183 -72
- package/utils/RawQueryFragment.d.ts +28 -34
- package/utils/RawQueryFragment.js +37 -72
- package/utils/RequestContext.js +2 -2
- package/utils/TransactionContext.js +2 -2
- package/utils/TransactionManager.js +11 -7
- package/utils/Utils.d.ts +16 -127
- package/utils/Utils.js +106 -401
- package/utils/clone.js +13 -23
- package/utils/env-vars.d.ts +7 -0
- package/utils/env-vars.js +98 -0
- package/utils/fs-utils.d.ts +34 -0
- package/utils/fs-utils.js +193 -0
- package/utils/index.d.ts +1 -3
- package/utils/index.js +1 -3
- package/utils/upsert-utils.d.ts +9 -4
- package/utils/upsert-utils.js +51 -5
- 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 -12
- package/decorators/Embedded.js +0 -18
- package/decorators/Entity.d.ts +0 -33
- package/decorators/Entity.js +0 -12
- 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 -4
- package/decorators/Formula.js +0 -15
- package/decorators/Indexed.d.ts +0 -19
- package/decorators/Indexed.js +0 -20
- package/decorators/ManyToMany.d.ts +0 -42
- package/decorators/ManyToMany.js +0 -14
- package/decorators/ManyToOne.d.ts +0 -34
- 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 -28
- package/decorators/OneToOne.js +0 -7
- package/decorators/PrimaryKey.d.ts +0 -8
- 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 -14
- 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 -118
- package/entity/ArrayCollection.js +0 -407
- 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;
|
|
@@ -29,10 +29,9 @@ export class EntityLoader {
|
|
|
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
|
-
options.filters ??= {};
|
|
36
35
|
options.lookup ??= true;
|
|
37
36
|
options.validate ??= true;
|
|
38
37
|
options.refresh ??= false;
|
|
@@ -40,9 +39,9 @@ export class EntityLoader {
|
|
|
40
39
|
if (references.length > 0) {
|
|
41
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,9 +56,10 @@ 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 => {
|
|
62
|
+
// oxfmt-ignore
|
|
63
63
|
return typeof field === 'boolean' || field.field === PopulatePath.ALL ? { all: !!field, field: meta.primaryKeys[0] } : field;
|
|
64
64
|
});
|
|
65
65
|
if (normalized.some(p => p.all)) {
|
|
@@ -68,7 +68,7 @@ export class EntityLoader {
|
|
|
68
68
|
// convert nested `field` with dot syntax to PopulateOptions with `children` array
|
|
69
69
|
expandDotPaths(meta, normalized, true);
|
|
70
70
|
if (lookup && populate !== false) {
|
|
71
|
-
normalized = this.lookupEagerLoadedRelationships(entityName, normalized, strategy);
|
|
71
|
+
normalized = this.lookupEagerLoadedRelationships(entityName, normalized, strategy, '', [], exclude);
|
|
72
72
|
// convert nested `field` with dot syntax produced by eager relations
|
|
73
73
|
expandDotPaths(meta, normalized, true);
|
|
74
74
|
}
|
|
@@ -90,6 +90,7 @@ export class EntityLoader {
|
|
|
90
90
|
*/
|
|
91
91
|
mergeNestedPopulate(populate) {
|
|
92
92
|
const tmp = populate.reduce((ret, item) => {
|
|
93
|
+
/* v8 ignore next */
|
|
93
94
|
if (item.field === PopulatePath.ALL) {
|
|
94
95
|
return ret;
|
|
95
96
|
}
|
|
@@ -126,7 +127,8 @@ export class EntityLoader {
|
|
|
126
127
|
}
|
|
127
128
|
}
|
|
128
129
|
if (prop.kind === ReferenceKind.SCALAR && prop.lazy) {
|
|
129
|
-
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));
|
|
130
132
|
if (options.ignoreLazyScalarProperties || filtered.length === 0) {
|
|
131
133
|
return entities;
|
|
132
134
|
}
|
|
@@ -138,19 +140,25 @@ export class EntityLoader {
|
|
|
138
140
|
}
|
|
139
141
|
const filtered = this.filterCollections(entities, field, options, ref);
|
|
140
142
|
const innerOrderBy = Utils.asArray(options.orderBy)
|
|
141
|
-
.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]))
|
|
142
145
|
.flatMap(orderBy => orderBy[prop.name]);
|
|
143
146
|
const where = await this.extractChildCondition(options, prop);
|
|
144
147
|
if (prop.kind === ReferenceKind.MANY_TO_MANY && this.driver.getPlatform().usesPivotTable()) {
|
|
145
|
-
const
|
|
148
|
+
const pivotOrderBy = QueryHelper.mergeOrderBy(innerOrderBy, prop.orderBy, prop.targetMeta?.orderBy);
|
|
149
|
+
const res = await this.findChildrenFromPivotTable(filtered, prop, options, pivotOrderBy, populate, !!ref);
|
|
146
150
|
return Utils.flatten(res);
|
|
147
151
|
}
|
|
152
|
+
if (prop.polymorphic && prop.polymorphTargets) {
|
|
153
|
+
return this.populatePolymorphic(entities, prop, options, !!ref);
|
|
154
|
+
}
|
|
148
155
|
const { items, partial } = await this.findChildren(options.filtered ?? entities, prop, populate, {
|
|
149
156
|
...options,
|
|
150
157
|
where,
|
|
151
158
|
orderBy: innerOrderBy,
|
|
152
159
|
}, !!(ref || prop.mapToPk));
|
|
153
|
-
|
|
160
|
+
const customOrder = innerOrderBy.length > 0 || !!prop.orderBy || !!prop.targetMeta?.orderBy;
|
|
161
|
+
this.initializeCollections(filtered, prop, field, items, customOrder, partial);
|
|
154
162
|
return items;
|
|
155
163
|
}
|
|
156
164
|
async populateScalar(meta, filtered, options) {
|
|
@@ -158,12 +166,76 @@ export class EntityLoader {
|
|
|
158
166
|
const ids = Utils.unique(filtered.map(e => Utils.getPrimaryKeyValues(e, meta, true)));
|
|
159
167
|
const where = this.mergePrimaryCondition(ids, pk, options, meta, this.metadata, this.driver.getPlatform());
|
|
160
168
|
const { filters, convertCustomTypes, lockMode, strategy, populateWhere, connectionType, logging, fields } = options;
|
|
161
|
-
await this.em.find(meta.
|
|
162
|
-
filters,
|
|
169
|
+
await this.em.find(meta.class, where, {
|
|
170
|
+
filters,
|
|
171
|
+
convertCustomTypes,
|
|
172
|
+
lockMode,
|
|
173
|
+
strategy,
|
|
174
|
+
populateWhere,
|
|
175
|
+
connectionType,
|
|
176
|
+
logging,
|
|
163
177
|
fields: fields,
|
|
164
178
|
populate: [],
|
|
165
179
|
});
|
|
166
180
|
}
|
|
181
|
+
async populatePolymorphic(entities, prop, options, ref) {
|
|
182
|
+
const ownerMeta = this.metadata.get(entities[0].constructor);
|
|
183
|
+
// Separate entities: those with loaded refs vs those needing FK load
|
|
184
|
+
const toPopulate = [];
|
|
185
|
+
const needsFkLoad = [];
|
|
186
|
+
for (const entity of entities) {
|
|
187
|
+
const refValue = entity[prop.name];
|
|
188
|
+
if (refValue && helper(refValue).hasPrimaryKey()) {
|
|
189
|
+
if ((ref && !options.refresh) || // :ref hint - already have reference
|
|
190
|
+
(!ref && helper(refValue).__initialized && !options.refresh) // already loaded
|
|
191
|
+
) {
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
toPopulate.push(entity);
|
|
195
|
+
}
|
|
196
|
+
else if (refValue == null && !helper(entity).__loadedProperties.has(prop.name)) {
|
|
197
|
+
// FK columns weren't loaded (partial loading) — need to re-fetch them.
|
|
198
|
+
// If the property IS in __loadedProperties, the FK was loaded and is genuinely null.
|
|
199
|
+
needsFkLoad.push(entity);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// Load FK columns using populateScalar pattern
|
|
203
|
+
if (needsFkLoad.length > 0) {
|
|
204
|
+
await this.populateScalar(ownerMeta, needsFkLoad, {
|
|
205
|
+
...options,
|
|
206
|
+
fields: [...ownerMeta.primaryKeys, prop.name],
|
|
207
|
+
});
|
|
208
|
+
// After loading FKs, add to toPopulate if not using :ref hint
|
|
209
|
+
if (!ref) {
|
|
210
|
+
for (const entity of needsFkLoad) {
|
|
211
|
+
const refValue = entity[prop.name];
|
|
212
|
+
if (refValue && helper(refValue).hasPrimaryKey()) {
|
|
213
|
+
toPopulate.push(entity);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (toPopulate.length === 0) {
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
221
|
+
// Group references by target class for batch loading
|
|
222
|
+
const groups = new Map();
|
|
223
|
+
for (const entity of toPopulate) {
|
|
224
|
+
const refValue = Reference.unwrapReference(entity[prop.name]);
|
|
225
|
+
const discriminator = QueryHelper.findDiscriminatorValue(prop.discriminatorMap, helper(refValue).__meta.class);
|
|
226
|
+
const group = groups.get(discriminator) ?? [];
|
|
227
|
+
group.push(refValue);
|
|
228
|
+
groups.set(discriminator, group);
|
|
229
|
+
}
|
|
230
|
+
// Load each group concurrently - identity map handles merging with existing references
|
|
231
|
+
const allItems = [];
|
|
232
|
+
await Promise.all([...groups].map(async ([discriminator, children]) => {
|
|
233
|
+
const targetMeta = this.metadata.find(prop.discriminatorMap[discriminator]);
|
|
234
|
+
await this.populateScalar(targetMeta, children, options);
|
|
235
|
+
allItems.push(...children);
|
|
236
|
+
}));
|
|
237
|
+
return allItems;
|
|
238
|
+
}
|
|
167
239
|
initializeCollections(filtered, prop, field, children, customOrder, partial) {
|
|
168
240
|
if (prop.kind === ReferenceKind.ONE_TO_MANY) {
|
|
169
241
|
this.initializeOneToMany(filtered, children, prop, field, partial);
|
|
@@ -182,7 +254,7 @@ export class EntityLoader {
|
|
|
182
254
|
for (const child of children) {
|
|
183
255
|
const pk = child.__helper.__data[prop.mappedBy] ?? child[prop.mappedBy];
|
|
184
256
|
if (pk) {
|
|
185
|
-
const key = helper(mapToPk ? this.em.getReference(prop.
|
|
257
|
+
const key = helper(mapToPk ? this.em.getReference(prop.targetMeta.class, pk) : pk).getSerializedPrimaryKey();
|
|
186
258
|
map[key]?.push(child);
|
|
187
259
|
}
|
|
188
260
|
}
|
|
@@ -198,7 +270,8 @@ export class EntityLoader {
|
|
|
198
270
|
entity[field].hydrate(items, true, partial);
|
|
199
271
|
}
|
|
200
272
|
}
|
|
201
|
-
else {
|
|
273
|
+
else {
|
|
274
|
+
// owning side of M:N without pivot table needs to be reordered
|
|
202
275
|
for (const entity of filtered) {
|
|
203
276
|
const order = !customOrder ? [...entity[prop.name].getItems(false)] : []; // copy order of references
|
|
204
277
|
const items = children.filter(child => entity[prop.name].contains(child, false));
|
|
@@ -210,13 +283,23 @@ export class EntityLoader {
|
|
|
210
283
|
}
|
|
211
284
|
}
|
|
212
285
|
async findChildren(entities, prop, populate, options, ref) {
|
|
213
|
-
const children = this.getChildReferences(entities, prop, options, ref);
|
|
286
|
+
const children = Utils.unique(this.getChildReferences(entities, prop, options, ref));
|
|
214
287
|
const meta = prop.targetMeta;
|
|
215
|
-
|
|
288
|
+
// When targetKey is set, use it for FK lookup instead of the PK
|
|
289
|
+
let fk = prop.targetKey ?? Utils.getPrimaryKeyHash(meta.primaryKeys);
|
|
216
290
|
let schema = options.schema;
|
|
217
291
|
const partial = !Utils.isEmpty(prop.where) || !Utils.isEmpty(options.where);
|
|
292
|
+
let polymorphicOwnerProp;
|
|
218
293
|
if (prop.kind === ReferenceKind.ONE_TO_MANY || (prop.kind === ReferenceKind.MANY_TO_MANY && !prop.owner)) {
|
|
219
|
-
|
|
294
|
+
const ownerProp = meta.properties[prop.mappedBy];
|
|
295
|
+
if (ownerProp.polymorphic && ownerProp.fieldNames.length >= 2) {
|
|
296
|
+
const idColumns = ownerProp.fieldNames.slice(1);
|
|
297
|
+
fk = idColumns.length === 1 ? idColumns[0] : idColumns;
|
|
298
|
+
polymorphicOwnerProp = ownerProp;
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
fk = ownerProp.name;
|
|
302
|
+
}
|
|
220
303
|
}
|
|
221
304
|
if (prop.kind === ReferenceKind.ONE_TO_ONE && !prop.owner && !ref) {
|
|
222
305
|
children.length = 0;
|
|
@@ -229,8 +312,25 @@ export class EntityLoader {
|
|
|
229
312
|
if (!schema && [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind)) {
|
|
230
313
|
schema = children.find(e => e.__helper.__schema)?.__helper.__schema;
|
|
231
314
|
}
|
|
232
|
-
const ids = Utils.unique(children.map(e => e.__helper.getPrimaryKey()));
|
|
233
|
-
let where
|
|
315
|
+
const ids = Utils.unique(children.map(e => (prop.targetKey ? e[prop.targetKey] : e.__helper.getPrimaryKey())));
|
|
316
|
+
let where;
|
|
317
|
+
if (polymorphicOwnerProp && Array.isArray(fk)) {
|
|
318
|
+
const conditions = ids.map(id => {
|
|
319
|
+
const pkValues = Object.values(id);
|
|
320
|
+
return Object.fromEntries(fk.map((col, idx) => [col, pkValues[idx]]));
|
|
321
|
+
});
|
|
322
|
+
where = (conditions.length === 1 ? conditions[0] : { $or: conditions });
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
where = this.mergePrimaryCondition(ids, fk, options, meta, this.metadata, this.driver.getPlatform());
|
|
326
|
+
}
|
|
327
|
+
if (polymorphicOwnerProp) {
|
|
328
|
+
const parentMeta = this.metadata.find(entities[0].constructor);
|
|
329
|
+
const discriminatorValue = QueryHelper.findDiscriminatorValue(polymorphicOwnerProp.discriminatorMap, parentMeta.class) ??
|
|
330
|
+
parentMeta.tableName;
|
|
331
|
+
const discriminatorColumn = polymorphicOwnerProp.fieldNames[0];
|
|
332
|
+
where = { $and: [where, { [discriminatorColumn]: discriminatorValue }] };
|
|
333
|
+
}
|
|
234
334
|
const fields = this.buildFields(options.fields, prop, ref);
|
|
235
335
|
/* eslint-disable prefer-const */
|
|
236
336
|
let { refresh, filters, convertCustomTypes, lockMode, strategy, populateWhere = 'infer', connectionType, logging, } = options;
|
|
@@ -238,38 +338,74 @@ export class EntityLoader {
|
|
|
238
338
|
if (typeof populateWhere === 'object') {
|
|
239
339
|
populateWhere = await this.extractChildCondition({ where: populateWhere }, prop);
|
|
240
340
|
}
|
|
241
|
-
if (!Utils.isEmpty(prop.where)) {
|
|
341
|
+
if (!Utils.isEmpty(prop.where) || Raw.hasObjectFragments(prop.where)) {
|
|
242
342
|
where = { $and: [where, prop.where] };
|
|
243
343
|
}
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
propOrderBy.push({ [raw2.toString()]: item[field] });
|
|
252
|
-
continue;
|
|
253
|
-
}
|
|
254
|
-
propOrderBy.push({ [field]: item[field] });
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
const orderBy = [...Utils.asArray(options.orderBy), ...propOrderBy].filter((order, idx, array) => {
|
|
259
|
-
// skip consecutive ordering with the same key to get around mongo issues
|
|
260
|
-
return idx === 0 || !Utils.equals(Object.keys(array[idx - 1]), Object.keys(order));
|
|
261
|
-
});
|
|
262
|
-
const items = await this.em.find(prop.type, where, {
|
|
263
|
-
filters, convertCustomTypes, lockMode, populateWhere, logging,
|
|
344
|
+
const orderBy = QueryHelper.mergeOrderBy(options.orderBy, prop.orderBy);
|
|
345
|
+
const items = await this.em.find(meta.class, where, {
|
|
346
|
+
filters,
|
|
347
|
+
convertCustomTypes,
|
|
348
|
+
lockMode,
|
|
349
|
+
populateWhere,
|
|
350
|
+
logging,
|
|
264
351
|
orderBy,
|
|
265
352
|
populate: populate.children ?? populate.all ?? [],
|
|
266
|
-
exclude: Array.isArray(options.exclude)
|
|
267
|
-
|
|
353
|
+
exclude: Array.isArray(options.exclude)
|
|
354
|
+
? Utils.extractChildElements(options.exclude, prop.name)
|
|
355
|
+
: options.exclude,
|
|
356
|
+
strategy,
|
|
357
|
+
fields,
|
|
358
|
+
schema,
|
|
359
|
+
connectionType,
|
|
268
360
|
// @ts-ignore not a public option, will be propagated to the populate call
|
|
269
361
|
refresh: refresh && !children.every(item => options.visited.has(item)),
|
|
270
362
|
// @ts-ignore not a public option, will be propagated to the populate call
|
|
271
363
|
visited: options.visited,
|
|
272
364
|
});
|
|
365
|
+
// For targetKey relations, wire up loaded entities to parent references
|
|
366
|
+
// This is needed because the references were created under alternate key,
|
|
367
|
+
// but loaded entities are stored under PK, so they don't automatically merge
|
|
368
|
+
if (prop.targetKey && [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind)) {
|
|
369
|
+
const itemsByKey = new Map();
|
|
370
|
+
for (const item of items) {
|
|
371
|
+
itemsByKey.set('' + item[prop.targetKey], item);
|
|
372
|
+
}
|
|
373
|
+
for (const entity of entities) {
|
|
374
|
+
const ref = entity[prop.name];
|
|
375
|
+
/* v8 ignore next */
|
|
376
|
+
if (!ref) {
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
// oxfmt-ignore
|
|
380
|
+
const keyValue = '' + (Reference.isReference(ref) ? ref.unwrap()[prop.targetKey] : ref[prop.targetKey]);
|
|
381
|
+
const loadedItem = itemsByKey.get(keyValue);
|
|
382
|
+
if (loadedItem) {
|
|
383
|
+
entity[prop.name] = (Reference.isReference(ref) ? Reference.create(loadedItem) : loadedItem);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
if ([ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind) && items.length !== children.length) {
|
|
388
|
+
const nullVal = this.em.config.get('forceUndefined') ? undefined : null;
|
|
389
|
+
const itemsMap = new Set();
|
|
390
|
+
const childrenMap = new Set();
|
|
391
|
+
// Use targetKey value if set, otherwise use serialized PK
|
|
392
|
+
const getKey = (e) => (prop.targetKey ? '' + e[prop.targetKey] : helper(e).getSerializedPrimaryKey());
|
|
393
|
+
for (const item of items) {
|
|
394
|
+
/* v8 ignore next */
|
|
395
|
+
itemsMap.add(getKey(item));
|
|
396
|
+
}
|
|
397
|
+
for (const child of children) {
|
|
398
|
+
childrenMap.add(getKey(child));
|
|
399
|
+
}
|
|
400
|
+
for (const entity of entities) {
|
|
401
|
+
const ref = entity[prop.name] ?? {};
|
|
402
|
+
const key = helper(ref) ? getKey(ref) : undefined;
|
|
403
|
+
if (key && childrenMap.has(key) && !itemsMap.has(key)) {
|
|
404
|
+
entity[prop.name] = nullVal;
|
|
405
|
+
helper(entity).__originalEntityData[prop.name] = null;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
273
409
|
for (const item of items) {
|
|
274
410
|
if (ref && !helper(item).__onLoadFired) {
|
|
275
411
|
helper(item).__initialized = false;
|
|
@@ -280,12 +416,16 @@ export class EntityLoader {
|
|
|
280
416
|
return { items, partial };
|
|
281
417
|
}
|
|
282
418
|
mergePrimaryCondition(ids, pk, options, meta, metadata, platform) {
|
|
283
|
-
const cond1 = QueryHelper.processWhere({
|
|
419
|
+
const cond1 = QueryHelper.processWhere({
|
|
420
|
+
where: { [pk]: { $in: ids } },
|
|
421
|
+
entityName: meta.class,
|
|
422
|
+
metadata,
|
|
423
|
+
platform,
|
|
424
|
+
convertCustomTypes: !options.convertCustomTypes,
|
|
425
|
+
});
|
|
284
426
|
const where = { ...options.where };
|
|
285
427
|
Utils.dropUndefinedProperties(where);
|
|
286
|
-
return where[pk]
|
|
287
|
-
? { $and: [cond1, where] }
|
|
288
|
-
: { ...cond1, ...where };
|
|
428
|
+
return where[pk] ? { $and: [cond1, where] } : { ...cond1, ...where };
|
|
289
429
|
}
|
|
290
430
|
async populateField(entityName, entities, populate, options) {
|
|
291
431
|
const field = populate.field.split(':')[0];
|
|
@@ -293,6 +433,7 @@ export class EntityLoader {
|
|
|
293
433
|
if (prop.kind === ReferenceKind.SCALAR && !prop.lazy) {
|
|
294
434
|
return;
|
|
295
435
|
}
|
|
436
|
+
options = { ...options, filters: QueryHelper.mergePropertyFilters(prop.filters, options.filters) };
|
|
296
437
|
const populated = await this.populateMany(entityName, entities, populate, options);
|
|
297
438
|
if (!populate.children && !populate.all) {
|
|
298
439
|
return;
|
|
@@ -321,6 +462,7 @@ export class EntityLoader {
|
|
|
321
462
|
.filter(orderBy => Utils.isObject(orderBy[prop.name]))
|
|
322
463
|
.map(orderBy => orderBy[prop.name]);
|
|
323
464
|
const { refresh, filters, ignoreLazyScalarProperties, populateWhere, connectionType, logging, schema } = options;
|
|
465
|
+
// oxfmt-ignore
|
|
324
466
|
const exclude = Array.isArray(options.exclude) ? Utils.extractChildElements(options.exclude, prop.name) : options.exclude;
|
|
325
467
|
const visited = options.visited;
|
|
326
468
|
for (const entity of entities) {
|
|
@@ -331,26 +473,42 @@ export class EntityLoader {
|
|
|
331
473
|
for (const entity of entities) {
|
|
332
474
|
visited.add(entity);
|
|
333
475
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
476
|
+
if (!prop.targetMeta) {
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
const populateChildren = async (targetMeta, items) => {
|
|
480
|
+
await this.populate(targetMeta.class, items, populate.children ?? populate.all, {
|
|
481
|
+
where: (await this.extractChildCondition(options, prop, false)),
|
|
482
|
+
orderBy: innerOrderBy,
|
|
483
|
+
fields,
|
|
484
|
+
exclude,
|
|
485
|
+
validate: false,
|
|
486
|
+
lookup: false,
|
|
487
|
+
filters,
|
|
488
|
+
ignoreLazyScalarProperties,
|
|
489
|
+
populateWhere,
|
|
490
|
+
connectionType,
|
|
491
|
+
logging,
|
|
492
|
+
schema,
|
|
493
|
+
// @ts-ignore not a public option, will be propagated to the populate call
|
|
494
|
+
refresh: refresh && !filtered.every(item => options.visited.has(item)),
|
|
495
|
+
// @ts-ignore not a public option, will be propagated to the populate call
|
|
496
|
+
visited: options.visited,
|
|
497
|
+
// @ts-ignore not a public option
|
|
498
|
+
filtered,
|
|
499
|
+
});
|
|
500
|
+
};
|
|
501
|
+
if (prop.polymorphic && prop.polymorphTargets) {
|
|
502
|
+
await Promise.all(prop.polymorphTargets.map(async (targetMeta) => {
|
|
503
|
+
const targetChildren = unique.filter(child => helper(child).__meta.className === targetMeta.className);
|
|
504
|
+
if (targetChildren.length > 0) {
|
|
505
|
+
await populateChildren(targetMeta, targetChildren);
|
|
506
|
+
}
|
|
507
|
+
}));
|
|
508
|
+
}
|
|
509
|
+
else {
|
|
510
|
+
await populateChildren(prop.targetMeta, unique);
|
|
511
|
+
}
|
|
354
512
|
}
|
|
355
513
|
/** @internal */
|
|
356
514
|
async findChildrenFromPivotTable(filtered, prop, options, orderBy, populate, pivotJoin) {
|
|
@@ -358,30 +516,27 @@ export class EntityLoader {
|
|
|
358
516
|
const refresh = options.refresh;
|
|
359
517
|
let where = await this.extractChildCondition(options, prop, true);
|
|
360
518
|
const fields = this.buildFields(options.fields, prop);
|
|
519
|
+
// oxfmt-ignore
|
|
361
520
|
const exclude = Array.isArray(options.exclude) ? Utils.extractChildElements(options.exclude, prop.name) : options.exclude;
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
delete options2
|
|
365
|
-
options2.
|
|
366
|
-
options2.exclude = exclude;
|
|
367
|
-
options2.populate = (populate?.children ?? []);
|
|
368
|
-
if (prop.customType) {
|
|
369
|
-
ids.forEach((id, idx) => ids[idx] = QueryHelper.processCustomType(prop, id, this.driver.getPlatform()));
|
|
370
|
-
}
|
|
521
|
+
const populateFilter = options.populateFilter?.[prop.name];
|
|
522
|
+
const options2 = { ...options, fields, exclude, populateFilter };
|
|
523
|
+
['limit', 'offset', 'first', 'last', 'before', 'after', 'overfetch'].forEach(prop => delete options2[prop]);
|
|
524
|
+
options2.populate = populate?.children ?? [];
|
|
371
525
|
if (!Utils.isEmpty(prop.where)) {
|
|
372
526
|
where = { $and: [where, prop.where] };
|
|
373
527
|
}
|
|
374
528
|
const map = await this.driver.loadFromPivotTable(prop, ids, where, orderBy, this.em.getTransactionContext(), options2, pivotJoin);
|
|
375
529
|
const children = [];
|
|
376
|
-
for (
|
|
377
|
-
const
|
|
530
|
+
for (let i = 0; i < filtered.length; i++) {
|
|
531
|
+
const entity = filtered[i];
|
|
532
|
+
const items = map[Utils.getPrimaryKeyHash(ids[i])].map(item => {
|
|
378
533
|
if (pivotJoin) {
|
|
379
|
-
return this.em.getReference(prop.
|
|
534
|
+
return this.em.getReference(prop.targetMeta.class, item, {
|
|
380
535
|
convertCustomTypes: true,
|
|
381
536
|
schema: options.schema ?? this.em.config.get('schema'),
|
|
382
537
|
});
|
|
383
538
|
}
|
|
384
|
-
const entity = this.em.getEntityFactory().create(prop.
|
|
539
|
+
const entity = this.em.getEntityFactory().create(prop.targetMeta.class, item, {
|
|
385
540
|
refresh,
|
|
386
541
|
merge: true,
|
|
387
542
|
convertCustomTypes: true,
|
|
@@ -397,16 +552,14 @@ export class EntityLoader {
|
|
|
397
552
|
async extractChildCondition(options, prop, filters = false) {
|
|
398
553
|
const where = options.where;
|
|
399
554
|
const subCond = Utils.isPlainObject(where[prop.name]) ? where[prop.name] : {};
|
|
400
|
-
const meta2 =
|
|
401
|
-
if (!meta2) {
|
|
402
|
-
return {};
|
|
403
|
-
}
|
|
555
|
+
const meta2 = prop.targetMeta;
|
|
404
556
|
const pk = Utils.getPrimaryKeyHash(meta2.primaryKeys);
|
|
405
557
|
['$and', '$or'].forEach(op => {
|
|
406
558
|
if (where[op]) {
|
|
407
559
|
const child = where[op]
|
|
408
560
|
.map((cond) => cond[prop.name])
|
|
409
|
-
.filter((sub) => sub != null &&
|
|
561
|
+
.filter((sub) => sub != null &&
|
|
562
|
+
!(Utils.isPlainObject(sub) && Utils.getObjectQueryKeys(sub).every(key => Utils.isOperator(key, false))))
|
|
410
563
|
.map((cond) => {
|
|
411
564
|
if (Utils.isPrimaryKey(cond)) {
|
|
412
565
|
return { [pk]: cond };
|
|
@@ -427,7 +580,7 @@ export class EntityLoader {
|
|
|
427
580
|
});
|
|
428
581
|
}
|
|
429
582
|
if (filters) {
|
|
430
|
-
return this.em.applyFilters(
|
|
583
|
+
return this.em.applyFilters(meta2.class, subCond, options.filters, 'read', options);
|
|
431
584
|
}
|
|
432
585
|
return subCond;
|
|
433
586
|
}
|
|
@@ -445,23 +598,25 @@ export class EntityLoader {
|
|
|
445
598
|
const parts = f.toString().split('.');
|
|
446
599
|
const propName = parts.shift();
|
|
447
600
|
const childPropName = parts.join('.');
|
|
448
|
-
/* v8 ignore next
|
|
601
|
+
/* v8 ignore next */
|
|
449
602
|
if (propName === prop.name) {
|
|
450
603
|
ret.push(childPropName);
|
|
451
604
|
}
|
|
452
605
|
}
|
|
453
606
|
return ret;
|
|
454
607
|
}, []);
|
|
455
|
-
if (ret.length === 0) {
|
|
456
|
-
return undefined;
|
|
457
|
-
}
|
|
458
608
|
// we need to automatically select the FKs too, e.g. for 1:m relations to be able to wire them with the items
|
|
459
609
|
if (prop.kind === ReferenceKind.ONE_TO_MANY || prop.kind === ReferenceKind.MANY_TO_MANY) {
|
|
460
610
|
const owner = prop.targetMeta.properties[prop.mappedBy];
|
|
461
|
-
|
|
611
|
+
// when the owning FK is lazy, we need to explicitly select it even without user-provided fields,
|
|
612
|
+
// otherwise the driver will exclude it and we won't be able to map children to their parent collections
|
|
613
|
+
if (owner && !ret.includes(owner.name) && (ret.length > 0 || owner.lazy)) {
|
|
462
614
|
ret.push(owner.name);
|
|
463
615
|
}
|
|
464
616
|
}
|
|
617
|
+
if (ret.length === 0) {
|
|
618
|
+
return undefined;
|
|
619
|
+
}
|
|
465
620
|
return ret;
|
|
466
621
|
}
|
|
467
622
|
getChildReferences(entities, prop, options, ref) {
|
|
@@ -475,7 +630,8 @@ export class EntityLoader {
|
|
|
475
630
|
return a;
|
|
476
631
|
}, []);
|
|
477
632
|
}
|
|
478
|
-
if (prop.kind === ReferenceKind.MANY_TO_MANY) {
|
|
633
|
+
if (prop.kind === ReferenceKind.MANY_TO_MANY) {
|
|
634
|
+
// inverse side
|
|
479
635
|
return filtered;
|
|
480
636
|
}
|
|
481
637
|
// MANY_TO_ONE or ONE_TO_ONE
|
|
@@ -496,7 +652,7 @@ export class EntityLoader {
|
|
|
496
652
|
return wrapped.__loadedProperties.has(field);
|
|
497
653
|
}
|
|
498
654
|
const [f, ...r] = field.split('.');
|
|
499
|
-
/* v8 ignore next
|
|
655
|
+
/* v8 ignore next */
|
|
500
656
|
if (!wrapped.__loadedProperties.has(f) || !wrapped.__meta.properties[f]?.targetMeta) {
|
|
501
657
|
return false;
|
|
502
658
|
}
|
|
@@ -529,7 +685,7 @@ export class EntityLoader {
|
|
|
529
685
|
.map(e => Reference.unwrapReference(e[field]));
|
|
530
686
|
}
|
|
531
687
|
filterByReferences(entities, field, refresh) {
|
|
532
|
-
/* v8 ignore next
|
|
688
|
+
/* v8 ignore next */
|
|
533
689
|
if (refresh) {
|
|
534
690
|
return entities;
|
|
535
691
|
}
|
|
@@ -555,37 +711,43 @@ export class EntityLoader {
|
|
|
555
711
|
}
|
|
556
712
|
return `${this.getRelationName(meta, meta.properties[prop.embedded[0]])}.${prop.embedded[1]}`;
|
|
557
713
|
}
|
|
558
|
-
lookupEagerLoadedRelationships(entityName, populate, strategy, prefix = '', visited = []) {
|
|
714
|
+
lookupEagerLoadedRelationships(entityName, populate, strategy, prefix = '', visited = [], exclude) {
|
|
559
715
|
const meta = this.metadata.find(entityName);
|
|
560
716
|
if (!meta && !prefix) {
|
|
561
717
|
return populate;
|
|
562
718
|
}
|
|
563
|
-
if (visited.includes(
|
|
719
|
+
if (!meta || visited.includes(meta)) {
|
|
564
720
|
return [];
|
|
565
721
|
}
|
|
566
|
-
visited.push(
|
|
722
|
+
visited.push(meta);
|
|
567
723
|
const ret = prefix === '' ? [...populate] : [];
|
|
568
724
|
meta.relations
|
|
569
725
|
.filter(prop => {
|
|
726
|
+
const field = this.getRelationName(meta, prop);
|
|
727
|
+
const prefixed = prefix ? `${prefix}.${field}` : field;
|
|
728
|
+
const isExcluded = exclude?.includes(prefixed);
|
|
570
729
|
const eager = prop.eager && !populate.some(p => p.field === `${prop.name}:ref`);
|
|
571
730
|
const populated = populate.some(p => p.field === prop.name);
|
|
572
731
|
const disabled = populate.some(p => p.field === prop.name && p.all === false);
|
|
573
|
-
return !disabled && (eager || populated);
|
|
732
|
+
return !disabled && !isExcluded && (eager || populated);
|
|
574
733
|
})
|
|
575
734
|
.forEach(prop => {
|
|
576
735
|
const field = this.getRelationName(meta, prop);
|
|
577
736
|
const prefixed = prefix ? `${prefix}.${field}` : field;
|
|
578
|
-
const nestedPopulate = populate
|
|
579
|
-
|
|
737
|
+
const nestedPopulate = populate
|
|
738
|
+
.filter(p => p.field === prop.name)
|
|
739
|
+
.flatMap(p => p.children)
|
|
740
|
+
.filter(Boolean);
|
|
741
|
+
const nested = this.lookupEagerLoadedRelationships(prop.targetMeta.class, nestedPopulate, strategy, prefixed, visited.slice(), exclude);
|
|
580
742
|
if (nested.length > 0) {
|
|
581
743
|
ret.push(...nested);
|
|
582
744
|
}
|
|
583
745
|
else {
|
|
584
|
-
const selfReferencing = [meta.
|
|
746
|
+
const selfReferencing = [meta.tableName, ...visited.map(m => m.tableName)].includes(prop.targetMeta.tableName) && prop.eager;
|
|
585
747
|
ret.push({
|
|
586
748
|
field: prefixed,
|
|
587
749
|
// enforce select-in strategy for self-referencing relations
|
|
588
|
-
strategy: selfReferencing ? LoadStrategy.SELECT_IN : strategy ?? prop.strategy,
|
|
750
|
+
strategy: selfReferencing ? LoadStrategy.SELECT_IN : (strategy ?? prop.strategy),
|
|
589
751
|
});
|
|
590
752
|
}
|
|
591
753
|
});
|