@mikro-orm/knex 6.0.0-dev.7 → 6.0.0-dev.71
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 +22 -14
- package/AbstractSqlDriver.d.ts +17 -17
- package/AbstractSqlDriver.js +111 -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.d.ts +1 -0
- package/index.js +1 -0
- package/index.mjs +174 -0
- package/package.json +5 -5
- 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 +20 -5
- package/query/QueryBuilder.js +215 -50
- package/query/QueryBuilderHelper.d.ts +3 -5
- package/query/QueryBuilderHelper.js +90 -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 +10 -9
- package/schema/DatabaseTable.js +25 -24
- package/schema/SchemaComparator.d.ts +4 -4
- package/schema/SchemaComparator.js +9 -2
- package/schema/SchemaHelper.d.ts +14 -12
- package/schema/SchemaHelper.js +10 -4
- package/schema/SqlSchemaGenerator.d.ts +2 -4
- package/schema/SqlSchemaGenerator.js +40 -20
- package/typings.d.ts +15 -13
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);
|
|
@@ -164,9 +179,15 @@ class QueryBuilder {
|
|
|
164
179
|
}
|
|
165
180
|
where(cond, params, operator) {
|
|
166
181
|
this.ensureNotFinalized();
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
182
|
+
const rawField = core_1.RawQueryFragment.getKnownFragment(cond);
|
|
183
|
+
if (rawField) {
|
|
184
|
+
const sql = this.platform.formatQuery(rawField.sql, rawField.params);
|
|
185
|
+
cond = { [(0, core_1.raw)(`(${sql})`)]: core_1.Utils.asArray(params) };
|
|
186
|
+
operator ??= '$and';
|
|
187
|
+
}
|
|
188
|
+
else if (core_1.Utils.isString(cond)) {
|
|
189
|
+
cond = { [(0, core_1.raw)(`(${cond})`, core_1.Utils.asArray(params))]: [] };
|
|
190
|
+
operator ??= '$and';
|
|
170
191
|
}
|
|
171
192
|
else {
|
|
172
193
|
cond = core_1.QueryHelper.processWhere({
|
|
@@ -234,7 +255,7 @@ class QueryBuilder {
|
|
|
234
255
|
having(cond = {}, params) {
|
|
235
256
|
this.ensureNotFinalized();
|
|
236
257
|
if (core_1.Utils.isString(cond)) {
|
|
237
|
-
cond = { [`(${cond})
|
|
258
|
+
cond = { [(0, core_1.raw)(`(${cond})`, params)]: [] };
|
|
238
259
|
}
|
|
239
260
|
this._having = CriteriaNodeFactory_1.CriteriaNodeFactory.createNode(this.metadata, this.mainAlias.entityName, cond).process(this);
|
|
240
261
|
return this;
|
|
@@ -277,11 +298,6 @@ class QueryBuilder {
|
|
|
277
298
|
ref(field) {
|
|
278
299
|
return this.knex.ref(field);
|
|
279
300
|
}
|
|
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
301
|
limit(limit, offset = 0) {
|
|
286
302
|
this.ensureNotFinalized();
|
|
287
303
|
this._limit = limit;
|
|
@@ -354,13 +370,15 @@ class QueryBuilder {
|
|
|
354
370
|
getKnexQuery() {
|
|
355
371
|
this.finalize();
|
|
356
372
|
const qb = this.getQueryBase();
|
|
373
|
+
qb.__raw = true; // tag it as there is now way to check via `instanceof`
|
|
357
374
|
core_1.Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type ?? enums_1.QueryType.SELECT, this._cond, qb), this._cond && !this._onConflict);
|
|
358
375
|
core_1.Utils.runIfNotEmpty(() => qb.groupBy(this.prepareFields(this._groupBy, 'groupBy')), this._groupBy);
|
|
359
376
|
core_1.Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type ?? enums_1.QueryType.SELECT, this._having, qb, undefined, 'having'), this._having);
|
|
360
377
|
core_1.Utils.runIfNotEmpty(() => {
|
|
361
378
|
const queryOrder = this.helper.getQueryOrder(this.type ?? enums_1.QueryType.SELECT, this._orderBy, this._populateMap);
|
|
362
379
|
if (queryOrder) {
|
|
363
|
-
|
|
380
|
+
qb.orderByRaw(queryOrder);
|
|
381
|
+
return;
|
|
364
382
|
}
|
|
365
383
|
}, this._orderBy);
|
|
366
384
|
core_1.Utils.runIfNotEmpty(() => qb.limit(this._limit), this._limit != null);
|
|
@@ -372,26 +390,34 @@ class QueryBuilder {
|
|
|
372
390
|
if (this.lockMode) {
|
|
373
391
|
this.helper.getLockSQL(qb, this.lockMode, this.lockTables);
|
|
374
392
|
}
|
|
375
|
-
this.helper.finalize(this.type ?? enums_1.QueryType.SELECT, qb, this.mainAlias.metadata);
|
|
393
|
+
this.helper.finalize(this.type ?? enums_1.QueryType.SELECT, qb, this.mainAlias.metadata, this._data);
|
|
376
394
|
return qb;
|
|
377
395
|
}
|
|
378
396
|
/**
|
|
379
397
|
* Returns the query with parameters as wildcards.
|
|
380
398
|
*/
|
|
381
399
|
getQuery() {
|
|
382
|
-
return this.
|
|
400
|
+
return this.toQuery().sql;
|
|
401
|
+
}
|
|
402
|
+
toQuery() {
|
|
403
|
+
if (__classPrivateFieldGet(this, _QueryBuilder_query, "f")) {
|
|
404
|
+
return __classPrivateFieldGet(this, _QueryBuilder_query, "f");
|
|
405
|
+
}
|
|
406
|
+
const sql = this.getKnexQuery().toSQL();
|
|
407
|
+
const query = sql.toNative();
|
|
408
|
+
return __classPrivateFieldSet(this, _QueryBuilder_query, { sql: query.sql, params: query.bindings ?? [], _sql: sql }, "f");
|
|
383
409
|
}
|
|
384
410
|
/**
|
|
385
411
|
* Returns the list of all parameters for this query.
|
|
386
412
|
*/
|
|
387
413
|
getParams() {
|
|
388
|
-
return this.
|
|
414
|
+
return this.toQuery().params;
|
|
389
415
|
}
|
|
390
416
|
/**
|
|
391
417
|
* Returns raw interpolated query string with all the parameters inlined.
|
|
392
418
|
*/
|
|
393
419
|
getFormattedQuery() {
|
|
394
|
-
const query = this.
|
|
420
|
+
const query = this.toQuery()._sql;
|
|
395
421
|
return this.platform.formatQuery(query.sql, query.bindings);
|
|
396
422
|
}
|
|
397
423
|
/**
|
|
@@ -433,7 +459,7 @@ class QueryBuilder {
|
|
|
433
459
|
return cached.data;
|
|
434
460
|
}
|
|
435
461
|
const type = this.connectionType || (method === 'run' ? 'write' : 'read');
|
|
436
|
-
const res = await this.driver.getConnection(type).execute(query.sql, query.bindings, method, this.context);
|
|
462
|
+
const res = await this.driver.getConnection(type).execute(query.sql, query.bindings, method, this.context, this.logging);
|
|
437
463
|
const meta = this.mainAlias.metadata;
|
|
438
464
|
if (!mapResults || !meta) {
|
|
439
465
|
await this.em?.storeCache(this._cache, cached, res);
|
|
@@ -464,7 +490,27 @@ class QueryBuilder {
|
|
|
464
490
|
if (this._joinedProps.size > 0) {
|
|
465
491
|
res = this.driver.mergeJoinedResult(res, this.mainAlias.metadata);
|
|
466
492
|
}
|
|
467
|
-
|
|
493
|
+
const entities = [];
|
|
494
|
+
function propagatePopulateHint(entity, hint) {
|
|
495
|
+
(0, core_1.helper)(entity).__serializationContext.populate ??= hint;
|
|
496
|
+
hint.forEach(pop => {
|
|
497
|
+
const value = entity[pop.field];
|
|
498
|
+
if (core_1.Utils.isEntity(value, true)) {
|
|
499
|
+
(0, core_1.helper)(value).populated();
|
|
500
|
+
propagatePopulateHint(value, pop.children ?? []);
|
|
501
|
+
}
|
|
502
|
+
else if (core_1.Utils.isCollection(value)) {
|
|
503
|
+
value.populated();
|
|
504
|
+
value.getItems(false).forEach(item => propagatePopulateHint(item, pop.children ?? []));
|
|
505
|
+
}
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
for (const r of res) {
|
|
509
|
+
const entity = this.em.map(this.mainAlias.entityName, r, { schema: this._schema });
|
|
510
|
+
propagatePopulateHint(entity, this._populate);
|
|
511
|
+
entities.push(entity);
|
|
512
|
+
}
|
|
513
|
+
return entities;
|
|
468
514
|
}
|
|
469
515
|
/**
|
|
470
516
|
* Executes the query, returning the first result or null
|
|
@@ -488,11 +534,24 @@ class QueryBuilder {
|
|
|
488
534
|
}
|
|
489
535
|
return res ? +res.count : 0;
|
|
490
536
|
}
|
|
537
|
+
/**
|
|
538
|
+
* Executes the query, returning both array of results and total count query (without offset and limit).
|
|
539
|
+
*/
|
|
540
|
+
async getResultAndCount() {
|
|
541
|
+
return Promise.all([
|
|
542
|
+
this.getResultList(),
|
|
543
|
+
this.getCount(),
|
|
544
|
+
]);
|
|
545
|
+
}
|
|
491
546
|
/**
|
|
492
547
|
* Provides promise-like interface so we can await the QB instance.
|
|
493
548
|
*/
|
|
494
549
|
then(onfulfilled, onrejected) {
|
|
495
|
-
|
|
550
|
+
let type = this.type ?? enums_1.QueryType.SELECT;
|
|
551
|
+
if (this.flags.has(core_1.QueryFlag.UPDATE_SUB_QUERY) || this.flags.has(core_1.QueryFlag.DELETE_SUB_QUERY)) {
|
|
552
|
+
type = enums_1.QueryType.UPDATE;
|
|
553
|
+
}
|
|
554
|
+
switch (type) {
|
|
496
555
|
case enums_1.QueryType.INSERT:
|
|
497
556
|
case enums_1.QueryType.UPDATE:
|
|
498
557
|
case enums_1.QueryType.DELETE:
|
|
@@ -515,7 +574,10 @@ class QueryBuilder {
|
|
|
515
574
|
/* istanbul ignore next */
|
|
516
575
|
alias = meta?.properties[f]?.fieldNames[0] ?? alias;
|
|
517
576
|
}
|
|
518
|
-
|
|
577
|
+
const ret = qb.as(alias);
|
|
578
|
+
// tag the instance, so it is possible to detect it easily
|
|
579
|
+
Object.defineProperty(ret, '__as', { enumerable: false, value: alias });
|
|
580
|
+
return ret;
|
|
519
581
|
}
|
|
520
582
|
clone() {
|
|
521
583
|
const qb = new QueryBuilder(this.mainAlias.entityName, this.metadata, this.driver, this.context, this.mainAlias.aliasName, this.connectionType, this.em);
|
|
@@ -570,11 +632,11 @@ class QueryBuilder {
|
|
|
570
632
|
aliased: !this.type || [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(this.type),
|
|
571
633
|
});
|
|
572
634
|
let aliasedName = `${fromAlias}.${prop.name}#${alias}`;
|
|
573
|
-
path
|
|
574
|
-
if (prop.
|
|
635
|
+
path ??= `${(Object.values(this._joins).find(j => j.alias === fromAlias)?.path ?? entityName)}.${prop.name}`;
|
|
636
|
+
if (prop.kind === core_1.ReferenceKind.ONE_TO_MANY) {
|
|
575
637
|
this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond);
|
|
576
638
|
}
|
|
577
|
-
else if (prop.
|
|
639
|
+
else if (prop.kind === core_1.ReferenceKind.MANY_TO_MANY) {
|
|
578
640
|
let pivotAlias = alias;
|
|
579
641
|
if (type !== 'pivotJoin') {
|
|
580
642
|
const oldPivotAlias = this.getAliasForJoinPath(path + '[pivot]');
|
|
@@ -585,7 +647,7 @@ class QueryBuilder {
|
|
|
585
647
|
Object.assign(this._joins, joins);
|
|
586
648
|
this.createAlias(prop.pivotEntity, pivotAlias);
|
|
587
649
|
}
|
|
588
|
-
else if (prop.
|
|
650
|
+
else if (prop.kind === core_1.ReferenceKind.ONE_TO_ONE) {
|
|
589
651
|
this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond);
|
|
590
652
|
}
|
|
591
653
|
else { // MANY_TO_ONE
|
|
@@ -599,17 +661,25 @@ class QueryBuilder {
|
|
|
599
661
|
prepareFields(fields, type = 'where') {
|
|
600
662
|
const ret = [];
|
|
601
663
|
fields.forEach(field => {
|
|
664
|
+
const rawField = core_1.RawQueryFragment.getKnownFragment(field);
|
|
665
|
+
if (rawField) {
|
|
666
|
+
const sql = this.platform.formatQuery(rawField.sql, rawField.params);
|
|
667
|
+
ret.push(this.knex.raw(sql));
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
602
670
|
if (!core_1.Utils.isString(field)) {
|
|
603
|
-
|
|
671
|
+
ret.push(field);
|
|
672
|
+
return;
|
|
604
673
|
}
|
|
605
674
|
const join = Object.keys(this._joins).find(k => field === k.substring(0, k.indexOf('#')));
|
|
606
675
|
if (join && type === 'where') {
|
|
607
|
-
|
|
676
|
+
ret.push(...this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[join]));
|
|
677
|
+
return;
|
|
608
678
|
}
|
|
609
679
|
const [a, f] = this.helper.splitField(field);
|
|
610
680
|
const prop = this.helper.getProperty(f, a);
|
|
611
681
|
/* istanbul ignore next */
|
|
612
|
-
if (prop && [core_1.
|
|
682
|
+
if (prop && [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
|
|
613
683
|
return;
|
|
614
684
|
}
|
|
615
685
|
if (prop?.embedded) {
|
|
@@ -617,7 +687,7 @@ class QueryBuilder {
|
|
|
617
687
|
ret.push(fieldName);
|
|
618
688
|
return;
|
|
619
689
|
}
|
|
620
|
-
if (prop?.
|
|
690
|
+
if (prop?.kind === core_1.ReferenceKind.EMBEDDED) {
|
|
621
691
|
if (prop.object) {
|
|
622
692
|
ret.push(this.helper.mapper(prop.fieldNames[0], this.type));
|
|
623
693
|
}
|
|
@@ -640,7 +710,7 @@ class QueryBuilder {
|
|
|
640
710
|
});
|
|
641
711
|
const meta = this.mainAlias.metadata;
|
|
642
712
|
/* istanbul ignore next */
|
|
643
|
-
const requiresSQLConversion = meta?.props.filter(p => p.
|
|
713
|
+
const requiresSQLConversion = meta?.props.filter(p => p.hasConvertToJSValueSQL) ?? [];
|
|
644
714
|
if (this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES) && (fields.includes('*') || fields.includes(`${this.mainAlias.aliasName}.*`)) && requiresSQLConversion.length > 0) {
|
|
645
715
|
requiresSQLConversion.forEach(p => ret.push(this.helper.mapper(p.name, this.type)));
|
|
646
716
|
}
|
|
@@ -649,7 +719,7 @@ class QueryBuilder {
|
|
|
649
719
|
const cols = this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[f]);
|
|
650
720
|
ret.push(...cols);
|
|
651
721
|
}
|
|
652
|
-
if (this._joins[f].prop.
|
|
722
|
+
if (this._joins[f].prop.kind !== core_1.ReferenceKind.ONE_TO_ONE && this._joins[f].inverseJoinColumns) {
|
|
653
723
|
this._joins[f].inverseJoinColumns.forEach(inverseJoinColumn => {
|
|
654
724
|
core_1.Utils.renameKey(this._cond, inverseJoinColumn, `${this._joins[f].alias}.${inverseJoinColumn}`);
|
|
655
725
|
});
|
|
@@ -668,7 +738,7 @@ class QueryBuilder {
|
|
|
668
738
|
}
|
|
669
739
|
if (data) {
|
|
670
740
|
if (core_1.Utils.isEntity(data)) {
|
|
671
|
-
data = (0, core_1.
|
|
741
|
+
data = this.em?.getComparator().prepareEntity(data) ?? (0, core_1.serialize)(data);
|
|
672
742
|
}
|
|
673
743
|
this._data = this.helper.processData(data, this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES));
|
|
674
744
|
}
|
|
@@ -722,6 +792,24 @@ class QueryBuilder {
|
|
|
722
792
|
}
|
|
723
793
|
return qb;
|
|
724
794
|
}
|
|
795
|
+
applyDiscriminatorCondition() {
|
|
796
|
+
const meta = this.mainAlias.metadata;
|
|
797
|
+
if (!meta?.discriminatorValue) {
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
const types = Object.values(meta.root.discriminatorMap).map(cls => this.metadata.find(cls));
|
|
801
|
+
const children = [];
|
|
802
|
+
const lookUpChildren = (ret, type) => {
|
|
803
|
+
const children = types.filter(meta2 => meta2.extends === type);
|
|
804
|
+
children.forEach(m => lookUpChildren(ret, m.className));
|
|
805
|
+
ret.push(...children.filter(c => c.discriminatorValue));
|
|
806
|
+
return children;
|
|
807
|
+
};
|
|
808
|
+
lookUpChildren(children, meta.className);
|
|
809
|
+
this.andWhere({
|
|
810
|
+
[meta.root.discriminatorColumn]: children.length > 0 ? { $in: [meta.discriminatorValue, ...children.map(c => c.discriminatorValue)] } : meta.discriminatorValue,
|
|
811
|
+
});
|
|
812
|
+
}
|
|
725
813
|
finalize() {
|
|
726
814
|
if (this.finalized) {
|
|
727
815
|
return;
|
|
@@ -730,10 +818,11 @@ class QueryBuilder {
|
|
|
730
818
|
this.select('*');
|
|
731
819
|
}
|
|
732
820
|
const meta = this.mainAlias.metadata;
|
|
821
|
+
this.applyDiscriminatorCondition();
|
|
733
822
|
if (meta && this.flags.has(core_1.QueryFlag.AUTO_JOIN_ONE_TO_ONE_OWNER)) {
|
|
734
823
|
const relationsToPopulate = this._populate.map(({ field }) => field);
|
|
735
824
|
meta.relations
|
|
736
|
-
.filter(prop => prop.
|
|
825
|
+
.filter(prop => prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
|
|
737
826
|
.map(prop => ({ field: prop.name }))
|
|
738
827
|
.forEach(item => this._populate.push(item));
|
|
739
828
|
}
|
|
@@ -742,7 +831,8 @@ class QueryBuilder {
|
|
|
742
831
|
const aliasedField = `${fromAlias}.${fromField}`;
|
|
743
832
|
const join = Object.keys(this._joins).find(k => `${aliasedField}#${this._joins[k].alias}` === k);
|
|
744
833
|
if (join && this._joins[join] && this.helper.isOneToOneInverse(fromField)) {
|
|
745
|
-
|
|
834
|
+
this._populateMap[join] = this._joins[join].alias;
|
|
835
|
+
return;
|
|
746
836
|
}
|
|
747
837
|
if (this.metadata.find(field)?.pivotTable) { // pivot table entity
|
|
748
838
|
this.autoJoinPivotTable(field);
|
|
@@ -763,9 +853,15 @@ class QueryBuilder {
|
|
|
763
853
|
const aliased = this.knex.ref(prop.fieldNames[0]).toString();
|
|
764
854
|
return `${prop.formula(alias)} as ${aliased}`;
|
|
765
855
|
})
|
|
766
|
-
.filter(field => !this._fields.
|
|
767
|
-
|
|
856
|
+
.filter(field => !this._fields.some(f => {
|
|
857
|
+
if (f instanceof core_1.RawQueryFragment) {
|
|
858
|
+
return f.sql === field && f.params.length === 0;
|
|
859
|
+
}
|
|
860
|
+
return f === field;
|
|
861
|
+
}))
|
|
862
|
+
.forEach(field => this._fields.push((0, core_1.raw)(field)));
|
|
768
863
|
}
|
|
864
|
+
this.processPopulateWhere();
|
|
769
865
|
core_1.QueryHelper.processObjectParams(this._data);
|
|
770
866
|
core_1.QueryHelper.processObjectParams(this._cond);
|
|
771
867
|
core_1.QueryHelper.processObjectParams(this._having);
|
|
@@ -781,17 +877,54 @@ class QueryBuilder {
|
|
|
781
877
|
}
|
|
782
878
|
this.finalized = true;
|
|
783
879
|
}
|
|
880
|
+
processPopulateWhere() {
|
|
881
|
+
if (this._populateWhere == null || this._populateWhere === core_1.PopulateHint.ALL) {
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
const joins = Object.values(this._joins);
|
|
885
|
+
joins.forEach(join => {
|
|
886
|
+
join.cond_ = join.cond;
|
|
887
|
+
join.cond = {};
|
|
888
|
+
});
|
|
889
|
+
const replaceOnConditions = (cond) => {
|
|
890
|
+
Object.keys(cond).forEach(k => {
|
|
891
|
+
if (core_1.Utils.isOperator(k)) {
|
|
892
|
+
if (Array.isArray(cond[k])) {
|
|
893
|
+
return cond[k].forEach((c) => replaceOnConditions(c));
|
|
894
|
+
}
|
|
895
|
+
return replaceOnConditions(cond[k]);
|
|
896
|
+
}
|
|
897
|
+
const [a] = this.helper.splitField(k);
|
|
898
|
+
const join = joins.find(j => j.alias === a);
|
|
899
|
+
if (join) {
|
|
900
|
+
join.cond = { ...join.cond, [k]: cond[k] };
|
|
901
|
+
}
|
|
902
|
+
});
|
|
903
|
+
};
|
|
904
|
+
if (this._populateWhere === core_1.PopulateHint.INFER) {
|
|
905
|
+
replaceOnConditions(this._cond);
|
|
906
|
+
}
|
|
907
|
+
else if (typeof this._populateWhere === 'object') {
|
|
908
|
+
const cond = CriteriaNodeFactory_1.CriteriaNodeFactory
|
|
909
|
+
.createNode(this.metadata, this.mainAlias.entityName, this._populateWhere)
|
|
910
|
+
.process(this);
|
|
911
|
+
replaceOnConditions(cond);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
784
914
|
hasToManyJoins() {
|
|
785
915
|
return Object.values(this._joins).some(join => {
|
|
786
|
-
return [core_1.
|
|
916
|
+
return [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(join.prop.kind);
|
|
787
917
|
});
|
|
788
918
|
}
|
|
789
919
|
wrapPaginateSubQuery(meta) {
|
|
790
920
|
const pks = this.prepareFields(meta.primaryKeys, 'sub-query');
|
|
791
921
|
const subQuery = this.clone().select(pks).groupBy(pks).limit(this._limit);
|
|
922
|
+
// revert the on conditions added via populateWhere, we want to apply those only once
|
|
923
|
+
Object.values(subQuery._joins).forEach(join => join.cond = join.cond_ ?? {});
|
|
792
924
|
if (this._offset) {
|
|
793
925
|
subQuery.offset(this._offset);
|
|
794
926
|
}
|
|
927
|
+
const addToSelect = [];
|
|
795
928
|
if (this._orderBy.length > 0) {
|
|
796
929
|
const orderBy = [];
|
|
797
930
|
for (const orderMap of this._orderBy) {
|
|
@@ -799,28 +932,38 @@ class QueryBuilder {
|
|
|
799
932
|
const [a, f] = this.helper.splitField(field);
|
|
800
933
|
const prop = this.helper.getProperty(f, a);
|
|
801
934
|
const type = this.platform.castColumn(prop);
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
935
|
+
const fieldName = this.helper.mapper(field, this.type, undefined, null);
|
|
936
|
+
if (!prop?.persist && !prop?.formula) {
|
|
937
|
+
addToSelect.push(fieldName);
|
|
938
|
+
}
|
|
939
|
+
orderBy.push({ [(0, core_1.raw)(`min(${this.ref(fieldName)}${type})`)]: direction });
|
|
805
940
|
}
|
|
806
941
|
}
|
|
807
942
|
subQuery.orderBy(orderBy);
|
|
808
943
|
}
|
|
809
944
|
subQuery.finalized = true;
|
|
810
945
|
const knexQuery = subQuery.as(this.mainAlias.aliasName).clearSelect().select(pks);
|
|
946
|
+
if (addToSelect.length > 0) {
|
|
947
|
+
addToSelect.forEach(prop => {
|
|
948
|
+
const field = this._fields.find(field => {
|
|
949
|
+
if (typeof field === 'object' && field && '__as' in field) {
|
|
950
|
+
return field.__as === prop;
|
|
951
|
+
}
|
|
952
|
+
// not perfect, but should work most of the time, ideally we should check only the alias (`... as alias`)
|
|
953
|
+
return field.toString().includes(prop);
|
|
954
|
+
});
|
|
955
|
+
if (field) {
|
|
956
|
+
knexQuery.select(field);
|
|
957
|
+
}
|
|
958
|
+
});
|
|
959
|
+
}
|
|
811
960
|
// multiple sub-queries are needed to get around mysql limitations with order by + limit + where in + group by (o.O)
|
|
812
961
|
// https://stackoverflow.com/questions/17892762/mysql-this-version-of-mysql-doesnt-yet-support-limit-in-all-any-some-subqu
|
|
813
962
|
const subSubQuery = this.getKnex().select(pks).from(knexQuery);
|
|
963
|
+
subSubQuery.__raw = true; // tag it as there is now way to check via `instanceof`
|
|
814
964
|
this._limit = undefined;
|
|
815
965
|
this._offset = undefined;
|
|
816
|
-
const cond = this._cond;
|
|
817
966
|
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
967
|
}
|
|
825
968
|
wrapModifySubQuery(meta) {
|
|
826
969
|
const subQuery = this.clone();
|
|
@@ -841,8 +984,8 @@ class QueryBuilder {
|
|
|
841
984
|
const prop = this._cond[pivotMeta.name + '.' + owner.name] || this._orderBy[pivotMeta.name + '.' + owner.name] ? inverse : owner;
|
|
842
985
|
const pivotAlias = this.getNextAlias(pivotMeta.name);
|
|
843
986
|
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}`)));
|
|
987
|
+
core_1.Utils.renameKey(this._cond, `${field}.${owner.name}`, core_1.Utils.getPrimaryKeyHash(owner.fieldNames.map(fieldName => `${pivotAlias}.${fieldName}`)), true);
|
|
988
|
+
core_1.Utils.renameKey(this._cond, `${field}.${inverse.name}`, core_1.Utils.getPrimaryKeyHash(inverse.fieldNames.map(fieldName => `${pivotAlias}.${fieldName}`)), true);
|
|
846
989
|
this._populateMap[field] = this._joins[field].alias;
|
|
847
990
|
}
|
|
848
991
|
getSchema(alias) {
|
|
@@ -864,11 +1007,11 @@ class QueryBuilder {
|
|
|
864
1007
|
fromSubQuery(target, aliasName) {
|
|
865
1008
|
const subQuery = target.getKnexQuery();
|
|
866
1009
|
const { entityName } = target.mainAlias;
|
|
867
|
-
aliasName
|
|
1010
|
+
aliasName ??= this.getNextAlias(entityName);
|
|
868
1011
|
this.createMainAlias(entityName, aliasName, subQuery);
|
|
869
1012
|
}
|
|
870
1013
|
fromEntityName(entityName, aliasName) {
|
|
871
|
-
aliasName
|
|
1014
|
+
aliasName ??= this._mainAlias?.aliasName ?? this.getNextAlias(entityName);
|
|
872
1015
|
this.createMainAlias(entityName, aliasName);
|
|
873
1016
|
}
|
|
874
1017
|
createQueryBuilderHelper() {
|
|
@@ -885,5 +1028,27 @@ class QueryBuilder {
|
|
|
885
1028
|
throw new Error('This QueryBuilder instance is already finalized, clone it first if you want to modify it.');
|
|
886
1029
|
}
|
|
887
1030
|
}
|
|
1031
|
+
/* istanbul ignore next */
|
|
1032
|
+
[(_QueryBuilder_query = new WeakMap(), util_1.inspect.custom)](depth) {
|
|
1033
|
+
const object = { ...this };
|
|
1034
|
+
const hidden = ['metadata', 'driver', 'context', 'platform', 'knex', 'type'];
|
|
1035
|
+
Object.keys(object).filter(k => k.startsWith('_')).forEach(k => delete object[k]);
|
|
1036
|
+
Object.keys(object).filter(k => object[k] == null).forEach(k => delete object[k]);
|
|
1037
|
+
hidden.forEach(k => delete object[k]);
|
|
1038
|
+
let prefix = this.type ? this.type.substring(0, 1) + this.type.toLowerCase().substring(1) : '';
|
|
1039
|
+
if (this._data) {
|
|
1040
|
+
object.data = this._data;
|
|
1041
|
+
}
|
|
1042
|
+
if (!core_1.Utils.isEmpty(this._cond)) {
|
|
1043
|
+
object.where = this._cond;
|
|
1044
|
+
}
|
|
1045
|
+
if (this._onConflict?.[0]) {
|
|
1046
|
+
prefix = 'Upsert';
|
|
1047
|
+
object.onConflict = this._onConflict[0];
|
|
1048
|
+
}
|
|
1049
|
+
const name = this._mainAlias ? `${prefix}QueryBuilder<${this._mainAlias?.entityName}>` : 'QueryBuilder';
|
|
1050
|
+
const ret = (0, util_1.inspect)(object, { depth });
|
|
1051
|
+
return ret === '[Object]' ? `[${name}]` : name + ' ' + ret;
|
|
1052
|
+
}
|
|
888
1053
|
}
|
|
889
1054
|
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): 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;
|