@mikro-orm/core 7.0.0-dev.27 → 7.0.0-dev.271
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 +92 -60
- package/EntityManager.js +311 -256
- package/MikroORM.d.ts +44 -35
- package/MikroORM.js +109 -143
- package/README.md +2 -0
- 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 +12 -5
- package/connections/Connection.js +21 -12
- package/drivers/DatabaseDriver.d.ts +25 -16
- package/drivers/DatabaseDriver.js +118 -35
- package/drivers/IDatabaseDriver.d.ts +75 -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 +436 -104
- package/entity/EntityAssigner.js +17 -17
- package/entity/EntityFactory.d.ts +7 -1
- package/entity/EntityFactory.js +87 -55
- package/entity/EntityHelper.d.ts +2 -2
- package/entity/EntityHelper.js +50 -17
- package/entity/EntityLoader.d.ts +11 -10
- package/entity/EntityLoader.js +221 -93
- 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 +1 -5
- package/entity/Reference.js +15 -11
- package/entity/WrappedEntity.d.ts +3 -8
- package/entity/WrappedEntity.js +2 -7
- package/entity/defineEntity.d.ts +512 -310
- package/entity/defineEntity.js +134 -287
- package/entity/index.d.ts +2 -2
- package/entity/index.js +2 -2
- package/entity/utils.d.ts +6 -1
- package/entity/utils.js +34 -1
- package/entity/validators.d.ts +11 -0
- package/entity/validators.js +65 -0
- package/enums.d.ts +8 -6
- package/enums.js +2 -1
- package/errors.d.ts +20 -10
- package/errors.js +55 -23
- package/events/EventManager.d.ts +2 -1
- package/events/EventManager.js +19 -11
- package/hydration/Hydrator.js +1 -2
- package/hydration/ObjectHydrator.d.ts +4 -4
- package/hydration/ObjectHydrator.js +87 -35
- 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 +47 -23
- package/metadata/EntitySchema.js +92 -33
- package/metadata/MetadataDiscovery.d.ts +64 -9
- package/metadata/MetadataDiscovery.js +779 -326
- package/metadata/MetadataProvider.d.ts +11 -2
- package/metadata/MetadataProvider.js +46 -2
- package/metadata/MetadataStorage.d.ts +13 -11
- package/metadata/MetadataStorage.js +70 -37
- package/metadata/MetadataValidator.d.ts +32 -9
- package/metadata/MetadataValidator.js +196 -41
- 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 +10 -15
- package/platforms/Platform.js +21 -44
- package/serialization/EntitySerializer.d.ts +6 -3
- package/serialization/EntitySerializer.js +46 -26
- package/serialization/EntityTransformer.js +33 -21
- package/serialization/SerializationContext.d.ts +6 -6
- package/serialization/SerializationContext.js +3 -3
- package/types/ArrayType.d.ts +1 -1
- package/types/ArrayType.js +2 -3
- package/types/BigIntType.d.ts +8 -6
- 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.d.ts +6 -4
- 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/index.d.ts +1 -1
- package/typings.d.ts +446 -181
- package/typings.js +99 -44
- 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 +41 -20
- package/unit-of-work/ChangeSetPersister.d.ts +13 -12
- package/unit-of-work/ChangeSetPersister.js +94 -36
- 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 +27 -3
- package/unit-of-work/UnitOfWork.js +248 -90
- package/utils/AbstractMigrator.d.ts +101 -0
- package/utils/AbstractMigrator.js +305 -0
- package/utils/AbstractSchemaGenerator.d.ts +5 -5
- package/utils/AbstractSchemaGenerator.js +28 -17
- package/utils/AsyncContext.d.ts +6 -0
- package/utils/AsyncContext.js +42 -0
- package/utils/Configuration.d.ts +797 -209
- package/utils/Configuration.js +150 -192
- package/utils/ConfigurationLoader.d.ts +1 -54
- package/utils/ConfigurationLoader.js +1 -352
- package/utils/Cursor.d.ts +0 -3
- package/utils/Cursor.js +24 -11
- package/utils/DataloaderUtils.d.ts +10 -5
- package/utils/DataloaderUtils.js +29 -12
- package/utils/EntityComparator.d.ts +16 -9
- package/utils/EntityComparator.js +157 -57
- package/utils/QueryHelper.d.ts +18 -6
- package/utils/QueryHelper.js +76 -23
- package/utils/RawQueryFragment.d.ts +28 -34
- package/utils/RawQueryFragment.js +35 -71
- package/utils/RequestContext.js +2 -2
- package/utils/TransactionContext.js +2 -2
- package/utils/TransactionManager.js +28 -4
- package/utils/Utils.d.ts +14 -127
- package/utils/Utils.js +80 -396
- 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 +1 -3
- package/utils/index.js +1 -3
- package/utils/upsert-utils.d.ts +9 -4
- package/utils/upsert-utils.js +46 -3
- 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/EntityManager.js
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import DataLoader from 'dataloader';
|
|
3
|
-
import { getOnConflictReturningFields } from './utils/upsert-utils.js';
|
|
1
|
+
import { getOnConflictReturningFields, getWhereCondition } from './utils/upsert-utils.js';
|
|
4
2
|
import { Utils } from './utils/Utils.js';
|
|
5
3
|
import { Cursor } from './utils/Cursor.js';
|
|
6
|
-
import { DataloaderUtils } from './utils/DataloaderUtils.js';
|
|
7
4
|
import { QueryHelper } from './utils/QueryHelper.js';
|
|
8
5
|
import { TransactionContext } from './utils/TransactionContext.js';
|
|
9
|
-
import { isRaw,
|
|
6
|
+
import { isRaw, Raw } from './utils/RawQueryFragment.js';
|
|
10
7
|
import { EntityFactory } from './entity/EntityFactory.js';
|
|
11
8
|
import { EntityAssigner } from './entity/EntityAssigner.js';
|
|
12
|
-
import {
|
|
9
|
+
import { validateEmptyWhere, validateParams, validatePrimaryKey, validateProperty } from './entity/validators.js';
|
|
13
10
|
import { EntityLoader } from './entity/EntityLoader.js';
|
|
14
11
|
import { Reference } from './entity/Reference.js';
|
|
15
12
|
import { helper } from './entity/wrap.js';
|
|
@@ -19,7 +16,7 @@ import { EventType, FlushMode, LoadStrategy, LockMode, PopulateHint, PopulatePat
|
|
|
19
16
|
import { EventManager } from './events/EventManager.js';
|
|
20
17
|
import { TransactionEventBroadcaster } from './events/TransactionEventBroadcaster.js';
|
|
21
18
|
import { OptimisticLockError, ValidationError } from './errors.js';
|
|
22
|
-
import { getLoadingStrategy } from './entity/utils.js';
|
|
19
|
+
import { applyPopulateHints, getLoadingStrategy } from './entity/utils.js';
|
|
23
20
|
import { TransactionManager } from './utils/TransactionManager.js';
|
|
24
21
|
/**
|
|
25
22
|
* The EntityManager is the central access point to ORM functionality. It is a facade to all different ORM subsystems
|
|
@@ -36,11 +33,8 @@ export class EntityManager {
|
|
|
36
33
|
_id = EntityManager.counter++;
|
|
37
34
|
global = false;
|
|
38
35
|
name;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
colLoaderMtoN = new DataLoader(DataloaderUtils.getManyToManyColBatchLoadFn(this));
|
|
42
|
-
validator;
|
|
43
|
-
repositoryMap = {};
|
|
36
|
+
loaders = {};
|
|
37
|
+
repositoryMap = new Map();
|
|
44
38
|
entityLoader;
|
|
45
39
|
comparator;
|
|
46
40
|
entityFactory;
|
|
@@ -64,7 +58,6 @@ export class EntityManager {
|
|
|
64
58
|
this.eventManager = eventManager;
|
|
65
59
|
this.entityLoader = new EntityLoader(this);
|
|
66
60
|
this.name = this.config.get('contextName');
|
|
67
|
-
this.validator = new EntityValidator(this.config.get('strict'));
|
|
68
61
|
this.comparator = this.config.getComparator(this.metadata);
|
|
69
62
|
this.resultCache = this.config.getResultCacheAdapter();
|
|
70
63
|
this.disableTransactions = this.config.get('disableTransactions');
|
|
@@ -94,13 +87,12 @@ export class EntityManager {
|
|
|
94
87
|
* Gets repository for given entity. You can pass either string name or entity class reference.
|
|
95
88
|
*/
|
|
96
89
|
getRepository(entityName) {
|
|
97
|
-
|
|
98
|
-
if (!this.repositoryMap
|
|
99
|
-
const meta = this.metadata.get(entityName);
|
|
90
|
+
const meta = this.metadata.get(entityName);
|
|
91
|
+
if (!this.repositoryMap.has(meta)) {
|
|
100
92
|
const RepositoryClass = this.config.getRepositoryClass(meta.repository);
|
|
101
|
-
this.repositoryMap
|
|
93
|
+
this.repositoryMap.set(meta, new RepositoryClass(this, entityName));
|
|
102
94
|
}
|
|
103
|
-
return this.repositoryMap
|
|
95
|
+
return this.repositoryMap.get(meta);
|
|
104
96
|
}
|
|
105
97
|
/**
|
|
106
98
|
* Shortcut for `em.getRepository()`.
|
|
@@ -108,12 +100,6 @@ export class EntityManager {
|
|
|
108
100
|
repo(entityName) {
|
|
109
101
|
return this.getRepository(entityName);
|
|
110
102
|
}
|
|
111
|
-
/**
|
|
112
|
-
* Gets EntityValidator instance
|
|
113
|
-
*/
|
|
114
|
-
getValidator() {
|
|
115
|
-
return this.validator;
|
|
116
|
-
}
|
|
117
103
|
/**
|
|
118
104
|
* Finds all entities matching your `where` query. You can pass additional options via the `options` parameter.
|
|
119
105
|
*/
|
|
@@ -128,10 +114,15 @@ export class EntityManager {
|
|
|
128
114
|
const em = this.getContext();
|
|
129
115
|
em.prepareOptions(options);
|
|
130
116
|
await em.tryFlush(entityName, options);
|
|
131
|
-
entityName = Utils.className(entityName);
|
|
132
117
|
where = await em.processWhere(entityName, where, options, 'read');
|
|
133
|
-
|
|
134
|
-
|
|
118
|
+
validateParams(where);
|
|
119
|
+
const meta = this.metadata.get(entityName);
|
|
120
|
+
if (meta.orderBy) {
|
|
121
|
+
options.orderBy = QueryHelper.mergeOrderBy(options.orderBy, meta.orderBy);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
options.orderBy ??= {};
|
|
125
|
+
}
|
|
135
126
|
options.populate = await em.preparePopulate(entityName, options);
|
|
136
127
|
const populate = options.populate;
|
|
137
128
|
const cacheKey = em.cacheKey(entityName, options, 'em.find', where);
|
|
@@ -145,12 +136,11 @@ export class EntityManager {
|
|
|
145
136
|
});
|
|
146
137
|
return cached.data;
|
|
147
138
|
}
|
|
148
|
-
const meta = this.metadata.get(entityName);
|
|
149
139
|
options = { ...options };
|
|
150
140
|
// save the original hint value so we know it was infer/all
|
|
151
141
|
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
152
142
|
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
153
|
-
options.populateFilter = await this.getJoinedFilters(meta,
|
|
143
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
154
144
|
const results = await em.driver.find(entityName, where, { ctx: em.transactionContext, em, ...options });
|
|
155
145
|
if (results.length === 0) {
|
|
156
146
|
await em.storeCache(options.cache, cached, []);
|
|
@@ -182,6 +172,60 @@ export class EntityManager {
|
|
|
182
172
|
}
|
|
183
173
|
return unique;
|
|
184
174
|
}
|
|
175
|
+
/**
|
|
176
|
+
* Finds all entities and returns an async iterable (async generator) that yields results one by one.
|
|
177
|
+
* The results are merged and mapped to entity instances, without adding them to the identity map.
|
|
178
|
+
* You can disable merging by passing the options `{ mergeResults: false }`.
|
|
179
|
+
* With `mergeResults` disabled, to-many collections will contain at most one item, and you will get duplicate
|
|
180
|
+
* root entities when there are multiple items in the populated collection.
|
|
181
|
+
* This is useful for processing large datasets without loading everything into memory at once.
|
|
182
|
+
*
|
|
183
|
+
* ```ts
|
|
184
|
+
* const stream = em.stream(Book, { populate: ['author'] });
|
|
185
|
+
*
|
|
186
|
+
* for await (const book of stream) {
|
|
187
|
+
* // book is an instance of Book entity
|
|
188
|
+
* console.log(book.title, book.author.name);
|
|
189
|
+
* }
|
|
190
|
+
* ```
|
|
191
|
+
*/
|
|
192
|
+
async *stream(entityName, options = {}) {
|
|
193
|
+
const em = this.getContext();
|
|
194
|
+
em.prepareOptions(options);
|
|
195
|
+
options.strategy = 'joined';
|
|
196
|
+
await em.tryFlush(entityName, options);
|
|
197
|
+
const where = await em.processWhere(entityName, options.where ?? {}, options, 'read');
|
|
198
|
+
validateParams(where);
|
|
199
|
+
options.orderBy = options.orderBy || {};
|
|
200
|
+
options.populate = await em.preparePopulate(entityName, options);
|
|
201
|
+
const meta = this.metadata.get(entityName);
|
|
202
|
+
options = { ...options };
|
|
203
|
+
// save the original hint value so we know it was infer/all
|
|
204
|
+
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
205
|
+
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
206
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
207
|
+
const stream = em.driver.stream(entityName, where, {
|
|
208
|
+
ctx: em.transactionContext,
|
|
209
|
+
mapResults: false,
|
|
210
|
+
...options,
|
|
211
|
+
});
|
|
212
|
+
for await (const data of stream) {
|
|
213
|
+
const fork = em.fork();
|
|
214
|
+
const entity = fork.entityFactory.create(entityName, data, {
|
|
215
|
+
refresh: options.refresh,
|
|
216
|
+
schema: options.schema,
|
|
217
|
+
convertCustomTypes: true,
|
|
218
|
+
});
|
|
219
|
+
helper(entity).setSerializationContext({
|
|
220
|
+
populate: options.populate,
|
|
221
|
+
fields: options.fields,
|
|
222
|
+
exclude: options.exclude,
|
|
223
|
+
});
|
|
224
|
+
await fork.unitOfWork.dispatchOnLoadEvent();
|
|
225
|
+
fork.clear();
|
|
226
|
+
yield entity;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
185
229
|
/**
|
|
186
230
|
* Finds all entities of given type, optionally matching the `where` condition provided in the `options` parameter.
|
|
187
231
|
*/
|
|
@@ -195,7 +239,7 @@ export class EntityManager {
|
|
|
195
239
|
if (options.populateWhere === PopulateHint.ALL) {
|
|
196
240
|
return { where: {}, populateWhere: options.populateWhere };
|
|
197
241
|
}
|
|
198
|
-
/* v8 ignore next
|
|
242
|
+
/* v8 ignore next */
|
|
199
243
|
if (options.populateWhere === PopulateHint.INFER) {
|
|
200
244
|
return { where, populateWhere: options.populateWhere };
|
|
201
245
|
}
|
|
@@ -204,12 +248,12 @@ export class EntityManager {
|
|
|
204
248
|
/**
|
|
205
249
|
* Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
|
|
206
250
|
*/
|
|
207
|
-
addFilter(
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
options.entity = Utils.asArray(entityName).map(n => Utils.className(n));
|
|
251
|
+
addFilter(options) {
|
|
252
|
+
if (options.entity) {
|
|
253
|
+
options.entity = Utils.asArray(options.entity).map(n => Utils.className(n));
|
|
211
254
|
}
|
|
212
|
-
|
|
255
|
+
options.default ??= true;
|
|
256
|
+
this.getContext(false).filters[options.name] = options;
|
|
213
257
|
}
|
|
214
258
|
/**
|
|
215
259
|
* Sets filter parameter values globally inside context defined by this entity manager.
|
|
@@ -257,18 +301,18 @@ export class EntityManager {
|
|
|
257
301
|
// this method only handles the problem for mongo driver, SQL drivers have their implementation inside QueryBuilder
|
|
258
302
|
applyDiscriminatorCondition(entityName, where) {
|
|
259
303
|
const meta = this.metadata.find(entityName);
|
|
260
|
-
if (!meta?.discriminatorValue) {
|
|
304
|
+
if (meta?.root.inheritanceType !== 'sti' || !meta?.discriminatorValue) {
|
|
261
305
|
return where;
|
|
262
306
|
}
|
|
263
|
-
const types = Object.values(meta.root.discriminatorMap).map(cls => this.metadata.
|
|
307
|
+
const types = Object.values(meta.root.discriminatorMap).map(cls => this.metadata.get(cls));
|
|
264
308
|
const children = [];
|
|
265
309
|
const lookUpChildren = (ret, type) => {
|
|
266
310
|
const children = types.filter(meta2 => meta2.extends === type);
|
|
267
|
-
children.forEach(m => lookUpChildren(ret, m.
|
|
311
|
+
children.forEach(m => lookUpChildren(ret, m.class));
|
|
268
312
|
ret.push(...children.filter(c => c.discriminatorValue));
|
|
269
313
|
return children;
|
|
270
314
|
};
|
|
271
|
-
lookUpChildren(children, meta.
|
|
315
|
+
lookUpChildren(children, meta.class);
|
|
272
316
|
/* v8 ignore next */
|
|
273
317
|
where[meta.root.discriminatorColumn] = children.length > 0 ? { $in: [meta.discriminatorValue, ...children.map(c => c.discriminatorValue)] } : meta.discriminatorValue;
|
|
274
318
|
return where;
|
|
@@ -284,60 +328,77 @@ export class EntityManager {
|
|
|
284
328
|
}
|
|
285
329
|
return ret;
|
|
286
330
|
}
|
|
287
|
-
async getJoinedFilters(meta,
|
|
331
|
+
async getJoinedFilters(meta, options) {
|
|
332
|
+
// If user provided populateFilter, merge it with computed filters
|
|
333
|
+
const userFilter = options.populateFilter;
|
|
334
|
+
if (!this.config.get('filtersOnRelations') || !options.populate) {
|
|
335
|
+
return userFilter;
|
|
336
|
+
}
|
|
288
337
|
const ret = {};
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
338
|
+
for (const hint of options.populate) {
|
|
339
|
+
const field = hint.field.split(':')[0];
|
|
340
|
+
const prop = meta.properties[field];
|
|
341
|
+
const strategy = getLoadingStrategy(prop.strategy || hint.strategy || options.strategy || this.config.get('loadStrategy'), prop.kind);
|
|
342
|
+
const joined = strategy === LoadStrategy.JOINED && prop.kind !== ReferenceKind.SCALAR;
|
|
343
|
+
if (!joined && !hint.filter) {
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
const filters = QueryHelper.mergePropertyFilters(prop.filters, options.filters);
|
|
347
|
+
const where = await this.applyFilters(prop.targetMeta.class, {}, filters, 'read', {
|
|
348
|
+
...options,
|
|
349
|
+
populate: hint.children,
|
|
350
|
+
});
|
|
351
|
+
const where2 = await this.getJoinedFilters(prop.targetMeta, {
|
|
352
|
+
...options,
|
|
353
|
+
filters,
|
|
354
|
+
populate: hint.children,
|
|
355
|
+
populateWhere: PopulateHint.ALL,
|
|
356
|
+
});
|
|
357
|
+
if (Utils.hasObjectKeys(where)) {
|
|
358
|
+
ret[field] = ret[field] ? { $and: [where, ret[field]] } : where;
|
|
359
|
+
}
|
|
360
|
+
if (where2 && Utils.hasObjectKeys(where2)) {
|
|
361
|
+
if (ret[field]) {
|
|
362
|
+
Utils.merge(ret[field], where2);
|
|
302
363
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
Utils.merge(ret[field], where2);
|
|
306
|
-
}
|
|
307
|
-
else {
|
|
308
|
-
ret[field] = where2;
|
|
309
|
-
}
|
|
364
|
+
else {
|
|
365
|
+
ret[field] = where2;
|
|
310
366
|
}
|
|
311
367
|
}
|
|
312
368
|
}
|
|
313
|
-
|
|
369
|
+
// Merge user-provided populateFilter with computed filters
|
|
370
|
+
if (userFilter) {
|
|
371
|
+
Utils.merge(ret, userFilter);
|
|
372
|
+
}
|
|
373
|
+
return Utils.hasObjectKeys(ret) ? ret : undefined;
|
|
314
374
|
}
|
|
315
375
|
/**
|
|
316
376
|
* When filters are active on M:1 or 1:1 relations, we need to ref join them eagerly as they might affect the FK value.
|
|
317
377
|
*/
|
|
318
|
-
async autoJoinRefsForFilters(meta, options) {
|
|
319
|
-
if (!meta || !this.config.get('autoJoinRefsForFilters')) {
|
|
378
|
+
async autoJoinRefsForFilters(meta, options, parent) {
|
|
379
|
+
if (!meta || !this.config.get('autoJoinRefsForFilters') || options.filters === false) {
|
|
320
380
|
return;
|
|
321
381
|
}
|
|
322
|
-
const props = meta.relations.filter(prop => {
|
|
323
|
-
return !prop.object && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)
|
|
324
|
-
&& ((options.fields?.length ?? 0) === 0 || options.fields?.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)));
|
|
325
|
-
});
|
|
326
382
|
const ret = options.populate;
|
|
327
|
-
for (const prop of
|
|
328
|
-
|
|
383
|
+
for (const prop of meta.relations) {
|
|
384
|
+
if (prop.object
|
|
385
|
+
|| ![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)
|
|
386
|
+
|| !((options.fields?.length ?? 0) === 0 || options.fields?.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
|
|
387
|
+
|| (parent?.class === prop.targetMeta.root.class && parent.propName === prop.inversedBy)) {
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
options = { ...options, filters: QueryHelper.mergePropertyFilters(prop.filters, options.filters) };
|
|
391
|
+
const cond = await this.applyFilters(prop.targetMeta.class, {}, options.filters, 'read', options);
|
|
329
392
|
if (!Utils.isEmpty(cond)) {
|
|
330
393
|
const populated = options.populate.filter(({ field }) => field.split(':')[0] === prop.name);
|
|
331
394
|
let found = false;
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
found = true;
|
|
340
|
-
}
|
|
395
|
+
for (const hint of populated) {
|
|
396
|
+
if (!hint.all) {
|
|
397
|
+
hint.filter = true;
|
|
398
|
+
}
|
|
399
|
+
const strategy = getLoadingStrategy(prop.strategy || hint.strategy || options.strategy || this.config.get('loadStrategy'), prop.kind);
|
|
400
|
+
if (hint.field === `${prop.name}:ref` || (hint.filter && strategy === LoadStrategy.JOINED)) {
|
|
401
|
+
found = true;
|
|
341
402
|
}
|
|
342
403
|
}
|
|
343
404
|
if (!found) {
|
|
@@ -345,21 +406,26 @@ export class EntityManager {
|
|
|
345
406
|
}
|
|
346
407
|
}
|
|
347
408
|
}
|
|
409
|
+
for (const hint of ret) {
|
|
410
|
+
const [field, ref] = hint.field.split(':');
|
|
411
|
+
const prop = meta?.properties[field];
|
|
412
|
+
if (prop && !ref) {
|
|
413
|
+
hint.children ??= [];
|
|
414
|
+
await this.autoJoinRefsForFilters(prop.targetMeta, { ...options, populate: hint.children }, { class: meta.root.class, propName: prop.name });
|
|
415
|
+
}
|
|
416
|
+
}
|
|
348
417
|
}
|
|
349
418
|
/**
|
|
350
419
|
* @internal
|
|
351
420
|
*/
|
|
352
421
|
async applyFilters(entityName, where, options, type, findOptions) {
|
|
353
|
-
const meta = this.metadata.
|
|
422
|
+
const meta = this.metadata.get(entityName);
|
|
354
423
|
const filters = [];
|
|
355
424
|
const ret = [];
|
|
356
|
-
if (!meta) {
|
|
357
|
-
return where;
|
|
358
|
-
}
|
|
359
425
|
const active = new Set();
|
|
360
426
|
const push = (source) => {
|
|
361
427
|
const activeFilters = QueryHelper
|
|
362
|
-
.getActiveFilters(
|
|
428
|
+
.getActiveFilters(meta, options, source)
|
|
363
429
|
.filter(f => !active.has(f.name));
|
|
364
430
|
filters.push(...activeFilters);
|
|
365
431
|
activeFilters.forEach(f => active.add(f.name));
|
|
@@ -374,24 +440,28 @@ export class EntityManager {
|
|
|
374
440
|
let cond;
|
|
375
441
|
if (filter.cond instanceof Function) {
|
|
376
442
|
// @ts-ignore
|
|
377
|
-
const args = Utils.isPlainObject(options[filter.name]) ? options[filter.name] : this.getContext().filterParams[filter.name];
|
|
443
|
+
const args = Utils.isPlainObject(options?.[filter.name]) ? options[filter.name] : this.getContext().filterParams[filter.name];
|
|
378
444
|
if (!args && filter.cond.length > 0 && filter.args !== false) {
|
|
379
445
|
throw new Error(`No arguments provided for filter '${filter.name}'`);
|
|
380
446
|
}
|
|
381
|
-
cond = await filter.cond(args, type, this, findOptions, entityName);
|
|
447
|
+
cond = await filter.cond(args, type, this, findOptions, Utils.className(entityName));
|
|
382
448
|
}
|
|
383
449
|
else {
|
|
384
450
|
cond = filter.cond;
|
|
385
451
|
}
|
|
386
|
-
|
|
452
|
+
cond = QueryHelper.processWhere({
|
|
387
453
|
where: cond,
|
|
388
454
|
entityName,
|
|
389
455
|
metadata: this.metadata,
|
|
390
456
|
platform: this.driver.getPlatform(),
|
|
391
457
|
aliased: type === 'read',
|
|
392
|
-
})
|
|
458
|
+
});
|
|
459
|
+
if (filter.strict) {
|
|
460
|
+
Object.defineProperty(cond, '__strict', { value: filter.strict, enumerable: false });
|
|
461
|
+
}
|
|
462
|
+
ret.push(cond);
|
|
393
463
|
}
|
|
394
|
-
const conds = [...ret, where].filter(c => Utils.hasObjectKeys(c));
|
|
464
|
+
const conds = [...ret, where].filter(c => Utils.hasObjectKeys(c) || Raw.hasObjectFragments(c));
|
|
395
465
|
return conds.length > 1 ? { $and: conds } : conds[0];
|
|
396
466
|
}
|
|
397
467
|
/**
|
|
@@ -402,12 +472,10 @@ export class EntityManager {
|
|
|
402
472
|
const em = this.getContext(false);
|
|
403
473
|
await em.tryFlush(entityName, options);
|
|
404
474
|
options.flushMode = 'commit'; // do not try to auto flush again
|
|
405
|
-
return
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
]);
|
|
410
|
-
});
|
|
475
|
+
return Promise.all([
|
|
476
|
+
em.find(entityName, where, options),
|
|
477
|
+
em.count(entityName, where, options),
|
|
478
|
+
]);
|
|
411
479
|
}
|
|
412
480
|
/**
|
|
413
481
|
* Calls `em.find()` and `em.count()` with the same arguments (where applicable) and returns the results as {@apilink Cursor} object.
|
|
@@ -423,21 +491,21 @@ export class EntityManager {
|
|
|
423
491
|
* - POJO/entity instance
|
|
424
492
|
*
|
|
425
493
|
* ```ts
|
|
426
|
-
* const currentCursor = await em.findByCursor(User, {
|
|
494
|
+
* const currentCursor = await em.findByCursor(User, {
|
|
427
495
|
* first: 10,
|
|
428
496
|
* after: previousCursor, // cursor instance
|
|
429
497
|
* orderBy: { id: 'desc' },
|
|
430
498
|
* });
|
|
431
499
|
*
|
|
432
500
|
* // to fetch next page
|
|
433
|
-
* const nextCursor = await em.findByCursor(User, {
|
|
501
|
+
* const nextCursor = await em.findByCursor(User, {
|
|
434
502
|
* first: 10,
|
|
435
503
|
* after: currentCursor.endCursor, // opaque string
|
|
436
504
|
* orderBy: { id: 'desc' },
|
|
437
505
|
* });
|
|
438
506
|
*
|
|
439
507
|
* // to fetch next page
|
|
440
|
-
* const nextCursor2 = await em.findByCursor(User, {
|
|
508
|
+
* const nextCursor2 = await em.findByCursor(User, {
|
|
441
509
|
* first: 10,
|
|
442
510
|
* after: { id: lastSeenId }, // entity-like POJO
|
|
443
511
|
* orderBy: { id: 'desc' },
|
|
@@ -465,16 +533,16 @@ export class EntityManager {
|
|
|
465
533
|
* }
|
|
466
534
|
* ```
|
|
467
535
|
*/
|
|
468
|
-
async findByCursor(entityName,
|
|
536
|
+
async findByCursor(entityName, options) {
|
|
469
537
|
const em = this.getContext(false);
|
|
470
|
-
entityName = Utils.className(entityName);
|
|
471
538
|
options.overfetch ??= true;
|
|
472
|
-
|
|
539
|
+
options.where ??= {};
|
|
540
|
+
if (Utils.isEmpty(options.orderBy) && !Raw.hasObjectFragments(options.orderBy)) {
|
|
473
541
|
throw new Error('Explicit `orderBy` option required');
|
|
474
542
|
}
|
|
475
543
|
const [entities, count] = options.includeCount !== false
|
|
476
|
-
? await em.findAndCount(entityName, where, options)
|
|
477
|
-
: [await em.find(entityName, where, options)];
|
|
544
|
+
? await em.findAndCount(entityName, options.where, options)
|
|
545
|
+
: [await em.find(entityName, options.where, options)];
|
|
478
546
|
return new Cursor(entities, count, options, this.metadata.get(entityName));
|
|
479
547
|
}
|
|
480
548
|
/**
|
|
@@ -486,9 +554,9 @@ export class EntityManager {
|
|
|
486
554
|
const ret = await this.refresh(entity, options);
|
|
487
555
|
if (!ret) {
|
|
488
556
|
options.failHandler ??= this.config.get('findOneOrFailHandler');
|
|
489
|
-
const
|
|
490
|
-
const where =
|
|
491
|
-
throw options.failHandler(
|
|
557
|
+
const wrapped = helper(entity);
|
|
558
|
+
const where = wrapped.getPrimaryKey();
|
|
559
|
+
throw options.failHandler(wrapped.__meta.className, where);
|
|
492
560
|
}
|
|
493
561
|
return ret;
|
|
494
562
|
}
|
|
@@ -499,25 +567,31 @@ export class EntityManager {
|
|
|
499
567
|
*/
|
|
500
568
|
async refresh(entity, options = {}) {
|
|
501
569
|
const fork = this.fork({ keepTransactionContext: true });
|
|
502
|
-
const
|
|
503
|
-
const reloaded = await fork.findOne(
|
|
504
|
-
schema:
|
|
570
|
+
const wrapped = helper(entity);
|
|
571
|
+
const reloaded = await fork.findOne(wrapped.__meta.class, entity, {
|
|
572
|
+
schema: wrapped.__schema,
|
|
505
573
|
...options,
|
|
506
574
|
flushMode: FlushMode.COMMIT,
|
|
507
575
|
});
|
|
508
576
|
const em = this.getContext();
|
|
509
|
-
if (reloaded) {
|
|
510
|
-
for (const e of fork.unitOfWork.getIdentityMap()) {
|
|
511
|
-
const ref = em.getReference(e.constructor.name, helper(e).getPrimaryKey());
|
|
512
|
-
const data = this.comparator.prepareEntity(e);
|
|
513
|
-
em.config.getHydrator(this.metadata).hydrate(ref, helper(ref).__meta, helper(e).serialize({ ignoreSerializers: true, includeHidden: true }), em.entityFactory, 'full', false, true);
|
|
514
|
-
helper(ref).__originalEntityData = data;
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
else {
|
|
577
|
+
if (!reloaded) {
|
|
518
578
|
em.unitOfWork.unsetIdentity(entity);
|
|
579
|
+
return null;
|
|
519
580
|
}
|
|
520
|
-
|
|
581
|
+
let found = false;
|
|
582
|
+
for (const e of fork.unitOfWork.getIdentityMap()) {
|
|
583
|
+
const ref = em.getReference(e.constructor, helper(e).getPrimaryKey());
|
|
584
|
+
const data = helper(e).serialize({ ignoreSerializers: true, includeHidden: true, convertCustomTypes: false });
|
|
585
|
+
em.config.getHydrator(this.metadata).hydrate(ref, helper(ref).__meta, data, em.entityFactory, 'full', false, false);
|
|
586
|
+
Utils.merge(helper(ref).__originalEntityData, this.comparator.prepareEntity(e));
|
|
587
|
+
found ||= ref === entity;
|
|
588
|
+
}
|
|
589
|
+
if (!found) {
|
|
590
|
+
const data = helper(reloaded).serialize({ ignoreSerializers: true, includeHidden: true, convertCustomTypes: true });
|
|
591
|
+
em.config.getHydrator(this.metadata).hydrate(entity, wrapped.__meta, data, em.entityFactory, 'full', false, true);
|
|
592
|
+
Utils.merge(wrapped.__originalEntityData, this.comparator.prepareEntity(reloaded));
|
|
593
|
+
}
|
|
594
|
+
return entity;
|
|
521
595
|
}
|
|
522
596
|
/**
|
|
523
597
|
* Finds first entity matching your `where` query.
|
|
@@ -531,7 +605,6 @@ export class EntityManager {
|
|
|
531
605
|
return ret;
|
|
532
606
|
}
|
|
533
607
|
const em = this.getContext();
|
|
534
|
-
entityName = Utils.className(entityName);
|
|
535
608
|
em.prepareOptions(options);
|
|
536
609
|
let entity = em.unitOfWork.tryGetById(entityName, where, options.schema);
|
|
537
610
|
// query for a not managed entity which is already in the identity map as it
|
|
@@ -543,13 +616,13 @@ export class EntityManager {
|
|
|
543
616
|
await em.tryFlush(entityName, options);
|
|
544
617
|
const meta = em.metadata.get(entityName);
|
|
545
618
|
where = await em.processWhere(entityName, where, options, 'read');
|
|
546
|
-
|
|
619
|
+
validateEmptyWhere(where);
|
|
547
620
|
em.checkLockRequirements(options.lockMode, meta);
|
|
548
|
-
const isOptimisticLocking =
|
|
621
|
+
const isOptimisticLocking = options.lockMode == null || options.lockMode === LockMode.OPTIMISTIC;
|
|
549
622
|
if (entity && !em.shouldRefresh(meta, entity, options) && isOptimisticLocking) {
|
|
550
623
|
return em.lockAndPopulate(meta, entity, where, options);
|
|
551
624
|
}
|
|
552
|
-
|
|
625
|
+
validateParams(where);
|
|
553
626
|
options.populate = await em.preparePopulate(entityName, options);
|
|
554
627
|
const cacheKey = em.cacheKey(entityName, options, 'em.findOne', where);
|
|
555
628
|
const cached = await em.tryCache(entityName, options.cache, cacheKey, options.refresh, true);
|
|
@@ -568,7 +641,7 @@ export class EntityManager {
|
|
|
568
641
|
// save the original hint value so we know it was infer/all
|
|
569
642
|
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
570
643
|
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
571
|
-
options.populateFilter = await this.getJoinedFilters(meta,
|
|
644
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
572
645
|
const data = await em.driver.findOne(entityName, where, {
|
|
573
646
|
ctx: em.transactionContext,
|
|
574
647
|
em,
|
|
@@ -609,10 +682,10 @@ export class EntityManager {
|
|
|
609
682
|
if (!entity || isStrictViolation) {
|
|
610
683
|
const key = options.strict ? 'findExactlyOneOrFailHandler' : 'findOneOrFailHandler';
|
|
611
684
|
options.failHandler ??= this.config.get(key);
|
|
612
|
-
|
|
685
|
+
const name = Utils.className(entityName);
|
|
613
686
|
/* v8 ignore next */
|
|
614
687
|
where = Utils.isEntity(where) ? helper(where).getPrimaryKey() : where;
|
|
615
|
-
throw options.failHandler(
|
|
688
|
+
throw options.failHandler(name, where);
|
|
616
689
|
}
|
|
617
690
|
return entity;
|
|
618
691
|
}
|
|
@@ -652,11 +725,11 @@ export class EntityManager {
|
|
|
652
725
|
let where;
|
|
653
726
|
let entity = null;
|
|
654
727
|
if (data === undefined) {
|
|
655
|
-
entityName = entityNameOrEntity.constructor
|
|
728
|
+
entityName = entityNameOrEntity.constructor;
|
|
656
729
|
data = entityNameOrEntity;
|
|
657
730
|
}
|
|
658
731
|
else {
|
|
659
|
-
entityName =
|
|
732
|
+
entityName = entityNameOrEntity;
|
|
660
733
|
}
|
|
661
734
|
const meta = this.metadata.get(entityName);
|
|
662
735
|
const convertCustomTypes = !Utils.isEntity(data);
|
|
@@ -679,26 +752,9 @@ export class EntityManager {
|
|
|
679
752
|
}
|
|
680
753
|
}
|
|
681
754
|
}
|
|
682
|
-
|
|
683
|
-
const propIndex = !isRaw(unique) && unique.findIndex(p => data[p] != null);
|
|
684
|
-
if (options.onConflictFields || where == null) {
|
|
685
|
-
if (propIndex !== false && propIndex >= 0) {
|
|
686
|
-
where = { [unique[propIndex]]: data[unique[propIndex]] };
|
|
687
|
-
}
|
|
688
|
-
else if (meta.uniques.length > 0) {
|
|
689
|
-
for (const u of meta.uniques) {
|
|
690
|
-
if (Utils.asArray(u.properties).every(p => data[p] != null)) {
|
|
691
|
-
where = Utils.asArray(u.properties).reduce((o, key) => {
|
|
692
|
-
o[key] = data[key];
|
|
693
|
-
return o;
|
|
694
|
-
}, {});
|
|
695
|
-
break;
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
}
|
|
755
|
+
where = getWhereCondition(meta, options.onConflictFields, data, where).where;
|
|
700
756
|
data = QueryHelper.processObjectParams(data);
|
|
701
|
-
|
|
757
|
+
validateParams(data, 'insert data');
|
|
702
758
|
if (em.eventManager.hasListeners(EventType.beforeUpsert, meta)) {
|
|
703
759
|
await em.eventManager.dispatchEvent(EventType.beforeUpsert, { entity: data, em, meta }, meta);
|
|
704
760
|
}
|
|
@@ -736,7 +792,7 @@ export class EntityManager {
|
|
|
736
792
|
where[meta.primaryKeys[0]] = ret.insertId;
|
|
737
793
|
}
|
|
738
794
|
}
|
|
739
|
-
const data2 = await this.driver.findOne(meta.
|
|
795
|
+
const data2 = await this.driver.findOne(meta.class, where, {
|
|
740
796
|
fields: returning,
|
|
741
797
|
ctx: em.transactionContext,
|
|
742
798
|
convertCustomTypes: true,
|
|
@@ -791,11 +847,11 @@ export class EntityManager {
|
|
|
791
847
|
let entityName;
|
|
792
848
|
let propIndex;
|
|
793
849
|
if (data === undefined) {
|
|
794
|
-
entityName = entityNameOrEntity[0].constructor
|
|
850
|
+
entityName = entityNameOrEntity[0].constructor;
|
|
795
851
|
data = entityNameOrEntity;
|
|
796
852
|
}
|
|
797
853
|
else {
|
|
798
|
-
entityName =
|
|
854
|
+
entityName = entityNameOrEntity;
|
|
799
855
|
}
|
|
800
856
|
const batchSize = options.batchSize ?? this.config.get('batchSize');
|
|
801
857
|
if (data.length > batchSize) {
|
|
@@ -839,32 +895,18 @@ export class EntityManager {
|
|
|
839
895
|
}
|
|
840
896
|
}
|
|
841
897
|
}
|
|
842
|
-
const unique = meta.props.filter(p => p.unique).map(p => p.name);
|
|
843
|
-
propIndex = unique.findIndex(p =>
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
where = { [unique[propIndex]]: row[unique[propIndex]] };
|
|
847
|
-
}
|
|
848
|
-
else if (meta.uniques.length > 0) {
|
|
849
|
-
for (const u of meta.uniques) {
|
|
850
|
-
if (Utils.asArray(u.properties).every(p => row[p] != null)) {
|
|
851
|
-
where = Utils.asArray(u.properties).reduce((o, key) => {
|
|
852
|
-
o[key] = row[key];
|
|
853
|
-
return o;
|
|
854
|
-
}, {});
|
|
855
|
-
break;
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
|
-
}
|
|
859
|
-
}
|
|
860
|
-
row = QueryHelper.processObjectParams(row);
|
|
898
|
+
const unique = options.onConflictFields ?? meta.props.filter(p => p.unique).map(p => p.name);
|
|
899
|
+
propIndex = !isRaw(unique) && unique.findIndex(p => data[p] ?? data[p.substring(0, p.indexOf('.'))] != null);
|
|
900
|
+
const tmp = getWhereCondition(meta, options.onConflictFields, row, where);
|
|
901
|
+
propIndex = tmp.propIndex;
|
|
861
902
|
where = QueryHelper.processWhere({
|
|
862
|
-
where,
|
|
903
|
+
where: tmp.where,
|
|
863
904
|
entityName,
|
|
864
905
|
metadata: this.metadata,
|
|
865
906
|
platform: this.getPlatform(),
|
|
866
907
|
});
|
|
867
|
-
|
|
908
|
+
row = QueryHelper.processObjectParams(row);
|
|
909
|
+
validateParams(row, 'insert data');
|
|
868
910
|
allData.push(row);
|
|
869
911
|
allWhere.push(where);
|
|
870
912
|
}
|
|
@@ -905,7 +947,7 @@ export class EntityManager {
|
|
|
905
947
|
const reloadFields = returning.length > 0 && !(this.getPlatform().usesReturningStatement() && res.rows?.length);
|
|
906
948
|
if (options.onConflictAction === 'ignore' || (!res.rows?.length && loadPK.size > 0) || reloadFields) {
|
|
907
949
|
const unique = meta.hydrateProps.filter(p => !p.lazy).map(p => p.name);
|
|
908
|
-
const add = new Set(propIndex >= 0 ? [unique[propIndex]] : []);
|
|
950
|
+
const add = new Set(propIndex !== false && propIndex >= 0 ? [unique[propIndex]] : []);
|
|
909
951
|
for (const cond of loadPK.values()) {
|
|
910
952
|
Utils.keys(cond).forEach(key => add.add(key));
|
|
911
953
|
}
|
|
@@ -917,7 +959,7 @@ export class EntityManager {
|
|
|
917
959
|
where.$or[idx][prop] = item[prop];
|
|
918
960
|
});
|
|
919
961
|
});
|
|
920
|
-
const data2 = await this.driver.find(meta.
|
|
962
|
+
const data2 = await this.driver.find(meta.class, where, {
|
|
921
963
|
fields: returning.concat(...add).concat(...(Array.isArray(uniqueFields) ? uniqueFields : [])),
|
|
922
964
|
ctx: em.transactionContext,
|
|
923
965
|
convertCustomTypes: true,
|
|
@@ -934,7 +976,7 @@ export class EntityManager {
|
|
|
934
976
|
});
|
|
935
977
|
return this.comparator.matching(entityName, cond, tmp);
|
|
936
978
|
});
|
|
937
|
-
/* v8 ignore next
|
|
979
|
+
/* v8 ignore next */
|
|
938
980
|
if (!row) {
|
|
939
981
|
throw new Error(`Cannot find matching entity for condition ${JSON.stringify(cond)}`);
|
|
940
982
|
}
|
|
@@ -957,7 +999,7 @@ export class EntityManager {
|
|
|
957
999
|
}, {});
|
|
958
1000
|
return this.comparator.matching(entityName, cond, pk);
|
|
959
1001
|
});
|
|
960
|
-
/* v8 ignore next
|
|
1002
|
+
/* v8 ignore next */
|
|
961
1003
|
if (!row) {
|
|
962
1004
|
throw new Error(`Cannot find matching entity for condition ${JSON.stringify(cond)}`);
|
|
963
1005
|
}
|
|
@@ -979,6 +1021,29 @@ export class EntityManager {
|
|
|
979
1021
|
}
|
|
980
1022
|
/**
|
|
981
1023
|
* Runs your callback wrapped inside a database transaction.
|
|
1024
|
+
*
|
|
1025
|
+
* If a transaction is already active, a new savepoint (nested transaction) will be created by default. This behavior
|
|
1026
|
+
* can be controlled via the `propagation` option. Use the provided EntityManager instance for all operations that
|
|
1027
|
+
* should be part of the transaction. You can safely use a global EntityManager instance from a DI container, as this
|
|
1028
|
+
* method automatically creates an async context for the transaction.
|
|
1029
|
+
*
|
|
1030
|
+
* **Concurrency note:** When running multiple transactions concurrently (e.g. in parallel requests or jobs), use the
|
|
1031
|
+
* `clear: true` option. This ensures the callback runs in a clear fork of the EntityManager, providing full isolation
|
|
1032
|
+
* between concurrent transactional handlers. Using `clear: true` is an alternative to forking explicitly and calling
|
|
1033
|
+
* the method on the new fork – it already provides the necessary isolation for safe concurrent usage.
|
|
1034
|
+
*
|
|
1035
|
+
* **Propagation note:** Changes made within a transaction (whether top-level or nested) are always propagated to the
|
|
1036
|
+
* parent context, unless the parent context is a global one. If you want to avoid that, fork the EntityManager first
|
|
1037
|
+
* and then call this method on the fork.
|
|
1038
|
+
*
|
|
1039
|
+
* **Example:**
|
|
1040
|
+
* ```ts
|
|
1041
|
+
* await em.transactional(async (em) => {
|
|
1042
|
+
* const author = new Author('Jon');
|
|
1043
|
+
* em.persist(author);
|
|
1044
|
+
* // flush is called automatically at the end of the callback
|
|
1045
|
+
* });
|
|
1046
|
+
* ```
|
|
982
1047
|
*/
|
|
983
1048
|
async transactional(cb, options = {}) {
|
|
984
1049
|
const em = this.getContext(false);
|
|
@@ -1047,11 +1112,11 @@ export class EntityManager {
|
|
|
1047
1112
|
em.prepareOptions(options);
|
|
1048
1113
|
let entityName;
|
|
1049
1114
|
if (data === undefined) {
|
|
1050
|
-
entityName = entityNameOrEntity.constructor
|
|
1115
|
+
entityName = entityNameOrEntity.constructor;
|
|
1051
1116
|
data = entityNameOrEntity;
|
|
1052
1117
|
}
|
|
1053
1118
|
else {
|
|
1054
|
-
entityName =
|
|
1119
|
+
entityName = entityNameOrEntity;
|
|
1055
1120
|
}
|
|
1056
1121
|
if (Utils.isEntity(data)) {
|
|
1057
1122
|
if (options.schema && helper(data).getSchema() == null) {
|
|
@@ -1070,7 +1135,7 @@ export class EntityManager {
|
|
|
1070
1135
|
return cs.getPrimaryKey();
|
|
1071
1136
|
}
|
|
1072
1137
|
data = QueryHelper.processObjectParams(data);
|
|
1073
|
-
|
|
1138
|
+
validateParams(data, 'insert data');
|
|
1074
1139
|
const res = await em.driver.nativeInsert(entityName, data, { ctx: em.transactionContext, ...options });
|
|
1075
1140
|
return res.insertId;
|
|
1076
1141
|
}
|
|
@@ -1082,11 +1147,11 @@ export class EntityManager {
|
|
|
1082
1147
|
em.prepareOptions(options);
|
|
1083
1148
|
let entityName;
|
|
1084
1149
|
if (data === undefined) {
|
|
1085
|
-
entityName = entityNameOrEntities[0].constructor
|
|
1150
|
+
entityName = entityNameOrEntities[0].constructor;
|
|
1086
1151
|
data = entityNameOrEntities;
|
|
1087
1152
|
}
|
|
1088
1153
|
else {
|
|
1089
|
-
entityName =
|
|
1154
|
+
entityName = entityNameOrEntities;
|
|
1090
1155
|
}
|
|
1091
1156
|
if (data.length === 0) {
|
|
1092
1157
|
return [];
|
|
@@ -1110,7 +1175,7 @@ export class EntityManager {
|
|
|
1110
1175
|
return css.map(cs => cs.getPrimaryKey());
|
|
1111
1176
|
}
|
|
1112
1177
|
data = data.map(row => QueryHelper.processObjectParams(row));
|
|
1113
|
-
data.forEach(row =>
|
|
1178
|
+
data.forEach(row => validateParams(row, 'insert data'));
|
|
1114
1179
|
const res = await em.driver.nativeInsertMany(entityName, data, { ctx: em.transactionContext, ...options });
|
|
1115
1180
|
if (res.insertedIds) {
|
|
1116
1181
|
return res.insertedIds;
|
|
@@ -1123,11 +1188,10 @@ export class EntityManager {
|
|
|
1123
1188
|
async nativeUpdate(entityName, where, data, options = {}) {
|
|
1124
1189
|
const em = this.getContext(false);
|
|
1125
1190
|
em.prepareOptions(options);
|
|
1126
|
-
entityName = Utils.className(entityName);
|
|
1127
1191
|
data = QueryHelper.processObjectParams(data);
|
|
1128
1192
|
where = await em.processWhere(entityName, where, { ...options, convertCustomTypes: false }, 'update');
|
|
1129
|
-
|
|
1130
|
-
|
|
1193
|
+
validateParams(data, 'update data');
|
|
1194
|
+
validateParams(where, 'update condition');
|
|
1131
1195
|
const res = await em.driver.nativeUpdate(entityName, where, data, { ctx: em.transactionContext, ...options });
|
|
1132
1196
|
return res.affectedRows;
|
|
1133
1197
|
}
|
|
@@ -1137,9 +1201,8 @@ export class EntityManager {
|
|
|
1137
1201
|
async nativeDelete(entityName, where, options = {}) {
|
|
1138
1202
|
const em = this.getContext(false);
|
|
1139
1203
|
em.prepareOptions(options);
|
|
1140
|
-
entityName = Utils.className(entityName);
|
|
1141
1204
|
where = await em.processWhere(entityName, where, options, 'delete');
|
|
1142
|
-
|
|
1205
|
+
validateParams(where, 'delete condition');
|
|
1143
1206
|
const res = await em.driver.nativeDelete(entityName, where, { ctx: em.transactionContext, ...options });
|
|
1144
1207
|
return res.affectedRows;
|
|
1145
1208
|
}
|
|
@@ -1147,18 +1210,19 @@ export class EntityManager {
|
|
|
1147
1210
|
* Maps raw database result to an entity and merges it to this EntityManager.
|
|
1148
1211
|
*/
|
|
1149
1212
|
map(entityName, result, options = {}) {
|
|
1150
|
-
entityName = Utils.className(entityName);
|
|
1151
1213
|
const meta = this.metadata.get(entityName);
|
|
1152
1214
|
const data = this.driver.mapResult(result, meta);
|
|
1153
|
-
Object.keys(data)
|
|
1215
|
+
for (const k of Object.keys(data)) {
|
|
1154
1216
|
const prop = meta.properties[k];
|
|
1155
|
-
if (prop
|
|
1156
|
-
|
|
1217
|
+
if (prop?.kind === ReferenceKind.SCALAR && SCALAR_TYPES.has(prop.runtimeType) && !prop.customType && (prop.setter || !prop.getter)) {
|
|
1218
|
+
validateProperty(prop, data[k], data);
|
|
1157
1219
|
}
|
|
1158
|
-
}
|
|
1220
|
+
}
|
|
1159
1221
|
return this.merge(entityName, data, {
|
|
1160
1222
|
convertCustomTypes: true,
|
|
1161
|
-
refresh: true,
|
|
1223
|
+
refresh: true,
|
|
1224
|
+
validate: false,
|
|
1225
|
+
...options,
|
|
1162
1226
|
});
|
|
1163
1227
|
}
|
|
1164
1228
|
/**
|
|
@@ -1167,26 +1231,21 @@ export class EntityManager {
|
|
|
1167
1231
|
*/
|
|
1168
1232
|
merge(entityName, data, options = {}) {
|
|
1169
1233
|
if (Utils.isEntity(entityName)) {
|
|
1170
|
-
return this.merge(entityName.constructor
|
|
1234
|
+
return this.merge(entityName.constructor, entityName, data);
|
|
1171
1235
|
}
|
|
1172
1236
|
const em = options.disableContextResolution ? this : this.getContext();
|
|
1173
1237
|
options.schema ??= em._schema;
|
|
1174
|
-
|
|
1175
|
-
|
|
1238
|
+
options.validate ??= true;
|
|
1239
|
+
options.cascade ??= true;
|
|
1240
|
+
validatePrimaryKey(data, em.metadata.get(entityName));
|
|
1176
1241
|
let entity = em.unitOfWork.tryGetById(entityName, data, options.schema, false);
|
|
1177
1242
|
if (entity && helper(entity).__managed && helper(entity).__initialized && !options.refresh) {
|
|
1178
1243
|
return entity;
|
|
1179
1244
|
}
|
|
1180
|
-
const meta = em.metadata.find(entityName);
|
|
1181
|
-
const childMeta = em.metadata.getByDiscriminatorColumn(meta, data);
|
|
1182
1245
|
const dataIsEntity = Utils.isEntity(data);
|
|
1183
|
-
if (options.keepIdentity && entity && dataIsEntity && entity !== data) {
|
|
1184
|
-
em.entityFactory.mergeData(meta, entity, helper(data).__originalEntityData, { initialized: true, merge: true, ...options });
|
|
1185
|
-
return entity;
|
|
1186
|
-
}
|
|
1187
1246
|
entity = dataIsEntity ? data : em.entityFactory.create(entityName, data, { merge: true, ...options });
|
|
1188
|
-
|
|
1189
|
-
em.unitOfWork.merge(entity);
|
|
1247
|
+
const visited = options.cascade ? undefined : new Set([entity]);
|
|
1248
|
+
em.unitOfWork.merge(entity, visited);
|
|
1190
1249
|
return entity;
|
|
1191
1250
|
}
|
|
1192
1251
|
/**
|
|
@@ -1211,6 +1270,7 @@ export class EntityManager {
|
|
|
1211
1270
|
...options,
|
|
1212
1271
|
newEntity: !options.managed,
|
|
1213
1272
|
merge: options.managed,
|
|
1273
|
+
normalizeAccessors: true,
|
|
1214
1274
|
});
|
|
1215
1275
|
options.persist ??= em.config.get('persistOnCreate');
|
|
1216
1276
|
if (options.persist && !this.getMetadata(entityName).embeddable) {
|
|
@@ -1230,7 +1290,7 @@ export class EntityManager {
|
|
|
1230
1290
|
getReference(entityName, id, options = {}) {
|
|
1231
1291
|
options.schema ??= this.schema;
|
|
1232
1292
|
options.convertCustomTypes ??= false;
|
|
1233
|
-
const meta = this.metadata.get(
|
|
1293
|
+
const meta = this.metadata.get(entityName);
|
|
1234
1294
|
if (Utils.isPrimaryKey(id)) {
|
|
1235
1295
|
if (meta.compositePK) {
|
|
1236
1296
|
throw ValidationError.invalidCompositeIdentifier(meta);
|
|
@@ -1251,7 +1311,6 @@ export class EntityManager {
|
|
|
1251
1311
|
// Shallow copy options since the object will be modified when deleting orderBy
|
|
1252
1312
|
options = { ...options };
|
|
1253
1313
|
em.prepareOptions(options);
|
|
1254
|
-
entityName = Utils.className(entityName);
|
|
1255
1314
|
await em.tryFlush(entityName, options);
|
|
1256
1315
|
where = await em.processWhere(entityName, where, options, 'read');
|
|
1257
1316
|
options.populate = await em.preparePopulate(entityName, options);
|
|
@@ -1260,8 +1319,8 @@ export class EntityManager {
|
|
|
1260
1319
|
const meta = em.metadata.find(entityName);
|
|
1261
1320
|
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
1262
1321
|
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
1263
|
-
options.populateFilter = await this.getJoinedFilters(meta,
|
|
1264
|
-
|
|
1322
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
1323
|
+
validateParams(where);
|
|
1265
1324
|
delete options.orderBy;
|
|
1266
1325
|
const cacheKey = em.cacheKey(entityName, options, 'em.count', where);
|
|
1267
1326
|
const cached = await em.tryCache(entityName, options.cache, cacheKey);
|
|
@@ -1287,7 +1346,7 @@ export class EntityManager {
|
|
|
1287
1346
|
for (const ent of entities) {
|
|
1288
1347
|
if (!Utils.isEntity(ent, true)) {
|
|
1289
1348
|
/* v8 ignore next */
|
|
1290
|
-
const meta = typeof ent === 'object' ? em.metadata.find(ent.constructor
|
|
1349
|
+
const meta = typeof ent === 'object' ? em.metadata.find(ent.constructor) : undefined;
|
|
1291
1350
|
throw ValidationError.notDiscoveredEntity(ent, meta);
|
|
1292
1351
|
}
|
|
1293
1352
|
// do not cascade just yet, cascading of entities in persist stack is done when flushing
|
|
@@ -1295,13 +1354,6 @@ export class EntityManager {
|
|
|
1295
1354
|
}
|
|
1296
1355
|
return this;
|
|
1297
1356
|
}
|
|
1298
|
-
/**
|
|
1299
|
-
* Persists your entity immediately, flushing all not yet persisted changes to the database too.
|
|
1300
|
-
* Equivalent to `em.persist(e).flush()`.
|
|
1301
|
-
*/
|
|
1302
|
-
async persistAndFlush(entity) {
|
|
1303
|
-
await this.persist(entity).flush();
|
|
1304
|
-
}
|
|
1305
1357
|
/**
|
|
1306
1358
|
* Marks entity for removal.
|
|
1307
1359
|
* A removed entity will be removed from the database at or before transaction commit or as a result of the flush operation.
|
|
@@ -1325,13 +1377,6 @@ export class EntityManager {
|
|
|
1325
1377
|
}
|
|
1326
1378
|
return em;
|
|
1327
1379
|
}
|
|
1328
|
-
/**
|
|
1329
|
-
* Removes an entity instance immediately, flushing all not yet persisted changes to the database too.
|
|
1330
|
-
* Equivalent to `em.remove(e).flush()`
|
|
1331
|
-
*/
|
|
1332
|
-
async removeAndFlush(entity) {
|
|
1333
|
-
await this.remove(entity).flush();
|
|
1334
|
-
}
|
|
1335
1380
|
/**
|
|
1336
1381
|
* Flushes all changes to objects that have been queued up to now to the database.
|
|
1337
1382
|
* This effectively synchronizes the in-memory state of managed objects with the database.
|
|
@@ -1345,7 +1390,6 @@ export class EntityManager {
|
|
|
1345
1390
|
async tryFlush(entityName, options) {
|
|
1346
1391
|
const em = this.getContext();
|
|
1347
1392
|
const flushMode = options.flushMode ?? em.flushMode ?? em.config.get('flushMode');
|
|
1348
|
-
entityName = Utils.className(entityName);
|
|
1349
1393
|
const meta = em.metadata.get(entityName);
|
|
1350
1394
|
if (flushMode === FlushMode.COMMIT) {
|
|
1351
1395
|
return;
|
|
@@ -1364,7 +1408,6 @@ export class EntityManager {
|
|
|
1364
1408
|
* Checks whether given property can be populated on the entity.
|
|
1365
1409
|
*/
|
|
1366
1410
|
canPopulate(entityName, property) {
|
|
1367
|
-
entityName = Utils.className(entityName);
|
|
1368
1411
|
// eslint-disable-next-line prefer-const
|
|
1369
1412
|
let [p, ...parts] = property.split('.');
|
|
1370
1413
|
const meta = this.metadata.find(entityName);
|
|
@@ -1374,12 +1417,11 @@ export class EntityManager {
|
|
|
1374
1417
|
if (p.includes(':')) {
|
|
1375
1418
|
p = p.split(':', 2)[0];
|
|
1376
1419
|
}
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
}
|
|
1420
|
+
// For TPT inheritance, check the entity's own properties, not just the root's
|
|
1421
|
+
// For STI, meta.properties includes all properties anyway
|
|
1422
|
+
const ret = p in meta.properties;
|
|
1381
1423
|
if (parts.length > 0) {
|
|
1382
|
-
return this.canPopulate(
|
|
1424
|
+
return this.canPopulate(meta.properties[p].targetMeta.class, parts.join('.'));
|
|
1383
1425
|
}
|
|
1384
1426
|
return ret;
|
|
1385
1427
|
}
|
|
@@ -1393,8 +1435,8 @@ export class EntityManager {
|
|
|
1393
1435
|
}
|
|
1394
1436
|
const em = this.getContext();
|
|
1395
1437
|
em.prepareOptions(options);
|
|
1396
|
-
const entityName = arr[0].constructor
|
|
1397
|
-
const preparedPopulate = await em.preparePopulate(entityName, { populate: populate }, options.validate);
|
|
1438
|
+
const entityName = arr[0].constructor;
|
|
1439
|
+
const preparedPopulate = await em.preparePopulate(entityName, { populate: populate, filters: options.filters }, options.validate);
|
|
1398
1440
|
await em.entityLoader.populate(entityName, arr, preparedPopulate, options);
|
|
1399
1441
|
return entities;
|
|
1400
1442
|
}
|
|
@@ -1522,7 +1564,6 @@ export class EntityManager {
|
|
|
1522
1564
|
*/
|
|
1523
1565
|
getMetadata(entityName) {
|
|
1524
1566
|
if (entityName) {
|
|
1525
|
-
entityName = Utils.className(entityName);
|
|
1526
1567
|
return this.metadata.get(entityName);
|
|
1527
1568
|
}
|
|
1528
1569
|
return this.metadata;
|
|
@@ -1551,8 +1592,8 @@ export class EntityManager {
|
|
|
1551
1592
|
lockTableAliases: options.lockTableAliases,
|
|
1552
1593
|
});
|
|
1553
1594
|
}
|
|
1554
|
-
const preparedPopulate = await this.preparePopulate(meta.
|
|
1555
|
-
await this.entityLoader.populate(meta.
|
|
1595
|
+
const preparedPopulate = await this.preparePopulate(meta.class, options);
|
|
1596
|
+
await this.entityLoader.populate(meta.class, [entity], preparedPopulate, {
|
|
1556
1597
|
...options,
|
|
1557
1598
|
...this.getPopulateWhere(where, options),
|
|
1558
1599
|
orderBy: options.populateOrderBy ?? options.orderBy,
|
|
@@ -1572,6 +1613,7 @@ export class EntityManager {
|
|
|
1572
1613
|
return ret;
|
|
1573
1614
|
}, []);
|
|
1574
1615
|
}
|
|
1616
|
+
/** @internal */
|
|
1575
1617
|
async preparePopulate(entityName, options, validate = true) {
|
|
1576
1618
|
if (options.populate === false) {
|
|
1577
1619
|
return [];
|
|
@@ -1612,13 +1654,13 @@ export class EntityManager {
|
|
|
1612
1654
|
options.populate = pruneToOneRelations(meta, this.buildFields(options.fields));
|
|
1613
1655
|
}
|
|
1614
1656
|
if (!options.populate) {
|
|
1615
|
-
const populate = this.entityLoader.normalizePopulate(entityName, [], options.strategy);
|
|
1657
|
+
const populate = this.entityLoader.normalizePopulate(entityName, [], options.strategy, true, options.exclude);
|
|
1616
1658
|
await this.autoJoinRefsForFilters(meta, { ...options, populate });
|
|
1617
1659
|
return populate;
|
|
1618
1660
|
}
|
|
1619
1661
|
if (typeof options.populate !== 'boolean') {
|
|
1620
1662
|
options.populate = Utils.asArray(options.populate).map(field => {
|
|
1621
|
-
/* v8 ignore next
|
|
1663
|
+
/* v8 ignore next */
|
|
1622
1664
|
if (typeof field === 'boolean' || field === PopulatePath.ALL) {
|
|
1623
1665
|
return [{ field: meta.primaryKeys[0], strategy: options.strategy, all: !!field }]; //
|
|
1624
1666
|
}
|
|
@@ -1628,24 +1670,27 @@ export class EntityManager {
|
|
|
1628
1670
|
options.flags.push(QueryFlag.INFER_POPULATE);
|
|
1629
1671
|
return [];
|
|
1630
1672
|
}
|
|
1631
|
-
if (
|
|
1673
|
+
if (typeof field === 'string') {
|
|
1632
1674
|
return [{ field, strategy: options.strategy }];
|
|
1633
1675
|
}
|
|
1634
1676
|
return [field];
|
|
1635
1677
|
}).flat();
|
|
1636
1678
|
}
|
|
1637
|
-
const populate = this.entityLoader.normalizePopulate(entityName, options.populate, options.strategy);
|
|
1679
|
+
const populate = this.entityLoader.normalizePopulate(entityName, options.populate, options.strategy, true, options.exclude);
|
|
1638
1680
|
const invalid = populate.find(({ field }) => !this.canPopulate(entityName, field));
|
|
1639
1681
|
if (validate && invalid) {
|
|
1640
1682
|
throw ValidationError.invalidPropertyName(entityName, invalid.field);
|
|
1641
1683
|
}
|
|
1642
1684
|
await this.autoJoinRefsForFilters(meta, { ...options, populate });
|
|
1643
|
-
|
|
1685
|
+
for (const field of populate) {
|
|
1644
1686
|
// force select-in strategy when populating all relations as otherwise we could cause infinite loops when self-referencing
|
|
1645
1687
|
const all = field.all ?? (Array.isArray(options.populate) && options.populate.includes('*'));
|
|
1646
1688
|
field.strategy = all ? LoadStrategy.SELECT_IN : (options.strategy ?? field.strategy);
|
|
1647
|
-
|
|
1648
|
-
|
|
1689
|
+
}
|
|
1690
|
+
if (options.populateHints) {
|
|
1691
|
+
applyPopulateHints(populate, options.populateHints);
|
|
1692
|
+
}
|
|
1693
|
+
return populate;
|
|
1649
1694
|
}
|
|
1650
1695
|
/**
|
|
1651
1696
|
* when the entity is found in identity map, we check if it was partially loaded or we are trying to populate
|
|
@@ -1665,7 +1710,7 @@ export class EntityManager {
|
|
|
1665
1710
|
return !inlineEmbedded && !prop.lazy && !helper(entity).__loadedProperties.has(prop.name);
|
|
1666
1711
|
});
|
|
1667
1712
|
}
|
|
1668
|
-
if (autoRefresh) {
|
|
1713
|
+
if (autoRefresh || options.filters) {
|
|
1669
1714
|
return true;
|
|
1670
1715
|
}
|
|
1671
1716
|
if (Array.isArray(options.populate)) {
|
|
@@ -1689,7 +1734,7 @@ export class EntityManager {
|
|
|
1689
1734
|
for (const k of ['ctx', 'strategy', 'flushMode', 'logging', 'loggerContext']) {
|
|
1690
1735
|
delete opts[k];
|
|
1691
1736
|
}
|
|
1692
|
-
return [entityName, method, opts, where];
|
|
1737
|
+
return [Utils.className(entityName), method, opts, where];
|
|
1693
1738
|
}
|
|
1694
1739
|
/**
|
|
1695
1740
|
* @internal
|
|
@@ -1706,21 +1751,17 @@ export class EntityManager {
|
|
|
1706
1751
|
return { key: cacheKey, data: cached };
|
|
1707
1752
|
}
|
|
1708
1753
|
let data;
|
|
1754
|
+
const createOptions = {
|
|
1755
|
+
merge: true,
|
|
1756
|
+
convertCustomTypes: false,
|
|
1757
|
+
refresh,
|
|
1758
|
+
recomputeSnapshot: true,
|
|
1759
|
+
};
|
|
1709
1760
|
if (Array.isArray(cached) && merge) {
|
|
1710
|
-
data = cached.map(item => em.entityFactory.create(entityName, item,
|
|
1711
|
-
merge: true,
|
|
1712
|
-
convertCustomTypes: true,
|
|
1713
|
-
refresh,
|
|
1714
|
-
recomputeSnapshot: true,
|
|
1715
|
-
}));
|
|
1761
|
+
data = cached.map(item => em.entityFactory.create(entityName, item, createOptions));
|
|
1716
1762
|
}
|
|
1717
1763
|
else if (Utils.isObject(cached) && merge) {
|
|
1718
|
-
data = em.entityFactory.create(entityName, cached,
|
|
1719
|
-
merge: true,
|
|
1720
|
-
convertCustomTypes: true,
|
|
1721
|
-
refresh,
|
|
1722
|
-
recomputeSnapshot: true,
|
|
1723
|
-
});
|
|
1764
|
+
data = em.entityFactory.create(entityName, cached, createOptions);
|
|
1724
1765
|
}
|
|
1725
1766
|
else {
|
|
1726
1767
|
data = cached;
|
|
@@ -1735,7 +1776,7 @@ export class EntityManager {
|
|
|
1735
1776
|
config ??= this.config.get('resultCache').global;
|
|
1736
1777
|
if (config) {
|
|
1737
1778
|
const em = this.getContext();
|
|
1738
|
-
const expiration = Array.isArray(config) ? config[1] : (
|
|
1779
|
+
const expiration = Array.isArray(config) ? config[1] : (typeof config === 'number' ? config : undefined);
|
|
1739
1780
|
await em.resultCache.set(key.key, data instanceof Function ? data() : data, '', expiration);
|
|
1740
1781
|
}
|
|
1741
1782
|
}
|
|
@@ -1768,6 +1809,20 @@ export class EntityManager {
|
|
|
1768
1809
|
set schema(schema) {
|
|
1769
1810
|
this.getContext(false)._schema = schema ?? undefined;
|
|
1770
1811
|
}
|
|
1812
|
+
/** @internal */
|
|
1813
|
+
async getDataLoader(type) {
|
|
1814
|
+
const em = this.getContext();
|
|
1815
|
+
if (em.loaders[type]) {
|
|
1816
|
+
return em.loaders[type];
|
|
1817
|
+
}
|
|
1818
|
+
const { DataloaderUtils } = await import('@mikro-orm/core/dataloader');
|
|
1819
|
+
const DataLoader = await DataloaderUtils.getDataLoader();
|
|
1820
|
+
switch (type) {
|
|
1821
|
+
case 'ref': return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getRefBatchLoadFn(em)));
|
|
1822
|
+
case '1:m': return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getColBatchLoadFn(em)));
|
|
1823
|
+
case 'm:n': return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getManyToManyColBatchLoadFn(em)));
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1771
1826
|
/**
|
|
1772
1827
|
* Returns the ID of this EntityManager. Respects the context, so global EM will give you the contextual ID
|
|
1773
1828
|
* if executed inside request context handler.
|
|
@@ -1776,7 +1831,7 @@ export class EntityManager {
|
|
|
1776
1831
|
return this.getContext(false)._id;
|
|
1777
1832
|
}
|
|
1778
1833
|
/** @ignore */
|
|
1779
|
-
[inspect.custom]() {
|
|
1834
|
+
[Symbol.for('nodejs.util.inspect.custom')]() {
|
|
1780
1835
|
return `[EntityManager<${this.id}>]`;
|
|
1781
1836
|
}
|
|
1782
1837
|
}
|