@mikro-orm/core 7.0.0-dev.99 → 7.0.0-rc.1
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 +34 -17
- package/EntityManager.js +95 -103
- package/MikroORM.d.ts +5 -5
- package/MikroORM.js +25 -20
- package/cache/FileCacheAdapter.js +11 -3
- package/connections/Connection.d.ts +3 -2
- package/connections/Connection.js +4 -3
- package/drivers/DatabaseDriver.d.ts +11 -11
- package/drivers/DatabaseDriver.js +91 -25
- package/drivers/IDatabaseDriver.d.ts +50 -20
- package/entity/BaseEntity.d.ts +61 -1
- package/entity/Collection.d.ts +8 -1
- package/entity/Collection.js +12 -13
- package/entity/EntityAssigner.js +9 -9
- package/entity/EntityFactory.d.ts +6 -1
- package/entity/EntityFactory.js +40 -22
- package/entity/EntityHelper.d.ts +2 -2
- package/entity/EntityHelper.js +27 -4
- package/entity/EntityLoader.d.ts +5 -4
- package/entity/EntityLoader.js +193 -80
- package/entity/EntityRepository.d.ts +27 -7
- package/entity/EntityRepository.js +8 -2
- package/entity/PolymorphicRef.d.ts +12 -0
- package/entity/PolymorphicRef.js +18 -0
- package/entity/WrappedEntity.d.ts +2 -2
- package/entity/WrappedEntity.js +1 -1
- package/entity/defineEntity.d.ts +89 -50
- package/entity/defineEntity.js +12 -0
- package/entity/index.d.ts +1 -0
- package/entity/index.js +1 -0
- package/entity/utils.d.ts +6 -1
- package/entity/utils.js +33 -0
- package/entity/validators.js +2 -2
- package/enums.d.ts +2 -2
- package/enums.js +1 -0
- package/errors.d.ts +16 -8
- package/errors.js +40 -13
- package/hydration/ObjectHydrator.js +63 -21
- package/index.d.ts +1 -1
- package/logging/colors.d.ts +1 -1
- package/logging/colors.js +7 -6
- package/logging/inspect.js +1 -6
- package/metadata/EntitySchema.d.ts +43 -13
- package/metadata/EntitySchema.js +82 -27
- package/metadata/MetadataDiscovery.d.ts +60 -3
- package/metadata/MetadataDiscovery.js +665 -154
- package/metadata/MetadataProvider.js +3 -1
- package/metadata/MetadataStorage.d.ts +13 -6
- package/metadata/MetadataStorage.js +64 -19
- package/metadata/MetadataValidator.d.ts +32 -2
- package/metadata/MetadataValidator.js +196 -31
- package/metadata/discover-entities.js +5 -5
- package/metadata/types.d.ts +111 -14
- package/naming-strategy/AbstractNamingStrategy.d.ts +11 -3
- package/naming-strategy/AbstractNamingStrategy.js +12 -0
- package/naming-strategy/EntityCaseNamingStrategy.d.ts +3 -3
- package/naming-strategy/EntityCaseNamingStrategy.js +6 -5
- package/naming-strategy/MongoNamingStrategy.d.ts +3 -3
- package/naming-strategy/MongoNamingStrategy.js +6 -6
- package/naming-strategy/NamingStrategy.d.ts +17 -3
- package/naming-strategy/UnderscoreNamingStrategy.d.ts +3 -3
- package/naming-strategy/UnderscoreNamingStrategy.js +6 -6
- package/package.json +2 -2
- package/platforms/Platform.d.ts +4 -2
- package/platforms/Platform.js +5 -2
- package/serialization/EntitySerializer.d.ts +3 -0
- package/serialization/EntitySerializer.js +15 -13
- package/serialization/EntityTransformer.js +6 -6
- package/serialization/SerializationContext.d.ts +6 -6
- package/typings.d.ts +325 -110
- package/typings.js +84 -17
- package/unit-of-work/ChangeSet.d.ts +4 -3
- package/unit-of-work/ChangeSet.js +2 -3
- package/unit-of-work/ChangeSetComputer.d.ts +3 -6
- package/unit-of-work/ChangeSetComputer.js +34 -13
- package/unit-of-work/ChangeSetPersister.d.ts +12 -10
- package/unit-of-work/ChangeSetPersister.js +55 -25
- package/unit-of-work/CommitOrderCalculator.d.ts +12 -10
- package/unit-of-work/CommitOrderCalculator.js +13 -13
- package/unit-of-work/IdentityMap.d.ts +12 -0
- package/unit-of-work/IdentityMap.js +39 -1
- package/unit-of-work/UnitOfWork.d.ts +21 -3
- package/unit-of-work/UnitOfWork.js +203 -56
- package/utils/AbstractSchemaGenerator.js +17 -8
- package/utils/AsyncContext.d.ts +6 -0
- package/utils/AsyncContext.js +42 -0
- package/utils/Configuration.d.ts +52 -11
- package/utils/Configuration.js +12 -8
- package/utils/Cursor.js +21 -8
- package/utils/DataloaderUtils.js +13 -11
- package/utils/EntityComparator.d.ts +14 -7
- package/utils/EntityComparator.js +132 -46
- package/utils/QueryHelper.d.ts +16 -6
- package/utils/QueryHelper.js +53 -18
- package/utils/RawQueryFragment.d.ts +28 -23
- package/utils/RawQueryFragment.js +34 -56
- package/utils/RequestContext.js +2 -2
- package/utils/TransactionContext.js +2 -2
- package/utils/TransactionManager.js +1 -1
- package/utils/Utils.d.ts +7 -26
- package/utils/Utils.js +25 -79
- package/utils/clone.js +7 -21
- package/utils/env-vars.d.ts +4 -0
- package/utils/env-vars.js +13 -3
- package/utils/fs-utils.d.ts +21 -0
- package/utils/fs-utils.js +106 -11
- package/utils/upsert-utils.d.ts +4 -4
|
@@ -2,10 +2,13 @@ import { clone } from './clone.js';
|
|
|
2
2
|
import { ReferenceKind } from '../enums.js';
|
|
3
3
|
import { compareArrays, compareBooleans, compareBuffers, compareObjects, equals, parseJsonSafe, Utils } from './Utils.js';
|
|
4
4
|
import { JsonType } from '../types/JsonType.js';
|
|
5
|
-
import {
|
|
5
|
+
import { Raw } from './RawQueryFragment.js';
|
|
6
|
+
import { EntityIdentifier } from '../entity/EntityIdentifier.js';
|
|
7
|
+
import { PolymorphicRef } from '../entity/PolymorphicRef.js';
|
|
6
8
|
export class EntityComparator {
|
|
7
9
|
metadata;
|
|
8
10
|
platform;
|
|
11
|
+
config;
|
|
9
12
|
comparators = new Map();
|
|
10
13
|
mappers = new Map();
|
|
11
14
|
snapshotGenerators = new Map();
|
|
@@ -13,9 +16,10 @@ export class EntityComparator {
|
|
|
13
16
|
pkGettersConverted = new Map();
|
|
14
17
|
pkSerializers = new Map();
|
|
15
18
|
tmpIndex = 0;
|
|
16
|
-
constructor(metadata, platform) {
|
|
19
|
+
constructor(metadata, platform, config) {
|
|
17
20
|
this.metadata = metadata;
|
|
18
21
|
this.platform = platform;
|
|
22
|
+
this.config = config;
|
|
19
23
|
}
|
|
20
24
|
/**
|
|
21
25
|
* Computes difference between two entities.
|
|
@@ -33,21 +37,21 @@ export class EntityComparator {
|
|
|
33
37
|
* References will be mapped to primary keys, collections to arrays of primary keys.
|
|
34
38
|
*/
|
|
35
39
|
prepareEntity(entity) {
|
|
36
|
-
const generator = this.getSnapshotGenerator(entity.constructor
|
|
40
|
+
const generator = this.getSnapshotGenerator(entity.constructor);
|
|
37
41
|
return Utils.callCompiledFunction(generator, entity);
|
|
38
42
|
}
|
|
39
43
|
/**
|
|
40
44
|
* Maps database columns to properties.
|
|
41
45
|
*/
|
|
42
|
-
mapResult(
|
|
43
|
-
const mapper = this.getResultMapper(
|
|
46
|
+
mapResult(meta, result) {
|
|
47
|
+
const mapper = this.getResultMapper(meta);
|
|
44
48
|
return Utils.callCompiledFunction(mapper, result);
|
|
45
49
|
}
|
|
46
50
|
/**
|
|
47
51
|
* @internal Highly performance-sensitive method.
|
|
48
52
|
*/
|
|
49
53
|
getPkGetter(meta) {
|
|
50
|
-
const exists = this.pkGetters.get(meta
|
|
54
|
+
const exists = this.pkGetters.get(meta);
|
|
51
55
|
/* v8 ignore next */
|
|
52
56
|
if (exists) {
|
|
53
57
|
return exists;
|
|
@@ -87,17 +91,18 @@ export class EntityComparator {
|
|
|
87
91
|
}
|
|
88
92
|
lines.push(` return entity${this.wrap(pk)};`);
|
|
89
93
|
}
|
|
90
|
-
const code = `// compiled pk
|
|
94
|
+
const code = `// compiled pk getter for entity ${meta.className}\n`
|
|
91
95
|
+ `return function(entity) {\n${lines.join('\n')}\n}`;
|
|
92
|
-
const
|
|
93
|
-
this.
|
|
96
|
+
const fnKey = `pkGetter-${meta.uniqueName}`;
|
|
97
|
+
const pkSerializer = Utils.createFunction(context, code, this.config?.get('compiledFunctions'), fnKey);
|
|
98
|
+
this.pkGetters.set(meta, pkSerializer);
|
|
94
99
|
return pkSerializer;
|
|
95
100
|
}
|
|
96
101
|
/**
|
|
97
102
|
* @internal Highly performance-sensitive method.
|
|
98
103
|
*/
|
|
99
104
|
getPkGetterConverted(meta) {
|
|
100
|
-
const exists = this.pkGettersConverted.get(meta
|
|
105
|
+
const exists = this.pkGettersConverted.get(meta);
|
|
101
106
|
/* v8 ignore next */
|
|
102
107
|
if (exists) {
|
|
103
108
|
return exists;
|
|
@@ -139,15 +144,16 @@ export class EntityComparator {
|
|
|
139
144
|
}
|
|
140
145
|
const code = `// compiled pk getter (with converted custom types) for entity ${meta.className}\n`
|
|
141
146
|
+ `return function(entity) {\n${lines.join('\n')}\n}`;
|
|
142
|
-
const
|
|
143
|
-
this.
|
|
147
|
+
const fnKey = `pkGetterConverted-${meta.uniqueName}`;
|
|
148
|
+
const pkSerializer = Utils.createFunction(context, code, this.config?.get('compiledFunctions'), fnKey);
|
|
149
|
+
this.pkGettersConverted.set(meta, pkSerializer);
|
|
144
150
|
return pkSerializer;
|
|
145
151
|
}
|
|
146
152
|
/**
|
|
147
153
|
* @internal Highly performance-sensitive method.
|
|
148
154
|
*/
|
|
149
155
|
getPkSerializer(meta) {
|
|
150
|
-
const exists = this.pkSerializers.get(meta
|
|
156
|
+
const exists = this.pkSerializers.get(meta);
|
|
151
157
|
/* v8 ignore next */
|
|
152
158
|
if (exists) {
|
|
153
159
|
return exists;
|
|
@@ -191,25 +197,25 @@ export class EntityComparator {
|
|
|
191
197
|
}
|
|
192
198
|
const code = `// compiled pk serializer for entity ${meta.className}\n`
|
|
193
199
|
+ `return function(entity) {\n${lines.join('\n')}\n}`;
|
|
194
|
-
const
|
|
195
|
-
this.
|
|
200
|
+
const fnKey = `pkSerializer-${meta.uniqueName}`;
|
|
201
|
+
const pkSerializer = Utils.createFunction(context, code, this.config?.get('compiledFunctions'), fnKey);
|
|
202
|
+
this.pkSerializers.set(meta, pkSerializer);
|
|
196
203
|
return pkSerializer;
|
|
197
204
|
}
|
|
198
205
|
/**
|
|
199
206
|
* @internal Highly performance-sensitive method.
|
|
200
207
|
*/
|
|
201
208
|
getSnapshotGenerator(entityName) {
|
|
202
|
-
|
|
203
|
-
const exists = this.snapshotGenerators.get(
|
|
209
|
+
const meta = this.metadata.find(entityName);
|
|
210
|
+
const exists = this.snapshotGenerators.get(meta);
|
|
204
211
|
if (exists) {
|
|
205
212
|
return exists;
|
|
206
213
|
}
|
|
207
|
-
const meta = this.metadata.find(entityName);
|
|
208
214
|
const lines = [];
|
|
209
215
|
const context = new Map();
|
|
210
216
|
context.set('clone', clone);
|
|
211
217
|
context.set('cloneEmbeddable', (o) => this.platform.cloneEmbeddable(o)); // do not clone prototypes
|
|
212
|
-
if (meta.discriminatorValue) {
|
|
218
|
+
if (meta.root.inheritanceType === 'sti' && meta.discriminatorValue) {
|
|
213
219
|
lines.push(` ret${this.wrap(meta.root.discriminatorColumn)} = '${meta.discriminatorValue}'`);
|
|
214
220
|
}
|
|
215
221
|
const getRootProperty = (prop) => prop.embedded ? getRootProperty(meta.properties[prop.embedded[0]]) : prop;
|
|
@@ -221,8 +227,9 @@ export class EntityComparator {
|
|
|
221
227
|
})
|
|
222
228
|
.forEach(prop => lines.push(this.getPropertySnapshot(meta, prop, context, this.wrap(prop.name), this.wrap(prop.name), [prop.name])));
|
|
223
229
|
const code = `return function(entity) {\n const ret = {};\n${lines.join('\n')}\n return ret;\n}`;
|
|
224
|
-
const
|
|
225
|
-
this.
|
|
230
|
+
const fnKey = `snapshotGenerator-${meta.uniqueName}`;
|
|
231
|
+
const snapshotGenerator = Utils.createFunction(context, code, this.config?.get('compiledFunctions'), fnKey);
|
|
232
|
+
this.snapshotGenerators.set(meta, snapshotGenerator);
|
|
226
233
|
return snapshotGenerator;
|
|
227
234
|
}
|
|
228
235
|
/**
|
|
@@ -272,14 +279,14 @@ export class EntityComparator {
|
|
|
272
279
|
/**
|
|
273
280
|
* @internal Highly performance-sensitive method.
|
|
274
281
|
*/
|
|
275
|
-
getResultMapper(
|
|
276
|
-
const exists = this.mappers.get(
|
|
282
|
+
getResultMapper(meta) {
|
|
283
|
+
const exists = this.mappers.get(meta);
|
|
277
284
|
if (exists) {
|
|
278
285
|
return exists;
|
|
279
286
|
}
|
|
280
|
-
const meta = this.metadata.get(entityName);
|
|
281
287
|
const lines = [];
|
|
282
288
|
const context = new Map();
|
|
289
|
+
context.set('PolymorphicRef', PolymorphicRef);
|
|
283
290
|
const tz = this.platform.getTimezone();
|
|
284
291
|
const parseDate = (key, value, padding = '') => {
|
|
285
292
|
lines.push(`${padding} if (${value} == null || ${value} instanceof Date) {`);
|
|
@@ -306,12 +313,28 @@ export class EntityComparator {
|
|
|
306
313
|
if (!prop.fieldNames) {
|
|
307
314
|
continue;
|
|
308
315
|
}
|
|
316
|
+
if (prop.polymorphic && prop.fieldNames.length >= 2) {
|
|
317
|
+
const discriminatorField = prop.fieldNames[0];
|
|
318
|
+
const idFields = prop.fieldNames.slice(1);
|
|
319
|
+
lines.push(`${padding} if (${prop.fieldNames.map(field => `typeof ${this.propName(field)} === 'undefined'`).join(' && ')}) {`);
|
|
320
|
+
lines.push(`${padding} } else if (${prop.fieldNames.map(field => `${this.propName(field)} != null`).join(' && ')}) {`);
|
|
321
|
+
if (idFields.length === 1) {
|
|
322
|
+
lines.push(`${padding} ret${this.wrap(prop.name)} = new PolymorphicRef(${this.propName(discriminatorField)}, ${this.propName(idFields[0])});`);
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
lines.push(`${padding} ret${this.wrap(prop.name)} = new PolymorphicRef(${this.propName(discriminatorField)}, [${idFields.map(f => this.propName(f)).join(', ')}]);`);
|
|
326
|
+
}
|
|
327
|
+
lines.push(...prop.fieldNames.map(field => `${padding} ${this.propName(field, 'mapped')} = true;`));
|
|
328
|
+
lines.push(`${padding} } else if (${prop.fieldNames.map(field => `${this.propName(field)} == null`).join(' && ')}) {\n${padding} ret${this.wrap(prop.name)} = null;`);
|
|
329
|
+
lines.push(...prop.fieldNames.map(field => `${padding} ${this.propName(field, 'mapped')} = true;`), ' }');
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
309
332
|
if (prop.targetMeta && prop.fieldNames.length > 1) {
|
|
310
333
|
lines.push(`${padding} if (${prop.fieldNames.map(field => `typeof ${this.propName(field)} === 'undefined'`).join(' && ')}) {`);
|
|
311
334
|
lines.push(`${padding} } else if (${prop.fieldNames.map(field => `${this.propName(field)} != null`).join(' && ')}) {`);
|
|
312
335
|
lines.push(`${padding} ret${this.wrap(prop.name)} = ${this.createCompositeKeyArray(prop)};`);
|
|
313
336
|
lines.push(...prop.fieldNames.map(field => `${padding} ${this.propName(field, 'mapped')} = true;`));
|
|
314
|
-
lines.push(`${padding} } else if (${prop.fieldNames.map(field => `${this.propName(field)} == null`).join(' && ')}) {\n ret${this.wrap(prop.name)} = null;`);
|
|
337
|
+
lines.push(`${padding} } else if (${prop.fieldNames.map(field => `${this.propName(field)} == null`).join(' && ')}) {\n${padding} ret${this.wrap(prop.name)} = null;`);
|
|
315
338
|
lines.push(...prop.fieldNames.map(field => `${padding} ${this.propName(field, 'mapped')} = true;`), ' }');
|
|
316
339
|
continue;
|
|
317
340
|
}
|
|
@@ -336,9 +359,9 @@ export class EntityComparator {
|
|
|
336
359
|
context.set(`mapEmbeddedResult_${idx}`, (data) => {
|
|
337
360
|
const item = parseJsonSafe(data);
|
|
338
361
|
if (Array.isArray(item)) {
|
|
339
|
-
return item.map(row => row == null ? row : this.getResultMapper(prop.
|
|
362
|
+
return item.map(row => row == null ? row : this.getResultMapper(prop.targetMeta)(row));
|
|
340
363
|
}
|
|
341
|
-
return item == null ? item : this.getResultMapper(prop.
|
|
364
|
+
return item == null ? item : this.getResultMapper(prop.targetMeta)(item);
|
|
342
365
|
});
|
|
343
366
|
lines.push(`${padding} if (typeof ${this.propName(prop.fieldNames[0])} !== 'undefined') {`);
|
|
344
367
|
lines.push(`${padding} ret${this.wrap(prop.name)} = ${this.propName(prop.fieldNames[0])} == null ? ${this.propName(prop.fieldNames[0])} : mapEmbeddedResult_${idx}(${this.propName(prop.fieldNames[0])});`);
|
|
@@ -370,8 +393,9 @@ export class EntityComparator {
|
|
|
370
393
|
lines.push(` for (let k in result) { if (Object.hasOwn(result, k) && !mapped[k] && ret[k] === undefined) ret[k] = result[k]; }`);
|
|
371
394
|
const code = `// compiled mapper for entity ${meta.className}\n`
|
|
372
395
|
+ `return function(result) {\n const ret = {};\n${lines.join('\n')}\n return ret;\n}`;
|
|
373
|
-
const
|
|
374
|
-
this.
|
|
396
|
+
const fnKey = `resultMapper-${meta.uniqueName}`;
|
|
397
|
+
const resultMapper = Utils.createFunction(context, code, this.config?.get('compiledFunctions'), fnKey);
|
|
398
|
+
this.mappers.set(meta, resultMapper);
|
|
375
399
|
return resultMapper;
|
|
376
400
|
}
|
|
377
401
|
getPropertyCondition(path) {
|
|
@@ -482,7 +506,7 @@ export class EntityComparator {
|
|
|
482
506
|
const convertorKey = this.safeKey(prop.name);
|
|
483
507
|
context.set(`convertToDatabaseValue_${convertorKey}`, (val) => {
|
|
484
508
|
/* v8 ignore next */
|
|
485
|
-
if (
|
|
509
|
+
if (Raw.isKnownFragment(val)) {
|
|
486
510
|
return val;
|
|
487
511
|
}
|
|
488
512
|
return prop.customType.convertToDatabaseValue(val, this.platform, { mode: 'serialization' });
|
|
@@ -511,16 +535,51 @@ export class EntityComparator {
|
|
|
511
535
|
ret += ` ret${dataKey} = entity${entityKey};\n`;
|
|
512
536
|
}
|
|
513
537
|
}
|
|
538
|
+
else if (prop.polymorphic) {
|
|
539
|
+
const discriminatorMapKey = `discriminatorMapReverse_${prop.name}`;
|
|
540
|
+
const reverseMap = new Map();
|
|
541
|
+
for (const [key, value] of Object.entries(prop.discriminatorMap)) {
|
|
542
|
+
reverseMap.set(value, key);
|
|
543
|
+
}
|
|
544
|
+
context.set(discriminatorMapKey, reverseMap);
|
|
545
|
+
this.setToArrayHelper(context);
|
|
546
|
+
context.set('EntityIdentifier', EntityIdentifier);
|
|
547
|
+
context.set('PolymorphicRef', PolymorphicRef);
|
|
548
|
+
ret += ` if (entity${entityKey} === null) {\n`;
|
|
549
|
+
ret += ` ret${dataKey} = null;\n`;
|
|
550
|
+
ret += ` } else if (typeof entity${entityKey} !== 'undefined') {\n`;
|
|
551
|
+
ret += ` const val${level} = entity${entityKey}${unwrap};\n`;
|
|
552
|
+
ret += ` const discriminator = ${discriminatorMapKey}.get(val${level}?.constructor);\n`;
|
|
553
|
+
ret += ` const pk = val${level}?.__helper?.__identifier && !val${level}?.__helper?.hasPrimaryKey()\n`;
|
|
554
|
+
ret += ` ? val${level}.__helper.__identifier\n`;
|
|
555
|
+
ret += ` : toArray(val${level}?.__helper?.getPrimaryKey(true));\n`;
|
|
556
|
+
ret += ` ret${dataKey} = new PolymorphicRef(discriminator, pk);\n`;
|
|
557
|
+
ret += ` }\n`;
|
|
558
|
+
}
|
|
559
|
+
else if (prop.targetKey) {
|
|
560
|
+
// When targetKey is set, extract that property value instead of the PK
|
|
561
|
+
const targetProp = prop.targetMeta?.properties[prop.targetKey];
|
|
562
|
+
ret += ` if (entity${entityKey} === null) {\n`;
|
|
563
|
+
ret += ` ret${dataKey} = null;\n`;
|
|
564
|
+
ret += ` } else if (typeof entity${entityKey} !== 'undefined') {\n`;
|
|
565
|
+
ret += ` const val${level} = entity${entityKey}${unwrap};\n`;
|
|
566
|
+
if (targetProp?.customType) {
|
|
567
|
+
// If targetKey property has a custom type, convert to database value
|
|
568
|
+
const convertorKey = this.registerCustomType(targetProp, context);
|
|
569
|
+
ret += ` ret${dataKey} = convertToDatabaseValue_${convertorKey}(val${level}?.${prop.targetKey});\n`;
|
|
570
|
+
}
|
|
571
|
+
else {
|
|
572
|
+
ret += ` ret${dataKey} = val${level}?.${prop.targetKey};\n`;
|
|
573
|
+
}
|
|
574
|
+
ret += ` }\n`;
|
|
575
|
+
}
|
|
514
576
|
else {
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
return Object.values(val).map(v => toArray(v));
|
|
518
|
-
}
|
|
519
|
-
return val;
|
|
520
|
-
};
|
|
521
|
-
context.set('toArray', toArray);
|
|
577
|
+
this.setToArrayHelper(context);
|
|
578
|
+
context.set('EntityIdentifier', EntityIdentifier);
|
|
522
579
|
ret += ` if (entity${entityKey} === null) {\n`;
|
|
523
580
|
ret += ` ret${dataKey} = null;\n`;
|
|
581
|
+
ret += ` } else if (entity${entityKey}?.__helper.__identifier && !entity${entityKey}.__helper.hasPrimaryKey()) {\n`;
|
|
582
|
+
ret += ` ret${dataKey} = entity${entityKey}?.__helper.__identifier;\n`;
|
|
524
583
|
ret += ` } else if (typeof entity${entityKey} !== 'undefined') {\n`;
|
|
525
584
|
ret += ` ret${dataKey} = toArray(entity${entityKey}.__helper.getPrimaryKey(true));\n`;
|
|
526
585
|
ret += ` }\n`;
|
|
@@ -544,11 +603,11 @@ export class EntityComparator {
|
|
|
544
603
|
* @internal Highly performance-sensitive method.
|
|
545
604
|
*/
|
|
546
605
|
getEntityComparator(entityName) {
|
|
547
|
-
const
|
|
606
|
+
const meta = this.metadata.find(entityName);
|
|
607
|
+
const exists = this.comparators.get(meta);
|
|
548
608
|
if (exists) {
|
|
549
609
|
return exists;
|
|
550
610
|
}
|
|
551
|
-
const meta = this.metadata.find(entityName);
|
|
552
611
|
const lines = [];
|
|
553
612
|
const context = new Map();
|
|
554
613
|
context.set('compareArrays', compareArrays);
|
|
@@ -569,8 +628,9 @@ export class EntityComparator {
|
|
|
569
628
|
lines.push(`}`);
|
|
570
629
|
const code = `// compiled comparator for entity ${meta.className}\n`
|
|
571
630
|
+ `return function(last, current, options) {\n const diff = {};\n${lines.join('\n')}\n return diff;\n}`;
|
|
572
|
-
const
|
|
573
|
-
this.
|
|
631
|
+
const fnKey = `comparator-${meta.uniqueName}`;
|
|
632
|
+
const comparator = Utils.createFunction(context, code, this.config?.get('compiledFunctions'), fnKey);
|
|
633
|
+
this.comparators.set(meta, comparator);
|
|
574
634
|
return comparator;
|
|
575
635
|
}
|
|
576
636
|
getGenericComparator(prop, cond) {
|
|
@@ -586,18 +646,28 @@ export class EntityComparator {
|
|
|
586
646
|
getPropertyComparator(prop, context) {
|
|
587
647
|
let type = prop.type.toLowerCase();
|
|
588
648
|
if (prop.kind !== ReferenceKind.SCALAR && prop.kind !== ReferenceKind.EMBEDDED) {
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
type = 'array';
|
|
649
|
+
if (prop.polymorphic) {
|
|
650
|
+
type = 'object';
|
|
592
651
|
}
|
|
593
652
|
else {
|
|
594
|
-
|
|
653
|
+
const meta2 = prop.targetMeta;
|
|
654
|
+
if (meta2.primaryKeys.length > 1) {
|
|
655
|
+
type = 'array';
|
|
656
|
+
}
|
|
657
|
+
else {
|
|
658
|
+
type = meta2.getPrimaryProp().type.toLowerCase();
|
|
659
|
+
}
|
|
595
660
|
}
|
|
596
661
|
}
|
|
597
662
|
if (prop.customType) {
|
|
598
663
|
if (prop.customType.compareValues) {
|
|
599
664
|
const idx = this.tmpIndex++;
|
|
600
|
-
context.set(`compareValues_${idx}`, (a, b) =>
|
|
665
|
+
context.set(`compareValues_${idx}`, (a, b) => {
|
|
666
|
+
if (Raw.isKnownFragment(a) || Raw.isKnownFragment(b)) {
|
|
667
|
+
return Raw.getKnownFragment(a) === Raw.getKnownFragment(b);
|
|
668
|
+
}
|
|
669
|
+
return prop.customType.compareValues(a, b);
|
|
670
|
+
});
|
|
601
671
|
return this.getGenericComparator(this.wrap(prop.name), `!compareValues_${idx}(last${this.wrap(prop.name)}, current${this.wrap(prop.name)})`);
|
|
602
672
|
}
|
|
603
673
|
type = prop.customType.compareAsType().toLowerCase();
|
|
@@ -638,6 +708,22 @@ export class EntityComparator {
|
|
|
638
708
|
safeKey(key) {
|
|
639
709
|
return key.replace(/\W/g, '_');
|
|
640
710
|
}
|
|
711
|
+
/**
|
|
712
|
+
* Sets the toArray helper in the context if not already set.
|
|
713
|
+
* Used for converting composite PKs to arrays.
|
|
714
|
+
*/
|
|
715
|
+
setToArrayHelper(context) {
|
|
716
|
+
if (context.has('toArray')) {
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
const toArray = (val) => {
|
|
720
|
+
if (Utils.isPlainObject(val)) {
|
|
721
|
+
return Object.values(val).map(v => toArray(v));
|
|
722
|
+
}
|
|
723
|
+
return val;
|
|
724
|
+
};
|
|
725
|
+
context.set('toArray', toArray);
|
|
726
|
+
}
|
|
641
727
|
/**
|
|
642
728
|
* perf: used to generate list of comparable properties during discovery, so we speed up the runtime comparison
|
|
643
729
|
*/
|
package/utils/QueryHelper.d.ts
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
import type { Dictionary, EntityMetadata, EntityProperty, FilterDef, FilterQuery } from '../typings.js';
|
|
1
|
+
import type { Dictionary, EntityMetadata, EntityName, EntityProperty, FilterDef, FilterQuery } from '../typings.js';
|
|
2
|
+
import { type QueryOrderMap } from '../enums.js';
|
|
2
3
|
import type { Platform } from '../platforms/Platform.js';
|
|
3
4
|
import type { MetadataStorage } from '../metadata/MetadataStorage.js';
|
|
4
5
|
import type { FilterOptions } from '../drivers/IDatabaseDriver.js';
|
|
5
6
|
/** @internal */
|
|
6
7
|
export declare class QueryHelper {
|
|
7
8
|
static readonly SUPPORTED_OPERATORS: string[];
|
|
9
|
+
/**
|
|
10
|
+
* Finds the discriminator value (key) for a given entity class in a discriminator map.
|
|
11
|
+
*/
|
|
12
|
+
static findDiscriminatorValue<T>(discriminatorMap: Dictionary<T>, targetClass: T): string | undefined;
|
|
8
13
|
static processParams(params: unknown): any;
|
|
9
|
-
static processObjectParams<T extends
|
|
14
|
+
static processObjectParams<T extends Dictionary>(params?: T): T;
|
|
10
15
|
/**
|
|
11
16
|
* converts `{ account: { $or: [ [Object], [Object] ] } }`
|
|
12
17
|
* to `{ $or: [ { account: [Object] }, { account: [Object] } ] }`
|
|
@@ -14,22 +19,27 @@ export declare class QueryHelper {
|
|
|
14
19
|
static liftGroupOperators<T extends object>(where: Dictionary, meta: EntityMetadata<T>, metadata: MetadataStorage, key?: string): string | undefined;
|
|
15
20
|
static inlinePrimaryKeyObjects<T extends object>(where: Dictionary, meta: EntityMetadata<T>, metadata: MetadataStorage, key?: string): boolean;
|
|
16
21
|
static processWhere<T extends object>(options: ProcessWhereOptions<T>): FilterQuery<T>;
|
|
17
|
-
static getActiveFilters(
|
|
22
|
+
static getActiveFilters<T>(meta: EntityMetadata<T>, options: FilterOptions | undefined, filters: Dictionary<FilterDef>): FilterDef[];
|
|
18
23
|
static mergePropertyFilters(propFilters: FilterOptions | undefined, options: FilterOptions | undefined): FilterOptions | undefined;
|
|
19
|
-
static isFilterActive(
|
|
24
|
+
static isFilterActive<T>(meta: EntityMetadata<T>, filterName: string, filter: FilterDef, options: Dictionary<boolean | Dictionary>): boolean;
|
|
20
25
|
static processCustomType<T extends object>(prop: EntityProperty<T>, cond: FilterQuery<T>, platform: Platform, key?: string, fromQuery?: boolean): FilterQuery<T>;
|
|
21
26
|
private static isSupportedOperator;
|
|
22
27
|
private static processJsonCondition;
|
|
23
28
|
private static getValueType;
|
|
24
29
|
static findProperty<T>(fieldName: string, options: ProcessWhereOptions<T>): EntityProperty<T> | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Merges multiple orderBy sources with key-level deduplication (first-seen key wins).
|
|
32
|
+
* RawQueryFragment symbol keys are never deduped (each is unique).
|
|
33
|
+
*/
|
|
34
|
+
static mergeOrderBy<T>(...sources: (QueryOrderMap<T> | QueryOrderMap<T>[] | undefined)[]): QueryOrderMap<T>[];
|
|
25
35
|
}
|
|
26
36
|
interface ProcessWhereOptions<T> {
|
|
27
37
|
where: FilterQuery<T>;
|
|
28
|
-
entityName:
|
|
38
|
+
entityName: EntityName<T>;
|
|
29
39
|
metadata: MetadataStorage;
|
|
30
40
|
platform: Platform;
|
|
31
41
|
aliased?: boolean;
|
|
32
|
-
aliasMap?: Dictionary<
|
|
42
|
+
aliasMap?: Dictionary<EntityName>;
|
|
33
43
|
convertCustomTypes?: boolean;
|
|
34
44
|
root?: boolean;
|
|
35
45
|
type?: 'where' | 'orderBy';
|
package/utils/QueryHelper.js
CHANGED
|
@@ -3,10 +3,16 @@ import { Utils } from './Utils.js';
|
|
|
3
3
|
import { ARRAY_OPERATORS, GroupOperator, JSON_KEY_OPERATORS, ReferenceKind } from '../enums.js';
|
|
4
4
|
import { JsonType } from '../types/JsonType.js';
|
|
5
5
|
import { helper } from '../entity/wrap.js';
|
|
6
|
-
import { isRaw,
|
|
6
|
+
import { isRaw, Raw } from './RawQueryFragment.js';
|
|
7
7
|
/** @internal */
|
|
8
8
|
export class QueryHelper {
|
|
9
9
|
static SUPPORTED_OPERATORS = ['>', '<', '<=', '>=', '!', '!='];
|
|
10
|
+
/**
|
|
11
|
+
* Finds the discriminator value (key) for a given entity class in a discriminator map.
|
|
12
|
+
*/
|
|
13
|
+
static findDiscriminatorValue(discriminatorMap, targetClass) {
|
|
14
|
+
return Object.entries(discriminatorMap).find(([, cls]) => cls === targetClass)?.[0];
|
|
15
|
+
}
|
|
10
16
|
static processParams(params) {
|
|
11
17
|
if (Reference.isReference(params)) {
|
|
12
18
|
params = params.unwrap();
|
|
@@ -29,7 +35,7 @@ export class QueryHelper {
|
|
|
29
35
|
return params;
|
|
30
36
|
}
|
|
31
37
|
static processObjectParams(params = {}) {
|
|
32
|
-
Utils.
|
|
38
|
+
Utils.getObjectQueryKeys(params).forEach(k => {
|
|
33
39
|
params[k] = QueryHelper.processParams(params[k]);
|
|
34
40
|
});
|
|
35
41
|
return params;
|
|
@@ -62,7 +68,10 @@ export class QueryHelper {
|
|
|
62
68
|
for (const k of keys) {
|
|
63
69
|
const value = where[k];
|
|
64
70
|
const prop = meta.properties[k];
|
|
65
|
-
|
|
71
|
+
// Polymorphic relations use multiple columns (discriminator + FK), so they cannot
|
|
72
|
+
// participate in the standard single-column FK expansion. Query by discriminator
|
|
73
|
+
// column directly instead, e.g. { likeableType: 'post', likeableId: 1 }.
|
|
74
|
+
if (!prop || ![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) || prop.polymorphic) {
|
|
66
75
|
continue;
|
|
67
76
|
}
|
|
68
77
|
const op = this.liftGroupOperators(value, prop.targetMeta, metadata, k);
|
|
@@ -98,7 +107,7 @@ export class QueryHelper {
|
|
|
98
107
|
}));
|
|
99
108
|
}
|
|
100
109
|
Object.keys(where).forEach(k => {
|
|
101
|
-
const meta2 = metadata.find(meta.properties[k]?.
|
|
110
|
+
const meta2 = metadata.find(meta.properties[k]?.targetMeta?.class) || meta;
|
|
102
111
|
if (this.inlinePrimaryKeyObjects(where[k], meta2, metadata, k)) {
|
|
103
112
|
where[k] = Utils.getPrimaryKeyValues(where[k], meta2, true);
|
|
104
113
|
}
|
|
@@ -126,7 +135,7 @@ export class QueryHelper {
|
|
|
126
135
|
where = { [Utils.getPrimaryKeyHash(meta.primaryKeys)]: where };
|
|
127
136
|
}
|
|
128
137
|
if (Array.isArray(where) && root) {
|
|
129
|
-
const rootPrimaryKey = meta ? Utils.getPrimaryKeyHash(meta.primaryKeys) : entityName;
|
|
138
|
+
const rootPrimaryKey = meta ? Utils.getPrimaryKeyHash(meta.primaryKeys) : Utils.className(entityName);
|
|
130
139
|
let cond = { [rootPrimaryKey]: { $in: where } };
|
|
131
140
|
// @ts-ignore
|
|
132
141
|
// detect tuple comparison, use `$or` in case the number of constituents don't match
|
|
@@ -138,12 +147,10 @@ export class QueryHelper {
|
|
|
138
147
|
if (!Utils.isPlainObject(where)) {
|
|
139
148
|
return where;
|
|
140
149
|
}
|
|
141
|
-
return
|
|
150
|
+
return Utils.getObjectQueryKeys(where).reduce((o, key) => {
|
|
142
151
|
let value = where[key];
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
const composite = keys > 1;
|
|
146
|
-
if (Array.isArray(value) && value.length === 0 && RawQueryFragment.isKnownFragment(key)) {
|
|
152
|
+
const customExpression = Raw.isKnownFragmentSymbol(key);
|
|
153
|
+
if (Array.isArray(value) && value.length === 0 && customExpression) {
|
|
147
154
|
o[key] = value;
|
|
148
155
|
return o;
|
|
149
156
|
}
|
|
@@ -157,6 +164,9 @@ export class QueryHelper {
|
|
|
157
164
|
o[rootPrimaryKey] = { [key]: QueryHelper.processWhere({ ...options, where: value, root: false }) };
|
|
158
165
|
return o;
|
|
159
166
|
}
|
|
167
|
+
const prop = customExpression ? null : this.findProperty(key, options);
|
|
168
|
+
const keys = prop?.joinColumns?.length ?? 0;
|
|
169
|
+
const composite = keys > 1;
|
|
160
170
|
if (prop?.customType && convertCustomTypes && !isRaw(value)) {
|
|
161
171
|
value = QueryHelper.processCustomType(prop, value, platform, undefined, true);
|
|
162
172
|
}
|
|
@@ -164,7 +174,7 @@ export class QueryHelper {
|
|
|
164
174
|
if (isJsonProperty && prop?.kind !== ReferenceKind.EMBEDDED) {
|
|
165
175
|
return this.processJsonCondition(o, value, [prop.fieldNames[0]], platform, aliased);
|
|
166
176
|
}
|
|
167
|
-
if (Array.isArray(value) && !Utils.isOperator(key) && !QueryHelper.isSupportedOperator(key) && !
|
|
177
|
+
if (Array.isArray(value) && !Utils.isOperator(key) && !QueryHelper.isSupportedOperator(key) && !(customExpression && Raw.getKnownFragment(key).params.length > 0) && options.type !== 'orderBy') {
|
|
168
178
|
// comparing single composite key - use $eq instead of $in
|
|
169
179
|
const op = composite && !value.every(v => Array.isArray(v)) ? '$eq' : '$in';
|
|
170
180
|
o[key] = { [op]: value };
|
|
@@ -174,7 +184,7 @@ export class QueryHelper {
|
|
|
174
184
|
o[key] = QueryHelper.processWhere({
|
|
175
185
|
...options,
|
|
176
186
|
where: value,
|
|
177
|
-
entityName: prop?.
|
|
187
|
+
entityName: prop?.targetMeta?.class ?? entityName,
|
|
178
188
|
root: false,
|
|
179
189
|
});
|
|
180
190
|
}
|
|
@@ -184,7 +194,7 @@ export class QueryHelper {
|
|
|
184
194
|
return o;
|
|
185
195
|
}, {});
|
|
186
196
|
}
|
|
187
|
-
static getActiveFilters(
|
|
197
|
+
static getActiveFilters(meta, options, filters) {
|
|
188
198
|
if (options === false) {
|
|
189
199
|
return [];
|
|
190
200
|
}
|
|
@@ -196,7 +206,7 @@ export class QueryHelper {
|
|
|
196
206
|
Object.keys(options).forEach(filter => opts[filter] = options[filter]);
|
|
197
207
|
}
|
|
198
208
|
return Object.keys(filters)
|
|
199
|
-
.filter(f => QueryHelper.isFilterActive(
|
|
209
|
+
.filter(f => QueryHelper.isFilterActive(meta, f, filters[f], opts))
|
|
200
210
|
.map(f => {
|
|
201
211
|
filters[f].name = f;
|
|
202
212
|
return filters[f];
|
|
@@ -220,8 +230,8 @@ export class QueryHelper {
|
|
|
220
230
|
}
|
|
221
231
|
return Utils.mergeConfig({}, propFilters, options);
|
|
222
232
|
}
|
|
223
|
-
static isFilterActive(
|
|
224
|
-
if (filter.entity && !filter.entity.includes(
|
|
233
|
+
static isFilterActive(meta, filterName, filter, options) {
|
|
234
|
+
if (filter.entity && !filter.entity.includes(meta.className)) {
|
|
225
235
|
return false;
|
|
226
236
|
}
|
|
227
237
|
if (options[filterName] === false) {
|
|
@@ -231,8 +241,8 @@ export class QueryHelper {
|
|
|
231
241
|
}
|
|
232
242
|
static processCustomType(prop, cond, platform, key, fromQuery) {
|
|
233
243
|
if (Utils.isPlainObject(cond)) {
|
|
234
|
-
return Utils.
|
|
235
|
-
if (Utils.isOperator(k, true) || prop.referencedPKs?.includes(k)) {
|
|
244
|
+
return Utils.getObjectQueryKeys(cond).reduce((o, k) => {
|
|
245
|
+
if (!Raw.isKnownFragmentSymbol(k) && (Utils.isOperator(k, true) || prop.referencedPKs?.includes(k))) {
|
|
236
246
|
o[k] = QueryHelper.processCustomType(prop, cond[k], platform, k, fromQuery);
|
|
237
247
|
}
|
|
238
248
|
else {
|
|
@@ -290,4 +300,29 @@ export class QueryHelper {
|
|
|
290
300
|
const meta = entityName ? options.metadata.find(entityName) : undefined;
|
|
291
301
|
return meta?.properties[propName];
|
|
292
302
|
}
|
|
303
|
+
/**
|
|
304
|
+
* Merges multiple orderBy sources with key-level deduplication (first-seen key wins).
|
|
305
|
+
* RawQueryFragment symbol keys are never deduped (each is unique).
|
|
306
|
+
*/
|
|
307
|
+
static mergeOrderBy(...sources) {
|
|
308
|
+
const result = [];
|
|
309
|
+
const seenKeys = new Set();
|
|
310
|
+
for (const source of sources) {
|
|
311
|
+
if (source == null) {
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
for (const item of Utils.asArray(source)) {
|
|
315
|
+
for (const key of Utils.getObjectQueryKeys(item)) {
|
|
316
|
+
if (typeof key === 'symbol') {
|
|
317
|
+
result.push({ [key]: item[key] });
|
|
318
|
+
}
|
|
319
|
+
else if (!seenKeys.has(key)) {
|
|
320
|
+
seenKeys.add(key);
|
|
321
|
+
result.push({ [key]: item[key] });
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return result;
|
|
327
|
+
}
|
|
293
328
|
}
|