@mikro-orm/knex 6.0.0-dev.2 → 6.0.0-dev.20
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/{dist/AbstractSqlConnection.d.ts → AbstractSqlConnection.d.ts} +0 -0
- package/{dist/AbstractSqlConnection.js → AbstractSqlConnection.js} +5 -0
- package/AbstractSqlDriver.d.ts +66 -0
- package/{dist/AbstractSqlDriver.js → AbstractSqlDriver.js} +230 -71
- package/{dist/AbstractSqlPlatform.d.ts → AbstractSqlPlatform.d.ts} +7 -7
- package/{dist/AbstractSqlPlatform.js → AbstractSqlPlatform.js} +12 -14
- package/{dist/MonkeyPatchable.d.ts → MonkeyPatchable.d.ts} +0 -0
- package/{dist/MonkeyPatchable.js → MonkeyPatchable.js} +0 -0
- package/{dist/README.md → README.md} +2 -2
- package/{dist/SqlEntityManager.d.ts → SqlEntityManager.d.ts} +5 -5
- package/{dist/SqlEntityManager.js → SqlEntityManager.js} +1 -2
- package/{dist/SqlEntityRepository.d.ts → SqlEntityRepository.d.ts} +3 -3
- package/{dist/SqlEntityRepository.js → SqlEntityRepository.js} +1 -1
- package/{dist/index.d.ts → index.d.ts} +1 -0
- package/{dist/index.js → index.js} +6 -1
- package/index.mjs +192 -0
- package/package.json +18 -47
- package/{dist/query → query}/ArrayCriteriaNode.d.ts +0 -0
- package/{dist/query → query}/ArrayCriteriaNode.js +0 -0
- package/{dist/query → query}/CriteriaNode.d.ts +0 -0
- package/{dist/query → query}/CriteriaNode.js +7 -4
- package/{dist/query → query}/CriteriaNodeFactory.d.ts +0 -0
- package/{dist/query → query}/CriteriaNodeFactory.js +0 -0
- package/{dist/query → query}/ObjectCriteriaNode.d.ts +0 -0
- package/{dist/query → query}/ObjectCriteriaNode.js +14 -1
- package/{dist/query → query}/QueryBuilder.d.ts +65 -24
- package/{dist/query → query}/QueryBuilder.js +318 -84
- package/{dist/query → query}/QueryBuilderHelper.d.ts +19 -11
- package/{dist/query → query}/QueryBuilderHelper.js +224 -113
- package/{dist/query → query}/ScalarCriteriaNode.d.ts +0 -0
- package/{dist/query → query}/ScalarCriteriaNode.js +0 -0
- package/{dist/query → query}/enums.d.ts +0 -0
- package/{dist/query → query}/enums.js +0 -0
- package/{dist/query → query}/index.d.ts +0 -0
- package/{dist/query → query}/index.js +5 -1
- package/{dist/schema → schema}/DatabaseSchema.d.ts +1 -1
- package/{dist/schema → schema}/DatabaseSchema.js +8 -16
- package/{dist/schema → schema}/DatabaseTable.d.ts +8 -8
- package/{dist/schema → schema}/DatabaseTable.js +2 -1
- package/{dist/schema → schema}/SchemaComparator.d.ts +7 -5
- package/{dist/schema → schema}/SchemaComparator.js +60 -10
- package/{dist/schema → schema}/SchemaHelper.d.ts +15 -10
- package/{dist/schema → schema}/SchemaHelper.js +46 -8
- package/{dist/schema/SchemaGenerator.d.ts → schema/SqlSchemaGenerator.d.ts} +12 -12
- package/{dist/schema/SchemaGenerator.js → schema/SqlSchemaGenerator.js} +85 -34
- package/{dist/schema → schema}/index.d.ts +1 -1
- package/{dist/schema → schema}/index.js +6 -2
- package/{dist/typings.d.ts → typings.d.ts} +16 -12
- package/{dist/typings.js → typings.js} +0 -0
- package/CHANGELOG.md +0 -661
- package/dist/AbstractSqlDriver.d.ts +0 -55
- package/dist/LICENSE +0 -21
- package/dist/index.mjs +0 -26
- package/dist/package.json +0 -99
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { Knex } from 'knex';
|
|
2
|
-
import type { Dictionary, EntityData, EntityMetadata, EntityProperty, FlatQueryOrderMap,
|
|
2
|
+
import type { Dictionary, EntityData, EntityMetadata, EntityProperty, FlatQueryOrderMap, QBFilterQuery } from '@mikro-orm/core';
|
|
3
3
|
import { LockMode } from '@mikro-orm/core';
|
|
4
4
|
import { QueryType } from './enums';
|
|
5
5
|
import type { Field, JoinOptions } from '../typings';
|
|
6
|
+
import type { AbstractSqlDriver } from '../AbstractSqlDriver';
|
|
6
7
|
/**
|
|
7
8
|
* @internal
|
|
8
9
|
*/
|
|
@@ -11,25 +12,28 @@ export declare class QueryBuilderHelper {
|
|
|
11
12
|
private readonly alias;
|
|
12
13
|
private readonly aliasMap;
|
|
13
14
|
private readonly subQueries;
|
|
14
|
-
private readonly metadata;
|
|
15
15
|
private readonly knex;
|
|
16
|
+
private readonly driver;
|
|
16
17
|
private readonly platform;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
mapper(field: string, type?: QueryType
|
|
18
|
+
private readonly metadata;
|
|
19
|
+
constructor(entityName: string, alias: string, aliasMap: Dictionary<Alias>, subQueries: Dictionary<string>, knex: Knex, driver: AbstractSqlDriver);
|
|
20
|
+
mapper(field: string | Knex.Raw, type?: QueryType): string;
|
|
21
|
+
mapper(field: string | Knex.Raw, type?: QueryType, value?: any, alias?: string | null): string;
|
|
20
22
|
processData(data: Dictionary, convertCustomTypes: boolean, multi?: boolean): any;
|
|
21
23
|
joinOneToReference(prop: EntityProperty, ownerAlias: string, alias: string, type: 'leftJoin' | 'innerJoin' | 'pivotJoin', cond?: Dictionary): JoinOptions;
|
|
22
24
|
joinManyToOneReference(prop: EntityProperty, ownerAlias: string, alias: string, type: 'leftJoin' | 'innerJoin' | 'pivotJoin', cond?: Dictionary): JoinOptions;
|
|
23
25
|
joinManyToManyReference(prop: EntityProperty, ownerAlias: string, alias: string, pivotAlias: string, type: 'leftJoin' | 'innerJoin' | 'pivotJoin', cond: Dictionary, path: string): Dictionary<JoinOptions>;
|
|
24
26
|
joinPivotTable(field: string, prop: EntityProperty, ownerAlias: string, alias: string, type: 'leftJoin' | 'innerJoin' | 'pivotJoin', cond?: Dictionary): JoinOptions;
|
|
25
27
|
processJoins(qb: Knex.QueryBuilder, joins: Dictionary<JoinOptions>, schema?: string): void;
|
|
28
|
+
private processJoinClause;
|
|
29
|
+
private wrapQueryGroup;
|
|
26
30
|
mapJoinColumns(type: QueryType, join: JoinOptions): (string | Knex.Raw)[];
|
|
27
31
|
isOneToOneInverse(field: string): boolean;
|
|
28
32
|
getTableName(entityName: string): string;
|
|
29
33
|
/**
|
|
30
34
|
* Checks whether the RE can be rewritten to simple LIKE query
|
|
31
35
|
*/
|
|
32
|
-
isSimpleRegExp(re: any):
|
|
36
|
+
isSimpleRegExp(re: any): re is RegExp;
|
|
33
37
|
getRegExpParam(re: RegExp): string;
|
|
34
38
|
appendOnConflictClause<T>(type: QueryType, onConflict: {
|
|
35
39
|
fields: string[];
|
|
@@ -42,13 +46,10 @@ export declare class QueryBuilderHelper {
|
|
|
42
46
|
private processCustomExpression;
|
|
43
47
|
private processObjectSubCondition;
|
|
44
48
|
private getOperatorReplacement;
|
|
45
|
-
private appendJoinClause;
|
|
46
|
-
private appendJoinSubClause;
|
|
47
|
-
private processObjectSubClause;
|
|
48
49
|
getQueryOrder(type: QueryType, orderBy: FlatQueryOrderMap | FlatQueryOrderMap[], populate: Dictionary<string>): string;
|
|
49
50
|
getQueryOrderFromObject(type: QueryType, orderBy: FlatQueryOrderMap, populate: Dictionary<string>): string;
|
|
50
51
|
finalize(type: QueryType, qb: Knex.QueryBuilder, meta?: EntityMetadata): void;
|
|
51
|
-
splitField(field: string): [string, string];
|
|
52
|
+
splitField(field: string, greedyAlias?: boolean): [string, string];
|
|
52
53
|
getLockSQL(qb: Knex.QueryBuilder, lockMode: LockMode, lockTables?: string[]): void;
|
|
53
54
|
updateVersionProperty(qb: Knex.QueryBuilder, data: Dictionary): void;
|
|
54
55
|
static isCustomExpression(field: string, hasAlias?: boolean): boolean;
|
|
@@ -57,5 +58,12 @@ export declare class QueryBuilderHelper {
|
|
|
57
58
|
private isPrefixed;
|
|
58
59
|
private fieldName;
|
|
59
60
|
getProperty(field: string, alias?: string): EntityProperty | undefined;
|
|
60
|
-
isTableNameAliasRequired(type
|
|
61
|
+
isTableNameAliasRequired(type?: QueryType): boolean;
|
|
62
|
+
private mapData;
|
|
63
|
+
}
|
|
64
|
+
export interface Alias {
|
|
65
|
+
aliasName: string;
|
|
66
|
+
entityName: string;
|
|
67
|
+
metadata?: EntityMetadata;
|
|
68
|
+
subQuery?: Knex.QueryBuilder;
|
|
61
69
|
}
|
|
@@ -8,16 +8,20 @@ const enums_1 = require("./enums");
|
|
|
8
8
|
* @internal
|
|
9
9
|
*/
|
|
10
10
|
class QueryBuilderHelper {
|
|
11
|
-
constructor(entityName, alias, aliasMap, subQueries,
|
|
11
|
+
constructor(entityName, alias, aliasMap, subQueries, knex, driver) {
|
|
12
12
|
this.entityName = entityName;
|
|
13
13
|
this.alias = alias;
|
|
14
14
|
this.aliasMap = aliasMap;
|
|
15
15
|
this.subQueries = subQueries;
|
|
16
|
-
this.metadata = metadata;
|
|
17
16
|
this.knex = knex;
|
|
18
|
-
this.
|
|
17
|
+
this.driver = driver;
|
|
18
|
+
this.platform = this.driver.getPlatform();
|
|
19
|
+
this.metadata = this.driver.getMetadata();
|
|
19
20
|
}
|
|
20
21
|
mapper(field, type = enums_1.QueryType.SELECT, value, alias) {
|
|
22
|
+
if (typeof field !== 'string') {
|
|
23
|
+
return field;
|
|
24
|
+
}
|
|
21
25
|
const isTableNameAliasRequired = this.isTableNameAliasRequired(type);
|
|
22
26
|
const fields = core_1.Utils.splitPrimaryKeys(field);
|
|
23
27
|
if (fields.length > 1) {
|
|
@@ -26,18 +30,33 @@ class QueryBuilderHelper {
|
|
|
26
30
|
const [a, f] = this.splitField(p);
|
|
27
31
|
const prop = this.getProperty(f, a);
|
|
28
32
|
if (prop) {
|
|
29
|
-
parts.push(...prop.fieldNames.map(f => this.mapper(f, type, value, alias)));
|
|
33
|
+
parts.push(...prop.fieldNames.map(f => this.mapper(a !== this.alias ? `${a}.${f}` : f, type, value, alias)));
|
|
30
34
|
}
|
|
31
35
|
else {
|
|
32
|
-
parts.push(this.mapper(`${a}.${f}
|
|
36
|
+
parts.push(this.mapper(a !== this.alias ? `${a}.${f}` : f, type, value, alias));
|
|
33
37
|
}
|
|
34
38
|
}
|
|
39
|
+
// flatten the value if we see we are expanding nested composite key
|
|
40
|
+
// hackish, but cleaner solution would require quite a lot of refactoring
|
|
41
|
+
if (fields.length !== parts.length && Array.isArray(value)) {
|
|
42
|
+
value.forEach(row => {
|
|
43
|
+
if (Array.isArray(row)) {
|
|
44
|
+
const tmp = core_1.Utils.flatten(row);
|
|
45
|
+
row.length = 0;
|
|
46
|
+
row.push(...tmp);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
35
50
|
return this.knex.raw('(' + parts.map(part => this.knex.ref(part)).join(', ') + ')');
|
|
36
51
|
}
|
|
37
52
|
let ret = field;
|
|
38
53
|
const customExpression = QueryBuilderHelper.isCustomExpression(field, !!alias);
|
|
39
54
|
const [a, f] = this.splitField(field);
|
|
40
55
|
const prop = this.getProperty(f, a);
|
|
56
|
+
// embeddable nested path instead of a regular property with table alias, reset alias
|
|
57
|
+
if (prop?.name === a && prop.embeddedProps[f]) {
|
|
58
|
+
return this.alias + '.' + prop.fieldNames[0];
|
|
59
|
+
}
|
|
41
60
|
const noPrefix = prop && prop.persist === false;
|
|
42
61
|
if (prop?.fieldNameRaw) {
|
|
43
62
|
return this.knex.raw(this.prefix(field, isTableNameAliasRequired));
|
|
@@ -64,7 +83,8 @@ class QueryBuilderHelper {
|
|
|
64
83
|
ret += ' as ' + alias;
|
|
65
84
|
}
|
|
66
85
|
if (customExpression) {
|
|
67
|
-
|
|
86
|
+
const bindings = core_1.Utils.asArray(value).slice(0, ret.match(/\?/g)?.length ?? 0);
|
|
87
|
+
return this.knex.raw(ret, bindings);
|
|
68
88
|
}
|
|
69
89
|
if (!isTableNameAliasRequired || this.isPrefixed(ret) || noPrefix) {
|
|
70
90
|
return ret;
|
|
@@ -75,47 +95,23 @@ class QueryBuilderHelper {
|
|
|
75
95
|
if (Array.isArray(data)) {
|
|
76
96
|
return data.map(d => this.processData(d, convertCustomTypes, true));
|
|
77
97
|
}
|
|
78
|
-
data = Object.assign({}, data); // copy first
|
|
79
98
|
const meta = this.metadata.find(this.entityName);
|
|
80
|
-
|
|
81
|
-
if (!meta?.properties[k]) {
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
const prop = meta.properties[k];
|
|
85
|
-
if (prop.joinColumns && Array.isArray(data[k])) {
|
|
86
|
-
const copy = data[k];
|
|
87
|
-
delete data[k];
|
|
88
|
-
prop.joinColumns.forEach((joinColumn, idx) => data[joinColumn] = copy[idx]);
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
if (prop.customType && convertCustomTypes && !this.platform.isRaw(data[k])) {
|
|
92
|
-
data[k] = prop.customType.convertToDatabaseValue(data[k], this.platform, true);
|
|
93
|
-
}
|
|
94
|
-
if (prop.customType && 'convertToDatabaseValueSQL' in prop.customType && !this.platform.isRaw(data[k])) {
|
|
95
|
-
const quoted = this.platform.quoteValue(data[k]);
|
|
96
|
-
data[k] = this.knex.raw(prop.customType.convertToDatabaseValueSQL(quoted, this.platform));
|
|
97
|
-
}
|
|
98
|
-
if (!prop.customType && (Array.isArray(data[k]) || core_1.Utils.isPlainObject(data[k]))) {
|
|
99
|
-
data[k] = JSON.stringify(data[k]);
|
|
100
|
-
}
|
|
101
|
-
if (prop.fieldNames) {
|
|
102
|
-
core_1.Utils.renameKey(data, k, prop.fieldNames[0]);
|
|
103
|
-
}
|
|
104
|
-
});
|
|
99
|
+
data = this.mapData(data, meta?.properties, convertCustomTypes);
|
|
105
100
|
if (!core_1.Utils.hasObjectKeys(data) && meta && multi) {
|
|
101
|
+
/* istanbul ignore next */
|
|
106
102
|
data[meta.primaryKeys[0]] = this.platform.usesDefaultKeyword() ? this.knex.raw('default') : undefined;
|
|
107
103
|
}
|
|
108
104
|
return data;
|
|
109
105
|
}
|
|
110
106
|
joinOneToReference(prop, ownerAlias, alias, type, cond = {}) {
|
|
111
|
-
const
|
|
112
|
-
const prop2 = meta.properties[prop.mappedBy || prop.inversedBy];
|
|
107
|
+
const prop2 = prop.targetMeta.properties[prop.mappedBy || prop.inversedBy];
|
|
113
108
|
const table = this.getTableName(prop.type);
|
|
109
|
+
const schema = this.driver.getSchemaName(prop.targetMeta);
|
|
114
110
|
const joinColumns = prop.owner ? prop.referencedColumnNames : prop2.joinColumns;
|
|
115
111
|
const inverseJoinColumns = prop.referencedColumnNames;
|
|
116
112
|
const primaryKeys = prop.owner ? prop.joinColumns : prop2.referencedColumnNames;
|
|
117
113
|
return {
|
|
118
|
-
prop, type, cond, ownerAlias, alias, table,
|
|
114
|
+
prop, type, cond, ownerAlias, alias, table, schema,
|
|
119
115
|
joinColumns, inverseJoinColumns, primaryKeys,
|
|
120
116
|
};
|
|
121
117
|
}
|
|
@@ -123,11 +119,13 @@ class QueryBuilderHelper {
|
|
|
123
119
|
return {
|
|
124
120
|
prop, type, cond, ownerAlias, alias,
|
|
125
121
|
table: this.getTableName(prop.type),
|
|
122
|
+
schema: this.driver.getSchemaName(prop.targetMeta),
|
|
126
123
|
joinColumns: prop.referencedColumnNames,
|
|
127
124
|
primaryKeys: prop.fieldNames,
|
|
128
125
|
};
|
|
129
126
|
}
|
|
130
127
|
joinManyToManyReference(prop, ownerAlias, alias, pivotAlias, type, cond, path) {
|
|
128
|
+
const pivotMeta = this.metadata.find(prop.pivotEntity);
|
|
131
129
|
const ret = {
|
|
132
130
|
[`${ownerAlias}.${prop.name}#${pivotAlias}`]: {
|
|
133
131
|
prop, type, cond, ownerAlias,
|
|
@@ -136,21 +134,22 @@ class QueryBuilderHelper {
|
|
|
136
134
|
joinColumns: prop.joinColumns,
|
|
137
135
|
inverseJoinColumns: prop.inverseJoinColumns,
|
|
138
136
|
primaryKeys: prop.referencedColumnNames,
|
|
139
|
-
table:
|
|
137
|
+
table: pivotMeta.tableName,
|
|
138
|
+
schema: this.driver.getSchemaName(pivotMeta),
|
|
140
139
|
path: path.endsWith('[pivot]') ? path : `${path}[pivot]`,
|
|
141
140
|
},
|
|
142
141
|
};
|
|
143
142
|
if (type === 'pivotJoin') {
|
|
144
143
|
return ret;
|
|
145
144
|
}
|
|
146
|
-
const prop2 =
|
|
145
|
+
const prop2 = prop.owner ? pivotMeta.relations[1] : pivotMeta.relations[0];
|
|
147
146
|
ret[`${pivotAlias}.${prop2.name}#${alias}`] = this.joinManyToOneReference(prop2, pivotAlias, alias, type);
|
|
148
147
|
ret[`${pivotAlias}.${prop2.name}#${alias}`].path = path;
|
|
149
148
|
return ret;
|
|
150
149
|
}
|
|
151
150
|
joinPivotTable(field, prop, ownerAlias, alias, type, cond = {}) {
|
|
152
151
|
const pivotMeta = this.metadata.find(field);
|
|
153
|
-
const prop2 = pivotMeta.
|
|
152
|
+
const prop2 = pivotMeta.relations[0] === prop ? pivotMeta.relations[1] : pivotMeta.relations[0];
|
|
154
153
|
return {
|
|
155
154
|
prop, type, cond, ownerAlias, alias,
|
|
156
155
|
table: pivotMeta.collection,
|
|
@@ -163,21 +162,96 @@ class QueryBuilderHelper {
|
|
|
163
162
|
processJoins(qb, joins, schema) {
|
|
164
163
|
Object.values(joins).forEach(join => {
|
|
165
164
|
let table = `${join.table} as ${join.alias}`;
|
|
166
|
-
const method = join.type === '
|
|
165
|
+
const method = join.type === 'innerJoin' ? 'inner join' : 'left join';
|
|
167
166
|
schema = join.schema && join.schema !== '*' ? join.schema : schema;
|
|
168
167
|
if (schema) {
|
|
169
168
|
table = `${schema}.${table}`;
|
|
170
169
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
});
|
|
177
|
-
this.appendJoinClause(inner, join.cond);
|
|
170
|
+
const conditions = [];
|
|
171
|
+
const params = [];
|
|
172
|
+
join.primaryKeys.forEach((primaryKey, idx) => {
|
|
173
|
+
const left = `${join.ownerAlias}.${primaryKey}`;
|
|
174
|
+
const right = `${join.alias}.${join.joinColumns[idx]}`;
|
|
175
|
+
conditions.push(`${this.knex.ref(left)} = ${this.knex.ref(right)}`);
|
|
178
176
|
});
|
|
177
|
+
Object.keys(join.cond).forEach(key => {
|
|
178
|
+
conditions.push(this.processJoinClause(key, join.cond[key], params));
|
|
179
|
+
});
|
|
180
|
+
return qb.joinRaw(`${method} ${this.knex.ref(table)} on ${conditions.join(' and ')}`, params);
|
|
179
181
|
});
|
|
180
182
|
}
|
|
183
|
+
processJoinClause(key, value, params, operator = '$eq') {
|
|
184
|
+
if (core_1.Utils.isGroupOperator(key) && Array.isArray(value)) {
|
|
185
|
+
const parts = value.map(sub => {
|
|
186
|
+
return this.wrapQueryGroup(Object.keys(sub).map(k => this.processJoinClause(k, sub[k], params)));
|
|
187
|
+
});
|
|
188
|
+
return this.wrapQueryGroup(parts, key);
|
|
189
|
+
}
|
|
190
|
+
if (this.isSimpleRegExp(value)) {
|
|
191
|
+
params.push(this.getRegExpParam(value));
|
|
192
|
+
return `${this.knex.ref(this.mapper(key))} like ?`;
|
|
193
|
+
}
|
|
194
|
+
if (value instanceof RegExp) {
|
|
195
|
+
value = this.platform.getRegExpValue(value);
|
|
196
|
+
}
|
|
197
|
+
if (core_1.Utils.isOperator(key, false) && core_1.Utils.isPlainObject(value)) {
|
|
198
|
+
const parts = Object.keys(value).map(k => this.processJoinClause(k, value[k], params, key));
|
|
199
|
+
return key === '$not' ? `not ${this.wrapQueryGroup(parts)}` : this.wrapQueryGroup(parts);
|
|
200
|
+
}
|
|
201
|
+
if (core_1.Utils.isPlainObject(value) && Object.keys(value).every(k => core_1.Utils.isOperator(k, false))) {
|
|
202
|
+
const parts = Object.keys(value).map(op => this.processJoinClause(key, value[op], params, op));
|
|
203
|
+
return this.wrapQueryGroup(parts);
|
|
204
|
+
}
|
|
205
|
+
operator = operator === '$not' ? '$eq' : operator;
|
|
206
|
+
if (value === null) {
|
|
207
|
+
return `${this.knex.ref(this.mapper(key))} is ${operator === '$ne' ? 'not ' : ''}null`;
|
|
208
|
+
}
|
|
209
|
+
if (operator === '$fulltext') {
|
|
210
|
+
const [fromAlias, fromField] = this.splitField(key);
|
|
211
|
+
const property = this.getProperty(fromField, fromAlias);
|
|
212
|
+
const query = this.knex.raw(this.platform.getFullTextWhereClause(property), {
|
|
213
|
+
column: this.mapper(key),
|
|
214
|
+
query: this.knex.raw('?'),
|
|
215
|
+
}).toSQL().toNative();
|
|
216
|
+
params.push(value);
|
|
217
|
+
return query.sql;
|
|
218
|
+
}
|
|
219
|
+
const replacement = this.getOperatorReplacement(operator, { [operator]: value });
|
|
220
|
+
if (['$in', '$nin'].includes(operator) && Array.isArray(value)) {
|
|
221
|
+
params.push(...value);
|
|
222
|
+
return `${this.knex.ref(this.mapper(key))} ${replacement} (${value.map(() => '?').join(', ')})`;
|
|
223
|
+
}
|
|
224
|
+
if (operator === '$exists') {
|
|
225
|
+
value = null;
|
|
226
|
+
}
|
|
227
|
+
if (QueryBuilderHelper.isCustomExpression(key)) {
|
|
228
|
+
// unwind parameters when ? found in field name
|
|
229
|
+
const count = key.concat('?').match(/\?/g).length - 1;
|
|
230
|
+
const values = core_1.Utils.asArray(value);
|
|
231
|
+
const params1 = values.slice(0, count).map((c) => core_1.Utils.isObject(c) ? JSON.stringify(c) : c);
|
|
232
|
+
const params2 = values.slice(count);
|
|
233
|
+
const k = this.mapper(key, enums_1.QueryType.SELECT, params1);
|
|
234
|
+
const { sql, bindings } = k.toSQL().toNative();
|
|
235
|
+
if (params2.length > 0) {
|
|
236
|
+
params.push(...bindings);
|
|
237
|
+
params.push(...params2);
|
|
238
|
+
return sql + ' = ?';
|
|
239
|
+
}
|
|
240
|
+
params.push(...bindings);
|
|
241
|
+
return sql;
|
|
242
|
+
}
|
|
243
|
+
const sql = this.mapper(key);
|
|
244
|
+
if (value !== null) {
|
|
245
|
+
params.push(value);
|
|
246
|
+
}
|
|
247
|
+
return `${this.knex.ref(sql)} ${replacement} ${value === null ? 'null' : '?'}`;
|
|
248
|
+
}
|
|
249
|
+
wrapQueryGroup(parts, operator = '$and') {
|
|
250
|
+
if (parts.length === 1) {
|
|
251
|
+
return parts[0];
|
|
252
|
+
}
|
|
253
|
+
return `(${parts.join(` ${core_1.GroupOperator[operator]} `)})`;
|
|
254
|
+
}
|
|
181
255
|
mapJoinColumns(type, join) {
|
|
182
256
|
if (join.prop && join.prop.reference === core_1.ReferenceType.ONE_TO_ONE && !join.prop.owner) {
|
|
183
257
|
return join.prop.fieldNames.map((fieldName, idx) => {
|
|
@@ -205,6 +279,9 @@ class QueryBuilderHelper {
|
|
|
205
279
|
if (!(re instanceof RegExp)) {
|
|
206
280
|
return false;
|
|
207
281
|
}
|
|
282
|
+
if (re.flags.includes('i')) {
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
208
285
|
// when including the opening bracket/paren we consider it complex
|
|
209
286
|
return !re.source.match(/[{[(]/);
|
|
210
287
|
}
|
|
@@ -280,7 +357,7 @@ class QueryBuilderHelper {
|
|
|
280
357
|
}
|
|
281
358
|
qb[m](this.mapper(key, type, cond[key], null), op, cond[key]);
|
|
282
359
|
}
|
|
283
|
-
processCustomExpression(clause, m, key, cond, type
|
|
360
|
+
processCustomExpression(clause, m, key, cond, type) {
|
|
284
361
|
// unwind parameters when ? found in field name
|
|
285
362
|
const count = key.concat('?').match(/\?/g).length - 1;
|
|
286
363
|
const value = core_1.Utils.asArray(cond[key]);
|
|
@@ -301,7 +378,7 @@ class QueryBuilderHelper {
|
|
|
301
378
|
return subCondition.forEach(sub => this.appendQueryCondition(type, sub, qb, '$and', method));
|
|
302
379
|
}
|
|
303
380
|
if (value instanceof RegExp) {
|
|
304
|
-
value =
|
|
381
|
+
value = this.platform.getRegExpValue(value);
|
|
305
382
|
}
|
|
306
383
|
// operators
|
|
307
384
|
const op = Object.keys(core_1.QueryOperator).find(op => op in value);
|
|
@@ -311,68 +388,42 @@ class QueryBuilderHelper {
|
|
|
311
388
|
const replacement = this.getOperatorReplacement(op, value);
|
|
312
389
|
const fields = core_1.Utils.splitPrimaryKeys(key);
|
|
313
390
|
if (fields.length > 1 && Array.isArray(value[op]) && !value[op].every((v) => Array.isArray(v))) {
|
|
314
|
-
|
|
315
|
-
value[op] = this.knex.raw(`${values}(${fields.map(() => '?').join(', ')})`, value[op]);
|
|
391
|
+
value[op] = this.knex.raw(`(${fields.map(() => '?').join(', ')})`, value[op]);
|
|
316
392
|
}
|
|
317
393
|
if (this.subQueries[key]) {
|
|
318
394
|
return void qb[m](this.knex.raw(`(${this.subQueries[key]})`), replacement, value[op]);
|
|
319
395
|
}
|
|
320
|
-
|
|
396
|
+
if (op === '$fulltext') {
|
|
397
|
+
const [a, f] = this.splitField(key);
|
|
398
|
+
const prop = this.getProperty(f, a);
|
|
399
|
+
/* istanbul ignore next */
|
|
400
|
+
if (!prop) {
|
|
401
|
+
throw new Error(`Cannot use $fulltext operator on ${key}, property not found`);
|
|
402
|
+
}
|
|
403
|
+
qb[m](this.knex.raw(this.platform.getFullTextWhereClause(prop), {
|
|
404
|
+
column: this.mapper(key, type, undefined, null),
|
|
405
|
+
query: value[op],
|
|
406
|
+
}));
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
const mappedKey = this.mapper(key, type, value[op], null);
|
|
410
|
+
qb[m](mappedKey, replacement, value[op]);
|
|
411
|
+
}
|
|
321
412
|
}
|
|
322
413
|
getOperatorReplacement(op, value) {
|
|
323
414
|
let replacement = core_1.QueryOperator[op];
|
|
415
|
+
if (op === '$exists') {
|
|
416
|
+
replacement = value[op] ? 'is not' : 'is';
|
|
417
|
+
value[op] = null;
|
|
418
|
+
}
|
|
324
419
|
if (value[op] === null && ['$eq', '$ne'].includes(op)) {
|
|
325
420
|
replacement = op === '$eq' ? 'is' : 'is not';
|
|
326
421
|
}
|
|
327
422
|
if (op === '$re') {
|
|
328
|
-
replacement = this.platform.getRegExpOperator();
|
|
423
|
+
replacement = this.platform.getRegExpOperator(value[op], value.$flags);
|
|
329
424
|
}
|
|
330
425
|
return replacement;
|
|
331
426
|
}
|
|
332
|
-
appendJoinClause(clause, cond, operator) {
|
|
333
|
-
Object.keys(cond).forEach(k => {
|
|
334
|
-
if (k === '$and' || k === '$or') {
|
|
335
|
-
const method = operator === '$or' ? 'orOn' : 'andOn';
|
|
336
|
-
const m = k === '$or' ? 'orOn' : 'andOn';
|
|
337
|
-
return clause[method](outer => cond[k].forEach((sub) => {
|
|
338
|
-
if (core_1.Utils.getObjectKeysSize(sub) === 1) {
|
|
339
|
-
return this.appendJoinClause(outer, sub, k);
|
|
340
|
-
}
|
|
341
|
-
outer[m](inner => this.appendJoinClause(inner, sub, '$and'));
|
|
342
|
-
}));
|
|
343
|
-
}
|
|
344
|
-
this.appendJoinSubClause(clause, cond, k, operator);
|
|
345
|
-
});
|
|
346
|
-
}
|
|
347
|
-
appendJoinSubClause(clause, cond, key, operator) {
|
|
348
|
-
const m = operator === '$or' ? 'orOn' : 'andOn';
|
|
349
|
-
if (cond[key] instanceof RegExp) {
|
|
350
|
-
return void clause[m](this.mapper(key), 'like', this.knex.raw('?', this.getRegExpParam(cond[key])));
|
|
351
|
-
}
|
|
352
|
-
if (core_1.Utils.isPlainObject(cond[key])) {
|
|
353
|
-
return this.processObjectSubClause(cond, key, clause, m);
|
|
354
|
-
}
|
|
355
|
-
if (QueryBuilderHelper.isCustomExpression(key)) {
|
|
356
|
-
return this.processCustomExpression(clause, m, key, cond);
|
|
357
|
-
}
|
|
358
|
-
const op = cond[key] === null ? 'is' : '=';
|
|
359
|
-
clause[m](this.knex.raw(`${this.knex.ref(this.mapper(key, enums_1.QueryType.SELECT, cond[key]))} ${op} ?`, cond[key]));
|
|
360
|
-
}
|
|
361
|
-
processObjectSubClause(cond, key, clause, m) {
|
|
362
|
-
// grouped condition for one field
|
|
363
|
-
if (core_1.Utils.getObjectKeysSize(cond[key]) > 1) {
|
|
364
|
-
const subCondition = Object.entries(cond[key]).map(([subKey, subValue]) => ({ [key]: { [subKey]: subValue } }));
|
|
365
|
-
return void clause[m](inner => subCondition.map(sub => this.appendJoinClause(inner, sub, '$and')));
|
|
366
|
-
}
|
|
367
|
-
// operators
|
|
368
|
-
for (const [op, replacement] of Object.entries(core_1.QueryOperator)) {
|
|
369
|
-
if (!(op in cond[key])) {
|
|
370
|
-
continue;
|
|
371
|
-
}
|
|
372
|
-
clause[m](this.mapper(key), replacement, this.knex.raw('?', cond[key][op]));
|
|
373
|
-
break;
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
427
|
getQueryOrder(type, orderBy, populate) {
|
|
377
428
|
if (Array.isArray(orderBy)) {
|
|
378
429
|
return orderBy
|
|
@@ -391,13 +442,13 @@ class QueryBuilderHelper {
|
|
|
391
442
|
ret.push(`${k} ${order.toLowerCase()}`);
|
|
392
443
|
return;
|
|
393
444
|
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
const prop = this.getProperty(
|
|
399
|
-
const noPrefix = (prop && prop.persist === false) || QueryBuilderHelper.isCustomExpression(f);
|
|
400
|
-
const column = this.mapper(noPrefix ?
|
|
445
|
+
core_1.Utils.splitPrimaryKeys(k).forEach(f => {
|
|
446
|
+
// eslint-disable-next-line prefer-const
|
|
447
|
+
let [alias, field] = this.splitField(f, true);
|
|
448
|
+
alias = populate[alias] || alias;
|
|
449
|
+
const prop = this.getProperty(field, alias);
|
|
450
|
+
const noPrefix = (prop && prop.persist === false && !prop.formula) || QueryBuilderHelper.isCustomExpression(f);
|
|
451
|
+
const column = this.mapper(noPrefix ? field : `${alias}.${field}`, type, undefined, null);
|
|
401
452
|
/* istanbul ignore next */
|
|
402
453
|
const rawColumn = core_1.Utils.isString(column) ? column.split('.').map(e => this.knex.ref(e)).join('.') : column;
|
|
403
454
|
const customOrder = prop?.customOrder;
|
|
@@ -412,14 +463,22 @@ class QueryBuilderHelper {
|
|
|
412
463
|
finalize(type, qb, meta) {
|
|
413
464
|
const useReturningStatement = type === enums_1.QueryType.INSERT && this.platform.usesReturningStatement() && meta && !meta.compositePK;
|
|
414
465
|
if (useReturningStatement) {
|
|
415
|
-
const returningProps = meta.
|
|
466
|
+
const returningProps = meta.hydrateProps.filter(prop => prop.persist !== false && (prop.primary || prop.defaultRaw));
|
|
416
467
|
qb.returning(core_1.Utils.flatten(returningProps.map(prop => prop.fieldNames)));
|
|
417
468
|
}
|
|
418
469
|
}
|
|
419
|
-
splitField(field) {
|
|
470
|
+
splitField(field, greedyAlias = false) {
|
|
420
471
|
const parts = field.split('.');
|
|
421
|
-
|
|
422
|
-
|
|
472
|
+
if (parts.length === 1) {
|
|
473
|
+
return [this.alias, parts[0]];
|
|
474
|
+
}
|
|
475
|
+
if (greedyAlias) {
|
|
476
|
+
const fromField = parts.pop();
|
|
477
|
+
const fromAlias = parts.join('.');
|
|
478
|
+
return [fromAlias, fromField];
|
|
479
|
+
}
|
|
480
|
+
const fromAlias = parts.shift();
|
|
481
|
+
const fromField = parts.join('.');
|
|
423
482
|
return [fromAlias, fromField];
|
|
424
483
|
}
|
|
425
484
|
getLockSQL(qb, lockMode, lockTables = []) {
|
|
@@ -438,7 +497,7 @@ class QueryBuilderHelper {
|
|
|
438
497
|
}
|
|
439
498
|
updateVersionProperty(qb, data) {
|
|
440
499
|
const meta = this.metadata.find(this.entityName);
|
|
441
|
-
if (!meta
|
|
500
|
+
if (!meta?.versionProperty || meta.versionProperty in data) {
|
|
442
501
|
return;
|
|
443
502
|
}
|
|
444
503
|
const versionProperty = meta.properties[meta.versionProperty];
|
|
@@ -464,7 +523,8 @@ class QueryBuilderHelper {
|
|
|
464
523
|
ret = alias + fieldName;
|
|
465
524
|
}
|
|
466
525
|
else {
|
|
467
|
-
const [a,
|
|
526
|
+
const [a, ...rest] = field.split('.');
|
|
527
|
+
const f = rest.join('.');
|
|
468
528
|
ret = a + '.' + this.fieldName(f, a);
|
|
469
529
|
}
|
|
470
530
|
if (quote) {
|
|
@@ -509,15 +569,66 @@ class QueryBuilderHelper {
|
|
|
509
569
|
return prop.fieldNameRaw;
|
|
510
570
|
}
|
|
511
571
|
/* istanbul ignore next */
|
|
512
|
-
return prop.fieldNames[0] ?? field;
|
|
572
|
+
return prop.fieldNames?.[0] ?? field;
|
|
513
573
|
}
|
|
514
574
|
getProperty(field, alias) {
|
|
515
|
-
const entityName = this.aliasMap[alias] || this.entityName;
|
|
575
|
+
const entityName = this.aliasMap[alias]?.entityName || this.entityName;
|
|
516
576
|
const meta = this.metadata.find(entityName);
|
|
517
|
-
|
|
577
|
+
// check if `alias` is not matching an embedded property name instead of alias, e.g. `address.city`
|
|
578
|
+
if (alias && meta) {
|
|
579
|
+
const prop = meta.properties[alias];
|
|
580
|
+
if (prop?.reference === core_1.ReferenceType.EMBEDDED) {
|
|
581
|
+
// we want to select the full object property so hydration works as expected
|
|
582
|
+
if (prop.object) {
|
|
583
|
+
return prop;
|
|
584
|
+
}
|
|
585
|
+
const parts = field.split('.');
|
|
586
|
+
const nest = (p) => parts.length > 0 ? nest(p.embeddedProps[parts.shift()]) : p;
|
|
587
|
+
return nest(prop);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
return meta?.properties[field];
|
|
518
591
|
}
|
|
519
592
|
isTableNameAliasRequired(type) {
|
|
520
|
-
return [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(type);
|
|
593
|
+
return [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(type ?? enums_1.QueryType.SELECT);
|
|
594
|
+
}
|
|
595
|
+
mapData(data, properties, convertCustomTypes) {
|
|
596
|
+
if (!properties) {
|
|
597
|
+
return data;
|
|
598
|
+
}
|
|
599
|
+
data = Object.assign({}, data); // copy first
|
|
600
|
+
Object.keys(data).forEach(k => {
|
|
601
|
+
const prop = properties[k];
|
|
602
|
+
if (!prop) {
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
if (prop.embeddedProps && !prop.object) {
|
|
606
|
+
const copy = data[k];
|
|
607
|
+
delete data[k];
|
|
608
|
+
Object.assign(data, this.mapData(copy, prop.embeddedProps, convertCustomTypes));
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
if (prop.joinColumns && Array.isArray(data[k])) {
|
|
612
|
+
const copy = data[k];
|
|
613
|
+
delete data[k];
|
|
614
|
+
prop.joinColumns.forEach((joinColumn, idx) => data[joinColumn] = copy[idx]);
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
if (prop.customType && convertCustomTypes && !this.platform.isRaw(data[k])) {
|
|
618
|
+
data[k] = prop.customType.convertToDatabaseValue(data[k], this.platform, { fromQuery: true, key: k, mode: 'query' });
|
|
619
|
+
}
|
|
620
|
+
if (prop.customType && 'convertToDatabaseValueSQL' in prop.customType && !this.platform.isRaw(data[k])) {
|
|
621
|
+
const quoted = this.platform.quoteValue(data[k]);
|
|
622
|
+
data[k] = this.knex.raw(prop.customType.convertToDatabaseValueSQL(quoted, this.platform).replace(/\?/, '\\?'));
|
|
623
|
+
}
|
|
624
|
+
if (!prop.customType && (Array.isArray(data[k]) || core_1.Utils.isPlainObject(data[k]))) {
|
|
625
|
+
data[k] = JSON.stringify(data[k]);
|
|
626
|
+
}
|
|
627
|
+
if (prop.fieldNames) {
|
|
628
|
+
core_1.Utils.renameKey(data, k, prop.fieldNames[0]);
|
|
629
|
+
}
|
|
630
|
+
});
|
|
631
|
+
return data;
|
|
521
632
|
}
|
|
522
633
|
}
|
|
523
634
|
exports.QueryBuilderHelper = QueryBuilderHelper;
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
3
|
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
5
9
|
}) : (function(o, m, k, k2) {
|
|
6
10
|
if (k2 === undefined) k2 = k;
|
|
7
11
|
o[k2] = m[k];
|
|
@@ -11,7 +11,7 @@ export declare class DatabaseSchema {
|
|
|
11
11
|
private tables;
|
|
12
12
|
private namespaces;
|
|
13
13
|
constructor(platform: AbstractSqlPlatform, name: string);
|
|
14
|
-
addTable(name: string, schema: string | undefined | null): DatabaseTable;
|
|
14
|
+
addTable(name: string, schema: string | undefined | null, comment?: string): DatabaseTable;
|
|
15
15
|
getTables(): DatabaseTable[];
|
|
16
16
|
getTable(name: string): DatabaseTable | undefined;
|
|
17
17
|
hasTable(name: string): boolean;
|