@mikro-orm/core 7.0.0-dev.12 → 7.0.0-dev.121
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 +85 -56
- package/EntityManager.js +332 -293
- package/MikroORM.d.ts +41 -32
- package/MikroORM.js +100 -140
- package/README.md +3 -2
- package/cache/FileCacheAdapter.d.ts +1 -1
- package/cache/FileCacheAdapter.js +8 -7
- package/cache/GeneratedCacheAdapter.d.ts +0 -1
- package/cache/GeneratedCacheAdapter.js +0 -2
- package/cache/index.d.ts +0 -1
- package/cache/index.js +0 -1
- package/connections/Connection.d.ts +16 -7
- package/connections/Connection.js +23 -14
- package/drivers/DatabaseDriver.d.ts +25 -16
- package/drivers/DatabaseDriver.js +40 -24
- package/drivers/IDatabaseDriver.d.ts +38 -17
- package/entity/BaseEntity.d.ts +0 -1
- package/entity/BaseEntity.js +0 -3
- package/entity/Collection.d.ts +95 -30
- package/entity/Collection.js +439 -99
- package/entity/EntityAssigner.d.ts +1 -1
- package/entity/EntityAssigner.js +26 -18
- package/entity/EntityFactory.d.ts +7 -0
- package/entity/EntityFactory.js +72 -53
- package/entity/EntityHelper.d.ts +2 -2
- package/entity/EntityHelper.js +30 -15
- package/entity/EntityLoader.d.ts +7 -6
- package/entity/EntityLoader.js +84 -72
- package/entity/EntityRepository.d.ts +1 -1
- package/entity/EntityRepository.js +2 -2
- package/entity/Reference.d.ts +6 -5
- package/entity/Reference.js +34 -9
- package/entity/WrappedEntity.d.ts +2 -7
- package/entity/WrappedEntity.js +3 -8
- package/entity/defineEntity.d.ts +568 -0
- package/entity/defineEntity.js +529 -0
- package/entity/index.d.ts +3 -2
- package/entity/index.js +3 -2
- package/entity/utils.d.ts +7 -0
- package/entity/utils.js +16 -4
- package/entity/validators.d.ts +11 -0
- package/entity/validators.js +65 -0
- package/enums.d.ts +21 -6
- package/enums.js +14 -1
- package/errors.d.ts +17 -9
- package/errors.js +41 -21
- 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 +50 -33
- 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 +22 -24
- package/metadata/EntitySchema.js +73 -51
- package/metadata/MetadataDiscovery.d.ts +6 -10
- package/metadata/MetadataDiscovery.js +289 -298
- 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 +2 -9
- package/metadata/MetadataValidator.js +22 -38
- 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 +480 -0
- package/metadata/types.js +1 -0
- package/naming-strategy/AbstractNamingStrategy.d.ts +8 -4
- package/naming-strategy/AbstractNamingStrategy.js +8 -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 +14 -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 +19 -11
- package/platforms/ExceptionConverter.js +1 -1
- package/platforms/Platform.d.ts +6 -13
- package/platforms/Platform.js +17 -43
- package/serialization/EntitySerializer.d.ts +5 -0
- package/serialization/EntitySerializer.js +47 -27
- package/serialization/EntityTransformer.js +28 -18
- package/serialization/SerializationContext.d.ts +6 -6
- package/serialization/SerializationContext.js +16 -13
- package/types/ArrayType.d.ts +1 -1
- package/types/ArrayType.js +2 -3
- package/types/BigIntType.d.ts +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 +2 -1
- package/types/BooleanType.js +3 -0
- package/types/DecimalType.d.ts +6 -4
- package/types/DecimalType.js +3 -3
- package/types/DoubleType.js +2 -2
- package/types/EnumArrayType.js +1 -2
- package/types/JsonType.d.ts +1 -1
- package/types/JsonType.js +7 -2
- package/types/TinyIntType.js +1 -1
- package/types/Type.d.ts +2 -4
- package/types/Type.js +3 -3
- package/types/Uint8ArrayType.d.ts +0 -1
- package/types/Uint8ArrayType.js +1 -4
- package/types/index.d.ts +1 -1
- package/typings.d.ts +148 -109
- package/typings.js +50 -42
- package/unit-of-work/ChangeSet.d.ts +2 -6
- package/unit-of-work/ChangeSet.js +4 -5
- package/unit-of-work/ChangeSetComputer.d.ts +1 -3
- package/unit-of-work/ChangeSetComputer.js +14 -12
- package/unit-of-work/ChangeSetPersister.d.ts +5 -4
- package/unit-of-work/ChangeSetPersister.js +65 -33
- package/unit-of-work/CommitOrderCalculator.d.ts +12 -10
- package/unit-of-work/CommitOrderCalculator.js +13 -13
- package/unit-of-work/UnitOfWork.d.ts +10 -3
- package/unit-of-work/UnitOfWork.js +139 -96
- package/utils/AbstractSchemaGenerator.d.ts +5 -5
- package/utils/AbstractSchemaGenerator.js +18 -16
- package/utils/AsyncContext.d.ts +6 -0
- package/utils/AsyncContext.js +42 -0
- package/utils/Configuration.d.ts +753 -207
- package/utils/Configuration.js +145 -190
- package/utils/ConfigurationLoader.d.ts +1 -54
- package/utils/ConfigurationLoader.js +1 -352
- package/utils/Cursor.d.ts +0 -3
- package/utils/Cursor.js +9 -6
- package/utils/DataloaderUtils.d.ts +15 -5
- package/utils/DataloaderUtils.js +65 -17
- package/utils/EntityComparator.d.ts +13 -9
- package/utils/EntityComparator.js +85 -43
- package/utils/QueryHelper.d.ts +14 -6
- package/utils/QueryHelper.js +87 -25
- package/utils/RawQueryFragment.d.ts +48 -25
- package/utils/RawQueryFragment.js +66 -70
- package/utils/RequestContext.js +2 -2
- package/utils/TransactionContext.js +2 -2
- package/utils/TransactionManager.d.ts +65 -0
- package/utils/TransactionManager.js +223 -0
- package/utils/Utils.d.ts +12 -119
- package/utils/Utils.js +97 -373
- 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 +32 -0
- package/utils/fs-utils.js +178 -0
- package/utils/index.d.ts +2 -1
- package/utils/index.js +2 -1
- package/utils/upsert-utils.d.ts +9 -4
- package/utils/upsert-utils.js +55 -4
- package/decorators/Check.d.ts +0 -3
- package/decorators/Check.js +0 -13
- package/decorators/CreateRequestContext.d.ts +0 -3
- package/decorators/CreateRequestContext.js +0 -32
- package/decorators/Embeddable.d.ts +0 -8
- package/decorators/Embeddable.js +0 -11
- package/decorators/Embedded.d.ts +0 -18
- package/decorators/Embedded.js +0 -18
- package/decorators/Entity.d.ts +0 -18
- package/decorators/Entity.js +0 -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 -40
- package/decorators/ManyToMany.js +0 -14
- package/decorators/ManyToOne.d.ts +0 -30
- package/decorators/ManyToOne.js +0 -14
- package/decorators/OneToMany.d.ts +0 -28
- package/decorators/OneToMany.js +0 -17
- package/decorators/OneToOne.d.ts +0 -24
- package/decorators/OneToOne.js +0 -7
- package/decorators/PrimaryKey.d.ts +0 -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 -13
- package/decorators/Transactional.js +0 -28
- package/decorators/hooks.d.ts +0 -16
- package/decorators/hooks.js +0 -47
- package/decorators/index.d.ts +0 -17
- package/decorators/index.js +0 -17
- package/entity/ArrayCollection.d.ts +0 -116
- package/entity/ArrayCollection.js +0 -402
- 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,13 @@
|
|
|
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
4
|
import { DataloaderUtils } from './utils/DataloaderUtils.js';
|
|
7
5
|
import { QueryHelper } from './utils/QueryHelper.js';
|
|
8
6
|
import { TransactionContext } from './utils/TransactionContext.js';
|
|
9
|
-
import { isRaw,
|
|
7
|
+
import { isRaw, Raw } from './utils/RawQueryFragment.js';
|
|
10
8
|
import { EntityFactory } from './entity/EntityFactory.js';
|
|
11
9
|
import { EntityAssigner } from './entity/EntityAssigner.js';
|
|
12
|
-
import {
|
|
10
|
+
import { validateEmptyWhere, validateParams, validatePrimaryKey, validateProperty } from './entity/validators.js';
|
|
13
11
|
import { EntityLoader } from './entity/EntityLoader.js';
|
|
14
12
|
import { Reference } from './entity/Reference.js';
|
|
15
13
|
import { helper } from './entity/wrap.js';
|
|
@@ -19,6 +17,8 @@ import { EventType, FlushMode, LoadStrategy, LockMode, PopulateHint, PopulatePat
|
|
|
19
17
|
import { EventManager } from './events/EventManager.js';
|
|
20
18
|
import { TransactionEventBroadcaster } from './events/TransactionEventBroadcaster.js';
|
|
21
19
|
import { OptimisticLockError, ValidationError } from './errors.js';
|
|
20
|
+
import { getLoadingStrategy } from './entity/utils.js';
|
|
21
|
+
import { TransactionManager } from './utils/TransactionManager.js';
|
|
22
22
|
/**
|
|
23
23
|
* The EntityManager is the central access point to ORM functionality. It is a facade to all different ORM subsystems
|
|
24
24
|
* such as UnitOfWork, Query Language, and Repository API.
|
|
@@ -34,10 +34,8 @@ export class EntityManager {
|
|
|
34
34
|
_id = EntityManager.counter++;
|
|
35
35
|
global = false;
|
|
36
36
|
name;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
validator;
|
|
40
|
-
repositoryMap = {};
|
|
37
|
+
loaders = {};
|
|
38
|
+
repositoryMap = new Map();
|
|
41
39
|
entityLoader;
|
|
42
40
|
comparator;
|
|
43
41
|
entityFactory;
|
|
@@ -61,7 +59,6 @@ export class EntityManager {
|
|
|
61
59
|
this.eventManager = eventManager;
|
|
62
60
|
this.entityLoader = new EntityLoader(this);
|
|
63
61
|
this.name = this.config.get('contextName');
|
|
64
|
-
this.validator = new EntityValidator(this.config.get('strict'));
|
|
65
62
|
this.comparator = this.config.getComparator(this.metadata);
|
|
66
63
|
this.resultCache = this.config.getResultCacheAdapter();
|
|
67
64
|
this.disableTransactions = this.config.get('disableTransactions');
|
|
@@ -91,13 +88,12 @@ export class EntityManager {
|
|
|
91
88
|
* Gets repository for given entity. You can pass either string name or entity class reference.
|
|
92
89
|
*/
|
|
93
90
|
getRepository(entityName) {
|
|
94
|
-
|
|
95
|
-
if (!this.repositoryMap
|
|
96
|
-
const meta = this.metadata.get(entityName);
|
|
91
|
+
const meta = this.metadata.get(entityName);
|
|
92
|
+
if (!this.repositoryMap.has(meta)) {
|
|
97
93
|
const RepositoryClass = this.config.getRepositoryClass(meta.repository);
|
|
98
|
-
this.repositoryMap
|
|
94
|
+
this.repositoryMap.set(meta, new RepositoryClass(this, entityName));
|
|
99
95
|
}
|
|
100
|
-
return this.repositoryMap
|
|
96
|
+
return this.repositoryMap.get(meta);
|
|
101
97
|
}
|
|
102
98
|
/**
|
|
103
99
|
* Shortcut for `em.getRepository()`.
|
|
@@ -105,12 +101,6 @@ export class EntityManager {
|
|
|
105
101
|
repo(entityName) {
|
|
106
102
|
return this.getRepository(entityName);
|
|
107
103
|
}
|
|
108
|
-
/**
|
|
109
|
-
* Gets EntityValidator instance
|
|
110
|
-
*/
|
|
111
|
-
getValidator() {
|
|
112
|
-
return this.validator;
|
|
113
|
-
}
|
|
114
104
|
/**
|
|
115
105
|
* Finds all entities matching your `where` query. You can pass additional options via the `options` parameter.
|
|
116
106
|
*/
|
|
@@ -125,9 +115,8 @@ export class EntityManager {
|
|
|
125
115
|
const em = this.getContext();
|
|
126
116
|
em.prepareOptions(options);
|
|
127
117
|
await em.tryFlush(entityName, options);
|
|
128
|
-
entityName = Utils.className(entityName);
|
|
129
118
|
where = await em.processWhere(entityName, where, options, 'read');
|
|
130
|
-
|
|
119
|
+
validateParams(where);
|
|
131
120
|
options.orderBy = options.orderBy || {};
|
|
132
121
|
options.populate = await em.preparePopulate(entityName, options);
|
|
133
122
|
const populate = options.populate;
|
|
@@ -137,7 +126,6 @@ export class EntityManager {
|
|
|
137
126
|
await em.entityLoader.populate(entityName, cached.data, populate, {
|
|
138
127
|
...options,
|
|
139
128
|
...em.getPopulateWhere(where, options),
|
|
140
|
-
convertCustomTypes: false,
|
|
141
129
|
ignoreLazyScalarProperties: true,
|
|
142
130
|
lookup: false,
|
|
143
131
|
});
|
|
@@ -148,7 +136,7 @@ export class EntityManager {
|
|
|
148
136
|
// save the original hint value so we know it was infer/all
|
|
149
137
|
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
150
138
|
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
151
|
-
options.populateFilter = await this.getJoinedFilters(meta,
|
|
139
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
152
140
|
const results = await em.driver.find(entityName, where, { ctx: em.transactionContext, em, ...options });
|
|
153
141
|
if (results.length === 0) {
|
|
154
142
|
await em.storeCache(options.cache, cached, []);
|
|
@@ -168,7 +156,6 @@ export class EntityManager {
|
|
|
168
156
|
await em.entityLoader.populate(entityName, unique, populate, {
|
|
169
157
|
...options,
|
|
170
158
|
...em.getPopulateWhere(where, options),
|
|
171
|
-
convertCustomTypes: false,
|
|
172
159
|
ignoreLazyScalarProperties: true,
|
|
173
160
|
lookup: false,
|
|
174
161
|
});
|
|
@@ -181,6 +168,60 @@ export class EntityManager {
|
|
|
181
168
|
}
|
|
182
169
|
return unique;
|
|
183
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* Finds all entities and returns an async iterable (async generator) that yields results one by one.
|
|
173
|
+
* The results are merged and mapped to entity instances, without adding them to the identity map.
|
|
174
|
+
* You can disable merging by passing the options `{ mergeResults: false }`.
|
|
175
|
+
* With `mergeResults` disabled, to-many collections will contain at most one item, and you will get duplicate
|
|
176
|
+
* root entities when there are multiple items in the populated collection.
|
|
177
|
+
* This is useful for processing large datasets without loading everything into memory at once.
|
|
178
|
+
*
|
|
179
|
+
* ```ts
|
|
180
|
+
* const stream = em.stream(Book, { populate: ['author'] });
|
|
181
|
+
*
|
|
182
|
+
* for await (const book of stream) {
|
|
183
|
+
* // book is an instance of Book entity
|
|
184
|
+
* console.log(book.title, book.author.name);
|
|
185
|
+
* }
|
|
186
|
+
* ```
|
|
187
|
+
*/
|
|
188
|
+
async *stream(entityName, options = {}) {
|
|
189
|
+
const em = this.getContext();
|
|
190
|
+
em.prepareOptions(options);
|
|
191
|
+
options.strategy = 'joined';
|
|
192
|
+
await em.tryFlush(entityName, options);
|
|
193
|
+
const where = await em.processWhere(entityName, options.where ?? {}, options, 'read');
|
|
194
|
+
validateParams(where);
|
|
195
|
+
options.orderBy = options.orderBy || {};
|
|
196
|
+
options.populate = await em.preparePopulate(entityName, options);
|
|
197
|
+
const meta = this.metadata.get(entityName);
|
|
198
|
+
options = { ...options };
|
|
199
|
+
// save the original hint value so we know it was infer/all
|
|
200
|
+
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
201
|
+
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
202
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
203
|
+
const stream = em.driver.stream(entityName, where, {
|
|
204
|
+
ctx: em.transactionContext,
|
|
205
|
+
mapResults: false,
|
|
206
|
+
...options,
|
|
207
|
+
});
|
|
208
|
+
for await (const data of stream) {
|
|
209
|
+
const fork = em.fork();
|
|
210
|
+
const entity = fork.entityFactory.create(entityName, data, {
|
|
211
|
+
refresh: options.refresh,
|
|
212
|
+
schema: options.schema,
|
|
213
|
+
convertCustomTypes: true,
|
|
214
|
+
});
|
|
215
|
+
helper(entity).setSerializationContext({
|
|
216
|
+
populate: options.populate,
|
|
217
|
+
fields: options.fields,
|
|
218
|
+
exclude: options.exclude,
|
|
219
|
+
});
|
|
220
|
+
await fork.unitOfWork.dispatchOnLoadEvent();
|
|
221
|
+
fork.clear();
|
|
222
|
+
yield entity;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
184
225
|
/**
|
|
185
226
|
* Finds all entities of given type, optionally matching the `where` condition provided in the `options` parameter.
|
|
186
227
|
*/
|
|
@@ -194,7 +235,7 @@ export class EntityManager {
|
|
|
194
235
|
if (options.populateWhere === PopulateHint.ALL) {
|
|
195
236
|
return { where: {}, populateWhere: options.populateWhere };
|
|
196
237
|
}
|
|
197
|
-
/* v8 ignore next
|
|
238
|
+
/* v8 ignore next */
|
|
198
239
|
if (options.populateWhere === PopulateHint.INFER) {
|
|
199
240
|
return { where, populateWhere: options.populateWhere };
|
|
200
241
|
}
|
|
@@ -203,12 +244,12 @@ export class EntityManager {
|
|
|
203
244
|
/**
|
|
204
245
|
* Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
|
|
205
246
|
*/
|
|
206
|
-
addFilter(
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
options.entity = Utils.asArray(entityName).map(n => Utils.className(n));
|
|
247
|
+
addFilter(options) {
|
|
248
|
+
if (options.entity) {
|
|
249
|
+
options.entity = Utils.asArray(options.entity).map(n => Utils.className(n));
|
|
210
250
|
}
|
|
211
|
-
|
|
251
|
+
options.default ??= true;
|
|
252
|
+
this.getContext(false).filters[options.name] = options;
|
|
212
253
|
}
|
|
213
254
|
/**
|
|
214
255
|
* Sets filter parameter values globally inside context defined by this entity manager.
|
|
@@ -232,8 +273,8 @@ export class EntityManager {
|
|
|
232
273
|
/**
|
|
233
274
|
* Gets logger context for this entity manager.
|
|
234
275
|
*/
|
|
235
|
-
getLoggerContext() {
|
|
236
|
-
const em = this.getContext();
|
|
276
|
+
getLoggerContext(options) {
|
|
277
|
+
const em = options?.disableContextResolution ? this : this.getContext();
|
|
237
278
|
em.loggerContext ??= {};
|
|
238
279
|
return em.loggerContext;
|
|
239
280
|
}
|
|
@@ -259,15 +300,15 @@ export class EntityManager {
|
|
|
259
300
|
if (!meta?.discriminatorValue) {
|
|
260
301
|
return where;
|
|
261
302
|
}
|
|
262
|
-
const types = Object.values(meta.root.discriminatorMap).map(cls => this.metadata.
|
|
303
|
+
const types = Object.values(meta.root.discriminatorMap).map(cls => this.metadata.get(cls));
|
|
263
304
|
const children = [];
|
|
264
305
|
const lookUpChildren = (ret, type) => {
|
|
265
306
|
const children = types.filter(meta2 => meta2.extends === type);
|
|
266
|
-
children.forEach(m => lookUpChildren(ret, m.
|
|
307
|
+
children.forEach(m => lookUpChildren(ret, m.class));
|
|
267
308
|
ret.push(...children.filter(c => c.discriminatorValue));
|
|
268
309
|
return children;
|
|
269
310
|
};
|
|
270
|
-
lookUpChildren(children, meta.
|
|
311
|
+
lookUpChildren(children, meta.class);
|
|
271
312
|
/* v8 ignore next */
|
|
272
313
|
where[meta.root.discriminatorColumn] = children.length > 0 ? { $in: [meta.discriminatorValue, ...children.map(c => c.discriminatorValue)] } : meta.discriminatorValue;
|
|
273
314
|
return where;
|
|
@@ -283,28 +324,39 @@ export class EntityManager {
|
|
|
283
324
|
}
|
|
284
325
|
return ret;
|
|
285
326
|
}
|
|
286
|
-
async getJoinedFilters(meta,
|
|
327
|
+
async getJoinedFilters(meta, options) {
|
|
328
|
+
if (!this.config.get('filtersOnRelations') || !options.populate) {
|
|
329
|
+
return undefined;
|
|
330
|
+
}
|
|
287
331
|
const ret = {};
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
332
|
+
for (const hint of options.populate) {
|
|
333
|
+
const field = hint.field.split(':')[0];
|
|
334
|
+
const prop = meta.properties[field];
|
|
335
|
+
const strategy = getLoadingStrategy(prop.strategy || hint.strategy || options.strategy || this.config.get('loadStrategy'), prop.kind);
|
|
336
|
+
const joined = strategy === LoadStrategy.JOINED && prop.kind !== ReferenceKind.SCALAR;
|
|
337
|
+
if (!joined && !hint.filter) {
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
const filters = QueryHelper.mergePropertyFilters(prop.filters, options.filters);
|
|
341
|
+
const where = await this.applyFilters(prop.targetMeta.class, {}, filters, 'read', {
|
|
342
|
+
...options,
|
|
343
|
+
populate: hint.children,
|
|
344
|
+
});
|
|
345
|
+
const where2 = await this.getJoinedFilters(prop.targetMeta, {
|
|
346
|
+
...options,
|
|
347
|
+
filters,
|
|
348
|
+
populate: hint.children,
|
|
349
|
+
populateWhere: PopulateHint.ALL,
|
|
350
|
+
});
|
|
351
|
+
if (Utils.hasObjectKeys(where)) {
|
|
352
|
+
ret[field] = ret[field] ? { $and: [where, ret[field]] } : where;
|
|
353
|
+
}
|
|
354
|
+
if (where2 && Utils.hasObjectKeys(where2)) {
|
|
355
|
+
if (ret[field]) {
|
|
356
|
+
Utils.merge(ret[field], where2);
|
|
300
357
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
Utils.merge(ret[field], where2);
|
|
304
|
-
}
|
|
305
|
-
else {
|
|
306
|
-
ret[field] = where2;
|
|
307
|
-
}
|
|
358
|
+
else {
|
|
359
|
+
ret[field] = where2;
|
|
308
360
|
}
|
|
309
361
|
}
|
|
310
362
|
}
|
|
@@ -313,42 +365,57 @@ export class EntityManager {
|
|
|
313
365
|
/**
|
|
314
366
|
* 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.
|
|
315
367
|
*/
|
|
316
|
-
async autoJoinRefsForFilters(meta, options) {
|
|
317
|
-
if (!meta || !this.config.get('autoJoinRefsForFilters')) {
|
|
368
|
+
async autoJoinRefsForFilters(meta, options, parent) {
|
|
369
|
+
if (!meta || !this.config.get('autoJoinRefsForFilters') || options.filters === false) {
|
|
318
370
|
return;
|
|
319
371
|
}
|
|
320
|
-
const props = meta.relations.filter(prop => {
|
|
321
|
-
return !prop.object && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)
|
|
322
|
-
&& ((options.fields?.length ?? 0) === 0 || options.fields?.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)));
|
|
323
|
-
});
|
|
324
372
|
const ret = options.populate;
|
|
325
|
-
for (const prop of
|
|
326
|
-
|
|
373
|
+
for (const prop of meta.relations) {
|
|
374
|
+
if (prop.object
|
|
375
|
+
|| ![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)
|
|
376
|
+
|| !((options.fields?.length ?? 0) === 0 || options.fields?.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
|
|
377
|
+
|| (parent?.class === prop.targetMeta.root.class && parent.propName === prop.inversedBy)) {
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
options = { ...options, filters: QueryHelper.mergePropertyFilters(prop.filters, options.filters) };
|
|
381
|
+
const cond = await this.applyFilters(prop.targetMeta.class, {}, options.filters, 'read', options);
|
|
327
382
|
if (!Utils.isEmpty(cond)) {
|
|
328
383
|
const populated = options.populate.filter(({ field }) => field.split(':')[0] === prop.name);
|
|
329
|
-
|
|
330
|
-
|
|
384
|
+
let found = false;
|
|
385
|
+
for (const hint of populated) {
|
|
386
|
+
if (!hint.all) {
|
|
387
|
+
hint.filter = true;
|
|
388
|
+
}
|
|
389
|
+
const strategy = getLoadingStrategy(prop.strategy || hint.strategy || options.strategy || this.config.get('loadStrategy'), prop.kind);
|
|
390
|
+
if (hint.field === `${prop.name}:ref` || (hint.filter && strategy === LoadStrategy.JOINED)) {
|
|
391
|
+
found = true;
|
|
392
|
+
}
|
|
331
393
|
}
|
|
332
|
-
|
|
394
|
+
if (!found) {
|
|
333
395
|
ret.push({ field: `${prop.name}:ref`, strategy: LoadStrategy.JOINED, filter: true });
|
|
334
396
|
}
|
|
335
397
|
}
|
|
336
398
|
}
|
|
399
|
+
for (const hint of ret) {
|
|
400
|
+
const [field, ref] = hint.field.split(':');
|
|
401
|
+
const prop = meta?.properties[field];
|
|
402
|
+
if (prop && !ref) {
|
|
403
|
+
hint.children ??= [];
|
|
404
|
+
await this.autoJoinRefsForFilters(prop.targetMeta, { ...options, populate: hint.children }, { class: meta.root.class, propName: prop.name });
|
|
405
|
+
}
|
|
406
|
+
}
|
|
337
407
|
}
|
|
338
408
|
/**
|
|
339
409
|
* @internal
|
|
340
410
|
*/
|
|
341
411
|
async applyFilters(entityName, where, options, type, findOptions) {
|
|
342
|
-
const meta = this.metadata.
|
|
412
|
+
const meta = this.metadata.get(entityName);
|
|
343
413
|
const filters = [];
|
|
344
414
|
const ret = [];
|
|
345
|
-
if (!meta) {
|
|
346
|
-
return where;
|
|
347
|
-
}
|
|
348
415
|
const active = new Set();
|
|
349
416
|
const push = (source) => {
|
|
350
417
|
const activeFilters = QueryHelper
|
|
351
|
-
.getActiveFilters(
|
|
418
|
+
.getActiveFilters(meta, options, source)
|
|
352
419
|
.filter(f => !active.has(f.name));
|
|
353
420
|
filters.push(...activeFilters);
|
|
354
421
|
activeFilters.forEach(f => active.add(f.name));
|
|
@@ -363,24 +430,28 @@ export class EntityManager {
|
|
|
363
430
|
let cond;
|
|
364
431
|
if (filter.cond instanceof Function) {
|
|
365
432
|
// @ts-ignore
|
|
366
|
-
const args = Utils.isPlainObject(options[filter.name]) ? options[filter.name] : this.getContext().filterParams[filter.name];
|
|
433
|
+
const args = Utils.isPlainObject(options?.[filter.name]) ? options[filter.name] : this.getContext().filterParams[filter.name];
|
|
367
434
|
if (!args && filter.cond.length > 0 && filter.args !== false) {
|
|
368
435
|
throw new Error(`No arguments provided for filter '${filter.name}'`);
|
|
369
436
|
}
|
|
370
|
-
cond = await filter.cond(args, type, this, findOptions, entityName);
|
|
437
|
+
cond = await filter.cond(args, type, this, findOptions, Utils.className(entityName));
|
|
371
438
|
}
|
|
372
439
|
else {
|
|
373
440
|
cond = filter.cond;
|
|
374
441
|
}
|
|
375
|
-
|
|
442
|
+
cond = QueryHelper.processWhere({
|
|
376
443
|
where: cond,
|
|
377
444
|
entityName,
|
|
378
445
|
metadata: this.metadata,
|
|
379
446
|
platform: this.driver.getPlatform(),
|
|
380
447
|
aliased: type === 'read',
|
|
381
|
-
})
|
|
448
|
+
});
|
|
449
|
+
if (filter.strict) {
|
|
450
|
+
Object.defineProperty(cond, '__strict', { value: filter.strict, enumerable: false });
|
|
451
|
+
}
|
|
452
|
+
ret.push(cond);
|
|
382
453
|
}
|
|
383
|
-
const conds = [...ret, where].filter(c => Utils.hasObjectKeys(c));
|
|
454
|
+
const conds = [...ret, where].filter(c => Utils.hasObjectKeys(c) || Raw.hasObjectFragments(c));
|
|
384
455
|
return conds.length > 1 ? { $and: conds } : conds[0];
|
|
385
456
|
}
|
|
386
457
|
/**
|
|
@@ -391,12 +462,10 @@ export class EntityManager {
|
|
|
391
462
|
const em = this.getContext(false);
|
|
392
463
|
await em.tryFlush(entityName, options);
|
|
393
464
|
options.flushMode = 'commit'; // do not try to auto flush again
|
|
394
|
-
return
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
]);
|
|
399
|
-
});
|
|
465
|
+
return Promise.all([
|
|
466
|
+
em.find(entityName, where, options),
|
|
467
|
+
em.count(entityName, where, options),
|
|
468
|
+
]);
|
|
400
469
|
}
|
|
401
470
|
/**
|
|
402
471
|
* Calls `em.find()` and `em.count()` with the same arguments (where applicable) and returns the results as {@apilink Cursor} object.
|
|
@@ -412,21 +481,21 @@ export class EntityManager {
|
|
|
412
481
|
* - POJO/entity instance
|
|
413
482
|
*
|
|
414
483
|
* ```ts
|
|
415
|
-
* const currentCursor = await em.findByCursor(User, {
|
|
484
|
+
* const currentCursor = await em.findByCursor(User, {
|
|
416
485
|
* first: 10,
|
|
417
486
|
* after: previousCursor, // cursor instance
|
|
418
487
|
* orderBy: { id: 'desc' },
|
|
419
488
|
* });
|
|
420
489
|
*
|
|
421
490
|
* // to fetch next page
|
|
422
|
-
* const nextCursor = await em.findByCursor(User, {
|
|
491
|
+
* const nextCursor = await em.findByCursor(User, {
|
|
423
492
|
* first: 10,
|
|
424
493
|
* after: currentCursor.endCursor, // opaque string
|
|
425
494
|
* orderBy: { id: 'desc' },
|
|
426
495
|
* });
|
|
427
496
|
*
|
|
428
497
|
* // to fetch next page
|
|
429
|
-
* const nextCursor2 = await em.findByCursor(User, {
|
|
498
|
+
* const nextCursor2 = await em.findByCursor(User, {
|
|
430
499
|
* first: 10,
|
|
431
500
|
* after: { id: lastSeenId }, // entity-like POJO
|
|
432
501
|
* orderBy: { id: 'desc' },
|
|
@@ -454,16 +523,16 @@ export class EntityManager {
|
|
|
454
523
|
* }
|
|
455
524
|
* ```
|
|
456
525
|
*/
|
|
457
|
-
async findByCursor(entityName,
|
|
526
|
+
async findByCursor(entityName, options) {
|
|
458
527
|
const em = this.getContext(false);
|
|
459
|
-
entityName = Utils.className(entityName);
|
|
460
528
|
options.overfetch ??= true;
|
|
461
|
-
|
|
529
|
+
options.where ??= {};
|
|
530
|
+
if (Utils.isEmpty(options.orderBy) && !Raw.hasObjectFragments(options.orderBy)) {
|
|
462
531
|
throw new Error('Explicit `orderBy` option required');
|
|
463
532
|
}
|
|
464
533
|
const [entities, count] = options.includeCount !== false
|
|
465
|
-
? await em.findAndCount(entityName, where, options)
|
|
466
|
-
: [await em.find(entityName, where, options)];
|
|
534
|
+
? await em.findAndCount(entityName, options.where, options)
|
|
535
|
+
: [await em.find(entityName, options.where, options)];
|
|
467
536
|
return new Cursor(entities, count, options, this.metadata.get(entityName));
|
|
468
537
|
}
|
|
469
538
|
/**
|
|
@@ -475,9 +544,9 @@ export class EntityManager {
|
|
|
475
544
|
const ret = await this.refresh(entity, options);
|
|
476
545
|
if (!ret) {
|
|
477
546
|
options.failHandler ??= this.config.get('findOneOrFailHandler');
|
|
478
|
-
const
|
|
479
|
-
const where =
|
|
480
|
-
throw options.failHandler(
|
|
547
|
+
const wrapped = helper(entity);
|
|
548
|
+
const where = wrapped.getPrimaryKey();
|
|
549
|
+
throw options.failHandler(wrapped.__meta.className, where);
|
|
481
550
|
}
|
|
482
551
|
return ret;
|
|
483
552
|
}
|
|
@@ -488,19 +557,31 @@ export class EntityManager {
|
|
|
488
557
|
*/
|
|
489
558
|
async refresh(entity, options = {}) {
|
|
490
559
|
const fork = this.fork({ keepTransactionContext: true });
|
|
491
|
-
const
|
|
492
|
-
const reloaded = await fork.findOne(
|
|
493
|
-
schema:
|
|
560
|
+
const wrapped = helper(entity);
|
|
561
|
+
const reloaded = await fork.findOne(wrapped.__meta.class, entity, {
|
|
562
|
+
schema: wrapped.__schema,
|
|
494
563
|
...options,
|
|
495
564
|
flushMode: FlushMode.COMMIT,
|
|
496
565
|
});
|
|
497
|
-
|
|
498
|
-
|
|
566
|
+
const em = this.getContext();
|
|
567
|
+
if (!reloaded) {
|
|
568
|
+
em.unitOfWork.unsetIdentity(entity);
|
|
569
|
+
return null;
|
|
499
570
|
}
|
|
500
|
-
|
|
501
|
-
|
|
571
|
+
let found = false;
|
|
572
|
+
for (const e of fork.unitOfWork.getIdentityMap()) {
|
|
573
|
+
const ref = em.getReference(e.constructor, helper(e).getPrimaryKey());
|
|
574
|
+
const data = helper(e).serialize({ ignoreSerializers: true, includeHidden: true, convertCustomTypes: false });
|
|
575
|
+
em.config.getHydrator(this.metadata).hydrate(ref, helper(ref).__meta, data, em.entityFactory, 'full', false, false);
|
|
576
|
+
Utils.merge(helper(ref).__originalEntityData, this.comparator.prepareEntity(e));
|
|
577
|
+
found ||= ref === entity;
|
|
578
|
+
}
|
|
579
|
+
if (!found) {
|
|
580
|
+
const data = helper(reloaded).serialize({ ignoreSerializers: true, includeHidden: true, convertCustomTypes: true });
|
|
581
|
+
em.config.getHydrator(this.metadata).hydrate(entity, wrapped.__meta, data, em.entityFactory, 'full', false, true);
|
|
582
|
+
Utils.merge(wrapped.__originalEntityData, this.comparator.prepareEntity(reloaded));
|
|
502
583
|
}
|
|
503
|
-
return
|
|
584
|
+
return entity;
|
|
504
585
|
}
|
|
505
586
|
/**
|
|
506
587
|
* Finds first entity matching your `where` query.
|
|
@@ -514,7 +595,6 @@ export class EntityManager {
|
|
|
514
595
|
return ret;
|
|
515
596
|
}
|
|
516
597
|
const em = this.getContext();
|
|
517
|
-
entityName = Utils.className(entityName);
|
|
518
598
|
em.prepareOptions(options);
|
|
519
599
|
let entity = em.unitOfWork.tryGetById(entityName, where, options.schema);
|
|
520
600
|
// query for a not managed entity which is already in the identity map as it
|
|
@@ -526,31 +606,32 @@ export class EntityManager {
|
|
|
526
606
|
await em.tryFlush(entityName, options);
|
|
527
607
|
const meta = em.metadata.get(entityName);
|
|
528
608
|
where = await em.processWhere(entityName, where, options, 'read');
|
|
529
|
-
|
|
609
|
+
validateEmptyWhere(where);
|
|
530
610
|
em.checkLockRequirements(options.lockMode, meta);
|
|
531
|
-
const isOptimisticLocking =
|
|
611
|
+
const isOptimisticLocking = options.lockMode == null || options.lockMode === LockMode.OPTIMISTIC;
|
|
532
612
|
if (entity && !em.shouldRefresh(meta, entity, options) && isOptimisticLocking) {
|
|
533
613
|
return em.lockAndPopulate(meta, entity, where, options);
|
|
534
614
|
}
|
|
535
|
-
|
|
615
|
+
validateParams(where);
|
|
536
616
|
options.populate = await em.preparePopulate(entityName, options);
|
|
537
617
|
const cacheKey = em.cacheKey(entityName, options, 'em.findOne', where);
|
|
538
618
|
const cached = await em.tryCache(entityName, options.cache, cacheKey, options.refresh, true);
|
|
539
|
-
if (cached?.data) {
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
619
|
+
if (cached?.data !== undefined) {
|
|
620
|
+
if (cached.data) {
|
|
621
|
+
await em.entityLoader.populate(entityName, [cached.data], options.populate, {
|
|
622
|
+
...options,
|
|
623
|
+
...em.getPopulateWhere(where, options),
|
|
624
|
+
ignoreLazyScalarProperties: true,
|
|
625
|
+
lookup: false,
|
|
626
|
+
});
|
|
627
|
+
}
|
|
547
628
|
return cached.data;
|
|
548
629
|
}
|
|
549
630
|
options = { ...options };
|
|
550
631
|
// save the original hint value so we know it was infer/all
|
|
551
632
|
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
552
633
|
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
553
|
-
options.populateFilter = await this.getJoinedFilters(meta,
|
|
634
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
554
635
|
const data = await em.driver.findOne(entityName, where, {
|
|
555
636
|
ctx: em.transactionContext,
|
|
556
637
|
em,
|
|
@@ -591,10 +672,10 @@ export class EntityManager {
|
|
|
591
672
|
if (!entity || isStrictViolation) {
|
|
592
673
|
const key = options.strict ? 'findExactlyOneOrFailHandler' : 'findOneOrFailHandler';
|
|
593
674
|
options.failHandler ??= this.config.get(key);
|
|
594
|
-
|
|
675
|
+
const name = Utils.className(entityName);
|
|
595
676
|
/* v8 ignore next */
|
|
596
677
|
where = Utils.isEntity(where) ? helper(where).getPrimaryKey() : where;
|
|
597
|
-
throw options.failHandler(
|
|
678
|
+
throw options.failHandler(name, where);
|
|
598
679
|
}
|
|
599
680
|
return entity;
|
|
600
681
|
}
|
|
@@ -634,11 +715,11 @@ export class EntityManager {
|
|
|
634
715
|
let where;
|
|
635
716
|
let entity = null;
|
|
636
717
|
if (data === undefined) {
|
|
637
|
-
entityName = entityNameOrEntity.constructor
|
|
718
|
+
entityName = entityNameOrEntity.constructor;
|
|
638
719
|
data = entityNameOrEntity;
|
|
639
720
|
}
|
|
640
721
|
else {
|
|
641
|
-
entityName =
|
|
722
|
+
entityName = entityNameOrEntity;
|
|
642
723
|
}
|
|
643
724
|
const meta = this.metadata.get(entityName);
|
|
644
725
|
const convertCustomTypes = !Utils.isEntity(data);
|
|
@@ -661,26 +742,9 @@ export class EntityManager {
|
|
|
661
742
|
}
|
|
662
743
|
}
|
|
663
744
|
}
|
|
664
|
-
|
|
665
|
-
const propIndex = !isRaw(unique) && unique.findIndex(p => data[p] != null);
|
|
666
|
-
if (options.onConflictFields || where == null) {
|
|
667
|
-
if (propIndex !== false && propIndex >= 0) {
|
|
668
|
-
where = { [unique[propIndex]]: data[unique[propIndex]] };
|
|
669
|
-
}
|
|
670
|
-
else if (meta.uniques.length > 0) {
|
|
671
|
-
for (const u of meta.uniques) {
|
|
672
|
-
if (Utils.asArray(u.properties).every(p => data[p] != null)) {
|
|
673
|
-
where = Utils.asArray(u.properties).reduce((o, key) => {
|
|
674
|
-
o[key] = data[key];
|
|
675
|
-
return o;
|
|
676
|
-
}, {});
|
|
677
|
-
break;
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
}
|
|
745
|
+
where = getWhereCondition(meta, options.onConflictFields, data, where).where;
|
|
682
746
|
data = QueryHelper.processObjectParams(data);
|
|
683
|
-
|
|
747
|
+
validateParams(data, 'insert data');
|
|
684
748
|
if (em.eventManager.hasListeners(EventType.beforeUpsert, meta)) {
|
|
685
749
|
await em.eventManager.dispatchEvent(EventType.beforeUpsert, { entity: data, em, meta }, meta);
|
|
686
750
|
}
|
|
@@ -718,11 +782,12 @@ export class EntityManager {
|
|
|
718
782
|
where[meta.primaryKeys[0]] = ret.insertId;
|
|
719
783
|
}
|
|
720
784
|
}
|
|
721
|
-
const data2 = await this.driver.findOne(meta.
|
|
785
|
+
const data2 = await this.driver.findOne(meta.class, where, {
|
|
722
786
|
fields: returning,
|
|
723
787
|
ctx: em.transactionContext,
|
|
724
788
|
convertCustomTypes: true,
|
|
725
789
|
connectionType: 'write',
|
|
790
|
+
schema: options.schema,
|
|
726
791
|
});
|
|
727
792
|
em.getHydrator().hydrate(entity, meta, data2, em.entityFactory, 'full', false, true);
|
|
728
793
|
}
|
|
@@ -772,11 +837,11 @@ export class EntityManager {
|
|
|
772
837
|
let entityName;
|
|
773
838
|
let propIndex;
|
|
774
839
|
if (data === undefined) {
|
|
775
|
-
entityName = entityNameOrEntity[0].constructor
|
|
840
|
+
entityName = entityNameOrEntity[0].constructor;
|
|
776
841
|
data = entityNameOrEntity;
|
|
777
842
|
}
|
|
778
843
|
else {
|
|
779
|
-
entityName =
|
|
844
|
+
entityName = entityNameOrEntity;
|
|
780
845
|
}
|
|
781
846
|
const batchSize = options.batchSize ?? this.config.get('batchSize');
|
|
782
847
|
if (data.length > batchSize) {
|
|
@@ -820,32 +885,18 @@ export class EntityManager {
|
|
|
820
885
|
}
|
|
821
886
|
}
|
|
822
887
|
}
|
|
823
|
-
const unique = meta.props.filter(p => p.unique).map(p => p.name);
|
|
824
|
-
propIndex = unique.findIndex(p =>
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
where = { [unique[propIndex]]: row[unique[propIndex]] };
|
|
828
|
-
}
|
|
829
|
-
else if (meta.uniques.length > 0) {
|
|
830
|
-
for (const u of meta.uniques) {
|
|
831
|
-
if (Utils.asArray(u.properties).every(p => row[p] != null)) {
|
|
832
|
-
where = Utils.asArray(u.properties).reduce((o, key) => {
|
|
833
|
-
o[key] = row[key];
|
|
834
|
-
return o;
|
|
835
|
-
}, {});
|
|
836
|
-
break;
|
|
837
|
-
}
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
row = QueryHelper.processObjectParams(row);
|
|
888
|
+
const unique = options.onConflictFields ?? meta.props.filter(p => p.unique).map(p => p.name);
|
|
889
|
+
propIndex = !isRaw(unique) && unique.findIndex(p => data[p] ?? data[p.substring(0, p.indexOf('.'))] != null);
|
|
890
|
+
const tmp = getWhereCondition(meta, options.onConflictFields, row, where);
|
|
891
|
+
propIndex = tmp.propIndex;
|
|
842
892
|
where = QueryHelper.processWhere({
|
|
843
|
-
where,
|
|
893
|
+
where: tmp.where,
|
|
844
894
|
entityName,
|
|
845
895
|
metadata: this.metadata,
|
|
846
896
|
platform: this.getPlatform(),
|
|
847
897
|
});
|
|
848
|
-
|
|
898
|
+
row = QueryHelper.processObjectParams(row);
|
|
899
|
+
validateParams(row, 'insert data');
|
|
849
900
|
allData.push(row);
|
|
850
901
|
allWhere.push(where);
|
|
851
902
|
}
|
|
@@ -886,7 +937,7 @@ export class EntityManager {
|
|
|
886
937
|
const reloadFields = returning.length > 0 && !(this.getPlatform().usesReturningStatement() && res.rows?.length);
|
|
887
938
|
if (options.onConflictAction === 'ignore' || (!res.rows?.length && loadPK.size > 0) || reloadFields) {
|
|
888
939
|
const unique = meta.hydrateProps.filter(p => !p.lazy).map(p => p.name);
|
|
889
|
-
const add = new Set(propIndex >= 0 ? [unique[propIndex]] : []);
|
|
940
|
+
const add = new Set(propIndex !== false && propIndex >= 0 ? [unique[propIndex]] : []);
|
|
890
941
|
for (const cond of loadPK.values()) {
|
|
891
942
|
Utils.keys(cond).forEach(key => add.add(key));
|
|
892
943
|
}
|
|
@@ -898,11 +949,12 @@ export class EntityManager {
|
|
|
898
949
|
where.$or[idx][prop] = item[prop];
|
|
899
950
|
});
|
|
900
951
|
});
|
|
901
|
-
const data2 = await this.driver.find(meta.
|
|
952
|
+
const data2 = await this.driver.find(meta.class, where, {
|
|
902
953
|
fields: returning.concat(...add).concat(...(Array.isArray(uniqueFields) ? uniqueFields : [])),
|
|
903
954
|
ctx: em.transactionContext,
|
|
904
955
|
convertCustomTypes: true,
|
|
905
956
|
connectionType: 'write',
|
|
957
|
+
schema: options.schema,
|
|
906
958
|
});
|
|
907
959
|
for (const [entity, cond] of loadPK.entries()) {
|
|
908
960
|
const row = data2.find(row => {
|
|
@@ -914,7 +966,7 @@ export class EntityManager {
|
|
|
914
966
|
});
|
|
915
967
|
return this.comparator.matching(entityName, cond, tmp);
|
|
916
968
|
});
|
|
917
|
-
/* v8 ignore next
|
|
969
|
+
/* v8 ignore next */
|
|
918
970
|
if (!row) {
|
|
919
971
|
throw new Error(`Cannot find matching entity for condition ${JSON.stringify(cond)}`);
|
|
920
972
|
}
|
|
@@ -937,7 +989,7 @@ export class EntityManager {
|
|
|
937
989
|
}, {});
|
|
938
990
|
return this.comparator.matching(entityName, cond, pk);
|
|
939
991
|
});
|
|
940
|
-
/* v8 ignore next
|
|
992
|
+
/* v8 ignore next */
|
|
941
993
|
if (!row) {
|
|
942
994
|
throw new Error(`Cannot find matching entity for condition ${JSON.stringify(cond)}`);
|
|
943
995
|
}
|
|
@@ -959,45 +1011,37 @@ export class EntityManager {
|
|
|
959
1011
|
}
|
|
960
1012
|
/**
|
|
961
1013
|
* Runs your callback wrapped inside a database transaction.
|
|
1014
|
+
*
|
|
1015
|
+
* If a transaction is already active, a new savepoint (nested transaction) will be created by default. This behavior
|
|
1016
|
+
* can be controlled via the `propagation` option. Use the provided EntityManager instance for all operations that
|
|
1017
|
+
* should be part of the transaction. You can safely use a global EntityManager instance from a DI container, as this
|
|
1018
|
+
* method automatically creates an async context for the transaction.
|
|
1019
|
+
*
|
|
1020
|
+
* **Concurrency note:** When running multiple transactions concurrently (e.g. in parallel requests or jobs), use the
|
|
1021
|
+
* `clear: true` option. This ensures the callback runs in a clear fork of the EntityManager, providing full isolation
|
|
1022
|
+
* between concurrent transactional handlers. Using `clear: true` is an alternative to forking explicitly and calling
|
|
1023
|
+
* the method on the new fork – it already provides the necessary isolation for safe concurrent usage.
|
|
1024
|
+
*
|
|
1025
|
+
* **Propagation note:** Changes made within a transaction (whether top-level or nested) are always propagated to the
|
|
1026
|
+
* parent context, unless the parent context is a global one. If you want to avoid that, fork the EntityManager first
|
|
1027
|
+
* and then call this method on the fork.
|
|
1028
|
+
*
|
|
1029
|
+
* **Example:**
|
|
1030
|
+
* ```ts
|
|
1031
|
+
* await em.transactional(async (em) => {
|
|
1032
|
+
* const author = new Author('Jon');
|
|
1033
|
+
* em.persist(author);
|
|
1034
|
+
* // flush is called automatically at the end of the callback
|
|
1035
|
+
* });
|
|
1036
|
+
* ```
|
|
962
1037
|
*/
|
|
963
1038
|
async transactional(cb, options = {}) {
|
|
964
1039
|
const em = this.getContext(false);
|
|
965
1040
|
if (this.disableTransactions || em.disableTransactions) {
|
|
966
1041
|
return cb(em);
|
|
967
1042
|
}
|
|
968
|
-
const
|
|
969
|
-
|
|
970
|
-
flushMode: options.flushMode,
|
|
971
|
-
cloneEventManager: true,
|
|
972
|
-
disableTransactions: options.ignoreNestedTransactions,
|
|
973
|
-
loggerContext: options.loggerContext,
|
|
974
|
-
});
|
|
975
|
-
options.ctx ??= em.transactionContext;
|
|
976
|
-
const propagateToUpperContext = !em.global || this.config.get('allowGlobalContext');
|
|
977
|
-
return TransactionContext.create(fork, async () => {
|
|
978
|
-
return fork.getConnection().transactional(async (trx) => {
|
|
979
|
-
fork.transactionContext = trx;
|
|
980
|
-
if (propagateToUpperContext) {
|
|
981
|
-
fork.eventManager.registerSubscriber({
|
|
982
|
-
afterFlush(args) {
|
|
983
|
-
args.uow.getChangeSets()
|
|
984
|
-
.filter(cs => [ChangeSetType.DELETE, ChangeSetType.DELETE_EARLY].includes(cs.type))
|
|
985
|
-
.forEach(cs => em.unitOfWork.unsetIdentity(cs.entity));
|
|
986
|
-
},
|
|
987
|
-
});
|
|
988
|
-
}
|
|
989
|
-
const ret = await cb(fork);
|
|
990
|
-
await fork.flush();
|
|
991
|
-
if (propagateToUpperContext) {
|
|
992
|
-
// ensure all entities from inner context are merged to the upper one
|
|
993
|
-
for (const entity of fork.unitOfWork.getIdentityMap()) {
|
|
994
|
-
em.unitOfWork.register(entity);
|
|
995
|
-
entity.__helper.__em = em;
|
|
996
|
-
}
|
|
997
|
-
}
|
|
998
|
-
return ret;
|
|
999
|
-
}, { ...options, eventBroadcaster: new TransactionEventBroadcaster(fork, { topLevelTransaction: !options.ctx }) });
|
|
1000
|
-
});
|
|
1043
|
+
const manager = new TransactionManager(this);
|
|
1044
|
+
return manager.handle(cb, options);
|
|
1001
1045
|
}
|
|
1002
1046
|
/**
|
|
1003
1047
|
* Starts new transaction bound to this EntityManager. Use `ctx` parameter to provide the parent when nesting transactions.
|
|
@@ -1058,11 +1102,11 @@ export class EntityManager {
|
|
|
1058
1102
|
em.prepareOptions(options);
|
|
1059
1103
|
let entityName;
|
|
1060
1104
|
if (data === undefined) {
|
|
1061
|
-
entityName = entityNameOrEntity.constructor
|
|
1105
|
+
entityName = entityNameOrEntity.constructor;
|
|
1062
1106
|
data = entityNameOrEntity;
|
|
1063
1107
|
}
|
|
1064
1108
|
else {
|
|
1065
|
-
entityName =
|
|
1109
|
+
entityName = entityNameOrEntity;
|
|
1066
1110
|
}
|
|
1067
1111
|
if (Utils.isEntity(data)) {
|
|
1068
1112
|
if (options.schema && helper(data).getSchema() == null) {
|
|
@@ -1081,7 +1125,7 @@ export class EntityManager {
|
|
|
1081
1125
|
return cs.getPrimaryKey();
|
|
1082
1126
|
}
|
|
1083
1127
|
data = QueryHelper.processObjectParams(data);
|
|
1084
|
-
|
|
1128
|
+
validateParams(data, 'insert data');
|
|
1085
1129
|
const res = await em.driver.nativeInsert(entityName, data, { ctx: em.transactionContext, ...options });
|
|
1086
1130
|
return res.insertId;
|
|
1087
1131
|
}
|
|
@@ -1093,11 +1137,11 @@ export class EntityManager {
|
|
|
1093
1137
|
em.prepareOptions(options);
|
|
1094
1138
|
let entityName;
|
|
1095
1139
|
if (data === undefined) {
|
|
1096
|
-
entityName = entityNameOrEntities[0].constructor
|
|
1140
|
+
entityName = entityNameOrEntities[0].constructor;
|
|
1097
1141
|
data = entityNameOrEntities;
|
|
1098
1142
|
}
|
|
1099
1143
|
else {
|
|
1100
|
-
entityName =
|
|
1144
|
+
entityName = entityNameOrEntities;
|
|
1101
1145
|
}
|
|
1102
1146
|
if (data.length === 0) {
|
|
1103
1147
|
return [];
|
|
@@ -1121,7 +1165,7 @@ export class EntityManager {
|
|
|
1121
1165
|
return css.map(cs => cs.getPrimaryKey());
|
|
1122
1166
|
}
|
|
1123
1167
|
data = data.map(row => QueryHelper.processObjectParams(row));
|
|
1124
|
-
data.forEach(row =>
|
|
1168
|
+
data.forEach(row => validateParams(row, 'insert data'));
|
|
1125
1169
|
const res = await em.driver.nativeInsertMany(entityName, data, { ctx: em.transactionContext, ...options });
|
|
1126
1170
|
if (res.insertedIds) {
|
|
1127
1171
|
return res.insertedIds;
|
|
@@ -1134,11 +1178,10 @@ export class EntityManager {
|
|
|
1134
1178
|
async nativeUpdate(entityName, where, data, options = {}) {
|
|
1135
1179
|
const em = this.getContext(false);
|
|
1136
1180
|
em.prepareOptions(options);
|
|
1137
|
-
entityName = Utils.className(entityName);
|
|
1138
1181
|
data = QueryHelper.processObjectParams(data);
|
|
1139
1182
|
where = await em.processWhere(entityName, where, { ...options, convertCustomTypes: false }, 'update');
|
|
1140
|
-
|
|
1141
|
-
|
|
1183
|
+
validateParams(data, 'update data');
|
|
1184
|
+
validateParams(where, 'update condition');
|
|
1142
1185
|
const res = await em.driver.nativeUpdate(entityName, where, data, { ctx: em.transactionContext, ...options });
|
|
1143
1186
|
return res.affectedRows;
|
|
1144
1187
|
}
|
|
@@ -1148,9 +1191,8 @@ export class EntityManager {
|
|
|
1148
1191
|
async nativeDelete(entityName, where, options = {}) {
|
|
1149
1192
|
const em = this.getContext(false);
|
|
1150
1193
|
em.prepareOptions(options);
|
|
1151
|
-
entityName = Utils.className(entityName);
|
|
1152
1194
|
where = await em.processWhere(entityName, where, options, 'delete');
|
|
1153
|
-
|
|
1195
|
+
validateParams(where, 'delete condition');
|
|
1154
1196
|
const res = await em.driver.nativeDelete(entityName, where, { ctx: em.transactionContext, ...options });
|
|
1155
1197
|
return res.affectedRows;
|
|
1156
1198
|
}
|
|
@@ -1158,18 +1200,19 @@ export class EntityManager {
|
|
|
1158
1200
|
* Maps raw database result to an entity and merges it to this EntityManager.
|
|
1159
1201
|
*/
|
|
1160
1202
|
map(entityName, result, options = {}) {
|
|
1161
|
-
entityName = Utils.className(entityName);
|
|
1162
1203
|
const meta = this.metadata.get(entityName);
|
|
1163
1204
|
const data = this.driver.mapResult(result, meta);
|
|
1164
|
-
Object.keys(data)
|
|
1205
|
+
for (const k of Object.keys(data)) {
|
|
1165
1206
|
const prop = meta.properties[k];
|
|
1166
|
-
if (prop
|
|
1167
|
-
|
|
1207
|
+
if (prop?.kind === ReferenceKind.SCALAR && SCALAR_TYPES.has(prop.runtimeType) && !prop.customType && (prop.setter || !prop.getter)) {
|
|
1208
|
+
validateProperty(prop, data[k], data);
|
|
1168
1209
|
}
|
|
1169
|
-
}
|
|
1210
|
+
}
|
|
1170
1211
|
return this.merge(entityName, data, {
|
|
1171
1212
|
convertCustomTypes: true,
|
|
1172
|
-
refresh: true,
|
|
1213
|
+
refresh: true,
|
|
1214
|
+
validate: false,
|
|
1215
|
+
...options,
|
|
1173
1216
|
});
|
|
1174
1217
|
}
|
|
1175
1218
|
/**
|
|
@@ -1177,22 +1220,22 @@ export class EntityManager {
|
|
|
1177
1220
|
* via second parameter. By default, it will return already loaded entities without modifying them.
|
|
1178
1221
|
*/
|
|
1179
1222
|
merge(entityName, data, options = {}) {
|
|
1180
|
-
const em = this.getContext();
|
|
1181
1223
|
if (Utils.isEntity(entityName)) {
|
|
1182
|
-
return
|
|
1224
|
+
return this.merge(entityName.constructor, entityName, data);
|
|
1183
1225
|
}
|
|
1226
|
+
const em = options.disableContextResolution ? this : this.getContext();
|
|
1184
1227
|
options.schema ??= em._schema;
|
|
1185
|
-
|
|
1186
|
-
|
|
1228
|
+
options.validate ??= true;
|
|
1229
|
+
options.cascade ??= true;
|
|
1230
|
+
validatePrimaryKey(data, em.metadata.get(entityName));
|
|
1187
1231
|
let entity = em.unitOfWork.tryGetById(entityName, data, options.schema, false);
|
|
1188
1232
|
if (entity && helper(entity).__managed && helper(entity).__initialized && !options.refresh) {
|
|
1189
1233
|
return entity;
|
|
1190
1234
|
}
|
|
1191
|
-
const
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
em.
|
|
1195
|
-
em.unitOfWork.merge(entity);
|
|
1235
|
+
const dataIsEntity = Utils.isEntity(data);
|
|
1236
|
+
entity = dataIsEntity ? data : em.entityFactory.create(entityName, data, { merge: true, ...options });
|
|
1237
|
+
const visited = options.cascade ? undefined : new Set([entity]);
|
|
1238
|
+
em.unitOfWork.merge(entity, visited);
|
|
1196
1239
|
return entity;
|
|
1197
1240
|
}
|
|
1198
1241
|
/**
|
|
@@ -1217,6 +1260,7 @@ export class EntityManager {
|
|
|
1217
1260
|
...options,
|
|
1218
1261
|
newEntity: !options.managed,
|
|
1219
1262
|
merge: options.managed,
|
|
1263
|
+
normalizeAccessors: true,
|
|
1220
1264
|
});
|
|
1221
1265
|
options.persist ??= em.config.get('persistOnCreate');
|
|
1222
1266
|
if (options.persist && !this.getMetadata(entityName).embeddable) {
|
|
@@ -1236,7 +1280,7 @@ export class EntityManager {
|
|
|
1236
1280
|
getReference(entityName, id, options = {}) {
|
|
1237
1281
|
options.schema ??= this.schema;
|
|
1238
1282
|
options.convertCustomTypes ??= false;
|
|
1239
|
-
const meta = this.metadata.get(
|
|
1283
|
+
const meta = this.metadata.get(entityName);
|
|
1240
1284
|
if (Utils.isPrimaryKey(id)) {
|
|
1241
1285
|
if (meta.compositePK) {
|
|
1242
1286
|
throw ValidationError.invalidCompositeIdentifier(meta);
|
|
@@ -1255,11 +1299,8 @@ export class EntityManager {
|
|
|
1255
1299
|
async count(entityName, where = {}, options = {}) {
|
|
1256
1300
|
const em = this.getContext(false);
|
|
1257
1301
|
// Shallow copy options since the object will be modified when deleting orderBy
|
|
1258
|
-
options = {
|
|
1259
|
-
|
|
1260
|
-
...options,
|
|
1261
|
-
};
|
|
1262
|
-
entityName = Utils.className(entityName);
|
|
1302
|
+
options = { ...options };
|
|
1303
|
+
em.prepareOptions(options);
|
|
1263
1304
|
await em.tryFlush(entityName, options);
|
|
1264
1305
|
where = await em.processWhere(entityName, where, options, 'read');
|
|
1265
1306
|
options.populate = await em.preparePopulate(entityName, options);
|
|
@@ -1268,15 +1309,15 @@ export class EntityManager {
|
|
|
1268
1309
|
const meta = em.metadata.find(entityName);
|
|
1269
1310
|
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
1270
1311
|
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
1271
|
-
options.populateFilter = await this.getJoinedFilters(meta,
|
|
1272
|
-
|
|
1312
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
1313
|
+
validateParams(where);
|
|
1273
1314
|
delete options.orderBy;
|
|
1274
1315
|
const cacheKey = em.cacheKey(entityName, options, 'em.count', where);
|
|
1275
1316
|
const cached = await em.tryCache(entityName, options.cache, cacheKey);
|
|
1276
|
-
if (cached?.data) {
|
|
1317
|
+
if (cached?.data !== undefined) {
|
|
1277
1318
|
return cached.data;
|
|
1278
1319
|
}
|
|
1279
|
-
const count = await em.driver.count(entityName, where, { ctx: em.transactionContext, ...options });
|
|
1320
|
+
const count = await em.driver.count(entityName, where, { ctx: em.transactionContext, em, ...options });
|
|
1280
1321
|
await em.storeCache(options.cache, cached, () => +count);
|
|
1281
1322
|
return +count;
|
|
1282
1323
|
}
|
|
@@ -1295,7 +1336,7 @@ export class EntityManager {
|
|
|
1295
1336
|
for (const ent of entities) {
|
|
1296
1337
|
if (!Utils.isEntity(ent, true)) {
|
|
1297
1338
|
/* v8 ignore next */
|
|
1298
|
-
const meta = typeof ent === 'object' ? em.metadata.find(ent.constructor
|
|
1339
|
+
const meta = typeof ent === 'object' ? em.metadata.find(ent.constructor) : undefined;
|
|
1299
1340
|
throw ValidationError.notDiscoveredEntity(ent, meta);
|
|
1300
1341
|
}
|
|
1301
1342
|
// do not cascade just yet, cascading of entities in persist stack is done when flushing
|
|
@@ -1303,13 +1344,6 @@ export class EntityManager {
|
|
|
1303
1344
|
}
|
|
1304
1345
|
return this;
|
|
1305
1346
|
}
|
|
1306
|
-
/**
|
|
1307
|
-
* Persists your entity immediately, flushing all not yet persisted changes to the database too.
|
|
1308
|
-
* Equivalent to `em.persist(e).flush()`.
|
|
1309
|
-
*/
|
|
1310
|
-
async persistAndFlush(entity) {
|
|
1311
|
-
await this.persist(entity).flush();
|
|
1312
|
-
}
|
|
1313
1347
|
/**
|
|
1314
1348
|
* Marks entity for removal.
|
|
1315
1349
|
* A removed entity will be removed from the database at or before transaction commit or as a result of the flush operation.
|
|
@@ -1333,13 +1367,6 @@ export class EntityManager {
|
|
|
1333
1367
|
}
|
|
1334
1368
|
return em;
|
|
1335
1369
|
}
|
|
1336
|
-
/**
|
|
1337
|
-
* Removes an entity instance immediately, flushing all not yet persisted changes to the database too.
|
|
1338
|
-
* Equivalent to `em.remove(e).flush()`
|
|
1339
|
-
*/
|
|
1340
|
-
async removeAndFlush(entity) {
|
|
1341
|
-
await this.remove(entity).flush();
|
|
1342
|
-
}
|
|
1343
1370
|
/**
|
|
1344
1371
|
* Flushes all changes to objects that have been queued up to now to the database.
|
|
1345
1372
|
* This effectively synchronizes the in-memory state of managed objects with the database.
|
|
@@ -1353,7 +1380,6 @@ export class EntityManager {
|
|
|
1353
1380
|
async tryFlush(entityName, options) {
|
|
1354
1381
|
const em = this.getContext();
|
|
1355
1382
|
const flushMode = options.flushMode ?? em.flushMode ?? em.config.get('flushMode');
|
|
1356
|
-
entityName = Utils.className(entityName);
|
|
1357
1383
|
const meta = em.metadata.get(entityName);
|
|
1358
1384
|
if (flushMode === FlushMode.COMMIT) {
|
|
1359
1385
|
return;
|
|
@@ -1372,7 +1398,6 @@ export class EntityManager {
|
|
|
1372
1398
|
* Checks whether given property can be populated on the entity.
|
|
1373
1399
|
*/
|
|
1374
1400
|
canPopulate(entityName, property) {
|
|
1375
|
-
entityName = Utils.className(entityName);
|
|
1376
1401
|
// eslint-disable-next-line prefer-const
|
|
1377
1402
|
let [p, ...parts] = property.split('.');
|
|
1378
1403
|
const meta = this.metadata.find(entityName);
|
|
@@ -1383,11 +1408,8 @@ export class EntityManager {
|
|
|
1383
1408
|
p = p.split(':', 2)[0];
|
|
1384
1409
|
}
|
|
1385
1410
|
const ret = p in meta.root.properties;
|
|
1386
|
-
if (!ret) {
|
|
1387
|
-
return !!this.metadata.find(property)?.pivotTable;
|
|
1388
|
-
}
|
|
1389
1411
|
if (parts.length > 0) {
|
|
1390
|
-
return this.canPopulate(
|
|
1412
|
+
return this.canPopulate(meta.root.properties[p].targetMeta.class, parts.join('.'));
|
|
1391
1413
|
}
|
|
1392
1414
|
return ret;
|
|
1393
1415
|
}
|
|
@@ -1401,8 +1423,8 @@ export class EntityManager {
|
|
|
1401
1423
|
}
|
|
1402
1424
|
const em = this.getContext();
|
|
1403
1425
|
em.prepareOptions(options);
|
|
1404
|
-
const entityName = arr[0].constructor
|
|
1405
|
-
const preparedPopulate = await em.preparePopulate(entityName, { populate: populate }, options.validate);
|
|
1426
|
+
const entityName = arr[0].constructor;
|
|
1427
|
+
const preparedPopulate = await em.preparePopulate(entityName, { populate: populate, filters: options.filters }, options.validate);
|
|
1406
1428
|
await em.entityLoader.populate(entityName, arr, preparedPopulate, options);
|
|
1407
1429
|
return entities;
|
|
1408
1430
|
}
|
|
@@ -1462,6 +1484,12 @@ export class EntityManager {
|
|
|
1462
1484
|
getEntityFactory() {
|
|
1463
1485
|
return this.getContext().entityFactory;
|
|
1464
1486
|
}
|
|
1487
|
+
/**
|
|
1488
|
+
* @internal use `em.populate()` as the user facing API, this is exposed only for internal usage
|
|
1489
|
+
*/
|
|
1490
|
+
getEntityLoader() {
|
|
1491
|
+
return this.getContext().entityLoader;
|
|
1492
|
+
}
|
|
1465
1493
|
/**
|
|
1466
1494
|
* Gets the Hydrator used by the EntityManager.
|
|
1467
1495
|
*/
|
|
@@ -1524,7 +1552,6 @@ export class EntityManager {
|
|
|
1524
1552
|
*/
|
|
1525
1553
|
getMetadata(entityName) {
|
|
1526
1554
|
if (entityName) {
|
|
1527
|
-
entityName = Utils.className(entityName);
|
|
1528
1555
|
return this.metadata.get(entityName);
|
|
1529
1556
|
}
|
|
1530
1557
|
return this.metadata;
|
|
@@ -1553,12 +1580,11 @@ export class EntityManager {
|
|
|
1553
1580
|
lockTableAliases: options.lockTableAliases,
|
|
1554
1581
|
});
|
|
1555
1582
|
}
|
|
1556
|
-
const preparedPopulate = await this.preparePopulate(meta.
|
|
1557
|
-
await this.entityLoader.populate(meta.
|
|
1583
|
+
const preparedPopulate = await this.preparePopulate(meta.class, options);
|
|
1584
|
+
await this.entityLoader.populate(meta.class, [entity], preparedPopulate, {
|
|
1558
1585
|
...options,
|
|
1559
1586
|
...this.getPopulateWhere(where, options),
|
|
1560
1587
|
orderBy: options.populateOrderBy ?? options.orderBy,
|
|
1561
|
-
convertCustomTypes: false,
|
|
1562
1588
|
ignoreLazyScalarProperties: true,
|
|
1563
1589
|
lookup: false,
|
|
1564
1590
|
});
|
|
@@ -1621,7 +1647,7 @@ export class EntityManager {
|
|
|
1621
1647
|
}
|
|
1622
1648
|
if (typeof options.populate !== 'boolean') {
|
|
1623
1649
|
options.populate = Utils.asArray(options.populate).map(field => {
|
|
1624
|
-
/* v8 ignore next
|
|
1650
|
+
/* v8 ignore next */
|
|
1625
1651
|
if (typeof field === 'boolean' || field === PopulatePath.ALL) {
|
|
1626
1652
|
return [{ field: meta.primaryKeys[0], strategy: options.strategy, all: !!field }]; //
|
|
1627
1653
|
}
|
|
@@ -1631,7 +1657,7 @@ export class EntityManager {
|
|
|
1631
1657
|
options.flags.push(QueryFlag.INFER_POPULATE);
|
|
1632
1658
|
return [];
|
|
1633
1659
|
}
|
|
1634
|
-
if (
|
|
1660
|
+
if (typeof field === 'string') {
|
|
1635
1661
|
return [{ field, strategy: options.strategy }];
|
|
1636
1662
|
}
|
|
1637
1663
|
return [field];
|
|
@@ -1681,7 +1707,7 @@ export class EntityManager {
|
|
|
1681
1707
|
throw new ValidationError(`Cannot combine 'fields' and 'exclude' option.`);
|
|
1682
1708
|
}
|
|
1683
1709
|
options.schema ??= this._schema;
|
|
1684
|
-
options.logging = Utils.merge({ id: this.id }, this.loggerContext, options.loggerContext, options.logging);
|
|
1710
|
+
options.logging = options.loggerContext = Utils.merge({ id: this.id }, this.loggerContext, options.loggerContext, options.logging);
|
|
1685
1711
|
}
|
|
1686
1712
|
/**
|
|
1687
1713
|
* @internal
|
|
@@ -1692,7 +1718,7 @@ export class EntityManager {
|
|
|
1692
1718
|
for (const k of ['ctx', 'strategy', 'flushMode', 'logging', 'loggerContext']) {
|
|
1693
1719
|
delete opts[k];
|
|
1694
1720
|
}
|
|
1695
|
-
return [entityName, method, opts, where];
|
|
1721
|
+
return [Utils.className(entityName), method, opts, where];
|
|
1696
1722
|
}
|
|
1697
1723
|
/**
|
|
1698
1724
|
* @internal
|
|
@@ -1705,31 +1731,31 @@ export class EntityManager {
|
|
|
1705
1731
|
const em = this.getContext();
|
|
1706
1732
|
const cacheKey = Array.isArray(config) ? config[0] : JSON.stringify(key);
|
|
1707
1733
|
const cached = await em.resultCache.get(cacheKey);
|
|
1708
|
-
if (cached) {
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
data = cached;
|
|
1728
|
-
}
|
|
1729
|
-
await em.unitOfWork.dispatchOnLoadEvent();
|
|
1730
|
-
return { key: cacheKey, data };
|
|
1734
|
+
if (!cached) {
|
|
1735
|
+
return { key: cacheKey, data: cached };
|
|
1736
|
+
}
|
|
1737
|
+
let data;
|
|
1738
|
+
if (Array.isArray(cached) && merge) {
|
|
1739
|
+
data = cached.map(item => em.entityFactory.create(entityName, item, {
|
|
1740
|
+
merge: true,
|
|
1741
|
+
convertCustomTypes: true,
|
|
1742
|
+
refresh,
|
|
1743
|
+
recomputeSnapshot: true,
|
|
1744
|
+
}));
|
|
1745
|
+
}
|
|
1746
|
+
else if (Utils.isObject(cached) && merge) {
|
|
1747
|
+
data = em.entityFactory.create(entityName, cached, {
|
|
1748
|
+
merge: true,
|
|
1749
|
+
convertCustomTypes: true,
|
|
1750
|
+
refresh,
|
|
1751
|
+
recomputeSnapshot: true,
|
|
1752
|
+
});
|
|
1731
1753
|
}
|
|
1732
|
-
|
|
1754
|
+
else {
|
|
1755
|
+
data = cached;
|
|
1756
|
+
}
|
|
1757
|
+
await em.unitOfWork.dispatchOnLoadEvent();
|
|
1758
|
+
return { key: cacheKey, data };
|
|
1733
1759
|
}
|
|
1734
1760
|
/**
|
|
1735
1761
|
* @internal
|
|
@@ -1738,7 +1764,7 @@ export class EntityManager {
|
|
|
1738
1764
|
config ??= this.config.get('resultCache').global;
|
|
1739
1765
|
if (config) {
|
|
1740
1766
|
const em = this.getContext();
|
|
1741
|
-
const expiration = Array.isArray(config) ? config[1] : (
|
|
1767
|
+
const expiration = Array.isArray(config) ? config[1] : (typeof config === 'number' ? config : undefined);
|
|
1742
1768
|
await em.resultCache.set(key.key, data instanceof Function ? data() : data, '', expiration);
|
|
1743
1769
|
}
|
|
1744
1770
|
}
|
|
@@ -1771,6 +1797,19 @@ export class EntityManager {
|
|
|
1771
1797
|
set schema(schema) {
|
|
1772
1798
|
this.getContext(false)._schema = schema ?? undefined;
|
|
1773
1799
|
}
|
|
1800
|
+
/** @internal */
|
|
1801
|
+
async getDataLoader(type) {
|
|
1802
|
+
const em = this.getContext();
|
|
1803
|
+
if (em.loaders[type]) {
|
|
1804
|
+
return em.loaders[type];
|
|
1805
|
+
}
|
|
1806
|
+
const DataLoader = await DataloaderUtils.getDataLoader();
|
|
1807
|
+
switch (type) {
|
|
1808
|
+
case 'ref': return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getRefBatchLoadFn(em)));
|
|
1809
|
+
case '1:m': return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getColBatchLoadFn(em)));
|
|
1810
|
+
case 'm:n': return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getManyToManyColBatchLoadFn(em)));
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1774
1813
|
/**
|
|
1775
1814
|
* Returns the ID of this EntityManager. Respects the context, so global EM will give you the contextual ID
|
|
1776
1815
|
* if executed inside request context handler.
|
|
@@ -1779,7 +1818,7 @@ export class EntityManager {
|
|
|
1779
1818
|
return this.getContext(false)._id;
|
|
1780
1819
|
}
|
|
1781
1820
|
/** @ignore */
|
|
1782
|
-
[inspect.custom]() {
|
|
1821
|
+
[Symbol.for('nodejs.util.inspect.custom')]() {
|
|
1783
1822
|
return `[EntityManager<${this.id}>]`;
|
|
1784
1823
|
}
|
|
1785
1824
|
}
|