@mikro-orm/core 6.5.10-dev.2 → 6.5.10-dev.20
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 +10 -7
- package/EntityManager.js +65 -45
- package/entity/EntityFactory.js +1 -0
- package/entity/EntityLoader.js +19 -1
- package/index.d.ts +1 -1
- package/naming-strategy/AbstractNamingStrategy.d.ts +5 -1
- package/naming-strategy/AbstractNamingStrategy.js +7 -1
- package/naming-strategy/NamingStrategy.d.ts +11 -1
- package/package.json +2 -2
- package/typings.d.ts +6 -1
- package/unit-of-work/UnitOfWork.d.ts +6 -0
- package/unit-of-work/UnitOfWork.js +37 -23
- package/utils/Configuration.d.ts +4 -0
- package/utils/Configuration.js +9 -0
- package/utils/RawQueryFragment.d.ts +2 -2
- package/utils/Utils.js +2 -2
package/EntityManager.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { type Configuration, Cursor } from './utils';
|
|
|
4
4
|
import { type AssignOptions, EntityFactory, EntityLoader, type EntityLoaderOptions, type EntityRepository, EntityValidator, Reference } from './entity';
|
|
5
5
|
import { UnitOfWork } from './unit-of-work';
|
|
6
6
|
import type { CountOptions, DeleteOptions, FindAllOptions, FindByCursorOptions, FindOneOptions, FindOneOrFailOptions, FindOptions, GetReferenceOptions, IDatabaseDriver, LockOptions, NativeInsertUpdateOptions, UpdateOptions, UpsertManyOptions, UpsertOptions } from './drivers';
|
|
7
|
-
import type { AnyEntity, AnyString, ArrayElement, AutoPath, ConnectionType, Dictionary, EntityData, EntityDictionary, EntityDTO, EntityMetadata, EntityName, FilterQuery, FromEntityType, GetRepository, IHydrator, IsSubset, Loaded, MaybePromise, MergeLoaded, MergeSelected, NoInfer, ObjectQuery, Primary, Ref, RequiredEntityData, UnboxArray } from './typings';
|
|
7
|
+
import type { AnyEntity, AnyString, ArrayElement, AutoPath, ConnectionType, Dictionary, EntityData, EntityDictionary, EntityDTO, EntityMetadata, EntityName, FilterDef, FilterQuery, FromEntityType, GetRepository, IHydrator, IsSubset, Loaded, MaybePromise, MergeLoaded, MergeSelected, NoInfer, ObjectQuery, Primary, Ref, RequiredEntityData, UnboxArray } from './typings';
|
|
8
8
|
import { FlushMode, LockMode, PopulatePath, type TransactionOptions } from './enums';
|
|
9
9
|
import type { MetadataStorage } from './metadata';
|
|
10
10
|
import type { Transaction } from './connections';
|
|
@@ -83,19 +83,19 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
|
|
|
83
83
|
/**
|
|
84
84
|
* Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
|
|
85
85
|
*/
|
|
86
|
-
addFilter<T1>(name: string, cond: FilterQuery<T1> | ((args: Dictionary) => MaybePromise<FilterQuery<T1>>), entityName?: EntityName<T1> | [EntityName<T1>],
|
|
86
|
+
addFilter<T1>(name: string, cond: FilterQuery<T1> | ((args: Dictionary) => MaybePromise<FilterQuery<T1>>), entityName?: EntityName<T1> | [EntityName<T1>], options?: boolean | Partial<FilterDef>): void;
|
|
87
87
|
/**
|
|
88
88
|
* Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
|
|
89
89
|
*/
|
|
90
|
-
addFilter<T1, T2>(name: string, cond: FilterQuery<T1 | T2> | ((args: Dictionary) => MaybePromise<FilterQuery<T1 | T2>>), entityName?: [EntityName<T1>, EntityName<T2>],
|
|
90
|
+
addFilter<T1, T2>(name: string, cond: FilterQuery<T1 | T2> | ((args: Dictionary) => MaybePromise<FilterQuery<T1 | T2>>), entityName?: [EntityName<T1>, EntityName<T2>], options?: boolean | Partial<FilterDef>): void;
|
|
91
91
|
/**
|
|
92
92
|
* Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
|
|
93
93
|
*/
|
|
94
|
-
addFilter<T1, T2, T3>(name: string, cond: FilterQuery<T1 | T2 | T3> | ((args: Dictionary) => MaybePromise<FilterQuery<T1 | T2 | T3>>), entityName?: [EntityName<T1>, EntityName<T2>, EntityName<T3>],
|
|
94
|
+
addFilter<T1, T2, T3>(name: string, cond: FilterQuery<T1 | T2 | T3> | ((args: Dictionary) => MaybePromise<FilterQuery<T1 | T2 | T3>>), entityName?: [EntityName<T1>, EntityName<T2>, EntityName<T3>], options?: boolean | Partial<FilterDef>): void;
|
|
95
95
|
/**
|
|
96
96
|
* Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
|
|
97
97
|
*/
|
|
98
|
-
addFilter(name: string, cond: Dictionary | ((args: Dictionary) => MaybePromise<FilterQuery<AnyEntity>>), entityName?: EntityName<AnyEntity> | EntityName<AnyEntity>[],
|
|
98
|
+
addFilter(name: string, cond: Dictionary | ((args: Dictionary) => MaybePromise<FilterQuery<AnyEntity>>), entityName?: EntityName<AnyEntity> | EntityName<AnyEntity>[], options?: boolean | Partial<FilterDef>): void;
|
|
99
99
|
/**
|
|
100
100
|
* Sets filter parameter values globally inside context defined by this entity manager.
|
|
101
101
|
* If you want to set shared value for all contexts, be sure to use the root entity manager.
|
|
@@ -119,11 +119,14 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
|
|
|
119
119
|
protected processWhere<Entity extends object, Hint extends string = never, Fields extends string = '*', Excludes extends string = never>(entityName: string, where: FilterQuery<Entity>, options: FindOptions<Entity, Hint, Fields, Excludes> | FindOneOptions<Entity, Hint, Fields, Excludes>, type: 'read' | 'update' | 'delete'): Promise<FilterQuery<Entity>>;
|
|
120
120
|
protected applyDiscriminatorCondition<Entity extends object>(entityName: string, where: FilterQuery<Entity>): FilterQuery<Entity>;
|
|
121
121
|
protected createPopulateWhere<Entity extends object>(cond: ObjectQuery<Entity>, options: FindOptions<Entity, any, any, any> | FindOneOptions<Entity, any, any, any> | CountOptions<Entity, any>): ObjectQuery<Entity>;
|
|
122
|
-
protected getJoinedFilters<Entity extends object>(meta: EntityMetadata<Entity>,
|
|
122
|
+
protected getJoinedFilters<Entity extends object>(meta: EntityMetadata<Entity>, options: FindOptions<Entity, any, any, any> | FindOneOptions<Entity, any, any, any>): Promise<ObjectQuery<Entity> | undefined>;
|
|
123
123
|
/**
|
|
124
124
|
* 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.
|
|
125
125
|
*/
|
|
126
|
-
protected autoJoinRefsForFilters<T extends object>(meta: EntityMetadata<T>, options: FindOptions<T, any, any, any> | FindOneOptions<T, any, any, any
|
|
126
|
+
protected autoJoinRefsForFilters<T extends object>(meta: EntityMetadata<T>, options: FindOptions<T, any, any, any> | FindOneOptions<T, any, any, any>, parent?: {
|
|
127
|
+
className: string;
|
|
128
|
+
propName: string;
|
|
129
|
+
}): Promise<void>;
|
|
127
130
|
/**
|
|
128
131
|
* @internal
|
|
129
132
|
*/
|
package/EntityManager.js
CHANGED
|
@@ -143,7 +143,7 @@ class EntityManager {
|
|
|
143
143
|
// save the original hint value so we know it was infer/all
|
|
144
144
|
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
145
145
|
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
146
|
-
options.populateFilter = await this.getJoinedFilters(meta,
|
|
146
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
147
147
|
const results = await em.driver.find(entityName, where, { ctx: em.transactionContext, em, ...options });
|
|
148
148
|
if (results.length === 0) {
|
|
149
149
|
await em.storeCache(options.cache, cached, []);
|
|
@@ -197,8 +197,8 @@ class EntityManager {
|
|
|
197
197
|
/**
|
|
198
198
|
* Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
|
|
199
199
|
*/
|
|
200
|
-
addFilter(name, cond, entityName,
|
|
201
|
-
|
|
200
|
+
addFilter(name, cond, entityName, options = true) {
|
|
201
|
+
options = typeof options === 'object' ? { name, cond, default: true, ...options } : { name, cond, default: options };
|
|
202
202
|
if (entityName) {
|
|
203
203
|
options.entity = utils_1.Utils.asArray(entityName).map(n => utils_1.Utils.className(n));
|
|
204
204
|
}
|
|
@@ -277,29 +277,37 @@ class EntityManager {
|
|
|
277
277
|
}
|
|
278
278
|
return ret;
|
|
279
279
|
}
|
|
280
|
-
async getJoinedFilters(meta,
|
|
280
|
+
async getJoinedFilters(meta, options) {
|
|
281
|
+
if (!this.config.get('filtersOnRelations') || !options.populate) {
|
|
282
|
+
return undefined;
|
|
283
|
+
}
|
|
281
284
|
const ret = {};
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
285
|
+
for (const hint of options.populate) {
|
|
286
|
+
const field = hint.field.split(':')[0];
|
|
287
|
+
const prop = meta.properties[field];
|
|
288
|
+
const strategy = (0, utils_2.getLoadingStrategy)(prop.strategy || hint.strategy || options.strategy || this.config.get('loadStrategy'), prop.kind);
|
|
289
|
+
const joined = strategy === enums_1.LoadStrategy.JOINED && prop.kind !== enums_1.ReferenceKind.SCALAR;
|
|
290
|
+
if (!joined && !hint.filter) {
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
const where = await this.applyFilters(prop.type, {}, options.filters ?? {}, 'read', {
|
|
294
|
+
...options,
|
|
295
|
+
populate: hint.children,
|
|
296
|
+
});
|
|
297
|
+
const where2 = await this.getJoinedFilters(prop.targetMeta, {
|
|
298
|
+
...options,
|
|
299
|
+
populate: hint.children,
|
|
300
|
+
populateWhere: enums_1.PopulateHint.ALL,
|
|
301
|
+
});
|
|
302
|
+
if (utils_1.Utils.hasObjectKeys(where)) {
|
|
303
|
+
ret[field] = ret[field] ? { $and: [where, ret[field]] } : where;
|
|
304
|
+
}
|
|
305
|
+
if (where2 && utils_1.Utils.hasObjectKeys(where2)) {
|
|
306
|
+
if (ret[field]) {
|
|
307
|
+
utils_1.Utils.merge(ret[field], where2);
|
|
295
308
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
utils_1.Utils.merge(ret[field], where2);
|
|
299
|
-
}
|
|
300
|
-
else {
|
|
301
|
-
ret[field] = where2;
|
|
302
|
-
}
|
|
309
|
+
else {
|
|
310
|
+
ret[field] = where2;
|
|
303
311
|
}
|
|
304
312
|
}
|
|
305
313
|
}
|
|
@@ -308,29 +316,29 @@ class EntityManager {
|
|
|
308
316
|
/**
|
|
309
317
|
* 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.
|
|
310
318
|
*/
|
|
311
|
-
async autoJoinRefsForFilters(meta, options) {
|
|
312
|
-
if (!meta || !this.config.get('autoJoinRefsForFilters')) {
|
|
319
|
+
async autoJoinRefsForFilters(meta, options, parent) {
|
|
320
|
+
if (!meta || !this.config.get('autoJoinRefsForFilters') || options.filters === false) {
|
|
313
321
|
return;
|
|
314
322
|
}
|
|
315
|
-
const props = meta.relations.filter(prop => {
|
|
316
|
-
return !prop.object && [enums_1.ReferenceKind.MANY_TO_ONE, enums_1.ReferenceKind.ONE_TO_ONE].includes(prop.kind)
|
|
317
|
-
&& ((options.fields?.length ?? 0) === 0 || options.fields?.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)));
|
|
318
|
-
});
|
|
319
323
|
const ret = options.populate;
|
|
320
|
-
for (const prop of
|
|
324
|
+
for (const prop of meta.relations) {
|
|
325
|
+
if (prop.object
|
|
326
|
+
|| ![enums_1.ReferenceKind.MANY_TO_ONE, enums_1.ReferenceKind.ONE_TO_ONE].includes(prop.kind)
|
|
327
|
+
|| !((options.fields?.length ?? 0) === 0 || options.fields?.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
|
|
328
|
+
|| (parent?.className === prop.targetMeta.root.className && parent.propName === prop.inversedBy)) {
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
321
331
|
const cond = await this.applyFilters(prop.type, {}, options.filters ?? {}, 'read', options);
|
|
322
332
|
if (!utils_1.Utils.isEmpty(cond)) {
|
|
323
333
|
const populated = options.populate.filter(({ field }) => field.split(':')[0] === prop.name);
|
|
324
334
|
let found = false;
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
found = true;
|
|
333
|
-
}
|
|
335
|
+
for (const hint of populated) {
|
|
336
|
+
if (!hint.all) {
|
|
337
|
+
hint.filter = true;
|
|
338
|
+
}
|
|
339
|
+
const strategy = (0, utils_2.getLoadingStrategy)(prop.strategy || hint.strategy || options.strategy || this.config.get('loadStrategy'), prop.kind);
|
|
340
|
+
if (hint.field === `${prop.name}:ref` || (hint.filter && strategy === enums_1.LoadStrategy.JOINED)) {
|
|
341
|
+
found = true;
|
|
334
342
|
}
|
|
335
343
|
}
|
|
336
344
|
if (!found) {
|
|
@@ -338,6 +346,14 @@ class EntityManager {
|
|
|
338
346
|
}
|
|
339
347
|
}
|
|
340
348
|
}
|
|
349
|
+
for (const hint of ret) {
|
|
350
|
+
const [field, ref] = hint.field.split(':');
|
|
351
|
+
const prop = meta?.properties[field];
|
|
352
|
+
if (prop && !ref) {
|
|
353
|
+
hint.children ??= [];
|
|
354
|
+
await this.autoJoinRefsForFilters(prop.targetMeta, { ...options, populate: hint.children }, { className: meta.root.className, propName: prop.name });
|
|
355
|
+
}
|
|
356
|
+
}
|
|
341
357
|
}
|
|
342
358
|
/**
|
|
343
359
|
* @internal
|
|
@@ -376,13 +392,17 @@ class EntityManager {
|
|
|
376
392
|
else {
|
|
377
393
|
cond = filter.cond;
|
|
378
394
|
}
|
|
379
|
-
|
|
395
|
+
cond = utils_1.QueryHelper.processWhere({
|
|
380
396
|
where: cond,
|
|
381
397
|
entityName,
|
|
382
398
|
metadata: this.metadata,
|
|
383
399
|
platform: this.driver.getPlatform(),
|
|
384
400
|
aliased: type === 'read',
|
|
385
|
-
})
|
|
401
|
+
});
|
|
402
|
+
if (filter.strict) {
|
|
403
|
+
Object.defineProperty(cond, '__strict', { value: filter.strict, enumerable: false });
|
|
404
|
+
}
|
|
405
|
+
ret.push(cond);
|
|
386
406
|
}
|
|
387
407
|
const conds = [...ret, where].filter(c => utils_1.Utils.hasObjectKeys(c));
|
|
388
408
|
return conds.length > 1 ? { $and: conds } : conds[0];
|
|
@@ -568,7 +588,7 @@ class EntityManager {
|
|
|
568
588
|
// save the original hint value so we know it was infer/all
|
|
569
589
|
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
570
590
|
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
571
|
-
options.populateFilter = await this.getJoinedFilters(meta,
|
|
591
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
572
592
|
const data = await em.driver.findOne(entityName, where, {
|
|
573
593
|
ctx: em.transactionContext,
|
|
574
594
|
em,
|
|
@@ -1253,7 +1273,7 @@ class EntityManager {
|
|
|
1253
1273
|
const meta = em.metadata.find(entityName);
|
|
1254
1274
|
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
1255
1275
|
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
1256
|
-
options.populateFilter = await this.getJoinedFilters(meta,
|
|
1276
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
1257
1277
|
em.validator.validateParams(where);
|
|
1258
1278
|
delete options.orderBy;
|
|
1259
1279
|
const cacheKey = em.cacheKey(entityName, options, 'em.count', where);
|
|
@@ -1387,7 +1407,7 @@ class EntityManager {
|
|
|
1387
1407
|
const em = this.getContext();
|
|
1388
1408
|
em.prepareOptions(options);
|
|
1389
1409
|
const entityName = arr[0].constructor.name;
|
|
1390
|
-
const preparedPopulate = await em.preparePopulate(entityName, { populate: populate }, options.validate);
|
|
1410
|
+
const preparedPopulate = await em.preparePopulate(entityName, { populate: populate, filters: options.filters }, options.validate);
|
|
1391
1411
|
await em.entityLoader.populate(entityName, arr, preparedPopulate, options);
|
|
1392
1412
|
return entities;
|
|
1393
1413
|
}
|
package/entity/EntityFactory.js
CHANGED
|
@@ -160,6 +160,7 @@ class EntityFactory {
|
|
|
160
160
|
this.create(prop.type, data[prop.name], options); // we can ignore the value, we just care about the `mergeData` call
|
|
161
161
|
}
|
|
162
162
|
});
|
|
163
|
+
this.unitOfWork.normalizeEntityData(meta, originalEntityData);
|
|
163
164
|
(0, wrap_1.helper)(entity).__touched = false;
|
|
164
165
|
}
|
|
165
166
|
createReference(entityName, id, options = {}) {
|
package/entity/EntityLoader.js
CHANGED
|
@@ -213,7 +213,7 @@ class EntityLoader {
|
|
|
213
213
|
}
|
|
214
214
|
}
|
|
215
215
|
async findChildren(entities, prop, populate, options, ref) {
|
|
216
|
-
const children = this.getChildReferences(entities, prop, options, ref);
|
|
216
|
+
const children = Utils_1.Utils.unique(this.getChildReferences(entities, prop, options, ref));
|
|
217
217
|
const meta = prop.targetMeta;
|
|
218
218
|
let fk = Utils_1.Utils.getPrimaryKeyHash(meta.primaryKeys);
|
|
219
219
|
let schema = options.schema;
|
|
@@ -269,6 +269,24 @@ class EntityLoader {
|
|
|
269
269
|
// @ts-ignore not a public option, will be propagated to the populate call
|
|
270
270
|
visited: options.visited,
|
|
271
271
|
});
|
|
272
|
+
if ([enums_1.ReferenceKind.ONE_TO_ONE, enums_1.ReferenceKind.MANY_TO_ONE].includes(prop.kind) && items.length !== children.length) {
|
|
273
|
+
const nullVal = this.em.config.get('forceUndefined') ? undefined : null;
|
|
274
|
+
const itemsMap = new Set();
|
|
275
|
+
const childrenMap = new Set();
|
|
276
|
+
for (const item of items) {
|
|
277
|
+
itemsMap.add((0, wrap_1.helper)(item).getSerializedPrimaryKey());
|
|
278
|
+
}
|
|
279
|
+
for (const child of children) {
|
|
280
|
+
childrenMap.add((0, wrap_1.helper)(child).getSerializedPrimaryKey());
|
|
281
|
+
}
|
|
282
|
+
for (const entity of entities) {
|
|
283
|
+
const key = (0, wrap_1.helper)(entity[prop.name] ?? {})?.getSerializedPrimaryKey();
|
|
284
|
+
if (childrenMap.has(key) && !itemsMap.has(key)) {
|
|
285
|
+
entity[prop.name] = nullVal;
|
|
286
|
+
(0, wrap_1.helper)(entity).__originalEntityData[prop.name] = null;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
272
290
|
for (const item of items) {
|
|
273
291
|
if (ref && !(0, wrap_1.helper)(item).__onLoadFired) {
|
|
274
292
|
(0, wrap_1.helper)(item).__initialized = false;
|
package/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @packageDocumentation
|
|
3
3
|
* @module core
|
|
4
4
|
*/
|
|
5
|
-
export { Constructor, ConnectionType, Dictionary, PrimaryKeyProp, Primary, IPrimaryKey, ObjectQuery, FilterQuery, IWrappedEntity, EntityName, EntityData, Highlighter, MaybePromise, AnyEntity, EntityClass, EntityProperty, EntityMetadata, QBFilterQuery, PopulateOptions, Populate, Loaded, New, LoadedReference, LoadedCollection, IMigrator, IMigrationGenerator, MigratorEvent, GetRepository, EntityRepositoryType, MigrationObject, DeepPartial, PrimaryProperty, Cast, IsUnknown, EntityDictionary, EntityDTO, MigrationDiff, GenerateOptions, FilterObject, IEntityGenerator, ISeedManager, EntityClassGroup, OptionalProps, EagerProps, HiddenProps, RequiredEntityData, CheckCallback, IndexCallback, SimpleColumnMeta, Rel, Ref, ScalarRef, EntityRef, ISchemaGenerator, UmzugMigration, MigrateOptions, MigrationResult, MigrationRow, EntityKey, EntityValue, EntityDataValue, FilterKey, Opt, EntityType, FromEntityType, Selected, IsSubset, NoInfer, EntityProps, ExpandProperty, ExpandScalar, FilterItemValue, ExpandQuery, Scalar, ExpandHint, Hidden, FilterValue, MergeLoaded, MergeSelected, Config, DefineConfig, TypeConfig, ClearDatabaseOptions, CreateSchemaOptions, EnsureDatabaseOptions, UpdateSchemaOptions, DropSchemaOptions, RefreshDatabaseOptions, AutoPath, UnboxArray, MetadataProcessor, ImportsResolver, RequiredNullable, } from './typings';
|
|
5
|
+
export { Constructor, ConnectionType, Dictionary, PrimaryKeyProp, Primary, IPrimaryKey, ObjectQuery, FilterQuery, IWrappedEntity, EntityName, EntityData, Highlighter, MaybePromise, AnyEntity, EntityClass, EntityProperty, EntityMetadata, QBFilterQuery, PopulateOptions, Populate, Loaded, New, LoadedReference, LoadedCollection, IMigrator, IMigrationGenerator, MigratorEvent, GetRepository, EntityRepositoryType, MigrationObject, DeepPartial, PrimaryProperty, Cast, IsUnknown, EntityDictionary, EntityDTO, MigrationDiff, GenerateOptions, FilterObject, IEntityGenerator, ISeedManager, EntityClassGroup, OptionalProps, EagerProps, HiddenProps, RequiredEntityData, CheckCallback, IndexCallback, SimpleColumnMeta, Rel, Ref, ScalarRef, EntityRef, ISchemaGenerator, UmzugMigration, MigrateOptions, MigrationResult, MigrationRow, EntityKey, EntityValue, EntityDataValue, FilterKey, Opt, EntityType, FromEntityType, Selected, IsSubset, NoInfer, EntityProps, ExpandProperty, ExpandScalar, FilterItemValue, ExpandQuery, Scalar, ExpandHint, Hidden, FilterValue, MergeLoaded, MergeSelected, Config, DefineConfig, TypeConfig, AnyString, ClearDatabaseOptions, CreateSchemaOptions, EnsureDatabaseOptions, UpdateSchemaOptions, DropSchemaOptions, RefreshDatabaseOptions, AutoPath, UnboxArray, MetadataProcessor, ImportsResolver, RequiredNullable, } from './typings';
|
|
6
6
|
export * from './enums';
|
|
7
7
|
export * from './errors';
|
|
8
8
|
export * from './exceptions';
|
|
@@ -12,7 +12,11 @@ export declare abstract class AbstractNamingStrategy implements NamingStrategy {
|
|
|
12
12
|
/**
|
|
13
13
|
* @inheritDoc
|
|
14
14
|
*/
|
|
15
|
-
getEnumClassName(columnName: string, tableName: string, schemaName?: string): string;
|
|
15
|
+
getEnumClassName(columnName: string, tableName: string | undefined, schemaName?: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* @inheritDoc
|
|
18
|
+
*/
|
|
19
|
+
getEnumTypeName(columnName: string, tableName: string | undefined, schemaName?: string): string;
|
|
16
20
|
/**
|
|
17
21
|
* @inheritDoc
|
|
18
22
|
*/
|
|
@@ -51,7 +51,13 @@ class AbstractNamingStrategy {
|
|
|
51
51
|
* @inheritDoc
|
|
52
52
|
*/
|
|
53
53
|
getEnumClassName(columnName, tableName, schemaName) {
|
|
54
|
-
return this.getEntityName(`${tableName}_${columnName}
|
|
54
|
+
return this.getEntityName(tableName ? `${tableName}_${columnName}` : columnName, schemaName);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* @inheritDoc
|
|
58
|
+
*/
|
|
59
|
+
getEnumTypeName(columnName, tableName, schemaName) {
|
|
60
|
+
return 'T' + this.getEnumClassName(columnName, tableName, schemaName);
|
|
55
61
|
}
|
|
56
62
|
/**
|
|
57
63
|
* @inheritDoc
|
|
@@ -25,7 +25,17 @@ export interface NamingStrategy {
|
|
|
25
25
|
*
|
|
26
26
|
* @return A new class name that will be used for the enum.
|
|
27
27
|
*/
|
|
28
|
-
getEnumClassName(columnName: string, tableName: string, schemaName?: string): string;
|
|
28
|
+
getEnumClassName(columnName: string, tableName: string | undefined, schemaName?: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Get an enum type name. Used with `enumType: 'dictionary'` and `enumType: 'union-type'` entity generator option.
|
|
31
|
+
*
|
|
32
|
+
* @param columnName The column name which has the enum.
|
|
33
|
+
* @param tableName The table name of the column.
|
|
34
|
+
* @param schemaName The schema name of the column.
|
|
35
|
+
*
|
|
36
|
+
* @return A new type name that will be used for the enum.
|
|
37
|
+
*/
|
|
38
|
+
getEnumTypeName(columnName: string, tableName: string | undefined, schemaName?: string): string;
|
|
29
39
|
/**
|
|
30
40
|
* Get an enum option name for a given enum value.
|
|
31
41
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/core",
|
|
3
|
-
"version": "6.5.10-dev.
|
|
3
|
+
"version": "6.5.10-dev.20",
|
|
4
4
|
"description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"module": "index.mjs",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"esprima": "4.0.1",
|
|
65
65
|
"fs-extra": "11.3.2",
|
|
66
66
|
"globby": "11.1.0",
|
|
67
|
-
"mikro-orm": "6.5.10-dev.
|
|
67
|
+
"mikro-orm": "6.5.10-dev.20",
|
|
68
68
|
"reflect-metadata": "0.2.2"
|
|
69
69
|
}
|
|
70
70
|
}
|
package/typings.d.ts
CHANGED
|
@@ -577,7 +577,9 @@ export interface GenerateOptions {
|
|
|
577
577
|
undefinedDefaults?: boolean;
|
|
578
578
|
bidirectionalRelations?: boolean;
|
|
579
579
|
identifiedReferences?: boolean;
|
|
580
|
-
|
|
580
|
+
entityDefinition?: 'decorators' | 'defineEntity' | 'entitySchema';
|
|
581
|
+
inferEntityType?: boolean;
|
|
582
|
+
enumMode?: 'ts-enum' | 'union-type' | 'dictionary';
|
|
581
583
|
esmImport?: boolean;
|
|
582
584
|
scalarTypeInDecorator?: boolean;
|
|
583
585
|
scalarPropertiesForRelations?: 'always' | 'never' | 'smart';
|
|
@@ -592,6 +594,8 @@ export interface GenerateOptions {
|
|
|
592
594
|
coreImportsPrefix?: string;
|
|
593
595
|
onInitialMetadata?: MetadataProcessor;
|
|
594
596
|
onProcessedMetadata?: MetadataProcessor;
|
|
597
|
+
/** @deprecated use `entityDefinition: 'entitySchema'` instead */
|
|
598
|
+
entitySchema?: boolean;
|
|
595
599
|
}
|
|
596
600
|
export interface IEntityGenerator {
|
|
597
601
|
generate(options?: GenerateOptions): Promise<string[]>;
|
|
@@ -710,6 +714,7 @@ export type FilterDef = {
|
|
|
710
714
|
default?: boolean;
|
|
711
715
|
entity?: string[];
|
|
712
716
|
args?: boolean;
|
|
717
|
+
strict?: boolean;
|
|
713
718
|
};
|
|
714
719
|
export type Populate<T, P extends string = never> = readonly AutoPath<T, P, `${PopulatePath}`>[] | false;
|
|
715
720
|
export type PopulateOptions<T> = {
|
|
@@ -27,6 +27,12 @@ export declare class UnitOfWork {
|
|
|
27
27
|
private working;
|
|
28
28
|
constructor(em: EntityManager);
|
|
29
29
|
merge<T extends object>(entity: T, visited?: Set<AnyEntity>): void;
|
|
30
|
+
/**
|
|
31
|
+
* Entity data can wary in its shape, e.g. we might get a deep relation graph with joined strategy, but for diffing,
|
|
32
|
+
* we need to normalize the shape, so relation values are only raw FKs. This method handles that.
|
|
33
|
+
* @internal
|
|
34
|
+
*/
|
|
35
|
+
normalizeEntityData<T extends object>(meta: EntityMetadata<T>, data: EntityData<T>): void;
|
|
30
36
|
/**
|
|
31
37
|
* @internal
|
|
32
38
|
*/
|
|
@@ -64,6 +64,40 @@ class UnitOfWork {
|
|
|
64
64
|
}
|
|
65
65
|
this.cascade(entity, enums_1.Cascade.MERGE, visited ?? new Set());
|
|
66
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Entity data can wary in its shape, e.g. we might get a deep relation graph with joined strategy, but for diffing,
|
|
69
|
+
* we need to normalize the shape, so relation values are only raw FKs. This method handles that.
|
|
70
|
+
* @internal
|
|
71
|
+
*/
|
|
72
|
+
normalizeEntityData(meta, data) {
|
|
73
|
+
const forceUndefined = this.em.config.get('forceUndefined');
|
|
74
|
+
for (const key of Utils_1.Utils.keys(data)) {
|
|
75
|
+
const prop = meta.properties[key];
|
|
76
|
+
if (!prop) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if ([enums_1.ReferenceKind.MANY_TO_ONE, enums_1.ReferenceKind.ONE_TO_ONE].includes(prop.kind) && Utils_1.Utils.isPlainObject(data[prop.name])) {
|
|
80
|
+
data[prop.name] = Utils_1.Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
|
|
81
|
+
}
|
|
82
|
+
else if (prop.kind === enums_1.ReferenceKind.EMBEDDED && !prop.object && Utils_1.Utils.isPlainObject(data[prop.name])) {
|
|
83
|
+
for (const p of prop.targetMeta.props) {
|
|
84
|
+
/* istanbul ignore next */
|
|
85
|
+
const prefix = prop.prefix === false ? '' : prop.prefix === true ? prop.name + '_' : prop.prefix;
|
|
86
|
+
data[prefix + p.name] = data[prop.name][p.name];
|
|
87
|
+
}
|
|
88
|
+
data[prop.name] = Utils_1.Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
|
|
89
|
+
}
|
|
90
|
+
if (prop.hydrate === false && prop.customType?.ensureComparable(meta, prop)) {
|
|
91
|
+
const converted = prop.customType.convertToJSValue(data[key], this.platform, { key, mode: 'hydration', force: true });
|
|
92
|
+
data[key] = prop.customType.convertToDatabaseValue(converted, this.platform, { key, mode: 'hydration' });
|
|
93
|
+
}
|
|
94
|
+
if (forceUndefined) {
|
|
95
|
+
if (data[key] === null) {
|
|
96
|
+
data[key] = undefined;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
67
101
|
/**
|
|
68
102
|
* @internal
|
|
69
103
|
*/
|
|
@@ -81,31 +115,11 @@ class UnitOfWork {
|
|
|
81
115
|
wrapped.__em ??= this.em;
|
|
82
116
|
wrapped.__managed = true;
|
|
83
117
|
if (data && (options?.refresh || !wrapped.__originalEntityData)) {
|
|
118
|
+
this.normalizeEntityData(wrapped.__meta, data);
|
|
84
119
|
for (const key of Utils_1.Utils.keys(data)) {
|
|
85
120
|
const prop = wrapped.__meta.properties[key];
|
|
86
|
-
if (
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
wrapped.__loadedProperties.add(key);
|
|
90
|
-
if ([enums_1.ReferenceKind.MANY_TO_ONE, enums_1.ReferenceKind.ONE_TO_ONE].includes(prop.kind) && Utils_1.Utils.isPlainObject(data[prop.name])) {
|
|
91
|
-
data[prop.name] = Utils_1.Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
|
|
92
|
-
}
|
|
93
|
-
else if (prop.kind === enums_1.ReferenceKind.EMBEDDED && !prop.object && Utils_1.Utils.isPlainObject(data[prop.name])) {
|
|
94
|
-
for (const p of prop.targetMeta.props) {
|
|
95
|
-
/* istanbul ignore next */
|
|
96
|
-
const prefix = prop.prefix === false ? '' : prop.prefix === true ? prop.name + '_' : prop.prefix;
|
|
97
|
-
data[prefix + p.name] = data[prop.name][p.name];
|
|
98
|
-
}
|
|
99
|
-
data[prop.name] = Utils_1.Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
|
|
100
|
-
}
|
|
101
|
-
if (prop.hydrate === false && prop.customType?.ensureComparable(wrapped.__meta, prop)) {
|
|
102
|
-
const converted = prop.customType.convertToJSValue(data[key], this.platform, { key, mode: 'hydration', force: true });
|
|
103
|
-
data[key] = prop.customType.convertToDatabaseValue(converted, this.platform, { key, mode: 'hydration' });
|
|
104
|
-
}
|
|
105
|
-
if (forceUndefined) {
|
|
106
|
-
if (data[key] === null) {
|
|
107
|
-
data[key] = undefined;
|
|
108
|
-
}
|
|
121
|
+
if (prop) {
|
|
122
|
+
wrapped.__loadedProperties.add(key);
|
|
109
123
|
}
|
|
110
124
|
}
|
|
111
125
|
wrapped.__originalEntityData = data;
|
package/utils/Configuration.d.ts
CHANGED
|
@@ -61,6 +61,7 @@ export declare class Configuration<D extends IDatabaseDriver = IDatabaseDriver,
|
|
|
61
61
|
onQuery: (sql: string) => string;
|
|
62
62
|
autoJoinOneToOneOwner: true;
|
|
63
63
|
autoJoinRefsForFilters: true;
|
|
64
|
+
filtersOnRelations: true;
|
|
64
65
|
propagationOnPrototype: true;
|
|
65
66
|
populateAfterFlush: true;
|
|
66
67
|
serialization: {
|
|
@@ -117,6 +118,8 @@ export declare class Configuration<D extends IDatabaseDriver = IDatabaseDriver,
|
|
|
117
118
|
identifiedReferences: false;
|
|
118
119
|
scalarTypeInDecorator: false;
|
|
119
120
|
scalarPropertiesForRelations: "never";
|
|
121
|
+
entityDefinition: "decorators";
|
|
122
|
+
enumMode: "ts-enum";
|
|
120
123
|
fileName: (className: string) => string;
|
|
121
124
|
onlyPurePivotTables: false;
|
|
122
125
|
outputPurePivotTables: false;
|
|
@@ -337,6 +340,7 @@ export interface MikroORMOptions<D extends IDatabaseDriver = IDatabaseDriver, EM
|
|
|
337
340
|
onQuery: (sql: string, params: unknown[]) => string;
|
|
338
341
|
autoJoinOneToOneOwner: boolean;
|
|
339
342
|
autoJoinRefsForFilters: boolean;
|
|
343
|
+
filtersOnRelations: boolean;
|
|
340
344
|
propagationOnPrototype: boolean;
|
|
341
345
|
populateAfterFlush: boolean;
|
|
342
346
|
serialization: {
|
package/utils/Configuration.js
CHANGED
|
@@ -55,6 +55,7 @@ class Configuration {
|
|
|
55
55
|
onQuery: sql => sql,
|
|
56
56
|
autoJoinOneToOneOwner: true,
|
|
57
57
|
autoJoinRefsForFilters: true,
|
|
58
|
+
filtersOnRelations: true,
|
|
58
59
|
propagationOnPrototype: true,
|
|
59
60
|
populateAfterFlush: true,
|
|
60
61
|
serialization: {
|
|
@@ -111,6 +112,8 @@ class Configuration {
|
|
|
111
112
|
identifiedReferences: false,
|
|
112
113
|
scalarTypeInDecorator: false,
|
|
113
114
|
scalarPropertiesForRelations: 'never',
|
|
115
|
+
entityDefinition: 'decorators',
|
|
116
|
+
enumMode: 'ts-enum',
|
|
114
117
|
fileName: (className) => className,
|
|
115
118
|
onlyPurePivotTables: false,
|
|
116
119
|
outputPurePivotTables: false,
|
|
@@ -339,6 +342,9 @@ class Configuration {
|
|
|
339
342
|
Object.keys(this.options.filters).forEach(key => {
|
|
340
343
|
this.options.filters[key].default ??= true;
|
|
341
344
|
});
|
|
345
|
+
if (!this.options.filtersOnRelations) {
|
|
346
|
+
this.options.autoJoinRefsForFilters ??= false;
|
|
347
|
+
}
|
|
342
348
|
this.options.subscribers = Utils_1.Utils.unique(this.options.subscribers).map(subscriber => {
|
|
343
349
|
return subscriber.constructor.name === 'Function' ? new subscriber() : subscriber;
|
|
344
350
|
});
|
|
@@ -350,6 +356,9 @@ class Configuration {
|
|
|
350
356
|
sync() {
|
|
351
357
|
process.env.MIKRO_ORM_COLORS = '' + this.options.colors;
|
|
352
358
|
this.options.tsNode = this.options.preferTs;
|
|
359
|
+
if (this.options.entityGenerator.entitySchema) {
|
|
360
|
+
this.options.entityGenerator.entityDefinition = 'entitySchema';
|
|
361
|
+
}
|
|
353
362
|
this.logger.setDebugMode(this.options.debug);
|
|
354
363
|
}
|
|
355
364
|
/**
|
|
@@ -89,7 +89,7 @@ export declare const ALIAS_REPLACEMENT_RE = "\\[::alias::\\]";
|
|
|
89
89
|
* export class Author { ... }
|
|
90
90
|
* ```
|
|
91
91
|
*/
|
|
92
|
-
export declare function raw<T extends object = any, R = any>(sql: EntityKey<T> | EntityKey<T>[] | AnyString | ((alias: string) => string) | RawQueryFragment, params?: readonly unknown[] | Dictionary<unknown>): R
|
|
92
|
+
export declare function raw<T extends object = any, R = any>(sql: EntityKey<T> | EntityKey<T>[] | AnyString | ((alias: string) => string) | RawQueryFragment, params?: readonly unknown[] | Dictionary<unknown>): NoInfer<R>;
|
|
93
93
|
/**
|
|
94
94
|
* Alternative to the `raw()` helper allowing to use it as a tagged template function for the simple cases.
|
|
95
95
|
*
|
|
@@ -106,7 +106,7 @@ export declare function raw<T extends object = any, R = any>(sql: EntityKey<T> |
|
|
|
106
106
|
*/
|
|
107
107
|
export declare function sql(sql: readonly string[], ...values: unknown[]): any;
|
|
108
108
|
export declare namespace sql {
|
|
109
|
-
var ref: <T extends object>(...keys: string[]) => RawQueryFragment
|
|
109
|
+
var ref: <T extends object>(...keys: string[]) => NoInfer<RawQueryFragment>;
|
|
110
110
|
var now: (length?: number) => string;
|
|
111
111
|
var lower: <T extends object>(key: string | ((alias: string) => string)) => string;
|
|
112
112
|
var upper: <T extends object>(key: string | ((alias: string) => string)) => string;
|
package/utils/Utils.js
CHANGED
|
@@ -717,11 +717,11 @@ class Utils {
|
|
|
717
717
|
return simple;
|
|
718
718
|
}
|
|
719
719
|
const objectType = Object.prototype.toString.call(value);
|
|
720
|
-
const type = objectType.match(
|
|
720
|
+
const type = objectType.match(/^\[object (.+)]$/)[1];
|
|
721
721
|
if (type === 'Uint8Array') {
|
|
722
722
|
return 'Buffer';
|
|
723
723
|
}
|
|
724
|
-
return
|
|
724
|
+
return type;
|
|
725
725
|
}
|
|
726
726
|
/**
|
|
727
727
|
* Checks whether the value is POJO (e.g. `{ foo: 'bar' }`, and not instance of `Foo`)
|