nestjs-query-mikro-orm 0.0.8 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +602 -301
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +67 -68
- package/dist/index.d.ts +67 -68
- package/dist/index.mjs +580 -279
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -5
package/dist/index.cjs
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var nestjs = require('@mikro-orm/nestjs');
|
|
4
|
-
var core
|
|
5
|
-
var core = require('@mikro-orm/core');
|
|
4
|
+
var core = require('@nestjs-query/core');
|
|
5
|
+
var core$1 = require('@mikro-orm/core');
|
|
6
6
|
var assembler_serializer = require('@nestjs-query/core/dist/src/assemblers/assembler.serializer');
|
|
7
7
|
var common = require('@nestjs/common');
|
|
8
8
|
var classTransformer = require('class-transformer');
|
|
9
9
|
var merge = require('lodash.merge');
|
|
10
|
-
var camelCase = require('camel-case');
|
|
11
10
|
|
|
12
11
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
13
12
|
|
|
@@ -15,146 +14,6 @@ var merge__default = /*#__PURE__*/_interopDefault(merge);
|
|
|
15
14
|
|
|
16
15
|
var __defProp = Object.defineProperty;
|
|
17
16
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
18
|
-
var AGG_REGEXP = /(AVG|SUM|COUNT|MAX|MIN|GROUP_BY)_(.*)/;
|
|
19
|
-
var AggregateBuilder = class _AggregateBuilder {
|
|
20
|
-
static {
|
|
21
|
-
__name(this, "AggregateBuilder");
|
|
22
|
-
}
|
|
23
|
-
static async asyncConvertToAggregateResponse(responsePromise) {
|
|
24
|
-
const aggResponse = await responsePromise;
|
|
25
|
-
return this.convertToAggregateResponse(aggResponse);
|
|
26
|
-
}
|
|
27
|
-
static getAggregateSelects(query) {
|
|
28
|
-
return [
|
|
29
|
-
...this.getAggregateGroupBySelects(query),
|
|
30
|
-
...this.getAggregateFuncSelects(query)
|
|
31
|
-
];
|
|
32
|
-
}
|
|
33
|
-
static getAggregateGroupBySelects(query) {
|
|
34
|
-
return (query.groupBy ?? []).map((f) => this.getGroupByAlias(f));
|
|
35
|
-
}
|
|
36
|
-
static getAggregateFuncSelects(query) {
|
|
37
|
-
const aggs = [
|
|
38
|
-
[
|
|
39
|
-
"COUNT",
|
|
40
|
-
query.count
|
|
41
|
-
],
|
|
42
|
-
[
|
|
43
|
-
"SUM",
|
|
44
|
-
query.sum
|
|
45
|
-
],
|
|
46
|
-
[
|
|
47
|
-
"AVG",
|
|
48
|
-
query.avg
|
|
49
|
-
],
|
|
50
|
-
[
|
|
51
|
-
"MAX",
|
|
52
|
-
query.max
|
|
53
|
-
],
|
|
54
|
-
[
|
|
55
|
-
"MIN",
|
|
56
|
-
query.min
|
|
57
|
-
]
|
|
58
|
-
];
|
|
59
|
-
return aggs.reduce((cols, [func, fields]) => {
|
|
60
|
-
const aliases = (fields ?? []).map((f) => this.getAggregateAlias(func, f));
|
|
61
|
-
return [
|
|
62
|
-
...cols,
|
|
63
|
-
...aliases
|
|
64
|
-
];
|
|
65
|
-
}, []);
|
|
66
|
-
}
|
|
67
|
-
static getAggregateAlias(func, field) {
|
|
68
|
-
return `${func}_${field}`;
|
|
69
|
-
}
|
|
70
|
-
static getGroupByAlias(field) {
|
|
71
|
-
return `GROUP_BY_${field}`;
|
|
72
|
-
}
|
|
73
|
-
static convertToAggregateResponse(rawAggregates) {
|
|
74
|
-
return rawAggregates.map((response) => {
|
|
75
|
-
return Object.keys(response).reduce((agg, resultField) => {
|
|
76
|
-
const matchResult = AGG_REGEXP.exec(resultField);
|
|
77
|
-
if (!matchResult) {
|
|
78
|
-
throw new Error("Unknown aggregate column encountered.");
|
|
79
|
-
}
|
|
80
|
-
const [matchedFunc, matchedFieldName] = matchResult.slice(1);
|
|
81
|
-
const aggFunc = camelCase.camelCase(matchedFunc.toLowerCase());
|
|
82
|
-
const fieldName = matchedFieldName;
|
|
83
|
-
const aggResult = agg[aggFunc] || {};
|
|
84
|
-
return {
|
|
85
|
-
...agg,
|
|
86
|
-
[aggFunc]: {
|
|
87
|
-
...aggResult,
|
|
88
|
-
[fieldName]: response[resultField]
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
}, {});
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Gets the actual database column name for a property from entity metadata.
|
|
96
|
-
* @param metadata - the entity metadata
|
|
97
|
-
* @param propertyName - the property name
|
|
98
|
-
* @returns the database column name
|
|
99
|
-
*/
|
|
100
|
-
getColumnName(metadata, propertyName) {
|
|
101
|
-
const prop = metadata.properties[propertyName];
|
|
102
|
-
if (prop && prop.fieldNames && prop.fieldNames.length > 0) {
|
|
103
|
-
return prop.fieldNames[0];
|
|
104
|
-
}
|
|
105
|
-
return propertyName;
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Builds aggregate SELECT clause for MikroORM QueryBuilder.
|
|
109
|
-
* @param qb - the MikroORM QueryBuilder
|
|
110
|
-
* @param aggregate - the aggregates to select.
|
|
111
|
-
* @param alias - optional alias to use to qualify an identifier
|
|
112
|
-
*/
|
|
113
|
-
build(qb, aggregate, alias) {
|
|
114
|
-
const metadata = qb.mainAlias?.metadata;
|
|
115
|
-
const selects = [
|
|
116
|
-
...this.createGroupBySelect(aggregate.groupBy, alias, metadata),
|
|
117
|
-
...this.createAggSelect("COUNT", aggregate.count, alias, metadata),
|
|
118
|
-
...this.createAggSelect("SUM", aggregate.sum, alias, metadata),
|
|
119
|
-
...this.createAggSelect("AVG", aggregate.avg, alias, metadata),
|
|
120
|
-
...this.createAggSelect("MAX", aggregate.max, alias, metadata),
|
|
121
|
-
...this.createAggSelect("MIN", aggregate.min, alias, metadata)
|
|
122
|
-
];
|
|
123
|
-
if (!selects.length) {
|
|
124
|
-
throw new common.BadRequestException("No aggregate fields found.");
|
|
125
|
-
}
|
|
126
|
-
selects.forEach(([selectExpr, selectAlias]) => {
|
|
127
|
-
qb.addSelect(core.raw(`${selectExpr} as "${selectAlias}"`));
|
|
128
|
-
});
|
|
129
|
-
return qb;
|
|
130
|
-
}
|
|
131
|
-
createAggSelect(func, fields, alias, metadata) {
|
|
132
|
-
if (!fields) {
|
|
133
|
-
return [];
|
|
134
|
-
}
|
|
135
|
-
return fields.map((field) => {
|
|
136
|
-
const columnName = metadata ? this.getColumnName(metadata, field) : field;
|
|
137
|
-
const col = alias ? `\`${alias}\`.\`${columnName}\`` : `\`${columnName}\``;
|
|
138
|
-
return [
|
|
139
|
-
`${func}(${col})`,
|
|
140
|
-
_AggregateBuilder.getAggregateAlias(func, field)
|
|
141
|
-
];
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
createGroupBySelect(fields, alias, metadata) {
|
|
145
|
-
if (!fields) {
|
|
146
|
-
return [];
|
|
147
|
-
}
|
|
148
|
-
return fields.map((field) => {
|
|
149
|
-
const columnName = metadata ? this.getColumnName(metadata, field) : field;
|
|
150
|
-
const col = alias ? `\`${alias}\`.\`${columnName}\`` : `\`${columnName}\``;
|
|
151
|
-
return [
|
|
152
|
-
`${col}`,
|
|
153
|
-
_AggregateBuilder.getGroupByAlias(field)
|
|
154
|
-
];
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
};
|
|
158
17
|
|
|
159
18
|
// src/lib/query/where.builder.ts
|
|
160
19
|
var WhereBuilder = class {
|
|
@@ -394,81 +253,50 @@ var FilterQueryBuilder = class {
|
|
|
394
253
|
}
|
|
395
254
|
repo;
|
|
396
255
|
whereBuilder;
|
|
397
|
-
|
|
398
|
-
constructor(repo, whereBuilder = new WhereBuilder(), aggregateBuilder = new AggregateBuilder()) {
|
|
256
|
+
constructor(repo, whereBuilder = new WhereBuilder()) {
|
|
399
257
|
this.repo = repo;
|
|
400
258
|
this.whereBuilder = whereBuilder;
|
|
401
|
-
this.aggregateBuilder = aggregateBuilder;
|
|
402
259
|
}
|
|
403
260
|
/**
|
|
404
|
-
*
|
|
405
|
-
*
|
|
406
|
-
* @param query - the query to apply.
|
|
261
|
+
* NOTE: QueryBuilder-specific helpers removed; use `buildFindOptions` to
|
|
262
|
+
* produce a filter and options for `em.find`/`repo.find`.
|
|
407
263
|
*/
|
|
408
|
-
select(query) {
|
|
409
|
-
const alias = this.getEntityAlias();
|
|
410
|
-
const qb = this.createQueryBuilder(alias);
|
|
411
|
-
this.applyFilter(qb, query.filter, alias);
|
|
412
|
-
this.applySorting(qb, query.sorting, alias);
|
|
413
|
-
this.applyPaging(qb, query.paging);
|
|
414
|
-
return qb;
|
|
415
|
-
}
|
|
416
|
-
selectById(id, query) {
|
|
417
|
-
const alias = this.getEntityAlias();
|
|
418
|
-
const qb = this.createQueryBuilder(alias);
|
|
419
|
-
const metadata = this.repo.getEntityManager().getMetadata().get(this.repo.getEntityName());
|
|
420
|
-
const primaryKey = metadata.primaryKeys[0];
|
|
421
|
-
if (Array.isArray(id)) {
|
|
422
|
-
qb.where({
|
|
423
|
-
[primaryKey]: {
|
|
424
|
-
$in: id
|
|
425
|
-
}
|
|
426
|
-
});
|
|
427
|
-
} else {
|
|
428
|
-
qb.where({
|
|
429
|
-
[primaryKey]: id
|
|
430
|
-
});
|
|
431
|
-
}
|
|
432
|
-
this.applyFilter(qb, query.filter, alias);
|
|
433
|
-
this.applySorting(qb, query.sorting, alias);
|
|
434
|
-
this.applyPaging(qb, query.paging);
|
|
435
|
-
return qb;
|
|
436
|
-
}
|
|
437
|
-
aggregate(query, aggregate) {
|
|
438
|
-
const alias = this.getEntityAlias();
|
|
439
|
-
const qb = this.createQueryBuilder(alias);
|
|
440
|
-
this.applyAggregate(qb, aggregate, alias);
|
|
441
|
-
this.applyFilter(qb, query.filter, alias);
|
|
442
|
-
this.applyAggregateSorting(qb, aggregate.groupBy, alias);
|
|
443
|
-
this.applyGroupBy(qb, aggregate.groupBy, alias);
|
|
444
|
-
return qb;
|
|
445
|
-
}
|
|
446
264
|
/**
|
|
447
|
-
*
|
|
448
|
-
*
|
|
449
|
-
*
|
|
265
|
+
* Build a filter query and find options suitable for `em.find`/`repo.find` calls.
|
|
266
|
+
* This keeps usage DB-agnostic by returning plain filter objects and options
|
|
267
|
+
* instead of driver-specific QueryBuilder instances.
|
|
450
268
|
*/
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
269
|
+
buildFindOptions(query) {
|
|
270
|
+
const result = {};
|
|
271
|
+
if (query.filter) {
|
|
272
|
+
const mikroOrmFilter = this.whereBuilder.build(query.filter);
|
|
273
|
+
result.filterQuery = mikroOrmFilter;
|
|
274
|
+
}
|
|
275
|
+
const paging = query.paging;
|
|
276
|
+
const sorting = query.sorting;
|
|
277
|
+
if (paging && (paging.limit !== void 0 || paging.offset !== void 0) || sorting && sorting.length) {
|
|
278
|
+
const options = {};
|
|
279
|
+
if (paging) {
|
|
280
|
+
if (paging.limit !== void 0) options.limit = paging.limit;
|
|
281
|
+
if (paging.offset !== void 0) options.offset = paging.offset;
|
|
282
|
+
}
|
|
283
|
+
if (sorting && sorting.length > 0) {
|
|
284
|
+
const orderBy = sorting.reduce((acc, { field, direction, nulls }) => {
|
|
285
|
+
const order = direction === "ASC" ? "asc" : "desc";
|
|
286
|
+
let orderValue = order;
|
|
287
|
+
if (nulls) {
|
|
288
|
+
orderValue = `${order} ${nulls.toLowerCase().replace("_", " ")}`;
|
|
289
|
+
}
|
|
290
|
+
return {
|
|
291
|
+
...acc,
|
|
292
|
+
[field]: orderValue
|
|
293
|
+
};
|
|
294
|
+
}, {});
|
|
295
|
+
options.orderBy = orderBy;
|
|
296
|
+
}
|
|
297
|
+
result.options = options;
|
|
460
298
|
}
|
|
461
|
-
return
|
|
462
|
-
}
|
|
463
|
-
/**
|
|
464
|
-
* Applies the aggregate selects from a Query to a MikroORM QueryBuilder.
|
|
465
|
-
*
|
|
466
|
-
* @param qb - the MikroORM QueryBuilder.
|
|
467
|
-
* @param aggregate - the aggregates to select.
|
|
468
|
-
* @param alias - optional alias to use to qualify an identifier
|
|
469
|
-
*/
|
|
470
|
-
applyAggregate(qb, aggregate, alias) {
|
|
471
|
-
return this.aggregateBuilder.build(qb, aggregate, alias);
|
|
299
|
+
return result;
|
|
472
300
|
}
|
|
473
301
|
/**
|
|
474
302
|
* Applies the filter from a Query to a MikroORM QueryBuilder.
|
|
@@ -530,7 +358,7 @@ var FilterQueryBuilder = class {
|
|
|
530
358
|
* Create a MikroORM QueryBuilder.
|
|
531
359
|
*/
|
|
532
360
|
createQueryBuilder(alias) {
|
|
533
|
-
return this.repo.createQueryBuilder(alias);
|
|
361
|
+
return this.repo.createQueryBuilder?.(alias);
|
|
534
362
|
}
|
|
535
363
|
/**
|
|
536
364
|
* Gets the entity alias based on the entity name.
|
|
@@ -555,7 +383,7 @@ var FilterQueryBuilder = class {
|
|
|
555
383
|
}
|
|
556
384
|
getReferencedRelations(filter) {
|
|
557
385
|
const relationNames = this.relationNames;
|
|
558
|
-
const referencedFields = core
|
|
386
|
+
const referencedFields = core.getFilterFields(filter);
|
|
559
387
|
return referencedFields.filter((f) => relationNames.includes(f));
|
|
560
388
|
}
|
|
561
389
|
getReferencedRelationsRecursive(metadataOrFilter = {}, filter) {
|
|
@@ -619,10 +447,10 @@ var FilterQueryBuilder = class {
|
|
|
619
447
|
}
|
|
620
448
|
};
|
|
621
449
|
|
|
622
|
-
// src/lib/query/
|
|
623
|
-
var
|
|
450
|
+
// src/lib/query/comparison.builder.ts
|
|
451
|
+
var ComparisonBuilder = class {
|
|
624
452
|
static {
|
|
625
|
-
__name(this, "
|
|
453
|
+
__name(this, "ComparisonBuilder");
|
|
626
454
|
}
|
|
627
455
|
/**
|
|
628
456
|
* Maps a comparison operator to MikroORM filter format.
|
|
@@ -832,68 +660,162 @@ var RelationQueryBuilder = class {
|
|
|
832
660
|
this.filterQueryBuilder = new FilterQueryBuilder(relationRepo);
|
|
833
661
|
}
|
|
834
662
|
/**
|
|
835
|
-
*
|
|
836
|
-
* This is useful for testing or when you need to inspect/modify the query before execution.
|
|
663
|
+
* Executes a relation select using `em.find` so the implementation is database-agnostic.
|
|
837
664
|
*/
|
|
838
|
-
|
|
665
|
+
async selectAndExecute(entity, query) {
|
|
839
666
|
const relationMeta = this.getRelationMeta();
|
|
840
667
|
const em = this.repo.getEntityManager();
|
|
841
|
-
const
|
|
842
|
-
const
|
|
843
|
-
const
|
|
844
|
-
const
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
qb = this.filterQueryBuilder.applyFilter(qb, query.filter);
|
|
856
|
-
qb = this.filterQueryBuilder.applyPaging(qb, query.paging);
|
|
857
|
-
qb = this.filterQueryBuilder.applySorting(qb, query.sorting);
|
|
858
|
-
return qb;
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
const whereCondition = this.buildWhereCondition(entity, relationMeta);
|
|
862
|
-
qb = qb.where(whereCondition);
|
|
863
|
-
qb = this.filterQueryBuilder.applyFilter(qb, query.filter);
|
|
864
|
-
qb = this.filterQueryBuilder.applyPaging(qb, query.paging);
|
|
865
|
-
qb = this.filterQueryBuilder.applySorting(qb, query.sorting);
|
|
866
|
-
return qb;
|
|
867
|
-
}
|
|
868
|
-
/**
|
|
869
|
-
* Executes the select query and returns the results.
|
|
870
|
-
*/
|
|
871
|
-
async selectAndExecute(entity, query) {
|
|
872
|
-
const qb = this.select(entity, query);
|
|
873
|
-
return qb.getResultList();
|
|
668
|
+
const RelationEntity = relationMeta.type;
|
|
669
|
+
const baseWhere = this.buildWhereCondition(entity, relationMeta);
|
|
670
|
+
const { filterQuery, options } = this.filterQueryBuilder.buildFindOptions(query);
|
|
671
|
+
const finalWhere = filterQuery ? {
|
|
672
|
+
$and: [
|
|
673
|
+
baseWhere,
|
|
674
|
+
filterQuery
|
|
675
|
+
]
|
|
676
|
+
} : baseWhere;
|
|
677
|
+
const findOptions = {};
|
|
678
|
+
if (options?.orderBy) findOptions.orderBy = options.orderBy;
|
|
679
|
+
if (options?.limit !== void 0) findOptions.limit = options.limit;
|
|
680
|
+
if (options?.offset !== void 0) findOptions.offset = options.offset;
|
|
681
|
+
return await em.find(RelationEntity, finalWhere, findOptions);
|
|
874
682
|
}
|
|
875
683
|
async count(entity, query) {
|
|
876
684
|
const relationMeta = this.getRelationMeta();
|
|
877
685
|
const em = this.repo.getEntityManager();
|
|
878
|
-
const
|
|
879
|
-
|
|
880
|
-
const
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
686
|
+
const RelationEntity = relationMeta.type;
|
|
687
|
+
const baseWhere = this.buildWhereCondition(entity, relationMeta);
|
|
688
|
+
const { filterQuery } = this.filterQueryBuilder.buildFindOptions(query);
|
|
689
|
+
const finalWhere = filterQuery ? {
|
|
690
|
+
$and: [
|
|
691
|
+
baseWhere,
|
|
692
|
+
filterQuery
|
|
693
|
+
]
|
|
694
|
+
} : baseWhere;
|
|
695
|
+
return em.count(RelationEntity, finalWhere);
|
|
884
696
|
}
|
|
885
697
|
async aggregate(entity, query, aggregateQuery) {
|
|
886
698
|
const relationMeta = this.getRelationMeta();
|
|
887
699
|
const em = this.repo.getEntityManager();
|
|
888
|
-
const
|
|
889
|
-
|
|
890
|
-
const
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
700
|
+
const RelationEntity = relationMeta.type;
|
|
701
|
+
const baseWhere = this.buildWhereCondition(entity, relationMeta);
|
|
702
|
+
const { filterQuery } = this.filterQueryBuilder.buildFindOptions(query);
|
|
703
|
+
const finalWhere = filterQuery ? {
|
|
704
|
+
$and: [
|
|
705
|
+
baseWhere,
|
|
706
|
+
filterQuery
|
|
707
|
+
]
|
|
708
|
+
} : baseWhere;
|
|
709
|
+
const rows = await em.find(RelationEntity, finalWhere);
|
|
710
|
+
const aggs = aggregateQuery;
|
|
711
|
+
const groupBy = aggs.groupBy ?? [];
|
|
712
|
+
const makeAggKey = /* @__PURE__ */ __name((func, field) => `${func}_${field}`, "makeAggKey");
|
|
713
|
+
const makeGroupKey = /* @__PURE__ */ __name((field) => `GROUP_BY_${field}`, "makeGroupKey");
|
|
714
|
+
const records = [];
|
|
715
|
+
const isNumeric = /* @__PURE__ */ __name((v) => typeof v === "number" || v instanceof Date, "isNumeric");
|
|
716
|
+
if (groupBy.length === 0) {
|
|
717
|
+
const out = {};
|
|
718
|
+
const computeField = /* @__PURE__ */ __name((fn, field) => {
|
|
719
|
+
const values = rows.map((r) => r[field]).filter((v) => v !== void 0 && v !== null);
|
|
720
|
+
if (fn === "COUNT") {
|
|
721
|
+
out[makeAggKey("COUNT", field)] = values.length;
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
if (values.length === 0) {
|
|
725
|
+
out[makeAggKey(fn, field)] = null;
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
if (fn === "SUM" || fn === "AVG") {
|
|
729
|
+
const nums = values.map((v) => v instanceof Date ? v.getTime() : Number(v)).filter((n) => !Number.isNaN(n));
|
|
730
|
+
const sum = nums.reduce((s, v) => s + v, 0);
|
|
731
|
+
out[makeAggKey(fn, field)] = fn === "SUM" ? sum : nums.length ? sum / nums.length : null;
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
if (fn === "MAX") {
|
|
735
|
+
if (values.every(isNumeric)) {
|
|
736
|
+
const nums = values.map((v) => v instanceof Date ? v.getTime() : Number(v));
|
|
737
|
+
out[makeAggKey("MAX", field)] = Math.max(...nums);
|
|
738
|
+
} else {
|
|
739
|
+
out[makeAggKey("MAX", field)] = values.reduce((a, b) => String(a) > String(b) ? a : b);
|
|
740
|
+
}
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
if (fn === "MIN") {
|
|
744
|
+
if (values.every(isNumeric)) {
|
|
745
|
+
const nums = values.map((v) => v instanceof Date ? v.getTime() : Number(v));
|
|
746
|
+
out[makeAggKey("MIN", field)] = Math.min(...nums);
|
|
747
|
+
} else {
|
|
748
|
+
out[makeAggKey("MIN", field)] = values.reduce((a, b) => String(a) < String(b) ? a : b);
|
|
749
|
+
}
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
}, "computeField");
|
|
753
|
+
(aggs.count ?? []).forEach((f) => computeField("COUNT", String(f)));
|
|
754
|
+
(aggs.sum ?? []).forEach((f) => computeField("SUM", String(f)));
|
|
755
|
+
(aggs.avg ?? []).forEach((f) => computeField("AVG", String(f)));
|
|
756
|
+
(aggs.max ?? []).forEach((f) => computeField("MAX", String(f)));
|
|
757
|
+
(aggs.min ?? []).forEach((f) => computeField("MIN", String(f)));
|
|
758
|
+
records.push(out);
|
|
759
|
+
} else {
|
|
760
|
+
const groups = /* @__PURE__ */ new Map();
|
|
761
|
+
rows.forEach((r) => {
|
|
762
|
+
const keyParts = groupBy.map((g) => JSON.stringify(r[String(g)]));
|
|
763
|
+
const key = keyParts.join("|");
|
|
764
|
+
const arr = groups.get(key) ?? [];
|
|
765
|
+
arr.push(r);
|
|
766
|
+
groups.set(key, arr);
|
|
767
|
+
});
|
|
768
|
+
groups.forEach((groupRows, key) => {
|
|
769
|
+
const parts = key.split("|").map((p) => JSON.parse(p));
|
|
770
|
+
const out = {};
|
|
771
|
+
groupBy.forEach((g, i) => {
|
|
772
|
+
const val = parts[i];
|
|
773
|
+
out[makeGroupKey(String(g))] = typeof val === "boolean" ? val ? 1 : 0 : val;
|
|
774
|
+
});
|
|
775
|
+
const computeField = /* @__PURE__ */ __name((fn, field) => {
|
|
776
|
+
const values = groupRows.map((r) => r[field]).filter((v) => v !== void 0 && v !== null);
|
|
777
|
+
if (fn === "COUNT") {
|
|
778
|
+
out[makeAggKey("COUNT", field)] = values.length;
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
if (values.length === 0) {
|
|
782
|
+
out[makeAggKey(fn, field)] = null;
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
if (fn === "SUM" || fn === "AVG") {
|
|
786
|
+
const nums = values.map((v) => v instanceof Date ? v.getTime() : Number(v)).filter((n) => !Number.isNaN(n));
|
|
787
|
+
const sum = nums.reduce((s, v) => s + v, 0);
|
|
788
|
+
out[makeAggKey(fn, field)] = fn === "SUM" ? sum : nums.length ? sum / nums.length : null;
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
if (fn === "MAX") {
|
|
792
|
+
if (values.every(isNumeric)) {
|
|
793
|
+
const nums = values.map((v) => v instanceof Date ? v.getTime() : Number(v));
|
|
794
|
+
out[makeAggKey("MAX", field)] = Math.max(...nums);
|
|
795
|
+
} else {
|
|
796
|
+
out[makeAggKey("MAX", field)] = values.reduce((a, b) => String(a) > String(b) ? a : b);
|
|
797
|
+
}
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
if (fn === "MIN") {
|
|
801
|
+
if (values.every(isNumeric)) {
|
|
802
|
+
const nums = values.map((v) => v instanceof Date ? v.getTime() : Number(v));
|
|
803
|
+
out[makeAggKey("MIN", field)] = Math.min(...nums);
|
|
804
|
+
} else {
|
|
805
|
+
out[makeAggKey("MIN", field)] = values.reduce((a, b) => String(a) < String(b) ? a : b);
|
|
806
|
+
}
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
}, "computeField");
|
|
810
|
+
(aggs.count ?? []).forEach((f) => computeField("COUNT", String(f)));
|
|
811
|
+
(aggs.sum ?? []).forEach((f) => computeField("SUM", String(f)));
|
|
812
|
+
(aggs.avg ?? []).forEach((f) => computeField("AVG", String(f)));
|
|
813
|
+
(aggs.max ?? []).forEach((f) => computeField("MAX", String(f)));
|
|
814
|
+
(aggs.min ?? []).forEach((f) => computeField("MIN", String(f)));
|
|
815
|
+
records.push(out);
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
return records;
|
|
897
819
|
}
|
|
898
820
|
buildWhereCondition(entity, relationMeta) {
|
|
899
821
|
const em = this.repo.getEntityManager();
|
|
@@ -1011,6 +933,217 @@ var RelationQueryBuilder = class {
|
|
|
1011
933
|
});
|
|
1012
934
|
}
|
|
1013
935
|
};
|
|
936
|
+
var AGG_REGEXP = /^(AVG|SUM|COUNT|MAX|MIN|GROUP_BY|group_by|groupBy|avg|sum|count|max|min)_(.*)$/i;
|
|
937
|
+
var AggregateBuilder = class _AggregateBuilder {
|
|
938
|
+
static {
|
|
939
|
+
__name(this, "AggregateBuilder");
|
|
940
|
+
}
|
|
941
|
+
static buildSelectExpressions(aggregate, alias) {
|
|
942
|
+
const aggs = [
|
|
943
|
+
[
|
|
944
|
+
"COUNT",
|
|
945
|
+
aggregate.count
|
|
946
|
+
],
|
|
947
|
+
[
|
|
948
|
+
"SUM",
|
|
949
|
+
aggregate.sum
|
|
950
|
+
],
|
|
951
|
+
[
|
|
952
|
+
"AVG",
|
|
953
|
+
aggregate.avg
|
|
954
|
+
],
|
|
955
|
+
[
|
|
956
|
+
"MAX",
|
|
957
|
+
aggregate.max
|
|
958
|
+
],
|
|
959
|
+
[
|
|
960
|
+
"MIN",
|
|
961
|
+
aggregate.min
|
|
962
|
+
]
|
|
963
|
+
];
|
|
964
|
+
const groupBySelects = (aggregate.groupBy ?? []).map((f) => {
|
|
965
|
+
const col = alias ? `\`${alias}\`.\`${String(f)}\`` : `\`${String(f)}\``;
|
|
966
|
+
return [
|
|
967
|
+
col,
|
|
968
|
+
_AggregateBuilder.getGroupByAlias(f)
|
|
969
|
+
];
|
|
970
|
+
});
|
|
971
|
+
const funcSelects = [];
|
|
972
|
+
aggs.forEach(([func, fields]) => {
|
|
973
|
+
const aliases = (fields ?? []).map((f) => {
|
|
974
|
+
const col = alias ? `\`${alias}\`.\`${String(f)}\`` : `\`${String(f)}\``;
|
|
975
|
+
return [
|
|
976
|
+
`${func}(${col})`,
|
|
977
|
+
_AggregateBuilder.getAggregateAlias(func, f)
|
|
978
|
+
];
|
|
979
|
+
});
|
|
980
|
+
funcSelects.push(...aliases);
|
|
981
|
+
});
|
|
982
|
+
const selects = [
|
|
983
|
+
...groupBySelects,
|
|
984
|
+
...funcSelects
|
|
985
|
+
];
|
|
986
|
+
if (!selects.length) {
|
|
987
|
+
throw new common.BadRequestException("No aggregate fields found.");
|
|
988
|
+
}
|
|
989
|
+
return selects;
|
|
990
|
+
}
|
|
991
|
+
static async asyncConvertToAggregateResponse(responsePromise) {
|
|
992
|
+
const aggResponse = await responsePromise;
|
|
993
|
+
return this.convertToAggregateResponse(aggResponse);
|
|
994
|
+
}
|
|
995
|
+
static getAggregateSelects(query) {
|
|
996
|
+
return [
|
|
997
|
+
...this.getAggregateGroupBySelects(query),
|
|
998
|
+
...this.getAggregateFuncSelects(query)
|
|
999
|
+
];
|
|
1000
|
+
}
|
|
1001
|
+
static getAggregateGroupBySelects(query) {
|
|
1002
|
+
return (query.groupBy ?? []).map((f) => this.getGroupByAlias(f));
|
|
1003
|
+
}
|
|
1004
|
+
static getAggregateFuncSelects(query) {
|
|
1005
|
+
const aggs = [
|
|
1006
|
+
[
|
|
1007
|
+
"COUNT",
|
|
1008
|
+
query.count
|
|
1009
|
+
],
|
|
1010
|
+
[
|
|
1011
|
+
"SUM",
|
|
1012
|
+
query.sum
|
|
1013
|
+
],
|
|
1014
|
+
[
|
|
1015
|
+
"AVG",
|
|
1016
|
+
query.avg
|
|
1017
|
+
],
|
|
1018
|
+
[
|
|
1019
|
+
"MAX",
|
|
1020
|
+
query.max
|
|
1021
|
+
],
|
|
1022
|
+
[
|
|
1023
|
+
"MIN",
|
|
1024
|
+
query.min
|
|
1025
|
+
]
|
|
1026
|
+
];
|
|
1027
|
+
return aggs.reduce((cols, [func, fields]) => {
|
|
1028
|
+
const aliases = (fields ?? []).map((f) => this.getAggregateAlias(func, f));
|
|
1029
|
+
return [
|
|
1030
|
+
...cols,
|
|
1031
|
+
...aliases
|
|
1032
|
+
];
|
|
1033
|
+
}, []);
|
|
1034
|
+
}
|
|
1035
|
+
static getAggregateAlias(func, field) {
|
|
1036
|
+
return `${func}_${field}`;
|
|
1037
|
+
}
|
|
1038
|
+
static getGroupByAlias(field) {
|
|
1039
|
+
return `GROUP_BY_${field}`;
|
|
1040
|
+
}
|
|
1041
|
+
static convertToAggregateResponse(rawAggregates) {
|
|
1042
|
+
return rawAggregates.map((response) => {
|
|
1043
|
+
const agg = {};
|
|
1044
|
+
if (response._id && typeof response._id === "object") {
|
|
1045
|
+
const idObj = response._id;
|
|
1046
|
+
Object.keys(idObj).forEach((k) => {
|
|
1047
|
+
const m = /^(?:GROUP_BY|group_by|groupBy)_(.*)$/i.exec(k);
|
|
1048
|
+
if (m) {
|
|
1049
|
+
const field = m[1];
|
|
1050
|
+
agg.groupBy = {
|
|
1051
|
+
...agg.groupBy,
|
|
1052
|
+
[field]: idObj[k]
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
Object.keys(response).forEach((resultField) => {
|
|
1058
|
+
if (resultField === "_id") return;
|
|
1059
|
+
const matchResult = AGG_REGEXP.exec(resultField);
|
|
1060
|
+
if (!matchResult) {
|
|
1061
|
+
throw new Error("Unknown aggregate column encountered.");
|
|
1062
|
+
}
|
|
1063
|
+
const matchedFunc = matchResult[1];
|
|
1064
|
+
const matchedFieldName = matchResult[2];
|
|
1065
|
+
const funcKey = matchedFunc.toLowerCase();
|
|
1066
|
+
const aggFunc = funcKey === "group_by" || funcKey === "groupby" ? "groupBy" : funcKey;
|
|
1067
|
+
if (aggFunc === "groupBy") {
|
|
1068
|
+
agg.groupBy = {
|
|
1069
|
+
...agg.groupBy,
|
|
1070
|
+
[matchedFieldName]: response[resultField]
|
|
1071
|
+
};
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
const fieldName = matchedFieldName;
|
|
1075
|
+
agg[aggFunc] = {
|
|
1076
|
+
...agg[aggFunc],
|
|
1077
|
+
[fieldName]: response[resultField]
|
|
1078
|
+
};
|
|
1079
|
+
});
|
|
1080
|
+
return agg;
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
1083
|
+
/**
|
|
1084
|
+
* Gets the actual database column name for a property from entity metadata.
|
|
1085
|
+
* @param metadata - the entity metadata
|
|
1086
|
+
* @param propertyName - the property name
|
|
1087
|
+
* @returns the database column name
|
|
1088
|
+
*/
|
|
1089
|
+
getColumnName(metadata, propertyName) {
|
|
1090
|
+
const prop = metadata.properties[propertyName];
|
|
1091
|
+
if (prop && prop.fieldNames && prop.fieldNames.length > 0) {
|
|
1092
|
+
return prop.fieldNames[0];
|
|
1093
|
+
}
|
|
1094
|
+
return propertyName;
|
|
1095
|
+
}
|
|
1096
|
+
/**
|
|
1097
|
+
* Builds aggregate SELECT clause for MikroORM QueryBuilder.
|
|
1098
|
+
* @param qb - the MikroORM QueryBuilder
|
|
1099
|
+
* @param aggregate - the aggregates to select.
|
|
1100
|
+
* @param alias - optional alias to use to qualify an identifier
|
|
1101
|
+
*/
|
|
1102
|
+
build(qb, aggregate, alias) {
|
|
1103
|
+
const metadata = qb.mainAlias?.metadata;
|
|
1104
|
+
const selects = [
|
|
1105
|
+
...this.createGroupBySelect(aggregate.groupBy, alias, metadata),
|
|
1106
|
+
...this.createAggSelect("COUNT", aggregate.count, alias, metadata),
|
|
1107
|
+
...this.createAggSelect("SUM", aggregate.sum, alias, metadata),
|
|
1108
|
+
...this.createAggSelect("AVG", aggregate.avg, alias, metadata),
|
|
1109
|
+
...this.createAggSelect("MAX", aggregate.max, alias, metadata),
|
|
1110
|
+
...this.createAggSelect("MIN", aggregate.min, alias, metadata)
|
|
1111
|
+
];
|
|
1112
|
+
if (!selects.length) {
|
|
1113
|
+
throw new common.BadRequestException("No aggregate fields found.");
|
|
1114
|
+
}
|
|
1115
|
+
selects.forEach(([selectExpr, selectAlias]) => {
|
|
1116
|
+
qb.addSelect(core$1.raw(`${selectExpr} as "${selectAlias}"`));
|
|
1117
|
+
});
|
|
1118
|
+
return qb;
|
|
1119
|
+
}
|
|
1120
|
+
createAggSelect(func, fields, alias, metadata) {
|
|
1121
|
+
if (!fields) {
|
|
1122
|
+
return [];
|
|
1123
|
+
}
|
|
1124
|
+
return fields.map((field) => {
|
|
1125
|
+
const columnName = metadata ? this.getColumnName(metadata, field) : field;
|
|
1126
|
+
const col = alias ? `\`${alias}\`.\`${columnName}\`` : `\`${columnName}\``;
|
|
1127
|
+
return [
|
|
1128
|
+
`${func}(${col})`,
|
|
1129
|
+
_AggregateBuilder.getAggregateAlias(func, field)
|
|
1130
|
+
];
|
|
1131
|
+
});
|
|
1132
|
+
}
|
|
1133
|
+
createGroupBySelect(fields, alias, metadata) {
|
|
1134
|
+
if (!fields) {
|
|
1135
|
+
return [];
|
|
1136
|
+
}
|
|
1137
|
+
return fields.map((field) => {
|
|
1138
|
+
const columnName = metadata ? this.getColumnName(metadata, field) : field;
|
|
1139
|
+
const col = alias ? `\`${alias}\`.\`${columnName}\`` : `\`${columnName}\``;
|
|
1140
|
+
return [
|
|
1141
|
+
`${col}`,
|
|
1142
|
+
_AggregateBuilder.getGroupByAlias(field)
|
|
1143
|
+
];
|
|
1144
|
+
});
|
|
1145
|
+
}
|
|
1146
|
+
};
|
|
1014
1147
|
var RelationQueryService = class {
|
|
1015
1148
|
static {
|
|
1016
1149
|
__name(this, "RelationQueryService");
|
|
@@ -1019,7 +1152,7 @@ var RelationQueryService = class {
|
|
|
1019
1152
|
if (Array.isArray(dto)) {
|
|
1020
1153
|
return this.batchQueryRelations(RelationClass, relationName, dto, query);
|
|
1021
1154
|
}
|
|
1022
|
-
const assembler = core
|
|
1155
|
+
const assembler = core.AssemblerFactory.getAssembler(RelationClass, this.getRelationEntity(relationName));
|
|
1023
1156
|
const relationQueryBuilder = this.getRelationQueryBuilder(relationName);
|
|
1024
1157
|
return assembler.convertAsyncToDTOs(relationQueryBuilder.selectAndExecute(dto, assembler.convertQuery(query)));
|
|
1025
1158
|
}
|
|
@@ -1027,7 +1160,7 @@ var RelationQueryService = class {
|
|
|
1027
1160
|
if (Array.isArray(dto)) {
|
|
1028
1161
|
return this.batchAggregateRelations(RelationClass, relationName, dto, filter, aggregate);
|
|
1029
1162
|
}
|
|
1030
|
-
const assembler = core
|
|
1163
|
+
const assembler = core.AssemblerFactory.getAssembler(RelationClass, this.getRelationEntity(relationName));
|
|
1031
1164
|
const relationQueryBuilder = this.getRelationQueryBuilder(relationName);
|
|
1032
1165
|
const rawResults = await relationQueryBuilder.aggregate(dto, assembler.convertQuery({
|
|
1033
1166
|
filter
|
|
@@ -1042,7 +1175,7 @@ var RelationQueryService = class {
|
|
|
1042
1175
|
if (Array.isArray(dto)) {
|
|
1043
1176
|
return this.batchCountRelations(RelationClass, relationName, dto, filter);
|
|
1044
1177
|
}
|
|
1045
|
-
const assembler = core
|
|
1178
|
+
const assembler = core.AssemblerFactory.getAssembler(RelationClass, this.getRelationEntity(relationName));
|
|
1046
1179
|
const relationQueryBuilder = this.getRelationQueryBuilder(relationName);
|
|
1047
1180
|
return relationQueryBuilder.count(dto, assembler.convertQuery({
|
|
1048
1181
|
filter
|
|
@@ -1052,7 +1185,7 @@ var RelationQueryService = class {
|
|
|
1052
1185
|
if (Array.isArray(dto)) {
|
|
1053
1186
|
return this.batchFindRelations(RelationClass, relationName, dto, opts);
|
|
1054
1187
|
}
|
|
1055
|
-
const assembler = core
|
|
1188
|
+
const assembler = core.AssemblerFactory.getAssembler(RelationClass, this.getRelationEntity(relationName));
|
|
1056
1189
|
const relationQueryBuilder = this.getRelationQueryBuilder(relationName);
|
|
1057
1190
|
const relations = await relationQueryBuilder.selectAndExecute(dto, {
|
|
1058
1191
|
filter: opts?.filter,
|
|
@@ -1126,7 +1259,7 @@ var RelationQueryService = class {
|
|
|
1126
1259
|
if (!relation) {
|
|
1127
1260
|
throw new Error(`Unable to find ${relationName} to set on ${this.EntityClass.name}`);
|
|
1128
1261
|
}
|
|
1129
|
-
core.wrap(entity).assign({
|
|
1262
|
+
core$1.wrap(entity).assign({
|
|
1130
1263
|
[relationName]: relation
|
|
1131
1264
|
});
|
|
1132
1265
|
await this.repo.getEntityManager().flush();
|
|
@@ -1179,7 +1312,7 @@ var RelationQueryService = class {
|
|
|
1179
1312
|
if (fkFieldName in entity) {
|
|
1180
1313
|
assignData[fkFieldName] = null;
|
|
1181
1314
|
}
|
|
1182
|
-
core.wrap(entity).assign(assignData);
|
|
1315
|
+
core$1.wrap(entity).assign(assignData);
|
|
1183
1316
|
} else {
|
|
1184
1317
|
const collection = entity[relationName];
|
|
1185
1318
|
if (collection && typeof collection.remove === "function") {
|
|
@@ -1201,7 +1334,7 @@ var RelationQueryService = class {
|
|
|
1201
1334
|
* @param query - A query to filter, page or sort relations.
|
|
1202
1335
|
*/
|
|
1203
1336
|
async batchQueryRelations(RelationClass, relationName, entities, query) {
|
|
1204
|
-
const assembler = core
|
|
1337
|
+
const assembler = core.AssemblerFactory.getAssembler(RelationClass, this.getRelationEntity(relationName));
|
|
1205
1338
|
const relationQueryBuilder = this.getRelationQueryBuilder(relationName);
|
|
1206
1339
|
const convertedQuery = assembler.convertQuery(query);
|
|
1207
1340
|
const results = /* @__PURE__ */ new Map();
|
|
@@ -1222,7 +1355,7 @@ var RelationQueryService = class {
|
|
|
1222
1355
|
* @param query - A query to filter, page or sort relations.
|
|
1223
1356
|
*/
|
|
1224
1357
|
async batchAggregateRelations(RelationClass, relationName, entities, filter, aggregate) {
|
|
1225
|
-
const assembler = core
|
|
1358
|
+
const assembler = core.AssemblerFactory.getAssembler(RelationClass, this.getRelationEntity(relationName));
|
|
1226
1359
|
const relationQueryBuilder = this.getRelationQueryBuilder(relationName);
|
|
1227
1360
|
const convertedQuery = assembler.convertQuery({
|
|
1228
1361
|
filter
|
|
@@ -1243,7 +1376,7 @@ var RelationQueryService = class {
|
|
|
1243
1376
|
* @param filter - The filter to apply to the relation query.
|
|
1244
1377
|
*/
|
|
1245
1378
|
async batchCountRelations(RelationClass, relationName, entities, filter) {
|
|
1246
|
-
const assembler = core
|
|
1379
|
+
const assembler = core.AssemblerFactory.getAssembler(RelationClass, this.getRelationEntity(relationName));
|
|
1247
1380
|
const relationQueryBuilder = this.getRelationQueryBuilder(relationName);
|
|
1248
1381
|
const convertedQuery = assembler.convertQuery({
|
|
1249
1382
|
filter
|
|
@@ -1338,14 +1471,14 @@ var MikroOrmQueryService = class extends RelationQueryService {
|
|
|
1338
1471
|
this.useSoftDelete = opts?.useSoftDelete ?? false;
|
|
1339
1472
|
const serializer = assembler_serializer.getAssemblerSerializer(this.EntityClass);
|
|
1340
1473
|
if (!serializer) {
|
|
1341
|
-
core
|
|
1474
|
+
core.AssemblerSerializer((e) => {
|
|
1342
1475
|
const json = classTransformer.instanceToPlain(e, {
|
|
1343
1476
|
enableImplicitConversion: true,
|
|
1344
1477
|
excludeExtraneousValues: true,
|
|
1345
1478
|
exposeDefaultValues: true
|
|
1346
1479
|
});
|
|
1347
1480
|
const jsonWithRemovedEmptyObjects = Object.fromEntries(Object.entries(json).filter(([, value]) => !(value && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 0)));
|
|
1348
|
-
const wrapped = core.wrap(e, true);
|
|
1481
|
+
const wrapped = core$1.wrap(e, true);
|
|
1349
1482
|
const ormJson = "toObject" in wrapped ? wrapped.toObject() : {};
|
|
1350
1483
|
const data = {
|
|
1351
1484
|
...ormJson,
|
|
@@ -1353,7 +1486,7 @@ var MikroOrmQueryService = class extends RelationQueryService {
|
|
|
1353
1486
|
};
|
|
1354
1487
|
return data;
|
|
1355
1488
|
})(this.EntityClass);
|
|
1356
|
-
core
|
|
1489
|
+
core.AssemblerDeserializer((d) => {
|
|
1357
1490
|
const entity = this.repo.getEntityManager().create(this.EntityClass, classTransformer.instanceToPlain(d));
|
|
1358
1491
|
return entity;
|
|
1359
1492
|
})(this.EntityClass);
|
|
@@ -1378,24 +1511,169 @@ var MikroOrmQueryService = class extends RelationQueryService {
|
|
|
1378
1511
|
* @param query - The Query used to filter, page, and sort rows.
|
|
1379
1512
|
*/
|
|
1380
1513
|
async query(query) {
|
|
1381
|
-
const
|
|
1382
|
-
|
|
1383
|
-
|
|
1514
|
+
const { filterQuery, options } = this.filterQueryBuilder.buildFindOptions(query);
|
|
1515
|
+
const em = this.repo.getEntityManager();
|
|
1516
|
+
let where = filterQuery;
|
|
1517
|
+
if (this.useSoftDelete) {
|
|
1518
|
+
const deletedFilter = {
|
|
1519
|
+
deletedAt: null
|
|
1520
|
+
};
|
|
1521
|
+
where = where ? {
|
|
1522
|
+
$and: [
|
|
1523
|
+
where,
|
|
1524
|
+
deletedFilter
|
|
1525
|
+
]
|
|
1526
|
+
} : deletedFilter;
|
|
1527
|
+
}
|
|
1528
|
+
return em.find(this.EntityClass, where ?? {}, options);
|
|
1384
1529
|
}
|
|
1385
1530
|
async aggregate(filter, aggregate) {
|
|
1386
|
-
const
|
|
1531
|
+
const { filterQuery } = this.filterQueryBuilder.buildFindOptions({
|
|
1387
1532
|
filter
|
|
1388
|
-
}
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1533
|
+
});
|
|
1534
|
+
const em = this.repo.getEntityManager();
|
|
1535
|
+
let where = filterQuery;
|
|
1536
|
+
if (this.useSoftDelete) {
|
|
1537
|
+
const deletedFilter = {
|
|
1538
|
+
deletedAt: null
|
|
1539
|
+
};
|
|
1540
|
+
where = where ? {
|
|
1541
|
+
$and: [
|
|
1542
|
+
where,
|
|
1543
|
+
deletedFilter
|
|
1544
|
+
]
|
|
1545
|
+
} : deletedFilter;
|
|
1546
|
+
}
|
|
1547
|
+
const rows = await em.find(this.EntityClass, where ?? {});
|
|
1548
|
+
const aggs = aggregate;
|
|
1549
|
+
const groupBy = aggs.groupBy ?? [];
|
|
1550
|
+
const records = [];
|
|
1551
|
+
const makeAggKey = /* @__PURE__ */ __name((func, field) => `${func}_${field}`, "makeAggKey");
|
|
1552
|
+
const makeGroupKey = /* @__PURE__ */ __name((field) => `GROUP_BY_${field}`, "makeGroupKey");
|
|
1553
|
+
const isNumeric = /* @__PURE__ */ __name((v) => typeof v === "number" || v instanceof Date, "isNumeric");
|
|
1554
|
+
if (groupBy.length === 0) {
|
|
1555
|
+
const out = {};
|
|
1556
|
+
const computeField = /* @__PURE__ */ __name((fn, field) => {
|
|
1557
|
+
const values = rows.map((r) => r[field]).filter((v) => v !== void 0 && v !== null);
|
|
1558
|
+
if (fn === "COUNT") {
|
|
1559
|
+
out[makeAggKey("COUNT", field)] = values.length;
|
|
1560
|
+
return;
|
|
1561
|
+
}
|
|
1562
|
+
if (values.length === 0) {
|
|
1563
|
+
out[makeAggKey(fn, field)] = null;
|
|
1564
|
+
return;
|
|
1565
|
+
}
|
|
1566
|
+
if (fn === "SUM" || fn === "AVG") {
|
|
1567
|
+
const nums = values.map((v) => v instanceof Date ? v.getTime() : Number(v)).filter((n) => !Number.isNaN(n));
|
|
1568
|
+
const sum = nums.reduce((s, v) => s + v, 0);
|
|
1569
|
+
out[makeAggKey(fn, field)] = fn === "SUM" ? sum : nums.length ? sum / nums.length : null;
|
|
1570
|
+
return;
|
|
1571
|
+
}
|
|
1572
|
+
if (fn === "MAX") {
|
|
1573
|
+
if (values.every(isNumeric)) {
|
|
1574
|
+
const nums = values.map((v) => v instanceof Date ? v.getTime() : Number(v));
|
|
1575
|
+
out[makeAggKey("MAX", field)] = Math.max(...nums);
|
|
1576
|
+
} else {
|
|
1577
|
+
out[makeAggKey("MAX", field)] = values.reduce((a, b) => String(a) > String(b) ? a : b);
|
|
1578
|
+
}
|
|
1579
|
+
return;
|
|
1580
|
+
}
|
|
1581
|
+
if (fn === "MIN") {
|
|
1582
|
+
if (values.every(isNumeric)) {
|
|
1583
|
+
const nums = values.map((v) => v instanceof Date ? v.getTime() : Number(v));
|
|
1584
|
+
out[makeAggKey("MIN", field)] = Math.min(...nums);
|
|
1585
|
+
} else {
|
|
1586
|
+
out[makeAggKey("MIN", field)] = values.reduce((a, b) => String(a) < String(b) ? a : b);
|
|
1587
|
+
}
|
|
1588
|
+
return;
|
|
1589
|
+
}
|
|
1590
|
+
}, "computeField");
|
|
1591
|
+
(aggs.count ?? []).forEach((f) => computeField("COUNT", String(f)));
|
|
1592
|
+
(aggs.sum ?? []).forEach((f) => computeField("SUM", String(f)));
|
|
1593
|
+
(aggs.avg ?? []).forEach((f) => computeField("AVG", String(f)));
|
|
1594
|
+
(aggs.max ?? []).forEach((f) => computeField("MAX", String(f)));
|
|
1595
|
+
(aggs.min ?? []).forEach((f) => computeField("MIN", String(f)));
|
|
1596
|
+
records.push(out);
|
|
1597
|
+
} else {
|
|
1598
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1599
|
+
rows.forEach((r) => {
|
|
1600
|
+
const key = groupBy.map((g) => JSON.stringify(r[String(g)])).join("|");
|
|
1601
|
+
const arr = groups.get(key) ?? [];
|
|
1602
|
+
arr.push(r);
|
|
1603
|
+
groups.set(key, arr);
|
|
1604
|
+
});
|
|
1605
|
+
groups.forEach((groupRows, key) => {
|
|
1606
|
+
const parts = key.split("|").map((p) => JSON.parse(p));
|
|
1607
|
+
const out = {};
|
|
1608
|
+
groupBy.forEach((g, i) => {
|
|
1609
|
+
const val = parts[i];
|
|
1610
|
+
out[makeGroupKey(String(g))] = typeof val === "boolean" ? val ? 1 : 0 : val;
|
|
1611
|
+
});
|
|
1612
|
+
const computeField = /* @__PURE__ */ __name((fn, field) => {
|
|
1613
|
+
const values = groupRows.map((r) => r[field]).filter((v) => v !== void 0 && v !== null);
|
|
1614
|
+
if (fn === "COUNT") {
|
|
1615
|
+
out[makeAggKey("COUNT", field)] = values.length;
|
|
1616
|
+
return;
|
|
1617
|
+
}
|
|
1618
|
+
if (values.length === 0) {
|
|
1619
|
+
out[makeAggKey(fn, field)] = null;
|
|
1620
|
+
return;
|
|
1621
|
+
}
|
|
1622
|
+
if (fn === "SUM" || fn === "AVG") {
|
|
1623
|
+
const nums = values.map((v) => v instanceof Date ? v.getTime() : Number(v)).filter((n) => !Number.isNaN(n));
|
|
1624
|
+
const sum = nums.reduce((s, v) => s + v, 0);
|
|
1625
|
+
out[makeAggKey(fn, field)] = fn === "SUM" ? sum : nums.length ? sum / nums.length : null;
|
|
1626
|
+
return;
|
|
1627
|
+
}
|
|
1628
|
+
if (fn === "MAX") {
|
|
1629
|
+
if (values.every(isNumeric)) {
|
|
1630
|
+
const nums = values.map((v) => v instanceof Date ? v.getTime() : Number(v));
|
|
1631
|
+
out[makeAggKey("MAX", field)] = Math.max(...nums);
|
|
1632
|
+
} else {
|
|
1633
|
+
out[makeAggKey("MAX", field)] = values.reduce((a, b) => String(a) > String(b) ? a : b);
|
|
1634
|
+
}
|
|
1635
|
+
return;
|
|
1636
|
+
}
|
|
1637
|
+
if (fn === "MIN") {
|
|
1638
|
+
if (values.every(isNumeric)) {
|
|
1639
|
+
const nums = values.map((v) => v instanceof Date ? v.getTime() : Number(v));
|
|
1640
|
+
out[makeAggKey("MIN", field)] = Math.min(...nums);
|
|
1641
|
+
} else {
|
|
1642
|
+
out[makeAggKey("MIN", field)] = values.reduce((a, b) => String(a) < String(b) ? a : b);
|
|
1643
|
+
}
|
|
1644
|
+
return;
|
|
1645
|
+
}
|
|
1646
|
+
}, "computeField");
|
|
1647
|
+
(aggs.count ?? []).forEach((f) => computeField("COUNT", String(f)));
|
|
1648
|
+
(aggs.sum ?? []).forEach((f) => computeField("SUM", String(f)));
|
|
1649
|
+
(aggs.avg ?? []).forEach((f) => computeField("AVG", String(f)));
|
|
1650
|
+
(aggs.max ?? []).forEach((f) => computeField("MAX", String(f)));
|
|
1651
|
+
(aggs.min ?? []).forEach((f) => computeField("MIN", String(f)));
|
|
1652
|
+
records.push(out);
|
|
1653
|
+
});
|
|
1654
|
+
}
|
|
1655
|
+
return records.map((r) => AggregateBuilder.convertToAggregateResponse([
|
|
1656
|
+
r
|
|
1657
|
+
])[0]);
|
|
1392
1658
|
}
|
|
1393
1659
|
async count(filter) {
|
|
1394
|
-
const
|
|
1660
|
+
const { filterQuery } = this.filterQueryBuilder.buildFindOptions({
|
|
1395
1661
|
filter
|
|
1396
1662
|
});
|
|
1397
|
-
|
|
1398
|
-
|
|
1663
|
+
const em = this.repo.getEntityManager();
|
|
1664
|
+
let where = filterQuery;
|
|
1665
|
+
if (this.useSoftDelete) {
|
|
1666
|
+
const deletedFilter = {
|
|
1667
|
+
deletedAt: null
|
|
1668
|
+
};
|
|
1669
|
+
where = where ? {
|
|
1670
|
+
$and: [
|
|
1671
|
+
where,
|
|
1672
|
+
deletedFilter
|
|
1673
|
+
]
|
|
1674
|
+
} : deletedFilter;
|
|
1675
|
+
}
|
|
1676
|
+
return em.count(this.EntityClass, where ?? {});
|
|
1399
1677
|
}
|
|
1400
1678
|
/**
|
|
1401
1679
|
* Find an entity by it's `id`.
|
|
@@ -1407,10 +1685,33 @@ var MikroOrmQueryService = class extends RelationQueryService {
|
|
|
1407
1685
|
* @param id - The id of the record to find.
|
|
1408
1686
|
*/
|
|
1409
1687
|
async findById(id, opts) {
|
|
1410
|
-
const
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1688
|
+
const metadata = this.em.getMetadata().get(this.repo.getEntityName());
|
|
1689
|
+
const primaryKey = metadata.primaryKeys[0];
|
|
1690
|
+
let where = {
|
|
1691
|
+
[primaryKey]: id
|
|
1692
|
+
};
|
|
1693
|
+
if (opts?.filter) {
|
|
1694
|
+
const whereBuilder = new WhereBuilder();
|
|
1695
|
+
const additional = whereBuilder.build(opts.filter);
|
|
1696
|
+
where = {
|
|
1697
|
+
$and: [
|
|
1698
|
+
where,
|
|
1699
|
+
additional
|
|
1700
|
+
]
|
|
1701
|
+
};
|
|
1702
|
+
}
|
|
1703
|
+
if (this.useSoftDelete) {
|
|
1704
|
+
where = {
|
|
1705
|
+
$and: [
|
|
1706
|
+
where,
|
|
1707
|
+
{
|
|
1708
|
+
deletedAt: null
|
|
1709
|
+
}
|
|
1710
|
+
]
|
|
1711
|
+
};
|
|
1712
|
+
}
|
|
1713
|
+
const entity = await this.em.findOne(this.EntityClass, where);
|
|
1714
|
+
return entity ?? void 0;
|
|
1414
1715
|
}
|
|
1415
1716
|
/**
|
|
1416
1717
|
* Gets an entity by it's `id`. If the entity is not found a rejected promise is returned.
|
|
@@ -1478,7 +1779,7 @@ var MikroOrmQueryService = class extends RelationQueryService {
|
|
|
1478
1779
|
const dateWithClearUndefined = Object.fromEntries(Object.entries(update).filter(([, value]) => value !== void 0));
|
|
1479
1780
|
this.ensureIdIsNotPresent(dateWithClearUndefined);
|
|
1480
1781
|
const entity = await this.getById(id, opts);
|
|
1481
|
-
core.wrap(entity).assign(dateWithClearUndefined);
|
|
1782
|
+
core$1.wrap(entity).assign(dateWithClearUndefined);
|
|
1482
1783
|
await this.repo.getEntityManager().flush();
|
|
1483
1784
|
return entity;
|
|
1484
1785
|
}
|
|
@@ -1501,7 +1802,7 @@ var MikroOrmQueryService = class extends RelationQueryService {
|
|
|
1501
1802
|
filter
|
|
1502
1803
|
});
|
|
1503
1804
|
for (const entity of entities) {
|
|
1504
|
-
core.wrap(entity).assign(update);
|
|
1805
|
+
core$1.wrap(entity).assign(update);
|
|
1505
1806
|
}
|
|
1506
1807
|
await this.repo.getEntityManager().flush();
|
|
1507
1808
|
return {
|
|
@@ -1524,7 +1825,7 @@ var MikroOrmQueryService = class extends RelationQueryService {
|
|
|
1524
1825
|
const entity = await this.getById(id, opts);
|
|
1525
1826
|
const em = this.repo.getEntityManager();
|
|
1526
1827
|
if (this.useSoftDelete) {
|
|
1527
|
-
core.wrap(entity).assign({
|
|
1828
|
+
core$1.wrap(entity).assign({
|
|
1528
1829
|
deletedAt: /* @__PURE__ */ new Date()
|
|
1529
1830
|
});
|
|
1530
1831
|
await em.flush();
|
|
@@ -1553,7 +1854,7 @@ var MikroOrmQueryService = class extends RelationQueryService {
|
|
|
1553
1854
|
const em = this.repo.getEntityManager();
|
|
1554
1855
|
if (this.useSoftDelete) {
|
|
1555
1856
|
for (const entity of entities) {
|
|
1556
|
-
core.wrap(entity).assign({
|
|
1857
|
+
core$1.wrap(entity).assign({
|
|
1557
1858
|
deletedAt: /* @__PURE__ */ new Date()
|
|
1558
1859
|
});
|
|
1559
1860
|
}
|
|
@@ -1603,7 +1904,7 @@ var MikroOrmQueryService = class extends RelationQueryService {
|
|
|
1603
1904
|
if (!entity) {
|
|
1604
1905
|
throw new common.NotFoundException(`Unable to find ${this.EntityClass.name} with id: ${id}`);
|
|
1605
1906
|
}
|
|
1606
|
-
core.wrap(entity).assign({
|
|
1907
|
+
core$1.wrap(entity).assign({
|
|
1607
1908
|
deletedAt: null
|
|
1608
1909
|
});
|
|
1609
1910
|
await em.flush();
|
|
@@ -1631,7 +1932,7 @@ var MikroOrmQueryService = class extends RelationQueryService {
|
|
|
1631
1932
|
filters: false
|
|
1632
1933
|
});
|
|
1633
1934
|
for (const entity of entities) {
|
|
1634
|
-
core.wrap(entity).assign({
|
|
1935
|
+
core$1.wrap(entity).assign({
|
|
1635
1936
|
deletedAt: null
|
|
1636
1937
|
});
|
|
1637
1938
|
}
|
|
@@ -1683,7 +1984,7 @@ var MikroOrmQueryService = class extends RelationQueryService {
|
|
|
1683
1984
|
function createMikroOrmQueryServiceProvider(EntityClass, contextName) {
|
|
1684
1985
|
return {
|
|
1685
1986
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1686
|
-
provide: core
|
|
1987
|
+
provide: core.getQueryServiceToken(EntityClass),
|
|
1687
1988
|
useFactory(repo) {
|
|
1688
1989
|
return new MikroOrmQueryService(repo);
|
|
1689
1990
|
},
|
|
@@ -1720,12 +2021,12 @@ var NestjsQueryMikroOrmModule = class _NestjsQueryMikroOrmModule {
|
|
|
1720
2021
|
};
|
|
1721
2022
|
|
|
1722
2023
|
exports.AggregateBuilder = AggregateBuilder;
|
|
2024
|
+
exports.ComparisonBuilder = ComparisonBuilder;
|
|
1723
2025
|
exports.FilterQueryBuilder = FilterQueryBuilder;
|
|
1724
2026
|
exports.MikroOrmQueryService = MikroOrmQueryService;
|
|
1725
2027
|
exports.NestjsQueryMikroOrmModule = NestjsQueryMikroOrmModule;
|
|
1726
2028
|
exports.RelationQueryBuilder = RelationQueryBuilder;
|
|
1727
2029
|
exports.RelationQueryService = RelationQueryService;
|
|
1728
|
-
exports.SQLComparisonBuilder = SQLComparisonBuilder;
|
|
1729
2030
|
exports.WhereBuilder = WhereBuilder;
|
|
1730
2031
|
exports.createMikroOrmQueryServiceProviders = createMikroOrmQueryServiceProviders;
|
|
1731
2032
|
//# sourceMappingURL=index.cjs.map
|