@mikro-orm/core 7.0.0-dev.22 → 7.0.0-dev.221
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 +101 -59
- package/EntityManager.js +302 -276
- package/MikroORM.d.ts +44 -35
- package/MikroORM.js +109 -143
- package/README.md +3 -2
- package/cache/FileCacheAdapter.d.ts +1 -1
- package/cache/FileCacheAdapter.js +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 +80 -35
- package/drivers/IDatabaseDriver.d.ts +46 -19
- package/entity/BaseEntity.d.ts +61 -2
- package/entity/BaseEntity.js +0 -3
- package/entity/Collection.d.ts +94 -29
- package/entity/Collection.js +434 -97
- package/entity/EntityAssigner.d.ts +1 -1
- package/entity/EntityAssigner.js +26 -18
- package/entity/EntityFactory.d.ts +13 -1
- package/entity/EntityFactory.js +84 -53
- package/entity/EntityHelper.d.ts +2 -2
- package/entity/EntityHelper.js +40 -15
- package/entity/EntityLoader.d.ts +6 -6
- package/entity/EntityLoader.js +119 -82
- package/entity/EntityRepository.d.ts +27 -7
- package/entity/EntityRepository.js +8 -2
- package/entity/Reference.d.ts +6 -5
- package/entity/Reference.js +34 -9
- package/entity/WrappedEntity.d.ts +0 -5
- package/entity/WrappedEntity.js +3 -8
- package/entity/defineEntity.d.ts +595 -0
- package/entity/defineEntity.js +533 -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 +22 -7
- package/enums.js +15 -1
- package/errors.d.ts +22 -9
- package/errors.js +56 -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 +52 -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 +40 -23
- package/metadata/EntitySchema.js +81 -34
- package/metadata/MetadataDiscovery.d.ts +7 -10
- package/metadata/MetadataDiscovery.js +391 -331
- 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 +17 -9
- package/metadata/MetadataValidator.js +94 -40
- 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 +498 -0
- package/metadata/types.js +1 -0
- package/naming-strategy/AbstractNamingStrategy.d.ts +12 -4
- package/naming-strategy/AbstractNamingStrategy.js +14 -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 +24 -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 +10 -15
- package/platforms/Platform.js +20 -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 +315 -155
- package/typings.js +66 -44
- 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 +26 -13
- package/unit-of-work/ChangeSetPersister.d.ts +5 -4
- package/unit-of-work/ChangeSetPersister.js +70 -34
- 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 +23 -3
- package/unit-of-work/UnitOfWork.js +175 -98
- 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 +791 -207
- package/utils/Configuration.js +147 -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 +27 -11
- package/utils/DataloaderUtils.d.ts +15 -5
- package/utils/DataloaderUtils.js +64 -30
- package/utils/EntityComparator.d.ts +13 -9
- package/utils/EntityComparator.js +101 -42
- package/utils/QueryHelper.d.ts +14 -6
- package/utils/QueryHelper.js +87 -25
- package/utils/RawQueryFragment.d.ts +60 -32
- package/utils/RawQueryFragment.js +68 -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 +13 -126
- package/utils/Utils.js +100 -391
- 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 +33 -0
- package/utils/fs-utils.js +192 -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 -12
- 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 -32
- 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 -26
- 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;
|
|
502
578
|
}
|
|
503
|
-
|
|
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));
|
|
583
|
+
}
|
|
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,13 +606,13 @@ 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);
|
|
@@ -541,7 +621,6 @@ export class EntityManager {
|
|
|
541
621
|
await em.entityLoader.populate(entityName, [cached.data], options.populate, {
|
|
542
622
|
...options,
|
|
543
623
|
...em.getPopulateWhere(where, options),
|
|
544
|
-
convertCustomTypes: false,
|
|
545
624
|
ignoreLazyScalarProperties: true,
|
|
546
625
|
lookup: false,
|
|
547
626
|
});
|
|
@@ -552,7 +631,7 @@ export class EntityManager {
|
|
|
552
631
|
// save the original hint value so we know it was infer/all
|
|
553
632
|
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
554
633
|
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
555
|
-
options.populateFilter = await this.getJoinedFilters(meta,
|
|
634
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
556
635
|
const data = await em.driver.findOne(entityName, where, {
|
|
557
636
|
ctx: em.transactionContext,
|
|
558
637
|
em,
|
|
@@ -593,10 +672,10 @@ export class EntityManager {
|
|
|
593
672
|
if (!entity || isStrictViolation) {
|
|
594
673
|
const key = options.strict ? 'findExactlyOneOrFailHandler' : 'findOneOrFailHandler';
|
|
595
674
|
options.failHandler ??= this.config.get(key);
|
|
596
|
-
|
|
675
|
+
const name = Utils.className(entityName);
|
|
597
676
|
/* v8 ignore next */
|
|
598
677
|
where = Utils.isEntity(where) ? helper(where).getPrimaryKey() : where;
|
|
599
|
-
throw options.failHandler(
|
|
678
|
+
throw options.failHandler(name, where);
|
|
600
679
|
}
|
|
601
680
|
return entity;
|
|
602
681
|
}
|
|
@@ -636,11 +715,11 @@ export class EntityManager {
|
|
|
636
715
|
let where;
|
|
637
716
|
let entity = null;
|
|
638
717
|
if (data === undefined) {
|
|
639
|
-
entityName = entityNameOrEntity.constructor
|
|
718
|
+
entityName = entityNameOrEntity.constructor;
|
|
640
719
|
data = entityNameOrEntity;
|
|
641
720
|
}
|
|
642
721
|
else {
|
|
643
|
-
entityName =
|
|
722
|
+
entityName = entityNameOrEntity;
|
|
644
723
|
}
|
|
645
724
|
const meta = this.metadata.get(entityName);
|
|
646
725
|
const convertCustomTypes = !Utils.isEntity(data);
|
|
@@ -663,26 +742,9 @@ export class EntityManager {
|
|
|
663
742
|
}
|
|
664
743
|
}
|
|
665
744
|
}
|
|
666
|
-
|
|
667
|
-
const propIndex = !isRaw(unique) && unique.findIndex(p => data[p] != null);
|
|
668
|
-
if (options.onConflictFields || where == null) {
|
|
669
|
-
if (propIndex !== false && propIndex >= 0) {
|
|
670
|
-
where = { [unique[propIndex]]: data[unique[propIndex]] };
|
|
671
|
-
}
|
|
672
|
-
else if (meta.uniques.length > 0) {
|
|
673
|
-
for (const u of meta.uniques) {
|
|
674
|
-
if (Utils.asArray(u.properties).every(p => data[p] != null)) {
|
|
675
|
-
where = Utils.asArray(u.properties).reduce((o, key) => {
|
|
676
|
-
o[key] = data[key];
|
|
677
|
-
return o;
|
|
678
|
-
}, {});
|
|
679
|
-
break;
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
}
|
|
745
|
+
where = getWhereCondition(meta, options.onConflictFields, data, where).where;
|
|
684
746
|
data = QueryHelper.processObjectParams(data);
|
|
685
|
-
|
|
747
|
+
validateParams(data, 'insert data');
|
|
686
748
|
if (em.eventManager.hasListeners(EventType.beforeUpsert, meta)) {
|
|
687
749
|
await em.eventManager.dispatchEvent(EventType.beforeUpsert, { entity: data, em, meta }, meta);
|
|
688
750
|
}
|
|
@@ -720,7 +782,7 @@ export class EntityManager {
|
|
|
720
782
|
where[meta.primaryKeys[0]] = ret.insertId;
|
|
721
783
|
}
|
|
722
784
|
}
|
|
723
|
-
const data2 = await this.driver.findOne(meta.
|
|
785
|
+
const data2 = await this.driver.findOne(meta.class, where, {
|
|
724
786
|
fields: returning,
|
|
725
787
|
ctx: em.transactionContext,
|
|
726
788
|
convertCustomTypes: true,
|
|
@@ -775,11 +837,11 @@ export class EntityManager {
|
|
|
775
837
|
let entityName;
|
|
776
838
|
let propIndex;
|
|
777
839
|
if (data === undefined) {
|
|
778
|
-
entityName = entityNameOrEntity[0].constructor
|
|
840
|
+
entityName = entityNameOrEntity[0].constructor;
|
|
779
841
|
data = entityNameOrEntity;
|
|
780
842
|
}
|
|
781
843
|
else {
|
|
782
|
-
entityName =
|
|
844
|
+
entityName = entityNameOrEntity;
|
|
783
845
|
}
|
|
784
846
|
const batchSize = options.batchSize ?? this.config.get('batchSize');
|
|
785
847
|
if (data.length > batchSize) {
|
|
@@ -823,32 +885,18 @@ export class EntityManager {
|
|
|
823
885
|
}
|
|
824
886
|
}
|
|
825
887
|
}
|
|
826
|
-
const unique = meta.props.filter(p => p.unique).map(p => p.name);
|
|
827
|
-
propIndex = unique.findIndex(p =>
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
where = { [unique[propIndex]]: row[unique[propIndex]] };
|
|
831
|
-
}
|
|
832
|
-
else if (meta.uniques.length > 0) {
|
|
833
|
-
for (const u of meta.uniques) {
|
|
834
|
-
if (Utils.asArray(u.properties).every(p => row[p] != null)) {
|
|
835
|
-
where = Utils.asArray(u.properties).reduce((o, key) => {
|
|
836
|
-
o[key] = row[key];
|
|
837
|
-
return o;
|
|
838
|
-
}, {});
|
|
839
|
-
break;
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
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;
|
|
845
892
|
where = QueryHelper.processWhere({
|
|
846
|
-
where,
|
|
893
|
+
where: tmp.where,
|
|
847
894
|
entityName,
|
|
848
895
|
metadata: this.metadata,
|
|
849
896
|
platform: this.getPlatform(),
|
|
850
897
|
});
|
|
851
|
-
|
|
898
|
+
row = QueryHelper.processObjectParams(row);
|
|
899
|
+
validateParams(row, 'insert data');
|
|
852
900
|
allData.push(row);
|
|
853
901
|
allWhere.push(where);
|
|
854
902
|
}
|
|
@@ -889,7 +937,7 @@ export class EntityManager {
|
|
|
889
937
|
const reloadFields = returning.length > 0 && !(this.getPlatform().usesReturningStatement() && res.rows?.length);
|
|
890
938
|
if (options.onConflictAction === 'ignore' || (!res.rows?.length && loadPK.size > 0) || reloadFields) {
|
|
891
939
|
const unique = meta.hydrateProps.filter(p => !p.lazy).map(p => p.name);
|
|
892
|
-
const add = new Set(propIndex >= 0 ? [unique[propIndex]] : []);
|
|
940
|
+
const add = new Set(propIndex !== false && propIndex >= 0 ? [unique[propIndex]] : []);
|
|
893
941
|
for (const cond of loadPK.values()) {
|
|
894
942
|
Utils.keys(cond).forEach(key => add.add(key));
|
|
895
943
|
}
|
|
@@ -901,7 +949,7 @@ export class EntityManager {
|
|
|
901
949
|
where.$or[idx][prop] = item[prop];
|
|
902
950
|
});
|
|
903
951
|
});
|
|
904
|
-
const data2 = await this.driver.find(meta.
|
|
952
|
+
const data2 = await this.driver.find(meta.class, where, {
|
|
905
953
|
fields: returning.concat(...add).concat(...(Array.isArray(uniqueFields) ? uniqueFields : [])),
|
|
906
954
|
ctx: em.transactionContext,
|
|
907
955
|
convertCustomTypes: true,
|
|
@@ -918,7 +966,7 @@ export class EntityManager {
|
|
|
918
966
|
});
|
|
919
967
|
return this.comparator.matching(entityName, cond, tmp);
|
|
920
968
|
});
|
|
921
|
-
/* v8 ignore next
|
|
969
|
+
/* v8 ignore next */
|
|
922
970
|
if (!row) {
|
|
923
971
|
throw new Error(`Cannot find matching entity for condition ${JSON.stringify(cond)}`);
|
|
924
972
|
}
|
|
@@ -941,7 +989,7 @@ export class EntityManager {
|
|
|
941
989
|
}, {});
|
|
942
990
|
return this.comparator.matching(entityName, cond, pk);
|
|
943
991
|
});
|
|
944
|
-
/* v8 ignore next
|
|
992
|
+
/* v8 ignore next */
|
|
945
993
|
if (!row) {
|
|
946
994
|
throw new Error(`Cannot find matching entity for condition ${JSON.stringify(cond)}`);
|
|
947
995
|
}
|
|
@@ -963,45 +1011,37 @@ export class EntityManager {
|
|
|
963
1011
|
}
|
|
964
1012
|
/**
|
|
965
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
|
+
* ```
|
|
966
1037
|
*/
|
|
967
1038
|
async transactional(cb, options = {}) {
|
|
968
1039
|
const em = this.getContext(false);
|
|
969
1040
|
if (this.disableTransactions || em.disableTransactions) {
|
|
970
1041
|
return cb(em);
|
|
971
1042
|
}
|
|
972
|
-
const
|
|
973
|
-
|
|
974
|
-
flushMode: options.flushMode,
|
|
975
|
-
cloneEventManager: true,
|
|
976
|
-
disableTransactions: options.ignoreNestedTransactions,
|
|
977
|
-
loggerContext: options.loggerContext,
|
|
978
|
-
});
|
|
979
|
-
options.ctx ??= em.transactionContext;
|
|
980
|
-
const propagateToUpperContext = !em.global || this.config.get('allowGlobalContext');
|
|
981
|
-
return TransactionContext.create(fork, async () => {
|
|
982
|
-
return fork.getConnection().transactional(async (trx) => {
|
|
983
|
-
fork.transactionContext = trx;
|
|
984
|
-
if (propagateToUpperContext) {
|
|
985
|
-
fork.eventManager.registerSubscriber({
|
|
986
|
-
afterFlush(args) {
|
|
987
|
-
args.uow.getChangeSets()
|
|
988
|
-
.filter(cs => [ChangeSetType.DELETE, ChangeSetType.DELETE_EARLY].includes(cs.type))
|
|
989
|
-
.forEach(cs => em.unitOfWork.unsetIdentity(cs.entity));
|
|
990
|
-
},
|
|
991
|
-
});
|
|
992
|
-
}
|
|
993
|
-
const ret = await cb(fork);
|
|
994
|
-
await fork.flush();
|
|
995
|
-
if (propagateToUpperContext) {
|
|
996
|
-
// ensure all entities from inner context are merged to the upper one
|
|
997
|
-
for (const entity of fork.unitOfWork.getIdentityMap()) {
|
|
998
|
-
em.unitOfWork.register(entity);
|
|
999
|
-
entity.__helper.__em = em;
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
return ret;
|
|
1003
|
-
}, { ...options, eventBroadcaster: new TransactionEventBroadcaster(fork, { topLevelTransaction: !options.ctx }) });
|
|
1004
|
-
});
|
|
1043
|
+
const manager = new TransactionManager(this);
|
|
1044
|
+
return manager.handle(cb, options);
|
|
1005
1045
|
}
|
|
1006
1046
|
/**
|
|
1007
1047
|
* Starts new transaction bound to this EntityManager. Use `ctx` parameter to provide the parent when nesting transactions.
|
|
@@ -1062,11 +1102,11 @@ export class EntityManager {
|
|
|
1062
1102
|
em.prepareOptions(options);
|
|
1063
1103
|
let entityName;
|
|
1064
1104
|
if (data === undefined) {
|
|
1065
|
-
entityName = entityNameOrEntity.constructor
|
|
1105
|
+
entityName = entityNameOrEntity.constructor;
|
|
1066
1106
|
data = entityNameOrEntity;
|
|
1067
1107
|
}
|
|
1068
1108
|
else {
|
|
1069
|
-
entityName =
|
|
1109
|
+
entityName = entityNameOrEntity;
|
|
1070
1110
|
}
|
|
1071
1111
|
if (Utils.isEntity(data)) {
|
|
1072
1112
|
if (options.schema && helper(data).getSchema() == null) {
|
|
@@ -1085,7 +1125,7 @@ export class EntityManager {
|
|
|
1085
1125
|
return cs.getPrimaryKey();
|
|
1086
1126
|
}
|
|
1087
1127
|
data = QueryHelper.processObjectParams(data);
|
|
1088
|
-
|
|
1128
|
+
validateParams(data, 'insert data');
|
|
1089
1129
|
const res = await em.driver.nativeInsert(entityName, data, { ctx: em.transactionContext, ...options });
|
|
1090
1130
|
return res.insertId;
|
|
1091
1131
|
}
|
|
@@ -1097,11 +1137,11 @@ export class EntityManager {
|
|
|
1097
1137
|
em.prepareOptions(options);
|
|
1098
1138
|
let entityName;
|
|
1099
1139
|
if (data === undefined) {
|
|
1100
|
-
entityName = entityNameOrEntities[0].constructor
|
|
1140
|
+
entityName = entityNameOrEntities[0].constructor;
|
|
1101
1141
|
data = entityNameOrEntities;
|
|
1102
1142
|
}
|
|
1103
1143
|
else {
|
|
1104
|
-
entityName =
|
|
1144
|
+
entityName = entityNameOrEntities;
|
|
1105
1145
|
}
|
|
1106
1146
|
if (data.length === 0) {
|
|
1107
1147
|
return [];
|
|
@@ -1125,7 +1165,7 @@ export class EntityManager {
|
|
|
1125
1165
|
return css.map(cs => cs.getPrimaryKey());
|
|
1126
1166
|
}
|
|
1127
1167
|
data = data.map(row => QueryHelper.processObjectParams(row));
|
|
1128
|
-
data.forEach(row =>
|
|
1168
|
+
data.forEach(row => validateParams(row, 'insert data'));
|
|
1129
1169
|
const res = await em.driver.nativeInsertMany(entityName, data, { ctx: em.transactionContext, ...options });
|
|
1130
1170
|
if (res.insertedIds) {
|
|
1131
1171
|
return res.insertedIds;
|
|
@@ -1138,11 +1178,10 @@ export class EntityManager {
|
|
|
1138
1178
|
async nativeUpdate(entityName, where, data, options = {}) {
|
|
1139
1179
|
const em = this.getContext(false);
|
|
1140
1180
|
em.prepareOptions(options);
|
|
1141
|
-
entityName = Utils.className(entityName);
|
|
1142
1181
|
data = QueryHelper.processObjectParams(data);
|
|
1143
1182
|
where = await em.processWhere(entityName, where, { ...options, convertCustomTypes: false }, 'update');
|
|
1144
|
-
|
|
1145
|
-
|
|
1183
|
+
validateParams(data, 'update data');
|
|
1184
|
+
validateParams(where, 'update condition');
|
|
1146
1185
|
const res = await em.driver.nativeUpdate(entityName, where, data, { ctx: em.transactionContext, ...options });
|
|
1147
1186
|
return res.affectedRows;
|
|
1148
1187
|
}
|
|
@@ -1152,9 +1191,8 @@ export class EntityManager {
|
|
|
1152
1191
|
async nativeDelete(entityName, where, options = {}) {
|
|
1153
1192
|
const em = this.getContext(false);
|
|
1154
1193
|
em.prepareOptions(options);
|
|
1155
|
-
entityName = Utils.className(entityName);
|
|
1156
1194
|
where = await em.processWhere(entityName, where, options, 'delete');
|
|
1157
|
-
|
|
1195
|
+
validateParams(where, 'delete condition');
|
|
1158
1196
|
const res = await em.driver.nativeDelete(entityName, where, { ctx: em.transactionContext, ...options });
|
|
1159
1197
|
return res.affectedRows;
|
|
1160
1198
|
}
|
|
@@ -1162,18 +1200,19 @@ export class EntityManager {
|
|
|
1162
1200
|
* Maps raw database result to an entity and merges it to this EntityManager.
|
|
1163
1201
|
*/
|
|
1164
1202
|
map(entityName, result, options = {}) {
|
|
1165
|
-
entityName = Utils.className(entityName);
|
|
1166
1203
|
const meta = this.metadata.get(entityName);
|
|
1167
1204
|
const data = this.driver.mapResult(result, meta);
|
|
1168
|
-
Object.keys(data)
|
|
1205
|
+
for (const k of Object.keys(data)) {
|
|
1169
1206
|
const prop = meta.properties[k];
|
|
1170
|
-
if (prop
|
|
1171
|
-
|
|
1207
|
+
if (prop?.kind === ReferenceKind.SCALAR && SCALAR_TYPES.has(prop.runtimeType) && !prop.customType && (prop.setter || !prop.getter)) {
|
|
1208
|
+
validateProperty(prop, data[k], data);
|
|
1172
1209
|
}
|
|
1173
|
-
}
|
|
1210
|
+
}
|
|
1174
1211
|
return this.merge(entityName, data, {
|
|
1175
1212
|
convertCustomTypes: true,
|
|
1176
|
-
refresh: true,
|
|
1213
|
+
refresh: true,
|
|
1214
|
+
validate: false,
|
|
1215
|
+
...options,
|
|
1177
1216
|
});
|
|
1178
1217
|
}
|
|
1179
1218
|
/**
|
|
@@ -1181,22 +1220,22 @@ export class EntityManager {
|
|
|
1181
1220
|
* via second parameter. By default, it will return already loaded entities without modifying them.
|
|
1182
1221
|
*/
|
|
1183
1222
|
merge(entityName, data, options = {}) {
|
|
1184
|
-
const em = this.getContext();
|
|
1185
1223
|
if (Utils.isEntity(entityName)) {
|
|
1186
|
-
return
|
|
1224
|
+
return this.merge(entityName.constructor, entityName, data);
|
|
1187
1225
|
}
|
|
1226
|
+
const em = options.disableContextResolution ? this : this.getContext();
|
|
1188
1227
|
options.schema ??= em._schema;
|
|
1189
|
-
|
|
1190
|
-
|
|
1228
|
+
options.validate ??= true;
|
|
1229
|
+
options.cascade ??= true;
|
|
1230
|
+
validatePrimaryKey(data, em.metadata.get(entityName));
|
|
1191
1231
|
let entity = em.unitOfWork.tryGetById(entityName, data, options.schema, false);
|
|
1192
1232
|
if (entity && helper(entity).__managed && helper(entity).__initialized && !options.refresh) {
|
|
1193
1233
|
return entity;
|
|
1194
1234
|
}
|
|
1195
|
-
const
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
em.
|
|
1199
|
-
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);
|
|
1200
1239
|
return entity;
|
|
1201
1240
|
}
|
|
1202
1241
|
/**
|
|
@@ -1221,6 +1260,7 @@ export class EntityManager {
|
|
|
1221
1260
|
...options,
|
|
1222
1261
|
newEntity: !options.managed,
|
|
1223
1262
|
merge: options.managed,
|
|
1263
|
+
normalizeAccessors: true,
|
|
1224
1264
|
});
|
|
1225
1265
|
options.persist ??= em.config.get('persistOnCreate');
|
|
1226
1266
|
if (options.persist && !this.getMetadata(entityName).embeddable) {
|
|
@@ -1240,7 +1280,7 @@ export class EntityManager {
|
|
|
1240
1280
|
getReference(entityName, id, options = {}) {
|
|
1241
1281
|
options.schema ??= this.schema;
|
|
1242
1282
|
options.convertCustomTypes ??= false;
|
|
1243
|
-
const meta = this.metadata.get(
|
|
1283
|
+
const meta = this.metadata.get(entityName);
|
|
1244
1284
|
if (Utils.isPrimaryKey(id)) {
|
|
1245
1285
|
if (meta.compositePK) {
|
|
1246
1286
|
throw ValidationError.invalidCompositeIdentifier(meta);
|
|
@@ -1259,11 +1299,8 @@ export class EntityManager {
|
|
|
1259
1299
|
async count(entityName, where = {}, options = {}) {
|
|
1260
1300
|
const em = this.getContext(false);
|
|
1261
1301
|
// Shallow copy options since the object will be modified when deleting orderBy
|
|
1262
|
-
options = {
|
|
1263
|
-
|
|
1264
|
-
...options,
|
|
1265
|
-
};
|
|
1266
|
-
entityName = Utils.className(entityName);
|
|
1302
|
+
options = { ...options };
|
|
1303
|
+
em.prepareOptions(options);
|
|
1267
1304
|
await em.tryFlush(entityName, options);
|
|
1268
1305
|
where = await em.processWhere(entityName, where, options, 'read');
|
|
1269
1306
|
options.populate = await em.preparePopulate(entityName, options);
|
|
@@ -1272,15 +1309,15 @@ export class EntityManager {
|
|
|
1272
1309
|
const meta = em.metadata.find(entityName);
|
|
1273
1310
|
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
1274
1311
|
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
1275
|
-
options.populateFilter = await this.getJoinedFilters(meta,
|
|
1276
|
-
|
|
1312
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
1313
|
+
validateParams(where);
|
|
1277
1314
|
delete options.orderBy;
|
|
1278
1315
|
const cacheKey = em.cacheKey(entityName, options, 'em.count', where);
|
|
1279
1316
|
const cached = await em.tryCache(entityName, options.cache, cacheKey);
|
|
1280
1317
|
if (cached?.data !== undefined) {
|
|
1281
1318
|
return cached.data;
|
|
1282
1319
|
}
|
|
1283
|
-
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 });
|
|
1284
1321
|
await em.storeCache(options.cache, cached, () => +count);
|
|
1285
1322
|
return +count;
|
|
1286
1323
|
}
|
|
@@ -1299,7 +1336,7 @@ export class EntityManager {
|
|
|
1299
1336
|
for (const ent of entities) {
|
|
1300
1337
|
if (!Utils.isEntity(ent, true)) {
|
|
1301
1338
|
/* v8 ignore next */
|
|
1302
|
-
const meta = typeof ent === 'object' ? em.metadata.find(ent.constructor
|
|
1339
|
+
const meta = typeof ent === 'object' ? em.metadata.find(ent.constructor) : undefined;
|
|
1303
1340
|
throw ValidationError.notDiscoveredEntity(ent, meta);
|
|
1304
1341
|
}
|
|
1305
1342
|
// do not cascade just yet, cascading of entities in persist stack is done when flushing
|
|
@@ -1307,13 +1344,6 @@ export class EntityManager {
|
|
|
1307
1344
|
}
|
|
1308
1345
|
return this;
|
|
1309
1346
|
}
|
|
1310
|
-
/**
|
|
1311
|
-
* Persists your entity immediately, flushing all not yet persisted changes to the database too.
|
|
1312
|
-
* Equivalent to `em.persist(e).flush()`.
|
|
1313
|
-
*/
|
|
1314
|
-
async persistAndFlush(entity) {
|
|
1315
|
-
await this.persist(entity).flush();
|
|
1316
|
-
}
|
|
1317
1347
|
/**
|
|
1318
1348
|
* Marks entity for removal.
|
|
1319
1349
|
* A removed entity will be removed from the database at or before transaction commit or as a result of the flush operation.
|
|
@@ -1337,13 +1367,6 @@ export class EntityManager {
|
|
|
1337
1367
|
}
|
|
1338
1368
|
return em;
|
|
1339
1369
|
}
|
|
1340
|
-
/**
|
|
1341
|
-
* Removes an entity instance immediately, flushing all not yet persisted changes to the database too.
|
|
1342
|
-
* Equivalent to `em.remove(e).flush()`
|
|
1343
|
-
*/
|
|
1344
|
-
async removeAndFlush(entity) {
|
|
1345
|
-
await this.remove(entity).flush();
|
|
1346
|
-
}
|
|
1347
1370
|
/**
|
|
1348
1371
|
* Flushes all changes to objects that have been queued up to now to the database.
|
|
1349
1372
|
* This effectively synchronizes the in-memory state of managed objects with the database.
|
|
@@ -1357,7 +1380,6 @@ export class EntityManager {
|
|
|
1357
1380
|
async tryFlush(entityName, options) {
|
|
1358
1381
|
const em = this.getContext();
|
|
1359
1382
|
const flushMode = options.flushMode ?? em.flushMode ?? em.config.get('flushMode');
|
|
1360
|
-
entityName = Utils.className(entityName);
|
|
1361
1383
|
const meta = em.metadata.get(entityName);
|
|
1362
1384
|
if (flushMode === FlushMode.COMMIT) {
|
|
1363
1385
|
return;
|
|
@@ -1376,7 +1398,6 @@ export class EntityManager {
|
|
|
1376
1398
|
* Checks whether given property can be populated on the entity.
|
|
1377
1399
|
*/
|
|
1378
1400
|
canPopulate(entityName, property) {
|
|
1379
|
-
entityName = Utils.className(entityName);
|
|
1380
1401
|
// eslint-disable-next-line prefer-const
|
|
1381
1402
|
let [p, ...parts] = property.split('.');
|
|
1382
1403
|
const meta = this.metadata.find(entityName);
|
|
@@ -1387,11 +1408,8 @@ export class EntityManager {
|
|
|
1387
1408
|
p = p.split(':', 2)[0];
|
|
1388
1409
|
}
|
|
1389
1410
|
const ret = p in meta.root.properties;
|
|
1390
|
-
if (!ret) {
|
|
1391
|
-
return !!this.metadata.find(property)?.pivotTable;
|
|
1392
|
-
}
|
|
1393
1411
|
if (parts.length > 0) {
|
|
1394
|
-
return this.canPopulate(
|
|
1412
|
+
return this.canPopulate(meta.root.properties[p].targetMeta.class, parts.join('.'));
|
|
1395
1413
|
}
|
|
1396
1414
|
return ret;
|
|
1397
1415
|
}
|
|
@@ -1405,8 +1423,8 @@ export class EntityManager {
|
|
|
1405
1423
|
}
|
|
1406
1424
|
const em = this.getContext();
|
|
1407
1425
|
em.prepareOptions(options);
|
|
1408
|
-
const entityName = arr[0].constructor
|
|
1409
|
-
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);
|
|
1410
1428
|
await em.entityLoader.populate(entityName, arr, preparedPopulate, options);
|
|
1411
1429
|
return entities;
|
|
1412
1430
|
}
|
|
@@ -1534,7 +1552,6 @@ export class EntityManager {
|
|
|
1534
1552
|
*/
|
|
1535
1553
|
getMetadata(entityName) {
|
|
1536
1554
|
if (entityName) {
|
|
1537
|
-
entityName = Utils.className(entityName);
|
|
1538
1555
|
return this.metadata.get(entityName);
|
|
1539
1556
|
}
|
|
1540
1557
|
return this.metadata;
|
|
@@ -1563,12 +1580,11 @@ export class EntityManager {
|
|
|
1563
1580
|
lockTableAliases: options.lockTableAliases,
|
|
1564
1581
|
});
|
|
1565
1582
|
}
|
|
1566
|
-
const preparedPopulate = await this.preparePopulate(meta.
|
|
1567
|
-
await this.entityLoader.populate(meta.
|
|
1583
|
+
const preparedPopulate = await this.preparePopulate(meta.class, options);
|
|
1584
|
+
await this.entityLoader.populate(meta.class, [entity], preparedPopulate, {
|
|
1568
1585
|
...options,
|
|
1569
1586
|
...this.getPopulateWhere(where, options),
|
|
1570
1587
|
orderBy: options.populateOrderBy ?? options.orderBy,
|
|
1571
|
-
convertCustomTypes: false,
|
|
1572
1588
|
ignoreLazyScalarProperties: true,
|
|
1573
1589
|
lookup: false,
|
|
1574
1590
|
});
|
|
@@ -1585,6 +1601,7 @@ export class EntityManager {
|
|
|
1585
1601
|
return ret;
|
|
1586
1602
|
}, []);
|
|
1587
1603
|
}
|
|
1604
|
+
/** @internal */
|
|
1588
1605
|
async preparePopulate(entityName, options, validate = true) {
|
|
1589
1606
|
if (options.populate === false) {
|
|
1590
1607
|
return [];
|
|
@@ -1625,13 +1642,13 @@ export class EntityManager {
|
|
|
1625
1642
|
options.populate = pruneToOneRelations(meta, this.buildFields(options.fields));
|
|
1626
1643
|
}
|
|
1627
1644
|
if (!options.populate) {
|
|
1628
|
-
const populate = this.entityLoader.normalizePopulate(entityName, [], options.strategy);
|
|
1645
|
+
const populate = this.entityLoader.normalizePopulate(entityName, [], options.strategy, true, options.exclude);
|
|
1629
1646
|
await this.autoJoinRefsForFilters(meta, { ...options, populate });
|
|
1630
1647
|
return populate;
|
|
1631
1648
|
}
|
|
1632
1649
|
if (typeof options.populate !== 'boolean') {
|
|
1633
1650
|
options.populate = Utils.asArray(options.populate).map(field => {
|
|
1634
|
-
/* v8 ignore next
|
|
1651
|
+
/* v8 ignore next */
|
|
1635
1652
|
if (typeof field === 'boolean' || field === PopulatePath.ALL) {
|
|
1636
1653
|
return [{ field: meta.primaryKeys[0], strategy: options.strategy, all: !!field }]; //
|
|
1637
1654
|
}
|
|
@@ -1641,13 +1658,13 @@ export class EntityManager {
|
|
|
1641
1658
|
options.flags.push(QueryFlag.INFER_POPULATE);
|
|
1642
1659
|
return [];
|
|
1643
1660
|
}
|
|
1644
|
-
if (
|
|
1661
|
+
if (typeof field === 'string') {
|
|
1645
1662
|
return [{ field, strategy: options.strategy }];
|
|
1646
1663
|
}
|
|
1647
1664
|
return [field];
|
|
1648
1665
|
}).flat();
|
|
1649
1666
|
}
|
|
1650
|
-
const populate = this.entityLoader.normalizePopulate(entityName, options.populate, options.strategy);
|
|
1667
|
+
const populate = this.entityLoader.normalizePopulate(entityName, options.populate, options.strategy, true, options.exclude);
|
|
1651
1668
|
const invalid = populate.find(({ field }) => !this.canPopulate(entityName, field));
|
|
1652
1669
|
if (validate && invalid) {
|
|
1653
1670
|
throw ValidationError.invalidPropertyName(entityName, invalid.field);
|
|
@@ -1678,7 +1695,7 @@ export class EntityManager {
|
|
|
1678
1695
|
return !inlineEmbedded && !prop.lazy && !helper(entity).__loadedProperties.has(prop.name);
|
|
1679
1696
|
});
|
|
1680
1697
|
}
|
|
1681
|
-
if (autoRefresh) {
|
|
1698
|
+
if (autoRefresh || options.filters) {
|
|
1682
1699
|
return true;
|
|
1683
1700
|
}
|
|
1684
1701
|
if (Array.isArray(options.populate)) {
|
|
@@ -1691,7 +1708,7 @@ export class EntityManager {
|
|
|
1691
1708
|
throw new ValidationError(`Cannot combine 'fields' and 'exclude' option.`);
|
|
1692
1709
|
}
|
|
1693
1710
|
options.schema ??= this._schema;
|
|
1694
|
-
options.logging = Utils.merge({ id: this.id }, this.loggerContext, options.loggerContext, options.logging);
|
|
1711
|
+
options.logging = options.loggerContext = Utils.merge({ id: this.id }, this.loggerContext, options.loggerContext, options.logging);
|
|
1695
1712
|
}
|
|
1696
1713
|
/**
|
|
1697
1714
|
* @internal
|
|
@@ -1702,7 +1719,7 @@ export class EntityManager {
|
|
|
1702
1719
|
for (const k of ['ctx', 'strategy', 'flushMode', 'logging', 'loggerContext']) {
|
|
1703
1720
|
delete opts[k];
|
|
1704
1721
|
}
|
|
1705
|
-
return [entityName, method, opts, where];
|
|
1722
|
+
return [Utils.className(entityName), method, opts, where];
|
|
1706
1723
|
}
|
|
1707
1724
|
/**
|
|
1708
1725
|
* @internal
|
|
@@ -1719,21 +1736,17 @@ export class EntityManager {
|
|
|
1719
1736
|
return { key: cacheKey, data: cached };
|
|
1720
1737
|
}
|
|
1721
1738
|
let data;
|
|
1739
|
+
const createOptions = {
|
|
1740
|
+
merge: true,
|
|
1741
|
+
convertCustomTypes: false,
|
|
1742
|
+
refresh,
|
|
1743
|
+
recomputeSnapshot: true,
|
|
1744
|
+
};
|
|
1722
1745
|
if (Array.isArray(cached) && merge) {
|
|
1723
|
-
data = cached.map(item => em.entityFactory.create(entityName, item,
|
|
1724
|
-
merge: true,
|
|
1725
|
-
convertCustomTypes: true,
|
|
1726
|
-
refresh,
|
|
1727
|
-
recomputeSnapshot: true,
|
|
1728
|
-
}));
|
|
1746
|
+
data = cached.map(item => em.entityFactory.create(entityName, item, createOptions));
|
|
1729
1747
|
}
|
|
1730
1748
|
else if (Utils.isObject(cached) && merge) {
|
|
1731
|
-
data = em.entityFactory.create(entityName, cached,
|
|
1732
|
-
merge: true,
|
|
1733
|
-
convertCustomTypes: true,
|
|
1734
|
-
refresh,
|
|
1735
|
-
recomputeSnapshot: true,
|
|
1736
|
-
});
|
|
1749
|
+
data = em.entityFactory.create(entityName, cached, createOptions);
|
|
1737
1750
|
}
|
|
1738
1751
|
else {
|
|
1739
1752
|
data = cached;
|
|
@@ -1748,7 +1761,7 @@ export class EntityManager {
|
|
|
1748
1761
|
config ??= this.config.get('resultCache').global;
|
|
1749
1762
|
if (config) {
|
|
1750
1763
|
const em = this.getContext();
|
|
1751
|
-
const expiration = Array.isArray(config) ? config[1] : (
|
|
1764
|
+
const expiration = Array.isArray(config) ? config[1] : (typeof config === 'number' ? config : undefined);
|
|
1752
1765
|
await em.resultCache.set(key.key, data instanceof Function ? data() : data, '', expiration);
|
|
1753
1766
|
}
|
|
1754
1767
|
}
|
|
@@ -1781,6 +1794,19 @@ export class EntityManager {
|
|
|
1781
1794
|
set schema(schema) {
|
|
1782
1795
|
this.getContext(false)._schema = schema ?? undefined;
|
|
1783
1796
|
}
|
|
1797
|
+
/** @internal */
|
|
1798
|
+
async getDataLoader(type) {
|
|
1799
|
+
const em = this.getContext();
|
|
1800
|
+
if (em.loaders[type]) {
|
|
1801
|
+
return em.loaders[type];
|
|
1802
|
+
}
|
|
1803
|
+
const DataLoader = await DataloaderUtils.getDataLoader();
|
|
1804
|
+
switch (type) {
|
|
1805
|
+
case 'ref': return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getRefBatchLoadFn(em)));
|
|
1806
|
+
case '1:m': return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getColBatchLoadFn(em)));
|
|
1807
|
+
case 'm:n': return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getManyToManyColBatchLoadFn(em)));
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1784
1810
|
/**
|
|
1785
1811
|
* Returns the ID of this EntityManager. Respects the context, so global EM will give you the contextual ID
|
|
1786
1812
|
* if executed inside request context handler.
|
|
@@ -1789,7 +1815,7 @@ export class EntityManager {
|
|
|
1789
1815
|
return this.getContext(false)._id;
|
|
1790
1816
|
}
|
|
1791
1817
|
/** @ignore */
|
|
1792
|
-
[inspect.custom]() {
|
|
1818
|
+
[Symbol.for('nodejs.util.inspect.custom')]() {
|
|
1793
1819
|
return `[EntityManager<${this.id}>]`;
|
|
1794
1820
|
}
|
|
1795
1821
|
}
|