@mikro-orm/core 6.2.10-dev.99 → 6.3.0
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.js +1 -0
- package/MikroORM.js +2 -2
- package/drivers/IDatabaseDriver.d.ts +1 -0
- package/hydration/ObjectHydrator.js +27 -17
- package/metadata/EntitySchema.d.ts +6 -1
- package/metadata/MetadataDiscovery.js +24 -7
- package/package.json +2 -2
- package/platforms/Platform.d.ts +2 -0
- package/platforms/Platform.js +4 -0
- package/serialization/EntitySerializer.js +27 -20
- package/serialization/EntityTransformer.js +33 -22
- package/serialization/SerializationContext.js +19 -14
- package/typings.d.ts +2 -1
- package/typings.js +1 -0
- package/utils/EntityComparator.js +5 -5
- package/utils/QueryHelper.js +5 -0
- package/utils/RawQueryFragment.d.ts +1 -1
- package/utils/RawQueryFragment.js +3 -2
package/EntityManager.js
CHANGED
|
@@ -1218,6 +1218,7 @@ class EntityManager {
|
|
|
1218
1218
|
...options,
|
|
1219
1219
|
};
|
|
1220
1220
|
entityName = utils_1.Utils.className(entityName);
|
|
1221
|
+
await em.tryFlush(entityName, options);
|
|
1221
1222
|
where = await em.processWhere(entityName, where, options, 'read');
|
|
1222
1223
|
options.populate = await em.preparePopulate(entityName, options);
|
|
1223
1224
|
em.validator.validateParams(where);
|
package/MikroORM.js
CHANGED
|
@@ -43,7 +43,7 @@ class MikroORM {
|
|
|
43
43
|
orm.config.set('allowGlobalContext', true);
|
|
44
44
|
await orm.discoverEntities();
|
|
45
45
|
orm.config.set('allowGlobalContext', allowGlobalContext);
|
|
46
|
-
orm.driver.getPlatform().
|
|
46
|
+
orm.driver.getPlatform().init(orm);
|
|
47
47
|
if (orm.config.get('connect')) {
|
|
48
48
|
await orm.connect();
|
|
49
49
|
}
|
|
@@ -79,7 +79,7 @@ class MikroORM {
|
|
|
79
79
|
orm.config.set('allowGlobalContext', true);
|
|
80
80
|
orm.discoverEntitiesSync();
|
|
81
81
|
orm.config.set('allowGlobalContext', allowGlobalContext);
|
|
82
|
-
orm.driver.getPlatform().
|
|
82
|
+
orm.driver.getPlatform().init(orm);
|
|
83
83
|
for (const extension of orm.config.get('extensions')) {
|
|
84
84
|
extension.register(orm);
|
|
85
85
|
}
|
|
@@ -157,6 +157,7 @@ export interface CountOptions<T extends object, P extends string = never> {
|
|
|
157
157
|
populate?: Populate<T, P>;
|
|
158
158
|
ctx?: Transaction;
|
|
159
159
|
connectionType?: ConnectionType;
|
|
160
|
+
flushMode?: FlushMode | `${FlushMode}`;
|
|
160
161
|
/** sql only */
|
|
161
162
|
indexHint?: string;
|
|
162
163
|
/** sql only */
|
|
@@ -226,12 +226,27 @@ class ObjectHydrator extends Hydrator_1.Hydrator {
|
|
|
226
226
|
ret.push(` };`);
|
|
227
227
|
}
|
|
228
228
|
if (prop.targetMeta?.polymorphs) {
|
|
229
|
-
prop.targetMeta.polymorphs.forEach(
|
|
229
|
+
prop.targetMeta.polymorphs.forEach(childMeta => {
|
|
230
230
|
const childProp = prop.embeddedProps[prop.targetMeta.discriminatorColumn];
|
|
231
231
|
const childDataKey = prop.object ? dataKey + this.wrap(childProp.embedded[1]) : this.wrap(childProp.name);
|
|
232
232
|
// weak comparison as we can have numbers that might have been converted to strings due to being object keys
|
|
233
|
-
ret.push(` if (data${childDataKey} == '${
|
|
234
|
-
ret.push(` entity${entityKey}
|
|
233
|
+
ret.push(` if (data${childDataKey} == '${childMeta.discriminatorValue}') {`);
|
|
234
|
+
ret.push(` if (entity${entityKey} == null) {`);
|
|
235
|
+
ret.push(` entity${entityKey} = factory.createEmbeddable('${childMeta.className}', embeddedData, { newEntity, convertCustomTypes });`);
|
|
236
|
+
ret.push(` }`);
|
|
237
|
+
meta.props
|
|
238
|
+
.filter(p => p.embedded?.[0] === prop.name)
|
|
239
|
+
.forEach(childProp => {
|
|
240
|
+
const childDataKey = prop.object ? dataKey + this.wrap(childProp.embedded[1]) : this.wrap(childProp.name);
|
|
241
|
+
const prop2 = childMeta.properties[childProp.embedded[1]];
|
|
242
|
+
const prop3 = {
|
|
243
|
+
...prop2,
|
|
244
|
+
name: childProp.name,
|
|
245
|
+
embedded: childProp.embedded,
|
|
246
|
+
};
|
|
247
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
248
|
+
ret.push(...hydrateProperty(prop3, childProp.object, [...path, childProp.embedded[1]], childDataKey).map(l => ' ' + l));
|
|
249
|
+
});
|
|
235
250
|
ret.push(` }`);
|
|
236
251
|
});
|
|
237
252
|
}
|
|
@@ -239,14 +254,14 @@ class ObjectHydrator extends Hydrator_1.Hydrator {
|
|
|
239
254
|
ret.push(` if (entity${entityKey} == null) {`);
|
|
240
255
|
ret.push(` entity${entityKey} = factory.createEmbeddable('${prop.targetMeta.className}', embeddedData, { newEntity, convertCustomTypes });`);
|
|
241
256
|
ret.push(` }`);
|
|
257
|
+
meta.props
|
|
258
|
+
.filter(p => p.embedded?.[0] === prop.name)
|
|
259
|
+
.forEach(childProp => {
|
|
260
|
+
const childDataKey = prop.object ? dataKey + this.wrap(childProp.embedded[1]) : this.wrap(childProp.name);
|
|
261
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
262
|
+
ret.push(...hydrateProperty(childProp, prop.object, [...path, childProp.embedded[1]], childDataKey).map(l => ' ' + l));
|
|
263
|
+
});
|
|
242
264
|
}
|
|
243
|
-
meta.props
|
|
244
|
-
.filter(p => p.embedded?.[0] === prop.name)
|
|
245
|
-
.forEach(childProp => {
|
|
246
|
-
const childDataKey = prop.object ? dataKey + this.wrap(childProp.embedded[1]) : this.wrap(childProp.name);
|
|
247
|
-
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
248
|
-
ret.push(...hydrateProperty(childProp, prop.object, [...path, childProp.embedded[1]], childDataKey).map(l => ' ' + l));
|
|
249
|
-
});
|
|
250
265
|
/* istanbul ignore next */
|
|
251
266
|
const nullVal = this.config.get('forceUndefined') ? 'undefined' : 'null';
|
|
252
267
|
if (prop.object) {
|
|
@@ -316,19 +331,14 @@ class ObjectHydrator extends Hydrator_1.Hydrator {
|
|
|
316
331
|
const meta = this.metadata.get(prop.type);
|
|
317
332
|
const lines = [];
|
|
318
333
|
lines.push(` const createCollectionItem_${this.safeKey(prop.name)} = (value, entity) => {`);
|
|
319
|
-
const prop2 = prop.targetMeta
|
|
320
|
-
if (prop2
|
|
334
|
+
const prop2 = prop.targetMeta.properties[prop.mappedBy];
|
|
335
|
+
if (prop.kind === enums_1.ReferenceKind.ONE_TO_MANY && prop2.primary) {
|
|
321
336
|
lines.push(` if (typeof value === 'object' && value?.['${prop2.name}'] == null) {`);
|
|
322
337
|
lines.push(` value = { ...value, ['${prop2.name}']: Reference.wrapReference(entity, { ref: ${prop2.ref} }) };`);
|
|
323
338
|
lines.push(` }`);
|
|
324
339
|
}
|
|
325
340
|
lines.push(` if (isPrimaryKey(value, ${meta.compositePK})) return factory.createReference('${prop.type}', value, { convertCustomTypes, schema, merge: true });`);
|
|
326
341
|
lines.push(` if (value && value.__entity) return value;`);
|
|
327
|
-
if (prop2 && !prop2.primary) {
|
|
328
|
-
lines.push(` if (typeof value === 'object' && value?.['${prop2.name}'] == null) {`);
|
|
329
|
-
lines.push(` value = { ...value, ['${prop2.name}']: Reference.wrapReference(entity, { ref: ${prop2.ref} }) };`);
|
|
330
|
-
lines.push(` }`);
|
|
331
|
-
}
|
|
332
342
|
lines.push(` return factory.create('${prop.type}', value, { newEntity, convertCustomTypes, schema, merge: true });`);
|
|
333
343
|
lines.push(` }`);
|
|
334
344
|
return lines;
|
|
@@ -8,6 +8,11 @@ type TypeDef<Target> = {
|
|
|
8
8
|
} | {
|
|
9
9
|
entity: string | (() => string | EntityName<Target>);
|
|
10
10
|
};
|
|
11
|
+
type EmbeddedTypeDef<Target> = {
|
|
12
|
+
type: TypeType;
|
|
13
|
+
} | {
|
|
14
|
+
entity: string | (() => string | EntityName<Target> | EntityName<Target>[]);
|
|
15
|
+
};
|
|
11
16
|
export type EntitySchemaProperty<Target, Owner> = ({
|
|
12
17
|
kind: ReferenceKind.MANY_TO_ONE | 'm:1';
|
|
13
18
|
} & TypeDef<Target> & ManyToOneOptions<Owner, Target>) | ({
|
|
@@ -18,7 +23,7 @@ export type EntitySchemaProperty<Target, Owner> = ({
|
|
|
18
23
|
kind: ReferenceKind.MANY_TO_MANY | 'm:n';
|
|
19
24
|
} & TypeDef<Target> & ManyToManyOptions<Owner, Target>) | ({
|
|
20
25
|
kind: ReferenceKind.EMBEDDED | 'embedded';
|
|
21
|
-
} &
|
|
26
|
+
} & EmbeddedTypeDef<Target> & EmbeddedOptions & PropertyOptions<Owner>) | ({
|
|
22
27
|
enum: true;
|
|
23
28
|
} & EnumOptions<Owner>) | (TypeDef<Target> & PropertyOptions<Owner>);
|
|
24
29
|
type OmitBaseProps<Entity, Base> = IsNever<Base> extends true ? Entity : Omit<Entity, keyof Base>;
|
|
@@ -721,6 +721,15 @@ class MetadataDiscovery {
|
|
|
721
721
|
if (!embeddable && polymorphs.length > 0) {
|
|
722
722
|
const properties = {};
|
|
723
723
|
let discriminatorColumn;
|
|
724
|
+
const inlineProperties = (meta) => {
|
|
725
|
+
Object.values(meta.properties).forEach(prop => {
|
|
726
|
+
if (properties[prop.name] && properties[prop.name].type !== prop.type) {
|
|
727
|
+
properties[prop.name].type = `${properties[prop.name].type} | ${prop.type}`;
|
|
728
|
+
return properties[prop.name];
|
|
729
|
+
}
|
|
730
|
+
return properties[prop.name] = prop;
|
|
731
|
+
});
|
|
732
|
+
};
|
|
724
733
|
const processExtensions = (meta) => {
|
|
725
734
|
const parent = this.discovered.find(m => {
|
|
726
735
|
return meta.extends && Utils_1.Utils.className(meta.extends) === m.className;
|
|
@@ -729,11 +738,11 @@ class MetadataDiscovery {
|
|
|
729
738
|
return;
|
|
730
739
|
}
|
|
731
740
|
discriminatorColumn ??= parent.discriminatorColumn;
|
|
732
|
-
|
|
741
|
+
inlineProperties(parent);
|
|
733
742
|
processExtensions(parent);
|
|
734
743
|
};
|
|
735
744
|
polymorphs.forEach(meta => {
|
|
736
|
-
|
|
745
|
+
inlineProperties(meta);
|
|
737
746
|
processExtensions(meta);
|
|
738
747
|
});
|
|
739
748
|
const name = polymorphs.map(t => t.className).sort().join(' | ');
|
|
@@ -873,13 +882,21 @@ class MetadataDiscovery {
|
|
|
873
882
|
if (meta.root === meta) {
|
|
874
883
|
return;
|
|
875
884
|
}
|
|
885
|
+
let i = 1;
|
|
876
886
|
Object.values(meta.properties).forEach(prop => {
|
|
877
|
-
|
|
887
|
+
if (meta.root.properties[prop.name] && meta.root.properties[prop.name].type !== prop.type) {
|
|
888
|
+
const name = prop.name;
|
|
889
|
+
this.initFieldName(prop, prop.object);
|
|
890
|
+
prop.name = name + '_' + (i++);
|
|
891
|
+
meta.root.addProperty(prop);
|
|
892
|
+
prop.nullable = true;
|
|
893
|
+
prop.name = name;
|
|
894
|
+
prop.hydrate = false;
|
|
895
|
+
return;
|
|
896
|
+
}
|
|
878
897
|
prop = Utils_1.Utils.copy(prop, false);
|
|
879
898
|
prop.nullable = true;
|
|
880
|
-
|
|
881
|
-
prop.inherited = true;
|
|
882
|
-
}
|
|
899
|
+
prop.inherited = true;
|
|
883
900
|
meta.root.addProperty(prop);
|
|
884
901
|
});
|
|
885
902
|
meta.collection = meta.root.collection;
|
|
@@ -964,7 +981,7 @@ class MetadataDiscovery {
|
|
|
964
981
|
const entity1 = new meta.class();
|
|
965
982
|
const entity2 = new meta.class();
|
|
966
983
|
// we compare the two values by reference, this will discard things like `new Date()` or `Date.now()`
|
|
967
|
-
if (this.config.get('discovery').inferDefaultValues && prop.default === undefined && entity1[prop.name] != null && entity1[prop.name] === entity2[prop.name] && entity1[prop.name] !== now) {
|
|
984
|
+
if (!meta.embeddable && this.config.get('discovery').inferDefaultValues && prop.default === undefined && entity1[prop.name] != null && entity1[prop.name] === entity2[prop.name] && entity1[prop.name] !== now) {
|
|
968
985
|
prop.default ??= entity1[prop.name];
|
|
969
986
|
}
|
|
970
987
|
// if the default value is null, infer nullability
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/core",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.3.0",
|
|
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.2.0",
|
|
66
66
|
"globby": "11.1.0",
|
|
67
|
-
"mikro-orm": "6.
|
|
67
|
+
"mikro-orm": "6.3.0",
|
|
68
68
|
"reflect-metadata": "0.2.2"
|
|
69
69
|
}
|
|
70
70
|
}
|
package/platforms/Platform.d.ts
CHANGED
|
@@ -160,6 +160,8 @@ export declare abstract class Platform {
|
|
|
160
160
|
* Allows registering extensions of the driver automatically (e.g. `SchemaGenerator` extension in SQL drivers).
|
|
161
161
|
*/
|
|
162
162
|
lookupExtensions(orm: MikroORM): void;
|
|
163
|
+
/** @internal */
|
|
164
|
+
init(orm: MikroORM): void;
|
|
163
165
|
getExtension<T>(extensionName: string, extensionKey: string, moduleName: string, em: EntityManager): T;
|
|
164
166
|
getSchemaGenerator(driver: IDatabaseDriver, em?: EntityManager): ISchemaGenerator;
|
|
165
167
|
processDateProperty(value: unknown): string | number | Date;
|
package/platforms/Platform.js
CHANGED
|
@@ -320,6 +320,10 @@ class Platform {
|
|
|
320
320
|
lookupExtensions(orm) {
|
|
321
321
|
// no extensions by default
|
|
322
322
|
}
|
|
323
|
+
/** @internal */
|
|
324
|
+
init(orm) {
|
|
325
|
+
this.lookupExtensions(orm);
|
|
326
|
+
}
|
|
323
327
|
getExtension(extensionName, extensionKey, moduleName, em) {
|
|
324
328
|
const extension = this.config.getExtension(extensionKey);
|
|
325
329
|
if (extension) {
|
|
@@ -7,7 +7,6 @@ const Utils_1 = require("../utils/Utils");
|
|
|
7
7
|
const enums_1 = require("../enums");
|
|
8
8
|
const Reference_1 = require("../entity/Reference");
|
|
9
9
|
const SerializationContext_1 = require("./SerializationContext");
|
|
10
|
-
const RawQueryFragment_1 = require("../utils/RawQueryFragment");
|
|
11
10
|
function isVisible(meta, propName, options) {
|
|
12
11
|
const prop = meta.properties[propName];
|
|
13
12
|
if (options.groups && prop?.groups) {
|
|
@@ -51,12 +50,13 @@ class EntitySerializer {
|
|
|
51
50
|
if (!visited) {
|
|
52
51
|
root.visited.add(entity);
|
|
53
52
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
for (const prop of keys) {
|
|
54
|
+
if (!isVisible(meta, prop, options)) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
57
|
const cycle = root.visit(meta.className, prop);
|
|
58
58
|
if (cycle && visited) {
|
|
59
|
-
|
|
59
|
+
continue;
|
|
60
60
|
}
|
|
61
61
|
const val = this.processProperty(prop, entity, options);
|
|
62
62
|
if (!cycle) {
|
|
@@ -65,29 +65,36 @@ class EntitySerializer {
|
|
|
65
65
|
if (options.skipNull && Utils_1.Utils.isPlainObject(val)) {
|
|
66
66
|
Utils_1.Utils.dropUndefinedProperties(val, null);
|
|
67
67
|
}
|
|
68
|
-
if (val
|
|
68
|
+
if (Utils_1.Utils.isRawSql(val)) {
|
|
69
69
|
throw new Error(`Trying to serialize raw SQL fragment: '${val.sql}'`);
|
|
70
70
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
71
|
+
const visible = typeof val !== 'undefined' && !(val === null && options.skipNull);
|
|
72
|
+
if (visible) {
|
|
73
|
+
ret[this.propertyName(meta, prop, wrapped.__platform)] = val;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
75
76
|
if (contextCreated) {
|
|
76
77
|
root.close();
|
|
77
78
|
}
|
|
78
79
|
if (!wrapped.isInitialized()) {
|
|
79
80
|
return ret;
|
|
80
81
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
82
|
+
for (const prop of meta.getterProps) {
|
|
83
|
+
// decorated get methods
|
|
84
|
+
if (prop.getterName != null) {
|
|
85
|
+
const visible = entity[prop.getterName] instanceof Function && isVisible(meta, prop.name, options);
|
|
86
|
+
if (visible) {
|
|
87
|
+
ret[this.propertyName(meta, prop.name, wrapped.__platform)] = this.processProperty(prop.getterName, entity, options);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
// decorated getters
|
|
92
|
+
const visible = typeof entity[prop.name] !== 'undefined' && isVisible(meta, prop.name, options);
|
|
93
|
+
if (visible) {
|
|
94
|
+
ret[this.propertyName(meta, prop.name, wrapped.__platform)] = this.processProperty(prop.name, entity, options);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
91
98
|
return ret;
|
|
92
99
|
}
|
|
93
100
|
static propertyName(meta, prop, platform) {
|
|
@@ -5,7 +5,6 @@ const wrap_1 = require("../entity/wrap");
|
|
|
5
5
|
const Utils_1 = require("../utils/Utils");
|
|
6
6
|
const enums_1 = require("../enums");
|
|
7
7
|
const SerializationContext_1 = require("./SerializationContext");
|
|
8
|
-
const RawQueryFragment_1 = require("../utils/RawQueryFragment");
|
|
9
8
|
function isVisible(meta, propName, ignoreFields = []) {
|
|
10
9
|
const prop = meta.properties[propName];
|
|
11
10
|
const visible = prop && !prop.hidden;
|
|
@@ -38,49 +37,61 @@ class EntityTransformer {
|
|
|
38
37
|
Utils_1.Utils.keys(entity).forEach(prop => keys.add(prop));
|
|
39
38
|
}
|
|
40
39
|
const visited = root.visited.has(entity);
|
|
40
|
+
const includePrimaryKeys = wrapped.__config.get('serialization').includePrimaryKeys;
|
|
41
41
|
if (!visited) {
|
|
42
42
|
root.visited.add(entity);
|
|
43
43
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
for (const prop of keys) {
|
|
45
|
+
const visible = raw ? meta.properties[prop] : isVisible(meta, prop, ignoreFields);
|
|
46
|
+
if (!visible) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
47
49
|
const populated = root.isMarkedAsPopulated(meta.className, prop);
|
|
48
50
|
const partiallyLoaded = root.isPartiallyLoaded(meta.className, prop);
|
|
49
|
-
const isPrimary =
|
|
51
|
+
const isPrimary = includePrimaryKeys && meta.properties[prop].primary;
|
|
50
52
|
if (!partiallyLoaded && !populated && !isPrimary) {
|
|
51
|
-
|
|
53
|
+
continue;
|
|
52
54
|
}
|
|
53
55
|
const cycle = root.visit(meta.className, prop);
|
|
54
56
|
if (cycle && visited) {
|
|
55
|
-
|
|
57
|
+
continue;
|
|
56
58
|
}
|
|
57
59
|
const val = EntityTransformer.processProperty(prop, entity, raw, populated);
|
|
58
60
|
if (!cycle) {
|
|
59
61
|
root.leave(meta.className, prop);
|
|
60
62
|
}
|
|
61
|
-
if (val
|
|
63
|
+
if (Utils_1.Utils.isRawSql(val)) {
|
|
62
64
|
throw new Error(`Trying to serialize raw SQL fragment: '${val.sql}'`);
|
|
63
65
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
if (typeof val === 'undefined') {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
ret[this.propertyName(meta, prop, wrapped.__platform, raw)] = val;
|
|
70
|
+
}
|
|
68
71
|
if (!visited) {
|
|
69
72
|
root.visited.delete(entity);
|
|
70
73
|
}
|
|
71
74
|
if (!wrapped.isInitialized() && wrapped.hasPrimaryKey()) {
|
|
72
75
|
return ret;
|
|
73
76
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
77
|
+
for (const prop of meta.getterProps) {
|
|
78
|
+
// decorated get methods
|
|
79
|
+
if (prop.getterName != null) {
|
|
80
|
+
const visible = !prop.hidden && entity[prop.getterName] instanceof Function;
|
|
81
|
+
const populated = root.isMarkedAsPopulated(meta.className, prop.name);
|
|
82
|
+
if (visible) {
|
|
83
|
+
ret[this.propertyName(meta, prop.name, wrapped.__platform, raw)] = this.processProperty(prop.getterName, entity, raw, populated);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
// decorated getters
|
|
88
|
+
const visible = !prop.hidden && typeof entity[prop.name] !== 'undefined';
|
|
89
|
+
const populated = root.isMarkedAsPopulated(meta.className, prop.name);
|
|
90
|
+
if (visible) {
|
|
91
|
+
ret[this.propertyName(meta, prop.name, wrapped.__platform, raw)] = this.processProperty(prop.name, entity, raw, populated);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
84
95
|
if (contextCreated) {
|
|
85
96
|
root.close();
|
|
86
97
|
}
|
|
@@ -45,9 +45,9 @@ class SerializationContext {
|
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
close() {
|
|
48
|
-
this.entities
|
|
48
|
+
for (const entity of this.entities) {
|
|
49
49
|
delete (0, wrap_1.helper)(entity).__serializationContext.root;
|
|
50
|
-
}
|
|
50
|
+
}
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
53
|
* When initializing new context, we need to propagate it to the whole entity graph recursively.
|
|
@@ -55,20 +55,25 @@ class SerializationContext {
|
|
|
55
55
|
static propagate(root, entity, isVisible) {
|
|
56
56
|
root.register(entity);
|
|
57
57
|
const meta = (0, wrap_1.helper)(entity).__meta;
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
.forEach(key => {
|
|
62
|
-
if (Utils_1.Utils.isEntity(entity[key], true)) {
|
|
63
|
-
items.push(entity[key]);
|
|
58
|
+
for (const key of Object.keys(entity)) {
|
|
59
|
+
if (!isVisible(meta, key)) {
|
|
60
|
+
continue;
|
|
64
61
|
}
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
const target = entity[key];
|
|
63
|
+
if (Utils_1.Utils.isEntity(target, true)) {
|
|
64
|
+
if (!target.__helper.__serializationContext.root) {
|
|
65
|
+
this.propagate(root, target, isVisible);
|
|
66
|
+
}
|
|
67
|
+
continue;
|
|
67
68
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
if (Utils_1.Utils.isCollection(target)) {
|
|
70
|
+
for (const item of target.getItems(false)) {
|
|
71
|
+
if (!item.__helper.__serializationContext.root) {
|
|
72
|
+
this.propagate(root, item, isVisible);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
72
77
|
}
|
|
73
78
|
isMarkedAsPopulated(entityName, prop) {
|
|
74
79
|
let populate = this.populate;
|
package/typings.d.ts
CHANGED
|
@@ -223,7 +223,7 @@ type ExplicitlyOptionalProps<T> = (T extends {
|
|
|
223
223
|
type NullableKeys<T, V = null> = {
|
|
224
224
|
[K in keyof T]: V extends T[K] ? K : never;
|
|
225
225
|
}[keyof T];
|
|
226
|
-
type ProbablyOptionalProps<T> = PrimaryProperty<T> | ExplicitlyOptionalProps<T> | NonNullable<NullableKeys<T>>;
|
|
226
|
+
type ProbablyOptionalProps<T> = PrimaryProperty<T> | ExplicitlyOptionalProps<T> | NonNullable<NullableKeys<T, null | undefined>>;
|
|
227
227
|
type IsOptional<T, K extends keyof T, I> = T[K] extends Collection<any, any> ? true : ExtractType<T[K]> extends I ? true : K extends ProbablyOptionalProps<T> ? true : false;
|
|
228
228
|
type RequiredKeys<T, K extends keyof T, I> = IsOptional<T, K, I> extends false ? CleanKeys<T, K> : never;
|
|
229
229
|
type OptionalKeys<T, K extends keyof T, I> = IsOptional<T, K, I> extends false ? never : CleanKeys<T, K>;
|
|
@@ -432,6 +432,7 @@ export interface EntityMetadata<T = any> {
|
|
|
432
432
|
trackingProps: EntityProperty<T>[];
|
|
433
433
|
hydrateProps: EntityProperty<T>[];
|
|
434
434
|
uniqueProps: EntityProperty<T>[];
|
|
435
|
+
getterProps: EntityProperty<T>[];
|
|
435
436
|
indexes: {
|
|
436
437
|
properties: EntityKey<T> | EntityKey<T>[];
|
|
437
438
|
name?: string;
|
package/typings.js
CHANGED
|
@@ -63,6 +63,7 @@ class EntityMetadata {
|
|
|
63
63
|
this.relations = this.props.filter(prop => typeof prop.kind !== 'undefined' && prop.kind !== enums_1.ReferenceKind.SCALAR && prop.kind !== enums_1.ReferenceKind.EMBEDDED);
|
|
64
64
|
this.bidirectionalRelations = this.relations.filter(prop => prop.mappedBy || prop.inversedBy);
|
|
65
65
|
this.uniqueProps = this.props.filter(prop => prop.unique);
|
|
66
|
+
this.getterProps = this.props.filter(prop => prop.getter);
|
|
66
67
|
this.comparableProps = this.props.filter(prop => EntityComparator_1.EntityComparator.isComparable(prop, this));
|
|
67
68
|
this.hydrateProps = this.props.filter(prop => {
|
|
68
69
|
// `prop.userDefined` is either `undefined` or `false`
|
|
@@ -221,11 +221,11 @@ class EntityComparator {
|
|
|
221
221
|
// traverse all parents, mapping my field name to each parent's field name until we reach the root
|
|
222
222
|
for (let i = parents.length - 1; i >= 0; i--) {
|
|
223
223
|
const parent = parents[i];
|
|
224
|
-
//
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
const idx =
|
|
224
|
+
// skip m:n since it does not represent any column directly
|
|
225
|
+
if (parent.pivotEntity) {
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
const idx = parent.referencedColumnNames.indexOf(fieldName);
|
|
229
229
|
fieldName = parent.fieldNames[idx];
|
|
230
230
|
}
|
|
231
231
|
return this.propName(fieldName);
|
package/utils/QueryHelper.js
CHANGED
|
@@ -6,6 +6,7 @@ const Utils_1 = require("./Utils");
|
|
|
6
6
|
const enums_1 = require("../enums");
|
|
7
7
|
const JsonType_1 = require("../types/JsonType");
|
|
8
8
|
const wrap_1 = require("../entity/wrap");
|
|
9
|
+
const RawQueryFragment_1 = require("./RawQueryFragment");
|
|
9
10
|
class QueryHelper {
|
|
10
11
|
static SUPPORTED_OPERATORS = ['>', '<', '<=', '>=', '!', '!='];
|
|
11
12
|
static processParams(params) {
|
|
@@ -102,6 +103,10 @@ class QueryHelper {
|
|
|
102
103
|
const prop = this.findProperty(key, options);
|
|
103
104
|
const keys = prop?.joinColumns?.length ?? 0;
|
|
104
105
|
const composite = keys > 1;
|
|
106
|
+
if (Array.isArray(value) && value.length === 0 && RawQueryFragment_1.RawQueryFragment.isKnownFragment(key)) {
|
|
107
|
+
o[key] = value;
|
|
108
|
+
return o;
|
|
109
|
+
}
|
|
105
110
|
if (key in enums_1.GroupOperator) {
|
|
106
111
|
o[key] = value.map((sub) => QueryHelper.processWhere({ ...options, where: sub, root }));
|
|
107
112
|
return o;
|
|
@@ -70,7 +70,7 @@ export declare const ALIAS_REPLACEMENT_RE = "\\[::alias::\\]";
|
|
|
70
70
|
* @Filter({ name: 'long', cond: () => ({ [raw('length(perex)')]: { $gt: 10000 } }) })
|
|
71
71
|
* ```
|
|
72
72
|
*/
|
|
73
|
-
export declare function raw<T extends object = any, R = any>(sql: EntityKey<T> | EntityKey<T>[] | AnyString | ((alias: string) => string) | RawQueryFragment, params?: unknown[] | Dictionary<unknown>): R;
|
|
73
|
+
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;
|
|
74
74
|
/**
|
|
75
75
|
* Alternative to the `raw()` helper allowing to use it as a tagged template function for the simple cases.
|
|
76
76
|
*
|
|
@@ -147,11 +147,12 @@ function raw(sql, params) {
|
|
|
147
147
|
}
|
|
148
148
|
if (typeof params === 'object' && !Array.isArray(params)) {
|
|
149
149
|
const pairs = Object.entries(params);
|
|
150
|
-
|
|
150
|
+
const objectParams = [];
|
|
151
151
|
for (const [key, value] of pairs) {
|
|
152
152
|
sql = sql.replace(':' + key, '?');
|
|
153
|
-
|
|
153
|
+
objectParams.push(value);
|
|
154
154
|
}
|
|
155
|
+
return new RawQueryFragment(sql, objectParams);
|
|
155
156
|
}
|
|
156
157
|
return new RawQueryFragment(sql, params);
|
|
157
158
|
}
|