@mikro-orm/core 6.5.10-dev.1 → 6.5.10-dev.11
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 +9 -6
- package/EntityManager.js +33 -21
- package/entity/EntityFactory.js +1 -0
- package/package.json +2 -2
- package/typings.d.ts +1 -0
- package/unit-of-work/UnitOfWork.d.ts +6 -0
- package/unit-of-work/UnitOfWork.js +37 -23
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.
|
|
@@ -123,7 +123,10 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
|
|
|
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
|
@@ -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
|
}
|
|
@@ -308,29 +308,29 @@ class EntityManager {
|
|
|
308
308
|
/**
|
|
309
309
|
* 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
310
|
*/
|
|
311
|
-
async autoJoinRefsForFilters(meta, options) {
|
|
312
|
-
if (!meta || !this.config.get('autoJoinRefsForFilters')) {
|
|
311
|
+
async autoJoinRefsForFilters(meta, options, parent) {
|
|
312
|
+
if (!meta || !this.config.get('autoJoinRefsForFilters') || options.filters === false) {
|
|
313
313
|
return;
|
|
314
314
|
}
|
|
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
315
|
const ret = options.populate;
|
|
320
|
-
for (const prop of
|
|
316
|
+
for (const prop of meta.relations) {
|
|
317
|
+
if (prop.object
|
|
318
|
+
|| ![enums_1.ReferenceKind.MANY_TO_ONE, enums_1.ReferenceKind.ONE_TO_ONE].includes(prop.kind)
|
|
319
|
+
|| !((options.fields?.length ?? 0) === 0 || options.fields?.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
|
|
320
|
+
|| (parent?.className === prop.targetMeta.root.className && parent.propName === prop.inversedBy)) {
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
321
323
|
const cond = await this.applyFilters(prop.type, {}, options.filters ?? {}, 'read', options);
|
|
322
324
|
if (!utils_1.Utils.isEmpty(cond)) {
|
|
323
325
|
const populated = options.populate.filter(({ field }) => field.split(':')[0] === prop.name);
|
|
324
326
|
let found = false;
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
found = true;
|
|
333
|
-
}
|
|
327
|
+
for (const hint of populated) {
|
|
328
|
+
if (!hint.all) {
|
|
329
|
+
hint.filter = true;
|
|
330
|
+
}
|
|
331
|
+
const strategy = (0, utils_2.getLoadingStrategy)(prop.strategy || hint.strategy || options.strategy || this.config.get('loadStrategy'), prop.kind);
|
|
332
|
+
if (hint.field === `${prop.name}:ref` || (hint.filter && strategy === enums_1.LoadStrategy.JOINED)) {
|
|
333
|
+
found = true;
|
|
334
334
|
}
|
|
335
335
|
}
|
|
336
336
|
if (!found) {
|
|
@@ -338,6 +338,14 @@ class EntityManager {
|
|
|
338
338
|
}
|
|
339
339
|
}
|
|
340
340
|
}
|
|
341
|
+
for (const hint of ret) {
|
|
342
|
+
const [field, ref] = hint.field.split(':');
|
|
343
|
+
const prop = meta?.properties[field];
|
|
344
|
+
if (prop && !ref) {
|
|
345
|
+
hint.children ??= [];
|
|
346
|
+
await this.autoJoinRefsForFilters(prop.targetMeta, { ...options, populate: hint.children }, { className: meta.root.className, propName: prop.name });
|
|
347
|
+
}
|
|
348
|
+
}
|
|
341
349
|
}
|
|
342
350
|
/**
|
|
343
351
|
* @internal
|
|
@@ -376,13 +384,17 @@ class EntityManager {
|
|
|
376
384
|
else {
|
|
377
385
|
cond = filter.cond;
|
|
378
386
|
}
|
|
379
|
-
|
|
387
|
+
cond = utils_1.QueryHelper.processWhere({
|
|
380
388
|
where: cond,
|
|
381
389
|
entityName,
|
|
382
390
|
metadata: this.metadata,
|
|
383
391
|
platform: this.driver.getPlatform(),
|
|
384
392
|
aliased: type === 'read',
|
|
385
|
-
})
|
|
393
|
+
});
|
|
394
|
+
if (filter.strict) {
|
|
395
|
+
Object.defineProperty(cond, '__strict', { value: filter.strict, enumerable: false });
|
|
396
|
+
}
|
|
397
|
+
ret.push(cond);
|
|
386
398
|
}
|
|
387
399
|
const conds = [...ret, where].filter(c => utils_1.Utils.hasObjectKeys(c));
|
|
388
400
|
return conds.length > 1 ? { $and: conds } : conds[0];
|
|
@@ -1387,7 +1399,7 @@ class EntityManager {
|
|
|
1387
1399
|
const em = this.getContext();
|
|
1388
1400
|
em.prepareOptions(options);
|
|
1389
1401
|
const entityName = arr[0].constructor.name;
|
|
1390
|
-
const preparedPopulate = await em.preparePopulate(entityName, { populate: populate }, options.validate);
|
|
1402
|
+
const preparedPopulate = await em.preparePopulate(entityName, { populate: populate, filters: options.filters }, options.validate);
|
|
1391
1403
|
await em.entityLoader.populate(entityName, arr, preparedPopulate, options);
|
|
1392
1404
|
return entities;
|
|
1393
1405
|
}
|
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/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.11",
|
|
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.11",
|
|
68
68
|
"reflect-metadata": "0.2.2"
|
|
69
69
|
}
|
|
70
70
|
}
|
package/typings.d.ts
CHANGED
|
@@ -710,6 +710,7 @@ export type FilterDef = {
|
|
|
710
710
|
default?: boolean;
|
|
711
711
|
entity?: string[];
|
|
712
712
|
args?: boolean;
|
|
713
|
+
strict?: boolean;
|
|
713
714
|
};
|
|
714
715
|
export type Populate<T, P extends string = never> = readonly AutoPath<T, P, `${PopulatePath}`>[] | false;
|
|
715
716
|
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;
|