@mikro-orm/knex 6.0.0-dev.8 → 6.0.0-dev.80
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 +18 -18
- package/AbstractSqlDriver.js +117 -48
- package/AbstractSqlPlatform.d.ts +1 -0
- package/AbstractSqlPlatform.js +20 -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 +19 -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 +25 -7
- package/query/QueryBuilder.js +237 -61
- package/query/QueryBuilderHelper.d.ts +3 -5
- package/query/QueryBuilderHelper.js +96 -74
- package/query/ScalarCriteriaNode.d.ts +3 -2
- package/query/ScalarCriteriaNode.js +9 -6
- 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 +8 -5
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,7 +130,7 @@ 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);
|
|
@@ -129,12 +144,12 @@ class QueryBuilder {
|
|
|
129
144
|
leftJoin(field, alias, cond = {}) {
|
|
130
145
|
return this.join(field, alias, cond, 'leftJoin');
|
|
131
146
|
}
|
|
132
|
-
joinAndSelect(field, alias, cond = {}, type = 'innerJoin', path) {
|
|
147
|
+
joinAndSelect(field, alias, cond = {}, type = 'innerJoin', path, fields) {
|
|
133
148
|
if (!this.type) {
|
|
134
149
|
this.select('*');
|
|
135
150
|
}
|
|
136
151
|
const prop = this.joinReference(field, alias, cond, type, path);
|
|
137
|
-
this.addSelect(this.getFieldsForJoinedLoad(prop, alias));
|
|
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) {
|
|
166
|
+
return this.joinAndSelect(field, alias, cond, 'leftJoin', undefined, fields);
|
|
152
167
|
}
|
|
153
|
-
|
|
168
|
+
innerJoinAndSelect(field, alias, cond = {}, fields) {
|
|
169
|
+
return this.joinAndSelect(field, alias, cond, 'innerJoin', undefined, fields);
|
|
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
|
*/
|
|
@@ -277,11 +305,6 @@ class QueryBuilder {
|
|
|
277
305
|
ref(field) {
|
|
278
306
|
return this.knex.ref(field);
|
|
279
307
|
}
|
|
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
308
|
limit(limit, offset = 0) {
|
|
286
309
|
this.ensureNotFinalized();
|
|
287
310
|
this._limit = limit;
|
|
@@ -354,13 +377,15 @@ class QueryBuilder {
|
|
|
354
377
|
getKnexQuery() {
|
|
355
378
|
this.finalize();
|
|
356
379
|
const qb = this.getQueryBase();
|
|
380
|
+
qb.__raw = true; // tag it as there is now way to check via `instanceof`
|
|
357
381
|
core_1.Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type ?? enums_1.QueryType.SELECT, this._cond, qb), this._cond && !this._onConflict);
|
|
358
382
|
core_1.Utils.runIfNotEmpty(() => qb.groupBy(this.prepareFields(this._groupBy, 'groupBy')), this._groupBy);
|
|
359
383
|
core_1.Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type ?? enums_1.QueryType.SELECT, this._having, qb, undefined, 'having'), this._having);
|
|
360
384
|
core_1.Utils.runIfNotEmpty(() => {
|
|
361
385
|
const queryOrder = this.helper.getQueryOrder(this.type ?? enums_1.QueryType.SELECT, this._orderBy, this._populateMap);
|
|
362
386
|
if (queryOrder) {
|
|
363
|
-
|
|
387
|
+
qb.orderByRaw(queryOrder);
|
|
388
|
+
return;
|
|
364
389
|
}
|
|
365
390
|
}, this._orderBy);
|
|
366
391
|
core_1.Utils.runIfNotEmpty(() => qb.limit(this._limit), this._limit != null);
|
|
@@ -372,26 +397,34 @@ class QueryBuilder {
|
|
|
372
397
|
if (this.lockMode) {
|
|
373
398
|
this.helper.getLockSQL(qb, this.lockMode, this.lockTables);
|
|
374
399
|
}
|
|
375
|
-
this.helper.finalize(this.type ?? enums_1.QueryType.SELECT, qb, this.mainAlias.metadata);
|
|
400
|
+
this.helper.finalize(this.type ?? enums_1.QueryType.SELECT, qb, this.mainAlias.metadata, this._data, this._returning);
|
|
376
401
|
return qb;
|
|
377
402
|
}
|
|
378
403
|
/**
|
|
379
404
|
* Returns the query with parameters as wildcards.
|
|
380
405
|
*/
|
|
381
406
|
getQuery() {
|
|
382
|
-
return this.
|
|
407
|
+
return this.toQuery().sql;
|
|
408
|
+
}
|
|
409
|
+
toQuery() {
|
|
410
|
+
if (__classPrivateFieldGet(this, _QueryBuilder_query, "f")) {
|
|
411
|
+
return __classPrivateFieldGet(this, _QueryBuilder_query, "f");
|
|
412
|
+
}
|
|
413
|
+
const sql = this.getKnexQuery().toSQL();
|
|
414
|
+
const query = sql.toNative();
|
|
415
|
+
return __classPrivateFieldSet(this, _QueryBuilder_query, { sql: query.sql, params: query.bindings ?? [], _sql: sql }, "f");
|
|
383
416
|
}
|
|
384
417
|
/**
|
|
385
418
|
* Returns the list of all parameters for this query.
|
|
386
419
|
*/
|
|
387
420
|
getParams() {
|
|
388
|
-
return this.
|
|
421
|
+
return this.toQuery().params;
|
|
389
422
|
}
|
|
390
423
|
/**
|
|
391
424
|
* Returns raw interpolated query string with all the parameters inlined.
|
|
392
425
|
*/
|
|
393
426
|
getFormattedQuery() {
|
|
394
|
-
const query = this.
|
|
427
|
+
const query = this.toQuery()._sql;
|
|
395
428
|
return this.platform.formatQuery(query.sql, query.bindings);
|
|
396
429
|
}
|
|
397
430
|
/**
|
|
@@ -433,7 +466,7 @@ class QueryBuilder {
|
|
|
433
466
|
return cached.data;
|
|
434
467
|
}
|
|
435
468
|
const type = this.connectionType || (method === 'run' ? 'write' : 'read');
|
|
436
|
-
const res = await this.driver.getConnection(type).execute(query.sql, query.bindings, method, this.context);
|
|
469
|
+
const res = await this.driver.getConnection(type).execute(query.sql, query.bindings, method, this.context, this.logging);
|
|
437
470
|
const meta = this.mainAlias.metadata;
|
|
438
471
|
if (!mapResults || !meta) {
|
|
439
472
|
await this.em?.storeCache(this._cache, cached, res);
|
|
@@ -464,7 +497,27 @@ class QueryBuilder {
|
|
|
464
497
|
if (this._joinedProps.size > 0) {
|
|
465
498
|
res = this.driver.mergeJoinedResult(res, this.mainAlias.metadata);
|
|
466
499
|
}
|
|
467
|
-
|
|
500
|
+
const entities = [];
|
|
501
|
+
function propagatePopulateHint(entity, hint) {
|
|
502
|
+
(0, core_1.helper)(entity).__serializationContext.populate ??= hint;
|
|
503
|
+
hint.forEach(pop => {
|
|
504
|
+
const value = entity[pop.field];
|
|
505
|
+
if (core_1.Utils.isEntity(value, true)) {
|
|
506
|
+
(0, core_1.helper)(value).populated();
|
|
507
|
+
propagatePopulateHint(value, pop.children ?? []);
|
|
508
|
+
}
|
|
509
|
+
else if (core_1.Utils.isCollection(value)) {
|
|
510
|
+
value.populated();
|
|
511
|
+
value.getItems(false).forEach(item => propagatePopulateHint(item, pop.children ?? []));
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
for (const r of res) {
|
|
516
|
+
const entity = this.em.map(this.mainAlias.entityName, r, { schema: this._schema });
|
|
517
|
+
propagatePopulateHint(entity, this._populate);
|
|
518
|
+
entities.push(entity);
|
|
519
|
+
}
|
|
520
|
+
return entities;
|
|
468
521
|
}
|
|
469
522
|
/**
|
|
470
523
|
* Executes the query, returning the first result or null
|
|
@@ -488,11 +541,24 @@ class QueryBuilder {
|
|
|
488
541
|
}
|
|
489
542
|
return res ? +res.count : 0;
|
|
490
543
|
}
|
|
544
|
+
/**
|
|
545
|
+
* Executes the query, returning both array of results and total count query (without offset and limit).
|
|
546
|
+
*/
|
|
547
|
+
async getResultAndCount() {
|
|
548
|
+
return Promise.all([
|
|
549
|
+
this.getResultList(),
|
|
550
|
+
this.getCount(),
|
|
551
|
+
]);
|
|
552
|
+
}
|
|
491
553
|
/**
|
|
492
554
|
* Provides promise-like interface so we can await the QB instance.
|
|
493
555
|
*/
|
|
494
556
|
then(onfulfilled, onrejected) {
|
|
495
|
-
|
|
557
|
+
let type = this.type ?? enums_1.QueryType.SELECT;
|
|
558
|
+
if (this.flags.has(core_1.QueryFlag.UPDATE_SUB_QUERY) || this.flags.has(core_1.QueryFlag.DELETE_SUB_QUERY)) {
|
|
559
|
+
type = enums_1.QueryType.UPDATE;
|
|
560
|
+
}
|
|
561
|
+
switch (type) {
|
|
496
562
|
case enums_1.QueryType.INSERT:
|
|
497
563
|
case enums_1.QueryType.UPDATE:
|
|
498
564
|
case enums_1.QueryType.DELETE:
|
|
@@ -515,7 +581,10 @@ class QueryBuilder {
|
|
|
515
581
|
/* istanbul ignore next */
|
|
516
582
|
alias = meta?.properties[f]?.fieldNames[0] ?? alias;
|
|
517
583
|
}
|
|
518
|
-
|
|
584
|
+
const ret = qb.as(alias);
|
|
585
|
+
// tag the instance, so it is possible to detect it easily
|
|
586
|
+
Object.defineProperty(ret, '__as', { enumerable: false, value: alias });
|
|
587
|
+
return ret;
|
|
519
588
|
}
|
|
520
589
|
clone() {
|
|
521
590
|
const qb = new QueryBuilder(this.mainAlias.entityName, this.metadata, this.driver, this.context, this.mainAlias.aliasName, this.connectionType, this.em);
|
|
@@ -523,7 +592,7 @@ class QueryBuilder {
|
|
|
523
592
|
// clone array/object properties
|
|
524
593
|
const properties = [
|
|
525
594
|
'flags', '_populate', '_populateWhere', '_populateMap', '_joins', '_joinedProps', '_cond', '_data', '_orderBy',
|
|
526
|
-
'_schema', '_indexHint', '_cache', 'subQueries', 'lockMode', 'lockTables',
|
|
595
|
+
'_schema', '_indexHint', '_cache', 'subQueries', 'lockMode', 'lockTables', '_groupBy', '_having', '_returning',
|
|
527
596
|
];
|
|
528
597
|
properties.forEach(prop => qb[prop] = core_1.Utils.copy(this[prop]));
|
|
529
598
|
/* istanbul ignore else */
|
|
@@ -554,11 +623,15 @@ class QueryBuilder {
|
|
|
554
623
|
joinReference(field, alias, cond, type, path) {
|
|
555
624
|
this.ensureNotFinalized();
|
|
556
625
|
const [fromAlias, fromField] = this.helper.splitField(field);
|
|
557
|
-
const
|
|
626
|
+
const q = (str) => `'${str}'`;
|
|
627
|
+
if (!this._aliases[fromAlias]) {
|
|
628
|
+
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(', ')}.`);
|
|
629
|
+
}
|
|
630
|
+
const entityName = this._aliases[fromAlias].entityName;
|
|
558
631
|
const meta = this.metadata.get(entityName);
|
|
559
632
|
const prop = meta.properties[fromField];
|
|
560
633
|
if (!prop) {
|
|
561
|
-
throw new Error(`Trying to join ${field}, but ${fromField} is not a defined relation on ${meta.className}
|
|
634
|
+
throw new Error(`Trying to join ${q(field)}, but ${q(fromField)} is not a defined relation on ${meta.className}.`);
|
|
562
635
|
}
|
|
563
636
|
this.createAlias(prop.type, alias);
|
|
564
637
|
cond = core_1.QueryHelper.processWhere({
|
|
@@ -570,11 +643,11 @@ class QueryBuilder {
|
|
|
570
643
|
aliased: !this.type || [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(this.type),
|
|
571
644
|
});
|
|
572
645
|
let aliasedName = `${fromAlias}.${prop.name}#${alias}`;
|
|
573
|
-
path
|
|
574
|
-
if (prop.
|
|
646
|
+
path ??= `${(Object.values(this._joins).find(j => j.alias === fromAlias)?.path ?? entityName)}.${prop.name}`;
|
|
647
|
+
if (prop.kind === core_1.ReferenceKind.ONE_TO_MANY) {
|
|
575
648
|
this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond);
|
|
576
649
|
}
|
|
577
|
-
else if (prop.
|
|
650
|
+
else if (prop.kind === core_1.ReferenceKind.MANY_TO_MANY) {
|
|
578
651
|
let pivotAlias = alias;
|
|
579
652
|
if (type !== 'pivotJoin') {
|
|
580
653
|
const oldPivotAlias = this.getAliasForJoinPath(path + '[pivot]');
|
|
@@ -585,7 +658,7 @@ class QueryBuilder {
|
|
|
585
658
|
Object.assign(this._joins, joins);
|
|
586
659
|
this.createAlias(prop.pivotEntity, pivotAlias);
|
|
587
660
|
}
|
|
588
|
-
else if (prop.
|
|
661
|
+
else if (prop.kind === core_1.ReferenceKind.ONE_TO_ONE) {
|
|
589
662
|
this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond);
|
|
590
663
|
}
|
|
591
664
|
else { // MANY_TO_ONE
|
|
@@ -599,17 +672,25 @@ class QueryBuilder {
|
|
|
599
672
|
prepareFields(fields, type = 'where') {
|
|
600
673
|
const ret = [];
|
|
601
674
|
fields.forEach(field => {
|
|
675
|
+
const rawField = core_1.RawQueryFragment.getKnownFragment(field);
|
|
676
|
+
if (rawField) {
|
|
677
|
+
const sql = this.platform.formatQuery(rawField.sql, rawField.params);
|
|
678
|
+
ret.push(this.knex.raw(sql));
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
602
681
|
if (!core_1.Utils.isString(field)) {
|
|
603
|
-
|
|
682
|
+
ret.push(field);
|
|
683
|
+
return;
|
|
604
684
|
}
|
|
605
685
|
const join = Object.keys(this._joins).find(k => field === k.substring(0, k.indexOf('#')));
|
|
606
686
|
if (join && type === 'where') {
|
|
607
|
-
|
|
687
|
+
ret.push(...this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[join]));
|
|
688
|
+
return;
|
|
608
689
|
}
|
|
609
690
|
const [a, f] = this.helper.splitField(field);
|
|
610
691
|
const prop = this.helper.getProperty(f, a);
|
|
611
692
|
/* istanbul ignore next */
|
|
612
|
-
if (prop && [core_1.
|
|
693
|
+
if (prop && [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
|
|
613
694
|
return;
|
|
614
695
|
}
|
|
615
696
|
if (prop?.embedded) {
|
|
@@ -617,7 +698,7 @@ class QueryBuilder {
|
|
|
617
698
|
ret.push(fieldName);
|
|
618
699
|
return;
|
|
619
700
|
}
|
|
620
|
-
if (prop?.
|
|
701
|
+
if (prop?.kind === core_1.ReferenceKind.EMBEDDED) {
|
|
621
702
|
if (prop.object) {
|
|
622
703
|
ret.push(this.helper.mapper(prop.fieldNames[0], this.type));
|
|
623
704
|
}
|
|
@@ -640,7 +721,7 @@ class QueryBuilder {
|
|
|
640
721
|
});
|
|
641
722
|
const meta = this.mainAlias.metadata;
|
|
642
723
|
/* istanbul ignore next */
|
|
643
|
-
const requiresSQLConversion = meta?.props.filter(p => p.
|
|
724
|
+
const requiresSQLConversion = meta?.props.filter(p => p.hasConvertToJSValueSQL) ?? [];
|
|
644
725
|
if (this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES) && (fields.includes('*') || fields.includes(`${this.mainAlias.aliasName}.*`)) && requiresSQLConversion.length > 0) {
|
|
645
726
|
requiresSQLConversion.forEach(p => ret.push(this.helper.mapper(p.name, this.type)));
|
|
646
727
|
}
|
|
@@ -649,7 +730,7 @@ class QueryBuilder {
|
|
|
649
730
|
const cols = this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[f]);
|
|
650
731
|
ret.push(...cols);
|
|
651
732
|
}
|
|
652
|
-
if (this._joins[f].prop.
|
|
733
|
+
if (this._joins[f].prop.kind !== core_1.ReferenceKind.ONE_TO_ONE && this._joins[f].inverseJoinColumns) {
|
|
653
734
|
this._joins[f].inverseJoinColumns.forEach(inverseJoinColumn => {
|
|
654
735
|
core_1.Utils.renameKey(this._cond, inverseJoinColumn, `${this._joins[f].alias}.${inverseJoinColumn}`);
|
|
655
736
|
});
|
|
@@ -668,7 +749,7 @@ class QueryBuilder {
|
|
|
668
749
|
}
|
|
669
750
|
if (data) {
|
|
670
751
|
if (core_1.Utils.isEntity(data)) {
|
|
671
|
-
data = (0, core_1.
|
|
752
|
+
data = this.em?.getComparator().prepareEntity(data) ?? (0, core_1.serialize)(data);
|
|
672
753
|
}
|
|
673
754
|
this._data = this.helper.processData(data, this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES));
|
|
674
755
|
}
|
|
@@ -722,6 +803,24 @@ class QueryBuilder {
|
|
|
722
803
|
}
|
|
723
804
|
return qb;
|
|
724
805
|
}
|
|
806
|
+
applyDiscriminatorCondition() {
|
|
807
|
+
const meta = this.mainAlias.metadata;
|
|
808
|
+
if (!meta?.discriminatorValue) {
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
811
|
+
const types = Object.values(meta.root.discriminatorMap).map(cls => this.metadata.find(cls));
|
|
812
|
+
const children = [];
|
|
813
|
+
const lookUpChildren = (ret, type) => {
|
|
814
|
+
const children = types.filter(meta2 => meta2.extends === type);
|
|
815
|
+
children.forEach(m => lookUpChildren(ret, m.className));
|
|
816
|
+
ret.push(...children.filter(c => c.discriminatorValue));
|
|
817
|
+
return children;
|
|
818
|
+
};
|
|
819
|
+
lookUpChildren(children, meta.className);
|
|
820
|
+
this.andWhere({
|
|
821
|
+
[meta.root.discriminatorColumn]: children.length > 0 ? { $in: [meta.discriminatorValue, ...children.map(c => c.discriminatorValue)] } : meta.discriminatorValue,
|
|
822
|
+
});
|
|
823
|
+
}
|
|
725
824
|
finalize() {
|
|
726
825
|
if (this.finalized) {
|
|
727
826
|
return;
|
|
@@ -730,10 +829,11 @@ class QueryBuilder {
|
|
|
730
829
|
this.select('*');
|
|
731
830
|
}
|
|
732
831
|
const meta = this.mainAlias.metadata;
|
|
832
|
+
this.applyDiscriminatorCondition();
|
|
733
833
|
if (meta && this.flags.has(core_1.QueryFlag.AUTO_JOIN_ONE_TO_ONE_OWNER)) {
|
|
734
834
|
const relationsToPopulate = this._populate.map(({ field }) => field);
|
|
735
835
|
meta.relations
|
|
736
|
-
.filter(prop => prop.
|
|
836
|
+
.filter(prop => prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
|
|
737
837
|
.map(prop => ({ field: prop.name }))
|
|
738
838
|
.forEach(item => this._populate.push(item));
|
|
739
839
|
}
|
|
@@ -742,7 +842,8 @@ class QueryBuilder {
|
|
|
742
842
|
const aliasedField = `${fromAlias}.${fromField}`;
|
|
743
843
|
const join = Object.keys(this._joins).find(k => `${aliasedField}#${this._joins[k].alias}` === k);
|
|
744
844
|
if (join && this._joins[join] && this.helper.isOneToOneInverse(fromField)) {
|
|
745
|
-
|
|
845
|
+
this._populateMap[join] = this._joins[join].alias;
|
|
846
|
+
return;
|
|
746
847
|
}
|
|
747
848
|
if (this.metadata.find(field)?.pivotTable) { // pivot table entity
|
|
748
849
|
this.autoJoinPivotTable(field);
|
|
@@ -763,14 +864,20 @@ class QueryBuilder {
|
|
|
763
864
|
const aliased = this.knex.ref(prop.fieldNames[0]).toString();
|
|
764
865
|
return `${prop.formula(alias)} as ${aliased}`;
|
|
765
866
|
})
|
|
766
|
-
.filter(field => !this._fields.
|
|
767
|
-
|
|
867
|
+
.filter(field => !this._fields.some(f => {
|
|
868
|
+
if (f instanceof core_1.RawQueryFragment) {
|
|
869
|
+
return f.sql === field && f.params.length === 0;
|
|
870
|
+
}
|
|
871
|
+
return f === field;
|
|
872
|
+
}))
|
|
873
|
+
.forEach(field => this._fields.push((0, core_1.raw)(field)));
|
|
768
874
|
}
|
|
875
|
+
this.processPopulateWhere();
|
|
769
876
|
core_1.QueryHelper.processObjectParams(this._data);
|
|
770
877
|
core_1.QueryHelper.processObjectParams(this._cond);
|
|
771
878
|
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()) {
|
|
879
|
+
// automatically enable paginate flag when we detect to-many joins, but only if there is no `group by` clause
|
|
880
|
+
if (!this.flags.has(core_1.QueryFlag.DISABLE_PAGINATE) && this._groupBy.length === 0 && this.hasToManyJoins()) {
|
|
774
881
|
this.flags.add(core_1.QueryFlag.PAGINATE);
|
|
775
882
|
}
|
|
776
883
|
if (meta && this.flags.has(core_1.QueryFlag.PAGINATE) && (this._limit > 0 || this._offset > 0)) {
|
|
@@ -781,17 +888,54 @@ class QueryBuilder {
|
|
|
781
888
|
}
|
|
782
889
|
this.finalized = true;
|
|
783
890
|
}
|
|
891
|
+
processPopulateWhere() {
|
|
892
|
+
if (this._populateWhere == null || this._populateWhere === core_1.PopulateHint.ALL) {
|
|
893
|
+
return;
|
|
894
|
+
}
|
|
895
|
+
const joins = Object.values(this._joins);
|
|
896
|
+
joins.forEach(join => {
|
|
897
|
+
join.cond_ = join.cond;
|
|
898
|
+
join.cond = {};
|
|
899
|
+
});
|
|
900
|
+
const replaceOnConditions = (cond) => {
|
|
901
|
+
Object.keys(cond).forEach(k => {
|
|
902
|
+
if (core_1.Utils.isOperator(k)) {
|
|
903
|
+
if (Array.isArray(cond[k])) {
|
|
904
|
+
return cond[k].forEach((c) => replaceOnConditions(c));
|
|
905
|
+
}
|
|
906
|
+
return replaceOnConditions(cond[k]);
|
|
907
|
+
}
|
|
908
|
+
const [a] = this.helper.splitField(k);
|
|
909
|
+
const join = joins.find(j => j.alias === a);
|
|
910
|
+
if (join) {
|
|
911
|
+
join.cond = { ...join.cond, [k]: cond[k] };
|
|
912
|
+
}
|
|
913
|
+
});
|
|
914
|
+
};
|
|
915
|
+
if (this._populateWhere === core_1.PopulateHint.INFER) {
|
|
916
|
+
replaceOnConditions(this._cond);
|
|
917
|
+
}
|
|
918
|
+
else if (typeof this._populateWhere === 'object') {
|
|
919
|
+
const cond = CriteriaNodeFactory_1.CriteriaNodeFactory
|
|
920
|
+
.createNode(this.metadata, this.mainAlias.entityName, this._populateWhere)
|
|
921
|
+
.process(this);
|
|
922
|
+
replaceOnConditions(cond);
|
|
923
|
+
}
|
|
924
|
+
}
|
|
784
925
|
hasToManyJoins() {
|
|
785
926
|
return Object.values(this._joins).some(join => {
|
|
786
|
-
return [core_1.
|
|
927
|
+
return [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(join.prop.kind);
|
|
787
928
|
});
|
|
788
929
|
}
|
|
789
930
|
wrapPaginateSubQuery(meta) {
|
|
790
931
|
const pks = this.prepareFields(meta.primaryKeys, 'sub-query');
|
|
791
932
|
const subQuery = this.clone().select(pks).groupBy(pks).limit(this._limit);
|
|
933
|
+
// revert the on conditions added via populateWhere, we want to apply those only once
|
|
934
|
+
Object.values(subQuery._joins).forEach(join => join.cond = join.cond_ ?? {});
|
|
792
935
|
if (this._offset) {
|
|
793
936
|
subQuery.offset(this._offset);
|
|
794
937
|
}
|
|
938
|
+
const addToSelect = [];
|
|
795
939
|
if (this._orderBy.length > 0) {
|
|
796
940
|
const orderBy = [];
|
|
797
941
|
for (const orderMap of this._orderBy) {
|
|
@@ -799,28 +943,38 @@ class QueryBuilder {
|
|
|
799
943
|
const [a, f] = this.helper.splitField(field);
|
|
800
944
|
const prop = this.helper.getProperty(f, a);
|
|
801
945
|
const type = this.platform.castColumn(prop);
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
946
|
+
const fieldName = this.helper.mapper(field, this.type, undefined, null);
|
|
947
|
+
if (!prop?.persist && !prop?.formula) {
|
|
948
|
+
addToSelect.push(fieldName);
|
|
949
|
+
}
|
|
950
|
+
orderBy.push({ [(0, core_1.raw)(`min(${this.ref(fieldName)}${type})`)]: direction });
|
|
805
951
|
}
|
|
806
952
|
}
|
|
807
953
|
subQuery.orderBy(orderBy);
|
|
808
954
|
}
|
|
809
955
|
subQuery.finalized = true;
|
|
810
956
|
const knexQuery = subQuery.as(this.mainAlias.aliasName).clearSelect().select(pks);
|
|
957
|
+
if (addToSelect.length > 0) {
|
|
958
|
+
addToSelect.forEach(prop => {
|
|
959
|
+
const field = this._fields.find(field => {
|
|
960
|
+
if (typeof field === 'object' && field && '__as' in field) {
|
|
961
|
+
return field.__as === prop;
|
|
962
|
+
}
|
|
963
|
+
// not perfect, but should work most of the time, ideally we should check only the alias (`... as alias`)
|
|
964
|
+
return field.toString().includes(prop);
|
|
965
|
+
});
|
|
966
|
+
if (field) {
|
|
967
|
+
knexQuery.select(field);
|
|
968
|
+
}
|
|
969
|
+
});
|
|
970
|
+
}
|
|
811
971
|
// multiple sub-queries are needed to get around mysql limitations with order by + limit + where in + group by (o.O)
|
|
812
972
|
// https://stackoverflow.com/questions/17892762/mysql-this-version-of-mysql-doesnt-yet-support-limit-in-all-any-some-subqu
|
|
813
973
|
const subSubQuery = this.getKnex().select(pks).from(knexQuery);
|
|
974
|
+
subSubQuery.__raw = true; // tag it as there is now way to check via `instanceof`
|
|
814
975
|
this._limit = undefined;
|
|
815
976
|
this._offset = undefined;
|
|
816
|
-
const cond = this._cond;
|
|
817
977
|
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
978
|
}
|
|
825
979
|
wrapModifySubQuery(meta) {
|
|
826
980
|
const subQuery = this.clone();
|
|
@@ -841,8 +995,8 @@ class QueryBuilder {
|
|
|
841
995
|
const prop = this._cond[pivotMeta.name + '.' + owner.name] || this._orderBy[pivotMeta.name + '.' + owner.name] ? inverse : owner;
|
|
842
996
|
const pivotAlias = this.getNextAlias(pivotMeta.name);
|
|
843
997
|
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}`)));
|
|
998
|
+
core_1.Utils.renameKey(this._cond, `${field}.${owner.name}`, core_1.Utils.getPrimaryKeyHash(owner.fieldNames.map(fieldName => `${pivotAlias}.${fieldName}`)), true);
|
|
999
|
+
core_1.Utils.renameKey(this._cond, `${field}.${inverse.name}`, core_1.Utils.getPrimaryKeyHash(inverse.fieldNames.map(fieldName => `${pivotAlias}.${fieldName}`)), true);
|
|
846
1000
|
this._populateMap[field] = this._joins[field].alias;
|
|
847
1001
|
}
|
|
848
1002
|
getSchema(alias) {
|
|
@@ -864,11 +1018,11 @@ class QueryBuilder {
|
|
|
864
1018
|
fromSubQuery(target, aliasName) {
|
|
865
1019
|
const subQuery = target.getKnexQuery();
|
|
866
1020
|
const { entityName } = target.mainAlias;
|
|
867
|
-
aliasName
|
|
1021
|
+
aliasName ??= this.getNextAlias(entityName);
|
|
868
1022
|
this.createMainAlias(entityName, aliasName, subQuery);
|
|
869
1023
|
}
|
|
870
1024
|
fromEntityName(entityName, aliasName) {
|
|
871
|
-
aliasName
|
|
1025
|
+
aliasName ??= this._mainAlias?.aliasName ?? this.getNextAlias(entityName);
|
|
872
1026
|
this.createMainAlias(entityName, aliasName);
|
|
873
1027
|
}
|
|
874
1028
|
createQueryBuilderHelper() {
|
|
@@ -885,5 +1039,27 @@ class QueryBuilder {
|
|
|
885
1039
|
throw new Error('This QueryBuilder instance is already finalized, clone it first if you want to modify it.');
|
|
886
1040
|
}
|
|
887
1041
|
}
|
|
1042
|
+
/* istanbul ignore next */
|
|
1043
|
+
[(_QueryBuilder_query = new WeakMap(), util_1.inspect.custom)](depth) {
|
|
1044
|
+
const object = { ...this };
|
|
1045
|
+
const hidden = ['metadata', 'driver', 'context', 'platform', 'knex', 'type'];
|
|
1046
|
+
Object.keys(object).filter(k => k.startsWith('_')).forEach(k => delete object[k]);
|
|
1047
|
+
Object.keys(object).filter(k => object[k] == null).forEach(k => delete object[k]);
|
|
1048
|
+
hidden.forEach(k => delete object[k]);
|
|
1049
|
+
let prefix = this.type ? this.type.substring(0, 1) + this.type.toLowerCase().substring(1) : '';
|
|
1050
|
+
if (this._data) {
|
|
1051
|
+
object.data = this._data;
|
|
1052
|
+
}
|
|
1053
|
+
if (!core_1.Utils.isEmpty(this._cond)) {
|
|
1054
|
+
object.where = this._cond;
|
|
1055
|
+
}
|
|
1056
|
+
if (this._onConflict?.[0]) {
|
|
1057
|
+
prefix = 'Upsert';
|
|
1058
|
+
object.onConflict = this._onConflict[0];
|
|
1059
|
+
}
|
|
1060
|
+
const name = this._mainAlias ? `${prefix}QueryBuilder<${this._mainAlias?.entityName}>` : 'QueryBuilder';
|
|
1061
|
+
const ret = (0, util_1.inspect)(object, { depth });
|
|
1062
|
+
return ret === '[Object]' ? `[${name}]` : name + ' ' + ret;
|
|
1063
|
+
}
|
|
888
1064
|
}
|
|
889
1065
|
exports.QueryBuilder = QueryBuilder;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Knex } from 'knex';
|
|
2
|
-
import type { Dictionary, EntityData, EntityMetadata, EntityProperty, FlatQueryOrderMap, QBFilterQuery } from '@mikro-orm/core';
|
|
2
|
+
import type { Dictionary, EntityData, EntityKey, EntityMetadata, EntityProperty, FlatQueryOrderMap, QBFilterQuery } from '@mikro-orm/core';
|
|
3
3
|
import { LockMode } from '@mikro-orm/core';
|
|
4
4
|
import { QueryType } from './enums';
|
|
5
5
|
import type { Field, JoinOptions } from '../typings';
|
|
@@ -43,16 +43,14 @@ export declare class QueryBuilderHelper {
|
|
|
43
43
|
}[], qb: Knex.QueryBuilder): void;
|
|
44
44
|
appendQueryCondition(type: QueryType, cond: any, qb: Knex.QueryBuilder, operator?: '$and' | '$or', method?: 'where' | 'having'): void;
|
|
45
45
|
private appendQuerySubCondition;
|
|
46
|
-
private processCustomExpression;
|
|
47
46
|
private processObjectSubCondition;
|
|
48
47
|
private getOperatorReplacement;
|
|
49
48
|
getQueryOrder(type: QueryType, orderBy: FlatQueryOrderMap | FlatQueryOrderMap[], populate: Dictionary<string>): string;
|
|
50
49
|
getQueryOrderFromObject(type: QueryType, orderBy: FlatQueryOrderMap, populate: Dictionary<string>): string;
|
|
51
|
-
finalize(type: QueryType, qb: Knex.QueryBuilder, meta?: EntityMetadata): void;
|
|
52
|
-
splitField(field:
|
|
50
|
+
finalize(type: QueryType, qb: Knex.QueryBuilder, meta?: EntityMetadata, data?: Dictionary, returning?: Field<any>[]): void;
|
|
51
|
+
splitField<T>(field: EntityKey<T>, greedyAlias?: boolean): [string, EntityKey<T>];
|
|
53
52
|
getLockSQL(qb: Knex.QueryBuilder, lockMode: LockMode, lockTables?: string[]): void;
|
|
54
53
|
updateVersionProperty(qb: Knex.QueryBuilder, data: Dictionary): void;
|
|
55
|
-
static isCustomExpression(field: string, hasAlias?: boolean): boolean;
|
|
56
54
|
private prefix;
|
|
57
55
|
private appendGroupCondition;
|
|
58
56
|
private isPrefixed;
|