@mikro-orm/core 7.0.0-dev.113 → 7.0.0-dev.115
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 +8 -8
- package/EntityManager.js +47 -69
- package/MikroORM.d.ts +1 -1
- package/MikroORM.js +2 -3
- package/drivers/DatabaseDriver.d.ts +11 -11
- package/drivers/DatabaseDriver.js +8 -9
- package/drivers/IDatabaseDriver.d.ts +12 -12
- package/entity/Collection.js +7 -6
- package/entity/EntityAssigner.js +9 -9
- package/entity/EntityFactory.js +14 -17
- package/entity/EntityHelper.d.ts +2 -2
- package/entity/EntityHelper.js +2 -2
- package/entity/EntityLoader.d.ts +3 -3
- package/entity/EntityLoader.js +22 -35
- package/entity/WrappedEntity.js +1 -1
- package/entity/defineEntity.d.ts +11 -11
- package/entity/validators.js +2 -2
- package/errors.d.ts +8 -8
- package/errors.js +14 -13
- package/hydration/ObjectHydrator.js +25 -18
- package/metadata/EntitySchema.d.ts +5 -5
- package/metadata/EntitySchema.js +23 -21
- package/metadata/MetadataDiscovery.d.ts +2 -3
- package/metadata/MetadataDiscovery.js +119 -92
- package/metadata/MetadataProvider.js +2 -0
- package/metadata/MetadataStorage.d.ts +13 -6
- package/metadata/MetadataStorage.js +64 -19
- package/metadata/MetadataValidator.d.ts +2 -2
- package/metadata/MetadataValidator.js +22 -28
- package/metadata/types.d.ts +3 -3
- package/package.json +1 -1
- package/platforms/Platform.js +2 -2
- package/serialization/EntitySerializer.js +2 -2
- package/serialization/EntityTransformer.js +6 -6
- package/serialization/SerializationContext.d.ts +6 -6
- package/typings.d.ts +19 -17
- package/typings.js +15 -10
- package/unit-of-work/ChangeSet.d.ts +2 -3
- package/unit-of-work/ChangeSet.js +2 -3
- package/unit-of-work/ChangeSetComputer.js +3 -3
- package/unit-of-work/ChangeSetPersister.js +14 -14
- package/unit-of-work/CommitOrderCalculator.d.ts +12 -10
- package/unit-of-work/CommitOrderCalculator.js +13 -13
- package/unit-of-work/UnitOfWork.d.ts +3 -3
- package/unit-of-work/UnitOfWork.js +46 -45
- package/utils/AbstractSchemaGenerator.js +7 -7
- package/utils/Configuration.d.ts +0 -5
- package/utils/Cursor.js +3 -3
- package/utils/DataloaderUtils.js +13 -11
- package/utils/EntityComparator.d.ts +6 -6
- package/utils/EntityComparator.js +30 -27
- package/utils/QueryHelper.d.ts +6 -6
- package/utils/QueryHelper.js +18 -17
- package/utils/RawQueryFragment.d.ts +11 -12
- package/utils/RawQueryFragment.js +28 -55
- package/utils/TransactionManager.js +1 -1
- package/utils/Utils.d.ts +3 -1
- package/utils/Utils.js +10 -2
- package/utils/clone.js +7 -21
- package/utils/env-vars.js +0 -1
- package/utils/upsert-utils.d.ts +4 -4
package/utils/DataloaderUtils.js
CHANGED
|
@@ -11,7 +11,7 @@ export class DataloaderUtils {
|
|
|
11
11
|
static groupPrimaryKeysByEntityAndOpts(refsWithOpts) {
|
|
12
12
|
const map = new Map();
|
|
13
13
|
for (const [ref, opts] of refsWithOpts) {
|
|
14
|
-
/* The key is a combination of the
|
|
14
|
+
/* The key is a combination of the uniqueName (a unique table name based identifier) and a stringified version if the load options because we want
|
|
15
15
|
to map each combination of entities/options into separate find queries in order to return accurate results.
|
|
16
16
|
This could be further optimized finding the "lowest common denominator" among the different options
|
|
17
17
|
for each Entity and firing a single query for each Entity instead of Entity+options combination.
|
|
@@ -24,7 +24,7 @@ export class DataloaderUtils {
|
|
|
24
24
|
Thus such approach should probably be configurable, if not opt-in.
|
|
25
25
|
NOTE: meta + opts multi maps (https://github.com/martian17/ds-js) might be a more elegant way
|
|
26
26
|
to implement this but not necessarily faster. */
|
|
27
|
-
const key = `${helper(ref).__meta.
|
|
27
|
+
const key = `${helper(ref).__meta.uniqueName}|${JSON.stringify(opts ?? {})}`;
|
|
28
28
|
let primaryKeysSet = map.get(key);
|
|
29
29
|
if (primaryKeysSet == null) {
|
|
30
30
|
primaryKeysSet = new Set();
|
|
@@ -42,9 +42,10 @@ export class DataloaderUtils {
|
|
|
42
42
|
return async (refsWithOpts) => {
|
|
43
43
|
const groupedIdsMap = DataloaderUtils.groupPrimaryKeysByEntityAndOpts(refsWithOpts);
|
|
44
44
|
const promises = Array.from(groupedIdsMap).map(([key, idsSet]) => {
|
|
45
|
-
const
|
|
45
|
+
const uniqueName = key.substring(0, key.indexOf('|'));
|
|
46
46
|
const opts = JSON.parse(key.substring(key.indexOf('|') + 1));
|
|
47
|
-
|
|
47
|
+
const meta = em.getMetadata().getByUniqueName(uniqueName);
|
|
48
|
+
return em.find(meta.class, Array.from(idsSet), opts);
|
|
48
49
|
});
|
|
49
50
|
await Promise.all(promises);
|
|
50
51
|
/* Instead of assigning each find result to the original reference we use a shortcut
|
|
@@ -70,7 +71,7 @@ export class DataloaderUtils {
|
|
|
70
71
|
The value is another Map which we can use to filter the find query to get results pertaining to the collections that have been dataloaded:
|
|
71
72
|
its keys are the props we are going to filter to and its values are the corresponding PKs.
|
|
72
73
|
*/
|
|
73
|
-
const key = `${col.property.targetMeta.
|
|
74
|
+
const key = `${col.property.targetMeta.uniqueName}|${JSON.stringify(opts ?? {})}`;
|
|
74
75
|
let filterMap = entitiesMap.get(key); // We are going to use this map to filter the entities pertaining to the collections that have been dataloaded.
|
|
75
76
|
if (filterMap == null) {
|
|
76
77
|
filterMap = new Map();
|
|
@@ -97,9 +98,10 @@ export class DataloaderUtils {
|
|
|
97
98
|
*/
|
|
98
99
|
static entitiesAndOptsMapToQueries(entitiesAndOptsMap, em) {
|
|
99
100
|
return Array.from(entitiesAndOptsMap, async ([key, filterMap]) => {
|
|
100
|
-
const
|
|
101
|
+
const uniqueName = key.substring(0, key.indexOf('|'));
|
|
101
102
|
const opts = JSON.parse(key.substring(key.indexOf('|') + 1));
|
|
102
|
-
const
|
|
103
|
+
const meta = em.getMetadata().getByUniqueName(uniqueName);
|
|
104
|
+
const res = await em.find(meta.class, opts?.where != null && Object.keys(opts.where).length > 0 ?
|
|
103
105
|
{
|
|
104
106
|
$and: [
|
|
105
107
|
{
|
|
@@ -121,7 +123,7 @@ export class DataloaderUtils {
|
|
|
121
123
|
...(opts.populate === false ? [] : opts.populate ?? []),
|
|
122
124
|
...Array.from(filterMap.keys()).filter(
|
|
123
125
|
// We need to do so only if the inverse side is a collection, because we can already retrieve the PK from a reference without having to load it
|
|
124
|
-
prop =>
|
|
126
|
+
prop => meta.properties[prop]?.ref !== true),
|
|
125
127
|
],
|
|
126
128
|
});
|
|
127
129
|
return [key, res];
|
|
@@ -164,7 +166,7 @@ export class DataloaderUtils {
|
|
|
164
166
|
// We need to filter the results in order to map each input collection
|
|
165
167
|
// to a subset of each query matching the collection items.
|
|
166
168
|
return collsWithOpts.map(([col, opts]) => {
|
|
167
|
-
const key = `${col.property.targetMeta.
|
|
169
|
+
const key = `${col.property.targetMeta.uniqueName}|${JSON.stringify(opts ?? {})}`;
|
|
168
170
|
const entities = resultsMap.get(key);
|
|
169
171
|
if (entities == null) {
|
|
170
172
|
// Should never happen
|
|
@@ -183,7 +185,7 @@ export class DataloaderUtils {
|
|
|
183
185
|
return async (collsWithOpts) => {
|
|
184
186
|
const groups = new Map();
|
|
185
187
|
for (const [col, opts] of collsWithOpts) {
|
|
186
|
-
const key = `${col.property.targetMeta.
|
|
188
|
+
const key = `${col.property.targetMeta.uniqueName}.${col.property.name}|${JSON.stringify(opts ?? {})}`;
|
|
187
189
|
const value = groups.get(key) ?? [];
|
|
188
190
|
value.push([col, opts ?? {}]);
|
|
189
191
|
groups.set(key, value);
|
|
@@ -198,7 +200,7 @@ export class DataloaderUtils {
|
|
|
198
200
|
const owners = group.map(c => c[0].owner);
|
|
199
201
|
const $or = [];
|
|
200
202
|
// a bit of a hack, but we need to prefix the key, since we have only a column name, not a property name
|
|
201
|
-
const alias = em.config.getNamingStrategy().aliasName(prop.pivotEntity, 0);
|
|
203
|
+
const alias = em.config.getNamingStrategy().aliasName(Utils.className(prop.pivotEntity), 0);
|
|
202
204
|
const fk = `${alias}.${Utils.getPrimaryKeyHash(prop.joinColumns)}`;
|
|
203
205
|
for (const c of group) {
|
|
204
206
|
$or.push({ $and: [c[1]?.where ?? {}, { [fk]: c[0].owner }] });
|
|
@@ -20,19 +20,19 @@ export declare class EntityComparator {
|
|
|
20
20
|
/**
|
|
21
21
|
* Computes difference between two entities.
|
|
22
22
|
*/
|
|
23
|
-
diffEntities<T>(entityName:
|
|
23
|
+
diffEntities<T extends object>(entityName: EntityName<T>, a: EntityData<T>, b: EntityData<T>, options?: {
|
|
24
24
|
includeInverseSides?: boolean;
|
|
25
25
|
}): EntityData<T>;
|
|
26
|
-
matching<T>(entityName:
|
|
26
|
+
matching<T extends object>(entityName: EntityName<T>, a: EntityData<T>, b: EntityData<T>): boolean;
|
|
27
27
|
/**
|
|
28
28
|
* Removes ORM specific code from entities and prepares it for serializing. Used before change set computation.
|
|
29
29
|
* References will be mapped to primary keys, collections to arrays of primary keys.
|
|
30
30
|
*/
|
|
31
|
-
prepareEntity<T>(entity: T): EntityData<T>;
|
|
31
|
+
prepareEntity<T extends object>(entity: T): EntityData<T>;
|
|
32
32
|
/**
|
|
33
33
|
* Maps database columns to properties.
|
|
34
34
|
*/
|
|
35
|
-
mapResult<T>(
|
|
35
|
+
mapResult<T>(meta: EntityMetadata<T>, result: EntityDictionary<T>): EntityData<T>;
|
|
36
36
|
/**
|
|
37
37
|
* @internal Highly performance-sensitive method.
|
|
38
38
|
*/
|
|
@@ -64,7 +64,7 @@ export declare class EntityComparator {
|
|
|
64
64
|
/**
|
|
65
65
|
* @internal Highly performance-sensitive method.
|
|
66
66
|
*/
|
|
67
|
-
getResultMapper<T>(
|
|
67
|
+
getResultMapper<T>(meta: EntityMetadata<T>): ResultMapper<T>;
|
|
68
68
|
private getPropertyCondition;
|
|
69
69
|
private getEmbeddedArrayPropertySnapshot;
|
|
70
70
|
/**
|
|
@@ -78,7 +78,7 @@ export declare class EntityComparator {
|
|
|
78
78
|
/**
|
|
79
79
|
* @internal Highly performance-sensitive method.
|
|
80
80
|
*/
|
|
81
|
-
getEntityComparator<T extends object>(entityName:
|
|
81
|
+
getEntityComparator<T extends object>(entityName: EntityName<T>): Comparator<T>;
|
|
82
82
|
private getGenericComparator;
|
|
83
83
|
private getPropertyComparator;
|
|
84
84
|
private wrap;
|
|
@@ -2,7 +2,7 @@ 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
6
|
export class EntityComparator {
|
|
7
7
|
metadata;
|
|
8
8
|
platform;
|
|
@@ -33,21 +33,21 @@ export class EntityComparator {
|
|
|
33
33
|
* References will be mapped to primary keys, collections to arrays of primary keys.
|
|
34
34
|
*/
|
|
35
35
|
prepareEntity(entity) {
|
|
36
|
-
const generator = this.getSnapshotGenerator(entity.constructor
|
|
36
|
+
const generator = this.getSnapshotGenerator(entity.constructor);
|
|
37
37
|
return Utils.callCompiledFunction(generator, entity);
|
|
38
38
|
}
|
|
39
39
|
/**
|
|
40
40
|
* Maps database columns to properties.
|
|
41
41
|
*/
|
|
42
|
-
mapResult(
|
|
43
|
-
const mapper = this.getResultMapper(
|
|
42
|
+
mapResult(meta, result) {
|
|
43
|
+
const mapper = this.getResultMapper(meta);
|
|
44
44
|
return Utils.callCompiledFunction(mapper, result);
|
|
45
45
|
}
|
|
46
46
|
/**
|
|
47
47
|
* @internal Highly performance-sensitive method.
|
|
48
48
|
*/
|
|
49
49
|
getPkGetter(meta) {
|
|
50
|
-
const exists = this.pkGetters.get(meta
|
|
50
|
+
const exists = this.pkGetters.get(meta);
|
|
51
51
|
/* v8 ignore next */
|
|
52
52
|
if (exists) {
|
|
53
53
|
return exists;
|
|
@@ -90,14 +90,14 @@ export class EntityComparator {
|
|
|
90
90
|
const code = `// compiled pk serializer for entity ${meta.className}\n`
|
|
91
91
|
+ `return function(entity) {\n${lines.join('\n')}\n}`;
|
|
92
92
|
const pkSerializer = Utils.createFunction(context, code);
|
|
93
|
-
this.pkGetters.set(meta
|
|
93
|
+
this.pkGetters.set(meta, pkSerializer);
|
|
94
94
|
return pkSerializer;
|
|
95
95
|
}
|
|
96
96
|
/**
|
|
97
97
|
* @internal Highly performance-sensitive method.
|
|
98
98
|
*/
|
|
99
99
|
getPkGetterConverted(meta) {
|
|
100
|
-
const exists = this.pkGettersConverted.get(meta
|
|
100
|
+
const exists = this.pkGettersConverted.get(meta);
|
|
101
101
|
/* v8 ignore next */
|
|
102
102
|
if (exists) {
|
|
103
103
|
return exists;
|
|
@@ -140,14 +140,14 @@ export class EntityComparator {
|
|
|
140
140
|
const code = `// compiled pk getter (with converted custom types) for entity ${meta.className}\n`
|
|
141
141
|
+ `return function(entity) {\n${lines.join('\n')}\n}`;
|
|
142
142
|
const pkSerializer = Utils.createFunction(context, code);
|
|
143
|
-
this.pkGettersConverted.set(meta
|
|
143
|
+
this.pkGettersConverted.set(meta, pkSerializer);
|
|
144
144
|
return pkSerializer;
|
|
145
145
|
}
|
|
146
146
|
/**
|
|
147
147
|
* @internal Highly performance-sensitive method.
|
|
148
148
|
*/
|
|
149
149
|
getPkSerializer(meta) {
|
|
150
|
-
const exists = this.pkSerializers.get(meta
|
|
150
|
+
const exists = this.pkSerializers.get(meta);
|
|
151
151
|
/* v8 ignore next */
|
|
152
152
|
if (exists) {
|
|
153
153
|
return exists;
|
|
@@ -192,19 +192,18 @@ export class EntityComparator {
|
|
|
192
192
|
const code = `// compiled pk serializer for entity ${meta.className}\n`
|
|
193
193
|
+ `return function(entity) {\n${lines.join('\n')}\n}`;
|
|
194
194
|
const pkSerializer = Utils.createFunction(context, code);
|
|
195
|
-
this.pkSerializers.set(meta
|
|
195
|
+
this.pkSerializers.set(meta, pkSerializer);
|
|
196
196
|
return pkSerializer;
|
|
197
197
|
}
|
|
198
198
|
/**
|
|
199
199
|
* @internal Highly performance-sensitive method.
|
|
200
200
|
*/
|
|
201
201
|
getSnapshotGenerator(entityName) {
|
|
202
|
-
|
|
203
|
-
const exists = this.snapshotGenerators.get(
|
|
202
|
+
const meta = this.metadata.find(entityName);
|
|
203
|
+
const exists = this.snapshotGenerators.get(meta);
|
|
204
204
|
if (exists) {
|
|
205
205
|
return exists;
|
|
206
206
|
}
|
|
207
|
-
const meta = this.metadata.find(entityName);
|
|
208
207
|
const lines = [];
|
|
209
208
|
const context = new Map();
|
|
210
209
|
context.set('clone', clone);
|
|
@@ -222,7 +221,7 @@ export class EntityComparator {
|
|
|
222
221
|
.forEach(prop => lines.push(this.getPropertySnapshot(meta, prop, context, this.wrap(prop.name), this.wrap(prop.name), [prop.name])));
|
|
223
222
|
const code = `return function(entity) {\n const ret = {};\n${lines.join('\n')}\n return ret;\n}`;
|
|
224
223
|
const snapshotGenerator = Utils.createFunction(context, code);
|
|
225
|
-
this.snapshotGenerators.set(
|
|
224
|
+
this.snapshotGenerators.set(meta, snapshotGenerator);
|
|
226
225
|
return snapshotGenerator;
|
|
227
226
|
}
|
|
228
227
|
/**
|
|
@@ -272,12 +271,11 @@ export class EntityComparator {
|
|
|
272
271
|
/**
|
|
273
272
|
* @internal Highly performance-sensitive method.
|
|
274
273
|
*/
|
|
275
|
-
getResultMapper(
|
|
276
|
-
const exists = this.mappers.get(
|
|
274
|
+
getResultMapper(meta) {
|
|
275
|
+
const exists = this.mappers.get(meta);
|
|
277
276
|
if (exists) {
|
|
278
277
|
return exists;
|
|
279
278
|
}
|
|
280
|
-
const meta = this.metadata.get(entityName);
|
|
281
279
|
const lines = [];
|
|
282
280
|
const context = new Map();
|
|
283
281
|
const tz = this.platform.getTimezone();
|
|
@@ -336,9 +334,9 @@ export class EntityComparator {
|
|
|
336
334
|
context.set(`mapEmbeddedResult_${idx}`, (data) => {
|
|
337
335
|
const item = parseJsonSafe(data);
|
|
338
336
|
if (Array.isArray(item)) {
|
|
339
|
-
return item.map(row => row == null ? row : this.getResultMapper(prop.
|
|
337
|
+
return item.map(row => row == null ? row : this.getResultMapper(prop.targetMeta)(row));
|
|
340
338
|
}
|
|
341
|
-
return item == null ? item : this.getResultMapper(prop.
|
|
339
|
+
return item == null ? item : this.getResultMapper(prop.targetMeta)(item);
|
|
342
340
|
});
|
|
343
341
|
lines.push(`${padding} if (typeof ${this.propName(prop.fieldNames[0])} !== 'undefined') {`);
|
|
344
342
|
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])});`);
|
|
@@ -371,7 +369,7 @@ export class EntityComparator {
|
|
|
371
369
|
const code = `// compiled mapper for entity ${meta.className}\n`
|
|
372
370
|
+ `return function(result) {\n const ret = {};\n${lines.join('\n')}\n return ret;\n}`;
|
|
373
371
|
const resultMapper = Utils.createFunction(context, code);
|
|
374
|
-
this.mappers.set(
|
|
372
|
+
this.mappers.set(meta, resultMapper);
|
|
375
373
|
return resultMapper;
|
|
376
374
|
}
|
|
377
375
|
getPropertyCondition(path) {
|
|
@@ -482,7 +480,7 @@ export class EntityComparator {
|
|
|
482
480
|
const convertorKey = this.safeKey(prop.name);
|
|
483
481
|
context.set(`convertToDatabaseValue_${convertorKey}`, (val) => {
|
|
484
482
|
/* v8 ignore next */
|
|
485
|
-
if (
|
|
483
|
+
if (Raw.isKnownFragment(val)) {
|
|
486
484
|
return val;
|
|
487
485
|
}
|
|
488
486
|
return prop.customType.convertToDatabaseValue(val, this.platform, { mode: 'serialization' });
|
|
@@ -544,11 +542,11 @@ export class EntityComparator {
|
|
|
544
542
|
* @internal Highly performance-sensitive method.
|
|
545
543
|
*/
|
|
546
544
|
getEntityComparator(entityName) {
|
|
547
|
-
const
|
|
545
|
+
const meta = this.metadata.find(entityName);
|
|
546
|
+
const exists = this.comparators.get(meta);
|
|
548
547
|
if (exists) {
|
|
549
548
|
return exists;
|
|
550
549
|
}
|
|
551
|
-
const meta = this.metadata.find(entityName);
|
|
552
550
|
const lines = [];
|
|
553
551
|
const context = new Map();
|
|
554
552
|
context.set('compareArrays', compareArrays);
|
|
@@ -570,7 +568,7 @@ export class EntityComparator {
|
|
|
570
568
|
const code = `// compiled comparator for entity ${meta.className}\n`
|
|
571
569
|
+ `return function(last, current, options) {\n const diff = {};\n${lines.join('\n')}\n return diff;\n}`;
|
|
572
570
|
const comparator = Utils.createFunction(context, code);
|
|
573
|
-
this.comparators.set(
|
|
571
|
+
this.comparators.set(meta, comparator);
|
|
574
572
|
return comparator;
|
|
575
573
|
}
|
|
576
574
|
getGenericComparator(prop, cond) {
|
|
@@ -586,18 +584,23 @@ export class EntityComparator {
|
|
|
586
584
|
getPropertyComparator(prop, context) {
|
|
587
585
|
let type = prop.type.toLowerCase();
|
|
588
586
|
if (prop.kind !== ReferenceKind.SCALAR && prop.kind !== ReferenceKind.EMBEDDED) {
|
|
589
|
-
const meta2 =
|
|
587
|
+
const meta2 = prop.targetMeta;
|
|
590
588
|
if (meta2.primaryKeys.length > 1) {
|
|
591
589
|
type = 'array';
|
|
592
590
|
}
|
|
593
591
|
else {
|
|
594
|
-
type = meta2.
|
|
592
|
+
type = meta2.getPrimaryProp().type.toLowerCase();
|
|
595
593
|
}
|
|
596
594
|
}
|
|
597
595
|
if (prop.customType) {
|
|
598
596
|
if (prop.customType.compareValues) {
|
|
599
597
|
const idx = this.tmpIndex++;
|
|
600
|
-
context.set(`compareValues_${idx}`, (a, b) =>
|
|
598
|
+
context.set(`compareValues_${idx}`, (a, b) => {
|
|
599
|
+
if (Raw.isKnownFragment(a) || Raw.isKnownFragment(b)) {
|
|
600
|
+
return Raw.getKnownFragment(a) === Raw.getKnownFragment(b);
|
|
601
|
+
}
|
|
602
|
+
return prop.customType.compareValues(a, b);
|
|
603
|
+
});
|
|
601
604
|
return this.getGenericComparator(this.wrap(prop.name), `!compareValues_${idx}(last${this.wrap(prop.name)}, current${this.wrap(prop.name)})`);
|
|
602
605
|
}
|
|
603
606
|
type = prop.customType.compareAsType().toLowerCase();
|
package/utils/QueryHelper.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Dictionary, EntityMetadata, EntityProperty, FilterDef, FilterQuery } from '../typings.js';
|
|
1
|
+
import type { Dictionary, EntityMetadata, EntityName, EntityProperty, FilterDef, FilterQuery } from '../typings.js';
|
|
2
2
|
import type { Platform } from '../platforms/Platform.js';
|
|
3
3
|
import type { MetadataStorage } from '../metadata/MetadataStorage.js';
|
|
4
4
|
import type { FilterOptions } from '../drivers/IDatabaseDriver.js';
|
|
@@ -6,7 +6,7 @@ import type { FilterOptions } from '../drivers/IDatabaseDriver.js';
|
|
|
6
6
|
export declare class QueryHelper {
|
|
7
7
|
static readonly SUPPORTED_OPERATORS: string[];
|
|
8
8
|
static processParams(params: unknown): any;
|
|
9
|
-
static processObjectParams<T extends
|
|
9
|
+
static processObjectParams<T extends Dictionary>(params?: T): T;
|
|
10
10
|
/**
|
|
11
11
|
* converts `{ account: { $or: [ [Object], [Object] ] } }`
|
|
12
12
|
* to `{ $or: [ { account: [Object] }, { account: [Object] } ] }`
|
|
@@ -14,9 +14,9 @@ export declare class QueryHelper {
|
|
|
14
14
|
static liftGroupOperators<T extends object>(where: Dictionary, meta: EntityMetadata<T>, metadata: MetadataStorage, key?: string): string | undefined;
|
|
15
15
|
static inlinePrimaryKeyObjects<T extends object>(where: Dictionary, meta: EntityMetadata<T>, metadata: MetadataStorage, key?: string): boolean;
|
|
16
16
|
static processWhere<T extends object>(options: ProcessWhereOptions<T>): FilterQuery<T>;
|
|
17
|
-
static getActiveFilters(
|
|
17
|
+
static getActiveFilters<T>(meta: EntityMetadata<T>, options: FilterOptions | undefined, filters: Dictionary<FilterDef>): FilterDef[];
|
|
18
18
|
static mergePropertyFilters(propFilters: FilterOptions | undefined, options: FilterOptions | undefined): FilterOptions | undefined;
|
|
19
|
-
static isFilterActive(
|
|
19
|
+
static isFilterActive<T>(meta: EntityMetadata<T>, filterName: string, filter: FilterDef, options: Dictionary<boolean | Dictionary>): boolean;
|
|
20
20
|
static processCustomType<T extends object>(prop: EntityProperty<T>, cond: FilterQuery<T>, platform: Platform, key?: string, fromQuery?: boolean): FilterQuery<T>;
|
|
21
21
|
private static isSupportedOperator;
|
|
22
22
|
private static processJsonCondition;
|
|
@@ -25,11 +25,11 @@ export declare class QueryHelper {
|
|
|
25
25
|
}
|
|
26
26
|
interface ProcessWhereOptions<T> {
|
|
27
27
|
where: FilterQuery<T>;
|
|
28
|
-
entityName:
|
|
28
|
+
entityName: EntityName<T>;
|
|
29
29
|
metadata: MetadataStorage;
|
|
30
30
|
platform: Platform;
|
|
31
31
|
aliased?: boolean;
|
|
32
|
-
aliasMap?: Dictionary<
|
|
32
|
+
aliasMap?: Dictionary<EntityName>;
|
|
33
33
|
convertCustomTypes?: boolean;
|
|
34
34
|
root?: boolean;
|
|
35
35
|
type?: 'where' | 'orderBy';
|
package/utils/QueryHelper.js
CHANGED
|
@@ -3,7 +3,7 @@ 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 = ['>', '<', '<=', '>=', '!', '!='];
|
|
@@ -29,7 +29,7 @@ export class QueryHelper {
|
|
|
29
29
|
return params;
|
|
30
30
|
}
|
|
31
31
|
static processObjectParams(params = {}) {
|
|
32
|
-
Utils.
|
|
32
|
+
Utils.getObjectQueryKeys(params).forEach(k => {
|
|
33
33
|
params[k] = QueryHelper.processParams(params[k]);
|
|
34
34
|
});
|
|
35
35
|
return params;
|
|
@@ -98,7 +98,7 @@ export class QueryHelper {
|
|
|
98
98
|
}));
|
|
99
99
|
}
|
|
100
100
|
Object.keys(where).forEach(k => {
|
|
101
|
-
const meta2 = metadata.find(meta.properties[k]?.
|
|
101
|
+
const meta2 = metadata.find(meta.properties[k]?.targetMeta?.class) || meta;
|
|
102
102
|
if (this.inlinePrimaryKeyObjects(where[k], meta2, metadata, k)) {
|
|
103
103
|
where[k] = Utils.getPrimaryKeyValues(where[k], meta2, true);
|
|
104
104
|
}
|
|
@@ -126,7 +126,7 @@ export class QueryHelper {
|
|
|
126
126
|
where = { [Utils.getPrimaryKeyHash(meta.primaryKeys)]: where };
|
|
127
127
|
}
|
|
128
128
|
if (Array.isArray(where) && root) {
|
|
129
|
-
const rootPrimaryKey = meta ? Utils.getPrimaryKeyHash(meta.primaryKeys) : entityName;
|
|
129
|
+
const rootPrimaryKey = meta ? Utils.getPrimaryKeyHash(meta.primaryKeys) : Utils.className(entityName);
|
|
130
130
|
let cond = { [rootPrimaryKey]: { $in: where } };
|
|
131
131
|
// @ts-ignore
|
|
132
132
|
// detect tuple comparison, use `$or` in case the number of constituents don't match
|
|
@@ -138,12 +138,10 @@ export class QueryHelper {
|
|
|
138
138
|
if (!Utils.isPlainObject(where)) {
|
|
139
139
|
return where;
|
|
140
140
|
}
|
|
141
|
-
return
|
|
141
|
+
return Utils.getObjectQueryKeys(where).reduce((o, key) => {
|
|
142
142
|
let value = where[key];
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
const composite = keys > 1;
|
|
146
|
-
if (Array.isArray(value) && value.length === 0 && RawQueryFragment.isKnownFragment(key)) {
|
|
143
|
+
const customExpression = Raw.isKnownFragmentSymbol(key);
|
|
144
|
+
if (Array.isArray(value) && value.length === 0 && customExpression) {
|
|
147
145
|
o[key] = value;
|
|
148
146
|
return o;
|
|
149
147
|
}
|
|
@@ -157,6 +155,9 @@ export class QueryHelper {
|
|
|
157
155
|
o[rootPrimaryKey] = { [key]: QueryHelper.processWhere({ ...options, where: value, root: false }) };
|
|
158
156
|
return o;
|
|
159
157
|
}
|
|
158
|
+
const prop = customExpression ? null : this.findProperty(key, options);
|
|
159
|
+
const keys = prop?.joinColumns?.length ?? 0;
|
|
160
|
+
const composite = keys > 1;
|
|
160
161
|
if (prop?.customType && convertCustomTypes && !isRaw(value)) {
|
|
161
162
|
value = QueryHelper.processCustomType(prop, value, platform, undefined, true);
|
|
162
163
|
}
|
|
@@ -164,7 +165,7 @@ export class QueryHelper {
|
|
|
164
165
|
if (isJsonProperty && prop?.kind !== ReferenceKind.EMBEDDED) {
|
|
165
166
|
return this.processJsonCondition(o, value, [prop.fieldNames[0]], platform, aliased);
|
|
166
167
|
}
|
|
167
|
-
if (Array.isArray(value) && !Utils.isOperator(key) && !QueryHelper.isSupportedOperator(key) && !
|
|
168
|
+
if (Array.isArray(value) && !Utils.isOperator(key) && !QueryHelper.isSupportedOperator(key) && !(customExpression && Raw.getKnownFragment(key).params.length > 0) && options.type !== 'orderBy') {
|
|
168
169
|
// comparing single composite key - use $eq instead of $in
|
|
169
170
|
const op = composite && !value.every(v => Array.isArray(v)) ? '$eq' : '$in';
|
|
170
171
|
o[key] = { [op]: value };
|
|
@@ -174,7 +175,7 @@ export class QueryHelper {
|
|
|
174
175
|
o[key] = QueryHelper.processWhere({
|
|
175
176
|
...options,
|
|
176
177
|
where: value,
|
|
177
|
-
entityName: prop?.
|
|
178
|
+
entityName: prop?.targetMeta?.class ?? entityName,
|
|
178
179
|
root: false,
|
|
179
180
|
});
|
|
180
181
|
}
|
|
@@ -184,7 +185,7 @@ export class QueryHelper {
|
|
|
184
185
|
return o;
|
|
185
186
|
}, {});
|
|
186
187
|
}
|
|
187
|
-
static getActiveFilters(
|
|
188
|
+
static getActiveFilters(meta, options, filters) {
|
|
188
189
|
if (options === false) {
|
|
189
190
|
return [];
|
|
190
191
|
}
|
|
@@ -196,7 +197,7 @@ export class QueryHelper {
|
|
|
196
197
|
Object.keys(options).forEach(filter => opts[filter] = options[filter]);
|
|
197
198
|
}
|
|
198
199
|
return Object.keys(filters)
|
|
199
|
-
.filter(f => QueryHelper.isFilterActive(
|
|
200
|
+
.filter(f => QueryHelper.isFilterActive(meta, f, filters[f], opts))
|
|
200
201
|
.map(f => {
|
|
201
202
|
filters[f].name = f;
|
|
202
203
|
return filters[f];
|
|
@@ -220,8 +221,8 @@ export class QueryHelper {
|
|
|
220
221
|
}
|
|
221
222
|
return Utils.mergeConfig({}, propFilters, options);
|
|
222
223
|
}
|
|
223
|
-
static isFilterActive(
|
|
224
|
-
if (filter.entity && !filter.entity.includes(
|
|
224
|
+
static isFilterActive(meta, filterName, filter, options) {
|
|
225
|
+
if (filter.entity && !filter.entity.includes(meta.className)) {
|
|
225
226
|
return false;
|
|
226
227
|
}
|
|
227
228
|
if (options[filterName] === false) {
|
|
@@ -231,8 +232,8 @@ export class QueryHelper {
|
|
|
231
232
|
}
|
|
232
233
|
static processCustomType(prop, cond, platform, key, fromQuery) {
|
|
233
234
|
if (Utils.isPlainObject(cond)) {
|
|
234
|
-
return Utils.
|
|
235
|
-
if (Utils.isOperator(k, true) || prop.referencedPKs?.includes(k)) {
|
|
235
|
+
return Utils.getObjectQueryKeys(cond).reduce((o, k) => {
|
|
236
|
+
if (!Raw.isKnownFragmentSymbol(k) && (Utils.isOperator(k, true) || prop.referencedPKs?.includes(k))) {
|
|
236
237
|
o[k] = QueryHelper.processCustomType(prop, cond[k], platform, k, fromQuery);
|
|
237
238
|
}
|
|
238
239
|
else {
|
|
@@ -1,23 +1,22 @@
|
|
|
1
1
|
import type { AnyString, Dictionary, EntityKey } from '../typings.js';
|
|
2
|
+
declare const rawFragmentSymbolBrand: unique symbol;
|
|
3
|
+
export type RawQueryFragmentSymbol = symbol & {
|
|
4
|
+
readonly [rawFragmentSymbolBrand]: true;
|
|
5
|
+
};
|
|
2
6
|
export declare class RawQueryFragment {
|
|
3
7
|
#private;
|
|
4
8
|
readonly sql: string;
|
|
5
9
|
readonly params: unknown[];
|
|
6
|
-
static cloneRegistry?: Set<string>;
|
|
7
10
|
constructor(sql: string, params?: unknown[]);
|
|
11
|
+
get key(): RawQueryFragmentSymbol;
|
|
8
12
|
as(alias: string): RawQueryFragment;
|
|
9
|
-
|
|
13
|
+
[Symbol.toPrimitive](hint: 'string'): RawQueryFragmentSymbol;
|
|
14
|
+
get [Symbol.toStringTag](): string;
|
|
10
15
|
toJSON(): string;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
static
|
|
14
|
-
|
|
15
|
-
* @internal allows testing we don't leak memory, as the raw fragments cache needs to be cleared automatically
|
|
16
|
-
*/
|
|
17
|
-
static checkCacheSize(): number;
|
|
18
|
-
static isKnownFragment(key: string | RawQueryFragment): boolean;
|
|
19
|
-
static getKnownFragment(key: string | RawQueryFragment, cleanup?: boolean): RawQueryFragment | undefined;
|
|
20
|
-
static remove(key: string): void;
|
|
16
|
+
static isKnownFragmentSymbol(key: unknown): key is RawQueryFragmentSymbol;
|
|
17
|
+
static hasObjectFragments(object: unknown): boolean;
|
|
18
|
+
static isKnownFragment(key: unknown): key is RawQueryFragment | symbol;
|
|
19
|
+
static getKnownFragment(key: unknown): RawQueryFragment | undefined;
|
|
21
20
|
}
|
|
22
21
|
export { RawQueryFragment as Raw };
|
|
23
22
|
export declare function isRaw(value: unknown): value is RawQueryFragment;
|
|
@@ -1,81 +1,58 @@
|
|
|
1
1
|
import { Utils } from './Utils.js';
|
|
2
|
-
import { createAsyncContext } from './AsyncContext.js';
|
|
3
2
|
export class RawQueryFragment {
|
|
4
3
|
sql;
|
|
5
4
|
params;
|
|
6
|
-
|
|
7
|
-
static #
|
|
8
|
-
static #index = 0n;
|
|
9
|
-
static cloneRegistry;
|
|
10
|
-
#used = 0;
|
|
5
|
+
// holds a weak reference to fragments used as object keys
|
|
6
|
+
static #rawQueryReferences = new WeakMap();
|
|
11
7
|
#key;
|
|
12
8
|
constructor(sql, params = []) {
|
|
13
9
|
this.sql = sql;
|
|
14
10
|
this.params = params;
|
|
15
|
-
|
|
11
|
+
}
|
|
12
|
+
get key() {
|
|
13
|
+
if (!this.#key) {
|
|
14
|
+
this.#key = Symbol(this.toJSON());
|
|
15
|
+
RawQueryFragment.#rawQueryReferences.set(this.#key, this);
|
|
16
|
+
}
|
|
17
|
+
return this.#key;
|
|
16
18
|
}
|
|
17
19
|
as(alias) {
|
|
18
20
|
return new RawQueryFragment(`${this.sql} as ??`, [...this.params, alias]);
|
|
19
21
|
}
|
|
20
|
-
|
|
22
|
+
[Symbol.toPrimitive](hint) {
|
|
23
|
+
// if a fragment is converted to string (used as an object key), return a unique symbol
|
|
24
|
+
// and save a weak reference to map so we can retrieve it when compiling the query
|
|
25
|
+
if (hint === 'string') {
|
|
26
|
+
return this.key;
|
|
27
|
+
}
|
|
21
28
|
throw new Error(`Trying to modify raw SQL fragment: '${this.sql}'`);
|
|
22
29
|
}
|
|
23
|
-
|
|
24
|
-
return this
|
|
25
|
-
}
|
|
26
|
-
toString() {
|
|
27
|
-
RawQueryFragment.#rawQueryCache.set(this.#key, this);
|
|
28
|
-
this.#used++;
|
|
29
|
-
return this.#key;
|
|
30
|
+
get [Symbol.toStringTag]() {
|
|
31
|
+
return this.toJSON();
|
|
30
32
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return new RawQueryFragment(this.sql, this.params);
|
|
33
|
+
toJSON() {
|
|
34
|
+
return `raw('${this.sql}')`;
|
|
34
35
|
}
|
|
35
|
-
static
|
|
36
|
-
|
|
37
|
-
const res = await this.#storage.run(removeStack, cb);
|
|
38
|
-
removeStack.forEach(key => RawQueryFragment.remove(key));
|
|
39
|
-
removeStack.clear();
|
|
40
|
-
return res;
|
|
36
|
+
static isKnownFragmentSymbol(key) {
|
|
37
|
+
return typeof key === 'symbol' && this.#rawQueryReferences.has(key);
|
|
41
38
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
*/
|
|
45
|
-
static checkCacheSize() {
|
|
46
|
-
return this.#rawQueryCache.size;
|
|
39
|
+
static hasObjectFragments(object) {
|
|
40
|
+
return Utils.isPlainObject(object) && Object.getOwnPropertySymbols(object).some(symbol => this.isKnownFragmentSymbol(symbol));
|
|
47
41
|
}
|
|
48
42
|
static isKnownFragment(key) {
|
|
49
43
|
if (key instanceof RawQueryFragment) {
|
|
50
44
|
return true;
|
|
51
45
|
}
|
|
52
|
-
return this
|
|
46
|
+
return this.isKnownFragmentSymbol(key);
|
|
53
47
|
}
|
|
54
|
-
static getKnownFragment(key
|
|
48
|
+
static getKnownFragment(key) {
|
|
55
49
|
if (key instanceof RawQueryFragment) {
|
|
56
50
|
return key;
|
|
57
51
|
}
|
|
58
|
-
|
|
59
|
-
if (raw && cleanup) {
|
|
60
|
-
this.remove(key);
|
|
61
|
-
}
|
|
62
|
-
return raw;
|
|
63
|
-
}
|
|
64
|
-
static remove(key) {
|
|
65
|
-
const raw = this.#rawQueryCache.get(key);
|
|
66
|
-
if (!raw) {
|
|
52
|
+
if (typeof key !== 'symbol') {
|
|
67
53
|
return;
|
|
68
54
|
}
|
|
69
|
-
|
|
70
|
-
if (raw.#used <= 0) {
|
|
71
|
-
const removeStack = this.#storage.getStore();
|
|
72
|
-
if (removeStack) {
|
|
73
|
-
removeStack.add(key);
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
this.#rawQueryCache.delete(key);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
55
|
+
return this.#rawQueryReferences.get(key);
|
|
79
56
|
}
|
|
80
57
|
/** @ignore */
|
|
81
58
|
/* v8 ignore next */
|
|
@@ -193,11 +170,7 @@ export function raw(sql, params) {
|
|
|
193
170
|
* ```
|
|
194
171
|
*/
|
|
195
172
|
export function sql(sql, ...values) {
|
|
196
|
-
return raw(sql.
|
|
197
|
-
const valueExists = i < values.length;
|
|
198
|
-
const text = query + queryPart;
|
|
199
|
-
return valueExists ? text + '?' : text;
|
|
200
|
-
}, ''), values);
|
|
173
|
+
return raw(sql.join('?'), values);
|
|
201
174
|
}
|
|
202
175
|
export function createSqlFunction(func, key) {
|
|
203
176
|
if (typeof key === 'string') {
|
|
@@ -161,7 +161,7 @@ export class TransactionManager {
|
|
|
161
161
|
const wrapped = helper(entity);
|
|
162
162
|
const meta = wrapped.__meta;
|
|
163
163
|
// eslint-disable-next-line dot-notation
|
|
164
|
-
const parentEntity = parentUoW.getById(meta.
|
|
164
|
+
const parentEntity = parentUoW.getById(meta.class, wrapped.getPrimaryKey(), parent['_schema'], true);
|
|
165
165
|
if (parentEntity && parentEntity !== entity) {
|
|
166
166
|
const parentWrapped = helper(parentEntity);
|
|
167
167
|
parentWrapped.__data = wrapped.__data;
|