@mikro-orm/knex 6.0.0-dev.4 → 6.0.0-dev.40
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} +6 -1
- package/AbstractSqlDriver.d.ts +66 -0
- package/{dist/AbstractSqlDriver.js → AbstractSqlDriver.js} +268 -93
- 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 -4
- package/{dist/SqlEntityRepository.js → SqlEntityRepository.js} +1 -2
- package/{dist/index.d.ts → index.d.ts} +1 -0
- package/{dist/index.js → index.js} +6 -1
- package/index.mjs +194 -0
- package/package.json +18 -47
- package/query/ArrayCriteriaNode.d.ts +9 -0
- package/{dist/query → query}/ArrayCriteriaNode.js +0 -0
- package/{dist/query → query}/CriteriaNode.d.ts +8 -8
- package/{dist/query → query}/CriteriaNode.js +15 -12
- package/query/CriteriaNodeFactory.d.ts +12 -0
- package/{dist/query → query}/CriteriaNodeFactory.js +10 -10
- package/{dist/query → query}/ObjectCriteriaNode.d.ts +3 -3
- package/{dist/query → query}/ObjectCriteriaNode.js +19 -6
- package/{dist/query → query}/QueryBuilder.d.ts +66 -24
- package/{dist/query → query}/QueryBuilder.js +381 -94
- package/{dist/query → query}/QueryBuilderHelper.d.ts +19 -11
- package/{dist/query → query}/QueryBuilderHelper.js +227 -116
- package/{dist/query → query}/ScalarCriteriaNode.d.ts +2 -2
- package/{dist/query → query}/ScalarCriteriaNode.js +6 -6
- 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 +11 -19
- package/{dist/schema → schema}/DatabaseTable.d.ts +9 -9
- package/{dist/schema → schema}/DatabaseTable.js +22 -22
- 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} +10 -12
- package/{dist/schema/SchemaGenerator.js → schema/SqlSchemaGenerator.js} +89 -45
- 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} +21 -16
- 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
- package/dist/query/ArrayCriteriaNode.d.ts +0 -9
- package/dist/query/CriteriaNodeFactory.d.ts +0 -12
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.QueryBuilder = void 0;
|
|
4
|
+
const util_1 = require("util");
|
|
4
5
|
const core_1 = require("@mikro-orm/core");
|
|
5
6
|
const enums_1 = require("./enums");
|
|
6
7
|
const QueryBuilderHelper_1 = require("./QueryBuilderHelper");
|
|
@@ -25,11 +26,21 @@ const CriteriaNodeFactory_1 = require("./CriteriaNodeFactory");
|
|
|
25
26
|
* ```
|
|
26
27
|
*/
|
|
27
28
|
class QueryBuilder {
|
|
29
|
+
get mainAlias() {
|
|
30
|
+
this.ensureFromClause();
|
|
31
|
+
return this._mainAlias;
|
|
32
|
+
}
|
|
33
|
+
get alias() {
|
|
34
|
+
return this.mainAlias.aliasName;
|
|
35
|
+
}
|
|
36
|
+
get helper() {
|
|
37
|
+
this.ensureFromClause();
|
|
38
|
+
return this._helper;
|
|
39
|
+
}
|
|
28
40
|
/**
|
|
29
41
|
* @internal
|
|
30
42
|
*/
|
|
31
43
|
constructor(entityName, metadata, driver, context, alias, connectionType, em) {
|
|
32
|
-
this.entityName = entityName;
|
|
33
44
|
this.metadata = metadata;
|
|
34
45
|
this.driver = driver;
|
|
35
46
|
this.context = context;
|
|
@@ -43,23 +54,25 @@ class QueryBuilder {
|
|
|
43
54
|
this.flags = new Set([core_1.QueryFlag.CONVERT_CUSTOM_TYPES]);
|
|
44
55
|
this.finalized = false;
|
|
45
56
|
this._joins = {};
|
|
46
|
-
this.
|
|
57
|
+
this._explicitAlias = false;
|
|
47
58
|
this._cond = {};
|
|
48
59
|
this._orderBy = [];
|
|
49
60
|
this._groupBy = [];
|
|
50
61
|
this._having = {};
|
|
51
62
|
this._joinedProps = new Map();
|
|
52
63
|
this.subQueries = {};
|
|
64
|
+
this._aliases = {};
|
|
53
65
|
this.platform = this.driver.getPlatform();
|
|
54
66
|
this.knex = this.driver.getConnection(this.connectionType).getKnex();
|
|
55
67
|
if (alias) {
|
|
56
68
|
this.aliasCounter++;
|
|
69
|
+
this._explicitAlias = true;
|
|
57
70
|
}
|
|
58
|
-
|
|
59
|
-
this.
|
|
60
|
-
this.helper = new QueryBuilderHelper_1.QueryBuilderHelper(this.entityName, this.alias, this._aliasMap, this.subQueries, this.metadata, this.knex, this.platform);
|
|
71
|
+
// @ts-expect-error union type does not match the overloaded method signature
|
|
72
|
+
this.from(entityName, alias);
|
|
61
73
|
}
|
|
62
74
|
select(fields, distinct = false) {
|
|
75
|
+
this.ensureNotFinalized();
|
|
63
76
|
this._fields = core_1.Utils.asArray(fields);
|
|
64
77
|
if (distinct) {
|
|
65
78
|
this.flags.add(core_1.QueryFlag.DISTINCT);
|
|
@@ -67,11 +80,22 @@ class QueryBuilder {
|
|
|
67
80
|
return this.init(enums_1.QueryType.SELECT);
|
|
68
81
|
}
|
|
69
82
|
addSelect(fields) {
|
|
83
|
+
this.ensureNotFinalized();
|
|
70
84
|
if (this.type && this.type !== enums_1.QueryType.SELECT) {
|
|
71
85
|
return this;
|
|
72
86
|
}
|
|
73
87
|
return this.select([...core_1.Utils.asArray(this._fields), ...core_1.Utils.asArray(fields)]);
|
|
74
88
|
}
|
|
89
|
+
distinct() {
|
|
90
|
+
this.ensureNotFinalized();
|
|
91
|
+
return this.setFlag(core_1.QueryFlag.DISTINCT);
|
|
92
|
+
}
|
|
93
|
+
/** postgres only */
|
|
94
|
+
distinctOn(fields) {
|
|
95
|
+
this.ensureNotFinalized();
|
|
96
|
+
this._distinctOn = core_1.Utils.asArray(fields);
|
|
97
|
+
return this;
|
|
98
|
+
}
|
|
75
99
|
insert(data) {
|
|
76
100
|
return this.init(enums_1.QueryType.INSERT, data);
|
|
77
101
|
}
|
|
@@ -85,7 +109,15 @@ class QueryBuilder {
|
|
|
85
109
|
return this.init(enums_1.QueryType.TRUNCATE);
|
|
86
110
|
}
|
|
87
111
|
count(field, distinct = false) {
|
|
88
|
-
|
|
112
|
+
if (field) {
|
|
113
|
+
this._fields = core_1.Utils.asArray(field);
|
|
114
|
+
}
|
|
115
|
+
else if (this.hasToManyJoins()) {
|
|
116
|
+
this._fields = this.mainAlias.metadata.primaryKeys;
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
this._fields = [this.raw('*')];
|
|
120
|
+
}
|
|
89
121
|
if (distinct) {
|
|
90
122
|
this.flags.add(core_1.QueryFlag.DISTINCT);
|
|
91
123
|
}
|
|
@@ -99,6 +131,9 @@ class QueryBuilder {
|
|
|
99
131
|
return this.join(field, alias, cond, 'leftJoin');
|
|
100
132
|
}
|
|
101
133
|
joinAndSelect(field, alias, cond = {}, type = 'innerJoin', path) {
|
|
134
|
+
if (!this.type) {
|
|
135
|
+
this.select('*');
|
|
136
|
+
}
|
|
102
137
|
const prop = this.joinReference(field, alias, cond, type, path);
|
|
103
138
|
this.addSelect(this.getFieldsForJoinedLoad(prop, alias));
|
|
104
139
|
const [fromAlias] = this.helper.splitField(field);
|
|
@@ -124,24 +159,34 @@ class QueryBuilder {
|
|
|
124
159
|
return fields;
|
|
125
160
|
}
|
|
126
161
|
withSubQuery(subQuery, alias) {
|
|
162
|
+
this.ensureNotFinalized();
|
|
127
163
|
this.subQueries[alias] = subQuery.toString();
|
|
128
164
|
return this;
|
|
129
165
|
}
|
|
130
166
|
where(cond, params, operator) {
|
|
167
|
+
this.ensureNotFinalized();
|
|
131
168
|
if (core_1.Utils.isString(cond)) {
|
|
132
169
|
cond = { [`(${cond})`]: core_1.Utils.asArray(params) };
|
|
133
170
|
operator = operator || '$and';
|
|
134
171
|
}
|
|
135
172
|
else {
|
|
136
|
-
cond = core_1.QueryHelper.processWhere(
|
|
173
|
+
cond = core_1.QueryHelper.processWhere({
|
|
174
|
+
where: cond,
|
|
175
|
+
entityName: this.mainAlias.entityName,
|
|
176
|
+
metadata: this.metadata,
|
|
177
|
+
platform: this.platform,
|
|
178
|
+
aliasMap: this.getAliasMap(),
|
|
179
|
+
aliased: !this.type || [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(this.type),
|
|
180
|
+
convertCustomTypes: this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES),
|
|
181
|
+
});
|
|
137
182
|
}
|
|
138
183
|
const op = operator || params;
|
|
139
184
|
const topLevel = !op || !core_1.Utils.hasObjectKeys(this._cond);
|
|
140
|
-
const criteriaNode = CriteriaNodeFactory_1.CriteriaNodeFactory.createNode(this.metadata, this.entityName, cond);
|
|
185
|
+
const criteriaNode = CriteriaNodeFactory_1.CriteriaNodeFactory.createNode(this.metadata, this.mainAlias.entityName, cond);
|
|
141
186
|
if ([enums_1.QueryType.UPDATE, enums_1.QueryType.DELETE].includes(this.type) && criteriaNode.willAutoJoin(this)) {
|
|
142
187
|
// use sub-query to support joining
|
|
143
188
|
this.setFlag(this.type === enums_1.QueryType.UPDATE ? core_1.QueryFlag.UPDATE_SUB_QUERY : core_1.QueryFlag.DELETE_SUB_QUERY);
|
|
144
|
-
this.select(this.metadata.
|
|
189
|
+
this.select(this.mainAlias.metadata.primaryKeys, true);
|
|
145
190
|
}
|
|
146
191
|
if (topLevel) {
|
|
147
192
|
this._cond = criteriaNode.process(this);
|
|
@@ -166,27 +211,39 @@ class QueryBuilder {
|
|
|
166
211
|
return this.where(cond, params, '$or');
|
|
167
212
|
}
|
|
168
213
|
orderBy(orderBy) {
|
|
214
|
+
this.ensureNotFinalized();
|
|
169
215
|
this._orderBy = [];
|
|
170
216
|
core_1.Utils.asArray(orderBy).forEach(o => {
|
|
171
|
-
const processed = core_1.QueryHelper.processWhere(
|
|
172
|
-
|
|
217
|
+
const processed = core_1.QueryHelper.processWhere({
|
|
218
|
+
where: o,
|
|
219
|
+
entityName: this.mainAlias.entityName,
|
|
220
|
+
metadata: this.metadata,
|
|
221
|
+
platform: this.platform,
|
|
222
|
+
aliasMap: this.getAliasMap(),
|
|
223
|
+
aliased: !this.type || [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(this.type),
|
|
224
|
+
convertCustomTypes: false,
|
|
225
|
+
});
|
|
226
|
+
this._orderBy.push(CriteriaNodeFactory_1.CriteriaNodeFactory.createNode(this.metadata, this.mainAlias.entityName, processed).process(this));
|
|
173
227
|
});
|
|
174
228
|
return this;
|
|
175
229
|
}
|
|
176
230
|
groupBy(fields) {
|
|
231
|
+
this.ensureNotFinalized();
|
|
177
232
|
this._groupBy = core_1.Utils.asArray(fields);
|
|
178
233
|
return this;
|
|
179
234
|
}
|
|
180
235
|
having(cond = {}, params) {
|
|
236
|
+
this.ensureNotFinalized();
|
|
181
237
|
if (core_1.Utils.isString(cond)) {
|
|
182
238
|
cond = { [`(${cond})`]: core_1.Utils.asArray(params) };
|
|
183
239
|
}
|
|
184
|
-
this._having = CriteriaNodeFactory_1.CriteriaNodeFactory.createNode(this.metadata, this.entityName, cond).process(this);
|
|
240
|
+
this._having = CriteriaNodeFactory_1.CriteriaNodeFactory.createNode(this.metadata, this.mainAlias.entityName, cond).process(this);
|
|
185
241
|
return this;
|
|
186
242
|
}
|
|
187
243
|
onConflict(fields = []) {
|
|
244
|
+
this.ensureNotFinalized();
|
|
188
245
|
this._onConflict = this._onConflict || [];
|
|
189
|
-
this._onConflict.push({ fields: core_1.Utils.asArray(fields) });
|
|
246
|
+
this._onConflict.push({ fields: core_1.Utils.asArray(fields).map(f => f.toString()) });
|
|
190
247
|
return this;
|
|
191
248
|
}
|
|
192
249
|
ignore() {
|
|
@@ -200,14 +257,19 @@ class QueryBuilder {
|
|
|
200
257
|
if (!this._onConflict) {
|
|
201
258
|
throw new Error('You need to call `qb.onConflict()` first to use `qb.merge()`');
|
|
202
259
|
}
|
|
260
|
+
if (Array.isArray(data) && data.length === 0) {
|
|
261
|
+
return this.ignore();
|
|
262
|
+
}
|
|
203
263
|
this._onConflict[this._onConflict.length - 1].merge = data;
|
|
204
264
|
return this;
|
|
205
265
|
}
|
|
206
266
|
/**
|
|
207
267
|
* @internal
|
|
208
268
|
*/
|
|
209
|
-
populate(populate) {
|
|
269
|
+
populate(populate, populateWhere) {
|
|
270
|
+
this.ensureNotFinalized();
|
|
210
271
|
this._populate = populate;
|
|
272
|
+
this._populateWhere = populateWhere;
|
|
211
273
|
return this;
|
|
212
274
|
}
|
|
213
275
|
/**
|
|
@@ -222,6 +284,7 @@ class QueryBuilder {
|
|
|
222
284
|
return raw;
|
|
223
285
|
}
|
|
224
286
|
limit(limit, offset = 0) {
|
|
287
|
+
this.ensureNotFinalized();
|
|
225
288
|
this._limit = limit;
|
|
226
289
|
if (offset) {
|
|
227
290
|
this.offset(offset);
|
|
@@ -229,14 +292,17 @@ class QueryBuilder {
|
|
|
229
292
|
return this;
|
|
230
293
|
}
|
|
231
294
|
offset(offset) {
|
|
295
|
+
this.ensureNotFinalized();
|
|
232
296
|
this._offset = offset;
|
|
233
297
|
return this;
|
|
234
298
|
}
|
|
235
299
|
withSchema(schema) {
|
|
300
|
+
this.ensureNotFinalized();
|
|
236
301
|
this._schema = schema;
|
|
237
302
|
return this;
|
|
238
303
|
}
|
|
239
304
|
setLockMode(mode, tables) {
|
|
305
|
+
this.ensureNotFinalized();
|
|
240
306
|
if (mode != null && mode !== core_1.LockMode.OPTIMISTIC && !this.context) {
|
|
241
307
|
throw core_1.ValidationError.transactionRequired();
|
|
242
308
|
}
|
|
@@ -245,18 +311,22 @@ class QueryBuilder {
|
|
|
245
311
|
return this;
|
|
246
312
|
}
|
|
247
313
|
setFlushMode(flushMode) {
|
|
314
|
+
this.ensureNotFinalized();
|
|
248
315
|
this.flushMode = flushMode;
|
|
249
316
|
return this;
|
|
250
317
|
}
|
|
251
318
|
setFlag(flag) {
|
|
319
|
+
this.ensureNotFinalized();
|
|
252
320
|
this.flags.add(flag);
|
|
253
321
|
return this;
|
|
254
322
|
}
|
|
255
323
|
unsetFlag(flag) {
|
|
324
|
+
this.ensureNotFinalized();
|
|
256
325
|
this.flags.delete(flag);
|
|
257
326
|
return this;
|
|
258
327
|
}
|
|
259
328
|
cache(config = true) {
|
|
329
|
+
this.ensureNotFinalized();
|
|
260
330
|
this._cache = config;
|
|
261
331
|
return this;
|
|
262
332
|
}
|
|
@@ -264,31 +334,47 @@ class QueryBuilder {
|
|
|
264
334
|
* Adds index hint to the FROM clause.
|
|
265
335
|
*/
|
|
266
336
|
indexHint(sql) {
|
|
337
|
+
this.ensureNotFinalized();
|
|
267
338
|
this._indexHint = sql;
|
|
268
339
|
return this;
|
|
269
340
|
}
|
|
341
|
+
from(target, aliasName) {
|
|
342
|
+
this.ensureNotFinalized();
|
|
343
|
+
if (target instanceof QueryBuilder) {
|
|
344
|
+
this.fromSubQuery(target, aliasName);
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
const entityName = core_1.Utils.className(target);
|
|
348
|
+
if (aliasName && this._mainAlias && entityName !== this._mainAlias.aliasName) {
|
|
349
|
+
throw new Error(`Cannot override the alias to '${aliasName}' since a query already contains references to '${this._mainAlias.aliasName}'`);
|
|
350
|
+
}
|
|
351
|
+
this.fromEntityName(entityName, aliasName);
|
|
352
|
+
}
|
|
353
|
+
return this;
|
|
354
|
+
}
|
|
270
355
|
getKnexQuery() {
|
|
271
356
|
this.finalize();
|
|
272
357
|
const qb = this.getQueryBase();
|
|
273
|
-
core_1.Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type, this._cond, qb), this._cond && !this._onConflict);
|
|
358
|
+
core_1.Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type ?? enums_1.QueryType.SELECT, this._cond, qb), this._cond && !this._onConflict);
|
|
274
359
|
core_1.Utils.runIfNotEmpty(() => qb.groupBy(this.prepareFields(this._groupBy, 'groupBy')), this._groupBy);
|
|
275
|
-
core_1.Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type, this._having, qb, undefined, 'having'), this._having);
|
|
360
|
+
core_1.Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type ?? enums_1.QueryType.SELECT, this._having, qb, undefined, 'having'), this._having);
|
|
276
361
|
core_1.Utils.runIfNotEmpty(() => {
|
|
277
|
-
const queryOrder = this.helper.getQueryOrder(this.type, this._orderBy, this._populateMap);
|
|
362
|
+
const queryOrder = this.helper.getQueryOrder(this.type ?? enums_1.QueryType.SELECT, this._orderBy, this._populateMap);
|
|
278
363
|
if (queryOrder) {
|
|
279
|
-
|
|
364
|
+
qb.orderByRaw(queryOrder);
|
|
365
|
+
return;
|
|
280
366
|
}
|
|
281
367
|
}, this._orderBy);
|
|
282
368
|
core_1.Utils.runIfNotEmpty(() => qb.limit(this._limit), this._limit != null);
|
|
283
369
|
core_1.Utils.runIfNotEmpty(() => qb.offset(this._offset), this._offset);
|
|
284
|
-
core_1.Utils.runIfNotEmpty(() => this.helper.appendOnConflictClause(this.type, this._onConflict, qb), this._onConflict);
|
|
370
|
+
core_1.Utils.runIfNotEmpty(() => this.helper.appendOnConflictClause(this.type ?? enums_1.QueryType.SELECT, this._onConflict, qb), this._onConflict);
|
|
285
371
|
if (this.type === enums_1.QueryType.TRUNCATE && this.platform.usesCascadeStatement()) {
|
|
286
372
|
return this.knex.raw(qb.toSQL().toNative().sql + ' cascade');
|
|
287
373
|
}
|
|
288
374
|
if (this.lockMode) {
|
|
289
375
|
this.helper.getLockSQL(qb, this.lockMode, this.lockTables);
|
|
290
376
|
}
|
|
291
|
-
this.helper.finalize(this.type, qb, this.metadata
|
|
377
|
+
this.helper.finalize(this.type ?? enums_1.QueryType.SELECT, qb, this.mainAlias.metadata);
|
|
292
378
|
return qb;
|
|
293
379
|
}
|
|
294
380
|
/**
|
|
@@ -310,9 +396,12 @@ class QueryBuilder {
|
|
|
310
396
|
const query = this.getKnexQuery().toSQL();
|
|
311
397
|
return this.platform.formatQuery(query.sql, query.bindings);
|
|
312
398
|
}
|
|
399
|
+
/**
|
|
400
|
+
* @internal
|
|
401
|
+
*/
|
|
313
402
|
getAliasForJoinPath(path) {
|
|
314
|
-
if (!path || path === this.entityName) {
|
|
315
|
-
return this.
|
|
403
|
+
if (!path || path === this.mainAlias.entityName) {
|
|
404
|
+
return this.mainAlias.aliasName;
|
|
316
405
|
}
|
|
317
406
|
const join = Object.values(this._joins).find(j => j.path === path);
|
|
318
407
|
if (path.endsWith('[pivot]') && join) {
|
|
@@ -320,25 +409,34 @@ class QueryBuilder {
|
|
|
320
409
|
}
|
|
321
410
|
return join?.inverseAlias || join?.alias;
|
|
322
411
|
}
|
|
412
|
+
/**
|
|
413
|
+
* @internal
|
|
414
|
+
*/
|
|
323
415
|
getNextAlias(entityName = 'e') {
|
|
324
416
|
return this.driver.config.getNamingStrategy().aliasName(entityName, this.aliasCounter++);
|
|
325
417
|
}
|
|
418
|
+
/**
|
|
419
|
+
* @internal
|
|
420
|
+
*/
|
|
421
|
+
getAliasMap() {
|
|
422
|
+
return Object.fromEntries(Object.entries(this._aliases).map(([key, value]) => [key, value.entityName]));
|
|
423
|
+
}
|
|
326
424
|
/**
|
|
327
425
|
* Executes this QB and returns the raw results, mapped to the property names (unless disabled via last parameter).
|
|
328
426
|
* Use `method` to specify what kind of result you want to get (array/single/meta).
|
|
329
427
|
*/
|
|
330
428
|
async execute(method = 'all', mapResults = true) {
|
|
331
|
-
if (!this.connectionType && method !== 'run' && [enums_1.QueryType.INSERT, enums_1.QueryType.UPDATE, enums_1.QueryType.DELETE, enums_1.QueryType.TRUNCATE].includes(this.type)) {
|
|
429
|
+
if (!this.connectionType && method !== 'run' && [enums_1.QueryType.INSERT, enums_1.QueryType.UPDATE, enums_1.QueryType.DELETE, enums_1.QueryType.TRUNCATE].includes(this.type ?? enums_1.QueryType.SELECT)) {
|
|
332
430
|
this.connectionType = 'write';
|
|
333
431
|
}
|
|
334
432
|
const query = this.getKnexQuery().toSQL();
|
|
335
|
-
const cached = await this.em?.tryCache(this.entityName, this._cache, ['qb.execute', query.sql, query.bindings, method]);
|
|
433
|
+
const cached = await this.em?.tryCache(this.mainAlias.entityName, this._cache, ['qb.execute', query.sql, query.bindings, method]);
|
|
336
434
|
if (cached?.data) {
|
|
337
435
|
return cached.data;
|
|
338
436
|
}
|
|
339
437
|
const type = this.connectionType || (method === 'run' ? 'write' : 'read');
|
|
340
438
|
const res = await this.driver.getConnection(type).execute(query.sql, query.bindings, method, this.context);
|
|
341
|
-
const meta = this.metadata
|
|
439
|
+
const meta = this.mainAlias.metadata;
|
|
342
440
|
if (!mapResults || !meta) {
|
|
343
441
|
await this.em?.storeCache(this._cache, cached, res);
|
|
344
442
|
return res;
|
|
@@ -363,12 +461,32 @@ class QueryBuilder {
|
|
|
363
461
|
* Executes the query, returning array of results
|
|
364
462
|
*/
|
|
365
463
|
async getResultList() {
|
|
366
|
-
await this.em.tryFlush(this.entityName, { flushMode: this.flushMode });
|
|
464
|
+
await this.em.tryFlush(this.mainAlias.entityName, { flushMode: this.flushMode });
|
|
367
465
|
let res = await this.execute('all', true);
|
|
368
466
|
if (this._joinedProps.size > 0) {
|
|
369
|
-
res = this.driver.mergeJoinedResult(res, this.metadata
|
|
467
|
+
res = this.driver.mergeJoinedResult(res, this.mainAlias.metadata);
|
|
468
|
+
}
|
|
469
|
+
const entities = [];
|
|
470
|
+
function propagatePopulateHint(entity, hint) {
|
|
471
|
+
(0, core_1.helper)(entity).__serializationContext.populate ??= hint;
|
|
472
|
+
hint.forEach(pop => {
|
|
473
|
+
const value = entity[pop.field];
|
|
474
|
+
if (core_1.Utils.isEntity(value, true)) {
|
|
475
|
+
(0, core_1.helper)(value).populated();
|
|
476
|
+
propagatePopulateHint(value, pop.children ?? []);
|
|
477
|
+
}
|
|
478
|
+
else if (core_1.Utils.isCollection(value)) {
|
|
479
|
+
value.populated();
|
|
480
|
+
value.getItems(false).forEach(item => propagatePopulateHint(item, pop.children ?? []));
|
|
481
|
+
}
|
|
482
|
+
});
|
|
370
483
|
}
|
|
371
|
-
|
|
484
|
+
for (const r of res) {
|
|
485
|
+
const entity = this.em.map(this.mainAlias.entityName, r, { schema: this._schema });
|
|
486
|
+
propagatePopulateHint(entity, this._populate);
|
|
487
|
+
entities.push(entity);
|
|
488
|
+
}
|
|
489
|
+
return entities;
|
|
372
490
|
}
|
|
373
491
|
/**
|
|
374
492
|
* Executes the query, returning the first result or null
|
|
@@ -380,35 +498,41 @@ class QueryBuilder {
|
|
|
380
498
|
/**
|
|
381
499
|
* Executes count query (without offset and limit), returning total count of results
|
|
382
500
|
*/
|
|
383
|
-
async getCount(field, distinct
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
501
|
+
async getCount(field, distinct) {
|
|
502
|
+
let res;
|
|
503
|
+
if (this.type === enums_1.QueryType.COUNT) {
|
|
504
|
+
res = await this.execute('get', false);
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
const qb = this.clone();
|
|
508
|
+
qb.count(field, distinct ?? qb.hasToManyJoins()).limit(undefined).offset(undefined).orderBy([]);
|
|
509
|
+
res = await qb.execute('get', false);
|
|
510
|
+
}
|
|
387
511
|
return res ? +res.count : 0;
|
|
388
512
|
}
|
|
513
|
+
/**
|
|
514
|
+
* Executes the query, returning both array of results and total count query (without offset and limit).
|
|
515
|
+
*/
|
|
516
|
+
async getResultAndCount() {
|
|
517
|
+
return Promise.all([
|
|
518
|
+
this.getResultList(),
|
|
519
|
+
this.getCount(),
|
|
520
|
+
]);
|
|
521
|
+
}
|
|
389
522
|
/**
|
|
390
523
|
* Provides promise-like interface so we can await the QB instance.
|
|
391
524
|
*/
|
|
392
525
|
then(onfulfilled, onrejected) {
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
case enums_1.QueryType.TRUNCATE:
|
|
403
|
-
return this.execute('run');
|
|
404
|
-
case enums_1.QueryType.SELECT:
|
|
405
|
-
return this.getResultList();
|
|
406
|
-
case enums_1.QueryType.COUNT:
|
|
407
|
-
return this.getCount();
|
|
408
|
-
}
|
|
409
|
-
})();
|
|
526
|
+
switch (this.type ?? enums_1.QueryType.SELECT) {
|
|
527
|
+
case enums_1.QueryType.INSERT:
|
|
528
|
+
case enums_1.QueryType.UPDATE:
|
|
529
|
+
case enums_1.QueryType.DELETE:
|
|
530
|
+
case enums_1.QueryType.TRUNCATE:
|
|
531
|
+
return this.execute('run').then(onfulfilled, onrejected);
|
|
532
|
+
case enums_1.QueryType.COUNT:
|
|
533
|
+
return this.getCount().then(onfulfilled, onrejected);
|
|
534
|
+
case enums_1.QueryType.SELECT: return this.getResultList().then(onfulfilled, onrejected);
|
|
410
535
|
}
|
|
411
|
-
return this.innerPromise;
|
|
412
536
|
}
|
|
413
537
|
/**
|
|
414
538
|
* Returns knex instance with sub-query aliased with given alias.
|
|
@@ -425,11 +549,11 @@ class QueryBuilder {
|
|
|
425
549
|
return qb.as(alias);
|
|
426
550
|
}
|
|
427
551
|
clone() {
|
|
428
|
-
const qb = new QueryBuilder(this.entityName, this.metadata, this.driver, this.context, this.
|
|
552
|
+
const qb = new QueryBuilder(this.mainAlias.entityName, this.metadata, this.driver, this.context, this.mainAlias.aliasName, this.connectionType, this.em);
|
|
429
553
|
Object.assign(qb, this);
|
|
430
554
|
// clone array/object properties
|
|
431
555
|
const properties = [
|
|
432
|
-
'flags', '_populate', '
|
|
556
|
+
'flags', '_populate', '_populateWhere', '_populateMap', '_joins', '_joinedProps', '_cond', '_data', '_orderBy',
|
|
433
557
|
'_schema', '_indexHint', '_cache', 'subQueries', 'lockMode', 'lockTables',
|
|
434
558
|
];
|
|
435
559
|
properties.forEach(prop => qb[prop] = core_1.Utils.copy(this[prop]));
|
|
@@ -437,44 +561,62 @@ class QueryBuilder {
|
|
|
437
561
|
if (this._fields) {
|
|
438
562
|
qb._fields = [...this._fields];
|
|
439
563
|
}
|
|
564
|
+
qb._aliases = { ...this._aliases };
|
|
440
565
|
qb.finalized = false;
|
|
441
566
|
return qb;
|
|
442
567
|
}
|
|
443
568
|
getKnex() {
|
|
444
|
-
const
|
|
445
|
-
const
|
|
569
|
+
const qb = this.knex.queryBuilder();
|
|
570
|
+
const { subQuery, aliasName, entityName } = this.mainAlias;
|
|
571
|
+
const ref = subQuery ? subQuery : this.knex.ref(this.helper.getTableName(entityName));
|
|
572
|
+
if (this.finalized && (this._explicitAlias || this.helper.isTableNameAliasRequired(this.type))) {
|
|
573
|
+
ref.as(aliasName);
|
|
574
|
+
}
|
|
575
|
+
const schema = this.getSchema(this.mainAlias);
|
|
576
|
+
if (schema) {
|
|
577
|
+
ref.withSchema(schema);
|
|
578
|
+
}
|
|
579
|
+
qb.from(ref);
|
|
446
580
|
if (this.context) {
|
|
447
581
|
qb.transacting(this.context);
|
|
448
582
|
}
|
|
449
583
|
return qb;
|
|
450
584
|
}
|
|
451
585
|
joinReference(field, alias, cond, type, path) {
|
|
586
|
+
this.ensureNotFinalized();
|
|
452
587
|
const [fromAlias, fromField] = this.helper.splitField(field);
|
|
453
|
-
const entityName = this.
|
|
588
|
+
const entityName = this._aliases[fromAlias]?.entityName;
|
|
454
589
|
const meta = this.metadata.get(entityName);
|
|
455
590
|
const prop = meta.properties[fromField];
|
|
456
591
|
if (!prop) {
|
|
457
592
|
throw new Error(`Trying to join ${field}, but ${fromField} is not a defined relation on ${meta.className}`);
|
|
458
593
|
}
|
|
459
|
-
this.
|
|
460
|
-
cond = core_1.QueryHelper.processWhere(
|
|
594
|
+
this.createAlias(prop.type, alias);
|
|
595
|
+
cond = core_1.QueryHelper.processWhere({
|
|
596
|
+
where: cond,
|
|
597
|
+
entityName: this.mainAlias.entityName,
|
|
598
|
+
metadata: this.metadata,
|
|
599
|
+
platform: this.platform,
|
|
600
|
+
aliasMap: this.getAliasMap(),
|
|
601
|
+
aliased: !this.type || [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(this.type),
|
|
602
|
+
});
|
|
461
603
|
let aliasedName = `${fromAlias}.${prop.name}#${alias}`;
|
|
462
|
-
path
|
|
463
|
-
if (prop.
|
|
604
|
+
path ??= `${(Object.values(this._joins).find(j => j.alias === fromAlias)?.path ?? entityName)}.${prop.name}`;
|
|
605
|
+
if (prop.kind === core_1.ReferenceKind.ONE_TO_MANY) {
|
|
464
606
|
this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond);
|
|
465
607
|
}
|
|
466
|
-
else if (prop.
|
|
608
|
+
else if (prop.kind === core_1.ReferenceKind.MANY_TO_MANY) {
|
|
467
609
|
let pivotAlias = alias;
|
|
468
610
|
if (type !== 'pivotJoin') {
|
|
469
611
|
const oldPivotAlias = this.getAliasForJoinPath(path + '[pivot]');
|
|
470
|
-
pivotAlias = oldPivotAlias ?? this.getNextAlias(prop.
|
|
612
|
+
pivotAlias = oldPivotAlias ?? this.getNextAlias(prop.pivotEntity);
|
|
471
613
|
aliasedName = `${fromAlias}.${prop.name}#${pivotAlias}`;
|
|
472
614
|
}
|
|
473
615
|
const joins = this.helper.joinManyToManyReference(prop, fromAlias, alias, pivotAlias, type, cond, path);
|
|
474
616
|
Object.assign(this._joins, joins);
|
|
475
|
-
this.
|
|
617
|
+
this.createAlias(prop.pivotEntity, pivotAlias);
|
|
476
618
|
}
|
|
477
|
-
else if (prop.
|
|
619
|
+
else if (prop.kind === core_1.ReferenceKind.ONE_TO_ONE) {
|
|
478
620
|
this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond);
|
|
479
621
|
}
|
|
480
622
|
else { // MANY_TO_ONE
|
|
@@ -489,30 +631,58 @@ class QueryBuilder {
|
|
|
489
631
|
const ret = [];
|
|
490
632
|
fields.forEach(field => {
|
|
491
633
|
if (!core_1.Utils.isString(field)) {
|
|
492
|
-
|
|
634
|
+
ret.push(field);
|
|
635
|
+
return;
|
|
493
636
|
}
|
|
494
637
|
const join = Object.keys(this._joins).find(k => field === k.substring(0, k.indexOf('#')));
|
|
495
638
|
if (join && type === 'where') {
|
|
496
|
-
|
|
639
|
+
ret.push(...this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[join]));
|
|
640
|
+
return;
|
|
497
641
|
}
|
|
498
642
|
const [a, f] = this.helper.splitField(field);
|
|
499
643
|
const prop = this.helper.getProperty(f, a);
|
|
500
|
-
|
|
644
|
+
/* istanbul ignore next */
|
|
645
|
+
if (prop && [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
if (prop?.embedded) {
|
|
649
|
+
const fieldName = this.helper.mapper(prop.fieldNames[0], this.type);
|
|
650
|
+
ret.push(fieldName);
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
if (prop?.kind === core_1.ReferenceKind.EMBEDDED) {
|
|
654
|
+
if (prop.object) {
|
|
655
|
+
ret.push(this.helper.mapper(prop.fieldNames[0], this.type));
|
|
656
|
+
}
|
|
657
|
+
else {
|
|
658
|
+
const nest = (prop) => {
|
|
659
|
+
for (const childProp of Object.values(prop.embeddedProps)) {
|
|
660
|
+
if (childProp.fieldNames) {
|
|
661
|
+
ret.push(this.helper.mapper(childProp.fieldNames[0], this.type));
|
|
662
|
+
}
|
|
663
|
+
else {
|
|
664
|
+
nest(childProp);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
nest(prop);
|
|
669
|
+
}
|
|
501
670
|
return;
|
|
502
671
|
}
|
|
503
672
|
ret.push(this.helper.mapper(field, this.type));
|
|
504
673
|
});
|
|
505
|
-
const meta = this.metadata
|
|
674
|
+
const meta = this.mainAlias.metadata;
|
|
506
675
|
/* istanbul ignore next */
|
|
507
676
|
const requiresSQLConversion = meta?.props.filter(p => p.customType?.convertToJSValueSQL) ?? [];
|
|
508
|
-
if (this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES) && (fields.includes('*') || fields.includes(`${this.
|
|
677
|
+
if (this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES) && (fields.includes('*') || fields.includes(`${this.mainAlias.aliasName}.*`)) && requiresSQLConversion.length > 0) {
|
|
509
678
|
requiresSQLConversion.forEach(p => ret.push(this.helper.mapper(p.name, this.type)));
|
|
510
679
|
}
|
|
511
680
|
Object.keys(this._populateMap).forEach(f => {
|
|
512
681
|
if (!fields.includes(f.replace(/#\w+$/, '')) && type === 'where') {
|
|
513
|
-
|
|
682
|
+
const cols = this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[f]);
|
|
683
|
+
ret.push(...cols);
|
|
514
684
|
}
|
|
515
|
-
if (this._joins[f].prop.
|
|
685
|
+
if (this._joins[f].prop.kind !== core_1.ReferenceKind.ONE_TO_ONE && this._joins[f].inverseJoinColumns) {
|
|
516
686
|
this._joins[f].inverseJoinColumns.forEach(inverseJoinColumn => {
|
|
517
687
|
core_1.Utils.renameKey(this._cond, inverseJoinColumn, `${this._joins[f].alias}.${inverseJoinColumn}`);
|
|
518
688
|
});
|
|
@@ -521,8 +691,8 @@ class QueryBuilder {
|
|
|
521
691
|
return ret;
|
|
522
692
|
}
|
|
523
693
|
init(type, data, cond) {
|
|
694
|
+
this.ensureNotFinalized();
|
|
524
695
|
this.type = type;
|
|
525
|
-
this._aliasMap[this.alias] = this.entityName;
|
|
526
696
|
if ([enums_1.QueryType.UPDATE, enums_1.QueryType.DELETE].includes(type) && core_1.Utils.hasObjectKeys(this._cond)) {
|
|
527
697
|
throw new Error(`You are trying to call \`qb.where().${type.toLowerCase()}()\`. Calling \`qb.${type.toLowerCase()}()\` before \`qb.where()\` is required.`);
|
|
528
698
|
}
|
|
@@ -530,6 +700,9 @@ class QueryBuilder {
|
|
|
530
700
|
delete this._fields;
|
|
531
701
|
}
|
|
532
702
|
if (data) {
|
|
703
|
+
if (core_1.Utils.isEntity(data)) {
|
|
704
|
+
data = this.em?.getComparator().prepareEntity(data) ?? (0, core_1.serialize)(data);
|
|
705
|
+
}
|
|
533
706
|
this._data = this.helper.processData(data, this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES));
|
|
534
707
|
}
|
|
535
708
|
if (cond) {
|
|
@@ -539,22 +712,23 @@ class QueryBuilder {
|
|
|
539
712
|
}
|
|
540
713
|
getQueryBase() {
|
|
541
714
|
const qb = this.getKnex();
|
|
542
|
-
const
|
|
543
|
-
const metaSchema = meta?.schema && meta.schema !== '*' ? meta.schema : undefined;
|
|
544
|
-
const schema = this._schema ?? metaSchema ?? this.em?.config.get('schema');
|
|
715
|
+
const schema = this.getSchema(this.mainAlias);
|
|
545
716
|
if (schema) {
|
|
546
717
|
qb.withSchema(schema);
|
|
547
718
|
}
|
|
548
719
|
if (this._indexHint) {
|
|
549
|
-
const alias = this.helper.isTableNameAliasRequired(this.type) ? ` as ${this.platform.quoteIdentifier(this.
|
|
720
|
+
const alias = this.helper.isTableNameAliasRequired(this.type) ? ` as ${this.platform.quoteIdentifier(this.mainAlias.aliasName)}` : '';
|
|
550
721
|
const schemaQuoted = schema ? this.platform.quoteIdentifier(schema) + '.' : '';
|
|
551
|
-
const tableName = schemaQuoted + this.platform.quoteIdentifier(this.helper.getTableName(this.entityName)) + alias;
|
|
722
|
+
const tableName = schemaQuoted + this.platform.quoteIdentifier(this.helper.getTableName(this.mainAlias.entityName)) + alias;
|
|
552
723
|
qb.from(this.knex.raw(`${tableName} ${this._indexHint}`));
|
|
553
724
|
}
|
|
554
725
|
switch (this.type) {
|
|
555
726
|
case enums_1.QueryType.SELECT:
|
|
556
727
|
qb.select(this.prepareFields(this._fields));
|
|
557
|
-
if (this.
|
|
728
|
+
if (this._distinctOn) {
|
|
729
|
+
qb.distinctOn(this._distinctOn);
|
|
730
|
+
}
|
|
731
|
+
else if (this.flags.has(core_1.QueryFlag.DISTINCT)) {
|
|
558
732
|
qb.distinct();
|
|
559
733
|
}
|
|
560
734
|
this.helper.processJoins(qb, this._joins, schema);
|
|
@@ -588,11 +762,11 @@ class QueryBuilder {
|
|
|
588
762
|
if (!this.type) {
|
|
589
763
|
this.select('*');
|
|
590
764
|
}
|
|
591
|
-
const meta = this.metadata
|
|
765
|
+
const meta = this.mainAlias.metadata;
|
|
592
766
|
if (meta && this.flags.has(core_1.QueryFlag.AUTO_JOIN_ONE_TO_ONE_OWNER)) {
|
|
593
767
|
const relationsToPopulate = this._populate.map(({ field }) => field);
|
|
594
768
|
meta.relations
|
|
595
|
-
.filter(prop => prop.
|
|
769
|
+
.filter(prop => prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
|
|
596
770
|
.map(prop => ({ field: prop.name }))
|
|
597
771
|
.forEach(item => this._populate.push(item));
|
|
598
772
|
}
|
|
@@ -600,44 +774,92 @@ class QueryBuilder {
|
|
|
600
774
|
const [fromAlias, fromField] = this.helper.splitField(field);
|
|
601
775
|
const aliasedField = `${fromAlias}.${fromField}`;
|
|
602
776
|
const join = Object.keys(this._joins).find(k => `${aliasedField}#${this._joins[k].alias}` === k);
|
|
603
|
-
if (this._joins[join] && this.helper.isOneToOneInverse(fromField)) {
|
|
604
|
-
|
|
777
|
+
if (join && this._joins[join] && this.helper.isOneToOneInverse(fromField)) {
|
|
778
|
+
this._populateMap[join] = this._joins[join].alias;
|
|
779
|
+
return;
|
|
605
780
|
}
|
|
606
781
|
if (this.metadata.find(field)?.pivotTable) { // pivot table entity
|
|
607
782
|
this.autoJoinPivotTable(field);
|
|
608
783
|
}
|
|
609
784
|
else if (meta && this.helper.isOneToOneInverse(fromField)) {
|
|
610
785
|
const prop = meta.properties[fromField];
|
|
611
|
-
const alias = this.getNextAlias(prop.
|
|
612
|
-
|
|
613
|
-
this.
|
|
786
|
+
const alias = this.getNextAlias(prop.pivotEntity ?? prop.type);
|
|
787
|
+
const aliasedName = `${fromAlias}.${prop.name}#${alias}`;
|
|
788
|
+
this._joins[aliasedName] = this.helper.joinOneToReference(prop, this.mainAlias.aliasName, alias, 'leftJoin');
|
|
789
|
+
this._populateMap[aliasedName] = this._joins[aliasedName].alias;
|
|
614
790
|
}
|
|
615
791
|
});
|
|
616
|
-
if (meta && (this._fields?.includes('*') || this._fields?.includes(`${this.
|
|
792
|
+
if (meta && (this._fields?.includes('*') || this._fields?.includes(`${this.mainAlias.aliasName}.*`))) {
|
|
617
793
|
meta.props
|
|
618
794
|
.filter(prop => prop.formula && (!prop.lazy || this.flags.has(core_1.QueryFlag.INCLUDE_LAZY_FORMULAS)))
|
|
619
795
|
.map(prop => {
|
|
620
|
-
const alias = this.knex.ref(this.
|
|
796
|
+
const alias = this.knex.ref(this.mainAlias.aliasName).toString();
|
|
621
797
|
const aliased = this.knex.ref(prop.fieldNames[0]).toString();
|
|
622
798
|
return `${prop.formula(alias)} as ${aliased}`;
|
|
623
799
|
})
|
|
624
800
|
.filter(field => !this._fields.includes(field))
|
|
625
801
|
.forEach(field => this.addSelect(field));
|
|
626
802
|
}
|
|
803
|
+
this.processPopulateWhere();
|
|
627
804
|
core_1.QueryHelper.processObjectParams(this._data);
|
|
628
805
|
core_1.QueryHelper.processObjectParams(this._cond);
|
|
629
806
|
core_1.QueryHelper.processObjectParams(this._having);
|
|
630
|
-
|
|
631
|
-
if (
|
|
807
|
+
// automatically enable paginate flag when we detect to-many joins
|
|
808
|
+
if (!this.flags.has(core_1.QueryFlag.DISABLE_PAGINATE) && this.hasToManyJoins()) {
|
|
809
|
+
this.flags.add(core_1.QueryFlag.PAGINATE);
|
|
810
|
+
}
|
|
811
|
+
if (meta && this.flags.has(core_1.QueryFlag.PAGINATE) && (this._limit > 0 || this._offset > 0)) {
|
|
632
812
|
this.wrapPaginateSubQuery(meta);
|
|
633
813
|
}
|
|
634
814
|
if (meta && (this.flags.has(core_1.QueryFlag.UPDATE_SUB_QUERY) || this.flags.has(core_1.QueryFlag.DELETE_SUB_QUERY))) {
|
|
635
815
|
this.wrapModifySubQuery(meta);
|
|
636
816
|
}
|
|
817
|
+
this.finalized = true;
|
|
818
|
+
}
|
|
819
|
+
processPopulateWhere() {
|
|
820
|
+
if (this._populateWhere == null || this._populateWhere === core_1.PopulateHint.ALL) {
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
const joins = Object.values(this._joins);
|
|
824
|
+
joins.forEach(join => {
|
|
825
|
+
join.cond_ = join.cond;
|
|
826
|
+
join.cond = {};
|
|
827
|
+
});
|
|
828
|
+
const replaceOnConditions = (cond) => {
|
|
829
|
+
Object.keys(cond).forEach(k => {
|
|
830
|
+
if (core_1.Utils.isOperator(k)) {
|
|
831
|
+
if (Array.isArray(cond[k])) {
|
|
832
|
+
return cond[k].forEach((c) => replaceOnConditions(c));
|
|
833
|
+
}
|
|
834
|
+
return replaceOnConditions(cond[k]);
|
|
835
|
+
}
|
|
836
|
+
const [a] = this.helper.splitField(k);
|
|
837
|
+
const join = joins.find(j => j.alias === a);
|
|
838
|
+
if (join) {
|
|
839
|
+
join.cond = { ...join.cond, [k]: cond[k] };
|
|
840
|
+
}
|
|
841
|
+
});
|
|
842
|
+
};
|
|
843
|
+
if (this._populateWhere === core_1.PopulateHint.INFER) {
|
|
844
|
+
replaceOnConditions(this._cond);
|
|
845
|
+
}
|
|
846
|
+
else if (typeof this._populateWhere === 'object') {
|
|
847
|
+
const cond = CriteriaNodeFactory_1.CriteriaNodeFactory
|
|
848
|
+
.createNode(this.metadata, this.mainAlias.entityName, this._populateWhere)
|
|
849
|
+
.process(this);
|
|
850
|
+
replaceOnConditions(cond);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
hasToManyJoins() {
|
|
854
|
+
return Object.values(this._joins).some(join => {
|
|
855
|
+
return [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(join.prop.kind);
|
|
856
|
+
});
|
|
637
857
|
}
|
|
638
858
|
wrapPaginateSubQuery(meta) {
|
|
639
859
|
const pks = this.prepareFields(meta.primaryKeys, 'sub-query');
|
|
640
860
|
const subQuery = this.clone().select(pks).groupBy(pks).limit(this._limit);
|
|
861
|
+
// revert the on conditions added via populateWhere, we want to apply those only once
|
|
862
|
+
Object.values(subQuery._joins).forEach(join => join.cond = join.cond_ ?? {});
|
|
641
863
|
if (this._offset) {
|
|
642
864
|
subQuery.offset(this._offset);
|
|
643
865
|
}
|
|
@@ -645,15 +867,18 @@ class QueryBuilder {
|
|
|
645
867
|
const orderBy = [];
|
|
646
868
|
for (const orderMap of this._orderBy) {
|
|
647
869
|
for (const [field, direction] of Object.entries(orderMap)) {
|
|
870
|
+
const [a, f] = this.helper.splitField(field);
|
|
871
|
+
const prop = this.helper.getProperty(f, a);
|
|
872
|
+
const type = this.platform.castColumn(prop);
|
|
648
873
|
orderBy.push({
|
|
649
|
-
[`min(${this.ref(field)})`]: direction,
|
|
874
|
+
[`min(${this.ref(this.helper.mapper(field, this.type, undefined, null))}${type})`]: direction,
|
|
650
875
|
});
|
|
651
876
|
}
|
|
652
877
|
}
|
|
653
878
|
subQuery.orderBy(orderBy);
|
|
654
879
|
}
|
|
655
880
|
subQuery.finalized = true;
|
|
656
|
-
const knexQuery = subQuery.as(this.
|
|
881
|
+
const knexQuery = subQuery.as(this.mainAlias.aliasName).clearSelect().select(pks);
|
|
657
882
|
// multiple sub-queries are needed to get around mysql limitations with order by + limit + where in + group by (o.O)
|
|
658
883
|
// https://stackoverflow.com/questions/17892762/mysql-this-version-of-mysql-doesnt-yet-support-limit-in-all-any-some-subqu
|
|
659
884
|
const subSubQuery = this.getKnex().select(pks).from(knexQuery);
|
|
@@ -666,7 +891,7 @@ class QueryBuilder {
|
|
|
666
891
|
subQuery.finalized = true;
|
|
667
892
|
// wrap one more time to get around MySQL limitations
|
|
668
893
|
// https://stackoverflow.com/questions/45494/mysql-error-1093-cant-specify-target-table-for-update-in-from-clause
|
|
669
|
-
const subSubQuery = this.getKnex().select(this.prepareFields(meta.primaryKeys)).from(subQuery.as(this.
|
|
894
|
+
const subSubQuery = this.getKnex().select(this.prepareFields(meta.primaryKeys)).from(subQuery.as(this.mainAlias.aliasName));
|
|
670
895
|
const method = this.flags.has(core_1.QueryFlag.UPDATE_SUB_QUERY) ? 'update' : 'delete';
|
|
671
896
|
this._cond = {}; // otherwise we would trigger validation error
|
|
672
897
|
this[method](this._data).where({
|
|
@@ -675,14 +900,76 @@ class QueryBuilder {
|
|
|
675
900
|
}
|
|
676
901
|
autoJoinPivotTable(field) {
|
|
677
902
|
const pivotMeta = this.metadata.find(field);
|
|
678
|
-
const owner = pivotMeta.
|
|
679
|
-
const inverse = pivotMeta.
|
|
903
|
+
const owner = pivotMeta.relations[0];
|
|
904
|
+
const inverse = pivotMeta.relations[1];
|
|
680
905
|
const prop = this._cond[pivotMeta.name + '.' + owner.name] || this._orderBy[pivotMeta.name + '.' + owner.name] ? inverse : owner;
|
|
681
906
|
const pivotAlias = this.getNextAlias(pivotMeta.name);
|
|
682
|
-
this._joins[field] = this.helper.joinPivotTable(field, prop, this.
|
|
907
|
+
this._joins[field] = this.helper.joinPivotTable(field, prop, this.mainAlias.aliasName, pivotAlias, 'leftJoin');
|
|
683
908
|
core_1.Utils.renameKey(this._cond, `${field}.${owner.name}`, core_1.Utils.getPrimaryKeyHash(owner.fieldNames.map(fieldName => `${pivotAlias}.${fieldName}`)));
|
|
684
909
|
core_1.Utils.renameKey(this._cond, `${field}.${inverse.name}`, core_1.Utils.getPrimaryKeyHash(inverse.fieldNames.map(fieldName => `${pivotAlias}.${fieldName}`)));
|
|
685
910
|
this._populateMap[field] = this._joins[field].alias;
|
|
686
911
|
}
|
|
912
|
+
getSchema(alias) {
|
|
913
|
+
const { metadata } = alias;
|
|
914
|
+
const metaSchema = metadata?.schema && metadata.schema !== '*' ? metadata.schema : undefined;
|
|
915
|
+
return this._schema ?? metaSchema ?? this.em?.config.get('schema');
|
|
916
|
+
}
|
|
917
|
+
createAlias(entityName, aliasName, subQuery) {
|
|
918
|
+
const metadata = this.metadata.find(entityName);
|
|
919
|
+
const alias = { aliasName, entityName, metadata, subQuery };
|
|
920
|
+
this._aliases[aliasName] = alias;
|
|
921
|
+
return alias;
|
|
922
|
+
}
|
|
923
|
+
createMainAlias(entityName, aliasName, subQuery) {
|
|
924
|
+
this._mainAlias = this.createAlias(entityName, aliasName, subQuery);
|
|
925
|
+
this._helper = this.createQueryBuilderHelper();
|
|
926
|
+
return this._mainAlias;
|
|
927
|
+
}
|
|
928
|
+
fromSubQuery(target, aliasName) {
|
|
929
|
+
const subQuery = target.getKnexQuery();
|
|
930
|
+
const { entityName } = target.mainAlias;
|
|
931
|
+
aliasName ??= this.getNextAlias(entityName);
|
|
932
|
+
this.createMainAlias(entityName, aliasName, subQuery);
|
|
933
|
+
}
|
|
934
|
+
fromEntityName(entityName, aliasName) {
|
|
935
|
+
aliasName ??= this._mainAlias?.aliasName ?? this.getNextAlias(entityName);
|
|
936
|
+
this.createMainAlias(entityName, aliasName);
|
|
937
|
+
}
|
|
938
|
+
createQueryBuilderHelper() {
|
|
939
|
+
return new QueryBuilderHelper_1.QueryBuilderHelper(this.mainAlias.entityName, this.mainAlias.aliasName, this._aliases, this.subQueries, this.knex, this.driver);
|
|
940
|
+
}
|
|
941
|
+
ensureFromClause() {
|
|
942
|
+
/* istanbul ignore next */
|
|
943
|
+
if (!this._mainAlias) {
|
|
944
|
+
throw new Error(`Cannot proceed to build a query because the main alias is not set.`);
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
ensureNotFinalized() {
|
|
948
|
+
if (this.finalized) {
|
|
949
|
+
throw new Error('This QueryBuilder instance is already finalized, clone it first if you want to modify it.');
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
/* istanbul ignore next */
|
|
953
|
+
[util_1.inspect.custom](depth) {
|
|
954
|
+
const object = { ...this };
|
|
955
|
+
const hidden = ['metadata', 'driver', 'context', 'platform', 'knex', 'type'];
|
|
956
|
+
Object.keys(object).filter(k => k.startsWith('_')).forEach(k => delete object[k]);
|
|
957
|
+
Object.keys(object).filter(k => object[k] == null).forEach(k => delete object[k]);
|
|
958
|
+
hidden.forEach(k => delete object[k]);
|
|
959
|
+
let prefix = this.type ? this.type.substring(0, 1) + this.type.toLowerCase().substring(1) : '';
|
|
960
|
+
if (this._data) {
|
|
961
|
+
object.data = this._data;
|
|
962
|
+
}
|
|
963
|
+
if (!core_1.Utils.isEmpty(this._cond)) {
|
|
964
|
+
object.where = this._cond;
|
|
965
|
+
}
|
|
966
|
+
if (this._onConflict?.[0]) {
|
|
967
|
+
prefix = 'Upsert';
|
|
968
|
+
object.onConflict = this._onConflict[0];
|
|
969
|
+
}
|
|
970
|
+
const name = this._mainAlias ? `${prefix}QueryBuilder<${this._mainAlias?.entityName}>` : 'QueryBuilder';
|
|
971
|
+
const ret = (0, util_1.inspect)(object, { depth });
|
|
972
|
+
return ret === '[Object]' ? `[${name}]` : name + ' ' + ret;
|
|
973
|
+
}
|
|
687
974
|
}
|
|
688
975
|
exports.QueryBuilder = QueryBuilder;
|