@mikro-orm/knex 6.0.0-dev.27 → 6.0.0-dev.270
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 +22 -4
- package/AbstractSqlConnection.js +48 -18
- package/AbstractSqlDriver.d.ts +32 -24
- package/AbstractSqlDriver.js +578 -270
- package/AbstractSqlPlatform.d.ts +9 -4
- package/AbstractSqlPlatform.js +40 -5
- package/MonkeyPatchable.d.ts +1 -0
- package/MonkeyPatchable.js +3 -0
- package/PivotCollectionPersister.d.ts +17 -0
- package/PivotCollectionPersister.js +128 -0
- package/README.md +64 -107
- package/SqlEntityManager.d.ts +4 -8
- package/SqlEntityManager.js +8 -12
- package/SqlEntityRepository.d.ts +7 -6
- package/SqlEntityRepository.js +11 -8
- package/index.mjs +22 -3
- package/package.json +8 -8
- package/query/ArrayCriteriaNode.d.ts +5 -4
- package/query/ArrayCriteriaNode.js +7 -2
- package/query/CriteriaNode.d.ts +12 -10
- package/query/CriteriaNode.js +22 -9
- package/query/CriteriaNodeFactory.d.ts +6 -6
- package/query/CriteriaNodeFactory.js +32 -16
- package/query/ObjectCriteriaNode.d.ts +5 -4
- package/query/ObjectCriteriaNode.js +72 -28
- package/query/QueryBuilder.d.ts +58 -26
- package/query/QueryBuilder.js +438 -142
- package/query/QueryBuilderHelper.d.ts +13 -18
- package/query/QueryBuilderHelper.js +191 -164
- package/query/ScalarCriteriaNode.d.ts +4 -3
- package/query/ScalarCriteriaNode.js +14 -4
- package/query/enums.d.ts +7 -0
- package/query/enums.js +10 -2
- package/schema/DatabaseSchema.d.ts +5 -2
- package/schema/DatabaseSchema.js +26 -8
- package/schema/DatabaseTable.d.ts +12 -4
- package/schema/DatabaseTable.js +371 -46
- package/schema/SchemaComparator.d.ts +7 -5
- package/schema/SchemaComparator.js +68 -35
- package/schema/SchemaHelper.d.ts +7 -5
- package/schema/SchemaHelper.js +25 -11
- package/schema/SqlSchemaGenerator.d.ts +3 -4
- package/schema/SqlSchemaGenerator.js +71 -28
- package/typings.d.ts +30 -11
|
@@ -8,6 +8,14 @@ const enums_1 = require("./enums");
|
|
|
8
8
|
* @internal
|
|
9
9
|
*/
|
|
10
10
|
class QueryBuilderHelper {
|
|
11
|
+
entityName;
|
|
12
|
+
alias;
|
|
13
|
+
aliasMap;
|
|
14
|
+
subQueries;
|
|
15
|
+
knex;
|
|
16
|
+
driver;
|
|
17
|
+
platform;
|
|
18
|
+
metadata;
|
|
11
19
|
constructor(entityName, alias, aliasMap, subQueries, knex, driver) {
|
|
12
20
|
this.entityName = entityName;
|
|
13
21
|
this.alias = alias;
|
|
@@ -19,6 +27,10 @@ class QueryBuilderHelper {
|
|
|
19
27
|
this.metadata = this.driver.getMetadata();
|
|
20
28
|
}
|
|
21
29
|
mapper(field, type = enums_1.QueryType.SELECT, value, alias) {
|
|
30
|
+
if (core_1.Utils.isRawSql(field)) {
|
|
31
|
+
return this.knex.raw(field.sql, field.params);
|
|
32
|
+
}
|
|
33
|
+
/* istanbul ignore next */
|
|
22
34
|
if (typeof field !== 'string') {
|
|
23
35
|
return field;
|
|
24
36
|
}
|
|
@@ -29,7 +41,11 @@ class QueryBuilderHelper {
|
|
|
29
41
|
for (const p of fields) {
|
|
30
42
|
const [a, f] = this.splitField(p);
|
|
31
43
|
const prop = this.getProperty(f, a);
|
|
32
|
-
|
|
44
|
+
const fkIdx2 = prop?.fieldNames.findIndex(name => name === f) ?? -1;
|
|
45
|
+
if (fkIdx2 !== -1) {
|
|
46
|
+
parts.push(this.mapper(a !== this.alias ? `${a}.${prop.fieldNames[fkIdx2]}` : prop.fieldNames[fkIdx2], type, value, alias));
|
|
47
|
+
}
|
|
48
|
+
else if (prop) {
|
|
33
49
|
parts.push(...prop.fieldNames.map(f => this.mapper(a !== this.alias ? `${a}.${f}` : f, type, value, alias)));
|
|
34
50
|
}
|
|
35
51
|
else {
|
|
@@ -49,13 +65,18 @@ class QueryBuilderHelper {
|
|
|
49
65
|
}
|
|
50
66
|
return this.knex.raw('(' + parts.map(part => this.knex.ref(part)).join(', ') + ')');
|
|
51
67
|
}
|
|
52
|
-
|
|
53
|
-
|
|
68
|
+
const rawField = core_1.RawQueryFragment.getKnownFragment(field);
|
|
69
|
+
if (rawField) {
|
|
70
|
+
return this.knex.raw(rawField.sql, rawField.params);
|
|
71
|
+
}
|
|
54
72
|
const [a, f] = this.splitField(field);
|
|
55
73
|
const prop = this.getProperty(f, a);
|
|
74
|
+
const fkIdx2 = prop?.fieldNames.findIndex(name => name === f) ?? -1;
|
|
75
|
+
const fkIdx = fkIdx2 === -1 ? 0 : fkIdx2;
|
|
76
|
+
let ret = field;
|
|
56
77
|
// embeddable nested path instead of a regular property with table alias, reset alias
|
|
57
78
|
if (prop?.name === a && prop.embeddedProps[f]) {
|
|
58
|
-
return this.alias + '.' + prop.fieldNames[
|
|
79
|
+
return this.alias + '.' + prop.fieldNames[fkIdx];
|
|
59
80
|
}
|
|
60
81
|
const noPrefix = prop && prop.persist === false;
|
|
61
82
|
if (prop?.fieldNameRaw) {
|
|
@@ -67,25 +88,29 @@ class QueryBuilderHelper {
|
|
|
67
88
|
const as = alias === null ? '' : ` as ${aliased}`;
|
|
68
89
|
return this.knex.raw(`${prop.formula(alias2)}${as}`);
|
|
69
90
|
}
|
|
70
|
-
if (prop?.
|
|
71
|
-
|
|
72
|
-
|
|
91
|
+
if (prop?.hasConvertToJSValueSQL) {
|
|
92
|
+
let valueSQL;
|
|
93
|
+
if (prop.fieldNames.length > 1 && fkIdx !== -1) {
|
|
94
|
+
const fk = prop.targetMeta.getPrimaryProps()[fkIdx];
|
|
95
|
+
const prefixed = this.prefix(field, isTableNameAliasRequired, true, fkIdx);
|
|
96
|
+
valueSQL = fk.customType.convertToJSValueSQL(prefixed, this.platform);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
const prefixed = this.prefix(field, isTableNameAliasRequired, true);
|
|
100
|
+
valueSQL = prop.customType.convertToJSValueSQL(prefixed, this.platform);
|
|
101
|
+
}
|
|
73
102
|
if (alias === null) {
|
|
74
103
|
return this.knex.raw(valueSQL);
|
|
75
104
|
}
|
|
76
|
-
return this.knex.raw(`${valueSQL} as ${this.platform.quoteIdentifier(alias ?? prop.fieldNames[
|
|
105
|
+
return this.knex.raw(`${valueSQL} as ${this.platform.quoteIdentifier(alias ?? prop.fieldNames[fkIdx])}`);
|
|
77
106
|
}
|
|
78
107
|
// do not wrap custom expressions
|
|
79
|
-
if (!
|
|
80
|
-
ret = this.prefix(field);
|
|
108
|
+
if (!rawField) {
|
|
109
|
+
ret = this.prefix(field, false, false, fkIdx);
|
|
81
110
|
}
|
|
82
111
|
if (alias) {
|
|
83
112
|
ret += ' as ' + alias;
|
|
84
113
|
}
|
|
85
|
-
if (customExpression) {
|
|
86
|
-
const bindings = core_1.Utils.asArray(value).slice(0, ret.match(/\?/g)?.length ?? 0);
|
|
87
|
-
return this.knex.raw(ret, bindings);
|
|
88
|
-
}
|
|
89
114
|
if (!isTableNameAliasRequired || this.isPrefixed(ret) || noPrefix) {
|
|
90
115
|
return ret;
|
|
91
116
|
}
|
|
@@ -96,88 +121,104 @@ class QueryBuilderHelper {
|
|
|
96
121
|
return data.map(d => this.processData(d, convertCustomTypes, true));
|
|
97
122
|
}
|
|
98
123
|
const meta = this.metadata.find(this.entityName);
|
|
99
|
-
data = this.
|
|
124
|
+
data = this.driver.mapDataToFieldNames(data, true, meta?.properties, convertCustomTypes);
|
|
100
125
|
if (!core_1.Utils.hasObjectKeys(data) && meta && multi) {
|
|
101
126
|
/* istanbul ignore next */
|
|
102
127
|
data[meta.primaryKeys[0]] = this.platform.usesDefaultKeyword() ? this.knex.raw('default') : undefined;
|
|
103
128
|
}
|
|
104
129
|
return data;
|
|
105
130
|
}
|
|
106
|
-
joinOneToReference(prop, ownerAlias, alias, type, cond = {}) {
|
|
131
|
+
joinOneToReference(prop, ownerAlias, alias, type, cond = {}, schema) {
|
|
107
132
|
const prop2 = prop.targetMeta.properties[prop.mappedBy || prop.inversedBy];
|
|
108
133
|
const table = this.getTableName(prop.type);
|
|
109
|
-
const schema = this.driver.getSchemaName(prop.targetMeta);
|
|
110
134
|
const joinColumns = prop.owner ? prop.referencedColumnNames : prop2.joinColumns;
|
|
111
135
|
const inverseJoinColumns = prop.referencedColumnNames;
|
|
112
136
|
const primaryKeys = prop.owner ? prop.joinColumns : prop2.referencedColumnNames;
|
|
137
|
+
schema ??= prop.targetMeta?.schema === '*' ? '*' : this.driver.getSchemaName(prop.targetMeta);
|
|
113
138
|
return {
|
|
114
139
|
prop, type, cond, ownerAlias, alias, table, schema,
|
|
115
140
|
joinColumns, inverseJoinColumns, primaryKeys,
|
|
116
141
|
};
|
|
117
142
|
}
|
|
118
|
-
joinManyToOneReference(prop, ownerAlias, alias, type, cond = {}) {
|
|
143
|
+
joinManyToOneReference(prop, ownerAlias, alias, type, cond = {}, schema) {
|
|
119
144
|
return {
|
|
120
145
|
prop, type, cond, ownerAlias, alias,
|
|
121
146
|
table: this.getTableName(prop.type),
|
|
122
|
-
schema: this.driver.getSchemaName(prop.targetMeta),
|
|
147
|
+
schema: prop.targetMeta?.schema === '*' ? '*' : this.driver.getSchemaName(prop.targetMeta, { schema }),
|
|
123
148
|
joinColumns: prop.referencedColumnNames,
|
|
124
149
|
primaryKeys: prop.fieldNames,
|
|
125
150
|
};
|
|
126
151
|
}
|
|
127
|
-
joinManyToManyReference(prop, ownerAlias, alias, pivotAlias, type, cond, path) {
|
|
152
|
+
joinManyToManyReference(prop, ownerAlias, alias, pivotAlias, type, cond, path, schema) {
|
|
128
153
|
const pivotMeta = this.metadata.find(prop.pivotEntity);
|
|
129
154
|
const ret = {
|
|
130
155
|
[`${ownerAlias}.${prop.name}#${pivotAlias}`]: {
|
|
131
|
-
prop, type,
|
|
156
|
+
prop, type, ownerAlias,
|
|
132
157
|
alias: pivotAlias,
|
|
133
158
|
inverseAlias: alias,
|
|
134
159
|
joinColumns: prop.joinColumns,
|
|
135
160
|
inverseJoinColumns: prop.inverseJoinColumns,
|
|
136
161
|
primaryKeys: prop.referencedColumnNames,
|
|
162
|
+
cond: {},
|
|
137
163
|
table: pivotMeta.tableName,
|
|
138
|
-
schema: this.driver.getSchemaName(pivotMeta),
|
|
164
|
+
schema: prop.targetMeta?.schema === '*' ? '*' : this.driver.getSchemaName(pivotMeta, { schema }),
|
|
139
165
|
path: path.endsWith('[pivot]') ? path : `${path}[pivot]`,
|
|
140
166
|
},
|
|
141
167
|
};
|
|
142
|
-
if (type ===
|
|
168
|
+
if (type === enums_1.JoinType.pivotJoin) {
|
|
143
169
|
return ret;
|
|
144
170
|
}
|
|
145
171
|
const prop2 = prop.owner ? pivotMeta.relations[1] : pivotMeta.relations[0];
|
|
146
|
-
ret[`${pivotAlias}.${prop2.name}#${alias}`] = this.joinManyToOneReference(prop2, pivotAlias, alias, type);
|
|
172
|
+
ret[`${pivotAlias}.${prop2.name}#${alias}`] = this.joinManyToOneReference(prop2, pivotAlias, alias, type, cond, schema);
|
|
147
173
|
ret[`${pivotAlias}.${prop2.name}#${alias}`].path = path;
|
|
174
|
+
const tmp = prop2.referencedTableName.split('.');
|
|
175
|
+
ret[`${pivotAlias}.${prop2.name}#${alias}`].schema ??= tmp.length > 1 ? tmp[0] : undefined;
|
|
148
176
|
return ret;
|
|
149
177
|
}
|
|
150
|
-
joinPivotTable(field, prop, ownerAlias, alias, type, cond = {}) {
|
|
151
|
-
const pivotMeta = this.metadata.find(field);
|
|
152
|
-
const prop2 = pivotMeta.relations[0] === prop ? pivotMeta.relations[1] : pivotMeta.relations[0];
|
|
153
|
-
return {
|
|
154
|
-
prop, type, cond, ownerAlias, alias,
|
|
155
|
-
table: pivotMeta.collection,
|
|
156
|
-
schema: pivotMeta.schema,
|
|
157
|
-
joinColumns: prop.joinColumns,
|
|
158
|
-
inverseJoinColumns: prop2.joinColumns,
|
|
159
|
-
primaryKeys: prop.referencedColumnNames,
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
178
|
processJoins(qb, joins, schema) {
|
|
163
179
|
Object.values(joins).forEach(join => {
|
|
164
|
-
let table =
|
|
165
|
-
const method = join.type ===
|
|
180
|
+
let table = join.table;
|
|
181
|
+
const method = join.type === enums_1.JoinType.pivotJoin ? 'left join' : join.type;
|
|
182
|
+
const conditions = [];
|
|
183
|
+
const params = [];
|
|
166
184
|
schema = join.schema && join.schema !== '*' ? join.schema : schema;
|
|
167
185
|
if (schema) {
|
|
168
186
|
table = `${schema}.${table}`;
|
|
169
187
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
188
|
+
if (!join.subquery) {
|
|
189
|
+
join.primaryKeys.forEach((primaryKey, idx) => {
|
|
190
|
+
const right = `${join.alias}.${join.joinColumns[idx]}`;
|
|
191
|
+
if (join.prop.formula) {
|
|
192
|
+
const left = join.prop.formula(join.ownerAlias);
|
|
193
|
+
conditions.push(`${left} = ${this.knex.ref(right)}`);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
const left = `${join.ownerAlias}.${primaryKey}`;
|
|
197
|
+
conditions.push(`${this.knex.ref(left)} = ${this.knex.ref(right)}`);
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
if (join.prop.targetMeta?.discriminatorValue && !join.path?.endsWith('[pivot]')) {
|
|
201
|
+
const typeProperty = join.prop.targetMeta.root.discriminatorColumn;
|
|
202
|
+
const alias = join.inverseAlias ?? join.alias;
|
|
203
|
+
join.cond[`${alias}.${typeProperty}`] = join.prop.targetMeta.discriminatorValue;
|
|
204
|
+
}
|
|
177
205
|
Object.keys(join.cond).forEach(key => {
|
|
178
|
-
|
|
206
|
+
const needsPrefix = key.includes('.') || core_1.Utils.isOperator(key) || core_1.RawQueryFragment.isKnownFragment(key);
|
|
207
|
+
const newKey = needsPrefix ? key : `${join.alias}.${key}`;
|
|
208
|
+
conditions.push(this.processJoinClause(newKey, join.cond[key], params));
|
|
179
209
|
});
|
|
180
|
-
|
|
210
|
+
let sql = method + ' ';
|
|
211
|
+
if (join.subquery) {
|
|
212
|
+
sql += `(${join.subquery})`;
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
sql += this.knex.ref(table);
|
|
216
|
+
}
|
|
217
|
+
sql += ` as ${this.knex.ref(join.alias)}`;
|
|
218
|
+
if (conditions.length > 0) {
|
|
219
|
+
sql += ` on ${conditions.join(' and ')}`;
|
|
220
|
+
}
|
|
221
|
+
return qb.joinRaw(sql, params);
|
|
181
222
|
});
|
|
182
223
|
}
|
|
183
224
|
processJoinClause(key, value, params, operator = '$eq') {
|
|
@@ -224,20 +265,14 @@ class QueryBuilderHelper {
|
|
|
224
265
|
if (operator === '$exists') {
|
|
225
266
|
value = null;
|
|
226
267
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
const { sql, bindings } = k.toSQL().toNative();
|
|
235
|
-
if (params2.length > 0) {
|
|
236
|
-
params.push(...bindings);
|
|
237
|
-
params.push(...params2);
|
|
238
|
-
return sql + ' = ?';
|
|
268
|
+
const rawField = core_1.RawQueryFragment.getKnownFragment(key);
|
|
269
|
+
if (rawField) {
|
|
270
|
+
let sql = rawField.sql;
|
|
271
|
+
params.push(...rawField.params);
|
|
272
|
+
params.push(...core_1.Utils.asArray(value));
|
|
273
|
+
if (core_1.Utils.asArray(value).length > 0) {
|
|
274
|
+
sql += ' = ?';
|
|
239
275
|
}
|
|
240
|
-
params.push(...bindings);
|
|
241
276
|
return sql;
|
|
242
277
|
}
|
|
243
278
|
const sql = this.mapper(key);
|
|
@@ -263,9 +298,9 @@ class QueryBuilderHelper {
|
|
|
263
298
|
...join.inverseJoinColumns.map(col => this.mapper(`${join.alias}.${col}`, type, undefined, `fk__${col}`)),
|
|
264
299
|
];
|
|
265
300
|
}
|
|
266
|
-
isOneToOneInverse(field) {
|
|
267
|
-
|
|
268
|
-
const prop = meta.properties[field];
|
|
301
|
+
isOneToOneInverse(field, meta) {
|
|
302
|
+
meta ??= this.metadata.find(this.entityName);
|
|
303
|
+
const prop = meta.properties[field.replace(/:ref$/, '')];
|
|
269
304
|
return prop && prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner;
|
|
270
305
|
}
|
|
271
306
|
getTableName(entityName) {
|
|
@@ -305,13 +340,13 @@ class QueryBuilderHelper {
|
|
|
305
340
|
}
|
|
306
341
|
appendOnConflictClause(type, onConflict, qb) {
|
|
307
342
|
onConflict.forEach(item => {
|
|
308
|
-
const sub = qb.onConflict(item.fields);
|
|
343
|
+
const sub = item.fields.length > 0 ? qb.onConflict(item.fields) : qb.onConflict();
|
|
309
344
|
core_1.Utils.runIfNotEmpty(() => sub.ignore(), item.ignore);
|
|
310
345
|
core_1.Utils.runIfNotEmpty(() => {
|
|
311
346
|
let mergeParam = item.merge;
|
|
312
347
|
if (core_1.Utils.isObject(item.merge)) {
|
|
313
348
|
mergeParam = {};
|
|
314
|
-
|
|
349
|
+
core_1.Utils.keys(item.merge).forEach(key => {
|
|
315
350
|
const k = this.mapper(key, type);
|
|
316
351
|
mergeParam[k] = item.merge[key];
|
|
317
352
|
});
|
|
@@ -342,38 +377,37 @@ class QueryBuilderHelper {
|
|
|
342
377
|
}
|
|
343
378
|
appendQuerySubCondition(qb, type, method, cond, key, operator) {
|
|
344
379
|
const m = operator === '$or' ? 'orWhere' : method;
|
|
380
|
+
if (cond[key] instanceof core_1.RawQueryFragment) {
|
|
381
|
+
cond[key] = this.knex.raw(cond[key].sql, cond[key].params);
|
|
382
|
+
}
|
|
345
383
|
if (this.isSimpleRegExp(cond[key])) {
|
|
346
384
|
return void qb[m](this.mapper(key, type), 'like', this.getRegExpParam(cond[key]));
|
|
347
385
|
}
|
|
348
386
|
if (core_1.Utils.isPlainObject(cond[key]) || cond[key] instanceof RegExp) {
|
|
349
387
|
return this.processObjectSubCondition(cond, key, qb, method, m, type);
|
|
350
388
|
}
|
|
351
|
-
if (QueryBuilderHelper.isCustomExpression(key)) {
|
|
352
|
-
return this.processCustomExpression(qb, m, key, cond, type);
|
|
353
|
-
}
|
|
354
389
|
const op = cond[key] === null ? 'is' : '=';
|
|
390
|
+
const raw = core_1.RawQueryFragment.getKnownFragment(key);
|
|
391
|
+
if (raw) {
|
|
392
|
+
const value = core_1.Utils.asArray(cond[key]);
|
|
393
|
+
if (value.length > 0) {
|
|
394
|
+
return void qb[m](this.knex.raw(raw.sql, raw.params), op, value[0]);
|
|
395
|
+
}
|
|
396
|
+
return void qb[m](this.knex.raw(raw.sql, raw.params));
|
|
397
|
+
}
|
|
355
398
|
if (this.subQueries[key]) {
|
|
356
399
|
return void qb[m](this.knex.raw(`(${this.subQueries[key]})`), op, cond[key]);
|
|
357
400
|
}
|
|
358
401
|
qb[m](this.mapper(key, type, cond[key], null), op, cond[key]);
|
|
359
402
|
}
|
|
360
|
-
processCustomExpression(clause, m, key, cond, type) {
|
|
361
|
-
// unwind parameters when ? found in field name
|
|
362
|
-
const count = key.concat('?').match(/\?/g).length - 1;
|
|
363
|
-
const value = core_1.Utils.asArray(cond[key]);
|
|
364
|
-
const params1 = value.slice(0, count).map((c) => core_1.Utils.isObject(c) ? JSON.stringify(c) : c);
|
|
365
|
-
const params2 = value.slice(count);
|
|
366
|
-
const k = this.mapper(key, type, params1);
|
|
367
|
-
if (params2.length > 0) {
|
|
368
|
-
const val = params2.length === 1 && params2[0] === null ? null : this.knex.raw('?', params2);
|
|
369
|
-
return void clause[m](k, val);
|
|
370
|
-
}
|
|
371
|
-
clause[m](k);
|
|
372
|
-
}
|
|
373
403
|
processObjectSubCondition(cond, key, qb, method, m, type) {
|
|
374
404
|
// grouped condition for one field
|
|
375
405
|
let value = cond[key];
|
|
376
|
-
|
|
406
|
+
const size = core_1.Utils.getObjectKeysSize(value);
|
|
407
|
+
if (core_1.Utils.isPlainObject(value) && size === 0) {
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
if (size > 1) {
|
|
377
411
|
const subCondition = Object.entries(value).map(([subKey, subValue]) => ({ [key]: { [subKey]: subValue } }));
|
|
378
412
|
return subCondition.forEach(sub => this.appendQueryCondition(type, sub, qb, '$and', method));
|
|
379
413
|
}
|
|
@@ -382,13 +416,15 @@ class QueryBuilderHelper {
|
|
|
382
416
|
}
|
|
383
417
|
// operators
|
|
384
418
|
const op = Object.keys(core_1.QueryOperator).find(op => op in value);
|
|
419
|
+
/* istanbul ignore next */
|
|
385
420
|
if (!op) {
|
|
386
421
|
throw new Error(`Invalid query condition: ${(0, util_1.inspect)(cond)}`);
|
|
387
422
|
}
|
|
388
423
|
const replacement = this.getOperatorReplacement(op, value);
|
|
389
424
|
const fields = core_1.Utils.splitPrimaryKeys(key);
|
|
390
425
|
if (fields.length > 1 && Array.isArray(value[op]) && !value[op].every((v) => Array.isArray(v))) {
|
|
391
|
-
value[op]
|
|
426
|
+
const tmp = value[op].length === 1 && core_1.Utils.isPlainObject(value[op][0]) ? fields.map(f => value[op][0][f]) : value[op];
|
|
427
|
+
value[op] = this.knex.raw(`(${fields.map(() => '?').join(', ')})`, tmp);
|
|
392
428
|
}
|
|
393
429
|
if (this.subQueries[key]) {
|
|
394
430
|
return void qb[m](this.knex.raw(`(${this.subQueries[key]})`), replacement, value[op]);
|
|
@@ -426,60 +462,88 @@ class QueryBuilderHelper {
|
|
|
426
462
|
}
|
|
427
463
|
getQueryOrder(type, orderBy, populate) {
|
|
428
464
|
if (Array.isArray(orderBy)) {
|
|
429
|
-
return orderBy
|
|
430
|
-
.map(o => this.getQueryOrder(type, o, populate))
|
|
431
|
-
.filter(o => o)
|
|
432
|
-
.join(', ');
|
|
465
|
+
return orderBy.flatMap(o => this.getQueryOrder(type, o, populate));
|
|
433
466
|
}
|
|
434
467
|
return this.getQueryOrderFromObject(type, orderBy, populate);
|
|
435
468
|
}
|
|
436
469
|
getQueryOrderFromObject(type, orderBy, populate) {
|
|
437
470
|
const ret = [];
|
|
438
|
-
Object.keys(orderBy)
|
|
439
|
-
const direction = orderBy[
|
|
471
|
+
for (const key of Object.keys(orderBy)) {
|
|
472
|
+
const direction = orderBy[key];
|
|
440
473
|
const order = core_1.Utils.isNumber(direction) ? core_1.QueryOrderNumeric[direction] : direction;
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
474
|
+
const raw = core_1.RawQueryFragment.getKnownFragment(key);
|
|
475
|
+
if (raw) {
|
|
476
|
+
ret.push(`${this.platform.formatQuery(raw.sql, raw.params)} ${order.toLowerCase()}`);
|
|
477
|
+
continue;
|
|
444
478
|
}
|
|
445
|
-
core_1.Utils.splitPrimaryKeys(
|
|
479
|
+
for (const f of core_1.Utils.splitPrimaryKeys(key)) {
|
|
446
480
|
// eslint-disable-next-line prefer-const
|
|
447
481
|
let [alias, field] = this.splitField(f, true);
|
|
448
482
|
alias = populate[alias] || alias;
|
|
449
483
|
const prop = this.getProperty(field, alias);
|
|
450
|
-
const noPrefix = (prop && prop.persist === false && !prop.formula) ||
|
|
484
|
+
const noPrefix = (prop && prop.persist === false && !prop.formula) || core_1.RawQueryFragment.isKnownFragment(f);
|
|
451
485
|
const column = this.mapper(noPrefix ? field : `${alias}.${field}`, type, undefined, null);
|
|
452
486
|
/* istanbul ignore next */
|
|
453
487
|
const rawColumn = core_1.Utils.isString(column) ? column.split('.').map(e => this.knex.ref(e)).join('.') : column;
|
|
454
488
|
const customOrder = prop?.customOrder;
|
|
455
|
-
|
|
489
|
+
let colPart = customOrder
|
|
456
490
|
? this.platform.generateCustomOrder(rawColumn, customOrder)
|
|
457
491
|
: rawColumn;
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
492
|
+
if (core_1.Utils.isRawSql(colPart)) {
|
|
493
|
+
colPart = this.platform.formatQuery(colPart.sql, colPart.params);
|
|
494
|
+
}
|
|
495
|
+
if (Array.isArray(order)) {
|
|
496
|
+
order.forEach(part => ret.push(...this.getQueryOrderFromObject(type, part, populate)));
|
|
497
|
+
}
|
|
498
|
+
else {
|
|
499
|
+
ret.push(...this.platform.getOrderByExpression(colPart, order));
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
return ret;
|
|
462
504
|
}
|
|
463
|
-
finalize(type, qb, meta) {
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
505
|
+
finalize(type, qb, meta, data, returning) {
|
|
506
|
+
if (!meta || !data || !this.platform.usesReturningStatement()) {
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
// always respect explicit returning hint
|
|
510
|
+
if (returning && returning.length > 0) {
|
|
511
|
+
qb.returning(returning.map(field => this.mapper(field, type)));
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
if (type === enums_1.QueryType.INSERT) {
|
|
515
|
+
const returningProps = meta.hydrateProps
|
|
516
|
+
.filter(prop => prop.returning || (prop.persist !== false && ((prop.primary && prop.autoincrement) || prop.defaultRaw)))
|
|
517
|
+
.filter(prop => !(prop.name in data));
|
|
518
|
+
if (returningProps.length > 0) {
|
|
519
|
+
qb.returning(core_1.Utils.flatten(returningProps.map(prop => prop.fieldNames)));
|
|
520
|
+
}
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
if (type === enums_1.QueryType.UPDATE) {
|
|
524
|
+
const returningProps = meta.hydrateProps.filter(prop => core_1.Utils.isRawSql(data[prop.name]));
|
|
525
|
+
if (returningProps.length > 0) {
|
|
526
|
+
qb.returning(core_1.Utils.flatten(returningProps.map(prop => prop.fieldNames)));
|
|
527
|
+
}
|
|
468
528
|
}
|
|
469
529
|
}
|
|
470
530
|
splitField(field, greedyAlias = false) {
|
|
471
531
|
const parts = field.split('.');
|
|
532
|
+
const ref = parts[parts.length - 1].split(':')[1];
|
|
533
|
+
if (ref) {
|
|
534
|
+
parts[parts.length - 1] = parts[parts.length - 1].substring(0, parts[parts.length - 1].indexOf(':'));
|
|
535
|
+
}
|
|
472
536
|
if (parts.length === 1) {
|
|
473
|
-
return [this.alias, parts[0]];
|
|
537
|
+
return [this.alias, parts[0], ref];
|
|
474
538
|
}
|
|
475
539
|
if (greedyAlias) {
|
|
476
540
|
const fromField = parts.pop();
|
|
477
541
|
const fromAlias = parts.join('.');
|
|
478
|
-
return [fromAlias, fromField];
|
|
542
|
+
return [fromAlias, fromField, ref];
|
|
479
543
|
}
|
|
480
544
|
const fromAlias = parts.shift();
|
|
481
545
|
const fromField = parts.join('.');
|
|
482
|
-
return [fromAlias, fromField];
|
|
546
|
+
return [fromAlias, fromField, ref];
|
|
483
547
|
}
|
|
484
548
|
getLockSQL(qb, lockMode, lockTables = []) {
|
|
485
549
|
const meta = this.metadata.find(this.entityName);
|
|
@@ -502,30 +566,25 @@ class QueryBuilderHelper {
|
|
|
502
566
|
}
|
|
503
567
|
const versionProperty = meta.properties[meta.versionProperty];
|
|
504
568
|
let sql = this.platform.quoteIdentifier(versionProperty.fieldNames[0]) + ' + 1';
|
|
505
|
-
if (versionProperty.
|
|
569
|
+
if (versionProperty.runtimeType === 'Date') {
|
|
506
570
|
sql = this.platform.getCurrentTimestampSQL(versionProperty.length);
|
|
507
571
|
}
|
|
508
572
|
qb.update(versionProperty.fieldNames[0], this.knex.raw(sql));
|
|
509
573
|
}
|
|
510
|
-
|
|
511
|
-
// if we do not have alias, we don't consider spaces as custom expressions
|
|
512
|
-
const re = hasAlias ? /[ ?<>=()'"`:]|^\d/ : /[?<>=()'"`:]|^\d/;
|
|
513
|
-
return !!field.match(re);
|
|
514
|
-
}
|
|
515
|
-
prefix(field, always = false, quote = false) {
|
|
574
|
+
prefix(field, always = false, quote = false, idx) {
|
|
516
575
|
let ret;
|
|
517
576
|
if (!this.isPrefixed(field)) {
|
|
518
577
|
const alias = always ? (quote ? this.alias : this.platform.quoteIdentifier(this.alias)) + '.' : '';
|
|
519
|
-
const fieldName = this.fieldName(field, this.alias, always);
|
|
520
|
-
if (
|
|
521
|
-
return fieldName;
|
|
578
|
+
const fieldName = this.fieldName(field, this.alias, always, idx);
|
|
579
|
+
if (fieldName instanceof core_1.RawQueryFragment) {
|
|
580
|
+
return fieldName.sql;
|
|
522
581
|
}
|
|
523
582
|
ret = alias + fieldName;
|
|
524
583
|
}
|
|
525
584
|
else {
|
|
526
585
|
const [a, ...rest] = field.split('.');
|
|
527
586
|
const f = rest.join('.');
|
|
528
|
-
ret = a + '.' + this.fieldName(f, a);
|
|
587
|
+
ret = a + '.' + this.fieldName(f, a, always, idx);
|
|
529
588
|
}
|
|
530
589
|
if (quote) {
|
|
531
590
|
return this.platform.quoteIdentifier(ret);
|
|
@@ -551,25 +610,25 @@ class QueryBuilderHelper {
|
|
|
551
610
|
isPrefixed(field) {
|
|
552
611
|
return !!field.match(/[\w`"[\]]+\./);
|
|
553
612
|
}
|
|
554
|
-
fieldName(field, alias, always) {
|
|
613
|
+
fieldName(field, alias, always, idx = 0) {
|
|
555
614
|
const prop = this.getProperty(field, alias);
|
|
556
615
|
if (!prop) {
|
|
557
616
|
return field;
|
|
558
617
|
}
|
|
559
618
|
if (prop.fieldNameRaw) {
|
|
560
619
|
if (!always) {
|
|
561
|
-
return prop.fieldNameRaw
|
|
562
|
-
.replace(
|
|
563
|
-
.replace(this.platform.quoteIdentifier('') + '.', '');
|
|
620
|
+
return (0, core_1.raw)(prop.fieldNameRaw
|
|
621
|
+
.replace(new RegExp(core_1.ALIAS_REPLACEMENT_RE + '\\.?', 'g'), '')
|
|
622
|
+
.replace(this.platform.quoteIdentifier('') + '.', ''));
|
|
564
623
|
}
|
|
565
624
|
if (alias) {
|
|
566
|
-
return prop.fieldNameRaw.replace(
|
|
625
|
+
return (0, core_1.raw)(prop.fieldNameRaw.replace(new RegExp(core_1.ALIAS_REPLACEMENT_RE, 'g'), alias));
|
|
567
626
|
}
|
|
568
627
|
/* istanbul ignore next */
|
|
569
|
-
return prop.fieldNameRaw;
|
|
628
|
+
return (0, core_1.raw)(prop.fieldNameRaw);
|
|
570
629
|
}
|
|
571
630
|
/* istanbul ignore next */
|
|
572
|
-
return prop.fieldNames?.[
|
|
631
|
+
return prop.fieldNames?.[idx] ?? field;
|
|
573
632
|
}
|
|
574
633
|
getProperty(field, alias) {
|
|
575
634
|
const entityName = this.aliasMap[alias]?.entityName || this.entityName;
|
|
@@ -587,48 +646,16 @@ class QueryBuilderHelper {
|
|
|
587
646
|
return nest(prop);
|
|
588
647
|
}
|
|
589
648
|
}
|
|
590
|
-
|
|
649
|
+
if (meta) {
|
|
650
|
+
if (meta.properties[field]) {
|
|
651
|
+
return meta.properties[field];
|
|
652
|
+
}
|
|
653
|
+
return meta.relations.find(prop => prop.fieldNames?.some(name => field === name));
|
|
654
|
+
}
|
|
655
|
+
return undefined;
|
|
591
656
|
}
|
|
592
657
|
isTableNameAliasRequired(type) {
|
|
593
658
|
return [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(type ?? enums_1.QueryType.SELECT);
|
|
594
659
|
}
|
|
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;
|
|
632
|
-
}
|
|
633
660
|
}
|
|
634
661
|
exports.QueryBuilderHelper = QueryBuilderHelper;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { CriteriaNode } from './CriteriaNode';
|
|
2
|
-
import type { IQueryBuilder } from '../typings';
|
|
2
|
+
import type { IQueryBuilder, ICriteriaNodeProcessOptions } from '../typings';
|
|
3
3
|
/**
|
|
4
4
|
* @internal
|
|
5
5
|
*/
|
|
6
|
-
export declare class ScalarCriteriaNode extends CriteriaNode {
|
|
7
|
-
process
|
|
6
|
+
export declare class ScalarCriteriaNode<T extends object> extends CriteriaNode<T> {
|
|
7
|
+
process(qb: IQueryBuilder<T>, options?: ICriteriaNodeProcessOptions): any;
|
|
8
|
+
willAutoJoin<T>(qb: IQueryBuilder<T>, alias?: string): boolean;
|
|
8
9
|
shouldJoin(): boolean;
|
|
9
10
|
}
|
|
@@ -3,25 +3,35 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ScalarCriteriaNode = void 0;
|
|
4
4
|
const core_1 = require("@mikro-orm/core");
|
|
5
5
|
const CriteriaNode_1 = require("./CriteriaNode");
|
|
6
|
+
const enums_1 = require("./enums");
|
|
6
7
|
/**
|
|
7
8
|
* @internal
|
|
8
9
|
*/
|
|
9
10
|
class ScalarCriteriaNode extends CriteriaNode_1.CriteriaNode {
|
|
10
|
-
process(qb,
|
|
11
|
+
process(qb, options) {
|
|
11
12
|
if (this.shouldJoin()) {
|
|
12
13
|
const path = this.getPath();
|
|
13
14
|
const parentPath = this.parent.getPath(); // the parent is always there, otherwise `shouldJoin` would return `false`
|
|
14
15
|
const nestedAlias = qb.getAliasForJoinPath(path) || qb.getNextAlias(this.prop?.pivotTable ?? this.entityName);
|
|
15
|
-
const field =
|
|
16
|
-
const type = this.prop.kind === core_1.ReferenceKind.MANY_TO_MANY ?
|
|
16
|
+
const field = this.aliased(this.prop.name, options?.alias);
|
|
17
|
+
const type = this.prop.kind === core_1.ReferenceKind.MANY_TO_MANY ? enums_1.JoinType.pivotJoin : enums_1.JoinType.leftJoin;
|
|
17
18
|
qb.join(field, nestedAlias, undefined, type, path);
|
|
18
19
|
// select the owner as virtual property when joining from 1:1 inverse side, but only if the parent is root entity
|
|
19
|
-
if (this.prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !parentPath.includes('.')) {
|
|
20
|
+
if (this.prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !parentPath.includes('.') && !qb._fields?.includes(field)) {
|
|
20
21
|
qb.addSelect(field);
|
|
21
22
|
}
|
|
22
23
|
}
|
|
24
|
+
if (this.payload && typeof this.payload === 'object') {
|
|
25
|
+
const keys = Object.keys(this.payload).filter(key => core_1.Utils.isArrayOperator(key) && Array.isArray(this.payload[key]));
|
|
26
|
+
for (const key of keys) {
|
|
27
|
+
this.payload[key] = JSON.stringify(this.payload[key]);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
23
30
|
return this.payload;
|
|
24
31
|
}
|
|
32
|
+
willAutoJoin(qb, alias) {
|
|
33
|
+
return this.shouldJoin();
|
|
34
|
+
}
|
|
25
35
|
shouldJoin() {
|
|
26
36
|
if (!this.parent || !this.prop) {
|
|
27
37
|
return false;
|