@mikro-orm/core 7.0.0-dev.9 → 7.0.0-dev.90
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 +77 -48
- package/EntityManager.js +288 -225
- package/MikroORM.d.ts +40 -31
- package/MikroORM.js +98 -137
- package/README.md +3 -2
- package/cache/FileCacheAdapter.d.ts +1 -1
- package/cache/FileCacheAdapter.js +6 -5
- 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 +11 -7
- package/connections/Connection.js +16 -14
- package/drivers/DatabaseDriver.d.ts +11 -5
- package/drivers/DatabaseDriver.js +23 -11
- package/drivers/IDatabaseDriver.d.ts +25 -4
- package/entity/BaseEntity.d.ts +0 -1
- package/entity/BaseEntity.js +0 -3
- package/entity/Collection.d.ts +95 -30
- package/entity/Collection.js +432 -93
- package/entity/EntityAssigner.d.ts +1 -1
- package/entity/EntityAssigner.js +17 -9
- package/entity/EntityFactory.d.ts +7 -0
- package/entity/EntityFactory.js +63 -41
- package/entity/EntityHelper.js +26 -12
- package/entity/EntityLoader.d.ts +5 -4
- package/entity/EntityLoader.js +63 -38
- package/entity/EntityRepository.d.ts +1 -1
- package/entity/Reference.d.ts +6 -5
- package/entity/Reference.js +34 -9
- package/entity/WrappedEntity.d.ts +2 -7
- package/entity/WrappedEntity.js +2 -7
- package/entity/defineEntity.d.ts +568 -0
- package/entity/defineEntity.js +529 -0
- package/entity/index.d.ts +3 -2
- package/entity/index.js +3 -2
- package/entity/utils.d.ts +7 -0
- package/entity/utils.js +16 -4
- package/entity/validators.d.ts +11 -0
- package/entity/validators.js +65 -0
- package/enums.d.ts +21 -6
- package/enums.js +14 -1
- package/errors.d.ts +10 -2
- package/errors.js +29 -10
- package/events/EventManager.d.ts +2 -1
- package/events/EventManager.js +19 -11
- package/events/EventSubscriber.d.ts +3 -1
- package/hydration/Hydrator.js +1 -2
- package/hydration/ObjectHydrator.d.ts +4 -4
- package/hydration/ObjectHydrator.js +35 -25
- 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/index.d.ts +1 -0
- package/logging/index.js +1 -0
- package/logging/inspect.d.ts +2 -0
- package/logging/inspect.js +16 -0
- package/metadata/EntitySchema.d.ts +9 -13
- package/metadata/EntitySchema.js +44 -26
- package/metadata/MetadataDiscovery.d.ts +6 -9
- package/metadata/MetadataDiscovery.js +165 -205
- package/metadata/MetadataProvider.d.ts +11 -2
- package/metadata/MetadataProvider.js +44 -2
- package/metadata/MetadataStorage.d.ts +1 -6
- package/metadata/MetadataStorage.js +6 -18
- package/metadata/MetadataValidator.d.ts +0 -7
- package/metadata/MetadataValidator.js +0 -10
- package/metadata/discover-entities.d.ts +5 -0
- package/metadata/discover-entities.js +40 -0
- package/metadata/index.d.ts +1 -1
- package/metadata/index.js +1 -1
- package/metadata/types.d.ts +480 -0
- package/metadata/types.js +1 -0
- package/naming-strategy/AbstractNamingStrategy.d.ts +5 -1
- package/naming-strategy/AbstractNamingStrategy.js +8 -2
- package/naming-strategy/NamingStrategy.d.ts +11 -1
- package/not-supported.d.ts +2 -0
- package/not-supported.js +4 -0
- package/package.json +18 -10
- package/platforms/ExceptionConverter.js +1 -1
- package/platforms/Platform.d.ts +6 -13
- package/platforms/Platform.js +15 -41
- package/serialization/EntitySerializer.d.ts +2 -0
- package/serialization/EntitySerializer.js +32 -14
- package/serialization/EntityTransformer.js +22 -12
- package/serialization/SerializationContext.js +16 -13
- package/types/ArrayType.d.ts +1 -1
- package/types/ArrayType.js +2 -3
- package/types/BigIntType.d.ts +8 -6
- package/types/BigIntType.js +1 -1
- package/types/BlobType.d.ts +0 -1
- package/types/BlobType.js +0 -3
- package/types/BooleanType.d.ts +2 -1
- package/types/BooleanType.js +3 -0
- package/types/DecimalType.d.ts +6 -4
- package/types/DecimalType.js +3 -3
- package/types/DoubleType.js +2 -2
- package/types/EnumArrayType.js +1 -2
- package/types/JsonType.d.ts +1 -1
- package/types/JsonType.js +7 -2
- package/types/TinyIntType.js +1 -1
- package/types/Type.d.ts +2 -4
- package/types/Type.js +3 -3
- package/types/Uint8ArrayType.d.ts +0 -1
- package/types/Uint8ArrayType.js +1 -4
- package/types/index.d.ts +1 -1
- package/typings.d.ts +109 -73
- package/typings.js +38 -35
- package/unit-of-work/ChangeSet.d.ts +0 -3
- package/unit-of-work/ChangeSet.js +2 -2
- package/unit-of-work/ChangeSetComputer.d.ts +1 -3
- package/unit-of-work/ChangeSetComputer.js +11 -9
- package/unit-of-work/ChangeSetPersister.d.ts +5 -4
- package/unit-of-work/ChangeSetPersister.js +51 -19
- package/unit-of-work/UnitOfWork.d.ts +8 -1
- package/unit-of-work/UnitOfWork.js +91 -49
- package/utils/AbstractSchemaGenerator.d.ts +5 -5
- package/utils/AbstractSchemaGenerator.js +11 -9
- package/utils/Configuration.d.ts +757 -206
- package/utils/Configuration.js +140 -188
- package/utils/ConfigurationLoader.d.ts +1 -54
- package/utils/ConfigurationLoader.js +1 -352
- package/utils/Cursor.d.ts +0 -3
- package/utils/Cursor.js +6 -3
- package/utils/DataloaderUtils.d.ts +15 -5
- package/utils/DataloaderUtils.js +54 -8
- package/utils/EntityComparator.d.ts +8 -4
- package/utils/EntityComparator.js +52 -17
- package/utils/QueryHelper.d.ts +9 -1
- package/utils/QueryHelper.js +70 -9
- package/utils/RawQueryFragment.d.ts +36 -13
- package/utils/RawQueryFragment.js +36 -16
- package/utils/TransactionManager.d.ts +65 -0
- package/utils/TransactionManager.js +223 -0
- package/utils/Utils.d.ts +8 -97
- package/utils/Utils.js +82 -302
- package/utils/clone.js +2 -3
- package/utils/env-vars.d.ts +3 -0
- package/utils/env-vars.js +87 -0
- package/utils/fs-utils.d.ts +12 -0
- package/utils/fs-utils.js +97 -0
- package/utils/index.d.ts +2 -1
- package/utils/index.js +2 -1
- package/utils/upsert-utils.d.ts +7 -2
- package/utils/upsert-utils.js +55 -4
- package/decorators/Check.d.ts +0 -3
- package/decorators/Check.js +0 -13
- package/decorators/CreateRequestContext.d.ts +0 -3
- package/decorators/CreateRequestContext.js +0 -32
- package/decorators/Embeddable.d.ts +0 -8
- package/decorators/Embeddable.js +0 -11
- package/decorators/Embedded.d.ts +0 -18
- package/decorators/Embedded.js +0 -18
- package/decorators/Entity.d.ts +0 -18
- package/decorators/Entity.js +0 -12
- package/decorators/Enum.d.ts +0 -9
- package/decorators/Enum.js +0 -16
- package/decorators/Filter.d.ts +0 -2
- package/decorators/Filter.js +0 -8
- package/decorators/Formula.d.ts +0 -4
- package/decorators/Formula.js +0 -15
- package/decorators/Indexed.d.ts +0 -19
- package/decorators/Indexed.js +0 -20
- package/decorators/ManyToMany.d.ts +0 -40
- package/decorators/ManyToMany.js +0 -14
- package/decorators/ManyToOne.d.ts +0 -30
- package/decorators/ManyToOne.js +0 -14
- package/decorators/OneToMany.d.ts +0 -28
- package/decorators/OneToMany.js +0 -17
- package/decorators/OneToOne.d.ts +0 -24
- package/decorators/OneToOne.js +0 -7
- package/decorators/PrimaryKey.d.ts +0 -8
- package/decorators/PrimaryKey.js +0 -20
- package/decorators/Property.d.ts +0 -250
- package/decorators/Property.js +0 -32
- package/decorators/Transactional.d.ts +0 -13
- package/decorators/Transactional.js +0 -28
- package/decorators/hooks.d.ts +0 -16
- package/decorators/hooks.js +0 -47
- package/decorators/index.d.ts +0 -17
- package/decorators/index.js +0 -17
- package/entity/ArrayCollection.d.ts +0 -116
- package/entity/ArrayCollection.js +0 -402
- package/entity/EntityValidator.d.ts +0 -19
- package/entity/EntityValidator.js +0 -150
- package/metadata/ReflectMetadataProvider.d.ts +0 -8
- package/metadata/ReflectMetadataProvider.js +0 -44
- package/utils/resolveContextProvider.d.ts +0 -10
- package/utils/resolveContextProvider.js +0 -28
package/EntityManager.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
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';
|
|
@@ -9,7 +7,7 @@ import { TransactionContext } from './utils/TransactionContext.js';
|
|
|
9
7
|
import { isRaw, RawQueryFragment } from './utils/RawQueryFragment.js';
|
|
10
8
|
import { EntityFactory } from './entity/EntityFactory.js';
|
|
11
9
|
import { EntityAssigner } from './entity/EntityAssigner.js';
|
|
12
|
-
import {
|
|
10
|
+
import { validateEmptyWhere, validateParams, validatePrimaryKey, validateProperty } from './entity/validators.js';
|
|
13
11
|
import { EntityLoader } from './entity/EntityLoader.js';
|
|
14
12
|
import { Reference } from './entity/Reference.js';
|
|
15
13
|
import { helper } from './entity/wrap.js';
|
|
@@ -19,6 +17,8 @@ import { EventType, FlushMode, LoadStrategy, LockMode, PopulateHint, PopulatePat
|
|
|
19
17
|
import { EventManager } from './events/EventManager.js';
|
|
20
18
|
import { TransactionEventBroadcaster } from './events/TransactionEventBroadcaster.js';
|
|
21
19
|
import { OptimisticLockError, ValidationError } from './errors.js';
|
|
20
|
+
import { getLoadingStrategy } from './entity/utils.js';
|
|
21
|
+
import { TransactionManager } from './utils/TransactionManager.js';
|
|
22
22
|
/**
|
|
23
23
|
* The EntityManager is the central access point to ORM functionality. It is a facade to all different ORM subsystems
|
|
24
24
|
* such as UnitOfWork, Query Language, and Repository API.
|
|
@@ -34,9 +34,7 @@ export class EntityManager {
|
|
|
34
34
|
_id = EntityManager.counter++;
|
|
35
35
|
global = false;
|
|
36
36
|
name;
|
|
37
|
-
|
|
38
|
-
colLoader = new DataLoader(DataloaderUtils.getColBatchLoadFn(this));
|
|
39
|
-
validator;
|
|
37
|
+
loaders = {};
|
|
40
38
|
repositoryMap = {};
|
|
41
39
|
entityLoader;
|
|
42
40
|
comparator;
|
|
@@ -61,7 +59,6 @@ export class EntityManager {
|
|
|
61
59
|
this.eventManager = eventManager;
|
|
62
60
|
this.entityLoader = new EntityLoader(this);
|
|
63
61
|
this.name = this.config.get('contextName');
|
|
64
|
-
this.validator = new EntityValidator(this.config.get('strict'));
|
|
65
62
|
this.comparator = this.config.getComparator(this.metadata);
|
|
66
63
|
this.resultCache = this.config.getResultCacheAdapter();
|
|
67
64
|
this.disableTransactions = this.config.get('disableTransactions');
|
|
@@ -105,12 +102,6 @@ export class EntityManager {
|
|
|
105
102
|
repo(entityName) {
|
|
106
103
|
return this.getRepository(entityName);
|
|
107
104
|
}
|
|
108
|
-
/**
|
|
109
|
-
* Gets EntityValidator instance
|
|
110
|
-
*/
|
|
111
|
-
getValidator() {
|
|
112
|
-
return this.validator;
|
|
113
|
-
}
|
|
114
105
|
/**
|
|
115
106
|
* Finds all entities matching your `where` query. You can pass additional options via the `options` parameter.
|
|
116
107
|
*/
|
|
@@ -127,7 +118,7 @@ export class EntityManager {
|
|
|
127
118
|
await em.tryFlush(entityName, options);
|
|
128
119
|
entityName = Utils.className(entityName);
|
|
129
120
|
where = await em.processWhere(entityName, where, options, 'read');
|
|
130
|
-
|
|
121
|
+
validateParams(where);
|
|
131
122
|
options.orderBy = options.orderBy || {};
|
|
132
123
|
options.populate = await em.preparePopulate(entityName, options);
|
|
133
124
|
const populate = options.populate;
|
|
@@ -137,7 +128,6 @@ export class EntityManager {
|
|
|
137
128
|
await em.entityLoader.populate(entityName, cached.data, populate, {
|
|
138
129
|
...options,
|
|
139
130
|
...em.getPopulateWhere(where, options),
|
|
140
|
-
convertCustomTypes: false,
|
|
141
131
|
ignoreLazyScalarProperties: true,
|
|
142
132
|
lookup: false,
|
|
143
133
|
});
|
|
@@ -148,7 +138,7 @@ export class EntityManager {
|
|
|
148
138
|
// save the original hint value so we know it was infer/all
|
|
149
139
|
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
150
140
|
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
151
|
-
options.populateFilter = await this.getJoinedFilters(meta,
|
|
141
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
152
142
|
const results = await em.driver.find(entityName, where, { ctx: em.transactionContext, em, ...options });
|
|
153
143
|
if (results.length === 0) {
|
|
154
144
|
await em.storeCache(options.cache, cached, []);
|
|
@@ -168,7 +158,6 @@ export class EntityManager {
|
|
|
168
158
|
await em.entityLoader.populate(entityName, unique, populate, {
|
|
169
159
|
...options,
|
|
170
160
|
...em.getPopulateWhere(where, options),
|
|
171
|
-
convertCustomTypes: false,
|
|
172
161
|
ignoreLazyScalarProperties: true,
|
|
173
162
|
lookup: false,
|
|
174
163
|
});
|
|
@@ -181,6 +170,61 @@ export class EntityManager {
|
|
|
181
170
|
}
|
|
182
171
|
return unique;
|
|
183
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Finds all entities and returns an async iterable (async generator) that yields results one by one.
|
|
175
|
+
* The results are merged and mapped to entity instances, without adding them to the identity map.
|
|
176
|
+
* You can disable merging by passing the options `{ mergeResults: false }`.
|
|
177
|
+
* With `mergeResults` disabled, to-many collections will contain at most one item, and you will get duplicate
|
|
178
|
+
* root entities when there are multiple items in the populated collection.
|
|
179
|
+
* This is useful for processing large datasets without loading everything into memory at once.
|
|
180
|
+
*
|
|
181
|
+
* ```ts
|
|
182
|
+
* const stream = em.stream(Book, { populate: ['author'] });
|
|
183
|
+
*
|
|
184
|
+
* for await (const book of stream) {
|
|
185
|
+
* // book is an instance of Book entity
|
|
186
|
+
* console.log(book.title, book.author.name);
|
|
187
|
+
* }
|
|
188
|
+
* ```
|
|
189
|
+
*/
|
|
190
|
+
async *stream(entityName, options = {}) {
|
|
191
|
+
const em = this.getContext();
|
|
192
|
+
em.prepareOptions(options);
|
|
193
|
+
options.strategy = 'joined';
|
|
194
|
+
await em.tryFlush(entityName, options);
|
|
195
|
+
entityName = Utils.className(entityName);
|
|
196
|
+
const where = await em.processWhere(entityName, options.where ?? {}, options, 'read');
|
|
197
|
+
validateParams(where);
|
|
198
|
+
options.orderBy = options.orderBy || {};
|
|
199
|
+
options.populate = await em.preparePopulate(entityName, options);
|
|
200
|
+
const meta = this.metadata.get(entityName);
|
|
201
|
+
options = { ...options };
|
|
202
|
+
// save the original hint value so we know it was infer/all
|
|
203
|
+
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
204
|
+
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
205
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
206
|
+
const stream = em.driver.stream(entityName, where, {
|
|
207
|
+
ctx: em.transactionContext,
|
|
208
|
+
mapResults: false,
|
|
209
|
+
...options,
|
|
210
|
+
});
|
|
211
|
+
for await (const data of stream) {
|
|
212
|
+
const fork = em.fork();
|
|
213
|
+
const entity = fork.entityFactory.create(entityName, data, {
|
|
214
|
+
refresh: options.refresh,
|
|
215
|
+
schema: options.schema,
|
|
216
|
+
convertCustomTypes: true,
|
|
217
|
+
});
|
|
218
|
+
helper(entity).setSerializationContext({
|
|
219
|
+
populate: options.populate,
|
|
220
|
+
fields: options.fields,
|
|
221
|
+
exclude: options.exclude,
|
|
222
|
+
});
|
|
223
|
+
await fork.unitOfWork.dispatchOnLoadEvent();
|
|
224
|
+
fork.clear();
|
|
225
|
+
yield entity;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
184
228
|
/**
|
|
185
229
|
* Finds all entities of given type, optionally matching the `where` condition provided in the `options` parameter.
|
|
186
230
|
*/
|
|
@@ -194,7 +238,7 @@ export class EntityManager {
|
|
|
194
238
|
if (options.populateWhere === PopulateHint.ALL) {
|
|
195
239
|
return { where: {}, populateWhere: options.populateWhere };
|
|
196
240
|
}
|
|
197
|
-
/* v8 ignore next
|
|
241
|
+
/* v8 ignore next */
|
|
198
242
|
if (options.populateWhere === PopulateHint.INFER) {
|
|
199
243
|
return { where, populateWhere: options.populateWhere };
|
|
200
244
|
}
|
|
@@ -203,12 +247,12 @@ export class EntityManager {
|
|
|
203
247
|
/**
|
|
204
248
|
* Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
|
|
205
249
|
*/
|
|
206
|
-
addFilter(
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
options.entity = Utils.asArray(entityName).map(n => Utils.className(n));
|
|
250
|
+
addFilter(options) {
|
|
251
|
+
if (options.entity) {
|
|
252
|
+
options.entity = Utils.asArray(options.entity).map(n => Utils.className(n));
|
|
210
253
|
}
|
|
211
|
-
|
|
254
|
+
options.default ??= true;
|
|
255
|
+
this.getContext(false).filters[options.name] = options;
|
|
212
256
|
}
|
|
213
257
|
/**
|
|
214
258
|
* Sets filter parameter values globally inside context defined by this entity manager.
|
|
@@ -232,8 +276,8 @@ export class EntityManager {
|
|
|
232
276
|
/**
|
|
233
277
|
* Gets logger context for this entity manager.
|
|
234
278
|
*/
|
|
235
|
-
getLoggerContext() {
|
|
236
|
-
const em = this.getContext();
|
|
279
|
+
getLoggerContext(options) {
|
|
280
|
+
const em = options?.disableContextResolution ? this : this.getContext();
|
|
237
281
|
em.loggerContext ??= {};
|
|
238
282
|
return em.loggerContext;
|
|
239
283
|
}
|
|
@@ -283,28 +327,39 @@ export class EntityManager {
|
|
|
283
327
|
}
|
|
284
328
|
return ret;
|
|
285
329
|
}
|
|
286
|
-
async getJoinedFilters(meta,
|
|
330
|
+
async getJoinedFilters(meta, options) {
|
|
331
|
+
if (!this.config.get('filtersOnRelations') || !options.populate) {
|
|
332
|
+
return undefined;
|
|
333
|
+
}
|
|
287
334
|
const ret = {};
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
335
|
+
for (const hint of options.populate) {
|
|
336
|
+
const field = hint.field.split(':')[0];
|
|
337
|
+
const prop = meta.properties[field];
|
|
338
|
+
const strategy = getLoadingStrategy(prop.strategy || hint.strategy || options.strategy || this.config.get('loadStrategy'), prop.kind);
|
|
339
|
+
const joined = strategy === LoadStrategy.JOINED && prop.kind !== ReferenceKind.SCALAR;
|
|
340
|
+
if (!joined && !hint.filter) {
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
const filters = QueryHelper.mergePropertyFilters(prop.filters, options.filters);
|
|
344
|
+
const where = await this.applyFilters(prop.type, {}, filters, 'read', {
|
|
345
|
+
...options,
|
|
346
|
+
populate: hint.children,
|
|
347
|
+
});
|
|
348
|
+
const where2 = await this.getJoinedFilters(prop.targetMeta, {
|
|
349
|
+
...options,
|
|
350
|
+
filters,
|
|
351
|
+
populate: hint.children,
|
|
352
|
+
populateWhere: PopulateHint.ALL,
|
|
353
|
+
});
|
|
354
|
+
if (Utils.hasObjectKeys(where)) {
|
|
355
|
+
ret[field] = ret[field] ? { $and: [where, ret[field]] } : where;
|
|
356
|
+
}
|
|
357
|
+
if (where2 && Utils.hasObjectKeys(where2)) {
|
|
358
|
+
if (ret[field]) {
|
|
359
|
+
Utils.merge(ret[field], where2);
|
|
300
360
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
Utils.merge(ret[field], where2);
|
|
304
|
-
}
|
|
305
|
-
else {
|
|
306
|
-
ret[field] = where2;
|
|
307
|
-
}
|
|
361
|
+
else {
|
|
362
|
+
ret[field] = where2;
|
|
308
363
|
}
|
|
309
364
|
}
|
|
310
365
|
}
|
|
@@ -313,27 +368,45 @@ export class EntityManager {
|
|
|
313
368
|
/**
|
|
314
369
|
* When filters are active on M:1 or 1:1 relations, we need to ref join them eagerly as they might affect the FK value.
|
|
315
370
|
*/
|
|
316
|
-
async autoJoinRefsForFilters(meta, options) {
|
|
317
|
-
if (!meta || !this.config.get('autoJoinRefsForFilters')) {
|
|
371
|
+
async autoJoinRefsForFilters(meta, options, parent) {
|
|
372
|
+
if (!meta || !this.config.get('autoJoinRefsForFilters') || options.filters === false) {
|
|
318
373
|
return;
|
|
319
374
|
}
|
|
320
|
-
const props = meta.relations.filter(prop => {
|
|
321
|
-
return !prop.object && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)
|
|
322
|
-
&& ((options.fields?.length ?? 0) === 0 || options.fields?.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)));
|
|
323
|
-
});
|
|
324
375
|
const ret = options.populate;
|
|
325
|
-
for (const prop of
|
|
326
|
-
|
|
376
|
+
for (const prop of meta.relations) {
|
|
377
|
+
if (prop.object
|
|
378
|
+
|| ![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)
|
|
379
|
+
|| !((options.fields?.length ?? 0) === 0 || options.fields?.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
|
|
380
|
+
|| (parent?.className === prop.targetMeta.root.className && parent.propName === prop.inversedBy)) {
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
options = { ...options, filters: QueryHelper.mergePropertyFilters(prop.filters, options.filters) };
|
|
384
|
+
const cond = await this.applyFilters(prop.type, {}, options.filters, 'read', options);
|
|
327
385
|
if (!Utils.isEmpty(cond)) {
|
|
328
386
|
const populated = options.populate.filter(({ field }) => field.split(':')[0] === prop.name);
|
|
329
|
-
|
|
330
|
-
|
|
387
|
+
let found = false;
|
|
388
|
+
for (const hint of populated) {
|
|
389
|
+
if (!hint.all) {
|
|
390
|
+
hint.filter = true;
|
|
391
|
+
}
|
|
392
|
+
const strategy = getLoadingStrategy(prop.strategy || hint.strategy || options.strategy || this.config.get('loadStrategy'), prop.kind);
|
|
393
|
+
if (hint.field === `${prop.name}:ref` || (hint.filter && strategy === LoadStrategy.JOINED)) {
|
|
394
|
+
found = true;
|
|
395
|
+
}
|
|
331
396
|
}
|
|
332
|
-
|
|
397
|
+
if (!found) {
|
|
333
398
|
ret.push({ field: `${prop.name}:ref`, strategy: LoadStrategy.JOINED, filter: true });
|
|
334
399
|
}
|
|
335
400
|
}
|
|
336
401
|
}
|
|
402
|
+
for (const hint of ret) {
|
|
403
|
+
const [field, ref] = hint.field.split(':');
|
|
404
|
+
const prop = meta?.properties[field];
|
|
405
|
+
if (prop && !ref) {
|
|
406
|
+
hint.children ??= [];
|
|
407
|
+
await this.autoJoinRefsForFilters(prop.targetMeta, { ...options, populate: hint.children }, { className: meta.root.className, propName: prop.name });
|
|
408
|
+
}
|
|
409
|
+
}
|
|
337
410
|
}
|
|
338
411
|
/**
|
|
339
412
|
* @internal
|
|
@@ -363,7 +436,7 @@ export class EntityManager {
|
|
|
363
436
|
let cond;
|
|
364
437
|
if (filter.cond instanceof Function) {
|
|
365
438
|
// @ts-ignore
|
|
366
|
-
const args = Utils.isPlainObject(options[filter.name]) ? options[filter.name] : this.getContext().filterParams[filter.name];
|
|
439
|
+
const args = Utils.isPlainObject(options?.[filter.name]) ? options[filter.name] : this.getContext().filterParams[filter.name];
|
|
367
440
|
if (!args && filter.cond.length > 0 && filter.args !== false) {
|
|
368
441
|
throw new Error(`No arguments provided for filter '${filter.name}'`);
|
|
369
442
|
}
|
|
@@ -372,13 +445,17 @@ export class EntityManager {
|
|
|
372
445
|
else {
|
|
373
446
|
cond = filter.cond;
|
|
374
447
|
}
|
|
375
|
-
|
|
448
|
+
cond = QueryHelper.processWhere({
|
|
376
449
|
where: cond,
|
|
377
450
|
entityName,
|
|
378
451
|
metadata: this.metadata,
|
|
379
452
|
platform: this.driver.getPlatform(),
|
|
380
453
|
aliased: type === 'read',
|
|
381
|
-
})
|
|
454
|
+
});
|
|
455
|
+
if (filter.strict) {
|
|
456
|
+
Object.defineProperty(cond, '__strict', { value: filter.strict, enumerable: false });
|
|
457
|
+
}
|
|
458
|
+
ret.push(cond);
|
|
382
459
|
}
|
|
383
460
|
const conds = [...ret, where].filter(c => Utils.hasObjectKeys(c));
|
|
384
461
|
return conds.length > 1 ? { $and: conds } : conds[0];
|
|
@@ -489,18 +566,31 @@ export class EntityManager {
|
|
|
489
566
|
async refresh(entity, options = {}) {
|
|
490
567
|
const fork = this.fork({ keepTransactionContext: true });
|
|
491
568
|
const entityName = entity.constructor.name;
|
|
569
|
+
const wrapped = helper(entity);
|
|
492
570
|
const reloaded = await fork.findOne(entityName, entity, {
|
|
493
|
-
schema:
|
|
571
|
+
schema: wrapped.__schema,
|
|
494
572
|
...options,
|
|
495
573
|
flushMode: FlushMode.COMMIT,
|
|
496
574
|
});
|
|
497
|
-
|
|
498
|
-
|
|
575
|
+
const em = this.getContext();
|
|
576
|
+
if (!reloaded) {
|
|
577
|
+
em.unitOfWork.unsetIdentity(entity);
|
|
578
|
+
return null;
|
|
499
579
|
}
|
|
500
|
-
|
|
501
|
-
|
|
580
|
+
let found = false;
|
|
581
|
+
for (const e of fork.unitOfWork.getIdentityMap()) {
|
|
582
|
+
const ref = em.getReference(e.constructor.name, helper(e).getPrimaryKey());
|
|
583
|
+
const data = helper(e).serialize({ ignoreSerializers: true, includeHidden: true });
|
|
584
|
+
em.config.getHydrator(this.metadata).hydrate(ref, helper(ref).__meta, data, em.entityFactory, 'full', false, true);
|
|
585
|
+
Utils.merge(helper(ref).__originalEntityData, this.comparator.prepareEntity(e));
|
|
586
|
+
found ||= ref === entity;
|
|
502
587
|
}
|
|
503
|
-
|
|
588
|
+
if (!found) {
|
|
589
|
+
const data = helper(reloaded).serialize({ ignoreSerializers: true, includeHidden: true });
|
|
590
|
+
em.config.getHydrator(this.metadata).hydrate(entity, wrapped.__meta, data, em.entityFactory, 'full', false, true);
|
|
591
|
+
Utils.merge(wrapped.__originalEntityData, this.comparator.prepareEntity(reloaded));
|
|
592
|
+
}
|
|
593
|
+
return entity;
|
|
504
594
|
}
|
|
505
595
|
/**
|
|
506
596
|
* Finds first entity matching your `where` query.
|
|
@@ -526,31 +616,32 @@ export class EntityManager {
|
|
|
526
616
|
await em.tryFlush(entityName, options);
|
|
527
617
|
const meta = em.metadata.get(entityName);
|
|
528
618
|
where = await em.processWhere(entityName, where, options, 'read');
|
|
529
|
-
|
|
619
|
+
validateEmptyWhere(where);
|
|
530
620
|
em.checkLockRequirements(options.lockMode, meta);
|
|
531
|
-
const isOptimisticLocking =
|
|
621
|
+
const isOptimisticLocking = options.lockMode == null || options.lockMode === LockMode.OPTIMISTIC;
|
|
532
622
|
if (entity && !em.shouldRefresh(meta, entity, options) && isOptimisticLocking) {
|
|
533
623
|
return em.lockAndPopulate(meta, entity, where, options);
|
|
534
624
|
}
|
|
535
|
-
|
|
625
|
+
validateParams(where);
|
|
536
626
|
options.populate = await em.preparePopulate(entityName, options);
|
|
537
627
|
const cacheKey = em.cacheKey(entityName, options, 'em.findOne', where);
|
|
538
628
|
const cached = await em.tryCache(entityName, options.cache, cacheKey, options.refresh, true);
|
|
539
|
-
if (cached?.data) {
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
629
|
+
if (cached?.data !== undefined) {
|
|
630
|
+
if (cached.data) {
|
|
631
|
+
await em.entityLoader.populate(entityName, [cached.data], options.populate, {
|
|
632
|
+
...options,
|
|
633
|
+
...em.getPopulateWhere(where, options),
|
|
634
|
+
ignoreLazyScalarProperties: true,
|
|
635
|
+
lookup: false,
|
|
636
|
+
});
|
|
637
|
+
}
|
|
547
638
|
return cached.data;
|
|
548
639
|
}
|
|
549
640
|
options = { ...options };
|
|
550
641
|
// save the original hint value so we know it was infer/all
|
|
551
642
|
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
552
643
|
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
553
|
-
options.populateFilter = await this.getJoinedFilters(meta,
|
|
644
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
554
645
|
const data = await em.driver.findOne(entityName, where, {
|
|
555
646
|
ctx: em.transactionContext,
|
|
556
647
|
em,
|
|
@@ -661,26 +752,9 @@ export class EntityManager {
|
|
|
661
752
|
}
|
|
662
753
|
}
|
|
663
754
|
}
|
|
664
|
-
|
|
665
|
-
const propIndex = !isRaw(unique) && unique.findIndex(p => data[p] != null);
|
|
666
|
-
if (options.onConflictFields || where == null) {
|
|
667
|
-
if (propIndex !== false && propIndex >= 0) {
|
|
668
|
-
where = { [unique[propIndex]]: data[unique[propIndex]] };
|
|
669
|
-
}
|
|
670
|
-
else if (meta.uniques.length > 0) {
|
|
671
|
-
for (const u of meta.uniques) {
|
|
672
|
-
if (Utils.asArray(u.properties).every(p => data[p] != null)) {
|
|
673
|
-
where = Utils.asArray(u.properties).reduce((o, key) => {
|
|
674
|
-
o[key] = data[key];
|
|
675
|
-
return o;
|
|
676
|
-
}, {});
|
|
677
|
-
break;
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
}
|
|
755
|
+
where = getWhereCondition(meta, options.onConflictFields, data, where).where;
|
|
682
756
|
data = QueryHelper.processObjectParams(data);
|
|
683
|
-
|
|
757
|
+
validateParams(data, 'insert data');
|
|
684
758
|
if (em.eventManager.hasListeners(EventType.beforeUpsert, meta)) {
|
|
685
759
|
await em.eventManager.dispatchEvent(EventType.beforeUpsert, { entity: data, em, meta }, meta);
|
|
686
760
|
}
|
|
@@ -723,6 +797,7 @@ export class EntityManager {
|
|
|
723
797
|
ctx: em.transactionContext,
|
|
724
798
|
convertCustomTypes: true,
|
|
725
799
|
connectionType: 'write',
|
|
800
|
+
schema: options.schema,
|
|
726
801
|
});
|
|
727
802
|
em.getHydrator().hydrate(entity, meta, data2, em.entityFactory, 'full', false, true);
|
|
728
803
|
}
|
|
@@ -820,32 +895,18 @@ export class EntityManager {
|
|
|
820
895
|
}
|
|
821
896
|
}
|
|
822
897
|
}
|
|
823
|
-
const unique = meta.props.filter(p => p.unique).map(p => p.name);
|
|
824
|
-
propIndex = unique.findIndex(p =>
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
where = { [unique[propIndex]]: row[unique[propIndex]] };
|
|
828
|
-
}
|
|
829
|
-
else if (meta.uniques.length > 0) {
|
|
830
|
-
for (const u of meta.uniques) {
|
|
831
|
-
if (Utils.asArray(u.properties).every(p => row[p] != null)) {
|
|
832
|
-
where = Utils.asArray(u.properties).reduce((o, key) => {
|
|
833
|
-
o[key] = row[key];
|
|
834
|
-
return o;
|
|
835
|
-
}, {});
|
|
836
|
-
break;
|
|
837
|
-
}
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
row = QueryHelper.processObjectParams(row);
|
|
898
|
+
const unique = options.onConflictFields ?? meta.props.filter(p => p.unique).map(p => p.name);
|
|
899
|
+
propIndex = !isRaw(unique) && unique.findIndex(p => data[p] ?? data[p.substring(0, p.indexOf('.'))] != null);
|
|
900
|
+
const tmp = getWhereCondition(meta, options.onConflictFields, row, where);
|
|
901
|
+
propIndex = tmp.propIndex;
|
|
842
902
|
where = QueryHelper.processWhere({
|
|
843
|
-
where,
|
|
903
|
+
where: tmp.where,
|
|
844
904
|
entityName,
|
|
845
905
|
metadata: this.metadata,
|
|
846
906
|
platform: this.getPlatform(),
|
|
847
907
|
});
|
|
848
|
-
|
|
908
|
+
row = QueryHelper.processObjectParams(row);
|
|
909
|
+
validateParams(row, 'insert data');
|
|
849
910
|
allData.push(row);
|
|
850
911
|
allWhere.push(where);
|
|
851
912
|
}
|
|
@@ -886,7 +947,7 @@ export class EntityManager {
|
|
|
886
947
|
const reloadFields = returning.length > 0 && !(this.getPlatform().usesReturningStatement() && res.rows?.length);
|
|
887
948
|
if (options.onConflictAction === 'ignore' || (!res.rows?.length && loadPK.size > 0) || reloadFields) {
|
|
888
949
|
const unique = meta.hydrateProps.filter(p => !p.lazy).map(p => p.name);
|
|
889
|
-
const add = new Set(propIndex >= 0 ? [unique[propIndex]] : []);
|
|
950
|
+
const add = new Set(propIndex !== false && propIndex >= 0 ? [unique[propIndex]] : []);
|
|
890
951
|
for (const cond of loadPK.values()) {
|
|
891
952
|
Utils.keys(cond).forEach(key => add.add(key));
|
|
892
953
|
}
|
|
@@ -903,6 +964,7 @@ export class EntityManager {
|
|
|
903
964
|
ctx: em.transactionContext,
|
|
904
965
|
convertCustomTypes: true,
|
|
905
966
|
connectionType: 'write',
|
|
967
|
+
schema: options.schema,
|
|
906
968
|
});
|
|
907
969
|
for (const [entity, cond] of loadPK.entries()) {
|
|
908
970
|
const row = data2.find(row => {
|
|
@@ -914,7 +976,7 @@ export class EntityManager {
|
|
|
914
976
|
});
|
|
915
977
|
return this.comparator.matching(entityName, cond, tmp);
|
|
916
978
|
});
|
|
917
|
-
/* v8 ignore next
|
|
979
|
+
/* v8 ignore next */
|
|
918
980
|
if (!row) {
|
|
919
981
|
throw new Error(`Cannot find matching entity for condition ${JSON.stringify(cond)}`);
|
|
920
982
|
}
|
|
@@ -937,7 +999,7 @@ export class EntityManager {
|
|
|
937
999
|
}, {});
|
|
938
1000
|
return this.comparator.matching(entityName, cond, pk);
|
|
939
1001
|
});
|
|
940
|
-
/* v8 ignore next
|
|
1002
|
+
/* v8 ignore next */
|
|
941
1003
|
if (!row) {
|
|
942
1004
|
throw new Error(`Cannot find matching entity for condition ${JSON.stringify(cond)}`);
|
|
943
1005
|
}
|
|
@@ -959,45 +1021,37 @@ export class EntityManager {
|
|
|
959
1021
|
}
|
|
960
1022
|
/**
|
|
961
1023
|
* Runs your callback wrapped inside a database transaction.
|
|
1024
|
+
*
|
|
1025
|
+
* If a transaction is already active, a new savepoint (nested transaction) will be created by default. This behavior
|
|
1026
|
+
* can be controlled via the `propagation` option. Use the provided EntityManager instance for all operations that
|
|
1027
|
+
* should be part of the transaction. You can safely use a global EntityManager instance from a DI container, as this
|
|
1028
|
+
* method automatically creates an async context for the transaction.
|
|
1029
|
+
*
|
|
1030
|
+
* **Concurrency note:** When running multiple transactions concurrently (e.g. in parallel requests or jobs), use the
|
|
1031
|
+
* `clear: true` option. This ensures the callback runs in a clear fork of the EntityManager, providing full isolation
|
|
1032
|
+
* between concurrent transactional handlers. Using `clear: true` is an alternative to forking explicitly and calling
|
|
1033
|
+
* the method on the new fork – it already provides the necessary isolation for safe concurrent usage.
|
|
1034
|
+
*
|
|
1035
|
+
* **Propagation note:** Changes made within a transaction (whether top-level or nested) are always propagated to the
|
|
1036
|
+
* parent context, unless the parent context is a global one. If you want to avoid that, fork the EntityManager first
|
|
1037
|
+
* and then call this method on the fork.
|
|
1038
|
+
*
|
|
1039
|
+
* **Example:**
|
|
1040
|
+
* ```ts
|
|
1041
|
+
* await em.transactional(async (em) => {
|
|
1042
|
+
* const author = new Author('Jon');
|
|
1043
|
+
* em.persist(author);
|
|
1044
|
+
* // flush is called automatically at the end of the callback
|
|
1045
|
+
* });
|
|
1046
|
+
* ```
|
|
962
1047
|
*/
|
|
963
1048
|
async transactional(cb, options = {}) {
|
|
964
1049
|
const em = this.getContext(false);
|
|
965
1050
|
if (this.disableTransactions || em.disableTransactions) {
|
|
966
1051
|
return cb(em);
|
|
967
1052
|
}
|
|
968
|
-
const
|
|
969
|
-
|
|
970
|
-
flushMode: options.flushMode,
|
|
971
|
-
cloneEventManager: true,
|
|
972
|
-
disableTransactions: options.ignoreNestedTransactions,
|
|
973
|
-
loggerContext: options.loggerContext,
|
|
974
|
-
});
|
|
975
|
-
options.ctx ??= em.transactionContext;
|
|
976
|
-
const propagateToUpperContext = !em.global || this.config.get('allowGlobalContext');
|
|
977
|
-
return TransactionContext.create(fork, async () => {
|
|
978
|
-
return fork.getConnection().transactional(async (trx) => {
|
|
979
|
-
fork.transactionContext = trx;
|
|
980
|
-
if (propagateToUpperContext) {
|
|
981
|
-
fork.eventManager.registerSubscriber({
|
|
982
|
-
afterFlush(args) {
|
|
983
|
-
args.uow.getChangeSets()
|
|
984
|
-
.filter(cs => [ChangeSetType.DELETE, ChangeSetType.DELETE_EARLY].includes(cs.type))
|
|
985
|
-
.forEach(cs => em.unitOfWork.unsetIdentity(cs.entity));
|
|
986
|
-
},
|
|
987
|
-
});
|
|
988
|
-
}
|
|
989
|
-
const ret = await cb(fork);
|
|
990
|
-
await fork.flush();
|
|
991
|
-
if (propagateToUpperContext) {
|
|
992
|
-
// ensure all entities from inner context are merged to the upper one
|
|
993
|
-
for (const entity of fork.unitOfWork.getIdentityMap()) {
|
|
994
|
-
em.unitOfWork.register(entity);
|
|
995
|
-
entity.__helper.__em = em;
|
|
996
|
-
}
|
|
997
|
-
}
|
|
998
|
-
return ret;
|
|
999
|
-
}, { ...options, eventBroadcaster: new TransactionEventBroadcaster(fork, { topLevelTransaction: !options.ctx }) });
|
|
1000
|
-
});
|
|
1053
|
+
const manager = new TransactionManager(this);
|
|
1054
|
+
return manager.handle(cb, options);
|
|
1001
1055
|
}
|
|
1002
1056
|
/**
|
|
1003
1057
|
* Starts new transaction bound to this EntityManager. Use `ctx` parameter to provide the parent when nesting transactions.
|
|
@@ -1081,7 +1135,7 @@ export class EntityManager {
|
|
|
1081
1135
|
return cs.getPrimaryKey();
|
|
1082
1136
|
}
|
|
1083
1137
|
data = QueryHelper.processObjectParams(data);
|
|
1084
|
-
|
|
1138
|
+
validateParams(data, 'insert data');
|
|
1085
1139
|
const res = await em.driver.nativeInsert(entityName, data, { ctx: em.transactionContext, ...options });
|
|
1086
1140
|
return res.insertId;
|
|
1087
1141
|
}
|
|
@@ -1121,7 +1175,7 @@ export class EntityManager {
|
|
|
1121
1175
|
return css.map(cs => cs.getPrimaryKey());
|
|
1122
1176
|
}
|
|
1123
1177
|
data = data.map(row => QueryHelper.processObjectParams(row));
|
|
1124
|
-
data.forEach(row =>
|
|
1178
|
+
data.forEach(row => validateParams(row, 'insert data'));
|
|
1125
1179
|
const res = await em.driver.nativeInsertMany(entityName, data, { ctx: em.transactionContext, ...options });
|
|
1126
1180
|
if (res.insertedIds) {
|
|
1127
1181
|
return res.insertedIds;
|
|
@@ -1137,8 +1191,8 @@ export class EntityManager {
|
|
|
1137
1191
|
entityName = Utils.className(entityName);
|
|
1138
1192
|
data = QueryHelper.processObjectParams(data);
|
|
1139
1193
|
where = await em.processWhere(entityName, where, { ...options, convertCustomTypes: false }, 'update');
|
|
1140
|
-
|
|
1141
|
-
|
|
1194
|
+
validateParams(data, 'update data');
|
|
1195
|
+
validateParams(where, 'update condition');
|
|
1142
1196
|
const res = await em.driver.nativeUpdate(entityName, where, data, { ctx: em.transactionContext, ...options });
|
|
1143
1197
|
return res.affectedRows;
|
|
1144
1198
|
}
|
|
@@ -1150,7 +1204,7 @@ export class EntityManager {
|
|
|
1150
1204
|
em.prepareOptions(options);
|
|
1151
1205
|
entityName = Utils.className(entityName);
|
|
1152
1206
|
where = await em.processWhere(entityName, where, options, 'delete');
|
|
1153
|
-
|
|
1207
|
+
validateParams(where, 'delete condition');
|
|
1154
1208
|
const res = await em.driver.nativeDelete(entityName, where, { ctx: em.transactionContext, ...options });
|
|
1155
1209
|
return res.affectedRows;
|
|
1156
1210
|
}
|
|
@@ -1161,15 +1215,17 @@ export class EntityManager {
|
|
|
1161
1215
|
entityName = Utils.className(entityName);
|
|
1162
1216
|
const meta = this.metadata.get(entityName);
|
|
1163
1217
|
const data = this.driver.mapResult(result, meta);
|
|
1164
|
-
Object.keys(data)
|
|
1218
|
+
for (const k of Object.keys(data)) {
|
|
1165
1219
|
const prop = meta.properties[k];
|
|
1166
|
-
if (prop
|
|
1167
|
-
|
|
1220
|
+
if (prop?.kind === ReferenceKind.SCALAR && SCALAR_TYPES.has(prop.runtimeType) && !prop.customType && (prop.setter || !prop.getter)) {
|
|
1221
|
+
validateProperty(prop, data[k], data);
|
|
1168
1222
|
}
|
|
1169
|
-
}
|
|
1223
|
+
}
|
|
1170
1224
|
return this.merge(entityName, data, {
|
|
1171
1225
|
convertCustomTypes: true,
|
|
1172
|
-
refresh: true,
|
|
1226
|
+
refresh: true,
|
|
1227
|
+
validate: false,
|
|
1228
|
+
...options,
|
|
1173
1229
|
});
|
|
1174
1230
|
}
|
|
1175
1231
|
/**
|
|
@@ -1177,22 +1233,23 @@ export class EntityManager {
|
|
|
1177
1233
|
* via second parameter. By default, it will return already loaded entities without modifying them.
|
|
1178
1234
|
*/
|
|
1179
1235
|
merge(entityName, data, options = {}) {
|
|
1180
|
-
const em = this.getContext();
|
|
1181
1236
|
if (Utils.isEntity(entityName)) {
|
|
1182
|
-
return
|
|
1237
|
+
return this.merge(entityName.constructor.name, entityName, data);
|
|
1183
1238
|
}
|
|
1239
|
+
const em = options.disableContextResolution ? this : this.getContext();
|
|
1184
1240
|
options.schema ??= em._schema;
|
|
1241
|
+
options.validate ??= true;
|
|
1242
|
+
options.cascade ??= true;
|
|
1185
1243
|
entityName = Utils.className(entityName);
|
|
1186
|
-
|
|
1244
|
+
validatePrimaryKey(data, em.metadata.get(entityName));
|
|
1187
1245
|
let entity = em.unitOfWork.tryGetById(entityName, data, options.schema, false);
|
|
1188
1246
|
if (entity && helper(entity).__managed && helper(entity).__initialized && !options.refresh) {
|
|
1189
1247
|
return entity;
|
|
1190
1248
|
}
|
|
1191
|
-
const
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
em.
|
|
1195
|
-
em.unitOfWork.merge(entity);
|
|
1249
|
+
const dataIsEntity = Utils.isEntity(data);
|
|
1250
|
+
entity = dataIsEntity ? data : em.entityFactory.create(entityName, data, { merge: true, ...options });
|
|
1251
|
+
const visited = options.cascade ? undefined : new Set([entity]);
|
|
1252
|
+
em.unitOfWork.merge(entity, visited);
|
|
1196
1253
|
return entity;
|
|
1197
1254
|
}
|
|
1198
1255
|
/**
|
|
@@ -1217,6 +1274,7 @@ export class EntityManager {
|
|
|
1217
1274
|
...options,
|
|
1218
1275
|
newEntity: !options.managed,
|
|
1219
1276
|
merge: options.managed,
|
|
1277
|
+
normalizeAccessors: true,
|
|
1220
1278
|
});
|
|
1221
1279
|
options.persist ??= em.config.get('persistOnCreate');
|
|
1222
1280
|
if (options.persist && !this.getMetadata(entityName).embeddable) {
|
|
@@ -1255,10 +1313,8 @@ export class EntityManager {
|
|
|
1255
1313
|
async count(entityName, where = {}, options = {}) {
|
|
1256
1314
|
const em = this.getContext(false);
|
|
1257
1315
|
// Shallow copy options since the object will be modified when deleting orderBy
|
|
1258
|
-
options = {
|
|
1259
|
-
|
|
1260
|
-
...options,
|
|
1261
|
-
};
|
|
1316
|
+
options = { ...options };
|
|
1317
|
+
em.prepareOptions(options);
|
|
1262
1318
|
entityName = Utils.className(entityName);
|
|
1263
1319
|
await em.tryFlush(entityName, options);
|
|
1264
1320
|
where = await em.processWhere(entityName, where, options, 'read');
|
|
@@ -1268,15 +1324,15 @@ export class EntityManager {
|
|
|
1268
1324
|
const meta = em.metadata.find(entityName);
|
|
1269
1325
|
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
1270
1326
|
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
1271
|
-
options.populateFilter = await this.getJoinedFilters(meta,
|
|
1272
|
-
|
|
1327
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
1328
|
+
validateParams(where);
|
|
1273
1329
|
delete options.orderBy;
|
|
1274
1330
|
const cacheKey = em.cacheKey(entityName, options, 'em.count', where);
|
|
1275
1331
|
const cached = await em.tryCache(entityName, options.cache, cacheKey);
|
|
1276
|
-
if (cached?.data) {
|
|
1332
|
+
if (cached?.data !== undefined) {
|
|
1277
1333
|
return cached.data;
|
|
1278
1334
|
}
|
|
1279
|
-
const count = await em.driver.count(entityName, where, { ctx: em.transactionContext, ...options });
|
|
1335
|
+
const count = await em.driver.count(entityName, where, { ctx: em.transactionContext, em, ...options });
|
|
1280
1336
|
await em.storeCache(options.cache, cached, () => +count);
|
|
1281
1337
|
return +count;
|
|
1282
1338
|
}
|
|
@@ -1303,13 +1359,6 @@ export class EntityManager {
|
|
|
1303
1359
|
}
|
|
1304
1360
|
return this;
|
|
1305
1361
|
}
|
|
1306
|
-
/**
|
|
1307
|
-
* Persists your entity immediately, flushing all not yet persisted changes to the database too.
|
|
1308
|
-
* Equivalent to `em.persist(e).flush()`.
|
|
1309
|
-
*/
|
|
1310
|
-
async persistAndFlush(entity) {
|
|
1311
|
-
await this.persist(entity).flush();
|
|
1312
|
-
}
|
|
1313
1362
|
/**
|
|
1314
1363
|
* Marks entity for removal.
|
|
1315
1364
|
* A removed entity will be removed from the database at or before transaction commit or as a result of the flush operation.
|
|
@@ -1333,13 +1382,6 @@ export class EntityManager {
|
|
|
1333
1382
|
}
|
|
1334
1383
|
return em;
|
|
1335
1384
|
}
|
|
1336
|
-
/**
|
|
1337
|
-
* Removes an entity instance immediately, flushing all not yet persisted changes to the database too.
|
|
1338
|
-
* Equivalent to `em.remove(e).flush()`
|
|
1339
|
-
*/
|
|
1340
|
-
async removeAndFlush(entity) {
|
|
1341
|
-
await this.remove(entity).flush();
|
|
1342
|
-
}
|
|
1343
1385
|
/**
|
|
1344
1386
|
* Flushes all changes to objects that have been queued up to now to the database.
|
|
1345
1387
|
* This effectively synchronizes the in-memory state of managed objects with the database.
|
|
@@ -1402,7 +1444,7 @@ export class EntityManager {
|
|
|
1402
1444
|
const em = this.getContext();
|
|
1403
1445
|
em.prepareOptions(options);
|
|
1404
1446
|
const entityName = arr[0].constructor.name;
|
|
1405
|
-
const preparedPopulate = await em.preparePopulate(entityName, { populate: populate }, options.validate);
|
|
1447
|
+
const preparedPopulate = await em.preparePopulate(entityName, { populate: populate, filters: options.filters }, options.validate);
|
|
1406
1448
|
await em.entityLoader.populate(entityName, arr, preparedPopulate, options);
|
|
1407
1449
|
return entities;
|
|
1408
1450
|
}
|
|
@@ -1438,6 +1480,9 @@ export class EntityManager {
|
|
|
1438
1480
|
for (const entity of em.unitOfWork.getIdentityMap()) {
|
|
1439
1481
|
fork.unitOfWork.register(entity);
|
|
1440
1482
|
}
|
|
1483
|
+
for (const entity of em.unitOfWork.getPersistStack()) {
|
|
1484
|
+
fork.unitOfWork.persist(entity);
|
|
1485
|
+
}
|
|
1441
1486
|
for (const entity of em.unitOfWork.getOrphanRemoveStack()) {
|
|
1442
1487
|
fork.unitOfWork.getOrphanRemoveStack().add(entity);
|
|
1443
1488
|
}
|
|
@@ -1459,6 +1504,12 @@ export class EntityManager {
|
|
|
1459
1504
|
getEntityFactory() {
|
|
1460
1505
|
return this.getContext().entityFactory;
|
|
1461
1506
|
}
|
|
1507
|
+
/**
|
|
1508
|
+
* @internal use `em.populate()` as the user facing API, this is exposed only for internal usage
|
|
1509
|
+
*/
|
|
1510
|
+
getEntityLoader() {
|
|
1511
|
+
return this.getContext().entityLoader;
|
|
1512
|
+
}
|
|
1462
1513
|
/**
|
|
1463
1514
|
* Gets the Hydrator used by the EntityManager.
|
|
1464
1515
|
*/
|
|
@@ -1555,7 +1606,6 @@ export class EntityManager {
|
|
|
1555
1606
|
...options,
|
|
1556
1607
|
...this.getPopulateWhere(where, options),
|
|
1557
1608
|
orderBy: options.populateOrderBy ?? options.orderBy,
|
|
1558
|
-
convertCustomTypes: false,
|
|
1559
1609
|
ignoreLazyScalarProperties: true,
|
|
1560
1610
|
lookup: false,
|
|
1561
1611
|
});
|
|
@@ -1618,7 +1668,7 @@ export class EntityManager {
|
|
|
1618
1668
|
}
|
|
1619
1669
|
if (typeof options.populate !== 'boolean') {
|
|
1620
1670
|
options.populate = Utils.asArray(options.populate).map(field => {
|
|
1621
|
-
/* v8 ignore next
|
|
1671
|
+
/* v8 ignore next */
|
|
1622
1672
|
if (typeof field === 'boolean' || field === PopulatePath.ALL) {
|
|
1623
1673
|
return [{ field: meta.primaryKeys[0], strategy: options.strategy, all: !!field }]; //
|
|
1624
1674
|
}
|
|
@@ -1628,7 +1678,7 @@ export class EntityManager {
|
|
|
1628
1678
|
options.flags.push(QueryFlag.INFER_POPULATE);
|
|
1629
1679
|
return [];
|
|
1630
1680
|
}
|
|
1631
|
-
if (
|
|
1681
|
+
if (typeof field === 'string') {
|
|
1632
1682
|
return [{ field, strategy: options.strategy }];
|
|
1633
1683
|
}
|
|
1634
1684
|
return [field];
|
|
@@ -1678,7 +1728,7 @@ export class EntityManager {
|
|
|
1678
1728
|
throw new ValidationError(`Cannot combine 'fields' and 'exclude' option.`);
|
|
1679
1729
|
}
|
|
1680
1730
|
options.schema ??= this._schema;
|
|
1681
|
-
options.logging = Utils.merge({ id: this.id }, this.loggerContext, options.loggerContext, options.logging);
|
|
1731
|
+
options.logging = options.loggerContext = Utils.merge({ id: this.id }, this.loggerContext, options.loggerContext, options.logging);
|
|
1682
1732
|
}
|
|
1683
1733
|
/**
|
|
1684
1734
|
* @internal
|
|
@@ -1702,31 +1752,31 @@ export class EntityManager {
|
|
|
1702
1752
|
const em = this.getContext();
|
|
1703
1753
|
const cacheKey = Array.isArray(config) ? config[0] : JSON.stringify(key);
|
|
1704
1754
|
const cached = await em.resultCache.get(cacheKey);
|
|
1705
|
-
if (cached) {
|
|
1706
|
-
|
|
1707
|
-
if (Array.isArray(cached) && merge) {
|
|
1708
|
-
data = cached.map(item => em.entityFactory.create(entityName, item, {
|
|
1709
|
-
merge: true,
|
|
1710
|
-
convertCustomTypes: true,
|
|
1711
|
-
refresh,
|
|
1712
|
-
recomputeSnapshot: true,
|
|
1713
|
-
}));
|
|
1714
|
-
}
|
|
1715
|
-
else if (Utils.isObject(cached) && merge) {
|
|
1716
|
-
data = em.entityFactory.create(entityName, cached, {
|
|
1717
|
-
merge: true,
|
|
1718
|
-
convertCustomTypes: true,
|
|
1719
|
-
refresh,
|
|
1720
|
-
recomputeSnapshot: true,
|
|
1721
|
-
});
|
|
1722
|
-
}
|
|
1723
|
-
else {
|
|
1724
|
-
data = cached;
|
|
1725
|
-
}
|
|
1726
|
-
await em.unitOfWork.dispatchOnLoadEvent();
|
|
1727
|
-
return { key: cacheKey, data };
|
|
1755
|
+
if (!cached) {
|
|
1756
|
+
return { key: cacheKey, data: cached };
|
|
1728
1757
|
}
|
|
1729
|
-
|
|
1758
|
+
let data;
|
|
1759
|
+
if (Array.isArray(cached) && merge) {
|
|
1760
|
+
data = cached.map(item => em.entityFactory.create(entityName, item, {
|
|
1761
|
+
merge: true,
|
|
1762
|
+
convertCustomTypes: true,
|
|
1763
|
+
refresh,
|
|
1764
|
+
recomputeSnapshot: true,
|
|
1765
|
+
}));
|
|
1766
|
+
}
|
|
1767
|
+
else if (Utils.isObject(cached) && merge) {
|
|
1768
|
+
data = em.entityFactory.create(entityName, cached, {
|
|
1769
|
+
merge: true,
|
|
1770
|
+
convertCustomTypes: true,
|
|
1771
|
+
refresh,
|
|
1772
|
+
recomputeSnapshot: true,
|
|
1773
|
+
});
|
|
1774
|
+
}
|
|
1775
|
+
else {
|
|
1776
|
+
data = cached;
|
|
1777
|
+
}
|
|
1778
|
+
await em.unitOfWork.dispatchOnLoadEvent();
|
|
1779
|
+
return { key: cacheKey, data };
|
|
1730
1780
|
}
|
|
1731
1781
|
/**
|
|
1732
1782
|
* @internal
|
|
@@ -1735,7 +1785,7 @@ export class EntityManager {
|
|
|
1735
1785
|
config ??= this.config.get('resultCache').global;
|
|
1736
1786
|
if (config) {
|
|
1737
1787
|
const em = this.getContext();
|
|
1738
|
-
const expiration = Array.isArray(config) ? config[1] : (
|
|
1788
|
+
const expiration = Array.isArray(config) ? config[1] : (typeof config === 'number' ? config : undefined);
|
|
1739
1789
|
await em.resultCache.set(key.key, data instanceof Function ? data() : data, '', expiration);
|
|
1740
1790
|
}
|
|
1741
1791
|
}
|
|
@@ -1768,6 +1818,19 @@ export class EntityManager {
|
|
|
1768
1818
|
set schema(schema) {
|
|
1769
1819
|
this.getContext(false)._schema = schema ?? undefined;
|
|
1770
1820
|
}
|
|
1821
|
+
/** @internal */
|
|
1822
|
+
async getDataLoader(type) {
|
|
1823
|
+
const em = this.getContext();
|
|
1824
|
+
if (em.loaders[type]) {
|
|
1825
|
+
return em.loaders[type];
|
|
1826
|
+
}
|
|
1827
|
+
const DataLoader = await DataloaderUtils.getDataLoader();
|
|
1828
|
+
switch (type) {
|
|
1829
|
+
case 'ref': return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getRefBatchLoadFn(em)));
|
|
1830
|
+
case '1:m': return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getColBatchLoadFn(em)));
|
|
1831
|
+
case 'm:n': return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getManyToManyColBatchLoadFn(em)));
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1771
1834
|
/**
|
|
1772
1835
|
* Returns the ID of this EntityManager. Respects the context, so global EM will give you the contextual ID
|
|
1773
1836
|
* if executed inside request context handler.
|
|
@@ -1776,7 +1839,7 @@ export class EntityManager {
|
|
|
1776
1839
|
return this.getContext(false)._id;
|
|
1777
1840
|
}
|
|
1778
1841
|
/** @ignore */
|
|
1779
|
-
[inspect.custom]() {
|
|
1842
|
+
[Symbol.for('nodejs.util.inspect.custom')]() {
|
|
1780
1843
|
return `[EntityManager<${this.id}>]`;
|
|
1781
1844
|
}
|
|
1782
1845
|
}
|