@mikro-orm/core 7.0.0-dev.33 → 7.0.0-dev.331
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 +70 -75
- package/EntityManager.js +487 -402
- package/MikroORM.d.ts +45 -38
- package/MikroORM.js +123 -156
- package/README.md +7 -4
- package/cache/FileCacheAdapter.d.ts +2 -7
- package/cache/FileCacheAdapter.js +35 -30
- package/cache/GeneratedCacheAdapter.d.ts +1 -2
- package/cache/GeneratedCacheAdapter.js +6 -8
- package/cache/MemoryCacheAdapter.d.ts +1 -2
- package/cache/MemoryCacheAdapter.js +8 -8
- package/cache/index.d.ts +1 -2
- package/cache/index.js +0 -2
- package/connections/Connection.d.ts +12 -5
- package/connections/Connection.js +37 -15
- package/drivers/DatabaseDriver.d.ts +25 -18
- package/drivers/DatabaseDriver.js +144 -45
- package/drivers/IDatabaseDriver.d.ts +118 -23
- package/entity/BaseEntity.d.ts +63 -4
- package/entity/BaseEntity.js +0 -3
- package/entity/Collection.d.ts +95 -31
- package/entity/Collection.js +487 -139
- package/entity/EntityAssigner.js +37 -25
- package/entity/EntityFactory.d.ts +8 -9
- package/entity/EntityFactory.js +152 -100
- package/entity/EntityHelper.d.ts +2 -2
- package/entity/EntityHelper.js +69 -27
- package/entity/EntityLoader.d.ts +12 -13
- package/entity/EntityLoader.js +286 -125
- package/entity/EntityRepository.d.ts +28 -8
- package/entity/EntityRepository.js +8 -2
- package/entity/PolymorphicRef.d.ts +12 -0
- package/entity/PolymorphicRef.js +18 -0
- package/entity/Reference.d.ts +3 -8
- package/entity/Reference.js +62 -29
- package/entity/WrappedEntity.d.ts +7 -10
- package/entity/WrappedEntity.js +6 -7
- package/entity/defineEntity.d.ts +472 -313
- package/entity/defineEntity.js +134 -290
- package/entity/index.d.ts +2 -2
- package/entity/index.js +2 -2
- package/entity/utils.d.ts +6 -1
- package/entity/utils.js +46 -11
- package/entity/validators.d.ts +11 -0
- package/entity/validators.js +66 -0
- package/enums.d.ts +8 -6
- package/enums.js +13 -17
- package/errors.d.ts +26 -16
- package/errors.js +63 -31
- package/events/EventManager.d.ts +3 -5
- package/events/EventManager.js +37 -26
- package/events/index.d.ts +1 -1
- package/events/index.js +0 -1
- package/exceptions.js +9 -2
- package/hydration/Hydrator.js +1 -2
- package/hydration/ObjectHydrator.d.ts +5 -6
- package/hydration/ObjectHydrator.js +109 -50
- package/index.d.ts +2 -2
- package/index.js +1 -2
- package/logging/DefaultLogger.d.ts +1 -1
- package/logging/DefaultLogger.js +3 -4
- package/logging/SimpleLogger.d.ts +1 -1
- package/logging/colors.d.ts +1 -1
- package/logging/colors.js +4 -6
- package/logging/index.d.ts +2 -1
- package/logging/index.js +1 -1
- package/logging/inspect.d.ts +2 -0
- package/logging/inspect.js +11 -0
- package/metadata/EntitySchema.d.ts +47 -23
- package/metadata/EntitySchema.js +103 -34
- package/metadata/MetadataDiscovery.d.ts +65 -18
- package/metadata/MetadataDiscovery.js +940 -424
- package/metadata/MetadataProvider.d.ts +11 -2
- package/metadata/MetadataProvider.js +71 -2
- package/metadata/MetadataStorage.d.ts +11 -13
- package/metadata/MetadataStorage.js +79 -48
- package/metadata/MetadataValidator.d.ts +32 -9
- package/metadata/MetadataValidator.js +214 -44
- package/metadata/discover-entities.d.ts +5 -0
- package/metadata/discover-entities.js +40 -0
- package/metadata/index.d.ts +1 -1
- package/metadata/index.js +0 -1
- package/metadata/types.d.ts +577 -0
- package/metadata/types.js +1 -0
- package/naming-strategy/AbstractNamingStrategy.d.ts +16 -4
- package/naming-strategy/AbstractNamingStrategy.js +26 -5
- package/naming-strategy/EntityCaseNamingStrategy.d.ts +3 -3
- package/naming-strategy/EntityCaseNamingStrategy.js +7 -6
- package/naming-strategy/MongoNamingStrategy.d.ts +3 -3
- package/naming-strategy/MongoNamingStrategy.js +6 -6
- package/naming-strategy/NamingStrategy.d.ts +28 -4
- package/naming-strategy/UnderscoreNamingStrategy.d.ts +3 -3
- package/naming-strategy/UnderscoreNamingStrategy.js +6 -6
- package/naming-strategy/index.d.ts +1 -1
- package/naming-strategy/index.js +0 -1
- package/not-supported.d.ts +2 -0
- package/not-supported.js +8 -0
- package/package.json +47 -36
- package/platforms/ExceptionConverter.js +1 -1
- package/platforms/Platform.d.ts +33 -15
- package/platforms/Platform.js +125 -69
- package/serialization/EntitySerializer.d.ts +6 -3
- package/serialization/EntitySerializer.js +54 -30
- package/serialization/EntityTransformer.js +37 -22
- package/serialization/SerializationContext.d.ts +10 -14
- package/serialization/SerializationContext.js +24 -19
- package/types/ArrayType.d.ts +1 -1
- package/types/ArrayType.js +2 -3
- package/types/BigIntType.js +1 -1
- package/types/BlobType.d.ts +0 -1
- package/types/BlobType.js +0 -3
- package/types/BooleanType.d.ts +1 -0
- package/types/BooleanType.js +3 -0
- package/types/DecimalType.js +2 -2
- package/types/DoubleType.js +1 -1
- package/types/EnumArrayType.js +1 -2
- package/types/JsonType.d.ts +1 -1
- package/types/JsonType.js +7 -2
- package/types/TinyIntType.js +1 -1
- package/types/Type.d.ts +2 -4
- package/types/Type.js +3 -3
- package/types/Uint8ArrayType.d.ts +0 -1
- package/types/Uint8ArrayType.js +1 -4
- package/types/UuidType.d.ts +2 -0
- package/types/UuidType.js +14 -2
- package/types/index.d.ts +3 -2
- package/typings.d.ts +427 -170
- package/typings.js +100 -45
- package/unit-of-work/ChangeSet.d.ts +4 -6
- package/unit-of-work/ChangeSet.js +8 -9
- package/unit-of-work/ChangeSetComputer.d.ts +2 -12
- package/unit-of-work/ChangeSetComputer.js +61 -38
- package/unit-of-work/ChangeSetPersister.d.ts +10 -17
- package/unit-of-work/ChangeSetPersister.js +136 -73
- package/unit-of-work/CommitOrderCalculator.d.ts +13 -14
- package/unit-of-work/CommitOrderCalculator.js +22 -20
- package/unit-of-work/IdentityMap.d.ts +12 -3
- package/unit-of-work/IdentityMap.js +51 -13
- package/unit-of-work/UnitOfWork.d.ts +39 -23
- package/unit-of-work/UnitOfWork.js +441 -246
- package/utils/AbstractMigrator.d.ts +101 -0
- package/utils/AbstractMigrator.js +303 -0
- package/utils/AbstractSchemaGenerator.d.ts +5 -5
- package/utils/AbstractSchemaGenerator.js +30 -18
- package/utils/AsyncContext.d.ts +6 -0
- package/utils/AsyncContext.js +42 -0
- package/utils/Configuration.d.ts +647 -185
- package/utils/Configuration.js +215 -252
- package/utils/ConfigurationLoader.d.ts +1 -52
- package/utils/ConfigurationLoader.js +1 -330
- package/utils/Cursor.d.ts +3 -6
- package/utils/Cursor.js +32 -17
- package/utils/DataloaderUtils.d.ts +10 -5
- package/utils/DataloaderUtils.js +42 -22
- package/utils/EntityComparator.d.ts +21 -21
- package/utils/EntityComparator.js +224 -118
- package/utils/QueryHelper.d.ts +34 -7
- package/utils/QueryHelper.js +183 -72
- package/utils/RawQueryFragment.d.ts +28 -34
- package/utils/RawQueryFragment.js +37 -72
- package/utils/RequestContext.js +2 -2
- package/utils/TransactionContext.js +2 -2
- package/utils/TransactionManager.js +11 -8
- package/utils/Utils.d.ts +16 -127
- package/utils/Utils.js +104 -402
- package/utils/clone.js +13 -23
- package/utils/env-vars.d.ts +7 -0
- package/utils/env-vars.js +98 -0
- package/utils/fs-utils.d.ts +20 -0
- package/utils/fs-utils.js +193 -0
- package/utils/index.d.ts +1 -3
- package/utils/index.js +1 -3
- package/utils/upsert-utils.d.ts +9 -4
- package/utils/upsert-utils.js +51 -5
- package/decorators/Check.d.ts +0 -3
- package/decorators/Check.js +0 -13
- package/decorators/CreateRequestContext.d.ts +0 -3
- package/decorators/CreateRequestContext.js +0 -32
- package/decorators/Embeddable.d.ts +0 -8
- package/decorators/Embeddable.js +0 -11
- package/decorators/Embedded.d.ts +0 -12
- package/decorators/Embedded.js +0 -18
- package/decorators/Entity.d.ts +0 -33
- package/decorators/Entity.js +0 -12
- package/decorators/Enum.d.ts +0 -9
- package/decorators/Enum.js +0 -16
- package/decorators/Filter.d.ts +0 -2
- package/decorators/Filter.js +0 -8
- package/decorators/Formula.d.ts +0 -4
- package/decorators/Formula.js +0 -15
- package/decorators/Indexed.d.ts +0 -19
- package/decorators/Indexed.js +0 -20
- package/decorators/ManyToMany.d.ts +0 -42
- package/decorators/ManyToMany.js +0 -14
- package/decorators/ManyToOne.d.ts +0 -34
- package/decorators/ManyToOne.js +0 -14
- package/decorators/OneToMany.d.ts +0 -28
- package/decorators/OneToMany.js +0 -17
- package/decorators/OneToOne.d.ts +0 -28
- package/decorators/OneToOne.js +0 -7
- package/decorators/PrimaryKey.d.ts +0 -8
- package/decorators/PrimaryKey.js +0 -20
- package/decorators/Property.d.ts +0 -250
- package/decorators/Property.js +0 -32
- package/decorators/Transactional.d.ts +0 -14
- package/decorators/Transactional.js +0 -28
- package/decorators/hooks.d.ts +0 -16
- package/decorators/hooks.js +0 -47
- package/decorators/index.d.ts +0 -17
- package/decorators/index.js +0 -17
- package/entity/ArrayCollection.d.ts +0 -118
- package/entity/ArrayCollection.js +0 -407
- package/entity/EntityValidator.d.ts +0 -19
- package/entity/EntityValidator.js +0 -150
- package/metadata/ReflectMetadataProvider.d.ts +0 -8
- package/metadata/ReflectMetadataProvider.js +0 -44
- package/utils/resolveContextProvider.d.ts +0 -10
- package/utils/resolveContextProvider.js +0 -28
package/utils/QueryHelper.d.ts
CHANGED
|
@@ -1,11 +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';
|
|
5
|
+
import type { FilterOptions } from '../drivers/IDatabaseDriver.js';
|
|
4
6
|
/** @internal */
|
|
5
7
|
export declare class QueryHelper {
|
|
6
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;
|
|
7
13
|
static processParams(params: unknown): any;
|
|
8
|
-
static processObjectParams<T extends
|
|
14
|
+
static processObjectParams<T extends Dictionary>(params?: T): T;
|
|
9
15
|
/**
|
|
10
16
|
* converts `{ account: { $or: [ [Object], [Object] ] } }`
|
|
11
17
|
* to `{ $or: [ { account: [Object] }, { account: [Object] } ] }`
|
|
@@ -13,21 +19,42 @@ export declare class QueryHelper {
|
|
|
13
19
|
static liftGroupOperators<T extends object>(where: Dictionary, meta: EntityMetadata<T>, metadata: MetadataStorage, key?: string): string | undefined;
|
|
14
20
|
static inlinePrimaryKeyObjects<T extends object>(where: Dictionary, meta: EntityMetadata<T>, metadata: MetadataStorage, key?: string): boolean;
|
|
15
21
|
static processWhere<T extends object>(options: ProcessWhereOptions<T>): FilterQuery<T>;
|
|
16
|
-
static getActiveFilters(
|
|
17
|
-
static
|
|
22
|
+
static getActiveFilters<T>(meta: EntityMetadata<T>, options: FilterOptions | undefined, filters: Dictionary<FilterDef>): FilterDef[];
|
|
23
|
+
static mergePropertyFilters(propFilters: FilterOptions | undefined, options: FilterOptions | undefined): FilterOptions | undefined;
|
|
24
|
+
static isFilterActive<T>(meta: EntityMetadata<T>, filterName: string, filter: FilterDef, options: Dictionary<boolean | Dictionary>): boolean;
|
|
18
25
|
static processCustomType<T extends object>(prop: EntityProperty<T>, cond: FilterQuery<T>, platform: Platform, key?: string, fromQuery?: boolean): FilterQuery<T>;
|
|
19
26
|
private static isSupportedOperator;
|
|
20
27
|
private static processJsonCondition;
|
|
21
|
-
private static getValueType;
|
|
22
28
|
static findProperty<T>(fieldName: string, options: ProcessWhereOptions<T>): EntityProperty<T> | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* Converts entity references for composite FK properties into flat arrays
|
|
31
|
+
* of correctly-ordered join column values, before processParams flattens them
|
|
32
|
+
* incorrectly due to shared FK columns.
|
|
33
|
+
*/
|
|
34
|
+
private static convertCompositeEntityRefs;
|
|
35
|
+
/**
|
|
36
|
+
* Extracts values for a FK's join columns from an entity by traversing the FK chain.
|
|
37
|
+
* Handles shared FK columns (e.g., tenant_id referenced by multiple FKs) correctly.
|
|
38
|
+
*/
|
|
39
|
+
private static extractJoinColumnValues;
|
|
40
|
+
/**
|
|
41
|
+
* Extracts the value for a specific column from an entity by finding which PK property
|
|
42
|
+
* owns that column and recursively traversing FK references.
|
|
43
|
+
*/
|
|
44
|
+
private static extractColumnValue;
|
|
45
|
+
/**
|
|
46
|
+
* Merges multiple orderBy sources with key-level deduplication (first-seen key wins).
|
|
47
|
+
* RawQueryFragment symbol keys are never deduped (each is unique).
|
|
48
|
+
*/
|
|
49
|
+
static mergeOrderBy<T>(...sources: (QueryOrderMap<T> | QueryOrderMap<T>[] | undefined)[]): QueryOrderMap<T>[];
|
|
23
50
|
}
|
|
24
51
|
interface ProcessWhereOptions<T> {
|
|
25
52
|
where: FilterQuery<T>;
|
|
26
|
-
entityName:
|
|
53
|
+
entityName: EntityName<T>;
|
|
27
54
|
metadata: MetadataStorage;
|
|
28
55
|
platform: Platform;
|
|
29
56
|
aliased?: boolean;
|
|
30
|
-
aliasMap?: Dictionary<
|
|
57
|
+
aliasMap?: Dictionary<EntityName>;
|
|
31
58
|
convertCustomTypes?: boolean;
|
|
32
59
|
root?: boolean;
|
|
33
60
|
type?: 'where' | 'orderBy';
|
package/utils/QueryHelper.js
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { Reference } from '../entity/Reference.js';
|
|
2
2
|
import { Utils } from './Utils.js';
|
|
3
|
-
import { GroupOperator, ReferenceKind } from '../enums.js';
|
|
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 {
|
|
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;
|
|
@@ -44,17 +50,21 @@ export class QueryHelper {
|
|
|
44
50
|
}
|
|
45
51
|
const keys = Object.keys(where);
|
|
46
52
|
const groupOperator = keys.find(k => {
|
|
47
|
-
return
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
return (k in GroupOperator &&
|
|
54
|
+
Array.isArray(where[k]) &&
|
|
55
|
+
where[k].every(cond => {
|
|
56
|
+
return (Utils.isPlainObject(cond) &&
|
|
57
|
+
Object.keys(cond).every(k2 => {
|
|
58
|
+
if (Utils.isOperator(k2, false)) {
|
|
59
|
+
if (k2 === '$not') {
|
|
60
|
+
return Object.keys(cond[k2]).every(k3 => meta.primaryKeys.includes(k3));
|
|
61
|
+
}
|
|
62
|
+
/* v8 ignore next */
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
return meta.primaryKeys.includes(k2);
|
|
66
|
+
}));
|
|
67
|
+
}));
|
|
58
68
|
});
|
|
59
69
|
if (groupOperator) {
|
|
60
70
|
return groupOperator;
|
|
@@ -62,7 +72,10 @@ export class QueryHelper {
|
|
|
62
72
|
for (const k of keys) {
|
|
63
73
|
const value = where[k];
|
|
64
74
|
const prop = meta.properties[k];
|
|
65
|
-
|
|
75
|
+
// Polymorphic relations use multiple columns (discriminator + FK), so they cannot
|
|
76
|
+
// participate in the standard single-column FK expansion. Query by discriminator
|
|
77
|
+
// column directly instead, e.g. { likeableType: 'post', likeableId: 1 }.
|
|
78
|
+
if (!prop || ![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) || prop.polymorphic) {
|
|
66
79
|
continue;
|
|
67
80
|
}
|
|
68
81
|
const op = this.liftGroupOperators(value, prop.targetMeta, metadata, k);
|
|
@@ -87,18 +100,24 @@ export class QueryHelper {
|
|
|
87
100
|
return false;
|
|
88
101
|
}
|
|
89
102
|
if (meta.primaryKeys.every(pk => pk in where) && Utils.getObjectKeysSize(where) === meta.primaryKeys.length) {
|
|
90
|
-
return !!key &&
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
103
|
+
return (!!key &&
|
|
104
|
+
!GroupOperator[key] &&
|
|
105
|
+
key !== '$not' &&
|
|
106
|
+
Object.keys(where).every(k => !Utils.isPlainObject(where[k]) ||
|
|
107
|
+
Object.keys(where[k]).every(v => {
|
|
108
|
+
if (Utils.isOperator(v, false)) {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
if (meta.properties[k].primary &&
|
|
112
|
+
[ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(meta.properties[k].kind)) {
|
|
113
|
+
return this.inlinePrimaryKeyObjects(where[k], meta.properties[k].targetMeta, metadata, v);
|
|
114
|
+
}
|
|
115
|
+
/* v8 ignore next */
|
|
116
|
+
return true;
|
|
117
|
+
})));
|
|
99
118
|
}
|
|
100
119
|
Object.keys(where).forEach(k => {
|
|
101
|
-
const meta2 = metadata.find(meta.properties[k]?.
|
|
120
|
+
const meta2 = metadata.find(meta.properties[k]?.targetMeta?.class) || meta;
|
|
102
121
|
if (this.inlinePrimaryKeyObjects(where[k], meta2, metadata, k)) {
|
|
103
122
|
where[k] = Utils.getPrimaryKeyValues(where[k], meta2, true);
|
|
104
123
|
}
|
|
@@ -114,11 +133,14 @@ export class QueryHelper {
|
|
|
114
133
|
QueryHelper.liftGroupOperators(where, meta, metadata);
|
|
115
134
|
QueryHelper.inlinePrimaryKeyObjects(where, meta, metadata);
|
|
116
135
|
}
|
|
136
|
+
if (meta && root) {
|
|
137
|
+
QueryHelper.convertCompositeEntityRefs(where, meta);
|
|
138
|
+
}
|
|
117
139
|
if (platform.getConfig().get('ignoreUndefinedInQuery') && where && typeof where === 'object') {
|
|
118
140
|
Utils.dropUndefinedProperties(where);
|
|
119
141
|
}
|
|
120
142
|
where = QueryHelper.processParams(where) ?? {};
|
|
121
|
-
/* v8 ignore next
|
|
143
|
+
/* v8 ignore next */
|
|
122
144
|
if (!root && Utils.isPrimaryKey(where)) {
|
|
123
145
|
return where;
|
|
124
146
|
}
|
|
@@ -126,11 +148,13 @@ export class QueryHelper {
|
|
|
126
148
|
where = { [Utils.getPrimaryKeyHash(meta.primaryKeys)]: where };
|
|
127
149
|
}
|
|
128
150
|
if (Array.isArray(where) && root) {
|
|
129
|
-
const rootPrimaryKey = meta ? Utils.getPrimaryKeyHash(meta.primaryKeys) : entityName;
|
|
151
|
+
const rootPrimaryKey = meta ? Utils.getPrimaryKeyHash(meta.primaryKeys) : Utils.className(entityName);
|
|
130
152
|
let cond = { [rootPrimaryKey]: { $in: where } };
|
|
131
153
|
// @ts-ignore
|
|
132
154
|
// detect tuple comparison, use `$or` in case the number of constituents don't match
|
|
133
|
-
if (meta &&
|
|
155
|
+
if (meta &&
|
|
156
|
+
!where.every(c => Utils.isPrimaryKey(c) ||
|
|
157
|
+
(Array.isArray(c) && c.length === meta.primaryKeys.length && c.every(i => Utils.isPrimaryKey(i))))) {
|
|
134
158
|
cond = { $or: where };
|
|
135
159
|
}
|
|
136
160
|
return QueryHelper.processWhere({ ...options, where: cond, root: false });
|
|
@@ -138,12 +162,10 @@ export class QueryHelper {
|
|
|
138
162
|
if (!Utils.isPlainObject(where)) {
|
|
139
163
|
return where;
|
|
140
164
|
}
|
|
141
|
-
return
|
|
165
|
+
return Utils.getObjectQueryKeys(where).reduce((o, key) => {
|
|
142
166
|
let value = where[key];
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
const composite = keys > 1;
|
|
146
|
-
if (Array.isArray(value) && value.length === 0 && RawQueryFragment.isKnownFragment(key)) {
|
|
167
|
+
const customExpression = Raw.isKnownFragmentSymbol(key);
|
|
168
|
+
if (Array.isArray(value) && value.length === 0 && customExpression) {
|
|
147
169
|
o[key] = value;
|
|
148
170
|
return o;
|
|
149
171
|
}
|
|
@@ -157,14 +179,19 @@ export class QueryHelper {
|
|
|
157
179
|
o[rootPrimaryKey] = { [key]: QueryHelper.processWhere({ ...options, where: value, root: false }) };
|
|
158
180
|
return o;
|
|
159
181
|
}
|
|
182
|
+
const prop = customExpression ? null : this.findProperty(key, options);
|
|
183
|
+
const keys = prop?.joinColumns?.length ?? 0;
|
|
184
|
+
const composite = keys > 1;
|
|
160
185
|
if (prop?.customType && convertCustomTypes && !isRaw(value)) {
|
|
161
186
|
value = QueryHelper.processCustomType(prop, value, platform, undefined, true);
|
|
162
187
|
}
|
|
163
|
-
|
|
188
|
+
// oxfmt-ignore
|
|
189
|
+
const isJsonProperty = prop?.customType instanceof JsonType && !isRaw(value) && (Utils.isPlainObject(value) ? Object.keys(value)[0] !== '$eq' : !Array.isArray(value));
|
|
164
190
|
if (isJsonProperty && prop?.kind !== ReferenceKind.EMBEDDED) {
|
|
165
191
|
return this.processJsonCondition(o, value, [prop.fieldNames[0]], platform, aliased);
|
|
166
192
|
}
|
|
167
|
-
|
|
193
|
+
// oxfmt-ignore
|
|
194
|
+
if (Array.isArray(value) && !Utils.isOperator(key) && !QueryHelper.isSupportedOperator(key) && !(customExpression && Raw.getKnownFragment(key).params.length > 0) && options.type !== 'orderBy') {
|
|
168
195
|
// comparing single composite key - use $eq instead of $in
|
|
169
196
|
const op = composite && !value.every(v => Array.isArray(v)) ? '$eq' : '$in';
|
|
170
197
|
o[key] = { [op]: value };
|
|
@@ -174,7 +201,7 @@ export class QueryHelper {
|
|
|
174
201
|
o[key] = QueryHelper.processWhere({
|
|
175
202
|
...options,
|
|
176
203
|
where: value,
|
|
177
|
-
entityName: prop?.
|
|
204
|
+
entityName: prop?.targetMeta?.class ?? entityName,
|
|
178
205
|
root: false,
|
|
179
206
|
});
|
|
180
207
|
}
|
|
@@ -184,26 +211,44 @@ export class QueryHelper {
|
|
|
184
211
|
return o;
|
|
185
212
|
}, {});
|
|
186
213
|
}
|
|
187
|
-
static getActiveFilters(
|
|
214
|
+
static getActiveFilters(meta, options, filters) {
|
|
188
215
|
if (options === false) {
|
|
189
216
|
return [];
|
|
190
217
|
}
|
|
191
218
|
const opts = {};
|
|
192
219
|
if (Array.isArray(options)) {
|
|
193
|
-
options.forEach(filter => opts[filter] = true);
|
|
220
|
+
options.forEach(filter => (opts[filter] = true));
|
|
194
221
|
}
|
|
195
222
|
else if (Utils.isPlainObject(options)) {
|
|
196
|
-
Object.keys(options).forEach(filter => opts[filter] = options[filter]);
|
|
223
|
+
Object.keys(options).forEach(filter => (opts[filter] = options[filter]));
|
|
197
224
|
}
|
|
198
225
|
return Object.keys(filters)
|
|
199
|
-
.filter(f => QueryHelper.isFilterActive(
|
|
226
|
+
.filter(f => QueryHelper.isFilterActive(meta, f, filters[f], opts))
|
|
200
227
|
.map(f => {
|
|
201
228
|
filters[f].name = f;
|
|
202
229
|
return filters[f];
|
|
203
230
|
});
|
|
204
231
|
}
|
|
205
|
-
static
|
|
206
|
-
if (
|
|
232
|
+
static mergePropertyFilters(propFilters, options) {
|
|
233
|
+
if (!options || !propFilters || options === true || propFilters === true) {
|
|
234
|
+
return options ?? propFilters;
|
|
235
|
+
}
|
|
236
|
+
if (Array.isArray(propFilters)) {
|
|
237
|
+
propFilters = propFilters.reduce((o, item) => {
|
|
238
|
+
o[item] = true;
|
|
239
|
+
return o;
|
|
240
|
+
}, {});
|
|
241
|
+
}
|
|
242
|
+
if (Array.isArray(options)) {
|
|
243
|
+
options = options.reduce((o, item) => {
|
|
244
|
+
o[item] = true;
|
|
245
|
+
return o;
|
|
246
|
+
}, {});
|
|
247
|
+
}
|
|
248
|
+
return Utils.mergeConfig({}, propFilters, options);
|
|
249
|
+
}
|
|
250
|
+
static isFilterActive(meta, filterName, filter, options) {
|
|
251
|
+
if (filter.entity && !filter.entity.includes(meta.className)) {
|
|
207
252
|
return false;
|
|
208
253
|
}
|
|
209
254
|
if (options[filterName] === false) {
|
|
@@ -213,8 +258,8 @@ export class QueryHelper {
|
|
|
213
258
|
}
|
|
214
259
|
static processCustomType(prop, cond, platform, key, fromQuery) {
|
|
215
260
|
if (Utils.isPlainObject(cond)) {
|
|
216
|
-
return Utils.
|
|
217
|
-
if (Utils.isOperator(k, true) || prop.referencedPKs?.includes(k)) {
|
|
261
|
+
return Utils.getObjectQueryKeys(cond).reduce((o, k) => {
|
|
262
|
+
if (!Raw.isKnownFragmentSymbol(k) && (Utils.isOperator(k, true) || prop.referencedPKs?.includes(k))) {
|
|
218
263
|
o[k] = QueryHelper.processCustomType(prop, cond[k], platform, k, fromQuery);
|
|
219
264
|
}
|
|
220
265
|
else {
|
|
@@ -223,12 +268,10 @@ export class QueryHelper {
|
|
|
223
268
|
return o;
|
|
224
269
|
}, {});
|
|
225
270
|
}
|
|
226
|
-
if (key &&
|
|
227
|
-
return Array.isArray(cond)
|
|
228
|
-
? platform.marshallArray(cond)
|
|
229
|
-
: cond;
|
|
271
|
+
if (key && JSON_KEY_OPERATORS.includes(key)) {
|
|
272
|
+
return Array.isArray(cond) ? platform.marshallArray(cond) : cond;
|
|
230
273
|
}
|
|
231
|
-
if (Array.isArray(cond) && !(key &&
|
|
274
|
+
if (Array.isArray(cond) && !(key && ARRAY_OPERATORS.includes(key))) {
|
|
232
275
|
return cond.map(v => QueryHelper.processCustomType(prop, v, platform, key, fromQuery));
|
|
233
276
|
}
|
|
234
277
|
if (isRaw(cond)) {
|
|
@@ -240,29 +283,7 @@ export class QueryHelper {
|
|
|
240
283
|
return !!QueryHelper.SUPPORTED_OPERATORS.find(op => key === op);
|
|
241
284
|
}
|
|
242
285
|
static processJsonCondition(o, value, path, platform, alias) {
|
|
243
|
-
|
|
244
|
-
Utils.keys(value).forEach(k => {
|
|
245
|
-
this.processJsonCondition(o, value[k], [...path, k], platform, alias);
|
|
246
|
-
});
|
|
247
|
-
return o;
|
|
248
|
-
}
|
|
249
|
-
if (path.length === 1) {
|
|
250
|
-
o[path[0]] = value;
|
|
251
|
-
return o;
|
|
252
|
-
}
|
|
253
|
-
const type = this.getValueType(value);
|
|
254
|
-
const k = platform.getSearchJsonPropertyKey(path, type, alias, value);
|
|
255
|
-
o[k] = value;
|
|
256
|
-
return o;
|
|
257
|
-
}
|
|
258
|
-
static getValueType(value) {
|
|
259
|
-
if (Array.isArray(value)) {
|
|
260
|
-
return typeof value[0];
|
|
261
|
-
}
|
|
262
|
-
if (Utils.isPlainObject(value) && Object.keys(value).every(k => Utils.isOperator(k))) {
|
|
263
|
-
return this.getValueType(Object.values(value)[0]);
|
|
264
|
-
}
|
|
265
|
-
return typeof value;
|
|
286
|
+
return platform.processJsonCondition(o, value, path, alias);
|
|
266
287
|
}
|
|
267
288
|
static findProperty(fieldName, options) {
|
|
268
289
|
const parts = fieldName.split('.');
|
|
@@ -272,4 +293,94 @@ export class QueryHelper {
|
|
|
272
293
|
const meta = entityName ? options.metadata.find(entityName) : undefined;
|
|
273
294
|
return meta?.properties[propName];
|
|
274
295
|
}
|
|
296
|
+
/**
|
|
297
|
+
* Converts entity references for composite FK properties into flat arrays
|
|
298
|
+
* of correctly-ordered join column values, before processParams flattens them
|
|
299
|
+
* incorrectly due to shared FK columns.
|
|
300
|
+
*/
|
|
301
|
+
static convertCompositeEntityRefs(where, meta) {
|
|
302
|
+
if (!Utils.isPlainObject(where)) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
for (const k of Object.keys(where)) {
|
|
306
|
+
if (k in GroupOperator) {
|
|
307
|
+
if (Array.isArray(where[k])) {
|
|
308
|
+
where[k].forEach((sub) => this.convertCompositeEntityRefs(sub, meta));
|
|
309
|
+
}
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
if (k === '$not') {
|
|
313
|
+
this.convertCompositeEntityRefs(where[k], meta);
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
const prop = meta.properties[k];
|
|
317
|
+
if (!prop?.joinColumns || prop.joinColumns.length <= 1) {
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
const w = where[k];
|
|
321
|
+
if (Utils.isEntity(w)) {
|
|
322
|
+
where[k] = this.extractJoinColumnValues(w, prop);
|
|
323
|
+
}
|
|
324
|
+
else if (Utils.isPlainObject(w)) {
|
|
325
|
+
for (const op of Object.keys(w)) {
|
|
326
|
+
if (Utils.isOperator(op, false) && Array.isArray(w[op])) {
|
|
327
|
+
w[op] = w[op].map((item) => Utils.isEntity(item) ? this.extractJoinColumnValues(item, prop) : item);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Extracts values for a FK's join columns from an entity by traversing the FK chain.
|
|
335
|
+
* Handles shared FK columns (e.g., tenant_id referenced by multiple FKs) correctly.
|
|
336
|
+
*/
|
|
337
|
+
static extractJoinColumnValues(entity, prop) {
|
|
338
|
+
return prop.referencedColumnNames.map(refCol => {
|
|
339
|
+
return this.extractColumnValue(entity, prop.targetMeta, refCol);
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Extracts the value for a specific column from an entity by finding which PK property
|
|
344
|
+
* owns that column and recursively traversing FK references.
|
|
345
|
+
*/
|
|
346
|
+
static extractColumnValue(entity, meta, columnName) {
|
|
347
|
+
for (const pk of meta.primaryKeys) {
|
|
348
|
+
const pkProp = meta.properties[pk];
|
|
349
|
+
const colIdx = pkProp.fieldNames.indexOf(columnName);
|
|
350
|
+
if (colIdx !== -1) {
|
|
351
|
+
const value = entity[pk];
|
|
352
|
+
if (pkProp.targetMeta && Utils.isEntity(value, true)) {
|
|
353
|
+
const refCol = pkProp.referencedColumnNames[colIdx];
|
|
354
|
+
return this.extractColumnValue(value, pkProp.targetMeta, refCol);
|
|
355
|
+
}
|
|
356
|
+
return value;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return null;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Merges multiple orderBy sources with key-level deduplication (first-seen key wins).
|
|
363
|
+
* RawQueryFragment symbol keys are never deduped (each is unique).
|
|
364
|
+
*/
|
|
365
|
+
static mergeOrderBy(...sources) {
|
|
366
|
+
const result = [];
|
|
367
|
+
const seenKeys = new Set();
|
|
368
|
+
for (const source of sources) {
|
|
369
|
+
if (source == null) {
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
for (const item of Utils.asArray(source)) {
|
|
373
|
+
for (const key of Utils.getObjectQueryKeys(item)) {
|
|
374
|
+
if (typeof key === 'symbol') {
|
|
375
|
+
result.push({ [key]: item[key] });
|
|
376
|
+
}
|
|
377
|
+
else if (!seenKeys.has(key)) {
|
|
378
|
+
seenKeys.add(key);
|
|
379
|
+
result.push({ [key]: item[key] });
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return result;
|
|
385
|
+
}
|
|
275
386
|
}
|
|
@@ -1,34 +1,25 @@
|
|
|
1
|
-
import { inspect } from 'node:util';
|
|
2
1
|
import type { AnyString, Dictionary, EntityKey } from '../typings.js';
|
|
3
|
-
|
|
2
|
+
declare const rawFragmentSymbolBrand: unique symbol;
|
|
3
|
+
export type RawQueryFragmentSymbol = symbol & {
|
|
4
|
+
readonly [rawFragmentSymbolBrand]: true;
|
|
5
|
+
};
|
|
6
|
+
export declare class RawQueryFragment<Alias extends string = string> {
|
|
4
7
|
#private;
|
|
5
8
|
readonly sql: string;
|
|
6
9
|
readonly params: unknown[];
|
|
7
|
-
|
|
10
|
+
/** @internal Type-level only - used to track the alias for type inference */
|
|
11
|
+
private readonly __alias?;
|
|
8
12
|
constructor(sql: string, params?: unknown[]);
|
|
9
|
-
|
|
10
|
-
|
|
13
|
+
get key(): RawQueryFragmentSymbol;
|
|
14
|
+
as<A extends string>(alias: A): RawQueryFragment<A>;
|
|
15
|
+
[Symbol.toPrimitive](hint: 'string'): RawQueryFragmentSymbol;
|
|
16
|
+
get [Symbol.toStringTag](): string;
|
|
11
17
|
toJSON(): string;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
static
|
|
17
|
-
/**
|
|
18
|
-
* @internal allows testing we don't leak memory, as the raw fragments cache needs to be cleared automatically
|
|
19
|
-
*/
|
|
20
|
-
static checkCacheSize(): number;
|
|
21
|
-
static isKnownFragment(key: string | RawQueryFragment): boolean;
|
|
22
|
-
static getKnownFragment(key: string | RawQueryFragment, cleanup?: boolean): RawQueryFragment | undefined;
|
|
23
|
-
static remove(key: string): void;
|
|
24
|
-
/** @ignore */
|
|
25
|
-
[inspect.custom](): {
|
|
26
|
-
sql: string;
|
|
27
|
-
params: unknown[];
|
|
28
|
-
} | {
|
|
29
|
-
sql: string;
|
|
30
|
-
params?: undefined;
|
|
31
|
-
};
|
|
18
|
+
clone(): this;
|
|
19
|
+
static isKnownFragmentSymbol(key: unknown): key is RawQueryFragmentSymbol;
|
|
20
|
+
static hasObjectFragments(object: unknown): boolean;
|
|
21
|
+
static isKnownFragment(key: unknown): key is RawQueryFragment | symbol;
|
|
22
|
+
static getKnownFragment(key: unknown): RawQueryFragment | undefined;
|
|
32
23
|
}
|
|
33
24
|
export { RawQueryFragment as Raw };
|
|
34
25
|
export declare function isRaw(value: unknown): value is RawQueryFragment;
|
|
@@ -91,7 +82,7 @@ export declare const ALIAS_REPLACEMENT_RE = "\\[::alias::\\]";
|
|
|
91
82
|
* export class Author { ... }
|
|
92
83
|
* ```
|
|
93
84
|
*/
|
|
94
|
-
export declare function raw<
|
|
85
|
+
export declare function raw<R = RawQueryFragment & symbol, T extends object = any>(sql: EntityKey<T> | EntityKey<T>[] | AnyString | ((alias: string) => string) | RawQueryFragment, params?: readonly unknown[] | Dictionary<unknown>): R;
|
|
95
86
|
/**
|
|
96
87
|
* Alternative to the `raw()` helper allowing to use it as a tagged template function for the simple cases.
|
|
97
88
|
*
|
|
@@ -104,16 +95,19 @@ export declare function raw<T extends object = any, R = any>(sql: EntityKey<T> |
|
|
|
104
95
|
*
|
|
105
96
|
* // value can be empty array
|
|
106
97
|
* await em.find(User, { [sql`(select 1 = 1)`]: [] });
|
|
98
|
+
*
|
|
99
|
+
* // with type parameter for assignment without casting
|
|
100
|
+
* entity.date = sql<Date>`now()`;
|
|
107
101
|
* ```
|
|
108
102
|
*/
|
|
109
|
-
export declare function sql(sql: readonly string[], ...values: unknown[]):
|
|
103
|
+
export declare function sql<R = RawQueryFragment & symbol>(sql: readonly string[], ...values: unknown[]): R;
|
|
110
104
|
export declare namespace sql {
|
|
111
|
-
var ref: <T extends object>(...keys: string[]) => RawQueryFragment;
|
|
112
|
-
var now: (length?: number) =>
|
|
113
|
-
var lower: <T extends object>(key: string | ((alias: string) => string)) =>
|
|
114
|
-
var upper: <T extends object>(key: string | ((alias: string) => string)) =>
|
|
105
|
+
var ref: <T extends object = any>(...keys: string[]) => RawQueryFragment & symbol;
|
|
106
|
+
var now: (length?: number) => RawQueryFragment & symbol;
|
|
107
|
+
var lower: <R = RawQueryFragment<string> & symbol, T extends object = any>(key: string | ((alias: string) => string)) => R;
|
|
108
|
+
var upper: <R = RawQueryFragment<string> & symbol, T extends object = any>(key: string | ((alias: string) => string)) => R;
|
|
115
109
|
}
|
|
116
|
-
export declare function createSqlFunction<T extends object
|
|
110
|
+
export declare function createSqlFunction<R = RawQueryFragment & symbol, T extends object = any>(func: string, key: string | ((alias: string) => string)): R;
|
|
117
111
|
/**
|
|
118
112
|
* Tag function providing quoting of db identifiers (table name, columns names, index names, ...).
|
|
119
113
|
*
|
|
@@ -122,11 +116,11 @@ export declare function createSqlFunction<T extends object, R = string>(func: st
|
|
|
122
116
|
* ```ts
|
|
123
117
|
* // On postgres, will produce: create index "index custom_idx_on_name" on "library.author" ("name")
|
|
124
118
|
* // On mysql, will produce: create index `index custom_idx_on_name` on `library.author` (`name`)
|
|
125
|
-
* @Index({ name: 'custom_idx_on_name', expression: (table, columns) => quote`create index ${
|
|
119
|
+
* @Index({ name: 'custom_idx_on_name', expression: (table, columns, indexName) => quote`create index ${indexName} on ${table} (${columns.name})` })
|
|
126
120
|
* @Entity({ schema: 'library' })
|
|
127
121
|
* export class Author { ... }
|
|
128
122
|
* ```
|
|
129
123
|
*/
|
|
130
124
|
export declare function quote(expParts: readonly string[], ...values: (string | {
|
|
131
125
|
toString(): string;
|
|
132
|
-
})[]):
|
|
126
|
+
})[]): RawQueryFragment & symbol;
|