@mikro-orm/core 7.0.0-dev.32 → 7.0.0-dev.321
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/EntityManager.d.ts +71 -63
- package/EntityManager.js +365 -283
- package/MikroORM.d.ts +44 -35
- package/MikroORM.js +109 -142
- package/README.md +7 -4
- package/cache/FileCacheAdapter.d.ts +1 -2
- package/cache/FileCacheAdapter.js +19 -14
- package/cache/GeneratedCacheAdapter.d.ts +0 -1
- package/cache/GeneratedCacheAdapter.js +0 -2
- package/cache/index.d.ts +1 -2
- package/cache/index.js +0 -2
- package/connections/Connection.d.ts +12 -5
- package/connections/Connection.js +37 -15
- package/drivers/DatabaseDriver.d.ts +25 -18
- package/drivers/DatabaseDriver.js +144 -45
- package/drivers/IDatabaseDriver.d.ts +118 -23
- package/entity/BaseEntity.d.ts +63 -4
- package/entity/BaseEntity.js +0 -3
- package/entity/Collection.d.ts +101 -29
- package/entity/Collection.js +473 -115
- package/entity/EntityAssigner.js +37 -25
- package/entity/EntityFactory.d.ts +7 -1
- package/entity/EntityFactory.js +116 -64
- package/entity/EntityHelper.d.ts +2 -2
- package/entity/EntityHelper.js +69 -27
- package/entity/EntityLoader.d.ts +11 -10
- package/entity/EntityLoader.js +264 -102
- package/entity/EntityRepository.d.ts +28 -8
- package/entity/EntityRepository.js +8 -2
- package/entity/PolymorphicRef.d.ts +12 -0
- package/entity/PolymorphicRef.js +18 -0
- package/entity/Reference.d.ts +2 -6
- package/entity/Reference.js +52 -19
- package/entity/WrappedEntity.d.ts +3 -8
- package/entity/WrappedEntity.js +6 -7
- package/entity/defineEntity.d.ts +525 -311
- package/entity/defineEntity.js +134 -290
- package/entity/index.d.ts +2 -2
- package/entity/index.js +2 -2
- package/entity/utils.d.ts +6 -1
- package/entity/utils.js +46 -11
- package/entity/validators.d.ts +11 -0
- package/entity/validators.js +66 -0
- package/enums.d.ts +8 -6
- package/enums.js +13 -17
- package/errors.d.ts +20 -10
- package/errors.js +63 -31
- package/events/EventManager.d.ts +2 -1
- package/events/EventManager.js +24 -13
- package/events/index.d.ts +1 -1
- package/events/index.js +0 -1
- package/exceptions.js +9 -2
- package/hydration/Hydrator.js +1 -2
- package/hydration/ObjectHydrator.d.ts +4 -4
- package/hydration/ObjectHydrator.js +105 -46
- package/index.d.ts +2 -2
- package/index.js +1 -2
- package/logging/DefaultLogger.d.ts +1 -1
- package/logging/DefaultLogger.js +3 -4
- package/logging/SimpleLogger.d.ts +1 -1
- package/logging/colors.d.ts +1 -1
- package/logging/colors.js +5 -7
- package/logging/index.d.ts +2 -1
- package/logging/index.js +1 -1
- package/logging/inspect.d.ts +2 -0
- package/logging/inspect.js +11 -0
- package/metadata/EntitySchema.d.ts +47 -23
- package/metadata/EntitySchema.js +103 -34
- package/metadata/MetadataDiscovery.d.ts +64 -9
- package/metadata/MetadataDiscovery.js +867 -354
- package/metadata/MetadataProvider.d.ts +11 -2
- package/metadata/MetadataProvider.js +71 -2
- package/metadata/MetadataStorage.d.ts +13 -11
- package/metadata/MetadataStorage.js +72 -41
- package/metadata/MetadataValidator.d.ts +32 -9
- package/metadata/MetadataValidator.js +214 -44
- package/metadata/discover-entities.d.ts +5 -0
- package/metadata/discover-entities.js +40 -0
- package/metadata/index.d.ts +1 -1
- package/metadata/index.js +0 -1
- package/metadata/types.d.ts +577 -0
- package/metadata/types.js +1 -0
- package/naming-strategy/AbstractNamingStrategy.d.ts +16 -4
- package/naming-strategy/AbstractNamingStrategy.js +26 -5
- package/naming-strategy/EntityCaseNamingStrategy.d.ts +3 -3
- package/naming-strategy/EntityCaseNamingStrategy.js +6 -5
- package/naming-strategy/MongoNamingStrategy.d.ts +3 -3
- package/naming-strategy/MongoNamingStrategy.js +6 -6
- package/naming-strategy/NamingStrategy.d.ts +28 -4
- package/naming-strategy/UnderscoreNamingStrategy.d.ts +3 -3
- package/naming-strategy/UnderscoreNamingStrategy.js +6 -6
- package/naming-strategy/index.d.ts +1 -1
- package/naming-strategy/index.js +0 -1
- package/not-supported.d.ts +2 -0
- package/not-supported.js +8 -0
- package/package.json +47 -36
- package/platforms/ExceptionConverter.js +1 -1
- package/platforms/Platform.d.ts +33 -15
- package/platforms/Platform.js +125 -69
- package/serialization/EntitySerializer.d.ts +6 -3
- package/serialization/EntitySerializer.js +53 -29
- package/serialization/EntityTransformer.js +33 -21
- package/serialization/SerializationContext.d.ts +6 -6
- package/serialization/SerializationContext.js +4 -4
- package/types/ArrayType.d.ts +1 -1
- package/types/ArrayType.js +2 -3
- package/types/BigIntType.js +1 -1
- package/types/BlobType.d.ts +0 -1
- package/types/BlobType.js +0 -3
- package/types/BooleanType.d.ts +1 -0
- package/types/BooleanType.js +3 -0
- package/types/DecimalType.js +2 -2
- package/types/DoubleType.js +1 -1
- package/types/EnumArrayType.js +1 -2
- package/types/JsonType.d.ts +1 -1
- package/types/JsonType.js +7 -2
- package/types/TinyIntType.js +1 -1
- package/types/Type.d.ts +2 -4
- package/types/Type.js +3 -3
- package/types/Uint8ArrayType.d.ts +0 -1
- package/types/Uint8ArrayType.js +1 -4
- package/types/UuidType.d.ts +2 -0
- package/types/UuidType.js +14 -2
- package/types/index.d.ts +3 -2
- package/typings.d.ts +427 -170
- package/typings.js +100 -45
- package/unit-of-work/ChangeSet.d.ts +4 -6
- package/unit-of-work/ChangeSet.js +8 -9
- package/unit-of-work/ChangeSetComputer.d.ts +3 -8
- package/unit-of-work/ChangeSetComputer.js +49 -26
- package/unit-of-work/ChangeSetPersister.d.ts +13 -12
- package/unit-of-work/ChangeSetPersister.js +107 -44
- package/unit-of-work/CommitOrderCalculator.d.ts +12 -10
- package/unit-of-work/CommitOrderCalculator.js +17 -15
- package/unit-of-work/IdentityMap.d.ts +12 -0
- package/unit-of-work/IdentityMap.js +39 -1
- package/unit-of-work/UnitOfWork.d.ts +34 -4
- package/unit-of-work/UnitOfWork.js +294 -107
- package/utils/AbstractMigrator.d.ts +101 -0
- package/utils/AbstractMigrator.js +303 -0
- package/utils/AbstractSchemaGenerator.d.ts +5 -5
- package/utils/AbstractSchemaGenerator.js +30 -18
- package/utils/AsyncContext.d.ts +6 -0
- package/utils/AsyncContext.js +42 -0
- package/utils/Configuration.d.ts +796 -211
- package/utils/Configuration.js +160 -197
- package/utils/ConfigurationLoader.d.ts +1 -52
- package/utils/ConfigurationLoader.js +1 -330
- package/utils/Cursor.d.ts +0 -3
- package/utils/Cursor.js +29 -14
- package/utils/DataloaderUtils.d.ts +10 -5
- package/utils/DataloaderUtils.js +42 -22
- package/utils/EntityComparator.d.ts +16 -9
- package/utils/EntityComparator.js +202 -96
- package/utils/QueryHelper.d.ts +34 -7
- package/utils/QueryHelper.js +183 -72
- package/utils/RawQueryFragment.d.ts +28 -34
- package/utils/RawQueryFragment.js +37 -72
- package/utils/RequestContext.js +2 -2
- package/utils/TransactionContext.js +2 -2
- package/utils/TransactionManager.js +11 -7
- package/utils/Utils.d.ts +16 -127
- package/utils/Utils.js +106 -401
- package/utils/clone.js +13 -23
- package/utils/env-vars.d.ts +7 -0
- package/utils/env-vars.js +98 -0
- package/utils/fs-utils.d.ts +34 -0
- package/utils/fs-utils.js +193 -0
- package/utils/index.d.ts +1 -3
- package/utils/index.js +1 -3
- package/utils/upsert-utils.d.ts +9 -4
- package/utils/upsert-utils.js +51 -5
- package/decorators/Check.d.ts +0 -3
- package/decorators/Check.js +0 -13
- package/decorators/CreateRequestContext.d.ts +0 -3
- package/decorators/CreateRequestContext.js +0 -32
- package/decorators/Embeddable.d.ts +0 -8
- package/decorators/Embeddable.js +0 -11
- package/decorators/Embedded.d.ts +0 -12
- package/decorators/Embedded.js +0 -18
- package/decorators/Entity.d.ts +0 -33
- package/decorators/Entity.js +0 -12
- package/decorators/Enum.d.ts +0 -9
- package/decorators/Enum.js +0 -16
- package/decorators/Filter.d.ts +0 -2
- package/decorators/Filter.js +0 -8
- package/decorators/Formula.d.ts +0 -4
- package/decorators/Formula.js +0 -15
- package/decorators/Indexed.d.ts +0 -19
- package/decorators/Indexed.js +0 -20
- package/decorators/ManyToMany.d.ts +0 -42
- package/decorators/ManyToMany.js +0 -14
- package/decorators/ManyToOne.d.ts +0 -34
- package/decorators/ManyToOne.js +0 -14
- package/decorators/OneToMany.d.ts +0 -28
- package/decorators/OneToMany.js +0 -17
- package/decorators/OneToOne.d.ts +0 -28
- package/decorators/OneToOne.js +0 -7
- package/decorators/PrimaryKey.d.ts +0 -8
- package/decorators/PrimaryKey.js +0 -20
- package/decorators/Property.d.ts +0 -250
- package/decorators/Property.js +0 -32
- package/decorators/Transactional.d.ts +0 -14
- package/decorators/Transactional.js +0 -28
- package/decorators/hooks.d.ts +0 -16
- package/decorators/hooks.js +0 -47
- package/decorators/index.d.ts +0 -17
- package/decorators/index.js +0 -17
- package/entity/ArrayCollection.d.ts +0 -118
- package/entity/ArrayCollection.js +0 -407
- package/entity/EntityValidator.d.ts +0 -19
- package/entity/EntityValidator.js +0 -150
- package/metadata/ReflectMetadataProvider.d.ts +0 -8
- package/metadata/ReflectMetadataProvider.js +0 -44
- package/utils/resolveContextProvider.d.ts +0 -10
- package/utils/resolveContextProvider.js +0 -28
package/EntityManager.js
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import DataLoader from 'dataloader';
|
|
3
|
-
import { getOnConflictReturningFields } from './utils/upsert-utils.js';
|
|
1
|
+
import { getOnConflictReturningFields, getWhereCondition } from './utils/upsert-utils.js';
|
|
4
2
|
import { Utils } from './utils/Utils.js';
|
|
5
3
|
import { Cursor } from './utils/Cursor.js';
|
|
6
|
-
import { DataloaderUtils } from './utils/DataloaderUtils.js';
|
|
7
4
|
import { QueryHelper } from './utils/QueryHelper.js';
|
|
8
5
|
import { TransactionContext } from './utils/TransactionContext.js';
|
|
9
|
-
import { isRaw,
|
|
6
|
+
import { isRaw, Raw } from './utils/RawQueryFragment.js';
|
|
10
7
|
import { EntityFactory } from './entity/EntityFactory.js';
|
|
11
8
|
import { EntityAssigner } from './entity/EntityAssigner.js';
|
|
12
|
-
import {
|
|
9
|
+
import { validateEmptyWhere, validateParams, validatePrimaryKey, validateProperty } from './entity/validators.js';
|
|
13
10
|
import { EntityLoader } from './entity/EntityLoader.js';
|
|
14
11
|
import { Reference } from './entity/Reference.js';
|
|
15
12
|
import { helper } from './entity/wrap.js';
|
|
@@ -19,7 +16,7 @@ import { EventType, FlushMode, LoadStrategy, LockMode, PopulateHint, PopulatePat
|
|
|
19
16
|
import { EventManager } from './events/EventManager.js';
|
|
20
17
|
import { TransactionEventBroadcaster } from './events/TransactionEventBroadcaster.js';
|
|
21
18
|
import { OptimisticLockError, ValidationError } from './errors.js';
|
|
22
|
-
import { getLoadingStrategy } from './entity/utils.js';
|
|
19
|
+
import { applyPopulateHints, getLoadingStrategy } from './entity/utils.js';
|
|
23
20
|
import { TransactionManager } from './utils/TransactionManager.js';
|
|
24
21
|
/**
|
|
25
22
|
* The EntityManager is the central access point to ORM functionality. It is a facade to all different ORM subsystems
|
|
@@ -36,11 +33,8 @@ export class EntityManager {
|
|
|
36
33
|
_id = EntityManager.counter++;
|
|
37
34
|
global = false;
|
|
38
35
|
name;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
colLoaderMtoN = new DataLoader(DataloaderUtils.getManyToManyColBatchLoadFn(this));
|
|
42
|
-
validator;
|
|
43
|
-
repositoryMap = {};
|
|
36
|
+
loaders = {};
|
|
37
|
+
repositoryMap = new Map();
|
|
44
38
|
entityLoader;
|
|
45
39
|
comparator;
|
|
46
40
|
entityFactory;
|
|
@@ -64,7 +58,6 @@ export class EntityManager {
|
|
|
64
58
|
this.eventManager = eventManager;
|
|
65
59
|
this.entityLoader = new EntityLoader(this);
|
|
66
60
|
this.name = this.config.get('contextName');
|
|
67
|
-
this.validator = new EntityValidator(this.config.get('strict'));
|
|
68
61
|
this.comparator = this.config.getComparator(this.metadata);
|
|
69
62
|
this.resultCache = this.config.getResultCacheAdapter();
|
|
70
63
|
this.disableTransactions = this.config.get('disableTransactions');
|
|
@@ -94,13 +87,12 @@ export class EntityManager {
|
|
|
94
87
|
* Gets repository for given entity. You can pass either string name or entity class reference.
|
|
95
88
|
*/
|
|
96
89
|
getRepository(entityName) {
|
|
97
|
-
|
|
98
|
-
if (!this.repositoryMap
|
|
99
|
-
const meta = this.metadata.get(entityName);
|
|
90
|
+
const meta = this.metadata.get(entityName);
|
|
91
|
+
if (!this.repositoryMap.has(meta)) {
|
|
100
92
|
const RepositoryClass = this.config.getRepositoryClass(meta.repository);
|
|
101
|
-
this.repositoryMap
|
|
93
|
+
this.repositoryMap.set(meta, new RepositoryClass(this, entityName));
|
|
102
94
|
}
|
|
103
|
-
return this.repositoryMap
|
|
95
|
+
return this.repositoryMap.get(meta);
|
|
104
96
|
}
|
|
105
97
|
/**
|
|
106
98
|
* Shortcut for `em.getRepository()`.
|
|
@@ -108,12 +100,6 @@ export class EntityManager {
|
|
|
108
100
|
repo(entityName) {
|
|
109
101
|
return this.getRepository(entityName);
|
|
110
102
|
}
|
|
111
|
-
/**
|
|
112
|
-
* Gets EntityValidator instance
|
|
113
|
-
*/
|
|
114
|
-
getValidator() {
|
|
115
|
-
return this.validator;
|
|
116
|
-
}
|
|
117
103
|
/**
|
|
118
104
|
* Finds all entities matching your `where` query. You can pass additional options via the `options` parameter.
|
|
119
105
|
*/
|
|
@@ -128,11 +114,16 @@ export class EntityManager {
|
|
|
128
114
|
const em = this.getContext();
|
|
129
115
|
em.prepareOptions(options);
|
|
130
116
|
await em.tryFlush(entityName, options);
|
|
131
|
-
entityName = Utils.className(entityName);
|
|
132
117
|
where = await em.processWhere(entityName, where, options, 'read');
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
118
|
+
validateParams(where);
|
|
119
|
+
const meta = this.metadata.get(entityName);
|
|
120
|
+
if (meta.orderBy) {
|
|
121
|
+
options.orderBy = QueryHelper.mergeOrderBy(options.orderBy, meta.orderBy);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
options.orderBy ??= {};
|
|
125
|
+
}
|
|
126
|
+
options.populate = (await em.preparePopulate(entityName, options));
|
|
136
127
|
const populate = options.populate;
|
|
137
128
|
const cacheKey = em.cacheKey(entityName, options, 'em.find', where);
|
|
138
129
|
const cached = await em.tryCache(entityName, options.cache, cacheKey, options.refresh, true);
|
|
@@ -145,12 +136,12 @@ export class EntityManager {
|
|
|
145
136
|
});
|
|
146
137
|
return cached.data;
|
|
147
138
|
}
|
|
148
|
-
const meta = this.metadata.get(entityName);
|
|
149
139
|
options = { ...options };
|
|
150
140
|
// save the original hint value so we know it was infer/all
|
|
151
141
|
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
152
142
|
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
153
|
-
options.populateFilter = await this.getJoinedFilters(meta,
|
|
143
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
144
|
+
await em.processUnionWhere(entityName, options, 'read');
|
|
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.
|
|
@@ -254,23 +299,34 @@ export class EntityManager {
|
|
|
254
299
|
where = this.applyDiscriminatorCondition(entityName, where);
|
|
255
300
|
return where;
|
|
256
301
|
}
|
|
302
|
+
async processUnionWhere(entityName, options, type) {
|
|
303
|
+
if (options.unionWhere?.length) {
|
|
304
|
+
if (!this.driver.getPlatform().supportsUnionWhere()) {
|
|
305
|
+
throw new Error(`unionWhere is only supported on SQL drivers`);
|
|
306
|
+
}
|
|
307
|
+
options.unionWhere = (await Promise.all(options.unionWhere.map(branch => this.processWhere(entityName, branch, options, type))));
|
|
308
|
+
}
|
|
309
|
+
}
|
|
257
310
|
// this method only handles the problem for mongo driver, SQL drivers have their implementation inside QueryBuilder
|
|
258
311
|
applyDiscriminatorCondition(entityName, where) {
|
|
259
312
|
const meta = this.metadata.find(entityName);
|
|
260
|
-
if (!meta?.discriminatorValue) {
|
|
313
|
+
if (meta?.root.inheritanceType !== 'sti' || !meta?.discriminatorValue) {
|
|
261
314
|
return where;
|
|
262
315
|
}
|
|
263
|
-
const types = Object.values(meta.root.discriminatorMap).map(cls => this.metadata.
|
|
316
|
+
const types = Object.values(meta.root.discriminatorMap).map(cls => this.metadata.get(cls));
|
|
264
317
|
const children = [];
|
|
265
318
|
const lookUpChildren = (ret, type) => {
|
|
266
319
|
const children = types.filter(meta2 => meta2.extends === type);
|
|
267
|
-
children.forEach(m => lookUpChildren(ret, m.
|
|
320
|
+
children.forEach(m => lookUpChildren(ret, m.class));
|
|
268
321
|
ret.push(...children.filter(c => c.discriminatorValue));
|
|
269
322
|
return children;
|
|
270
323
|
};
|
|
271
|
-
lookUpChildren(children, meta.
|
|
324
|
+
lookUpChildren(children, meta.class);
|
|
272
325
|
/* v8 ignore next */
|
|
273
|
-
where[meta.root.discriminatorColumn] =
|
|
326
|
+
where[meta.root.discriminatorColumn] =
|
|
327
|
+
children.length > 0
|
|
328
|
+
? { $in: [meta.discriminatorValue, ...children.map(c => c.discriminatorValue)] }
|
|
329
|
+
: meta.discriminatorValue;
|
|
274
330
|
return where;
|
|
275
331
|
}
|
|
276
332
|
createPopulateWhere(cond, options) {
|
|
@@ -284,60 +340,78 @@ export class EntityManager {
|
|
|
284
340
|
}
|
|
285
341
|
return ret;
|
|
286
342
|
}
|
|
287
|
-
async getJoinedFilters(meta,
|
|
343
|
+
async getJoinedFilters(meta, options) {
|
|
344
|
+
// If user provided populateFilter, merge it with computed filters
|
|
345
|
+
const userFilter = options.populateFilter;
|
|
346
|
+
if (!this.config.get('filtersOnRelations') || !options.populate) {
|
|
347
|
+
return userFilter;
|
|
348
|
+
}
|
|
288
349
|
const ret = {};
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
350
|
+
for (const hint of options.populate) {
|
|
351
|
+
const field = hint.field.split(':')[0];
|
|
352
|
+
const prop = meta.properties[field];
|
|
353
|
+
const strategy = getLoadingStrategy(prop.strategy || hint.strategy || options.strategy || this.config.get('loadStrategy'), prop.kind);
|
|
354
|
+
const joined = strategy === LoadStrategy.JOINED && prop.kind !== ReferenceKind.SCALAR;
|
|
355
|
+
if (!joined && !hint.filter) {
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
const filters = QueryHelper.mergePropertyFilters(prop.filters, options.filters);
|
|
359
|
+
const where = await this.applyFilters(prop.targetMeta.class, {}, filters, 'read', {
|
|
360
|
+
...options,
|
|
361
|
+
populate: hint.children,
|
|
362
|
+
});
|
|
363
|
+
const where2 = await this.getJoinedFilters(prop.targetMeta, {
|
|
364
|
+
...options,
|
|
365
|
+
filters,
|
|
366
|
+
populate: hint.children,
|
|
367
|
+
populateWhere: PopulateHint.ALL,
|
|
368
|
+
});
|
|
369
|
+
if (Utils.hasObjectKeys(where)) {
|
|
370
|
+
ret[field] = ret[field] ? { $and: [where, ret[field]] } : where;
|
|
371
|
+
}
|
|
372
|
+
if (where2 && Utils.hasObjectKeys(where2)) {
|
|
373
|
+
if (ret[field]) {
|
|
374
|
+
Utils.merge(ret[field], where2);
|
|
302
375
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
Utils.merge(ret[field], where2);
|
|
306
|
-
}
|
|
307
|
-
else {
|
|
308
|
-
ret[field] = where2;
|
|
309
|
-
}
|
|
376
|
+
else {
|
|
377
|
+
ret[field] = where2;
|
|
310
378
|
}
|
|
311
379
|
}
|
|
312
380
|
}
|
|
313
|
-
|
|
381
|
+
// Merge user-provided populateFilter with computed filters
|
|
382
|
+
if (userFilter) {
|
|
383
|
+
Utils.merge(ret, userFilter);
|
|
384
|
+
}
|
|
385
|
+
return Utils.hasObjectKeys(ret) ? ret : undefined;
|
|
314
386
|
}
|
|
315
387
|
/**
|
|
316
388
|
* 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
389
|
*/
|
|
318
|
-
async autoJoinRefsForFilters(meta, options) {
|
|
319
|
-
if (!meta || !this.config.get('autoJoinRefsForFilters')) {
|
|
390
|
+
async autoJoinRefsForFilters(meta, options, parent) {
|
|
391
|
+
if (!meta || !this.config.get('autoJoinRefsForFilters') || options.filters === false) {
|
|
320
392
|
return;
|
|
321
393
|
}
|
|
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
394
|
const ret = options.populate;
|
|
327
|
-
for (const prop of
|
|
328
|
-
|
|
395
|
+
for (const prop of meta.relations) {
|
|
396
|
+
if (prop.object ||
|
|
397
|
+
![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) ||
|
|
398
|
+
!((options.fields?.length ?? 0) === 0 ||
|
|
399
|
+
options.fields?.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`))) ||
|
|
400
|
+
(parent?.class === prop.targetMeta.root.class && parent.propName === prop.inversedBy)) {
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
options = { ...options, filters: QueryHelper.mergePropertyFilters(prop.filters, options.filters) };
|
|
404
|
+
const cond = await this.applyFilters(prop.targetMeta.class, {}, options.filters, 'read', options);
|
|
329
405
|
if (!Utils.isEmpty(cond)) {
|
|
330
406
|
const populated = options.populate.filter(({ field }) => field.split(':')[0] === prop.name);
|
|
331
407
|
let found = false;
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
found = true;
|
|
340
|
-
}
|
|
408
|
+
for (const hint of populated) {
|
|
409
|
+
if (!hint.all) {
|
|
410
|
+
hint.filter = true;
|
|
411
|
+
}
|
|
412
|
+
const strategy = getLoadingStrategy(prop.strategy || hint.strategy || options.strategy || this.config.get('loadStrategy'), prop.kind);
|
|
413
|
+
if (hint.field === `${prop.name}:ref` || (hint.filter && strategy === LoadStrategy.JOINED)) {
|
|
414
|
+
found = true;
|
|
341
415
|
}
|
|
342
416
|
}
|
|
343
417
|
if (!found) {
|
|
@@ -345,22 +419,25 @@ export class EntityManager {
|
|
|
345
419
|
}
|
|
346
420
|
}
|
|
347
421
|
}
|
|
422
|
+
for (const hint of ret) {
|
|
423
|
+
const [field, ref] = hint.field.split(':');
|
|
424
|
+
const prop = meta?.properties[field];
|
|
425
|
+
if (prop && !ref) {
|
|
426
|
+
hint.children ??= [];
|
|
427
|
+
await this.autoJoinRefsForFilters(prop.targetMeta, { ...options, populate: hint.children }, { class: meta.root.class, propName: prop.name });
|
|
428
|
+
}
|
|
429
|
+
}
|
|
348
430
|
}
|
|
349
431
|
/**
|
|
350
432
|
* @internal
|
|
351
433
|
*/
|
|
352
434
|
async applyFilters(entityName, where, options, type, findOptions) {
|
|
353
|
-
const meta = this.metadata.
|
|
435
|
+
const meta = this.metadata.get(entityName);
|
|
354
436
|
const filters = [];
|
|
355
437
|
const ret = [];
|
|
356
|
-
if (!meta) {
|
|
357
|
-
return where;
|
|
358
|
-
}
|
|
359
438
|
const active = new Set();
|
|
360
439
|
const push = (source) => {
|
|
361
|
-
const activeFilters = QueryHelper
|
|
362
|
-
.getActiveFilters(entityName, options, source)
|
|
363
|
-
.filter(f => !active.has(f.name));
|
|
440
|
+
const activeFilters = QueryHelper.getActiveFilters(meta, options, source).filter(f => !active.has(f.name));
|
|
364
441
|
filters.push(...activeFilters);
|
|
365
442
|
activeFilters.forEach(f => active.add(f.name));
|
|
366
443
|
};
|
|
@@ -374,24 +451,29 @@ export class EntityManager {
|
|
|
374
451
|
let cond;
|
|
375
452
|
if (filter.cond instanceof Function) {
|
|
376
453
|
// @ts-ignore
|
|
377
|
-
|
|
454
|
+
// oxfmt-ignore
|
|
455
|
+
const args = Utils.isPlainObject(options?.[filter.name]) ? options[filter.name] : this.getContext().filterParams[filter.name];
|
|
378
456
|
if (!args && filter.cond.length > 0 && filter.args !== false) {
|
|
379
457
|
throw new Error(`No arguments provided for filter '${filter.name}'`);
|
|
380
458
|
}
|
|
381
|
-
cond = await filter.cond(args, type, this, findOptions, entityName);
|
|
459
|
+
cond = await filter.cond(args, type, this, findOptions, Utils.className(entityName));
|
|
382
460
|
}
|
|
383
461
|
else {
|
|
384
462
|
cond = filter.cond;
|
|
385
463
|
}
|
|
386
|
-
|
|
464
|
+
cond = QueryHelper.processWhere({
|
|
387
465
|
where: cond,
|
|
388
466
|
entityName,
|
|
389
467
|
metadata: this.metadata,
|
|
390
468
|
platform: this.driver.getPlatform(),
|
|
391
469
|
aliased: type === 'read',
|
|
392
|
-
})
|
|
470
|
+
});
|
|
471
|
+
if (filter.strict) {
|
|
472
|
+
Object.defineProperty(cond, '__strict', { value: filter.strict, enumerable: false });
|
|
473
|
+
}
|
|
474
|
+
ret.push(cond);
|
|
393
475
|
}
|
|
394
|
-
const conds = [...ret, where].filter(c => Utils.hasObjectKeys(c));
|
|
476
|
+
const conds = [...ret, where].filter(c => Utils.hasObjectKeys(c) || Raw.hasObjectFragments(c));
|
|
395
477
|
return conds.length > 1 ? { $and: conds } : conds[0];
|
|
396
478
|
}
|
|
397
479
|
/**
|
|
@@ -402,12 +484,10 @@ export class EntityManager {
|
|
|
402
484
|
const em = this.getContext(false);
|
|
403
485
|
await em.tryFlush(entityName, options);
|
|
404
486
|
options.flushMode = 'commit'; // do not try to auto flush again
|
|
405
|
-
return
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
]);
|
|
410
|
-
});
|
|
487
|
+
return Promise.all([
|
|
488
|
+
em.find(entityName, where, options),
|
|
489
|
+
em.count(entityName, where, options),
|
|
490
|
+
]);
|
|
411
491
|
}
|
|
412
492
|
/**
|
|
413
493
|
* Calls `em.find()` and `em.count()` with the same arguments (where applicable) and returns the results as {@apilink Cursor} object.
|
|
@@ -423,21 +503,21 @@ export class EntityManager {
|
|
|
423
503
|
* - POJO/entity instance
|
|
424
504
|
*
|
|
425
505
|
* ```ts
|
|
426
|
-
* const currentCursor = await em.findByCursor(User, {
|
|
506
|
+
* const currentCursor = await em.findByCursor(User, {
|
|
427
507
|
* first: 10,
|
|
428
508
|
* after: previousCursor, // cursor instance
|
|
429
509
|
* orderBy: { id: 'desc' },
|
|
430
510
|
* });
|
|
431
511
|
*
|
|
432
512
|
* // to fetch next page
|
|
433
|
-
* const nextCursor = await em.findByCursor(User, {
|
|
513
|
+
* const nextCursor = await em.findByCursor(User, {
|
|
434
514
|
* first: 10,
|
|
435
515
|
* after: currentCursor.endCursor, // opaque string
|
|
436
516
|
* orderBy: { id: 'desc' },
|
|
437
517
|
* });
|
|
438
518
|
*
|
|
439
519
|
* // to fetch next page
|
|
440
|
-
* const nextCursor2 = await em.findByCursor(User, {
|
|
520
|
+
* const nextCursor2 = await em.findByCursor(User, {
|
|
441
521
|
* first: 10,
|
|
442
522
|
* after: { id: lastSeenId }, // entity-like POJO
|
|
443
523
|
* orderBy: { id: 'desc' },
|
|
@@ -465,16 +545,16 @@ export class EntityManager {
|
|
|
465
545
|
* }
|
|
466
546
|
* ```
|
|
467
547
|
*/
|
|
468
|
-
async findByCursor(entityName,
|
|
548
|
+
async findByCursor(entityName, options) {
|
|
469
549
|
const em = this.getContext(false);
|
|
470
|
-
entityName = Utils.className(entityName);
|
|
471
550
|
options.overfetch ??= true;
|
|
472
|
-
|
|
551
|
+
options.where ??= {};
|
|
552
|
+
if (Utils.isEmpty(options.orderBy) && !Raw.hasObjectFragments(options.orderBy)) {
|
|
473
553
|
throw new Error('Explicit `orderBy` option required');
|
|
474
554
|
}
|
|
475
555
|
const [entities, count] = options.includeCount !== false
|
|
476
|
-
? await em.findAndCount(entityName, where, options)
|
|
477
|
-
: [await em.find(entityName, where, options)];
|
|
556
|
+
? await em.findAndCount(entityName, options.where, options)
|
|
557
|
+
: [await em.find(entityName, options.where, options)];
|
|
478
558
|
return new Cursor(entities, count, options, this.metadata.get(entityName));
|
|
479
559
|
}
|
|
480
560
|
/**
|
|
@@ -486,9 +566,9 @@ export class EntityManager {
|
|
|
486
566
|
const ret = await this.refresh(entity, options);
|
|
487
567
|
if (!ret) {
|
|
488
568
|
options.failHandler ??= this.config.get('findOneOrFailHandler');
|
|
489
|
-
const
|
|
490
|
-
const where =
|
|
491
|
-
throw options.failHandler(
|
|
569
|
+
const wrapped = helper(entity);
|
|
570
|
+
const where = wrapped.getPrimaryKey();
|
|
571
|
+
throw options.failHandler(wrapped.__meta.className, where);
|
|
492
572
|
}
|
|
493
573
|
return ret;
|
|
494
574
|
}
|
|
@@ -499,9 +579,9 @@ export class EntityManager {
|
|
|
499
579
|
*/
|
|
500
580
|
async refresh(entity, options = {}) {
|
|
501
581
|
const fork = this.fork({ keepTransactionContext: true });
|
|
502
|
-
const
|
|
503
|
-
const reloaded = await fork.findOne(
|
|
504
|
-
schema:
|
|
582
|
+
const wrapped = helper(entity);
|
|
583
|
+
const reloaded = await fork.findOne(wrapped.__meta.class, entity, {
|
|
584
|
+
schema: wrapped.__schema,
|
|
505
585
|
...options,
|
|
506
586
|
flushMode: FlushMode.COMMIT,
|
|
507
587
|
});
|
|
@@ -512,16 +592,22 @@ export class EntityManager {
|
|
|
512
592
|
}
|
|
513
593
|
let found = false;
|
|
514
594
|
for (const e of fork.unitOfWork.getIdentityMap()) {
|
|
515
|
-
const ref = em.getReference(e.constructor
|
|
516
|
-
const data = helper(e).serialize({ ignoreSerializers: true, includeHidden: true });
|
|
517
|
-
em.config
|
|
518
|
-
|
|
595
|
+
const ref = em.getReference(e.constructor, helper(e).getPrimaryKey());
|
|
596
|
+
const data = helper(e).serialize({ ignoreSerializers: true, includeHidden: true, convertCustomTypes: false });
|
|
597
|
+
em.config
|
|
598
|
+
.getHydrator(this.metadata)
|
|
599
|
+
.hydrate(ref, helper(ref).__meta, data, em.entityFactory, 'full', false, false);
|
|
600
|
+
Utils.merge(helper(ref).__originalEntityData, this.comparator.prepareEntity(e));
|
|
519
601
|
found ||= ref === entity;
|
|
520
602
|
}
|
|
521
603
|
if (!found) {
|
|
522
|
-
const data = helper(reloaded).serialize({
|
|
523
|
-
|
|
524
|
-
|
|
604
|
+
const data = helper(reloaded).serialize({
|
|
605
|
+
ignoreSerializers: true,
|
|
606
|
+
includeHidden: true,
|
|
607
|
+
convertCustomTypes: true,
|
|
608
|
+
});
|
|
609
|
+
em.config.getHydrator(this.metadata).hydrate(entity, wrapped.__meta, data, em.entityFactory, 'full', false, true);
|
|
610
|
+
Utils.merge(wrapped.__originalEntityData, this.comparator.prepareEntity(reloaded));
|
|
525
611
|
}
|
|
526
612
|
return entity;
|
|
527
613
|
}
|
|
@@ -537,7 +623,6 @@ export class EntityManager {
|
|
|
537
623
|
return ret;
|
|
538
624
|
}
|
|
539
625
|
const em = this.getContext();
|
|
540
|
-
entityName = Utils.className(entityName);
|
|
541
626
|
em.prepareOptions(options);
|
|
542
627
|
let entity = em.unitOfWork.tryGetById(entityName, where, options.schema);
|
|
543
628
|
// query for a not managed entity which is already in the identity map as it
|
|
@@ -549,14 +634,14 @@ export class EntityManager {
|
|
|
549
634
|
await em.tryFlush(entityName, options);
|
|
550
635
|
const meta = em.metadata.get(entityName);
|
|
551
636
|
where = await em.processWhere(entityName, where, options, 'read');
|
|
552
|
-
|
|
637
|
+
validateEmptyWhere(where);
|
|
553
638
|
em.checkLockRequirements(options.lockMode, meta);
|
|
554
|
-
const isOptimisticLocking =
|
|
639
|
+
const isOptimisticLocking = options.lockMode == null || options.lockMode === LockMode.OPTIMISTIC;
|
|
555
640
|
if (entity && !em.shouldRefresh(meta, entity, options) && isOptimisticLocking) {
|
|
556
641
|
return em.lockAndPopulate(meta, entity, where, options);
|
|
557
642
|
}
|
|
558
|
-
|
|
559
|
-
options.populate = await em.preparePopulate(entityName, options);
|
|
643
|
+
validateParams(where);
|
|
644
|
+
options.populate = (await em.preparePopulate(entityName, options));
|
|
560
645
|
const cacheKey = em.cacheKey(entityName, options, 'em.findOne', where);
|
|
561
646
|
const cached = await em.tryCache(entityName, options.cache, cacheKey, options.refresh, true);
|
|
562
647
|
if (cached?.data !== undefined) {
|
|
@@ -574,7 +659,8 @@ export class EntityManager {
|
|
|
574
659
|
// save the original hint value so we know it was infer/all
|
|
575
660
|
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
576
661
|
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
577
|
-
options.populateFilter = await this.getJoinedFilters(meta,
|
|
662
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
663
|
+
await em.processUnionWhere(entityName, options, 'read');
|
|
578
664
|
const data = await em.driver.findOne(entityName, where, {
|
|
579
665
|
ctx: em.transactionContext,
|
|
580
666
|
em,
|
|
@@ -615,10 +701,10 @@ export class EntityManager {
|
|
|
615
701
|
if (!entity || isStrictViolation) {
|
|
616
702
|
const key = options.strict ? 'findExactlyOneOrFailHandler' : 'findOneOrFailHandler';
|
|
617
703
|
options.failHandler ??= this.config.get(key);
|
|
618
|
-
|
|
704
|
+
const name = Utils.className(entityName);
|
|
619
705
|
/* v8 ignore next */
|
|
620
706
|
where = Utils.isEntity(where) ? helper(where).getPrimaryKey() : where;
|
|
621
|
-
throw options.failHandler(
|
|
707
|
+
throw options.failHandler(name, where);
|
|
622
708
|
}
|
|
623
709
|
return entity;
|
|
624
710
|
}
|
|
@@ -658,11 +744,11 @@ export class EntityManager {
|
|
|
658
744
|
let where;
|
|
659
745
|
let entity = null;
|
|
660
746
|
if (data === undefined) {
|
|
661
|
-
entityName = entityNameOrEntity.constructor
|
|
747
|
+
entityName = entityNameOrEntity.constructor;
|
|
662
748
|
data = entityNameOrEntity;
|
|
663
749
|
}
|
|
664
750
|
else {
|
|
665
|
-
entityName =
|
|
751
|
+
entityName = entityNameOrEntity;
|
|
666
752
|
}
|
|
667
753
|
const meta = this.metadata.get(entityName);
|
|
668
754
|
const convertCustomTypes = !Utils.isEntity(data);
|
|
@@ -685,26 +771,9 @@ export class EntityManager {
|
|
|
685
771
|
}
|
|
686
772
|
}
|
|
687
773
|
}
|
|
688
|
-
|
|
689
|
-
const propIndex = !isRaw(unique) && unique.findIndex(p => data[p] != null);
|
|
690
|
-
if (options.onConflictFields || where == null) {
|
|
691
|
-
if (propIndex !== false && propIndex >= 0) {
|
|
692
|
-
where = { [unique[propIndex]]: data[unique[propIndex]] };
|
|
693
|
-
}
|
|
694
|
-
else if (meta.uniques.length > 0) {
|
|
695
|
-
for (const u of meta.uniques) {
|
|
696
|
-
if (Utils.asArray(u.properties).every(p => data[p] != null)) {
|
|
697
|
-
where = Utils.asArray(u.properties).reduce((o, key) => {
|
|
698
|
-
o[key] = data[key];
|
|
699
|
-
return o;
|
|
700
|
-
}, {});
|
|
701
|
-
break;
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
}
|
|
774
|
+
where = getWhereCondition(meta, options.onConflictFields, data, where).where;
|
|
706
775
|
data = QueryHelper.processObjectParams(data);
|
|
707
|
-
|
|
776
|
+
validateParams(data, 'insert data');
|
|
708
777
|
if (em.eventManager.hasListeners(EventType.beforeUpsert, meta)) {
|
|
709
778
|
await em.eventManager.dispatchEvent(EventType.beforeUpsert, { entity: data, em, meta }, meta);
|
|
710
779
|
}
|
|
@@ -720,9 +789,12 @@ export class EntityManager {
|
|
|
720
789
|
initialized: true,
|
|
721
790
|
schema: options.schema,
|
|
722
791
|
});
|
|
723
|
-
const uniqueFields = options.onConflictFields ??
|
|
792
|
+
const uniqueFields = options.onConflictFields ??
|
|
793
|
+
(Utils.isPlainObject(where) ? Object.keys(where) : meta.primaryKeys);
|
|
724
794
|
const returning = getOnConflictReturningFields(meta, data, uniqueFields, options);
|
|
725
|
-
if (options.onConflictAction === 'ignore' ||
|
|
795
|
+
if (options.onConflictAction === 'ignore' ||
|
|
796
|
+
!helper(entity).hasPrimaryKey() ||
|
|
797
|
+
(returning.length > 0 && !(this.getPlatform().usesReturningStatement() && ret.row))) {
|
|
726
798
|
const where = {};
|
|
727
799
|
if (Array.isArray(uniqueFields)) {
|
|
728
800
|
for (const prop of uniqueFields) {
|
|
@@ -742,7 +814,7 @@ export class EntityManager {
|
|
|
742
814
|
where[meta.primaryKeys[0]] = ret.insertId;
|
|
743
815
|
}
|
|
744
816
|
}
|
|
745
|
-
const data2 = await this.driver.findOne(meta.
|
|
817
|
+
const data2 = await this.driver.findOne(meta.class, where, {
|
|
746
818
|
fields: returning,
|
|
747
819
|
ctx: em.transactionContext,
|
|
748
820
|
convertCustomTypes: true,
|
|
@@ -797,11 +869,11 @@ export class EntityManager {
|
|
|
797
869
|
let entityName;
|
|
798
870
|
let propIndex;
|
|
799
871
|
if (data === undefined) {
|
|
800
|
-
entityName = entityNameOrEntity[0].constructor
|
|
872
|
+
entityName = entityNameOrEntity[0].constructor;
|
|
801
873
|
data = entityNameOrEntity;
|
|
802
874
|
}
|
|
803
875
|
else {
|
|
804
|
-
entityName =
|
|
876
|
+
entityName = entityNameOrEntity;
|
|
805
877
|
}
|
|
806
878
|
const batchSize = options.batchSize ?? this.config.get('batchSize');
|
|
807
879
|
if (data.length > batchSize) {
|
|
@@ -845,32 +917,20 @@ export class EntityManager {
|
|
|
845
917
|
}
|
|
846
918
|
}
|
|
847
919
|
}
|
|
848
|
-
const unique = meta.props.filter(p => p.unique).map(p => p.name);
|
|
849
|
-
propIndex =
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
else if (meta.uniques.length > 0) {
|
|
855
|
-
for (const u of meta.uniques) {
|
|
856
|
-
if (Utils.asArray(u.properties).every(p => row[p] != null)) {
|
|
857
|
-
where = Utils.asArray(u.properties).reduce((o, key) => {
|
|
858
|
-
o[key] = row[key];
|
|
859
|
-
return o;
|
|
860
|
-
}, {});
|
|
861
|
-
break;
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
row = QueryHelper.processObjectParams(row);
|
|
920
|
+
const unique = options.onConflictFields ?? meta.props.filter(p => p.unique).map(p => p.name);
|
|
921
|
+
propIndex =
|
|
922
|
+
!isRaw(unique) &&
|
|
923
|
+
unique.findIndex(p => data[p] ?? data[p.substring(0, p.indexOf('.'))] != null);
|
|
924
|
+
const tmp = getWhereCondition(meta, options.onConflictFields, row, where);
|
|
925
|
+
propIndex = tmp.propIndex;
|
|
867
926
|
where = QueryHelper.processWhere({
|
|
868
|
-
where,
|
|
927
|
+
where: tmp.where,
|
|
869
928
|
entityName,
|
|
870
929
|
metadata: this.metadata,
|
|
871
930
|
platform: this.getPlatform(),
|
|
872
931
|
});
|
|
873
|
-
|
|
932
|
+
row = QueryHelper.processObjectParams(row);
|
|
933
|
+
validateParams(row, 'insert data');
|
|
874
934
|
allData.push(row);
|
|
875
935
|
allWhere.push(where);
|
|
876
936
|
}
|
|
@@ -893,12 +953,16 @@ export class EntityManager {
|
|
|
893
953
|
entitiesByData.clear();
|
|
894
954
|
const loadPK = new Map();
|
|
895
955
|
allData.forEach((row, i) => {
|
|
896
|
-
em.unitOfWork
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
956
|
+
em.unitOfWork
|
|
957
|
+
.getChangeSetPersister()
|
|
958
|
+
.mapReturnedValues(Utils.isEntity(data[i]) ? data[i] : null, Utils.isEntity(data[i]) ? {} : data[i], res.rows?.[i], meta, true);
|
|
959
|
+
const entity = Utils.isEntity(data[i])
|
|
960
|
+
? data[i]
|
|
961
|
+
: em.entityFactory.create(entityName, row, {
|
|
962
|
+
refresh: true,
|
|
963
|
+
initialized: true,
|
|
964
|
+
schema: options.schema,
|
|
965
|
+
});
|
|
902
966
|
if (!helper(entity).hasPrimaryKey()) {
|
|
903
967
|
loadPK.set(entity, allWhere[i]);
|
|
904
968
|
}
|
|
@@ -906,12 +970,15 @@ export class EntityManager {
|
|
|
906
970
|
entitiesByData.set(row, entity);
|
|
907
971
|
});
|
|
908
972
|
// skip if we got the PKs via returning statement (`rows`)
|
|
973
|
+
// oxfmt-ignore
|
|
909
974
|
const uniqueFields = options.onConflictFields ?? (Utils.isPlainObject(allWhere[0]) ? Object.keys(allWhere[0]).flatMap(key => Utils.splitPrimaryKeys(key)) : meta.primaryKeys);
|
|
910
975
|
const returning = getOnConflictReturningFields(meta, data[0], uniqueFields, options);
|
|
911
976
|
const reloadFields = returning.length > 0 && !(this.getPlatform().usesReturningStatement() && res.rows?.length);
|
|
912
977
|
if (options.onConflictAction === 'ignore' || (!res.rows?.length && loadPK.size > 0) || reloadFields) {
|
|
913
978
|
const unique = meta.hydrateProps.filter(p => !p.lazy).map(p => p.name);
|
|
914
|
-
const add = new Set(propIndex
|
|
979
|
+
const add = new Set(propIndex !== false && propIndex >= 0
|
|
980
|
+
? [unique[propIndex]]
|
|
981
|
+
: []);
|
|
915
982
|
for (const cond of loadPK.values()) {
|
|
916
983
|
Utils.keys(cond).forEach(key => add.add(key));
|
|
917
984
|
}
|
|
@@ -923,8 +990,10 @@ export class EntityManager {
|
|
|
923
990
|
where.$or[idx][prop] = item[prop];
|
|
924
991
|
});
|
|
925
992
|
});
|
|
926
|
-
const data2 = await this.driver.find(meta.
|
|
927
|
-
fields: returning
|
|
993
|
+
const data2 = await this.driver.find(meta.class, where, {
|
|
994
|
+
fields: returning
|
|
995
|
+
.concat(...add)
|
|
996
|
+
.concat(...(Array.isArray(uniqueFields) ? uniqueFields : [])),
|
|
928
997
|
ctx: em.transactionContext,
|
|
929
998
|
convertCustomTypes: true,
|
|
930
999
|
connectionType: 'write',
|
|
@@ -940,7 +1009,7 @@ export class EntityManager {
|
|
|
940
1009
|
});
|
|
941
1010
|
return this.comparator.matching(entityName, cond, tmp);
|
|
942
1011
|
});
|
|
943
|
-
/* v8 ignore next
|
|
1012
|
+
/* v8 ignore next */
|
|
944
1013
|
if (!row) {
|
|
945
1014
|
throw new Error(`Cannot find matching entity for condition ${JSON.stringify(cond)}`);
|
|
946
1015
|
}
|
|
@@ -963,7 +1032,7 @@ export class EntityManager {
|
|
|
963
1032
|
}, {});
|
|
964
1033
|
return this.comparator.matching(entityName, cond, pk);
|
|
965
1034
|
});
|
|
966
|
-
/* v8 ignore next
|
|
1035
|
+
/* v8 ignore next */
|
|
967
1036
|
if (!row) {
|
|
968
1037
|
throw new Error(`Cannot find matching entity for condition ${JSON.stringify(cond)}`);
|
|
969
1038
|
}
|
|
@@ -1076,11 +1145,11 @@ export class EntityManager {
|
|
|
1076
1145
|
em.prepareOptions(options);
|
|
1077
1146
|
let entityName;
|
|
1078
1147
|
if (data === undefined) {
|
|
1079
|
-
entityName = entityNameOrEntity.constructor
|
|
1148
|
+
entityName = entityNameOrEntity.constructor;
|
|
1080
1149
|
data = entityNameOrEntity;
|
|
1081
1150
|
}
|
|
1082
1151
|
else {
|
|
1083
|
-
entityName =
|
|
1152
|
+
entityName = entityNameOrEntity;
|
|
1084
1153
|
}
|
|
1085
1154
|
if (Utils.isEntity(data)) {
|
|
1086
1155
|
if (options.schema && helper(data).getSchema() == null) {
|
|
@@ -1099,8 +1168,11 @@ export class EntityManager {
|
|
|
1099
1168
|
return cs.getPrimaryKey();
|
|
1100
1169
|
}
|
|
1101
1170
|
data = QueryHelper.processObjectParams(data);
|
|
1102
|
-
|
|
1103
|
-
const res = await em.driver.nativeInsert(entityName, data, {
|
|
1171
|
+
validateParams(data, 'insert data');
|
|
1172
|
+
const res = await em.driver.nativeInsert(entityName, data, {
|
|
1173
|
+
ctx: em.transactionContext,
|
|
1174
|
+
...options,
|
|
1175
|
+
});
|
|
1104
1176
|
return res.insertId;
|
|
1105
1177
|
}
|
|
1106
1178
|
/**
|
|
@@ -1111,11 +1183,11 @@ export class EntityManager {
|
|
|
1111
1183
|
em.prepareOptions(options);
|
|
1112
1184
|
let entityName;
|
|
1113
1185
|
if (data === undefined) {
|
|
1114
|
-
entityName = entityNameOrEntities[0].constructor
|
|
1186
|
+
entityName = entityNameOrEntities[0].constructor;
|
|
1115
1187
|
data = entityNameOrEntities;
|
|
1116
1188
|
}
|
|
1117
1189
|
else {
|
|
1118
|
-
entityName =
|
|
1190
|
+
entityName = entityNameOrEntities;
|
|
1119
1191
|
}
|
|
1120
1192
|
if (data.length === 0) {
|
|
1121
1193
|
return [];
|
|
@@ -1139,8 +1211,11 @@ export class EntityManager {
|
|
|
1139
1211
|
return css.map(cs => cs.getPrimaryKey());
|
|
1140
1212
|
}
|
|
1141
1213
|
data = data.map(row => QueryHelper.processObjectParams(row));
|
|
1142
|
-
data.forEach(row =>
|
|
1143
|
-
const res = await em.driver.nativeInsertMany(entityName, data, {
|
|
1214
|
+
data.forEach(row => validateParams(row, 'insert data'));
|
|
1215
|
+
const res = await em.driver.nativeInsertMany(entityName, data, {
|
|
1216
|
+
ctx: em.transactionContext,
|
|
1217
|
+
...options,
|
|
1218
|
+
});
|
|
1144
1219
|
if (res.insertedIds) {
|
|
1145
1220
|
return res.insertedIds;
|
|
1146
1221
|
}
|
|
@@ -1152,12 +1227,16 @@ export class EntityManager {
|
|
|
1152
1227
|
async nativeUpdate(entityName, where, data, options = {}) {
|
|
1153
1228
|
const em = this.getContext(false);
|
|
1154
1229
|
em.prepareOptions(options);
|
|
1155
|
-
|
|
1230
|
+
await em.processUnionWhere(entityName, options, 'update');
|
|
1156
1231
|
data = QueryHelper.processObjectParams(data);
|
|
1157
1232
|
where = await em.processWhere(entityName, where, { ...options, convertCustomTypes: false }, 'update');
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
const res = await em.driver.nativeUpdate(entityName, where, data, {
|
|
1233
|
+
validateParams(data, 'update data');
|
|
1234
|
+
validateParams(where, 'update condition');
|
|
1235
|
+
const res = await em.driver.nativeUpdate(entityName, where, data, {
|
|
1236
|
+
ctx: em.transactionContext,
|
|
1237
|
+
em,
|
|
1238
|
+
...options,
|
|
1239
|
+
});
|
|
1161
1240
|
return res.affectedRows;
|
|
1162
1241
|
}
|
|
1163
1242
|
/**
|
|
@@ -1166,28 +1245,36 @@ export class EntityManager {
|
|
|
1166
1245
|
async nativeDelete(entityName, where, options = {}) {
|
|
1167
1246
|
const em = this.getContext(false);
|
|
1168
1247
|
em.prepareOptions(options);
|
|
1169
|
-
|
|
1170
|
-
where = await em.processWhere(entityName, where, options, 'delete');
|
|
1171
|
-
|
|
1172
|
-
const res = await em.driver.nativeDelete(entityName, where, {
|
|
1248
|
+
await em.processUnionWhere(entityName, options, 'delete');
|
|
1249
|
+
where = (await em.processWhere(entityName, where, options, 'delete'));
|
|
1250
|
+
validateParams(where, 'delete condition');
|
|
1251
|
+
const res = await em.driver.nativeDelete(entityName, where, {
|
|
1252
|
+
ctx: em.transactionContext,
|
|
1253
|
+
em,
|
|
1254
|
+
...options,
|
|
1255
|
+
});
|
|
1173
1256
|
return res.affectedRows;
|
|
1174
1257
|
}
|
|
1175
1258
|
/**
|
|
1176
1259
|
* Maps raw database result to an entity and merges it to this EntityManager.
|
|
1177
1260
|
*/
|
|
1178
1261
|
map(entityName, result, options = {}) {
|
|
1179
|
-
entityName = Utils.className(entityName);
|
|
1180
1262
|
const meta = this.metadata.get(entityName);
|
|
1181
1263
|
const data = this.driver.mapResult(result, meta);
|
|
1182
|
-
Object.keys(data)
|
|
1264
|
+
for (const k of Object.keys(data)) {
|
|
1183
1265
|
const prop = meta.properties[k];
|
|
1184
|
-
if (prop
|
|
1185
|
-
|
|
1266
|
+
if (prop?.kind === ReferenceKind.SCALAR &&
|
|
1267
|
+
SCALAR_TYPES.has(prop.runtimeType) &&
|
|
1268
|
+
!prop.customType &&
|
|
1269
|
+
(prop.setter || !prop.getter)) {
|
|
1270
|
+
validateProperty(prop, data[k], data);
|
|
1186
1271
|
}
|
|
1187
|
-
}
|
|
1272
|
+
}
|
|
1188
1273
|
return this.merge(entityName, data, {
|
|
1189
1274
|
convertCustomTypes: true,
|
|
1190
|
-
refresh: true,
|
|
1275
|
+
refresh: true,
|
|
1276
|
+
validate: false,
|
|
1277
|
+
...options,
|
|
1191
1278
|
});
|
|
1192
1279
|
}
|
|
1193
1280
|
/**
|
|
@@ -1196,32 +1283,21 @@ export class EntityManager {
|
|
|
1196
1283
|
*/
|
|
1197
1284
|
merge(entityName, data, options = {}) {
|
|
1198
1285
|
if (Utils.isEntity(entityName)) {
|
|
1199
|
-
return this.merge(entityName.constructor
|
|
1286
|
+
return this.merge(entityName.constructor, entityName, data);
|
|
1200
1287
|
}
|
|
1201
1288
|
const em = options.disableContextResolution ? this : this.getContext();
|
|
1202
1289
|
options.schema ??= em._schema;
|
|
1203
1290
|
options.validate ??= true;
|
|
1204
1291
|
options.cascade ??= true;
|
|
1205
|
-
|
|
1206
|
-
if (options.validate) {
|
|
1207
|
-
em.validator.validatePrimaryKey(data, em.metadata.get(entityName));
|
|
1208
|
-
}
|
|
1292
|
+
validatePrimaryKey(data, em.metadata.get(entityName));
|
|
1209
1293
|
let entity = em.unitOfWork.tryGetById(entityName, data, options.schema, false);
|
|
1210
1294
|
if (entity && helper(entity).__managed && helper(entity).__initialized && !options.refresh) {
|
|
1211
1295
|
return entity;
|
|
1212
1296
|
}
|
|
1213
|
-
const meta = em.metadata.find(entityName);
|
|
1214
|
-
const childMeta = em.metadata.getByDiscriminatorColumn(meta, data);
|
|
1215
1297
|
const dataIsEntity = Utils.isEntity(data);
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
return entity;
|
|
1220
|
-
}
|
|
1221
|
-
entity = dataIsEntity ? data : em.entityFactory.create(entityName, data, { merge: true, ...options });
|
|
1222
|
-
if (options.validate) {
|
|
1223
|
-
em.validator.validate(entity, data, childMeta ?? meta);
|
|
1224
|
-
}
|
|
1298
|
+
entity = dataIsEntity
|
|
1299
|
+
? data
|
|
1300
|
+
: em.entityFactory.create(entityName, data, { merge: true, ...options });
|
|
1225
1301
|
const visited = options.cascade ? undefined : new Set([entity]);
|
|
1226
1302
|
em.unitOfWork.merge(entity, visited);
|
|
1227
1303
|
return entity;
|
|
@@ -1248,6 +1324,7 @@ export class EntityManager {
|
|
|
1248
1324
|
...options,
|
|
1249
1325
|
newEntity: !options.managed,
|
|
1250
1326
|
merge: options.managed,
|
|
1327
|
+
normalizeAccessors: true,
|
|
1251
1328
|
});
|
|
1252
1329
|
options.persist ??= em.config.get('persistOnCreate');
|
|
1253
1330
|
if (options.persist && !this.getMetadata(entityName).embeddable) {
|
|
@@ -1267,7 +1344,7 @@ export class EntityManager {
|
|
|
1267
1344
|
getReference(entityName, id, options = {}) {
|
|
1268
1345
|
options.schema ??= this.schema;
|
|
1269
1346
|
options.convertCustomTypes ??= false;
|
|
1270
|
-
const meta = this.metadata.get(
|
|
1347
|
+
const meta = this.metadata.get(entityName);
|
|
1271
1348
|
if (Utils.isPrimaryKey(id)) {
|
|
1272
1349
|
if (meta.compositePK) {
|
|
1273
1350
|
throw ValidationError.invalidCompositeIdentifier(meta);
|
|
@@ -1288,18 +1365,18 @@ export class EntityManager {
|
|
|
1288
1365
|
// Shallow copy options since the object will be modified when deleting orderBy
|
|
1289
1366
|
options = { ...options };
|
|
1290
1367
|
em.prepareOptions(options);
|
|
1291
|
-
entityName = Utils.className(entityName);
|
|
1292
1368
|
await em.tryFlush(entityName, options);
|
|
1293
1369
|
where = await em.processWhere(entityName, where, options, 'read');
|
|
1294
|
-
options.populate = await em.preparePopulate(entityName, options);
|
|
1370
|
+
options.populate = (await em.preparePopulate(entityName, options));
|
|
1295
1371
|
options = { ...options };
|
|
1296
1372
|
// save the original hint value so we know it was infer/all
|
|
1297
1373
|
const meta = em.metadata.find(entityName);
|
|
1298
1374
|
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
1299
1375
|
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
1300
|
-
options.populateFilter = await this.getJoinedFilters(meta,
|
|
1301
|
-
|
|
1376
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
1377
|
+
validateParams(where);
|
|
1302
1378
|
delete options.orderBy;
|
|
1379
|
+
await em.processUnionWhere(entityName, options, 'read');
|
|
1303
1380
|
const cacheKey = em.cacheKey(entityName, options, 'em.count', where);
|
|
1304
1381
|
const cached = await em.tryCache(entityName, options.cache, cacheKey);
|
|
1305
1382
|
if (cached?.data !== undefined) {
|
|
@@ -1324,7 +1401,7 @@ export class EntityManager {
|
|
|
1324
1401
|
for (const ent of entities) {
|
|
1325
1402
|
if (!Utils.isEntity(ent, true)) {
|
|
1326
1403
|
/* v8 ignore next */
|
|
1327
|
-
const meta = typeof ent === 'object' ? em.metadata.find(ent.constructor
|
|
1404
|
+
const meta = typeof ent === 'object' ? em.metadata.find(ent.constructor) : undefined;
|
|
1328
1405
|
throw ValidationError.notDiscoveredEntity(ent, meta);
|
|
1329
1406
|
}
|
|
1330
1407
|
// do not cascade just yet, cascading of entities in persist stack is done when flushing
|
|
@@ -1332,13 +1409,6 @@ export class EntityManager {
|
|
|
1332
1409
|
}
|
|
1333
1410
|
return this;
|
|
1334
1411
|
}
|
|
1335
|
-
/**
|
|
1336
|
-
* Persists your entity immediately, flushing all not yet persisted changes to the database too.
|
|
1337
|
-
* Equivalent to `em.persist(e).flush()`.
|
|
1338
|
-
*/
|
|
1339
|
-
async persistAndFlush(entity) {
|
|
1340
|
-
await this.persist(entity).flush();
|
|
1341
|
-
}
|
|
1342
1412
|
/**
|
|
1343
1413
|
* Marks entity for removal.
|
|
1344
1414
|
* A removed entity will be removed from the database at or before transaction commit or as a result of the flush operation.
|
|
@@ -1362,13 +1432,6 @@ export class EntityManager {
|
|
|
1362
1432
|
}
|
|
1363
1433
|
return em;
|
|
1364
1434
|
}
|
|
1365
|
-
/**
|
|
1366
|
-
* Removes an entity instance immediately, flushing all not yet persisted changes to the database too.
|
|
1367
|
-
* Equivalent to `em.remove(e).flush()`
|
|
1368
|
-
*/
|
|
1369
|
-
async removeAndFlush(entity) {
|
|
1370
|
-
await this.remove(entity).flush();
|
|
1371
|
-
}
|
|
1372
1435
|
/**
|
|
1373
1436
|
* Flushes all changes to objects that have been queued up to now to the database.
|
|
1374
1437
|
* This effectively synchronizes the in-memory state of managed objects with the database.
|
|
@@ -1382,7 +1445,6 @@ export class EntityManager {
|
|
|
1382
1445
|
async tryFlush(entityName, options) {
|
|
1383
1446
|
const em = this.getContext();
|
|
1384
1447
|
const flushMode = options.flushMode ?? em.flushMode ?? em.config.get('flushMode');
|
|
1385
|
-
entityName = Utils.className(entityName);
|
|
1386
1448
|
const meta = em.metadata.get(entityName);
|
|
1387
1449
|
if (flushMode === FlushMode.COMMIT) {
|
|
1388
1450
|
return;
|
|
@@ -1401,7 +1463,6 @@ export class EntityManager {
|
|
|
1401
1463
|
* Checks whether given property can be populated on the entity.
|
|
1402
1464
|
*/
|
|
1403
1465
|
canPopulate(entityName, property) {
|
|
1404
|
-
entityName = Utils.className(entityName);
|
|
1405
1466
|
// eslint-disable-next-line prefer-const
|
|
1406
1467
|
let [p, ...parts] = property.split('.');
|
|
1407
1468
|
const meta = this.metadata.find(entityName);
|
|
@@ -1411,12 +1472,11 @@ export class EntityManager {
|
|
|
1411
1472
|
if (p.includes(':')) {
|
|
1412
1473
|
p = p.split(':', 2)[0];
|
|
1413
1474
|
}
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
}
|
|
1475
|
+
// For TPT inheritance, check the entity's own properties, not just the root's
|
|
1476
|
+
// For STI, meta.properties includes all properties anyway
|
|
1477
|
+
const ret = p in meta.properties;
|
|
1418
1478
|
if (parts.length > 0) {
|
|
1419
|
-
return this.canPopulate(
|
|
1479
|
+
return this.canPopulate(meta.properties[p].targetMeta.class, parts.join('.'));
|
|
1420
1480
|
}
|
|
1421
1481
|
return ret;
|
|
1422
1482
|
}
|
|
@@ -1430,8 +1490,8 @@ export class EntityManager {
|
|
|
1430
1490
|
}
|
|
1431
1491
|
const em = this.getContext();
|
|
1432
1492
|
em.prepareOptions(options);
|
|
1433
|
-
const entityName = arr[0].constructor
|
|
1434
|
-
const preparedPopulate = await em.preparePopulate(entityName, { populate: populate }, options.validate);
|
|
1493
|
+
const entityName = arr[0].constructor;
|
|
1494
|
+
const preparedPopulate = await em.preparePopulate(entityName, { populate: populate, filters: options.filters }, options.validate);
|
|
1435
1495
|
await em.entityLoader.populate(entityName, arr, preparedPopulate, options);
|
|
1436
1496
|
return entities;
|
|
1437
1497
|
}
|
|
@@ -1454,7 +1514,8 @@ export class EntityManager {
|
|
|
1454
1514
|
em.config.set('allowGlobalContext', true);
|
|
1455
1515
|
const fork = new em.constructor(em.config, em.driver, em.metadata, options.useContext, eventManager);
|
|
1456
1516
|
fork.setFlushMode(options.flushMode ?? em.flushMode);
|
|
1457
|
-
fork.disableTransactions =
|
|
1517
|
+
fork.disableTransactions =
|
|
1518
|
+
options.disableTransactions ?? this.disableTransactions ?? this.config.get('disableTransactions');
|
|
1458
1519
|
em.config.set('allowGlobalContext', allowGlobalContext);
|
|
1459
1520
|
if (options.keepTransactionContext) {
|
|
1460
1521
|
fork.transactionContext = em.transactionContext;
|
|
@@ -1559,7 +1620,6 @@ export class EntityManager {
|
|
|
1559
1620
|
*/
|
|
1560
1621
|
getMetadata(entityName) {
|
|
1561
1622
|
if (entityName) {
|
|
1562
|
-
entityName = Utils.className(entityName);
|
|
1563
1623
|
return this.metadata.get(entityName);
|
|
1564
1624
|
}
|
|
1565
1625
|
return this.metadata;
|
|
@@ -1588,8 +1648,8 @@ export class EntityManager {
|
|
|
1588
1648
|
lockTableAliases: options.lockTableAliases,
|
|
1589
1649
|
});
|
|
1590
1650
|
}
|
|
1591
|
-
const preparedPopulate = await this.preparePopulate(meta.
|
|
1592
|
-
await this.entityLoader.populate(meta.
|
|
1651
|
+
const preparedPopulate = await this.preparePopulate(meta.class, options);
|
|
1652
|
+
await this.entityLoader.populate(meta.class, [entity], preparedPopulate, {
|
|
1593
1653
|
...options,
|
|
1594
1654
|
...this.getPopulateWhere(where, options),
|
|
1595
1655
|
orderBy: options.populateOrderBy ?? options.orderBy,
|
|
@@ -1609,6 +1669,7 @@ export class EntityManager {
|
|
|
1609
1669
|
return ret;
|
|
1610
1670
|
}, []);
|
|
1611
1671
|
}
|
|
1672
|
+
/** @internal */
|
|
1612
1673
|
async preparePopulate(entityName, options, validate = true) {
|
|
1613
1674
|
if (options.populate === false) {
|
|
1614
1675
|
return [];
|
|
@@ -1621,11 +1682,14 @@ export class EntityManager {
|
|
|
1621
1682
|
const ret = [];
|
|
1622
1683
|
for (let field of fields) {
|
|
1623
1684
|
if (field === PopulatePath.ALL || field.startsWith(`${PopulatePath.ALL}.`)) {
|
|
1624
|
-
ret.push(...meta.props
|
|
1685
|
+
ret.push(...meta.props
|
|
1686
|
+
.filter(prop => prop.lazy || [ReferenceKind.SCALAR, ReferenceKind.EMBEDDED].includes(prop.kind))
|
|
1687
|
+
.map(prop => prop.name));
|
|
1625
1688
|
continue;
|
|
1626
1689
|
}
|
|
1627
1690
|
field = field.split(':')[0];
|
|
1628
|
-
if (!field.includes('.') &&
|
|
1691
|
+
if (!field.includes('.') &&
|
|
1692
|
+
![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(meta.properties[field].kind)) {
|
|
1629
1693
|
ret.push(field);
|
|
1630
1694
|
continue;
|
|
1631
1695
|
}
|
|
@@ -1649,13 +1713,14 @@ export class EntityManager {
|
|
|
1649
1713
|
options.populate = pruneToOneRelations(meta, this.buildFields(options.fields));
|
|
1650
1714
|
}
|
|
1651
1715
|
if (!options.populate) {
|
|
1652
|
-
const populate = this.entityLoader.normalizePopulate(entityName, [], options.strategy);
|
|
1716
|
+
const populate = this.entityLoader.normalizePopulate(entityName, [], options.strategy, true, options.exclude);
|
|
1653
1717
|
await this.autoJoinRefsForFilters(meta, { ...options, populate });
|
|
1654
1718
|
return populate;
|
|
1655
1719
|
}
|
|
1656
1720
|
if (typeof options.populate !== 'boolean') {
|
|
1657
|
-
options.populate = Utils.asArray(options.populate)
|
|
1658
|
-
|
|
1721
|
+
options.populate = Utils.asArray(options.populate)
|
|
1722
|
+
.map(field => {
|
|
1723
|
+
/* v8 ignore next */
|
|
1659
1724
|
if (typeof field === 'boolean' || field === PopulatePath.ALL) {
|
|
1660
1725
|
return [{ field: meta.primaryKeys[0], strategy: options.strategy, all: !!field }]; //
|
|
1661
1726
|
}
|
|
@@ -1665,24 +1730,28 @@ export class EntityManager {
|
|
|
1665
1730
|
options.flags.push(QueryFlag.INFER_POPULATE);
|
|
1666
1731
|
return [];
|
|
1667
1732
|
}
|
|
1668
|
-
if (
|
|
1733
|
+
if (typeof field === 'string') {
|
|
1669
1734
|
return [{ field, strategy: options.strategy }];
|
|
1670
1735
|
}
|
|
1671
1736
|
return [field];
|
|
1672
|
-
})
|
|
1737
|
+
})
|
|
1738
|
+
.flat();
|
|
1673
1739
|
}
|
|
1674
|
-
const populate = this.entityLoader.normalizePopulate(entityName, options.populate, options.strategy);
|
|
1740
|
+
const populate = this.entityLoader.normalizePopulate(entityName, options.populate, options.strategy, true, options.exclude);
|
|
1675
1741
|
const invalid = populate.find(({ field }) => !this.canPopulate(entityName, field));
|
|
1676
1742
|
if (validate && invalid) {
|
|
1677
1743
|
throw ValidationError.invalidPropertyName(entityName, invalid.field);
|
|
1678
1744
|
}
|
|
1679
1745
|
await this.autoJoinRefsForFilters(meta, { ...options, populate });
|
|
1680
|
-
|
|
1746
|
+
for (const field of populate) {
|
|
1681
1747
|
// force select-in strategy when populating all relations as otherwise we could cause infinite loops when self-referencing
|
|
1682
1748
|
const all = field.all ?? (Array.isArray(options.populate) && options.populate.includes('*'));
|
|
1683
1749
|
field.strategy = all ? LoadStrategy.SELECT_IN : (options.strategy ?? field.strategy);
|
|
1684
|
-
|
|
1685
|
-
|
|
1750
|
+
}
|
|
1751
|
+
if (options.populateHints) {
|
|
1752
|
+
applyPopulateHints(populate, options.populateHints);
|
|
1753
|
+
}
|
|
1754
|
+
return populate;
|
|
1686
1755
|
}
|
|
1687
1756
|
/**
|
|
1688
1757
|
* when the entity is found in identity map, we check if it was partially loaded or we are trying to populate
|
|
@@ -1702,7 +1771,7 @@ export class EntityManager {
|
|
|
1702
1771
|
return !inlineEmbedded && !prop.lazy && !helper(entity).__loadedProperties.has(prop.name);
|
|
1703
1772
|
});
|
|
1704
1773
|
}
|
|
1705
|
-
if (autoRefresh) {
|
|
1774
|
+
if (autoRefresh || options.filters) {
|
|
1706
1775
|
return true;
|
|
1707
1776
|
}
|
|
1708
1777
|
if (Array.isArray(options.populate)) {
|
|
@@ -1726,7 +1795,7 @@ export class EntityManager {
|
|
|
1726
1795
|
for (const k of ['ctx', 'strategy', 'flushMode', 'logging', 'loggerContext']) {
|
|
1727
1796
|
delete opts[k];
|
|
1728
1797
|
}
|
|
1729
|
-
return [entityName, method, opts, where];
|
|
1798
|
+
return [Utils.className(entityName), method, opts, where];
|
|
1730
1799
|
}
|
|
1731
1800
|
/**
|
|
1732
1801
|
* @internal
|
|
@@ -1743,21 +1812,17 @@ export class EntityManager {
|
|
|
1743
1812
|
return { key: cacheKey, data: cached };
|
|
1744
1813
|
}
|
|
1745
1814
|
let data;
|
|
1815
|
+
const createOptions = {
|
|
1816
|
+
merge: true,
|
|
1817
|
+
convertCustomTypes: false,
|
|
1818
|
+
refresh,
|
|
1819
|
+
recomputeSnapshot: true,
|
|
1820
|
+
};
|
|
1746
1821
|
if (Array.isArray(cached) && merge) {
|
|
1747
|
-
data = cached.map(item => em.entityFactory.create(entityName, item,
|
|
1748
|
-
merge: true,
|
|
1749
|
-
convertCustomTypes: true,
|
|
1750
|
-
refresh,
|
|
1751
|
-
recomputeSnapshot: true,
|
|
1752
|
-
}));
|
|
1822
|
+
data = cached.map(item => em.entityFactory.create(entityName, item, createOptions));
|
|
1753
1823
|
}
|
|
1754
1824
|
else if (Utils.isObject(cached) && merge) {
|
|
1755
|
-
data = em.entityFactory.create(entityName, cached,
|
|
1756
|
-
merge: true,
|
|
1757
|
-
convertCustomTypes: true,
|
|
1758
|
-
refresh,
|
|
1759
|
-
recomputeSnapshot: true,
|
|
1760
|
-
});
|
|
1825
|
+
data = em.entityFactory.create(entityName, cached, createOptions);
|
|
1761
1826
|
}
|
|
1762
1827
|
else {
|
|
1763
1828
|
data = cached;
|
|
@@ -1772,7 +1837,7 @@ export class EntityManager {
|
|
|
1772
1837
|
config ??= this.config.get('resultCache').global;
|
|
1773
1838
|
if (config) {
|
|
1774
1839
|
const em = this.getContext();
|
|
1775
|
-
const expiration = Array.isArray(config) ? config[1] :
|
|
1840
|
+
const expiration = Array.isArray(config) ? config[1] : typeof config === 'number' ? config : undefined;
|
|
1776
1841
|
await em.resultCache.set(key.key, data instanceof Function ? data() : data, '', expiration);
|
|
1777
1842
|
}
|
|
1778
1843
|
}
|
|
@@ -1805,6 +1870,23 @@ export class EntityManager {
|
|
|
1805
1870
|
set schema(schema) {
|
|
1806
1871
|
this.getContext(false)._schema = schema ?? undefined;
|
|
1807
1872
|
}
|
|
1873
|
+
/** @internal */
|
|
1874
|
+
async getDataLoader(type) {
|
|
1875
|
+
const em = this.getContext();
|
|
1876
|
+
if (em.loaders[type]) {
|
|
1877
|
+
return em.loaders[type];
|
|
1878
|
+
}
|
|
1879
|
+
const { DataloaderUtils } = await import('@mikro-orm/core/dataloader');
|
|
1880
|
+
const DataLoader = await DataloaderUtils.getDataLoader();
|
|
1881
|
+
switch (type) {
|
|
1882
|
+
case 'ref':
|
|
1883
|
+
return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getRefBatchLoadFn(em)));
|
|
1884
|
+
case '1:m':
|
|
1885
|
+
return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getColBatchLoadFn(em)));
|
|
1886
|
+
case 'm:n':
|
|
1887
|
+
return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getManyToManyColBatchLoadFn(em)));
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1808
1890
|
/**
|
|
1809
1891
|
* Returns the ID of this EntityManager. Respects the context, so global EM will give you the contextual ID
|
|
1810
1892
|
* if executed inside request context handler.
|
|
@@ -1813,7 +1895,7 @@ export class EntityManager {
|
|
|
1813
1895
|
return this.getContext(false)._id;
|
|
1814
1896
|
}
|
|
1815
1897
|
/** @ignore */
|
|
1816
|
-
[inspect.custom]() {
|
|
1898
|
+
[Symbol.for('nodejs.util.inspect.custom')]() {
|
|
1817
1899
|
return `[EntityManager<${this.id}>]`;
|
|
1818
1900
|
}
|
|
1819
1901
|
}
|