@mikro-orm/sql 7.0.0-rc.2 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AbstractSqlConnection.d.ts +5 -4
- package/AbstractSqlConnection.js +20 -6
- package/AbstractSqlDriver.d.ts +19 -13
- package/AbstractSqlDriver.js +225 -47
- package/AbstractSqlPlatform.d.ts +35 -0
- package/AbstractSqlPlatform.js +51 -5
- package/PivotCollectionPersister.d.ts +2 -11
- package/PivotCollectionPersister.js +59 -59
- package/README.md +5 -4
- package/SqlEntityManager.d.ts +2 -2
- package/SqlEntityManager.js +5 -5
- package/dialects/index.d.ts +1 -0
- package/dialects/index.js +1 -0
- package/dialects/mssql/MsSqlNativeQueryBuilder.d.ts +2 -0
- package/dialects/mssql/MsSqlNativeQueryBuilder.js +8 -4
- package/dialects/mysql/BaseMySqlPlatform.d.ts +6 -0
- package/dialects/mysql/BaseMySqlPlatform.js +18 -2
- package/dialects/mysql/MySqlSchemaHelper.d.ts +1 -1
- package/dialects/mysql/MySqlSchemaHelper.js +25 -14
- package/dialects/oracledb/OracleDialect.d.ts +78 -0
- package/dialects/oracledb/OracleDialect.js +166 -0
- package/dialects/oracledb/OracleNativeQueryBuilder.d.ts +19 -0
- package/dialects/oracledb/OracleNativeQueryBuilder.js +249 -0
- package/dialects/oracledb/index.d.ts +2 -0
- package/dialects/oracledb/index.js +2 -0
- package/dialects/postgresql/BasePostgreSqlPlatform.d.ts +6 -0
- package/dialects/postgresql/BasePostgreSqlPlatform.js +49 -37
- package/dialects/postgresql/PostgreSqlSchemaHelper.js +75 -59
- package/dialects/sqlite/BaseSqliteConnection.js +2 -2
- package/dialects/sqlite/NodeSqliteDialect.js +3 -1
- package/dialects/sqlite/SqlitePlatform.d.ts +1 -0
- package/dialects/sqlite/SqlitePlatform.js +7 -1
- package/dialects/sqlite/SqliteSchemaHelper.js +23 -17
- package/index.d.ts +1 -1
- package/index.js +0 -1
- package/package.json +30 -30
- package/plugin/index.d.ts +1 -14
- package/plugin/index.js +13 -13
- package/plugin/transformer.d.ts +6 -22
- package/plugin/transformer.js +91 -82
- package/query/ArrayCriteriaNode.d.ts +1 -1
- package/query/CriteriaNode.js +28 -10
- package/query/CriteriaNodeFactory.js +20 -4
- package/query/NativeQueryBuilder.d.ts +28 -3
- package/query/NativeQueryBuilder.js +65 -3
- package/query/ObjectCriteriaNode.js +75 -31
- package/query/QueryBuilder.d.ts +199 -100
- package/query/QueryBuilder.js +544 -358
- package/query/QueryBuilderHelper.d.ts +18 -14
- package/query/QueryBuilderHelper.js +364 -147
- package/query/ScalarCriteriaNode.js +17 -8
- package/query/enums.d.ts +2 -0
- package/query/enums.js +2 -0
- package/query/raw.js +1 -1
- package/schema/DatabaseSchema.d.ts +7 -5
- package/schema/DatabaseSchema.js +68 -45
- package/schema/DatabaseTable.d.ts +8 -6
- package/schema/DatabaseTable.js +191 -107
- package/schema/SchemaComparator.d.ts +1 -3
- package/schema/SchemaComparator.js +76 -50
- package/schema/SchemaHelper.d.ts +2 -13
- package/schema/SchemaHelper.js +30 -9
- package/schema/SqlSchemaGenerator.d.ts +4 -14
- package/schema/SqlSchemaGenerator.js +26 -12
- package/typings.d.ts +10 -5
- package/tsconfig.build.tsbuildinfo +0 -1
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import { type Dictionary, LockMode, type QueryFlag, RawQueryFragment, type Subquery } from '@mikro-orm/core';
|
|
2
2
|
import { QueryType } from './enums.js';
|
|
3
3
|
import type { AbstractSqlPlatform } from '../AbstractSqlPlatform.js';
|
|
4
|
+
export interface CteOptions {
|
|
5
|
+
columns?: string[];
|
|
6
|
+
/** PostgreSQL: MATERIALIZED / NOT MATERIALIZED */
|
|
7
|
+
materialized?: boolean;
|
|
8
|
+
}
|
|
9
|
+
interface CteClause extends CteOptions {
|
|
10
|
+
name: string;
|
|
11
|
+
sql: string;
|
|
12
|
+
params: unknown[];
|
|
13
|
+
recursive?: boolean;
|
|
14
|
+
}
|
|
4
15
|
interface Options {
|
|
5
16
|
tableName?: string | RawQueryFragment;
|
|
6
17
|
indexHint?: string;
|
|
@@ -27,13 +38,14 @@ interface Options {
|
|
|
27
38
|
onConflict?: OnConflictClause;
|
|
28
39
|
lockMode?: LockMode;
|
|
29
40
|
lockTables?: string[];
|
|
30
|
-
returning?: (string | RawQueryFragment)[];
|
|
41
|
+
returning?: (string | RawQueryFragment | [name: string, type: unknown])[];
|
|
31
42
|
comment?: string[];
|
|
32
43
|
hintComment?: string[];
|
|
33
44
|
flags?: Set<QueryFlag>;
|
|
34
45
|
wrap?: [prefix: string, suffix: string];
|
|
46
|
+
ctes?: CteClause[];
|
|
35
47
|
}
|
|
36
|
-
interface TableOptions {
|
|
48
|
+
export interface TableOptions {
|
|
37
49
|
schema?: string;
|
|
38
50
|
indexHint?: string;
|
|
39
51
|
alias?: string;
|
|
@@ -65,6 +77,17 @@ export declare class NativeQueryBuilder implements Subquery {
|
|
|
65
77
|
groupBy(groupBy: (string | RawQueryFragment)[]): this;
|
|
66
78
|
join(sql: string, params: unknown[]): this;
|
|
67
79
|
orderBy(orderBy: string): this;
|
|
80
|
+
/**
|
|
81
|
+
* The sub-query is compiled eagerly at call time — later mutations to the
|
|
82
|
+
* sub-query builder will not be reflected in this CTE.
|
|
83
|
+
*/
|
|
84
|
+
with(name: string, query: NativeQueryBuilder | RawQueryFragment, options?: CteOptions): this;
|
|
85
|
+
/**
|
|
86
|
+
* Adds a recursive CTE (`WITH RECURSIVE` on PostgreSQL/MySQL/SQLite, plain `WITH` on MSSQL).
|
|
87
|
+
* The sub-query is compiled eagerly — later mutations will not be reflected.
|
|
88
|
+
*/
|
|
89
|
+
withRecursive(name: string, query: NativeQueryBuilder | RawQueryFragment, options?: CteOptions): this;
|
|
90
|
+
private addCte;
|
|
68
91
|
toString(): string;
|
|
69
92
|
compile(): {
|
|
70
93
|
sql: string;
|
|
@@ -85,7 +108,7 @@ export declare class NativeQueryBuilder implements Subquery {
|
|
|
85
108
|
distinct(): this;
|
|
86
109
|
distinctOn(fields: string[]): this;
|
|
87
110
|
onConflict(options: OnConflictClause): OnConflictClause;
|
|
88
|
-
returning(fields: (string | RawQueryFragment)[]): this;
|
|
111
|
+
returning(fields: (string | RawQueryFragment | [name: string, type: unknown])[]): this;
|
|
89
112
|
lockMode(lockMode: LockMode, lockTables?: string[]): this;
|
|
90
113
|
comment(comment: string | string[]): this;
|
|
91
114
|
hintComment(comment: string | string[]): this;
|
|
@@ -103,6 +126,8 @@ export declare class NativeQueryBuilder implements Subquery {
|
|
|
103
126
|
protected compileDelete(): void;
|
|
104
127
|
protected compileTruncate(): void;
|
|
105
128
|
protected addHintComment(): void;
|
|
129
|
+
protected compileCtes(): void;
|
|
130
|
+
protected getCteKeyword(hasRecursive: boolean): string;
|
|
106
131
|
protected getTableName(): string;
|
|
107
132
|
protected quote(id: string | RawQueryFragment | NativeQueryBuilder): string;
|
|
108
133
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LockMode, raw, RawQueryFragment, Utils } from '@mikro-orm/core';
|
|
1
|
+
import { LockMode, raw, RawQueryFragment, Utils, } from '@mikro-orm/core';
|
|
2
2
|
import { QueryType } from './enums.js';
|
|
3
3
|
/** @internal */
|
|
4
4
|
export class NativeQueryBuilder {
|
|
@@ -30,7 +30,8 @@ export class NativeQueryBuilder {
|
|
|
30
30
|
tableName = tableName.toRaw();
|
|
31
31
|
}
|
|
32
32
|
if (typeof tableName === 'string') {
|
|
33
|
-
const
|
|
33
|
+
const asKeyword = this.platform.usesAsKeyword() ? ' as ' : ' ';
|
|
34
|
+
const alias = options?.alias ? `${asKeyword}${this.platform.quoteIdentifier(options.alias)}` : '';
|
|
34
35
|
const schema = options?.schema && options.schema !== this.platform.getDefaultSchemaName() ? `${options.schema}.` : '';
|
|
35
36
|
tableName = this.quote(schema + tableName) + alias;
|
|
36
37
|
}
|
|
@@ -59,6 +60,36 @@ export class NativeQueryBuilder {
|
|
|
59
60
|
this.options.orderBy = orderBy;
|
|
60
61
|
return this;
|
|
61
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* The sub-query is compiled eagerly at call time — later mutations to the
|
|
65
|
+
* sub-query builder will not be reflected in this CTE.
|
|
66
|
+
*/
|
|
67
|
+
with(name, query, options) {
|
|
68
|
+
return this.addCte(name, query, options);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Adds a recursive CTE (`WITH RECURSIVE` on PostgreSQL/MySQL/SQLite, plain `WITH` on MSSQL).
|
|
72
|
+
* The sub-query is compiled eagerly — later mutations will not be reflected.
|
|
73
|
+
*/
|
|
74
|
+
withRecursive(name, query, options) {
|
|
75
|
+
return this.addCte(name, query, options, true);
|
|
76
|
+
}
|
|
77
|
+
addCte(name, query, options, recursive) {
|
|
78
|
+
this.options.ctes ??= [];
|
|
79
|
+
if (this.options.ctes.some(cte => cte.name === name)) {
|
|
80
|
+
throw new Error(`CTE with name '${name}' already exists`);
|
|
81
|
+
}
|
|
82
|
+
const { sql, params } = query instanceof NativeQueryBuilder ? query.compile() : { sql: query.sql, params: [...query.params] };
|
|
83
|
+
this.options.ctes.push({
|
|
84
|
+
name,
|
|
85
|
+
sql,
|
|
86
|
+
params,
|
|
87
|
+
recursive,
|
|
88
|
+
columns: options?.columns,
|
|
89
|
+
materialized: options?.materialized,
|
|
90
|
+
});
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
62
93
|
toString() {
|
|
63
94
|
const { sql, params } = this.compile();
|
|
64
95
|
return this.platform.formatQuery(sql, params);
|
|
@@ -72,6 +103,7 @@ export class NativeQueryBuilder {
|
|
|
72
103
|
if (this.options.comment) {
|
|
73
104
|
this.parts.push(...this.options.comment.map(comment => `/* ${comment} */`));
|
|
74
105
|
}
|
|
106
|
+
this.compileCtes();
|
|
75
107
|
switch (this.type) {
|
|
76
108
|
case QueryType.SELECT:
|
|
77
109
|
case QueryType.COUNT:
|
|
@@ -387,6 +419,35 @@ export class NativeQueryBuilder {
|
|
|
387
419
|
this.parts.push(`/*+ ${this.options.hintComment.join(' ')} */`);
|
|
388
420
|
}
|
|
389
421
|
}
|
|
422
|
+
compileCtes() {
|
|
423
|
+
const ctes = this.options.ctes;
|
|
424
|
+
if (!ctes || ctes.length === 0) {
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
const hasRecursive = ctes.some(cte => cte.recursive);
|
|
428
|
+
const keyword = this.getCteKeyword(hasRecursive);
|
|
429
|
+
const cteParts = [];
|
|
430
|
+
for (const cte of ctes) {
|
|
431
|
+
let part = this.quote(cte.name);
|
|
432
|
+
if (cte.columns?.length) {
|
|
433
|
+
part += ` (${cte.columns.map(c => this.quote(c)).join(', ')})`;
|
|
434
|
+
}
|
|
435
|
+
part += ' as';
|
|
436
|
+
if (cte.materialized === true) {
|
|
437
|
+
part += ' materialized';
|
|
438
|
+
}
|
|
439
|
+
else if (cte.materialized === false) {
|
|
440
|
+
part += ' not materialized';
|
|
441
|
+
}
|
|
442
|
+
part += ` (${cte.sql})`;
|
|
443
|
+
this.params.push(...cte.params);
|
|
444
|
+
cteParts.push(part);
|
|
445
|
+
}
|
|
446
|
+
this.parts.push(`${keyword} ${cteParts.join(', ')}`);
|
|
447
|
+
}
|
|
448
|
+
getCteKeyword(hasRecursive) {
|
|
449
|
+
return hasRecursive ? 'with recursive' : 'with';
|
|
450
|
+
}
|
|
390
451
|
getTableName() {
|
|
391
452
|
if (!this.options.tableName) {
|
|
392
453
|
throw new Error('No table name provided');
|
|
@@ -414,7 +475,8 @@ export class NativeQueryBuilder {
|
|
|
414
475
|
const parts = id.split(/ as /i);
|
|
415
476
|
const a = this.platform.quoteIdentifier(parts[0]);
|
|
416
477
|
const b = this.platform.quoteIdentifier(parts[1]);
|
|
417
|
-
|
|
478
|
+
const asKeyword = this.platform.usesAsKeyword() ? ' as ' : ' ';
|
|
479
|
+
return `${a}${asKeyword}${b}`;
|
|
418
480
|
}
|
|
419
481
|
if (id === '*') {
|
|
420
482
|
return id;
|
|
@@ -7,7 +7,8 @@ const COLLECTION_OPERATORS = ['$some', '$none', '$every', '$size'];
|
|
|
7
7
|
*/
|
|
8
8
|
export class ObjectCriteriaNode extends CriteriaNode {
|
|
9
9
|
process(qb, options) {
|
|
10
|
-
const matchPopulateJoins = options?.matchPopulateJoins ||
|
|
10
|
+
const matchPopulateJoins = options?.matchPopulateJoins ||
|
|
11
|
+
(this.prop && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(this.prop.kind));
|
|
11
12
|
const nestedAlias = qb.getAliasForJoinPath(this.getPath(options), { ...options, matchPopulateJoins });
|
|
12
13
|
const ownerAlias = options?.alias || qb.alias;
|
|
13
14
|
const keys = Utils.getObjectQueryKeys(this.payload);
|
|
@@ -20,13 +21,15 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
20
21
|
if (![ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(this.prop.kind)) {
|
|
21
22
|
// ignore collection operators when used on a non-relational property - this can happen when they get into
|
|
22
23
|
// populateWhere via `infer` on m:n properties with select-in strategy
|
|
23
|
-
if (this.parent?.parent) {
|
|
24
|
+
if (this.parent?.parent) {
|
|
25
|
+
// we validate only usage on top level
|
|
24
26
|
return {};
|
|
25
27
|
}
|
|
26
28
|
throw new Error(`Collection operators can be used only inside a collection property context, but it was used for ${this.getPath()}.`);
|
|
27
29
|
}
|
|
28
30
|
const $and = [];
|
|
29
|
-
const knownKey = [ReferenceKind.SCALAR, ReferenceKind.MANY_TO_ONE, ReferenceKind.EMBEDDED].includes(this.prop.kind) ||
|
|
31
|
+
const knownKey = [ReferenceKind.SCALAR, ReferenceKind.MANY_TO_ONE, ReferenceKind.EMBEDDED].includes(this.prop.kind) ||
|
|
32
|
+
(this.prop.kind === ReferenceKind.ONE_TO_ONE && this.prop.owner);
|
|
30
33
|
const parentMeta = this.metadata.find(this.parent.entityName);
|
|
31
34
|
const primaryKeys = parentMeta.primaryKeys.map(pk => {
|
|
32
35
|
return [QueryType.SELECT, QueryType.COUNT].includes(qb.type) ? `${knownKey ? alias : ownerAlias}.${pk}` : pk;
|
|
@@ -36,7 +39,7 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
36
39
|
throw new Error('Mixing collection operators with other filters is not allowed.');
|
|
37
40
|
}
|
|
38
41
|
const payload = this.payload[key].unwrap();
|
|
39
|
-
const qb2 = qb.clone(true, ['
|
|
42
|
+
const qb2 = qb.clone(true, ['schema']);
|
|
40
43
|
const joinAlias = qb2.getNextAlias(this.prop.targetMeta.class);
|
|
41
44
|
const sub = qb2
|
|
42
45
|
.from(parentMeta.class)
|
|
@@ -48,7 +51,9 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
48
51
|
const pks = this.prop.referencedColumnNames;
|
|
49
52
|
const countExpr = raw(`count(${pks.map(() => '??').join(', ')})`, pks.map(pk => `${joinAlias}.${pk}`));
|
|
50
53
|
sub.groupBy(parentMeta.primaryKeys);
|
|
51
|
-
sub.having({
|
|
54
|
+
sub.having({
|
|
55
|
+
$and: Object.keys(sizeCondition).map(op => ({ [countExpr]: { [op]: sizeCondition[op] } })),
|
|
56
|
+
});
|
|
52
57
|
}
|
|
53
58
|
else if (key === '$every') {
|
|
54
59
|
sub.where({ $not: { [this.key]: payload } });
|
|
@@ -111,7 +116,11 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
111
116
|
// use '??' placeholder to properly quote the identifier
|
|
112
117
|
o[raw('??', [field])] = payload;
|
|
113
118
|
}
|
|
114
|
-
else if (primaryKey ||
|
|
119
|
+
else if (primaryKey ||
|
|
120
|
+
virtual ||
|
|
121
|
+
operator ||
|
|
122
|
+
field.includes('.') ||
|
|
123
|
+
![QueryType.SELECT, QueryType.COUNT].includes(qb.type)) {
|
|
115
124
|
this.inlineCondition(field.replaceAll(ALIAS_REPLACEMENT, alias), o, payload);
|
|
116
125
|
}
|
|
117
126
|
else {
|
|
@@ -121,9 +130,10 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
121
130
|
}, {});
|
|
122
131
|
}
|
|
123
132
|
isStrict() {
|
|
124
|
-
return this.strict ||
|
|
125
|
-
|
|
126
|
-
|
|
133
|
+
return (this.strict ||
|
|
134
|
+
Utils.getObjectQueryKeys(this.payload).some(key => {
|
|
135
|
+
return this.payload[key].isStrict();
|
|
136
|
+
}));
|
|
127
137
|
}
|
|
128
138
|
unwrap() {
|
|
129
139
|
return Utils.getObjectQueryKeys(this.payload).reduce((o, field) => {
|
|
@@ -148,8 +158,18 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
148
158
|
}
|
|
149
159
|
shouldInline(payload) {
|
|
150
160
|
const rawField = RawQueryFragment.isKnownFragmentSymbol(this.key);
|
|
151
|
-
const scalar = Utils.isPrimaryKey(payload) ||
|
|
152
|
-
|
|
161
|
+
const scalar = Utils.isPrimaryKey(payload) ||
|
|
162
|
+
payload instanceof RegExp ||
|
|
163
|
+
payload instanceof Date ||
|
|
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
|
+
});
|
|
153
173
|
return !!this.prop && this.prop.kind !== ReferenceKind.SCALAR && !scalar && !operator;
|
|
154
174
|
}
|
|
155
175
|
getChildKey(k, prop, childAlias, alias) {
|
|
@@ -161,7 +181,9 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
161
181
|
inlineArrayChildPayload(obj, payload, k, prop, childAlias, alias) {
|
|
162
182
|
const key = this.getChildKey(k, prop, childAlias);
|
|
163
183
|
const value = payload.map((child) => Utils.getObjectQueryKeys(child).reduce((inner, childKey) => {
|
|
164
|
-
const key =
|
|
184
|
+
const key = RawQueryFragment.isKnownFragmentSymbol(childKey) || this.isPrefixed(childKey) || Utils.isOperator(childKey)
|
|
185
|
+
? childKey
|
|
186
|
+
: this.aliased(childKey, childAlias);
|
|
165
187
|
inner[key] = child[childKey];
|
|
166
188
|
return inner;
|
|
167
189
|
}, {}));
|
|
@@ -173,6 +195,12 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
173
195
|
if (RawQueryFragment.isKnownFragmentSymbol(k)) {
|
|
174
196
|
o[k] = payload[k];
|
|
175
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
|
+
}
|
|
176
204
|
else if (Utils.isOperator(k, false)) {
|
|
177
205
|
const tmp = payload[k];
|
|
178
206
|
delete payload[k];
|
|
@@ -218,24 +246,42 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
218
246
|
}
|
|
219
247
|
const meta = this.metadata.find(this.entityName);
|
|
220
248
|
const embeddable = this.prop.kind === ReferenceKind.EMBEDDED;
|
|
221
|
-
const knownKey = [ReferenceKind.SCALAR, ReferenceKind.MANY_TO_ONE, ReferenceKind.EMBEDDED].includes(this.prop.kind) ||
|
|
222
|
-
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
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
|
+
});
|
|
232
274
|
return !primaryKeys && !nestedAlias && !operatorKeys && !embeddable;
|
|
233
275
|
}
|
|
234
276
|
autoJoin(qb, alias, options) {
|
|
235
277
|
const nestedAlias = qb.getNextAlias(this.prop?.pivotEntity ?? this.entityName);
|
|
236
278
|
const rawField = RawQueryFragment.isKnownFragmentSymbol(this.key);
|
|
237
|
-
const scalar = Utils.isPrimaryKey(this.payload) ||
|
|
238
|
-
|
|
279
|
+
const scalar = Utils.isPrimaryKey(this.payload) ||
|
|
280
|
+
this.payload instanceof RegExp ||
|
|
281
|
+
this.payload instanceof Date ||
|
|
282
|
+
rawField;
|
|
283
|
+
const operator = Utils.isPlainObject(this.payload) &&
|
|
284
|
+
Utils.getObjectQueryKeys(this.payload).every(k => Utils.isOperator(k, false));
|
|
239
285
|
const field = `${alias}.${this.prop.name}`;
|
|
240
286
|
const method = qb.hasFlag(QueryFlag.INFER_POPULATE) ? 'joinAndSelect' : 'join';
|
|
241
287
|
const path = this.getPath();
|
|
@@ -243,14 +289,12 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
243
289
|
qb.join(field, nestedAlias, undefined, JoinType.pivotJoin, path);
|
|
244
290
|
}
|
|
245
291
|
else {
|
|
246
|
-
const prev = qb.
|
|
292
|
+
const prev = qb.state.fields?.slice();
|
|
247
293
|
const toOneProperty = [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(this.prop.kind);
|
|
248
|
-
const joinType = toOneProperty && !this.prop.nullable
|
|
249
|
-
? JoinType.innerJoin
|
|
250
|
-
: JoinType.leftJoin;
|
|
294
|
+
const joinType = toOneProperty && !this.prop.nullable ? JoinType.innerJoin : JoinType.leftJoin;
|
|
251
295
|
qb[method](field, nestedAlias, undefined, joinType, path);
|
|
252
296
|
if (!qb.hasFlag(QueryFlag.INFER_POPULATE)) {
|
|
253
|
-
qb.
|
|
297
|
+
qb.state.fields = prev;
|
|
254
298
|
}
|
|
255
299
|
}
|
|
256
300
|
if (options?.type !== 'orderBy') {
|
|
@@ -259,6 +303,6 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
259
303
|
return nestedAlias;
|
|
260
304
|
}
|
|
261
305
|
isPrefixed(field) {
|
|
262
|
-
return
|
|
306
|
+
return !!/\w+\./.exec(field);
|
|
263
307
|
}
|
|
264
308
|
}
|