@mikro-orm/knex 6.0.0-dev.9 → 6.0.0-dev.91
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AbstractSqlConnection.d.ts +5 -2
- package/AbstractSqlConnection.js +25 -17
- package/AbstractSqlDriver.d.ts +19 -18
- package/AbstractSqlDriver.js +171 -70
- package/AbstractSqlPlatform.d.ts +1 -0
- package/AbstractSqlPlatform.js +26 -5
- package/MonkeyPatchable.d.ts +1 -0
- package/MonkeyPatchable.js +3 -0
- package/README.md +64 -107
- package/SqlEntityManager.d.ts +2 -5
- package/SqlEntityManager.js +5 -9
- package/SqlEntityRepository.d.ts +6 -4
- package/SqlEntityRepository.js +10 -8
- package/index.mjs +20 -6
- package/package.json +7 -7
- package/query/ArrayCriteriaNode.d.ts +3 -3
- package/query/CriteriaNode.d.ts +8 -8
- package/query/CriteriaNode.js +9 -9
- package/query/CriteriaNodeFactory.d.ts +6 -6
- package/query/CriteriaNodeFactory.js +10 -13
- package/query/ObjectCriteriaNode.d.ts +3 -3
- package/query/ObjectCriteriaNode.js +12 -8
- package/query/QueryBuilder.d.ts +27 -16
- package/query/QueryBuilder.js +250 -91
- package/query/QueryBuilderHelper.d.ts +6 -9
- package/query/QueryBuilderHelper.js +112 -96
- package/query/ScalarCriteriaNode.d.ts +3 -2
- package/query/ScalarCriteriaNode.js +9 -6
- package/query/enums.js +1 -1
- package/schema/DatabaseSchema.d.ts +4 -1
- package/schema/DatabaseSchema.js +22 -6
- package/schema/DatabaseTable.d.ts +2 -1
- package/schema/DatabaseTable.js +25 -24
- package/schema/SchemaComparator.js +9 -2
- package/schema/SchemaHelper.d.ts +5 -3
- package/schema/SchemaHelper.js +10 -4
- package/schema/SqlSchemaGenerator.d.ts +3 -5
- package/schema/SqlSchemaGenerator.js +41 -20
- package/typings.d.ts +10 -7
package/query/QueryBuilder.js
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
5
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
6
|
+
};
|
|
7
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
8
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
9
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
10
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
11
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
12
|
+
};
|
|
13
|
+
var _QueryBuilder_query;
|
|
2
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
15
|
exports.QueryBuilder = void 0;
|
|
16
|
+
const util_1 = require("util");
|
|
4
17
|
const core_1 = require("@mikro-orm/core");
|
|
5
18
|
const enums_1 = require("./enums");
|
|
6
19
|
const QueryBuilderHelper_1 = require("./QueryBuilderHelper");
|
|
@@ -39,12 +52,13 @@ class QueryBuilder {
|
|
|
39
52
|
/**
|
|
40
53
|
* @internal
|
|
41
54
|
*/
|
|
42
|
-
constructor(entityName, metadata, driver, context, alias, connectionType, em) {
|
|
55
|
+
constructor(entityName, metadata, driver, context, alias, connectionType, em, logging) {
|
|
43
56
|
this.metadata = metadata;
|
|
44
57
|
this.driver = driver;
|
|
45
58
|
this.context = context;
|
|
46
59
|
this.connectionType = connectionType;
|
|
47
60
|
this.em = em;
|
|
61
|
+
this.logging = logging;
|
|
48
62
|
/** @internal */
|
|
49
63
|
this._populate = [];
|
|
50
64
|
/** @internal */
|
|
@@ -63,6 +77,7 @@ class QueryBuilder {
|
|
|
63
77
|
this._aliases = {};
|
|
64
78
|
this.platform = this.driver.getPlatform();
|
|
65
79
|
this.knex = this.driver.getConnection(this.connectionType).getKnex();
|
|
80
|
+
_QueryBuilder_query.set(this, void 0);
|
|
66
81
|
if (alias) {
|
|
67
82
|
this.aliasCounter++;
|
|
68
83
|
this._explicitAlias = true;
|
|
@@ -115,26 +130,26 @@ class QueryBuilder {
|
|
|
115
130
|
this._fields = this.mainAlias.metadata.primaryKeys;
|
|
116
131
|
}
|
|
117
132
|
else {
|
|
118
|
-
this._fields = [
|
|
133
|
+
this._fields = [(0, core_1.raw)('*')];
|
|
119
134
|
}
|
|
120
135
|
if (distinct) {
|
|
121
136
|
this.flags.add(core_1.QueryFlag.DISTINCT);
|
|
122
137
|
}
|
|
123
138
|
return this.init(enums_1.QueryType.COUNT);
|
|
124
139
|
}
|
|
125
|
-
join(field, alias, cond = {}, type = 'innerJoin', path) {
|
|
126
|
-
this.joinReference(field, alias, cond, type, path);
|
|
140
|
+
join(field, alias, cond = {}, type = 'innerJoin', path, schema) {
|
|
141
|
+
this.joinReference(field, alias, cond, type, path, schema);
|
|
127
142
|
return this;
|
|
128
143
|
}
|
|
129
|
-
leftJoin(field, alias, cond = {}) {
|
|
130
|
-
return this.join(field, alias, cond, 'leftJoin');
|
|
144
|
+
leftJoin(field, alias, cond = {}, schema) {
|
|
145
|
+
return this.join(field, alias, cond, 'leftJoin', undefined, schema);
|
|
131
146
|
}
|
|
132
|
-
joinAndSelect(field, alias, cond = {}, type = 'innerJoin', path) {
|
|
147
|
+
joinAndSelect(field, alias, cond = {}, type = 'innerJoin', path, fields, schema) {
|
|
133
148
|
if (!this.type) {
|
|
134
149
|
this.select('*');
|
|
135
150
|
}
|
|
136
|
-
const prop = this.joinReference(field, alias, cond, type, path);
|
|
137
|
-
this.addSelect(this.getFieldsForJoinedLoad(prop, alias));
|
|
151
|
+
const prop = this.joinReference(field, alias, cond, type, path, schema);
|
|
152
|
+
this.addSelect(this.getFieldsForJoinedLoad(prop, alias, fields));
|
|
138
153
|
const [fromAlias] = this.helper.splitField(field);
|
|
139
154
|
const populate = this._joinedProps.get(fromAlias);
|
|
140
155
|
const item = { field: prop.name, strategy: core_1.LoadStrategy.JOINED, children: [] };
|
|
@@ -147,13 +162,16 @@ class QueryBuilder {
|
|
|
147
162
|
this._joinedProps.set(alias, item);
|
|
148
163
|
return this;
|
|
149
164
|
}
|
|
150
|
-
leftJoinAndSelect(field, alias, cond = {}) {
|
|
151
|
-
return this.joinAndSelect(field, alias, cond, 'leftJoin');
|
|
165
|
+
leftJoinAndSelect(field, alias, cond = {}, fields, schema) {
|
|
166
|
+
return this.joinAndSelect(field, alias, cond, 'leftJoin', undefined, fields, schema);
|
|
152
167
|
}
|
|
153
|
-
|
|
168
|
+
innerJoinAndSelect(field, alias, cond = {}, fields, schema) {
|
|
169
|
+
return this.joinAndSelect(field, alias, cond, 'innerJoin', undefined, fields, schema);
|
|
170
|
+
}
|
|
171
|
+
getFieldsForJoinedLoad(prop, alias, explicitFields) {
|
|
154
172
|
const fields = [];
|
|
155
173
|
prop.targetMeta.props
|
|
156
|
-
.filter(prop => this.platform.shouldHaveColumn(prop, this._populate))
|
|
174
|
+
.filter(prop => explicitFields ? explicitFields.includes(prop.name) || prop.primary : this.platform.shouldHaveColumn(prop, this._populate))
|
|
157
175
|
.forEach(prop => fields.push(...this.driver.mapPropToFieldNames(this, prop, alias)));
|
|
158
176
|
return fields;
|
|
159
177
|
}
|
|
@@ -164,9 +182,15 @@ class QueryBuilder {
|
|
|
164
182
|
}
|
|
165
183
|
where(cond, params, operator) {
|
|
166
184
|
this.ensureNotFinalized();
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
185
|
+
const rawField = core_1.RawQueryFragment.getKnownFragment(cond);
|
|
186
|
+
if (rawField) {
|
|
187
|
+
const sql = this.platform.formatQuery(rawField.sql, rawField.params);
|
|
188
|
+
cond = { [(0, core_1.raw)(`(${sql})`)]: core_1.Utils.asArray(params) };
|
|
189
|
+
operator ??= '$and';
|
|
190
|
+
}
|
|
191
|
+
else if (core_1.Utils.isString(cond)) {
|
|
192
|
+
cond = { [(0, core_1.raw)(`(${cond})`, core_1.Utils.asArray(params))]: [] };
|
|
193
|
+
operator ??= '$and';
|
|
170
194
|
}
|
|
171
195
|
else {
|
|
172
196
|
cond = core_1.QueryHelper.processWhere({
|
|
@@ -234,7 +258,7 @@ class QueryBuilder {
|
|
|
234
258
|
having(cond = {}, params) {
|
|
235
259
|
this.ensureNotFinalized();
|
|
236
260
|
if (core_1.Utils.isString(cond)) {
|
|
237
|
-
cond = { [`(${cond})
|
|
261
|
+
cond = { [(0, core_1.raw)(`(${cond})`, params)]: [] };
|
|
238
262
|
}
|
|
239
263
|
this._having = CriteriaNodeFactory_1.CriteriaNodeFactory.createNode(this.metadata, this.mainAlias.entityName, cond).process(this);
|
|
240
264
|
return this;
|
|
@@ -262,6 +286,10 @@ class QueryBuilder {
|
|
|
262
286
|
this._onConflict[this._onConflict.length - 1].merge = data;
|
|
263
287
|
return this;
|
|
264
288
|
}
|
|
289
|
+
returning(fields) {
|
|
290
|
+
this._returning = fields != null ? core_1.Utils.asArray(fields) : fields;
|
|
291
|
+
return this;
|
|
292
|
+
}
|
|
265
293
|
/**
|
|
266
294
|
* @internal
|
|
267
295
|
*/
|
|
@@ -271,17 +299,6 @@ class QueryBuilder {
|
|
|
271
299
|
this._populateWhere = populateWhere;
|
|
272
300
|
return this;
|
|
273
301
|
}
|
|
274
|
-
/**
|
|
275
|
-
* @internal
|
|
276
|
-
*/
|
|
277
|
-
ref(field) {
|
|
278
|
-
return this.knex.ref(field);
|
|
279
|
-
}
|
|
280
|
-
raw(sql, bindings = []) {
|
|
281
|
-
const raw = this.knex.raw(sql, bindings);
|
|
282
|
-
raw.__raw = true; // tag it as there is now way to check via `instanceof`
|
|
283
|
-
return raw;
|
|
284
|
-
}
|
|
285
302
|
limit(limit, offset = 0) {
|
|
286
303
|
this.ensureNotFinalized();
|
|
287
304
|
this._limit = limit;
|
|
@@ -354,13 +371,15 @@ class QueryBuilder {
|
|
|
354
371
|
getKnexQuery() {
|
|
355
372
|
this.finalize();
|
|
356
373
|
const qb = this.getQueryBase();
|
|
374
|
+
qb.__raw = true; // tag it as there is now way to check via `instanceof`
|
|
357
375
|
core_1.Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type ?? enums_1.QueryType.SELECT, this._cond, qb), this._cond && !this._onConflict);
|
|
358
376
|
core_1.Utils.runIfNotEmpty(() => qb.groupBy(this.prepareFields(this._groupBy, 'groupBy')), this._groupBy);
|
|
359
377
|
core_1.Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type ?? enums_1.QueryType.SELECT, this._having, qb, undefined, 'having'), this._having);
|
|
360
378
|
core_1.Utils.runIfNotEmpty(() => {
|
|
361
379
|
const queryOrder = this.helper.getQueryOrder(this.type ?? enums_1.QueryType.SELECT, this._orderBy, this._populateMap);
|
|
362
380
|
if (queryOrder) {
|
|
363
|
-
|
|
381
|
+
qb.orderByRaw(queryOrder);
|
|
382
|
+
return;
|
|
364
383
|
}
|
|
365
384
|
}, this._orderBy);
|
|
366
385
|
core_1.Utils.runIfNotEmpty(() => qb.limit(this._limit), this._limit != null);
|
|
@@ -372,26 +391,34 @@ class QueryBuilder {
|
|
|
372
391
|
if (this.lockMode) {
|
|
373
392
|
this.helper.getLockSQL(qb, this.lockMode, this.lockTables);
|
|
374
393
|
}
|
|
375
|
-
this.helper.finalize(this.type ?? enums_1.QueryType.SELECT, qb, this.mainAlias.metadata);
|
|
394
|
+
this.helper.finalize(this.type ?? enums_1.QueryType.SELECT, qb, this.mainAlias.metadata, this._data, this._returning);
|
|
376
395
|
return qb;
|
|
377
396
|
}
|
|
378
397
|
/**
|
|
379
398
|
* Returns the query with parameters as wildcards.
|
|
380
399
|
*/
|
|
381
400
|
getQuery() {
|
|
382
|
-
return this.
|
|
401
|
+
return this.toQuery().sql;
|
|
402
|
+
}
|
|
403
|
+
toQuery() {
|
|
404
|
+
if (__classPrivateFieldGet(this, _QueryBuilder_query, "f")) {
|
|
405
|
+
return __classPrivateFieldGet(this, _QueryBuilder_query, "f");
|
|
406
|
+
}
|
|
407
|
+
const sql = this.getKnexQuery().toSQL();
|
|
408
|
+
const query = sql.toNative();
|
|
409
|
+
return __classPrivateFieldSet(this, _QueryBuilder_query, { sql: query.sql, params: query.bindings ?? [], _sql: sql }, "f");
|
|
383
410
|
}
|
|
384
411
|
/**
|
|
385
412
|
* Returns the list of all parameters for this query.
|
|
386
413
|
*/
|
|
387
414
|
getParams() {
|
|
388
|
-
return this.
|
|
415
|
+
return this.toQuery().params;
|
|
389
416
|
}
|
|
390
417
|
/**
|
|
391
418
|
* Returns raw interpolated query string with all the parameters inlined.
|
|
392
419
|
*/
|
|
393
420
|
getFormattedQuery() {
|
|
394
|
-
const query = this.
|
|
421
|
+
const query = this.toQuery()._sql;
|
|
395
422
|
return this.platform.formatQuery(query.sql, query.bindings);
|
|
396
423
|
}
|
|
397
424
|
/**
|
|
@@ -433,7 +460,7 @@ class QueryBuilder {
|
|
|
433
460
|
return cached.data;
|
|
434
461
|
}
|
|
435
462
|
const type = this.connectionType || (method === 'run' ? 'write' : 'read');
|
|
436
|
-
const res = await this.driver.getConnection(type).execute(query.sql, query.bindings, method, this.context);
|
|
463
|
+
const res = await this.driver.getConnection(type).execute(query.sql, query.bindings, method, this.context, this.logging);
|
|
437
464
|
const meta = this.mainAlias.metadata;
|
|
438
465
|
if (!mapResults || !meta) {
|
|
439
466
|
await this.em?.storeCache(this._cache, cached, res);
|
|
@@ -464,7 +491,27 @@ class QueryBuilder {
|
|
|
464
491
|
if (this._joinedProps.size > 0) {
|
|
465
492
|
res = this.driver.mergeJoinedResult(res, this.mainAlias.metadata);
|
|
466
493
|
}
|
|
467
|
-
|
|
494
|
+
const entities = [];
|
|
495
|
+
function propagatePopulateHint(entity, hint) {
|
|
496
|
+
(0, core_1.helper)(entity).__serializationContext.populate ??= hint;
|
|
497
|
+
hint.forEach(pop => {
|
|
498
|
+
const value = entity[pop.field];
|
|
499
|
+
if (core_1.Utils.isEntity(value, true)) {
|
|
500
|
+
(0, core_1.helper)(value).populated();
|
|
501
|
+
propagatePopulateHint(value, pop.children ?? []);
|
|
502
|
+
}
|
|
503
|
+
else if (core_1.Utils.isCollection(value)) {
|
|
504
|
+
value.populated();
|
|
505
|
+
value.getItems(false).forEach(item => propagatePopulateHint(item, pop.children ?? []));
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
for (const r of res) {
|
|
510
|
+
const entity = this.em.map(this.mainAlias.entityName, r, { schema: this._schema });
|
|
511
|
+
propagatePopulateHint(entity, this._populate);
|
|
512
|
+
entities.push(entity);
|
|
513
|
+
}
|
|
514
|
+
return entities;
|
|
468
515
|
}
|
|
469
516
|
/**
|
|
470
517
|
* Executes the query, returning the first result or null
|
|
@@ -482,17 +529,30 @@ class QueryBuilder {
|
|
|
482
529
|
res = await this.execute('get', false);
|
|
483
530
|
}
|
|
484
531
|
else {
|
|
485
|
-
const qb = this.clone();
|
|
532
|
+
const qb = this.type === undefined ? this : this.clone();
|
|
486
533
|
qb.count(field, distinct ?? qb.hasToManyJoins()).limit(undefined).offset(undefined).orderBy([]);
|
|
487
534
|
res = await qb.execute('get', false);
|
|
488
535
|
}
|
|
489
536
|
return res ? +res.count : 0;
|
|
490
537
|
}
|
|
538
|
+
/**
|
|
539
|
+
* Executes the query, returning both array of results and total count query (without offset and limit).
|
|
540
|
+
*/
|
|
541
|
+
async getResultAndCount() {
|
|
542
|
+
return Promise.all([
|
|
543
|
+
this.getResultList(),
|
|
544
|
+
this.getCount(),
|
|
545
|
+
]);
|
|
546
|
+
}
|
|
491
547
|
/**
|
|
492
548
|
* Provides promise-like interface so we can await the QB instance.
|
|
493
549
|
*/
|
|
494
550
|
then(onfulfilled, onrejected) {
|
|
495
|
-
|
|
551
|
+
let type = this.type ?? enums_1.QueryType.SELECT;
|
|
552
|
+
if (this.flags.has(core_1.QueryFlag.UPDATE_SUB_QUERY) || this.flags.has(core_1.QueryFlag.DELETE_SUB_QUERY)) {
|
|
553
|
+
type = enums_1.QueryType.UPDATE;
|
|
554
|
+
}
|
|
555
|
+
switch (type) {
|
|
496
556
|
case enums_1.QueryType.INSERT:
|
|
497
557
|
case enums_1.QueryType.UPDATE:
|
|
498
558
|
case enums_1.QueryType.DELETE:
|
|
@@ -515,7 +575,10 @@ class QueryBuilder {
|
|
|
515
575
|
/* istanbul ignore next */
|
|
516
576
|
alias = meta?.properties[f]?.fieldNames[0] ?? alias;
|
|
517
577
|
}
|
|
518
|
-
|
|
578
|
+
const ret = qb.as(alias);
|
|
579
|
+
// tag the instance, so it is possible to detect it easily
|
|
580
|
+
Object.defineProperty(ret, '__as', { enumerable: false, value: alias });
|
|
581
|
+
return ret;
|
|
519
582
|
}
|
|
520
583
|
clone() {
|
|
521
584
|
const qb = new QueryBuilder(this.mainAlias.entityName, this.metadata, this.driver, this.context, this.mainAlias.aliasName, this.connectionType, this.em);
|
|
@@ -523,7 +586,7 @@ class QueryBuilder {
|
|
|
523
586
|
// clone array/object properties
|
|
524
587
|
const properties = [
|
|
525
588
|
'flags', '_populate', '_populateWhere', '_populateMap', '_joins', '_joinedProps', '_cond', '_data', '_orderBy',
|
|
526
|
-
'_schema', '_indexHint', '_cache', 'subQueries', 'lockMode', 'lockTables',
|
|
589
|
+
'_schema', '_indexHint', '_cache', 'subQueries', 'lockMode', 'lockTables', '_groupBy', '_having', '_returning',
|
|
527
590
|
];
|
|
528
591
|
properties.forEach(prop => qb[prop] = core_1.Utils.copy(this[prop]));
|
|
529
592
|
/* istanbul ignore else */
|
|
@@ -551,14 +614,18 @@ class QueryBuilder {
|
|
|
551
614
|
}
|
|
552
615
|
return qb;
|
|
553
616
|
}
|
|
554
|
-
joinReference(field, alias, cond, type, path) {
|
|
617
|
+
joinReference(field, alias, cond, type, path, schema) {
|
|
555
618
|
this.ensureNotFinalized();
|
|
556
619
|
const [fromAlias, fromField] = this.helper.splitField(field);
|
|
557
|
-
const
|
|
620
|
+
const q = (str) => `'${str}'`;
|
|
621
|
+
if (!this._aliases[fromAlias]) {
|
|
622
|
+
throw new Error(`Trying to join ${q(fromField)} with alias ${q(fromAlias)}, but ${q(fromAlias)} is not a known alias. Available aliases are: ${Object.keys(this._aliases).map(q).join(', ')}.`);
|
|
623
|
+
}
|
|
624
|
+
const entityName = this._aliases[fromAlias].entityName;
|
|
558
625
|
const meta = this.metadata.get(entityName);
|
|
559
626
|
const prop = meta.properties[fromField];
|
|
560
627
|
if (!prop) {
|
|
561
|
-
throw new Error(`Trying to join ${field}, but ${fromField} is not a defined relation on ${meta.className}
|
|
628
|
+
throw new Error(`Trying to join ${q(field)}, but ${q(fromField)} is not a defined relation on ${meta.className}.`);
|
|
562
629
|
}
|
|
563
630
|
this.createAlias(prop.type, alias);
|
|
564
631
|
cond = core_1.QueryHelper.processWhere({
|
|
@@ -570,26 +637,26 @@ class QueryBuilder {
|
|
|
570
637
|
aliased: !this.type || [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(this.type),
|
|
571
638
|
});
|
|
572
639
|
let aliasedName = `${fromAlias}.${prop.name}#${alias}`;
|
|
573
|
-
path
|
|
574
|
-
if (prop.
|
|
575
|
-
this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond);
|
|
640
|
+
path ??= `${(Object.values(this._joins).find(j => j.alias === fromAlias)?.path ?? entityName)}.${prop.name}`;
|
|
641
|
+
if (prop.kind === core_1.ReferenceKind.ONE_TO_MANY) {
|
|
642
|
+
this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond, schema);
|
|
576
643
|
}
|
|
577
|
-
else if (prop.
|
|
644
|
+
else if (prop.kind === core_1.ReferenceKind.MANY_TO_MANY) {
|
|
578
645
|
let pivotAlias = alias;
|
|
579
646
|
if (type !== 'pivotJoin') {
|
|
580
647
|
const oldPivotAlias = this.getAliasForJoinPath(path + '[pivot]');
|
|
581
648
|
pivotAlias = oldPivotAlias ?? this.getNextAlias(prop.pivotEntity);
|
|
582
649
|
aliasedName = `${fromAlias}.${prop.name}#${pivotAlias}`;
|
|
583
650
|
}
|
|
584
|
-
const joins = this.helper.joinManyToManyReference(prop, fromAlias, alias, pivotAlias, type, cond, path);
|
|
651
|
+
const joins = this.helper.joinManyToManyReference(prop, fromAlias, alias, pivotAlias, type, cond, path, schema);
|
|
585
652
|
Object.assign(this._joins, joins);
|
|
586
653
|
this.createAlias(prop.pivotEntity, pivotAlias);
|
|
587
654
|
}
|
|
588
|
-
else if (prop.
|
|
589
|
-
this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond);
|
|
655
|
+
else if (prop.kind === core_1.ReferenceKind.ONE_TO_ONE) {
|
|
656
|
+
this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond, schema);
|
|
590
657
|
}
|
|
591
658
|
else { // MANY_TO_ONE
|
|
592
|
-
this._joins[aliasedName] = this.helper.joinManyToOneReference(prop, fromAlias, alias, type, cond);
|
|
659
|
+
this._joins[aliasedName] = this.helper.joinManyToOneReference(prop, fromAlias, alias, type, cond, schema);
|
|
593
660
|
}
|
|
594
661
|
if (!this._joins[aliasedName].path && path) {
|
|
595
662
|
this._joins[aliasedName].path = path;
|
|
@@ -599,17 +666,25 @@ class QueryBuilder {
|
|
|
599
666
|
prepareFields(fields, type = 'where') {
|
|
600
667
|
const ret = [];
|
|
601
668
|
fields.forEach(field => {
|
|
669
|
+
const rawField = core_1.RawQueryFragment.getKnownFragment(field);
|
|
670
|
+
if (rawField) {
|
|
671
|
+
const sql = this.platform.formatQuery(rawField.sql, rawField.params);
|
|
672
|
+
ret.push(this.knex.raw(sql));
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
602
675
|
if (!core_1.Utils.isString(field)) {
|
|
603
|
-
|
|
676
|
+
ret.push(field);
|
|
677
|
+
return;
|
|
604
678
|
}
|
|
605
679
|
const join = Object.keys(this._joins).find(k => field === k.substring(0, k.indexOf('#')));
|
|
606
680
|
if (join && type === 'where') {
|
|
607
|
-
|
|
681
|
+
ret.push(...this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[join]));
|
|
682
|
+
return;
|
|
608
683
|
}
|
|
609
684
|
const [a, f] = this.helper.splitField(field);
|
|
610
685
|
const prop = this.helper.getProperty(f, a);
|
|
611
686
|
/* istanbul ignore next */
|
|
612
|
-
if (prop && [core_1.
|
|
687
|
+
if (prop && [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
|
|
613
688
|
return;
|
|
614
689
|
}
|
|
615
690
|
if (prop?.embedded) {
|
|
@@ -617,7 +692,7 @@ class QueryBuilder {
|
|
|
617
692
|
ret.push(fieldName);
|
|
618
693
|
return;
|
|
619
694
|
}
|
|
620
|
-
if (prop?.
|
|
695
|
+
if (prop?.kind === core_1.ReferenceKind.EMBEDDED) {
|
|
621
696
|
if (prop.object) {
|
|
622
697
|
ret.push(this.helper.mapper(prop.fieldNames[0], this.type));
|
|
623
698
|
}
|
|
@@ -640,7 +715,7 @@ class QueryBuilder {
|
|
|
640
715
|
});
|
|
641
716
|
const meta = this.mainAlias.metadata;
|
|
642
717
|
/* istanbul ignore next */
|
|
643
|
-
const requiresSQLConversion = meta?.props.filter(p => p.
|
|
718
|
+
const requiresSQLConversion = meta?.props.filter(p => p.hasConvertToJSValueSQL) ?? [];
|
|
644
719
|
if (this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES) && (fields.includes('*') || fields.includes(`${this.mainAlias.aliasName}.*`)) && requiresSQLConversion.length > 0) {
|
|
645
720
|
requiresSQLConversion.forEach(p => ret.push(this.helper.mapper(p.name, this.type)));
|
|
646
721
|
}
|
|
@@ -649,7 +724,7 @@ class QueryBuilder {
|
|
|
649
724
|
const cols = this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[f]);
|
|
650
725
|
ret.push(...cols);
|
|
651
726
|
}
|
|
652
|
-
if (this._joins[f].prop.
|
|
727
|
+
if (this._joins[f].prop.kind !== core_1.ReferenceKind.ONE_TO_ONE && this._joins[f].inverseJoinColumns) {
|
|
653
728
|
this._joins[f].inverseJoinColumns.forEach(inverseJoinColumn => {
|
|
654
729
|
core_1.Utils.renameKey(this._cond, inverseJoinColumn, `${this._joins[f].alias}.${inverseJoinColumn}`);
|
|
655
730
|
});
|
|
@@ -668,7 +743,7 @@ class QueryBuilder {
|
|
|
668
743
|
}
|
|
669
744
|
if (data) {
|
|
670
745
|
if (core_1.Utils.isEntity(data)) {
|
|
671
|
-
data = (0, core_1.
|
|
746
|
+
data = this.em?.getComparator().prepareEntity(data) ?? (0, core_1.serialize)(data);
|
|
672
747
|
}
|
|
673
748
|
this._data = this.helper.processData(data, this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES));
|
|
674
749
|
}
|
|
@@ -722,6 +797,24 @@ class QueryBuilder {
|
|
|
722
797
|
}
|
|
723
798
|
return qb;
|
|
724
799
|
}
|
|
800
|
+
applyDiscriminatorCondition() {
|
|
801
|
+
const meta = this.mainAlias.metadata;
|
|
802
|
+
if (!meta?.discriminatorValue) {
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
const types = Object.values(meta.root.discriminatorMap).map(cls => this.metadata.find(cls));
|
|
806
|
+
const children = [];
|
|
807
|
+
const lookUpChildren = (ret, type) => {
|
|
808
|
+
const children = types.filter(meta2 => meta2.extends === type);
|
|
809
|
+
children.forEach(m => lookUpChildren(ret, m.className));
|
|
810
|
+
ret.push(...children.filter(c => c.discriminatorValue));
|
|
811
|
+
return children;
|
|
812
|
+
};
|
|
813
|
+
lookUpChildren(children, meta.className);
|
|
814
|
+
this.andWhere({
|
|
815
|
+
[meta.root.discriminatorColumn]: children.length > 0 ? { $in: [meta.discriminatorValue, ...children.map(c => c.discriminatorValue)] } : meta.discriminatorValue,
|
|
816
|
+
});
|
|
817
|
+
}
|
|
725
818
|
finalize() {
|
|
726
819
|
if (this.finalized) {
|
|
727
820
|
return;
|
|
@@ -730,10 +823,11 @@ class QueryBuilder {
|
|
|
730
823
|
this.select('*');
|
|
731
824
|
}
|
|
732
825
|
const meta = this.mainAlias.metadata;
|
|
826
|
+
this.applyDiscriminatorCondition();
|
|
733
827
|
if (meta && this.flags.has(core_1.QueryFlag.AUTO_JOIN_ONE_TO_ONE_OWNER)) {
|
|
734
828
|
const relationsToPopulate = this._populate.map(({ field }) => field);
|
|
735
829
|
meta.relations
|
|
736
|
-
.filter(prop => prop.
|
|
830
|
+
.filter(prop => prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
|
|
737
831
|
.map(prop => ({ field: prop.name }))
|
|
738
832
|
.forEach(item => this._populate.push(item));
|
|
739
833
|
}
|
|
@@ -742,12 +836,10 @@ class QueryBuilder {
|
|
|
742
836
|
const aliasedField = `${fromAlias}.${fromField}`;
|
|
743
837
|
const join = Object.keys(this._joins).find(k => `${aliasedField}#${this._joins[k].alias}` === k);
|
|
744
838
|
if (join && this._joins[join] && this.helper.isOneToOneInverse(fromField)) {
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
if (this.metadata.find(field)?.pivotTable) { // pivot table entity
|
|
748
|
-
this.autoJoinPivotTable(field);
|
|
839
|
+
this._populateMap[join] = this._joins[join].alias;
|
|
840
|
+
return;
|
|
749
841
|
}
|
|
750
|
-
|
|
842
|
+
if (meta && this.helper.isOneToOneInverse(fromField)) {
|
|
751
843
|
const prop = meta.properties[fromField];
|
|
752
844
|
const alias = this.getNextAlias(prop.pivotEntity ?? prop.type);
|
|
753
845
|
const aliasedName = `${fromAlias}.${prop.name}#${alias}`;
|
|
@@ -763,14 +855,20 @@ class QueryBuilder {
|
|
|
763
855
|
const aliased = this.knex.ref(prop.fieldNames[0]).toString();
|
|
764
856
|
return `${prop.formula(alias)} as ${aliased}`;
|
|
765
857
|
})
|
|
766
|
-
.filter(field => !this._fields.
|
|
767
|
-
|
|
858
|
+
.filter(field => !this._fields.some(f => {
|
|
859
|
+
if (f instanceof core_1.RawQueryFragment) {
|
|
860
|
+
return f.sql === field && f.params.length === 0;
|
|
861
|
+
}
|
|
862
|
+
return f === field;
|
|
863
|
+
}))
|
|
864
|
+
.forEach(field => this._fields.push((0, core_1.raw)(field)));
|
|
768
865
|
}
|
|
866
|
+
this.processPopulateWhere();
|
|
769
867
|
core_1.QueryHelper.processObjectParams(this._data);
|
|
770
868
|
core_1.QueryHelper.processObjectParams(this._cond);
|
|
771
869
|
core_1.QueryHelper.processObjectParams(this._having);
|
|
772
|
-
// automatically enable paginate flag when we detect to-many joins
|
|
773
|
-
if (!this.flags.has(core_1.QueryFlag.DISABLE_PAGINATE) && this.hasToManyJoins()) {
|
|
870
|
+
// automatically enable paginate flag when we detect to-many joins, but only if there is no `group by` clause
|
|
871
|
+
if (!this.flags.has(core_1.QueryFlag.DISABLE_PAGINATE) && this._groupBy.length === 0 && this.hasToManyJoins()) {
|
|
774
872
|
this.flags.add(core_1.QueryFlag.PAGINATE);
|
|
775
873
|
}
|
|
776
874
|
if (meta && this.flags.has(core_1.QueryFlag.PAGINATE) && (this._limit > 0 || this._offset > 0)) {
|
|
@@ -781,17 +879,54 @@ class QueryBuilder {
|
|
|
781
879
|
}
|
|
782
880
|
this.finalized = true;
|
|
783
881
|
}
|
|
882
|
+
processPopulateWhere() {
|
|
883
|
+
if (this._populateWhere == null || this._populateWhere === core_1.PopulateHint.ALL) {
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
const joins = Object.values(this._joins);
|
|
887
|
+
joins.forEach(join => {
|
|
888
|
+
join.cond_ = join.cond;
|
|
889
|
+
join.cond = {};
|
|
890
|
+
});
|
|
891
|
+
const replaceOnConditions = (cond) => {
|
|
892
|
+
Object.keys(cond).forEach(k => {
|
|
893
|
+
if (core_1.Utils.isOperator(k)) {
|
|
894
|
+
if (Array.isArray(cond[k])) {
|
|
895
|
+
return cond[k].forEach((c) => replaceOnConditions(c));
|
|
896
|
+
}
|
|
897
|
+
return replaceOnConditions(cond[k]);
|
|
898
|
+
}
|
|
899
|
+
const [a] = this.helper.splitField(k);
|
|
900
|
+
const join = joins.find(j => j.alias === a);
|
|
901
|
+
if (join) {
|
|
902
|
+
join.cond = { ...join.cond, [k]: cond[k] };
|
|
903
|
+
}
|
|
904
|
+
});
|
|
905
|
+
};
|
|
906
|
+
if (this._populateWhere === core_1.PopulateHint.INFER) {
|
|
907
|
+
replaceOnConditions(this._cond);
|
|
908
|
+
}
|
|
909
|
+
else if (typeof this._populateWhere === 'object') {
|
|
910
|
+
const cond = CriteriaNodeFactory_1.CriteriaNodeFactory
|
|
911
|
+
.createNode(this.metadata, this.mainAlias.entityName, this._populateWhere)
|
|
912
|
+
.process(this);
|
|
913
|
+
replaceOnConditions(cond);
|
|
914
|
+
}
|
|
915
|
+
}
|
|
784
916
|
hasToManyJoins() {
|
|
785
917
|
return Object.values(this._joins).some(join => {
|
|
786
|
-
return [core_1.
|
|
918
|
+
return [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(join.prop.kind);
|
|
787
919
|
});
|
|
788
920
|
}
|
|
789
921
|
wrapPaginateSubQuery(meta) {
|
|
790
922
|
const pks = this.prepareFields(meta.primaryKeys, 'sub-query');
|
|
791
923
|
const subQuery = this.clone().select(pks).groupBy(pks).limit(this._limit);
|
|
924
|
+
// revert the on conditions added via populateWhere, we want to apply those only once
|
|
925
|
+
Object.values(subQuery._joins).forEach(join => join.cond = join.cond_ ?? {});
|
|
792
926
|
if (this._offset) {
|
|
793
927
|
subQuery.offset(this._offset);
|
|
794
928
|
}
|
|
929
|
+
const addToSelect = [];
|
|
795
930
|
if (this._orderBy.length > 0) {
|
|
796
931
|
const orderBy = [];
|
|
797
932
|
for (const orderMap of this._orderBy) {
|
|
@@ -799,28 +934,38 @@ class QueryBuilder {
|
|
|
799
934
|
const [a, f] = this.helper.splitField(field);
|
|
800
935
|
const prop = this.helper.getProperty(f, a);
|
|
801
936
|
const type = this.platform.castColumn(prop);
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
937
|
+
const fieldName = this.helper.mapper(field, this.type, undefined, null);
|
|
938
|
+
if (!prop?.persist && !prop?.formula) {
|
|
939
|
+
addToSelect.push(fieldName);
|
|
940
|
+
}
|
|
941
|
+
orderBy.push({ [(0, core_1.raw)(`min(${this.knex.ref(fieldName)}${type})`)]: direction });
|
|
805
942
|
}
|
|
806
943
|
}
|
|
807
944
|
subQuery.orderBy(orderBy);
|
|
808
945
|
}
|
|
809
946
|
subQuery.finalized = true;
|
|
810
947
|
const knexQuery = subQuery.as(this.mainAlias.aliasName).clearSelect().select(pks);
|
|
948
|
+
if (addToSelect.length > 0) {
|
|
949
|
+
addToSelect.forEach(prop => {
|
|
950
|
+
const field = this._fields.find(field => {
|
|
951
|
+
if (typeof field === 'object' && field && '__as' in field) {
|
|
952
|
+
return field.__as === prop;
|
|
953
|
+
}
|
|
954
|
+
// not perfect, but should work most of the time, ideally we should check only the alias (`... as alias`)
|
|
955
|
+
return field.toString().includes(prop);
|
|
956
|
+
});
|
|
957
|
+
if (field) {
|
|
958
|
+
knexQuery.select(field);
|
|
959
|
+
}
|
|
960
|
+
});
|
|
961
|
+
}
|
|
811
962
|
// multiple sub-queries are needed to get around mysql limitations with order by + limit + where in + group by (o.O)
|
|
812
963
|
// https://stackoverflow.com/questions/17892762/mysql-this-version-of-mysql-doesnt-yet-support-limit-in-all-any-some-subqu
|
|
813
964
|
const subSubQuery = this.getKnex().select(pks).from(knexQuery);
|
|
965
|
+
subSubQuery.__raw = true; // tag it as there is now way to check via `instanceof`
|
|
814
966
|
this._limit = undefined;
|
|
815
967
|
this._offset = undefined;
|
|
816
|
-
const cond = this._cond;
|
|
817
968
|
this.select(this._fields).where({ [core_1.Utils.getPrimaryKeyHash(meta.primaryKeys)]: { $in: subSubQuery } });
|
|
818
|
-
if (this._populateWhere === core_1.PopulateHint.INFER) {
|
|
819
|
-
this.andWhere(cond);
|
|
820
|
-
}
|
|
821
|
-
else if (typeof this._populateWhere === 'object') {
|
|
822
|
-
this.andWhere(this._populateWhere);
|
|
823
|
-
}
|
|
824
969
|
}
|
|
825
970
|
wrapModifySubQuery(meta) {
|
|
826
971
|
const subQuery = this.clone();
|
|
@@ -834,17 +979,6 @@ class QueryBuilder {
|
|
|
834
979
|
[core_1.Utils.getPrimaryKeyHash(meta.primaryKeys)]: { $in: subSubQuery },
|
|
835
980
|
});
|
|
836
981
|
}
|
|
837
|
-
autoJoinPivotTable(field) {
|
|
838
|
-
const pivotMeta = this.metadata.find(field);
|
|
839
|
-
const owner = pivotMeta.relations[0];
|
|
840
|
-
const inverse = pivotMeta.relations[1];
|
|
841
|
-
const prop = this._cond[pivotMeta.name + '.' + owner.name] || this._orderBy[pivotMeta.name + '.' + owner.name] ? inverse : owner;
|
|
842
|
-
const pivotAlias = this.getNextAlias(pivotMeta.name);
|
|
843
|
-
this._joins[field] = this.helper.joinPivotTable(field, prop, this.mainAlias.aliasName, pivotAlias, 'leftJoin');
|
|
844
|
-
core_1.Utils.renameKey(this._cond, `${field}.${owner.name}`, core_1.Utils.getPrimaryKeyHash(owner.fieldNames.map(fieldName => `${pivotAlias}.${fieldName}`)));
|
|
845
|
-
core_1.Utils.renameKey(this._cond, `${field}.${inverse.name}`, core_1.Utils.getPrimaryKeyHash(inverse.fieldNames.map(fieldName => `${pivotAlias}.${fieldName}`)));
|
|
846
|
-
this._populateMap[field] = this._joins[field].alias;
|
|
847
|
-
}
|
|
848
982
|
getSchema(alias) {
|
|
849
983
|
const { metadata } = alias;
|
|
850
984
|
const metaSchema = metadata?.schema && metadata.schema !== '*' ? metadata.schema : undefined;
|
|
@@ -864,11 +998,11 @@ class QueryBuilder {
|
|
|
864
998
|
fromSubQuery(target, aliasName) {
|
|
865
999
|
const subQuery = target.getKnexQuery();
|
|
866
1000
|
const { entityName } = target.mainAlias;
|
|
867
|
-
aliasName
|
|
1001
|
+
aliasName ??= this.getNextAlias(entityName);
|
|
868
1002
|
this.createMainAlias(entityName, aliasName, subQuery);
|
|
869
1003
|
}
|
|
870
1004
|
fromEntityName(entityName, aliasName) {
|
|
871
|
-
aliasName
|
|
1005
|
+
aliasName ??= this._mainAlias?.aliasName ?? this.getNextAlias(entityName);
|
|
872
1006
|
this.createMainAlias(entityName, aliasName);
|
|
873
1007
|
}
|
|
874
1008
|
createQueryBuilderHelper() {
|
|
@@ -885,5 +1019,30 @@ class QueryBuilder {
|
|
|
885
1019
|
throw new Error('This QueryBuilder instance is already finalized, clone it first if you want to modify it.');
|
|
886
1020
|
}
|
|
887
1021
|
}
|
|
1022
|
+
/* istanbul ignore next */
|
|
1023
|
+
[(_QueryBuilder_query = new WeakMap(), util_1.inspect.custom)](depth) {
|
|
1024
|
+
const object = { ...this };
|
|
1025
|
+
const hidden = ['metadata', 'driver', 'context', 'platform', 'knex', 'type'];
|
|
1026
|
+
Object.keys(object).filter(k => k.startsWith('_')).forEach(k => delete object[k]);
|
|
1027
|
+
Object.keys(object).filter(k => object[k] == null).forEach(k => delete object[k]);
|
|
1028
|
+
hidden.forEach(k => delete object[k]);
|
|
1029
|
+
let prefix = this.type ? this.type.substring(0, 1) + this.type.toLowerCase().substring(1) : '';
|
|
1030
|
+
if (this._data) {
|
|
1031
|
+
object.data = this._data;
|
|
1032
|
+
}
|
|
1033
|
+
if (this._schema) {
|
|
1034
|
+
object.schema = this._schema;
|
|
1035
|
+
}
|
|
1036
|
+
if (!core_1.Utils.isEmpty(this._cond)) {
|
|
1037
|
+
object.where = this._cond;
|
|
1038
|
+
}
|
|
1039
|
+
if (this._onConflict?.[0]) {
|
|
1040
|
+
prefix = 'Upsert';
|
|
1041
|
+
object.onConflict = this._onConflict[0];
|
|
1042
|
+
}
|
|
1043
|
+
const name = this._mainAlias ? `${prefix}QueryBuilder<${this._mainAlias?.entityName}>` : 'QueryBuilder';
|
|
1044
|
+
const ret = (0, util_1.inspect)(object, { depth });
|
|
1045
|
+
return ret === '[Object]' ? `[${name}]` : name + ' ' + ret;
|
|
1046
|
+
}
|
|
888
1047
|
}
|
|
889
1048
|
exports.QueryBuilder = QueryBuilder;
|