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