@mikro-orm/sql 7.0.2-dev.8 → 7.0.2
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/AbstractSqlConnection.d.ts +95 -47
- package/AbstractSqlConnection.js +240 -232
- package/AbstractSqlDriver.d.ts +412 -155
- package/AbstractSqlDriver.js +2062 -1937
- package/AbstractSqlPlatform.d.ts +84 -73
- package/AbstractSqlPlatform.js +163 -158
- package/PivotCollectionPersister.d.ts +33 -15
- package/PivotCollectionPersister.js +158 -160
- package/README.md +128 -294
- package/SqlEntityManager.d.ts +68 -20
- package/SqlEntityManager.js +54 -37
- package/SqlEntityRepository.d.ts +15 -14
- package/SqlEntityRepository.js +24 -23
- package/dialects/mssql/MsSqlNativeQueryBuilder.d.ts +12 -12
- package/dialects/mssql/MsSqlNativeQueryBuilder.js +192 -194
- package/dialects/mysql/BaseMySqlPlatform.d.ts +64 -45
- package/dialects/mysql/BaseMySqlPlatform.js +134 -131
- package/dialects/mysql/MySqlExceptionConverter.d.ts +6 -6
- package/dialects/mysql/MySqlExceptionConverter.js +91 -77
- package/dialects/mysql/MySqlNativeQueryBuilder.d.ts +3 -3
- package/dialects/mysql/MySqlNativeQueryBuilder.js +66 -69
- package/dialects/mysql/MySqlSchemaHelper.d.ts +39 -39
- package/dialects/mysql/MySqlSchemaHelper.js +327 -319
- package/dialects/oracledb/OracleDialect.d.ts +81 -52
- package/dialects/oracledb/OracleDialect.js +155 -149
- package/dialects/oracledb/OracleNativeQueryBuilder.d.ts +12 -12
- package/dialects/oracledb/OracleNativeQueryBuilder.js +232 -236
- package/dialects/postgresql/BasePostgreSqlPlatform.d.ts +108 -105
- package/dialects/postgresql/BasePostgreSqlPlatform.js +351 -350
- package/dialects/postgresql/FullTextType.d.ts +10 -6
- package/dialects/postgresql/FullTextType.js +51 -51
- package/dialects/postgresql/PostgreSqlExceptionConverter.d.ts +5 -5
- package/dialects/postgresql/PostgreSqlExceptionConverter.js +55 -43
- package/dialects/postgresql/PostgreSqlNativeQueryBuilder.d.ts +1 -1
- package/dialects/postgresql/PostgreSqlNativeQueryBuilder.js +4 -4
- package/dialects/postgresql/PostgreSqlSchemaHelper.d.ts +102 -82
- package/dialects/postgresql/PostgreSqlSchemaHelper.js +711 -683
- package/dialects/sqlite/BaseSqliteConnection.d.ts +3 -5
- package/dialects/sqlite/BaseSqliteConnection.js +21 -19
- package/dialects/sqlite/NodeSqliteDialect.d.ts +1 -1
- package/dialects/sqlite/NodeSqliteDialect.js +23 -23
- package/dialects/sqlite/SqliteDriver.d.ts +1 -1
- package/dialects/sqlite/SqliteDriver.js +3 -3
- package/dialects/sqlite/SqliteExceptionConverter.d.ts +6 -6
- package/dialects/sqlite/SqliteExceptionConverter.js +67 -51
- package/dialects/sqlite/SqliteNativeQueryBuilder.d.ts +2 -2
- package/dialects/sqlite/SqliteNativeQueryBuilder.js +7 -7
- package/dialects/sqlite/SqlitePlatform.d.ts +63 -72
- package/dialects/sqlite/SqlitePlatform.js +139 -139
- package/dialects/sqlite/SqliteSchemaHelper.d.ts +70 -60
- package/dialects/sqlite/SqliteSchemaHelper.js +533 -520
- package/package.json +4 -4
- package/plugin/index.d.ts +44 -35
- package/plugin/index.js +44 -36
- package/plugin/transformer.d.ts +117 -94
- package/plugin/transformer.js +890 -881
- package/query/ArrayCriteriaNode.d.ts +4 -4
- package/query/ArrayCriteriaNode.js +18 -18
- package/query/CriteriaNode.d.ts +35 -25
- package/query/CriteriaNode.js +133 -123
- package/query/CriteriaNodeFactory.d.ts +49 -6
- package/query/CriteriaNodeFactory.js +97 -94
- package/query/NativeQueryBuilder.d.ts +120 -117
- package/query/NativeQueryBuilder.js +484 -480
- package/query/ObjectCriteriaNode.d.ts +12 -12
- package/query/ObjectCriteriaNode.js +298 -282
- package/query/QueryBuilder.d.ts +1546 -904
- package/query/QueryBuilder.js +2270 -2145
- package/query/QueryBuilderHelper.d.ts +153 -72
- package/query/QueryBuilderHelper.js +1079 -1028
- package/query/ScalarCriteriaNode.d.ts +3 -3
- package/query/ScalarCriteriaNode.js +53 -46
- package/query/enums.d.ts +16 -14
- package/query/enums.js +16 -14
- package/query/raw.d.ts +16 -6
- package/query/raw.js +10 -10
- package/schema/DatabaseSchema.d.ts +73 -50
- package/schema/DatabaseSchema.js +331 -307
- package/schema/DatabaseTable.d.ts +96 -73
- package/schema/DatabaseTable.js +1012 -927
- package/schema/SchemaComparator.d.ts +58 -54
- package/schema/SchemaComparator.js +745 -719
- package/schema/SchemaHelper.d.ts +110 -80
- package/schema/SchemaHelper.js +676 -645
- package/schema/SqlSchemaGenerator.d.ts +79 -58
- package/schema/SqlSchemaGenerator.js +536 -501
- package/typings.d.ts +380 -266
|
@@ -4,16 +4,16 @@ import type { ICriteriaNodeProcessOptions, IQueryBuilder } from '../typings.js';
|
|
|
4
4
|
* @internal
|
|
5
5
|
*/
|
|
6
6
|
export declare class ObjectCriteriaNode<T extends object> extends CriteriaNode<T> {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
7
|
+
process(qb: IQueryBuilder<T>, options?: ICriteriaNodeProcessOptions): any;
|
|
8
|
+
isStrict(): boolean;
|
|
9
|
+
unwrap(): any;
|
|
10
|
+
willAutoJoin(qb: IQueryBuilder<T>, alias?: string, options?: ICriteriaNodeProcessOptions): boolean;
|
|
11
|
+
shouldInline(payload: any): boolean;
|
|
12
|
+
private getChildKey;
|
|
13
|
+
private inlineArrayChildPayload;
|
|
14
|
+
private inlineChildPayload;
|
|
15
|
+
private inlineCondition;
|
|
16
|
+
private shouldAutoJoin;
|
|
17
|
+
private autoJoin;
|
|
18
|
+
private isPrefixed;
|
|
19
19
|
}
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ALIAS_REPLACEMENT,
|
|
3
|
+
GroupOperator,
|
|
4
|
+
QueryFlag,
|
|
5
|
+
raw,
|
|
6
|
+
RawQueryFragment,
|
|
7
|
+
ReferenceKind,
|
|
8
|
+
Utils,
|
|
9
|
+
} from '@mikro-orm/core';
|
|
2
10
|
import { CriteriaNode } from './CriteriaNode.js';
|
|
3
11
|
import { JoinType, QueryType } from './enums.js';
|
|
4
12
|
const COLLECTION_OPERATORS = ['$some', '$none', '$every', '$size'];
|
|
@@ -6,303 +14,311 @@ const COLLECTION_OPERATORS = ['$some', '$none', '$every', '$size'];
|
|
|
6
14
|
* @internal
|
|
7
15
|
*/
|
|
8
16
|
export class ObjectCriteriaNode extends CriteriaNode {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
process(qb, options) {
|
|
18
|
+
const matchPopulateJoins =
|
|
19
|
+
options?.matchPopulateJoins ||
|
|
20
|
+
(this.prop && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(this.prop.kind));
|
|
21
|
+
const nestedAlias = qb.getAliasForJoinPath(this.getPath(options), { ...options, matchPopulateJoins });
|
|
22
|
+
const ownerAlias = options?.alias || qb.alias;
|
|
23
|
+
const keys = Utils.getObjectQueryKeys(this.payload);
|
|
24
|
+
let alias = options?.alias;
|
|
25
|
+
if (nestedAlias) {
|
|
26
|
+
alias = nestedAlias;
|
|
27
|
+
}
|
|
28
|
+
if (this.shouldAutoJoin(qb, nestedAlias)) {
|
|
29
|
+
if (keys.some(k => COLLECTION_OPERATORS.includes(k))) {
|
|
30
|
+
if (![ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(this.prop.kind)) {
|
|
31
|
+
// ignore collection operators when used on a non-relational property - this can happen when they get into
|
|
32
|
+
// populateWhere via `infer` on m:n properties with select-in strategy
|
|
33
|
+
if (this.parent?.parent) {
|
|
34
|
+
// we validate only usage on top level
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
throw new Error(
|
|
38
|
+
`Collection operators can be used only inside a collection property context, but it was used for ${this.getPath()}.`,
|
|
39
|
+
);
|
|
18
40
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
sub.where({ $not: { [this.key]: payload } });
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
sub.where({ [this.key]: payload });
|
|
63
|
-
}
|
|
64
|
-
const op = ['$size', '$some'].includes(key) ? '$in' : '$nin';
|
|
65
|
-
$and.push({
|
|
66
|
-
[Utils.getPrimaryKeyHash(primaryKeys)]: { [op]: sub.getNativeQuery().toRaw() },
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
if ($and.length === 1) {
|
|
70
|
-
return $and[0];
|
|
71
|
-
}
|
|
72
|
-
return { $and };
|
|
73
|
-
}
|
|
74
|
-
alias = this.autoJoin(qb, ownerAlias, options);
|
|
41
|
+
const $and = [];
|
|
42
|
+
const knownKey =
|
|
43
|
+
[ReferenceKind.SCALAR, ReferenceKind.MANY_TO_ONE, ReferenceKind.EMBEDDED].includes(this.prop.kind) ||
|
|
44
|
+
(this.prop.kind === ReferenceKind.ONE_TO_ONE && this.prop.owner);
|
|
45
|
+
const parentMeta = this.metadata.find(this.parent.entityName);
|
|
46
|
+
const primaryKeys = parentMeta.primaryKeys.map(pk => {
|
|
47
|
+
return [QueryType.SELECT, QueryType.COUNT].includes(qb.type) ? `${knownKey ? alias : ownerAlias}.${pk}` : pk;
|
|
48
|
+
});
|
|
49
|
+
for (const key of keys) {
|
|
50
|
+
if (typeof key !== 'string' || !COLLECTION_OPERATORS.includes(key)) {
|
|
51
|
+
throw new Error('Mixing collection operators with other filters is not allowed.');
|
|
52
|
+
}
|
|
53
|
+
const payload = this.payload[key].unwrap();
|
|
54
|
+
const qb2 = qb.clone(true, ['schema']);
|
|
55
|
+
const joinAlias = qb2.getNextAlias(this.prop.targetMeta.class);
|
|
56
|
+
const sub = qb2
|
|
57
|
+
.from(parentMeta.class)
|
|
58
|
+
// eslint-disable-next-line no-unexpected-multiline
|
|
59
|
+
[key === '$size' ? 'leftJoin' : 'innerJoin'](this.key, joinAlias)
|
|
60
|
+
.select(parentMeta.primaryKeys);
|
|
61
|
+
if (key === '$size') {
|
|
62
|
+
const sizeCondition = typeof payload === 'number' ? { $eq: payload } : payload;
|
|
63
|
+
const pks = this.prop.referencedColumnNames;
|
|
64
|
+
const countExpr = raw(
|
|
65
|
+
`count(${pks.map(() => '??').join(', ')})`,
|
|
66
|
+
pks.map(pk => `${joinAlias}.${pk}`),
|
|
67
|
+
);
|
|
68
|
+
sub.groupBy(parentMeta.primaryKeys);
|
|
69
|
+
sub.having({
|
|
70
|
+
$and: Object.keys(sizeCondition).map(op => ({ [countExpr]: { [op]: sizeCondition[op] } })),
|
|
71
|
+
});
|
|
72
|
+
} else if (key === '$every') {
|
|
73
|
+
sub.where({ $not: { [this.key]: payload } });
|
|
74
|
+
} else {
|
|
75
|
+
sub.where({ [this.key]: payload });
|
|
76
|
+
}
|
|
77
|
+
const op = ['$size', '$some'].includes(key) ? '$in' : '$nin';
|
|
78
|
+
$and.push({
|
|
79
|
+
[Utils.getPrimaryKeyHash(primaryKeys)]: { [op]: sub.getNativeQuery().toRaw() },
|
|
80
|
+
});
|
|
75
81
|
}
|
|
76
|
-
if (
|
|
77
|
-
|
|
78
|
-
// if the property is nullable and the filter is strict, we need to use left join, so we mimic the inner join behaviour
|
|
79
|
-
// with an exclusive condition on the join columns:
|
|
80
|
-
// - if the owning column is null, the row is missing, we don't apply the filter
|
|
81
|
-
// - if the target column is not null, the row is matched, we apply the filter
|
|
82
|
-
if (toOneProperty && this.prop.nullable && this.isStrict()) {
|
|
83
|
-
const key = this.prop.owner ? this.prop.name : this.prop.referencedPKs;
|
|
84
|
-
qb.andWhere({
|
|
85
|
-
$or: [
|
|
86
|
-
{ [ownerAlias + '.' + key]: null },
|
|
87
|
-
{ [nestedAlias + '.' + Utils.getPrimaryKeyHash(this.prop.referencedPKs)]: { $ne: null } },
|
|
88
|
-
],
|
|
89
|
-
});
|
|
90
|
-
}
|
|
82
|
+
if ($and.length === 1) {
|
|
83
|
+
return $and[0];
|
|
91
84
|
}
|
|
92
|
-
return
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const operator = Utils.isOperator(field);
|
|
96
|
-
const isRawField = RawQueryFragment.isKnownFragmentSymbol(field);
|
|
97
|
-
// we need to keep the prefixing for formulas otherwise we would lose aliasing context when nesting inside group operators
|
|
98
|
-
const virtual = childNode.prop?.persist === false && !childNode.prop?.formula && !!options?.type;
|
|
99
|
-
// if key is missing, we are inside group operator and we need to prefix with alias
|
|
100
|
-
const primaryKey = this.key && this.metadata.find(this.entityName)?.primaryKeys.includes(field);
|
|
101
|
-
const isToOne = childNode.prop && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(childNode.prop.kind);
|
|
102
|
-
if (childNode.shouldInline(payload)) {
|
|
103
|
-
const childAlias = qb.getAliasForJoinPath(childNode.getPath(), { preferNoBranch: isToOne, ...options });
|
|
104
|
-
const a = qb.helper.isTableNameAliasRequired(qb.type) ? alias : undefined;
|
|
105
|
-
this.inlineChildPayload(o, payload, field, a, childAlias);
|
|
106
|
-
}
|
|
107
|
-
else if (childNode.shouldRename(payload)) {
|
|
108
|
-
this.inlineCondition(childNode.renameFieldToPK(qb, alias), o, payload);
|
|
109
|
-
}
|
|
110
|
-
else if (isRawField) {
|
|
111
|
-
const rawField = RawQueryFragment.getKnownFragment(field);
|
|
112
|
-
o[raw(rawField.sql.replaceAll(ALIAS_REPLACEMENT, alias), rawField.params)] = payload;
|
|
113
|
-
}
|
|
114
|
-
else if (!childNode.validate && !childNode.prop && !field.includes('.') && !operator) {
|
|
115
|
-
// wrap unknown fields in raw() to prevent alias prefixing (e.g. raw SQL aliases in HAVING)
|
|
116
|
-
// use '??' placeholder to properly quote the identifier
|
|
117
|
-
o[raw('??', [field])] = payload;
|
|
118
|
-
}
|
|
119
|
-
else if (primaryKey ||
|
|
120
|
-
virtual ||
|
|
121
|
-
operator ||
|
|
122
|
-
field.includes('.') ||
|
|
123
|
-
![QueryType.SELECT, QueryType.COUNT].includes(qb.type)) {
|
|
124
|
-
this.inlineCondition(field.replaceAll(ALIAS_REPLACEMENT, alias), o, payload);
|
|
125
|
-
}
|
|
126
|
-
else {
|
|
127
|
-
this.inlineCondition(`${alias ?? qb.alias}.${field}`, o, payload);
|
|
128
|
-
}
|
|
129
|
-
return o;
|
|
130
|
-
}, {});
|
|
85
|
+
return { $and };
|
|
86
|
+
}
|
|
87
|
+
alias = this.autoJoin(qb, ownerAlias, options);
|
|
131
88
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
89
|
+
if (this.prop && nestedAlias) {
|
|
90
|
+
const toOneProperty = [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(this.prop.kind);
|
|
91
|
+
// if the property is nullable and the filter is strict, we need to use left join, so we mimic the inner join behaviour
|
|
92
|
+
// with an exclusive condition on the join columns:
|
|
93
|
+
// - if the owning column is null, the row is missing, we don't apply the filter
|
|
94
|
+
// - if the target column is not null, the row is matched, we apply the filter
|
|
95
|
+
if (toOneProperty && this.prop.nullable && this.isStrict()) {
|
|
96
|
+
const key = this.prop.owner ? this.prop.name : this.prop.referencedPKs;
|
|
97
|
+
qb.andWhere({
|
|
98
|
+
$or: [
|
|
99
|
+
{ [ownerAlias + '.' + key]: null },
|
|
100
|
+
{ [nestedAlias + '.' + Utils.getPrimaryKeyHash(this.prop.referencedPKs)]: { $ne: null } },
|
|
101
|
+
],
|
|
102
|
+
});
|
|
103
|
+
}
|
|
137
104
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
105
|
+
return keys.reduce((o, field) => {
|
|
106
|
+
const childNode = this.payload[field];
|
|
107
|
+
const payload = childNode.process(qb, { ...options, alias: this.prop ? alias : ownerAlias });
|
|
108
|
+
const operator = Utils.isOperator(field);
|
|
109
|
+
const isRawField = RawQueryFragment.isKnownFragmentSymbol(field);
|
|
110
|
+
// we need to keep the prefixing for formulas otherwise we would lose aliasing context when nesting inside group operators
|
|
111
|
+
const virtual = childNode.prop?.persist === false && !childNode.prop?.formula && !!options?.type;
|
|
112
|
+
// if key is missing, we are inside group operator and we need to prefix with alias
|
|
113
|
+
const primaryKey = this.key && this.metadata.find(this.entityName)?.primaryKeys.includes(field);
|
|
114
|
+
const isToOne =
|
|
115
|
+
childNode.prop && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(childNode.prop.kind);
|
|
116
|
+
if (childNode.shouldInline(payload)) {
|
|
117
|
+
const childAlias = qb.getAliasForJoinPath(childNode.getPath(), { preferNoBranch: isToOne, ...options });
|
|
118
|
+
const a = qb.helper.isTableNameAliasRequired(qb.type) ? alias : undefined;
|
|
119
|
+
this.inlineChildPayload(o, payload, field, a, childAlias);
|
|
120
|
+
} else if (childNode.shouldRename(payload)) {
|
|
121
|
+
this.inlineCondition(childNode.renameFieldToPK(qb, alias), o, payload);
|
|
122
|
+
} else if (isRawField) {
|
|
123
|
+
const rawField = RawQueryFragment.getKnownFragment(field);
|
|
124
|
+
o[raw(rawField.sql.replaceAll(ALIAS_REPLACEMENT, alias), rawField.params)] = payload;
|
|
125
|
+
} else if (!childNode.validate && !childNode.prop && !field.includes('.') && !operator) {
|
|
126
|
+
// wrap unknown fields in raw() to prevent alias prefixing (e.g. raw SQL aliases in HAVING)
|
|
127
|
+
// use '??' placeholder to properly quote the identifier
|
|
128
|
+
o[raw('??', [field])] = payload;
|
|
129
|
+
} else if (
|
|
130
|
+
primaryKey ||
|
|
131
|
+
virtual ||
|
|
132
|
+
operator ||
|
|
133
|
+
field.includes('.') ||
|
|
134
|
+
![QueryType.SELECT, QueryType.COUNT].includes(qb.type)
|
|
135
|
+
) {
|
|
136
|
+
this.inlineCondition(field.replaceAll(ALIAS_REPLACEMENT, alias), o, payload);
|
|
137
|
+
} else {
|
|
138
|
+
this.inlineCondition(`${alias ?? qb.alias}.${field}`, o, payload);
|
|
139
|
+
}
|
|
140
|
+
return o;
|
|
141
|
+
}, {});
|
|
142
|
+
}
|
|
143
|
+
isStrict() {
|
|
144
|
+
return (
|
|
145
|
+
this.strict ||
|
|
146
|
+
Utils.getObjectQueryKeys(this.payload).some(key => {
|
|
147
|
+
return this.payload[key].isStrict();
|
|
148
|
+
})
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
unwrap() {
|
|
152
|
+
return Utils.getObjectQueryKeys(this.payload).reduce((o, field) => {
|
|
153
|
+
o[field] = this.payload[field].unwrap();
|
|
154
|
+
return o;
|
|
155
|
+
}, {});
|
|
156
|
+
}
|
|
157
|
+
willAutoJoin(qb, alias, options) {
|
|
158
|
+
const nestedAlias = qb.getAliasForJoinPath(this.getPath(options), options);
|
|
159
|
+
const ownerAlias = alias || qb.alias;
|
|
160
|
+
const keys = Utils.getObjectQueryKeys(this.payload);
|
|
161
|
+
if (nestedAlias) {
|
|
162
|
+
alias = nestedAlias;
|
|
143
163
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
164
|
+
if (this.shouldAutoJoin(qb, nestedAlias)) {
|
|
165
|
+
return !keys.some(k => COLLECTION_OPERATORS.includes(k));
|
|
166
|
+
}
|
|
167
|
+
return keys.some(field => {
|
|
168
|
+
const childNode = this.payload[field];
|
|
169
|
+
return childNode.willAutoJoin(qb, this.prop ? alias : ownerAlias, options);
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
shouldInline(payload) {
|
|
173
|
+
const rawField = RawQueryFragment.isKnownFragmentSymbol(this.key);
|
|
174
|
+
const scalar = Utils.isPrimaryKey(payload) || payload instanceof RegExp || payload instanceof Date || rawField;
|
|
175
|
+
const operator =
|
|
176
|
+
Utils.isObject(payload) &&
|
|
177
|
+
Utils.getObjectQueryKeys(payload).every(k => {
|
|
178
|
+
if (k === '$not' && Utils.isPlainObject(payload[k])) {
|
|
179
|
+
// $not wrapping non-operator conditions (entity props) should be inlined
|
|
180
|
+
return Utils.getObjectQueryKeys(payload[k]).every(ik => Utils.isOperator(ik, false));
|
|
153
181
|
}
|
|
154
|
-
return
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
182
|
+
return Utils.isOperator(k, false);
|
|
183
|
+
});
|
|
184
|
+
return !!this.prop && this.prop.kind !== ReferenceKind.SCALAR && !scalar && !operator;
|
|
185
|
+
}
|
|
186
|
+
getChildKey(k, prop, childAlias, alias) {
|
|
187
|
+
const idx = prop.referencedPKs.indexOf(k);
|
|
188
|
+
return idx !== -1 && !childAlias && ![ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind)
|
|
189
|
+
? this.aliased(prop.joinColumns[idx], alias)
|
|
190
|
+
: k;
|
|
191
|
+
}
|
|
192
|
+
inlineArrayChildPayload(obj, payload, k, prop, childAlias, alias) {
|
|
193
|
+
const key = this.getChildKey(k, prop, childAlias);
|
|
194
|
+
const value = payload.map(child =>
|
|
195
|
+
Utils.getObjectQueryKeys(child).reduce((inner, childKey) => {
|
|
196
|
+
const key =
|
|
197
|
+
RawQueryFragment.isKnownFragmentSymbol(childKey) || this.isPrefixed(childKey) || Utils.isOperator(childKey)
|
|
198
|
+
? childKey
|
|
199
|
+
: this.aliased(childKey, childAlias);
|
|
200
|
+
inner[key] = child[childKey];
|
|
201
|
+
return inner;
|
|
202
|
+
}, {}),
|
|
203
|
+
);
|
|
204
|
+
this.inlineCondition(key, obj, value);
|
|
205
|
+
}
|
|
206
|
+
inlineChildPayload(o, payload, field, alias, childAlias) {
|
|
207
|
+
const prop = this.metadata.find(this.entityName).properties[field];
|
|
208
|
+
for (const k of Utils.getObjectQueryKeys(payload)) {
|
|
209
|
+
if (RawQueryFragment.isKnownFragmentSymbol(k)) {
|
|
210
|
+
o[k] = payload[k];
|
|
211
|
+
} else if (
|
|
212
|
+
k === '$not' &&
|
|
213
|
+
Utils.isPlainObject(payload[k]) &&
|
|
214
|
+
Utils.getObjectQueryKeys(payload[k]).some(ik => !Utils.isOperator(ik, false))
|
|
215
|
+
) {
|
|
216
|
+
// $not wraps entity conditions (from auto-join), inline at current level
|
|
217
|
+
this.inlineCondition(k, o, payload[k]);
|
|
218
|
+
} else if (Utils.isOperator(k, false)) {
|
|
219
|
+
const tmp = payload[k];
|
|
220
|
+
delete payload[k];
|
|
221
|
+
o[this.aliased(field, alias)] = { [k]: tmp, ...o[this.aliased(field, alias)] };
|
|
222
|
+
} else if (k in GroupOperator && Array.isArray(payload[k])) {
|
|
223
|
+
this.inlineArrayChildPayload(o, payload[k], k, prop, childAlias, alias);
|
|
224
|
+
} else if (this.isPrefixed(k) || Utils.isOperator(k) || !childAlias) {
|
|
225
|
+
const key = this.getChildKey(k, prop, childAlias, alias);
|
|
226
|
+
this.inlineCondition(key, o, payload[k]);
|
|
227
|
+
} else {
|
|
228
|
+
o[this.aliased(k, childAlias)] = payload[k];
|
|
229
|
+
}
|
|
158
230
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
rawField;
|
|
165
|
-
const operator = Utils.isObject(payload) &&
|
|
166
|
-
Utils.getObjectQueryKeys(payload).every(k => {
|
|
167
|
-
if (k === '$not' && Utils.isPlainObject(payload[k])) {
|
|
168
|
-
// $not wrapping non-operator conditions (entity props) should be inlined
|
|
169
|
-
return Utils.getObjectQueryKeys(payload[k]).every(ik => Utils.isOperator(ik, false));
|
|
170
|
-
}
|
|
171
|
-
return Utils.isOperator(k, false);
|
|
172
|
-
});
|
|
173
|
-
return !!this.prop && this.prop.kind !== ReferenceKind.SCALAR && !scalar && !operator;
|
|
231
|
+
}
|
|
232
|
+
inlineCondition(key, o, value) {
|
|
233
|
+
if (!(key in o)) {
|
|
234
|
+
o[key] = value;
|
|
235
|
+
return;
|
|
174
236
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
: k;
|
|
237
|
+
/* v8 ignore next */
|
|
238
|
+
if (key === '$and') {
|
|
239
|
+
o.$and.push({ [key]: value });
|
|
240
|
+
return;
|
|
180
241
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}, {}));
|
|
190
|
-
this.inlineCondition(key, obj, value);
|
|
242
|
+
const $and = o.$and ?? [];
|
|
243
|
+
$and.push({ [key]: o[key] }, { [key]: value });
|
|
244
|
+
delete o[key];
|
|
245
|
+
o.$and = $and;
|
|
246
|
+
}
|
|
247
|
+
shouldAutoJoin(qb, nestedAlias) {
|
|
248
|
+
if (!this.prop || !this.parent) {
|
|
249
|
+
return false;
|
|
191
250
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
if (RawQueryFragment.isKnownFragmentSymbol(k)) {
|
|
196
|
-
o[k] = payload[k];
|
|
197
|
-
}
|
|
198
|
-
else if (k === '$not' &&
|
|
199
|
-
Utils.isPlainObject(payload[k]) &&
|
|
200
|
-
Utils.getObjectQueryKeys(payload[k]).some(ik => !Utils.isOperator(ik, false))) {
|
|
201
|
-
// $not wraps entity conditions (from auto-join), inline at current level
|
|
202
|
-
this.inlineCondition(k, o, payload[k]);
|
|
203
|
-
}
|
|
204
|
-
else if (Utils.isOperator(k, false)) {
|
|
205
|
-
const tmp = payload[k];
|
|
206
|
-
delete payload[k];
|
|
207
|
-
o[this.aliased(field, alias)] = { [k]: tmp, ...o[this.aliased(field, alias)] };
|
|
208
|
-
}
|
|
209
|
-
else if (k in GroupOperator && Array.isArray(payload[k])) {
|
|
210
|
-
this.inlineArrayChildPayload(o, payload[k], k, prop, childAlias, alias);
|
|
211
|
-
}
|
|
212
|
-
else if (this.isPrefixed(k) || Utils.isOperator(k) || !childAlias) {
|
|
213
|
-
const key = this.getChildKey(k, prop, childAlias, alias);
|
|
214
|
-
this.inlineCondition(key, o, payload[k]);
|
|
215
|
-
}
|
|
216
|
-
else {
|
|
217
|
-
o[this.aliased(k, childAlias)] = payload[k];
|
|
218
|
-
}
|
|
219
|
-
}
|
|
251
|
+
const keys = Utils.getObjectQueryKeys(this.payload);
|
|
252
|
+
if (keys.every(k => typeof k === 'string' && k.includes('.') && k.startsWith(`${qb.alias}.`))) {
|
|
253
|
+
return false;
|
|
220
254
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
o[key] = value;
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
/* v8 ignore next */
|
|
227
|
-
if (key === '$and') {
|
|
228
|
-
o.$and.push({ [key]: value });
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
const $and = o.$and ?? [];
|
|
232
|
-
$and.push({ [key]: o[key] }, { [key]: value });
|
|
233
|
-
delete o[key];
|
|
234
|
-
o.$and = $and;
|
|
235
|
-
}
|
|
236
|
-
shouldAutoJoin(qb, nestedAlias) {
|
|
237
|
-
if (!this.prop || !this.parent) {
|
|
238
|
-
return false;
|
|
239
|
-
}
|
|
240
|
-
const keys = Utils.getObjectQueryKeys(this.payload);
|
|
241
|
-
if (keys.every(k => typeof k === 'string' && k.includes('.') && k.startsWith(`${qb.alias}.`))) {
|
|
242
|
-
return false;
|
|
243
|
-
}
|
|
244
|
-
if (keys.some(k => COLLECTION_OPERATORS.includes(k))) {
|
|
245
|
-
return true;
|
|
246
|
-
}
|
|
247
|
-
const meta = this.metadata.find(this.entityName);
|
|
248
|
-
const embeddable = this.prop.kind === ReferenceKind.EMBEDDED;
|
|
249
|
-
const knownKey = [ReferenceKind.SCALAR, ReferenceKind.MANY_TO_ONE, ReferenceKind.EMBEDDED].includes(this.prop.kind) ||
|
|
250
|
-
(this.prop.kind === ReferenceKind.ONE_TO_ONE && this.prop.owner);
|
|
251
|
-
const operatorKeys = knownKey &&
|
|
252
|
-
keys.every(key => {
|
|
253
|
-
if (key === '$not') {
|
|
254
|
-
// $not wraps conditions like $and/$or, check if it wraps entity property conditions (needs auto-join)
|
|
255
|
-
// vs simple operator conditions on the FK (doesn't need auto-join)
|
|
256
|
-
const childPayload = this.payload[key].payload;
|
|
257
|
-
if (Utils.isPlainObject(childPayload)) {
|
|
258
|
-
return Utils.getObjectQueryKeys(childPayload).every(k => Utils.isOperator(k, false));
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
return Utils.isOperator(key, false);
|
|
262
|
-
});
|
|
263
|
-
const primaryKeys = knownKey &&
|
|
264
|
-
keys.every(key => {
|
|
265
|
-
if (typeof key !== 'string' || !meta.primaryKeys.includes(key)) {
|
|
266
|
-
return false;
|
|
267
|
-
}
|
|
268
|
-
if (!Utils.isPlainObject(this.payload[key].payload) ||
|
|
269
|
-
![ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(meta.properties[key].kind)) {
|
|
270
|
-
return true;
|
|
271
|
-
}
|
|
272
|
-
return Utils.getObjectQueryKeys(this.payload[key].payload).every(k => typeof k === 'string' && meta.properties[key].targetMeta.primaryKeys.includes(k));
|
|
273
|
-
});
|
|
274
|
-
return !primaryKeys && !nestedAlias && !operatorKeys && !embeddable;
|
|
255
|
+
if (keys.some(k => COLLECTION_OPERATORS.includes(k))) {
|
|
256
|
+
return true;
|
|
275
257
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
258
|
+
const meta = this.metadata.find(this.entityName);
|
|
259
|
+
const embeddable = this.prop.kind === ReferenceKind.EMBEDDED;
|
|
260
|
+
const knownKey =
|
|
261
|
+
[ReferenceKind.SCALAR, ReferenceKind.MANY_TO_ONE, ReferenceKind.EMBEDDED].includes(this.prop.kind) ||
|
|
262
|
+
(this.prop.kind === ReferenceKind.ONE_TO_ONE && this.prop.owner);
|
|
263
|
+
const operatorKeys =
|
|
264
|
+
knownKey &&
|
|
265
|
+
keys.every(key => {
|
|
266
|
+
if (key === '$not') {
|
|
267
|
+
// $not wraps conditions like $and/$or, check if it wraps entity property conditions (needs auto-join)
|
|
268
|
+
// vs simple operator conditions on the FK (doesn't need auto-join)
|
|
269
|
+
const childPayload = this.payload[key].payload;
|
|
270
|
+
if (Utils.isPlainObject(childPayload)) {
|
|
271
|
+
return Utils.getObjectQueryKeys(childPayload).every(k => Utils.isOperator(k, false));
|
|
272
|
+
}
|
|
290
273
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
}
|
|
274
|
+
return Utils.isOperator(key, false);
|
|
275
|
+
});
|
|
276
|
+
const primaryKeys =
|
|
277
|
+
knownKey &&
|
|
278
|
+
keys.every(key => {
|
|
279
|
+
if (typeof key !== 'string' || !meta.primaryKeys.includes(key)) {
|
|
280
|
+
return false;
|
|
299
281
|
}
|
|
300
|
-
if (
|
|
301
|
-
|
|
282
|
+
if (
|
|
283
|
+
!Utils.isPlainObject(this.payload[key].payload) ||
|
|
284
|
+
![ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(meta.properties[key].kind)
|
|
285
|
+
) {
|
|
286
|
+
return true;
|
|
302
287
|
}
|
|
303
|
-
return
|
|
288
|
+
return Utils.getObjectQueryKeys(this.payload[key].payload).every(
|
|
289
|
+
k => typeof k === 'string' && meta.properties[key].targetMeta.primaryKeys.includes(k),
|
|
290
|
+
);
|
|
291
|
+
});
|
|
292
|
+
return !primaryKeys && !nestedAlias && !operatorKeys && !embeddable;
|
|
293
|
+
}
|
|
294
|
+
autoJoin(qb, alias, options) {
|
|
295
|
+
const nestedAlias = qb.getNextAlias(this.prop?.pivotEntity ?? this.entityName);
|
|
296
|
+
const rawField = RawQueryFragment.isKnownFragmentSymbol(this.key);
|
|
297
|
+
const scalar =
|
|
298
|
+
Utils.isPrimaryKey(this.payload) || this.payload instanceof RegExp || this.payload instanceof Date || rawField;
|
|
299
|
+
const operator =
|
|
300
|
+
Utils.isPlainObject(this.payload) &&
|
|
301
|
+
Utils.getObjectQueryKeys(this.payload).every(k => Utils.isOperator(k, false));
|
|
302
|
+
const field = `${alias}.${this.prop.name}`;
|
|
303
|
+
const method = qb.hasFlag(QueryFlag.INFER_POPULATE) ? 'joinAndSelect' : 'join';
|
|
304
|
+
const path = this.getPath();
|
|
305
|
+
if (this.prop.kind === ReferenceKind.MANY_TO_MANY && (scalar || operator)) {
|
|
306
|
+
qb.join(field, nestedAlias, undefined, JoinType.pivotJoin, path);
|
|
307
|
+
} else {
|
|
308
|
+
const prev = qb.state.fields?.slice();
|
|
309
|
+
const toOneProperty = [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(this.prop.kind);
|
|
310
|
+
const joinType = toOneProperty && !this.prop.nullable ? JoinType.innerJoin : JoinType.leftJoin;
|
|
311
|
+
qb[method](field, nestedAlias, undefined, joinType, path);
|
|
312
|
+
if (!qb.hasFlag(QueryFlag.INFER_POPULATE)) {
|
|
313
|
+
qb.state.fields = prev;
|
|
314
|
+
}
|
|
304
315
|
}
|
|
305
|
-
|
|
306
|
-
|
|
316
|
+
if (options?.type !== 'orderBy') {
|
|
317
|
+
qb.scheduleFilterCheck(path);
|
|
307
318
|
}
|
|
319
|
+
return nestedAlias;
|
|
320
|
+
}
|
|
321
|
+
isPrefixed(field) {
|
|
322
|
+
return !!/\w+\./.exec(field);
|
|
323
|
+
}
|
|
308
324
|
}
|