@taylordb/query-builder 0.9.15 → 0.10.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 +5 -2
- package/dist/cjs/@types/aggregate.d.ts +16 -1
- package/dist/cjs/__tests__/query-builder.spec.js +93 -3
- package/dist/cjs/__tests__/query-builder.spec.js.map +1 -1
- package/dist/cjs/__tests__/taylorclient.types.d.ts +3 -3
- package/dist/cjs/aggregation-helpers.d.ts +77 -0
- package/dist/cjs/aggregation-helpers.js +103 -0
- package/dist/cjs/aggregation-helpers.js.map +1 -0
- package/dist/cjs/aggregation-query-builder.d.ts +30 -22
- package/dist/cjs/aggregation-query-builder.js +124 -20
- package/dist/cjs/aggregation-query-builder.js.map +1 -1
- package/dist/cjs/batch-query-builder.d.ts +1 -1
- package/dist/cjs/batch-query-builder.js +1 -1
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.js +11 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/@types/aggregate.d.ts +16 -1
- package/dist/esm/__tests__/query-builder.spec.js +94 -4
- package/dist/esm/__tests__/query-builder.spec.js.map +1 -1
- package/dist/esm/__tests__/taylorclient.types.d.ts +3 -3
- package/dist/esm/aggregation-helpers.d.ts +77 -0
- package/dist/esm/aggregation-helpers.js +92 -0
- package/dist/esm/aggregation-helpers.js.map +1 -0
- package/dist/esm/aggregation-query-builder.d.ts +30 -22
- package/dist/esm/aggregation-query-builder.js +124 -20
- package/dist/esm/aggregation-query-builder.js.map +1 -1
- package/dist/esm/batch-query-builder.d.ts +1 -1
- package/dist/esm/batch-query-builder.js +1 -1
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper functions for creating aggregation metrics.
|
|
3
|
+
* These functions return objects that describe the aggregation to perform.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Creates a count aggregation for the specified field.
|
|
7
|
+
* @param field - The field name to count.
|
|
8
|
+
* @returns An aggregation helper object.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* count('id')
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export function count(field) {
|
|
16
|
+
return { field, aggregation: 'count' };
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Creates an average aggregation for the specified field.
|
|
20
|
+
* @param field - The field name to average.
|
|
21
|
+
* @returns An aggregation helper object.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* avg('age')
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export function avg(field) {
|
|
29
|
+
return { field, aggregation: 'average' };
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Creates a sum aggregation for the specified field.
|
|
33
|
+
* @param field - The field name to sum.
|
|
34
|
+
* @returns An aggregation helper object.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* sum('age')
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export function sum(field) {
|
|
42
|
+
return { field, aggregation: 'sum' };
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Creates a median aggregation for the specified field.
|
|
46
|
+
* @param field - The field name to calculate median for.
|
|
47
|
+
* @returns An aggregation helper object.
|
|
48
|
+
*/
|
|
49
|
+
export function median(field) {
|
|
50
|
+
return { field, aggregation: 'median' };
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Creates a min aggregation for the specified field.
|
|
54
|
+
* @param field - The field name to find minimum for.
|
|
55
|
+
* @returns An aggregation helper object.
|
|
56
|
+
*/
|
|
57
|
+
export function min(field) {
|
|
58
|
+
return { field, aggregation: 'min' };
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Creates a max aggregation for the specified field.
|
|
62
|
+
* @param field - The field name to find maximum for.
|
|
63
|
+
* @returns An aggregation helper object.
|
|
64
|
+
*/
|
|
65
|
+
export function max(field) {
|
|
66
|
+
return { field, aggregation: 'max' };
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Creates a range aggregation for the specified field.
|
|
70
|
+
* @param field - The field name to calculate range for.
|
|
71
|
+
* @returns An aggregation helper object.
|
|
72
|
+
*/
|
|
73
|
+
export function range(field) {
|
|
74
|
+
return { field, aggregation: 'range' };
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Creates a standard deviation aggregation for the specified field.
|
|
78
|
+
* @param field - The field name to calculate standard deviation for.
|
|
79
|
+
* @returns An aggregation helper object.
|
|
80
|
+
*/
|
|
81
|
+
export function stdDev(field) {
|
|
82
|
+
return { field, aggregation: 'standardDeviation' };
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Creates a unique count aggregation for the specified field.
|
|
86
|
+
* @param field - The field name to count unique values for.
|
|
87
|
+
* @returns An aggregation helper object.
|
|
88
|
+
*/
|
|
89
|
+
export function unique(field) {
|
|
90
|
+
return { field, aggregation: 'unique' };
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=aggregation-helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aggregation-helpers.js","sourceRoot":"","sources":["../../src/aggregation-helpers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAUH;;;;;;;;;GASG;AACH,MAAM,UAAU,KAAK,CACnB,KAAa;IAEb,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,OAAgB,EAAE,CAAC;AAClD,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,GAAG,CACjB,KAAa;IAEb,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,SAAkB,EAAE,CAAC;AACpD,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,GAAG,CACjB,KAAa;IAEb,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,KAAc,EAAE,CAAC;AAChD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,MAAM,CACpB,KAAa;IAEb,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,QAAiB,EAAE,CAAC;AACnD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,GAAG,CACjB,KAAa;IAEb,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,KAAc,EAAE,CAAC;AAChD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,GAAG,CACjB,KAAa;IAEb,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,KAAc,EAAE,CAAC;AAChD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,KAAK,CACnB,KAAa;IAEb,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,OAAgB,EAAE,CAAC;AAClD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,MAAM,CACpB,KAAa;IAEb,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,mBAA4B,EAAE,CAAC;AAC9D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,MAAM,CACpB,KAAa;IAEb,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,QAAiB,EAAE,CAAC;AACnD,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { AggregateNode, AggregateRecord } from './@types/aggregate.js';
|
|
1
|
+
import type { AggregateNode, AggregateRecord, MetricsRecord } from './@types/aggregate.js';
|
|
2
2
|
import type { AnyDB } from './@types/internal-types.js';
|
|
3
|
+
import type { AggregationHelper } from './aggregation-helpers.js';
|
|
3
4
|
import { Executor } from './executor.js';
|
|
4
5
|
import { FilterableQueryBuilder } from './where-query-builder.js';
|
|
5
6
|
/**
|
|
@@ -11,9 +12,9 @@ import { FilterableQueryBuilder } from './where-query-builder.js';
|
|
|
11
12
|
*/
|
|
12
13
|
export declare class AggregationQueryBuilder<DB extends AnyDB, TableName extends keyof DB, TGroupBy extends readonly (keyof DB[TableName] & string)[] = [], TAggregations extends {
|
|
13
14
|
[K in keyof DB[TableName] & string]?: readonly (keyof DB[TableName][K]['aggregations'])[];
|
|
14
|
-
} = object> extends FilterableQueryBuilder<DB, TableName> {
|
|
15
|
+
} = object, TMetrics extends Record<string, AggregationHelper> | undefined = undefined> extends FilterableQueryBuilder<DB, TableName> {
|
|
15
16
|
#private;
|
|
16
|
-
constructor(node: AggregateNode, executor: Executor);
|
|
17
|
+
constructor(node: AggregateNode, executor: Executor, metrics?: TMetrics);
|
|
17
18
|
/**
|
|
18
19
|
* Groups the results by a specified field.
|
|
19
20
|
* @param field - The field to group by.
|
|
@@ -25,73 +26,80 @@ export declare class AggregationQueryBuilder<DB extends AnyDB, TableName extends
|
|
|
25
26
|
* const userCounts = await qb
|
|
26
27
|
* .aggregateFrom('users')
|
|
27
28
|
* .groupBy('role')
|
|
28
|
-
* .
|
|
29
|
+
* .metrics({ count: count('id') })
|
|
29
30
|
* .execute();
|
|
30
31
|
* ```
|
|
31
32
|
*/
|
|
32
33
|
groupBy<const TField extends keyof DB[TableName] & string>(field: TField, direction?: 'asc' | 'desc'): AggregationQueryBuilder<DB, TableName, [
|
|
33
34
|
...TGroupBy,
|
|
34
35
|
TField
|
|
35
|
-
], TAggregations>;
|
|
36
|
+
], TAggregations, TMetrics>;
|
|
36
37
|
/**
|
|
37
|
-
* Specifies the
|
|
38
|
-
*
|
|
39
|
-
* @
|
|
38
|
+
* Specifies the metrics to compute using helper functions.
|
|
39
|
+
* Returns a flat array of records with groupBy fields and metrics at the top level.
|
|
40
|
+
* @param metrics - An object where keys are metric names and values are aggregation helper functions.
|
|
41
|
+
* @returns A new `AggregationQueryBuilder` instance with the metrics applied.
|
|
40
42
|
*
|
|
41
43
|
* @example
|
|
42
44
|
* ```typescript
|
|
43
45
|
* const userStats = await qb
|
|
44
46
|
* .aggregateFrom('users')
|
|
45
|
-
* .
|
|
46
|
-
*
|
|
47
|
-
*
|
|
47
|
+
* .groupBy('role', 'asc')
|
|
48
|
+
* .groupBy('permission', 'desc')
|
|
49
|
+
* .metrics({
|
|
50
|
+
* idCount: count('id'),
|
|
51
|
+
* ageAvg: avg('age'),
|
|
52
|
+
* ageSum: sum('age')
|
|
48
53
|
* })
|
|
49
54
|
* .execute();
|
|
50
55
|
* ```
|
|
51
56
|
*/
|
|
52
|
-
|
|
53
|
-
[K in keyof DB[TableName] & string]?: readonly (keyof DB[TableName][K]['aggregations'])[];
|
|
54
|
-
}>(aggregates: T): AggregationQueryBuilder<DB, TableName, TGroupBy, TAggregations & T>;
|
|
57
|
+
metrics<const T extends Record<string, AggregationHelper>>(metrics: T): AggregationQueryBuilder<DB, TableName, TGroupBy, TAggregations, T>;
|
|
55
58
|
/**
|
|
56
59
|
* Sets the maximum number of records to return.
|
|
57
60
|
* @param count - The maximum number of records.
|
|
58
61
|
* @returns A new `AggregationQueryBuilder` instance with the limit applied.
|
|
59
62
|
*/
|
|
60
|
-
limit(count: number): AggregationQueryBuilder<DB, TableName, TGroupBy, TAggregations>;
|
|
63
|
+
limit(count: number): AggregationQueryBuilder<DB, TableName, TGroupBy, TAggregations, TMetrics>;
|
|
61
64
|
/**
|
|
62
65
|
* Sets the number of records to skip.
|
|
63
66
|
* @param count - The number of records to skip.
|
|
64
67
|
* @returns A new `AggregationQueryBuilder` instance with the offset applied.
|
|
65
68
|
*/
|
|
66
|
-
offset(count: number): AggregationQueryBuilder<DB, TableName, TGroupBy, TAggregations>;
|
|
69
|
+
offset(count: number): AggregationQueryBuilder<DB, TableName, TGroupBy, TAggregations, TMetrics>;
|
|
67
70
|
/**
|
|
68
71
|
* Paginates the results.
|
|
69
72
|
* @param page - The page number to retrieve.
|
|
70
73
|
* @param limit - The number of records per page.
|
|
71
74
|
* @returns A new `AggregationQueryBuilder` instance with pagination applied.
|
|
72
75
|
*/
|
|
73
|
-
paginate(page: number, limit: number): AggregationQueryBuilder<DB, TableName, TGroupBy, TAggregations>;
|
|
76
|
+
paginate(page: number, limit: number): AggregationQueryBuilder<DB, TableName, TGroupBy, TAggregations, TMetrics>;
|
|
74
77
|
/**
|
|
75
78
|
* Sorts the results by a specified field.
|
|
76
79
|
* @param field - The field to sort by.
|
|
77
80
|
* @param direction - The sort direction ('asc' or 'desc').
|
|
78
81
|
* @returns A new `AggregationQueryBuilder` instance with the sorting applied.
|
|
79
82
|
*/
|
|
80
|
-
orderBy(field: keyof DB[TableName], direction?: 'asc' | 'desc'): AggregationQueryBuilder<DB, TableName, TGroupBy, TAggregations>;
|
|
83
|
+
orderBy(field: keyof DB[TableName], direction?: 'asc' | 'desc'): AggregationQueryBuilder<DB, TableName, TGroupBy, TAggregations, TMetrics>;
|
|
81
84
|
/**
|
|
82
85
|
* Executes the aggregation query.
|
|
83
86
|
* @returns A promise that resolves with an array of the aggregation results.
|
|
87
|
+
* If metrics were specified, returns a flat array. Otherwise, returns the nested structure.
|
|
84
88
|
*
|
|
85
89
|
* @example
|
|
86
90
|
* ```typescript
|
|
87
91
|
* const userCounts = await qb
|
|
88
92
|
* .aggregateFrom('users')
|
|
89
93
|
* .groupBy('role')
|
|
90
|
-
* .
|
|
94
|
+
* .metrics({ count: count('id') })
|
|
91
95
|
* .execute();
|
|
92
96
|
* ```
|
|
93
97
|
*/
|
|
94
|
-
execute(): Promise<AggregateRecord<DB, TableName, TGroupBy, TAggregations>[]>;
|
|
98
|
+
execute(): Promise<TMetrics extends Record<string, AggregationHelper> ? MetricsRecord<DB, TableName, TGroupBy, TMetrics>[] : AggregateRecord<DB, TableName, TGroupBy, TAggregations>[]>;
|
|
99
|
+
/**
|
|
100
|
+
* Flattens the nested AggregateRecord structure into a flat array of metrics records.
|
|
101
|
+
*/
|
|
102
|
+
private _flattenMetrics;
|
|
95
103
|
/**
|
|
96
104
|
* Subscribes to the results of the aggregation query.
|
|
97
105
|
* @param callback - A callback function that will be called with the results of the aggregation query.
|
|
@@ -102,7 +110,7 @@ export declare class AggregationQueryBuilder<DB extends AnyDB, TableName extends
|
|
|
102
110
|
* const unsubscribe = qb
|
|
103
111
|
* .aggregateFrom('users')
|
|
104
112
|
* .groupBy('role')
|
|
105
|
-
* .
|
|
113
|
+
* .metrics({ count: count('id') })
|
|
106
114
|
* .subscribe((userCounts) => {
|
|
107
115
|
* console.log('User counts by role:', userCounts);
|
|
108
116
|
* });
|
|
@@ -111,7 +119,7 @@ export declare class AggregationQueryBuilder<DB extends AnyDB, TableName extends
|
|
|
111
119
|
* unsubscribe();
|
|
112
120
|
* ```
|
|
113
121
|
*/
|
|
114
|
-
subscribe(callback: (result: AggregateRecord<DB, TableName, TGroupBy, TAggregations>[]) => void): Promise<{
|
|
122
|
+
subscribe(callback: (result: TMetrics extends Record<string, AggregationHelper> ? MetricsRecord<DB, TableName, TGroupBy, TMetrics>[] : AggregateRecord<DB, TableName, TGroupBy, TAggregations>[]) => void): Promise<{
|
|
115
123
|
unsubscribe: () => Promise<void>;
|
|
116
124
|
}>;
|
|
117
125
|
compile(): {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isEmpty } from 'lodash';
|
|
1
2
|
import { FilterableQueryBuilder } from './where-query-builder.js';
|
|
2
3
|
/**
|
|
3
4
|
* A query builder for performing aggregation queries.
|
|
@@ -8,9 +9,11 @@ import { FilterableQueryBuilder } from './where-query-builder.js';
|
|
|
8
9
|
*/
|
|
9
10
|
export class AggregationQueryBuilder extends FilterableQueryBuilder {
|
|
10
11
|
#node;
|
|
11
|
-
|
|
12
|
+
#metrics;
|
|
13
|
+
constructor(node, executor, metrics) {
|
|
12
14
|
super(node, executor);
|
|
13
15
|
this.#node = node;
|
|
16
|
+
this.#metrics = metrics;
|
|
14
17
|
}
|
|
15
18
|
/**
|
|
16
19
|
* Groups the results by a specified field.
|
|
@@ -23,7 +26,7 @@ export class AggregationQueryBuilder extends FilterableQueryBuilder {
|
|
|
23
26
|
* const userCounts = await qb
|
|
24
27
|
* .aggregateFrom('users')
|
|
25
28
|
* .groupBy('role')
|
|
26
|
-
* .
|
|
29
|
+
* .metrics({ count: count('id') })
|
|
27
30
|
* .execute();
|
|
28
31
|
* ```
|
|
29
32
|
*/
|
|
@@ -35,33 +38,48 @@ export class AggregationQueryBuilder extends FilterableQueryBuilder {
|
|
|
35
38
|
return new AggregationQueryBuilder({
|
|
36
39
|
...this.#node,
|
|
37
40
|
groupings: [...(this.#node.groupings || []), newGrouping],
|
|
38
|
-
}, this._executor);
|
|
41
|
+
}, this._executor, this.#metrics);
|
|
39
42
|
}
|
|
40
43
|
/**
|
|
41
|
-
* Specifies the
|
|
42
|
-
*
|
|
43
|
-
* @
|
|
44
|
+
* Specifies the metrics to compute using helper functions.
|
|
45
|
+
* Returns a flat array of records with groupBy fields and metrics at the top level.
|
|
46
|
+
* @param metrics - An object where keys are metric names and values are aggregation helper functions.
|
|
47
|
+
* @returns A new `AggregationQueryBuilder` instance with the metrics applied.
|
|
44
48
|
*
|
|
45
49
|
* @example
|
|
46
50
|
* ```typescript
|
|
47
51
|
* const userStats = await qb
|
|
48
52
|
* .aggregateFrom('users')
|
|
49
|
-
* .
|
|
50
|
-
*
|
|
51
|
-
*
|
|
53
|
+
* .groupBy('role', 'asc')
|
|
54
|
+
* .groupBy('permission', 'desc')
|
|
55
|
+
* .metrics({
|
|
56
|
+
* idCount: count('id'),
|
|
57
|
+
* ageAvg: avg('age'),
|
|
58
|
+
* ageSum: sum('age')
|
|
52
59
|
* })
|
|
53
60
|
* .execute();
|
|
54
61
|
* ```
|
|
55
62
|
*/
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
63
|
+
metrics(metrics) {
|
|
64
|
+
// Convert metrics format to aggregations format for the backend
|
|
65
|
+
const newAggregates = {
|
|
66
|
+
...this.#node.aggregations,
|
|
67
|
+
};
|
|
68
|
+
for (const metricName in metrics) {
|
|
69
|
+
const helper = metrics[metricName];
|
|
70
|
+
const field = helper.field;
|
|
71
|
+
const aggregation = helper.aggregation;
|
|
72
|
+
if (!newAggregates[field]) {
|
|
73
|
+
newAggregates[field] = [];
|
|
74
|
+
}
|
|
75
|
+
if (!newAggregates[field].includes(aggregation)) {
|
|
76
|
+
newAggregates[field].push(aggregation);
|
|
77
|
+
}
|
|
60
78
|
}
|
|
61
79
|
return new AggregationQueryBuilder({
|
|
62
80
|
...this.#node,
|
|
63
81
|
aggregations: newAggregates,
|
|
64
|
-
}, this._executor);
|
|
82
|
+
}, this._executor, metrics);
|
|
65
83
|
}
|
|
66
84
|
/**
|
|
67
85
|
* Sets the maximum number of records to return.
|
|
@@ -72,7 +90,7 @@ export class AggregationQueryBuilder extends FilterableQueryBuilder {
|
|
|
72
90
|
return new AggregationQueryBuilder({
|
|
73
91
|
...this.#node,
|
|
74
92
|
pagination: { ...this.#node.pagination, limit: count },
|
|
75
|
-
}, this._executor);
|
|
93
|
+
}, this._executor, this.#metrics);
|
|
76
94
|
}
|
|
77
95
|
/**
|
|
78
96
|
* Sets the number of records to skip.
|
|
@@ -83,7 +101,7 @@ export class AggregationQueryBuilder extends FilterableQueryBuilder {
|
|
|
83
101
|
return new AggregationQueryBuilder({
|
|
84
102
|
...this.#node,
|
|
85
103
|
pagination: { ...this.#node.pagination, offset: count },
|
|
86
|
-
}, this._executor);
|
|
104
|
+
}, this._executor, this.#metrics);
|
|
87
105
|
}
|
|
88
106
|
/**
|
|
89
107
|
* Paginates the results.
|
|
@@ -108,25 +126,101 @@ export class AggregationQueryBuilder extends FilterableQueryBuilder {
|
|
|
108
126
|
return new AggregationQueryBuilder({
|
|
109
127
|
...this.#node,
|
|
110
128
|
sorting: [...(this.#node.sorting || []), newSorting],
|
|
111
|
-
}, this._executor);
|
|
129
|
+
}, this._executor, this.#metrics);
|
|
112
130
|
}
|
|
113
131
|
/**
|
|
114
132
|
* Executes the aggregation query.
|
|
115
133
|
* @returns A promise that resolves with an array of the aggregation results.
|
|
134
|
+
* If metrics were specified, returns a flat array. Otherwise, returns the nested structure.
|
|
116
135
|
*
|
|
117
136
|
* @example
|
|
118
137
|
* ```typescript
|
|
119
138
|
* const userCounts = await qb
|
|
120
139
|
* .aggregateFrom('users')
|
|
121
140
|
* .groupBy('role')
|
|
122
|
-
* .
|
|
141
|
+
* .metrics({ count: count('id') })
|
|
123
142
|
* .execute();
|
|
124
143
|
* ```
|
|
125
144
|
*/
|
|
126
145
|
async execute() {
|
|
127
146
|
const response = await this._executor.execute(this);
|
|
147
|
+
// If metrics are used, flatten the nested structure
|
|
148
|
+
if (this.#metrics) {
|
|
149
|
+
return this._flattenMetrics(response[0], this.#node.groupings
|
|
150
|
+
?.filter((g) => 'field' in g && !('formula' in g))
|
|
151
|
+
.map(g => g.field) || [], this.#metrics);
|
|
152
|
+
}
|
|
128
153
|
return response;
|
|
129
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* Flattens the nested AggregateRecord structure into a flat array of metrics records.
|
|
157
|
+
*/
|
|
158
|
+
_flattenMetrics(records, groupByFields, metrics) {
|
|
159
|
+
const result = [];
|
|
160
|
+
const flatten = (record, path) => {
|
|
161
|
+
if ('slug' in record && 'value' in record) {
|
|
162
|
+
const currentPath = {
|
|
163
|
+
...path,
|
|
164
|
+
[record.slug]: record.value,
|
|
165
|
+
};
|
|
166
|
+
if ('children' in record &&
|
|
167
|
+
Array.isArray(record.children) &&
|
|
168
|
+
!isEmpty(record.children)) {
|
|
169
|
+
// Has children, recurse
|
|
170
|
+
for (const child of record.children) {
|
|
171
|
+
flatten(child, currentPath);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
// Leaf node, create flat record
|
|
176
|
+
const flatRecord = { ...currentPath };
|
|
177
|
+
// Add metrics
|
|
178
|
+
for (const metricName in metrics) {
|
|
179
|
+
const helper = metrics[metricName];
|
|
180
|
+
const field = helper.field;
|
|
181
|
+
const aggregation = helper.aggregation;
|
|
182
|
+
// Extract the metric value from aggregates
|
|
183
|
+
if (record.aggregates &&
|
|
184
|
+
field in record.aggregates &&
|
|
185
|
+
record.aggregates[field] &&
|
|
186
|
+
typeof record.aggregates[field] === 'object' &&
|
|
187
|
+
aggregation in record.aggregates[field]) {
|
|
188
|
+
flatRecord[metricName] = record.aggregates[field][aggregation];
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
flatRecord[metricName] = null;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
result.push(flatRecord);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
// No groupBy, just metrics
|
|
199
|
+
const flatRecord = {};
|
|
200
|
+
// Add metrics
|
|
201
|
+
for (const metricName in metrics) {
|
|
202
|
+
const helper = metrics[metricName];
|
|
203
|
+
const field = helper.field;
|
|
204
|
+
const aggregation = helper.aggregation;
|
|
205
|
+
if (record.aggregates &&
|
|
206
|
+
field in record.aggregates &&
|
|
207
|
+
record.aggregates[field] &&
|
|
208
|
+
typeof record.aggregates[field] === 'object' &&
|
|
209
|
+
aggregation in record.aggregates[field]) {
|
|
210
|
+
flatRecord[metricName] = record.aggregates[field][aggregation];
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
flatRecord[metricName] = null;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
result.push(flatRecord);
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
for (const record of records) {
|
|
220
|
+
flatten(record, {});
|
|
221
|
+
}
|
|
222
|
+
return result;
|
|
223
|
+
}
|
|
130
224
|
/**
|
|
131
225
|
* Subscribes to the results of the aggregation query.
|
|
132
226
|
* @param callback - A callback function that will be called with the results of the aggregation query.
|
|
@@ -137,7 +231,7 @@ export class AggregationQueryBuilder extends FilterableQueryBuilder {
|
|
|
137
231
|
* const unsubscribe = qb
|
|
138
232
|
* .aggregateFrom('users')
|
|
139
233
|
* .groupBy('role')
|
|
140
|
-
* .
|
|
234
|
+
* .metrics({ count: count('id') })
|
|
141
235
|
* .subscribe((userCounts) => {
|
|
142
236
|
* console.log('User counts by role:', userCounts);
|
|
143
237
|
* });
|
|
@@ -147,7 +241,17 @@ export class AggregationQueryBuilder extends FilterableQueryBuilder {
|
|
|
147
241
|
* ```
|
|
148
242
|
*/
|
|
149
243
|
subscribe(callback) {
|
|
150
|
-
return this._executor.subscribe([this],
|
|
244
|
+
return this._executor.subscribe([this], (rawResult) => {
|
|
245
|
+
if (this.#metrics) {
|
|
246
|
+
const flattened = this._flattenMetrics(rawResult, this.#node.groupings
|
|
247
|
+
?.filter((g) => 'field' in g && !('formula' in g))
|
|
248
|
+
.map(g => g.field) || [], this.#metrics);
|
|
249
|
+
callback(flattened);
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
callback(rawResult);
|
|
253
|
+
}
|
|
254
|
+
});
|
|
151
255
|
}
|
|
152
256
|
compile() {
|
|
153
257
|
const query = 'mutation ($metadata: JSON) { execute(metadata: $metadata) }';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aggregation-query-builder.js","sourceRoot":"","sources":["../../src/aggregation-query-builder.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"aggregation-query-builder.js","sourceRoot":"","sources":["../../src/aggregation-query-builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -42,7 +42,7 @@ export declare class BatchQueryBuilder<const TBuilders extends readonly AnyQuery
|
|
|
42
42
|
* ```typescript
|
|
43
43
|
* const unsubscribe = qb.batch([
|
|
44
44
|
* qb.selectFrom('users').select(['id', 'name']),
|
|
45
|
-
* qb.aggregateFrom('users').groupBy('role').
|
|
45
|
+
* qb.aggregateFrom('users').groupBy('role').metrics({ count: count('id') }),
|
|
46
46
|
* ]).subscribe(([users, userAggregates]) => {
|
|
47
47
|
* console.log('Users:', users);
|
|
48
48
|
* console.log('User Aggregates:', userAggregates);
|
|
@@ -36,7 +36,7 @@ export class BatchQueryBuilder {
|
|
|
36
36
|
* ```typescript
|
|
37
37
|
* const unsubscribe = qb.batch([
|
|
38
38
|
* qb.selectFrom('users').select(['id', 'name']),
|
|
39
|
-
* qb.aggregateFrom('users').groupBy('role').
|
|
39
|
+
* qb.aggregateFrom('users').groupBy('role').metrics({ count: count('id') }),
|
|
40
40
|
* ]).subscribe(([users, userAggregates]) => {
|
|
41
41
|
* console.log('Users:', users);
|
|
42
42
|
* console.log('User Aggregates:', userAggregates);
|
package/dist/esm/index.d.ts
CHANGED
package/dist/esm/index.js
CHANGED
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,GAAG,EACH,KAAK,EACL,GAAG,EACH,MAAM,EACN,GAAG,EACH,KAAK,EACL,MAAM,EACN,GAAG,EACH,MAAM,GACP,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC"}
|