nestjs-query-mikro-orm 0.0.8 → 0.1.0
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/README.md +1 -1
- package/dist/index.cjs +621 -298
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +71 -72
- package/dist/index.d.ts +71 -72
- package/dist/index.mjs +610 -287
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -9
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
|
|
4
|
+
var nestjsQueryCore = require('@ptc-org/nestjs-query-core');
|
|
5
5
|
var core = require('@mikro-orm/core');
|
|
6
|
-
var assembler_serializer = require('@nestjs-query
|
|
6
|
+
var assembler_serializer = require('@ptc-org/nestjs-query-core/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 =
|
|
386
|
+
const referencedFields = nestjsQueryCore.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,239 @@ 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
|
+
if (!fields || fields.length === 0) return;
|
|
974
|
+
const aliases = fields.map((f) => {
|
|
975
|
+
const col = alias ? `\`${alias}\`.\`${String(f)}\`` : `\`${String(f)}\``;
|
|
976
|
+
return [
|
|
977
|
+
`${func}(${col})`,
|
|
978
|
+
_AggregateBuilder.getAggregateAlias(func, f)
|
|
979
|
+
];
|
|
980
|
+
});
|
|
981
|
+
funcSelects.push(...aliases);
|
|
982
|
+
});
|
|
983
|
+
const selects = [
|
|
984
|
+
...groupBySelects,
|
|
985
|
+
...funcSelects
|
|
986
|
+
];
|
|
987
|
+
if (!selects.length) {
|
|
988
|
+
throw new common.BadRequestException("No aggregate fields found.");
|
|
989
|
+
}
|
|
990
|
+
return selects;
|
|
991
|
+
}
|
|
992
|
+
static async asyncConvertToAggregateResponse(responsePromise) {
|
|
993
|
+
const aggResponse = await responsePromise;
|
|
994
|
+
return this.convertToAggregateResponse(aggResponse);
|
|
995
|
+
}
|
|
996
|
+
static getAggregateSelects(query) {
|
|
997
|
+
return [
|
|
998
|
+
...this.getAggregateGroupBySelects(query),
|
|
999
|
+
...this.getAggregateFuncSelects(query)
|
|
1000
|
+
];
|
|
1001
|
+
}
|
|
1002
|
+
static getAggregateGroupBySelects(query) {
|
|
1003
|
+
return (query.groupBy ?? []).map((f) => this.getGroupByAlias(f));
|
|
1004
|
+
}
|
|
1005
|
+
static getAggregateFuncSelects(query) {
|
|
1006
|
+
const aggs = [
|
|
1007
|
+
[
|
|
1008
|
+
"COUNT",
|
|
1009
|
+
query.count
|
|
1010
|
+
],
|
|
1011
|
+
[
|
|
1012
|
+
"SUM",
|
|
1013
|
+
query.sum
|
|
1014
|
+
],
|
|
1015
|
+
[
|
|
1016
|
+
"AVG",
|
|
1017
|
+
query.avg
|
|
1018
|
+
],
|
|
1019
|
+
[
|
|
1020
|
+
"MAX",
|
|
1021
|
+
query.max
|
|
1022
|
+
],
|
|
1023
|
+
[
|
|
1024
|
+
"MIN",
|
|
1025
|
+
query.min
|
|
1026
|
+
]
|
|
1027
|
+
];
|
|
1028
|
+
return aggs.reduce((cols, [func, fields]) => {
|
|
1029
|
+
if (!fields || fields.length === 0) return cols;
|
|
1030
|
+
const aliases = fields.map((f) => this.getAggregateAlias(func, f));
|
|
1031
|
+
return [
|
|
1032
|
+
...cols,
|
|
1033
|
+
...aliases
|
|
1034
|
+
];
|
|
1035
|
+
}, []);
|
|
1036
|
+
}
|
|
1037
|
+
static getAggregateAlias(func, field) {
|
|
1038
|
+
return `${func}_${field}`;
|
|
1039
|
+
}
|
|
1040
|
+
static getGroupByAlias(field) {
|
|
1041
|
+
return `GROUP_BY_${field}`;
|
|
1042
|
+
}
|
|
1043
|
+
static convertToAggregateResponse(rawAggregates) {
|
|
1044
|
+
return rawAggregates.map((response) => {
|
|
1045
|
+
const agg = {};
|
|
1046
|
+
if (response._id && typeof response._id === "object") {
|
|
1047
|
+
const idObj = response._id;
|
|
1048
|
+
Object.keys(idObj).forEach((k) => {
|
|
1049
|
+
const m = /^(?:GROUP_BY|group_by|groupBy)_(.*)$/i.exec(k);
|
|
1050
|
+
if (m) {
|
|
1051
|
+
const field = m[1];
|
|
1052
|
+
agg.groupBy = {
|
|
1053
|
+
...agg.groupBy,
|
|
1054
|
+
[field]: idObj[k]
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
1057
|
+
});
|
|
1058
|
+
}
|
|
1059
|
+
Object.keys(response).forEach((resultField) => {
|
|
1060
|
+
if (resultField === "_id") return;
|
|
1061
|
+
const matchResult = AGG_REGEXP.exec(resultField);
|
|
1062
|
+
if (!matchResult) {
|
|
1063
|
+
throw new Error("Unknown aggregate column encountered.");
|
|
1064
|
+
}
|
|
1065
|
+
const matchedFunc = matchResult[1];
|
|
1066
|
+
const matchedFieldName = matchResult[2];
|
|
1067
|
+
const funcKey = matchedFunc.toLowerCase();
|
|
1068
|
+
const aggFunc = funcKey === "group_by" || funcKey === "groupby" ? "groupBy" : funcKey;
|
|
1069
|
+
if (aggFunc === "groupBy") {
|
|
1070
|
+
agg.groupBy = {
|
|
1071
|
+
...agg.groupBy,
|
|
1072
|
+
[matchedFieldName]: response[resultField]
|
|
1073
|
+
};
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
1076
|
+
const fieldName = matchedFieldName;
|
|
1077
|
+
agg[aggFunc] = {
|
|
1078
|
+
...agg[aggFunc],
|
|
1079
|
+
[fieldName]: response[resultField]
|
|
1080
|
+
};
|
|
1081
|
+
});
|
|
1082
|
+
return agg;
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
/**
|
|
1086
|
+
* Gets the actual database column name for a property from entity metadata.
|
|
1087
|
+
* @param metadata - the entity metadata
|
|
1088
|
+
* @param propertyName - the property name
|
|
1089
|
+
* @returns the database column name
|
|
1090
|
+
*/
|
|
1091
|
+
getColumnName(metadata, propertyName) {
|
|
1092
|
+
const prop = metadata.properties[propertyName];
|
|
1093
|
+
if (prop && prop.fieldNames && prop.fieldNames.length > 0) {
|
|
1094
|
+
return prop.fieldNames[0];
|
|
1095
|
+
}
|
|
1096
|
+
return propertyName;
|
|
1097
|
+
}
|
|
1098
|
+
/**
|
|
1099
|
+
* Builds aggregate SELECT clause for MikroORM QueryBuilder.
|
|
1100
|
+
* @param qb - the MikroORM QueryBuilder
|
|
1101
|
+
* @param aggregate - the aggregates to select.
|
|
1102
|
+
* @param alias - optional alias to use to qualify an identifier
|
|
1103
|
+
*/
|
|
1104
|
+
build(qb, aggregate, alias) {
|
|
1105
|
+
const metadata = qb.mainAlias?.metadata;
|
|
1106
|
+
const selects = [];
|
|
1107
|
+
selects.push(...this.createGroupBySelect(aggregate.groupBy, alias, metadata));
|
|
1108
|
+
const aggs = [
|
|
1109
|
+
[
|
|
1110
|
+
"COUNT",
|
|
1111
|
+
aggregate.count
|
|
1112
|
+
],
|
|
1113
|
+
[
|
|
1114
|
+
"SUM",
|
|
1115
|
+
aggregate.sum
|
|
1116
|
+
],
|
|
1117
|
+
[
|
|
1118
|
+
"AVG",
|
|
1119
|
+
aggregate.avg
|
|
1120
|
+
],
|
|
1121
|
+
[
|
|
1122
|
+
"MAX",
|
|
1123
|
+
aggregate.max
|
|
1124
|
+
],
|
|
1125
|
+
[
|
|
1126
|
+
"MIN",
|
|
1127
|
+
aggregate.min
|
|
1128
|
+
]
|
|
1129
|
+
];
|
|
1130
|
+
aggs.forEach(([func, fields]) => {
|
|
1131
|
+
if (!fields || fields.length === 0) return;
|
|
1132
|
+
selects.push(...this.createAggSelect(func, fields, alias, metadata));
|
|
1133
|
+
});
|
|
1134
|
+
if (!selects.length) {
|
|
1135
|
+
throw new common.BadRequestException("No aggregate fields found.");
|
|
1136
|
+
}
|
|
1137
|
+
selects.forEach(([selectExpr, selectAlias]) => {
|
|
1138
|
+
qb.addSelect(core.raw(`${selectExpr} as "${selectAlias}"`));
|
|
1139
|
+
});
|
|
1140
|
+
return qb;
|
|
1141
|
+
}
|
|
1142
|
+
createAggSelect(func, fields, alias, metadata) {
|
|
1143
|
+
if (!fields) {
|
|
1144
|
+
return [];
|
|
1145
|
+
}
|
|
1146
|
+
return fields.map((field) => {
|
|
1147
|
+
const columnName = metadata ? this.getColumnName(metadata, field) : field;
|
|
1148
|
+
const col = alias ? `\`${alias}\`.\`${columnName}\`` : `\`${columnName}\``;
|
|
1149
|
+
return [
|
|
1150
|
+
`${func}(${col})`,
|
|
1151
|
+
_AggregateBuilder.getAggregateAlias(func, field)
|
|
1152
|
+
];
|
|
1153
|
+
});
|
|
1154
|
+
}
|
|
1155
|
+
createGroupBySelect(fields, alias, metadata) {
|
|
1156
|
+
if (!fields) {
|
|
1157
|
+
return [];
|
|
1158
|
+
}
|
|
1159
|
+
return fields.map((field) => {
|
|
1160
|
+
const columnName = metadata ? this.getColumnName(metadata, field) : field;
|
|
1161
|
+
const col = alias ? `\`${alias}\`.\`${columnName}\`` : `\`${columnName}\``;
|
|
1162
|
+
return [
|
|
1163
|
+
`${col}`,
|
|
1164
|
+
_AggregateBuilder.getGroupByAlias(field)
|
|
1165
|
+
];
|
|
1166
|
+
});
|
|
1167
|
+
}
|
|
1168
|
+
};
|
|
1014
1169
|
var RelationQueryService = class {
|
|
1015
1170
|
static {
|
|
1016
1171
|
__name(this, "RelationQueryService");
|
|
@@ -1019,15 +1174,15 @@ var RelationQueryService = class {
|
|
|
1019
1174
|
if (Array.isArray(dto)) {
|
|
1020
1175
|
return this.batchQueryRelations(RelationClass, relationName, dto, query);
|
|
1021
1176
|
}
|
|
1022
|
-
const assembler =
|
|
1177
|
+
const assembler = nestjsQueryCore.AssemblerFactory.getAssembler(RelationClass, this.getRelationEntity(relationName));
|
|
1023
1178
|
const relationQueryBuilder = this.getRelationQueryBuilder(relationName);
|
|
1024
|
-
return assembler.
|
|
1179
|
+
return assembler.convertToDTOs(await relationQueryBuilder.selectAndExecute(dto, assembler.convertQuery(query)));
|
|
1025
1180
|
}
|
|
1026
1181
|
async aggregateRelations(RelationClass, relationName, dto, filter, aggregate) {
|
|
1027
1182
|
if (Array.isArray(dto)) {
|
|
1028
1183
|
return this.batchAggregateRelations(RelationClass, relationName, dto, filter, aggregate);
|
|
1029
1184
|
}
|
|
1030
|
-
const assembler =
|
|
1185
|
+
const assembler = nestjsQueryCore.AssemblerFactory.getAssembler(RelationClass, this.getRelationEntity(relationName));
|
|
1031
1186
|
const relationQueryBuilder = this.getRelationQueryBuilder(relationName);
|
|
1032
1187
|
const rawResults = await relationQueryBuilder.aggregate(dto, assembler.convertQuery({
|
|
1033
1188
|
filter
|
|
@@ -1042,7 +1197,7 @@ var RelationQueryService = class {
|
|
|
1042
1197
|
if (Array.isArray(dto)) {
|
|
1043
1198
|
return this.batchCountRelations(RelationClass, relationName, dto, filter);
|
|
1044
1199
|
}
|
|
1045
|
-
const assembler =
|
|
1200
|
+
const assembler = nestjsQueryCore.AssemblerFactory.getAssembler(RelationClass, this.getRelationEntity(relationName));
|
|
1046
1201
|
const relationQueryBuilder = this.getRelationQueryBuilder(relationName);
|
|
1047
1202
|
return relationQueryBuilder.count(dto, assembler.convertQuery({
|
|
1048
1203
|
filter
|
|
@@ -1052,7 +1207,7 @@ var RelationQueryService = class {
|
|
|
1052
1207
|
if (Array.isArray(dto)) {
|
|
1053
1208
|
return this.batchFindRelations(RelationClass, relationName, dto, opts);
|
|
1054
1209
|
}
|
|
1055
|
-
const assembler =
|
|
1210
|
+
const assembler = nestjsQueryCore.AssemblerFactory.getAssembler(RelationClass, this.getRelationEntity(relationName));
|
|
1056
1211
|
const relationQueryBuilder = this.getRelationQueryBuilder(relationName);
|
|
1057
1212
|
const relations = await relationQueryBuilder.selectAndExecute(dto, {
|
|
1058
1213
|
filter: opts?.filter,
|
|
@@ -1201,13 +1356,13 @@ var RelationQueryService = class {
|
|
|
1201
1356
|
* @param query - A query to filter, page or sort relations.
|
|
1202
1357
|
*/
|
|
1203
1358
|
async batchQueryRelations(RelationClass, relationName, entities, query) {
|
|
1204
|
-
const assembler =
|
|
1359
|
+
const assembler = nestjsQueryCore.AssemblerFactory.getAssembler(RelationClass, this.getRelationEntity(relationName));
|
|
1205
1360
|
const relationQueryBuilder = this.getRelationQueryBuilder(relationName);
|
|
1206
1361
|
const convertedQuery = assembler.convertQuery(query);
|
|
1207
1362
|
const results = /* @__PURE__ */ new Map();
|
|
1208
1363
|
await Promise.all(entities.map(async (entity) => {
|
|
1209
1364
|
const relations = await relationQueryBuilder.selectAndExecute(entity, convertedQuery);
|
|
1210
|
-
const relationDtos = assembler.convertToDTOs(relations);
|
|
1365
|
+
const relationDtos = await assembler.convertToDTOs(relations);
|
|
1211
1366
|
if (relationDtos.length > 0) {
|
|
1212
1367
|
results.set(entity, relationDtos);
|
|
1213
1368
|
}
|
|
@@ -1222,7 +1377,7 @@ var RelationQueryService = class {
|
|
|
1222
1377
|
* @param query - A query to filter, page or sort relations.
|
|
1223
1378
|
*/
|
|
1224
1379
|
async batchAggregateRelations(RelationClass, relationName, entities, filter, aggregate) {
|
|
1225
|
-
const assembler =
|
|
1380
|
+
const assembler = nestjsQueryCore.AssemblerFactory.getAssembler(RelationClass, this.getRelationEntity(relationName));
|
|
1226
1381
|
const relationQueryBuilder = this.getRelationQueryBuilder(relationName);
|
|
1227
1382
|
const convertedQuery = assembler.convertQuery({
|
|
1228
1383
|
filter
|
|
@@ -1243,7 +1398,7 @@ var RelationQueryService = class {
|
|
|
1243
1398
|
* @param filter - The filter to apply to the relation query.
|
|
1244
1399
|
*/
|
|
1245
1400
|
async batchCountRelations(RelationClass, relationName, entities, filter) {
|
|
1246
|
-
const assembler =
|
|
1401
|
+
const assembler = nestjsQueryCore.AssemblerFactory.getAssembler(RelationClass, this.getRelationEntity(relationName));
|
|
1247
1402
|
const relationQueryBuilder = this.getRelationQueryBuilder(relationName);
|
|
1248
1403
|
const convertedQuery = assembler.convertQuery({
|
|
1249
1404
|
filter
|
|
@@ -1338,7 +1493,7 @@ var MikroOrmQueryService = class extends RelationQueryService {
|
|
|
1338
1493
|
this.useSoftDelete = opts?.useSoftDelete ?? false;
|
|
1339
1494
|
const serializer = assembler_serializer.getAssemblerSerializer(this.EntityClass);
|
|
1340
1495
|
if (!serializer) {
|
|
1341
|
-
|
|
1496
|
+
nestjsQueryCore.AssemblerSerializer((e) => {
|
|
1342
1497
|
const json = classTransformer.instanceToPlain(e, {
|
|
1343
1498
|
enableImplicitConversion: true,
|
|
1344
1499
|
excludeExtraneousValues: true,
|
|
@@ -1353,7 +1508,7 @@ var MikroOrmQueryService = class extends RelationQueryService {
|
|
|
1353
1508
|
};
|
|
1354
1509
|
return data;
|
|
1355
1510
|
})(this.EntityClass);
|
|
1356
|
-
|
|
1511
|
+
nestjsQueryCore.AssemblerDeserializer((d) => {
|
|
1357
1512
|
const entity = this.repo.getEntityManager().create(this.EntityClass, classTransformer.instanceToPlain(d));
|
|
1358
1513
|
return entity;
|
|
1359
1514
|
})(this.EntityClass);
|
|
@@ -1365,7 +1520,7 @@ var MikroOrmQueryService = class extends RelationQueryService {
|
|
|
1365
1520
|
return metadata.class;
|
|
1366
1521
|
}
|
|
1367
1522
|
/**
|
|
1368
|
-
* Query for multiple entities, using a Query from `@nestjs-query
|
|
1523
|
+
* Query for multiple entities, using a Query from `@ptc-org/nestjs-query-core`.
|
|
1369
1524
|
*
|
|
1370
1525
|
* @example
|
|
1371
1526
|
* ```ts
|
|
@@ -1378,24 +1533,169 @@ var MikroOrmQueryService = class extends RelationQueryService {
|
|
|
1378
1533
|
* @param query - The Query used to filter, page, and sort rows.
|
|
1379
1534
|
*/
|
|
1380
1535
|
async query(query) {
|
|
1381
|
-
const
|
|
1382
|
-
|
|
1383
|
-
|
|
1536
|
+
const { filterQuery, options } = this.filterQueryBuilder.buildFindOptions(query);
|
|
1537
|
+
const em = this.repo.getEntityManager();
|
|
1538
|
+
let where = filterQuery;
|
|
1539
|
+
if (this.useSoftDelete) {
|
|
1540
|
+
const deletedFilter = {
|
|
1541
|
+
deletedAt: null
|
|
1542
|
+
};
|
|
1543
|
+
where = where ? {
|
|
1544
|
+
$and: [
|
|
1545
|
+
where,
|
|
1546
|
+
deletedFilter
|
|
1547
|
+
]
|
|
1548
|
+
} : deletedFilter;
|
|
1549
|
+
}
|
|
1550
|
+
return em.find(this.EntityClass, where ?? {}, options);
|
|
1384
1551
|
}
|
|
1385
1552
|
async aggregate(filter, aggregate) {
|
|
1386
|
-
const
|
|
1553
|
+
const { filterQuery } = this.filterQueryBuilder.buildFindOptions({
|
|
1387
1554
|
filter
|
|
1388
|
-
}
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1555
|
+
});
|
|
1556
|
+
const em = this.repo.getEntityManager();
|
|
1557
|
+
let where = filterQuery;
|
|
1558
|
+
if (this.useSoftDelete) {
|
|
1559
|
+
const deletedFilter = {
|
|
1560
|
+
deletedAt: null
|
|
1561
|
+
};
|
|
1562
|
+
where = where ? {
|
|
1563
|
+
$and: [
|
|
1564
|
+
where,
|
|
1565
|
+
deletedFilter
|
|
1566
|
+
]
|
|
1567
|
+
} : deletedFilter;
|
|
1568
|
+
}
|
|
1569
|
+
const rows = await em.find(this.EntityClass, where ?? {});
|
|
1570
|
+
const aggs = aggregate;
|
|
1571
|
+
const groupBy = aggs.groupBy ?? [];
|
|
1572
|
+
const records = [];
|
|
1573
|
+
const makeAggKey = /* @__PURE__ */ __name((func, field) => `${func}_${field}`, "makeAggKey");
|
|
1574
|
+
const makeGroupKey = /* @__PURE__ */ __name((field) => `GROUP_BY_${field}`, "makeGroupKey");
|
|
1575
|
+
const isNumeric = /* @__PURE__ */ __name((v) => typeof v === "number" || v instanceof Date, "isNumeric");
|
|
1576
|
+
if (groupBy.length === 0) {
|
|
1577
|
+
const out = {};
|
|
1578
|
+
const computeField = /* @__PURE__ */ __name((fn, field) => {
|
|
1579
|
+
const values = rows.map((r) => r[field]).filter((v) => v !== void 0 && v !== null);
|
|
1580
|
+
if (fn === "COUNT") {
|
|
1581
|
+
out[makeAggKey("COUNT", field)] = values.length;
|
|
1582
|
+
return;
|
|
1583
|
+
}
|
|
1584
|
+
if (values.length === 0) {
|
|
1585
|
+
out[makeAggKey(fn, field)] = null;
|
|
1586
|
+
return;
|
|
1587
|
+
}
|
|
1588
|
+
if (fn === "SUM" || fn === "AVG") {
|
|
1589
|
+
const nums = values.map((v) => v instanceof Date ? v.getTime() : Number(v)).filter((n) => !Number.isNaN(n));
|
|
1590
|
+
const sum = nums.reduce((s, v) => s + v, 0);
|
|
1591
|
+
out[makeAggKey(fn, field)] = fn === "SUM" ? sum : nums.length ? sum / nums.length : null;
|
|
1592
|
+
return;
|
|
1593
|
+
}
|
|
1594
|
+
if (fn === "MAX") {
|
|
1595
|
+
if (values.every(isNumeric)) {
|
|
1596
|
+
const nums = values.map((v) => v instanceof Date ? v.getTime() : Number(v));
|
|
1597
|
+
out[makeAggKey("MAX", field)] = Math.max(...nums);
|
|
1598
|
+
} else {
|
|
1599
|
+
out[makeAggKey("MAX", field)] = values.reduce((a, b) => String(a) > String(b) ? a : b);
|
|
1600
|
+
}
|
|
1601
|
+
return;
|
|
1602
|
+
}
|
|
1603
|
+
if (fn === "MIN") {
|
|
1604
|
+
if (values.every(isNumeric)) {
|
|
1605
|
+
const nums = values.map((v) => v instanceof Date ? v.getTime() : Number(v));
|
|
1606
|
+
out[makeAggKey("MIN", field)] = Math.min(...nums);
|
|
1607
|
+
} else {
|
|
1608
|
+
out[makeAggKey("MIN", field)] = values.reduce((a, b) => String(a) < String(b) ? a : b);
|
|
1609
|
+
}
|
|
1610
|
+
return;
|
|
1611
|
+
}
|
|
1612
|
+
}, "computeField");
|
|
1613
|
+
(aggs.count ?? []).forEach((f) => computeField("COUNT", String(f)));
|
|
1614
|
+
(aggs.sum ?? []).forEach((f) => computeField("SUM", String(f)));
|
|
1615
|
+
(aggs.avg ?? []).forEach((f) => computeField("AVG", String(f)));
|
|
1616
|
+
(aggs.max ?? []).forEach((f) => computeField("MAX", String(f)));
|
|
1617
|
+
(aggs.min ?? []).forEach((f) => computeField("MIN", String(f)));
|
|
1618
|
+
records.push(out);
|
|
1619
|
+
} else {
|
|
1620
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1621
|
+
rows.forEach((r) => {
|
|
1622
|
+
const key = groupBy.map((g) => JSON.stringify(r[String(g)])).join("|");
|
|
1623
|
+
const arr = groups.get(key) ?? [];
|
|
1624
|
+
arr.push(r);
|
|
1625
|
+
groups.set(key, arr);
|
|
1626
|
+
});
|
|
1627
|
+
groups.forEach((groupRows, key) => {
|
|
1628
|
+
const parts = key.split("|").map((p) => JSON.parse(p));
|
|
1629
|
+
const out = {};
|
|
1630
|
+
groupBy.forEach((g, i) => {
|
|
1631
|
+
const val = parts[i];
|
|
1632
|
+
out[makeGroupKey(String(g))] = typeof val === "boolean" ? val ? 1 : 0 : val;
|
|
1633
|
+
});
|
|
1634
|
+
const computeField = /* @__PURE__ */ __name((fn, field) => {
|
|
1635
|
+
const values = groupRows.map((r) => r[field]).filter((v) => v !== void 0 && v !== null);
|
|
1636
|
+
if (fn === "COUNT") {
|
|
1637
|
+
out[makeAggKey("COUNT", field)] = values.length;
|
|
1638
|
+
return;
|
|
1639
|
+
}
|
|
1640
|
+
if (values.length === 0) {
|
|
1641
|
+
out[makeAggKey(fn, field)] = null;
|
|
1642
|
+
return;
|
|
1643
|
+
}
|
|
1644
|
+
if (fn === "SUM" || fn === "AVG") {
|
|
1645
|
+
const nums = values.map((v) => v instanceof Date ? v.getTime() : Number(v)).filter((n) => !Number.isNaN(n));
|
|
1646
|
+
const sum = nums.reduce((s, v) => s + v, 0);
|
|
1647
|
+
out[makeAggKey(fn, field)] = fn === "SUM" ? sum : nums.length ? sum / nums.length : null;
|
|
1648
|
+
return;
|
|
1649
|
+
}
|
|
1650
|
+
if (fn === "MAX") {
|
|
1651
|
+
if (values.every(isNumeric)) {
|
|
1652
|
+
const nums = values.map((v) => v instanceof Date ? v.getTime() : Number(v));
|
|
1653
|
+
out[makeAggKey("MAX", field)] = Math.max(...nums);
|
|
1654
|
+
} else {
|
|
1655
|
+
out[makeAggKey("MAX", field)] = values.reduce((a, b) => String(a) > String(b) ? a : b);
|
|
1656
|
+
}
|
|
1657
|
+
return;
|
|
1658
|
+
}
|
|
1659
|
+
if (fn === "MIN") {
|
|
1660
|
+
if (values.every(isNumeric)) {
|
|
1661
|
+
const nums = values.map((v) => v instanceof Date ? v.getTime() : Number(v));
|
|
1662
|
+
out[makeAggKey("MIN", field)] = Math.min(...nums);
|
|
1663
|
+
} else {
|
|
1664
|
+
out[makeAggKey("MIN", field)] = values.reduce((a, b) => String(a) < String(b) ? a : b);
|
|
1665
|
+
}
|
|
1666
|
+
return;
|
|
1667
|
+
}
|
|
1668
|
+
}, "computeField");
|
|
1669
|
+
(aggs.count ?? []).forEach((f) => computeField("COUNT", String(f)));
|
|
1670
|
+
(aggs.sum ?? []).forEach((f) => computeField("SUM", String(f)));
|
|
1671
|
+
(aggs.avg ?? []).forEach((f) => computeField("AVG", String(f)));
|
|
1672
|
+
(aggs.max ?? []).forEach((f) => computeField("MAX", String(f)));
|
|
1673
|
+
(aggs.min ?? []).forEach((f) => computeField("MIN", String(f)));
|
|
1674
|
+
records.push(out);
|
|
1675
|
+
});
|
|
1676
|
+
}
|
|
1677
|
+
return records.map((r) => AggregateBuilder.convertToAggregateResponse([
|
|
1678
|
+
r
|
|
1679
|
+
])[0]);
|
|
1392
1680
|
}
|
|
1393
1681
|
async count(filter) {
|
|
1394
|
-
const
|
|
1682
|
+
const { filterQuery } = this.filterQueryBuilder.buildFindOptions({
|
|
1395
1683
|
filter
|
|
1396
1684
|
});
|
|
1397
|
-
|
|
1398
|
-
|
|
1685
|
+
const em = this.repo.getEntityManager();
|
|
1686
|
+
let where = filterQuery;
|
|
1687
|
+
if (this.useSoftDelete) {
|
|
1688
|
+
const deletedFilter = {
|
|
1689
|
+
deletedAt: null
|
|
1690
|
+
};
|
|
1691
|
+
where = where ? {
|
|
1692
|
+
$and: [
|
|
1693
|
+
where,
|
|
1694
|
+
deletedFilter
|
|
1695
|
+
]
|
|
1696
|
+
} : deletedFilter;
|
|
1697
|
+
}
|
|
1698
|
+
return em.count(this.EntityClass, where ?? {});
|
|
1399
1699
|
}
|
|
1400
1700
|
/**
|
|
1401
1701
|
* Find an entity by it's `id`.
|
|
@@ -1407,10 +1707,33 @@ var MikroOrmQueryService = class extends RelationQueryService {
|
|
|
1407
1707
|
* @param id - The id of the record to find.
|
|
1408
1708
|
*/
|
|
1409
1709
|
async findById(id, opts) {
|
|
1410
|
-
const
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1710
|
+
const metadata = this.em.getMetadata().get(this.repo.getEntityName());
|
|
1711
|
+
const primaryKey = metadata.primaryKeys[0];
|
|
1712
|
+
let where = {
|
|
1713
|
+
[primaryKey]: id
|
|
1714
|
+
};
|
|
1715
|
+
if (opts?.filter) {
|
|
1716
|
+
const whereBuilder = new WhereBuilder();
|
|
1717
|
+
const additional = whereBuilder.build(opts.filter);
|
|
1718
|
+
where = {
|
|
1719
|
+
$and: [
|
|
1720
|
+
where,
|
|
1721
|
+
additional
|
|
1722
|
+
]
|
|
1723
|
+
};
|
|
1724
|
+
}
|
|
1725
|
+
if (this.useSoftDelete) {
|
|
1726
|
+
where = {
|
|
1727
|
+
$and: [
|
|
1728
|
+
where,
|
|
1729
|
+
{
|
|
1730
|
+
deletedAt: null
|
|
1731
|
+
}
|
|
1732
|
+
]
|
|
1733
|
+
};
|
|
1734
|
+
}
|
|
1735
|
+
const entity = await this.em.findOne(this.EntityClass, where);
|
|
1736
|
+
return entity ?? void 0;
|
|
1414
1737
|
}
|
|
1415
1738
|
/**
|
|
1416
1739
|
* Gets an entity by it's `id`. If the entity is not found a rejected promise is returned.
|
|
@@ -1483,7 +1806,7 @@ var MikroOrmQueryService = class extends RelationQueryService {
|
|
|
1483
1806
|
return entity;
|
|
1484
1807
|
}
|
|
1485
1808
|
/**
|
|
1486
|
-
* Update multiple entities with a `@nestjs-query
|
|
1809
|
+
* Update multiple entities with a `@ptc-org/nestjs-query-core` Filter.
|
|
1487
1810
|
*
|
|
1488
1811
|
* @example
|
|
1489
1812
|
* ```ts
|
|
@@ -1534,7 +1857,7 @@ var MikroOrmQueryService = class extends RelationQueryService {
|
|
|
1534
1857
|
return entity;
|
|
1535
1858
|
}
|
|
1536
1859
|
/**
|
|
1537
|
-
* Delete multiple records with a `@nestjs-query
|
|
1860
|
+
* Delete multiple records with a `@ptc-org/nestjs-query-core` `Filter`.
|
|
1538
1861
|
*
|
|
1539
1862
|
* @example
|
|
1540
1863
|
*
|
|
@@ -1610,7 +1933,7 @@ var MikroOrmQueryService = class extends RelationQueryService {
|
|
|
1610
1933
|
return entity;
|
|
1611
1934
|
}
|
|
1612
1935
|
/**
|
|
1613
|
-
* Restores multiple records with a `@nestjs-query
|
|
1936
|
+
* Restores multiple records with a `@ptc-org/nestjs-query-core` `Filter`.
|
|
1614
1937
|
*
|
|
1615
1938
|
* @example
|
|
1616
1939
|
*
|
|
@@ -1683,7 +2006,7 @@ var MikroOrmQueryService = class extends RelationQueryService {
|
|
|
1683
2006
|
function createMikroOrmQueryServiceProvider(EntityClass, contextName) {
|
|
1684
2007
|
return {
|
|
1685
2008
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1686
|
-
provide:
|
|
2009
|
+
provide: nestjsQueryCore.getQueryServiceToken(EntityClass),
|
|
1687
2010
|
useFactory(repo) {
|
|
1688
2011
|
return new MikroOrmQueryService(repo);
|
|
1689
2012
|
},
|
|
@@ -1720,12 +2043,12 @@ var NestjsQueryMikroOrmModule = class _NestjsQueryMikroOrmModule {
|
|
|
1720
2043
|
};
|
|
1721
2044
|
|
|
1722
2045
|
exports.AggregateBuilder = AggregateBuilder;
|
|
2046
|
+
exports.ComparisonBuilder = ComparisonBuilder;
|
|
1723
2047
|
exports.FilterQueryBuilder = FilterQueryBuilder;
|
|
1724
2048
|
exports.MikroOrmQueryService = MikroOrmQueryService;
|
|
1725
2049
|
exports.NestjsQueryMikroOrmModule = NestjsQueryMikroOrmModule;
|
|
1726
2050
|
exports.RelationQueryBuilder = RelationQueryBuilder;
|
|
1727
2051
|
exports.RelationQueryService = RelationQueryService;
|
|
1728
|
-
exports.SQLComparisonBuilder = SQLComparisonBuilder;
|
|
1729
2052
|
exports.WhereBuilder = WhereBuilder;
|
|
1730
2053
|
exports.createMikroOrmQueryServiceProviders = createMikroOrmQueryServiceProviders;
|
|
1731
2054
|
//# sourceMappingURL=index.cjs.map
|