remult 0.27.8-next.2 → 0.27.8-next.4
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/esm/remult-knex/index.js +3 -3
- package/esm/remult-mongo.js +29 -34
- package/esm/src/data-api.js +27 -24
- package/esm/src/data-providers/array-entity-data-provider.js +102 -60
- package/esm/src/data-providers/json-data-provider.js +2 -2
- package/esm/src/data-providers/rest-data-provider.js +8 -5
- package/esm/src/data-providers/sql-database.js +36 -42
- package/esm/src/remult-proxy.js +5 -0
- package/esm/src/remult3/RepositoryImplementation.js +31 -9
- package/esm/src/remult3/remult3.js +10 -1
- package/index.d.ts +2 -2
- package/package.json +1 -1
- package/remult-knex/index.js +2 -2
- package/remult-mongo.js +63 -62
- package/src/data-api.d.ts +1 -1
- package/src/data-api.js +68 -44
- package/src/data-interfaces.d.ts +8 -5
- package/src/data-providers/array-entity-data-provider.d.ts +2 -2
- package/src/data-providers/array-entity-data-provider.js +125 -83
- package/src/data-providers/json-data-provider.js +2 -2
- package/src/data-providers/rest-data-provider.d.ts +2 -2
- package/src/data-providers/rest-data-provider.js +13 -10
- package/src/data-providers/sql-database.d.ts +2 -2
- package/src/data-providers/sql-database.js +145 -103
- package/src/remult-proxy.js +11 -0
- package/src/remult3/RepositoryImplementation.d.ts +4 -6
- package/src/remult3/RepositoryImplementation.js +294 -248
- package/src/remult3/remult3.d.ts +72 -18
- package/src/remult3/remult3.js +11 -2
package/esm/remult-knex/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import { Sort } from '../src/sort.js';
|
|
|
7
7
|
import { ValueConverters } from '../src/valueConverters.js';
|
|
8
8
|
import { getRepositoryInternals } from '../src/remult3/repository-internals.js';
|
|
9
9
|
import { remultStatic } from '../src/remult-static.js';
|
|
10
|
-
import {
|
|
10
|
+
import { groupByImpl, getRowAfterUpdate, } from '../src/data-providers/sql-database.js';
|
|
11
11
|
export class KnexDataProvider {
|
|
12
12
|
knex;
|
|
13
13
|
constructor(knex) {
|
|
@@ -117,8 +117,8 @@ class KnexEntityDataProvider {
|
|
|
117
117
|
this.rawSqlWrapIdentifier = rawSqlWrapIdentifier;
|
|
118
118
|
this.dp = dp;
|
|
119
119
|
}
|
|
120
|
-
async
|
|
121
|
-
return
|
|
120
|
+
async groupBy(options) {
|
|
121
|
+
return groupByImpl(options, await this.init(), this.dp.createCommand(), false, (limit, offset) => ' limit ' + limit + ' offset ' + offset);
|
|
122
122
|
}
|
|
123
123
|
async count(where) {
|
|
124
124
|
const e = await this.init();
|
package/esm/remult-mongo.js
CHANGED
|
@@ -5,7 +5,7 @@ import { remult as remultContext } from './src/remult-proxy.js';
|
|
|
5
5
|
import { getRepository } from './src/remult3/RepositoryImplementation.js';
|
|
6
6
|
import { getRepositoryInternals } from './src/remult3/repository-internals.js';
|
|
7
7
|
import { getRowAfterUpdate } from './src/data-providers/sql-database.js';
|
|
8
|
-
import {
|
|
8
|
+
import { GroupByCountMember, GroupByOperators } from './src/remult3/remult3.js';
|
|
9
9
|
export class MongoDataProvider {
|
|
10
10
|
db;
|
|
11
11
|
client;
|
|
@@ -98,7 +98,7 @@ class MongoEntityDataProvider {
|
|
|
98
98
|
let w = await x.resolveWhere();
|
|
99
99
|
return await collection.countDocuments(w, { session: this.session });
|
|
100
100
|
}
|
|
101
|
-
async
|
|
101
|
+
async groupBy(options) {
|
|
102
102
|
let { collection, e } = await this.collection();
|
|
103
103
|
let x = new FilterConsumerBridgeToMongo(e);
|
|
104
104
|
const pipeLine = [];
|
|
@@ -112,12 +112,14 @@ class MongoEntityDataProvider {
|
|
|
112
112
|
__count: { $sum: 1 },
|
|
113
113
|
};
|
|
114
114
|
processResultRow.push((mongoRow, resultRow) => {
|
|
115
|
-
resultRow[
|
|
115
|
+
resultRow[GroupByCountMember] = mongoRow.__count;
|
|
116
116
|
});
|
|
117
117
|
pipeLine.push({ $group });
|
|
118
|
-
|
|
118
|
+
const $addFields = {};
|
|
119
|
+
pipeLine.push({ $addFields });
|
|
120
|
+
if (options?.group) {
|
|
119
121
|
$group._id = {};
|
|
120
|
-
for (const element of options.
|
|
122
|
+
for (const element of options.group) {
|
|
121
123
|
const name = e.$dbNameOf(element);
|
|
122
124
|
$group._id[name] = `$${name}`;
|
|
123
125
|
processResultRow.push((mongoRow, resultRow) => {
|
|
@@ -128,28 +130,23 @@ class MongoEntityDataProvider {
|
|
|
128
130
|
else {
|
|
129
131
|
$group._id = null;
|
|
130
132
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
resultRow[element.key] = {
|
|
149
|
-
...resultRow[element.key],
|
|
150
|
-
avg: element.valueConverter.fromDb(mongoRow[name]),
|
|
151
|
-
};
|
|
152
|
-
});
|
|
133
|
+
for (const operator of GroupByOperators) {
|
|
134
|
+
if (options?.[operator]) {
|
|
135
|
+
for (const element of options[operator]) {
|
|
136
|
+
const name = e.$dbNameOf(element) + '_' + operator;
|
|
137
|
+
if (operator === 'distinctCount') {
|
|
138
|
+
$group[name + '_temp'] = { $addToSet: `$${e.$dbNameOf(element)}` };
|
|
139
|
+
$addFields[name] = { $size: `$${name + '_temp'}` };
|
|
140
|
+
}
|
|
141
|
+
else
|
|
142
|
+
$group[name] = { ['$' + operator]: `$${e.$dbNameOf(element)}` };
|
|
143
|
+
processResultRow.push((mongoRow, resultRow) => {
|
|
144
|
+
resultRow[element.key] = {
|
|
145
|
+
...resultRow[element.key],
|
|
146
|
+
[operator]: mongoRow[name],
|
|
147
|
+
};
|
|
148
|
+
});
|
|
149
|
+
}
|
|
153
150
|
}
|
|
154
151
|
}
|
|
155
152
|
if (options?.orderBy) {
|
|
@@ -157,17 +154,15 @@ class MongoEntityDataProvider {
|
|
|
157
154
|
for (const x of options.orderBy) {
|
|
158
155
|
const direction = x.isDescending ? -1 : 1;
|
|
159
156
|
switch (x.operation) {
|
|
160
|
-
case 'sum':
|
|
161
|
-
sort[e.$dbNameOf(x.field) + '_sum'] = direction;
|
|
162
|
-
break;
|
|
163
|
-
case 'avg':
|
|
164
|
-
sort[e.$dbNameOf(x.field) + '_avg'] = direction;
|
|
165
|
-
break;
|
|
166
157
|
case 'count':
|
|
167
158
|
sort['__count'] = direction;
|
|
168
159
|
break;
|
|
169
|
-
|
|
160
|
+
case undefined:
|
|
170
161
|
sort['_id.' + e.$dbNameOf(x.field)] = direction;
|
|
162
|
+
break;
|
|
163
|
+
default:
|
|
164
|
+
sort[e.$dbNameOf(x.field) + '_' + x.operation] = direction;
|
|
165
|
+
break;
|
|
171
166
|
}
|
|
172
167
|
}
|
|
173
168
|
pipeLine.push({ $sort: sort });
|
package/esm/src/data-api.js
CHANGED
|
@@ -2,7 +2,7 @@ import { doTransaction } from './context.js';
|
|
|
2
2
|
import { findOptionsToJson, liveQueryAction, } from './data-providers/rest-data-provider.js';
|
|
3
3
|
import { Filter, buildFilterFromRequestParameters, customUrlToken, } from './filter/filter-interfaces.js';
|
|
4
4
|
import { getRelationFieldInfo } from './remult3/relationInfoMember.js';
|
|
5
|
-
import {
|
|
5
|
+
import { GroupByCountMember, GroupByForApiKey, } from './remult3/remult3.js';
|
|
6
6
|
import { ForbiddenError } from './server-action.js';
|
|
7
7
|
export class DataApi {
|
|
8
8
|
repository;
|
|
@@ -19,8 +19,8 @@ export class DataApi {
|
|
|
19
19
|
case 'get':
|
|
20
20
|
case 'count':
|
|
21
21
|
return this.count(res, req, undefined);
|
|
22
|
-
case '
|
|
23
|
-
return this.
|
|
22
|
+
case 'groupBy':
|
|
23
|
+
return this.groupBy(res, req, undefined);
|
|
24
24
|
}
|
|
25
25
|
return this.getArray(res, req, undefined);
|
|
26
26
|
}
|
|
@@ -45,8 +45,8 @@ export class DataApi {
|
|
|
45
45
|
case 'count':
|
|
46
46
|
validateWhereInBody();
|
|
47
47
|
return this.count(res, req, body);
|
|
48
|
-
case '
|
|
49
|
-
return this.
|
|
48
|
+
case 'groupBy':
|
|
49
|
+
return this.groupBy(res, req, body);
|
|
50
50
|
case 'deleteMany':
|
|
51
51
|
validateWhereInBody();
|
|
52
52
|
return this.deleteMany(res, req, body);
|
|
@@ -103,46 +103,49 @@ export class DataApi {
|
|
|
103
103
|
response.error(err, this.repository.metadata);
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
|
-
async
|
|
106
|
+
async groupBy(response, request, body) {
|
|
107
107
|
let findOptions = await this.findOptionsFromRequest(request, body);
|
|
108
108
|
let orderBy = {};
|
|
109
109
|
if (body?.orderBy) {
|
|
110
110
|
for (const element of body?.orderBy) {
|
|
111
111
|
const direction = element.isDescending ? 'desc' : 'asc';
|
|
112
112
|
switch (element.operation) {
|
|
113
|
-
case
|
|
114
|
-
orderBy[element.field] =
|
|
115
|
-
...orderBy[element.field],
|
|
116
|
-
sum: direction,
|
|
117
|
-
};
|
|
118
|
-
break;
|
|
119
|
-
case 'avg':
|
|
120
|
-
orderBy[element.field] = {
|
|
121
|
-
...orderBy[element.field],
|
|
122
|
-
avg: direction,
|
|
123
|
-
};
|
|
113
|
+
case undefined:
|
|
114
|
+
orderBy[element.field] = direction;
|
|
124
115
|
break;
|
|
125
116
|
case 'count':
|
|
126
|
-
orderBy[
|
|
117
|
+
orderBy[GroupByCountMember] = direction;
|
|
127
118
|
break;
|
|
128
119
|
default:
|
|
129
|
-
orderBy[element.field] =
|
|
120
|
+
orderBy[element.field] = {
|
|
121
|
+
...orderBy[element.field],
|
|
122
|
+
[element.operation]: direction,
|
|
123
|
+
};
|
|
130
124
|
break;
|
|
131
125
|
}
|
|
132
126
|
}
|
|
133
127
|
}
|
|
134
|
-
|
|
128
|
+
const group = body?.groupBy?.filter((x) => this.repository.fields.find(x).includedInApi());
|
|
129
|
+
let result = await this.repository.groupBy({
|
|
135
130
|
where: findOptions.where,
|
|
136
131
|
limit: findOptions.limit,
|
|
137
132
|
page: findOptions.page,
|
|
138
|
-
|
|
133
|
+
//@ts-expect-error internal key
|
|
134
|
+
[GroupByForApiKey]: true,
|
|
135
|
+
group,
|
|
139
136
|
sum: body?.sum?.filter((x) => this.repository.fields.find(x).includedInApi()),
|
|
140
137
|
avg: body?.avg?.filter((x) => this.repository.fields.find(x).includedInApi()),
|
|
138
|
+
min: body?.min?.filter((x) => this.repository.fields.find(x).includedInApi()),
|
|
139
|
+
max: body?.max?.filter((x) => this.repository.fields.find(x).includedInApi()),
|
|
140
|
+
distinctCount: body?.distinctCount?.filter((x) => this.repository.fields.find(x).includedInApi()),
|
|
141
141
|
orderBy: orderBy,
|
|
142
142
|
});
|
|
143
|
-
if (
|
|
144
|
-
result
|
|
145
|
-
|
|
143
|
+
if (group)
|
|
144
|
+
result.forEach((x) => {
|
|
145
|
+
for (const f of group) {
|
|
146
|
+
x[f] = this.repository.fields.find(f).valueConverter.toJson(x[f]);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
146
149
|
response.success(result);
|
|
147
150
|
}
|
|
148
151
|
async getArrayImpl(response, request, body) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CompoundIdField } from '../CompoundIdField.js';
|
|
2
2
|
import { dbNamesOf, isDbReadonly, } from '../filter/filter-consumer-bridge-to-sql-request.js';
|
|
3
3
|
import { Filter, customDatabaseFilterToken, } from '../filter/filter-interfaces.js';
|
|
4
|
-
import {
|
|
4
|
+
import { GroupByCountMember, GroupByOperators, } from '../remult3/remult3.js';
|
|
5
5
|
import { compareForSort, Sort } from '../sort.js';
|
|
6
6
|
export class ArrayEntityDataProvider {
|
|
7
7
|
entity;
|
|
@@ -17,10 +17,10 @@ export class ArrayEntityDataProvider {
|
|
|
17
17
|
this.entity = entity;
|
|
18
18
|
this.rows = rows;
|
|
19
19
|
}
|
|
20
|
-
async
|
|
20
|
+
async groupBy(options) {
|
|
21
21
|
const sort = new Sort();
|
|
22
|
-
if (options?.
|
|
23
|
-
for (const field of options?.
|
|
22
|
+
if (options?.group)
|
|
23
|
+
for (const field of options?.group) {
|
|
24
24
|
sort.Segments.push({ field: field });
|
|
25
25
|
}
|
|
26
26
|
const rows = await this.find({ orderBy: sort, where: options?.where });
|
|
@@ -29,69 +29,110 @@ export class ArrayEntityDataProvider {
|
|
|
29
29
|
let first = true;
|
|
30
30
|
let count = 0;
|
|
31
31
|
let aggregates = [];
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
32
|
+
const operatorImpl = {
|
|
33
|
+
sum: (key) => {
|
|
34
|
+
let sum = 0;
|
|
35
|
+
return {
|
|
36
|
+
process(row) {
|
|
37
|
+
const val = row[key];
|
|
38
|
+
if (val !== undefined && val !== null)
|
|
39
|
+
sum += row[key];
|
|
40
|
+
},
|
|
41
|
+
finishGroup(result) {
|
|
42
|
+
result[key] = { ...result[key], sum };
|
|
43
|
+
sum = 0;
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
},
|
|
47
|
+
avg: (key) => {
|
|
48
|
+
let sum = 0;
|
|
49
|
+
let count = 0;
|
|
50
|
+
return {
|
|
51
|
+
process(row) {
|
|
52
|
+
const val = row[key];
|
|
53
|
+
if (val !== undefined && val !== null) {
|
|
54
|
+
sum += row[key];
|
|
55
|
+
count++;
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
finishGroup(result) {
|
|
59
|
+
result[key] = {
|
|
60
|
+
...result[key],
|
|
61
|
+
avg: sum / count,
|
|
62
|
+
};
|
|
63
|
+
sum = 0;
|
|
64
|
+
count = 0;
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
min: (key) => {
|
|
69
|
+
let min = undefined;
|
|
70
|
+
return {
|
|
71
|
+
process(row) {
|
|
72
|
+
const val = row[key];
|
|
73
|
+
if (val !== undefined && val !== null) {
|
|
74
|
+
if (min === undefined || val < min)
|
|
75
|
+
min = val;
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
finishGroup(result) {
|
|
79
|
+
result[key] = { ...result[key], min };
|
|
80
|
+
min = undefined;
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
},
|
|
84
|
+
max: (key) => {
|
|
85
|
+
let max = undefined;
|
|
86
|
+
return {
|
|
87
|
+
process(row) {
|
|
88
|
+
const val = row[key];
|
|
89
|
+
if (val !== undefined && val !== null) {
|
|
90
|
+
if (max === undefined || val > max)
|
|
91
|
+
max = val;
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
finishGroup(result) {
|
|
95
|
+
result[key] = { ...result[key], max };
|
|
96
|
+
max = undefined;
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
},
|
|
100
|
+
distinctCount: (key) => {
|
|
101
|
+
let distinct = new Set();
|
|
102
|
+
return {
|
|
103
|
+
process(row) {
|
|
104
|
+
const val = row[key];
|
|
105
|
+
if (val !== undefined && val !== null)
|
|
106
|
+
distinct.add(val);
|
|
107
|
+
},
|
|
108
|
+
finishGroup(result) {
|
|
109
|
+
result[key] = { ...result[key], distinctCount: distinct.size };
|
|
110
|
+
distinct.clear();
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
for (let operator of GroupByOperators) {
|
|
116
|
+
if (options?.[operator]) {
|
|
117
|
+
for (const element of options[operator]) {
|
|
118
|
+
aggregates.push(operatorImpl[operator](element.key));
|
|
75
119
|
}
|
|
76
120
|
}
|
|
77
|
-
for (const element of options.avg) {
|
|
78
|
-
aggregates.push(new Avg(element.key));
|
|
79
|
-
}
|
|
80
121
|
}
|
|
81
122
|
function finishGroup() {
|
|
82
123
|
const r = { ...group, $count: count };
|
|
83
124
|
for (const a of aggregates) {
|
|
84
125
|
a.finishGroup(r);
|
|
85
126
|
}
|
|
86
|
-
r[
|
|
127
|
+
r[GroupByCountMember] = count;
|
|
87
128
|
result.push(r);
|
|
88
129
|
first = true;
|
|
89
130
|
count = 0;
|
|
90
131
|
}
|
|
91
132
|
for (const row of rows) {
|
|
92
|
-
if (options?.
|
|
133
|
+
if (options?.group) {
|
|
93
134
|
if (!first) {
|
|
94
|
-
for (const field of options?.
|
|
135
|
+
for (const field of options?.group) {
|
|
95
136
|
if (group[field.key] != row[field.key]) {
|
|
96
137
|
finishGroup();
|
|
97
138
|
break;
|
|
@@ -99,7 +140,7 @@ export class ArrayEntityDataProvider {
|
|
|
99
140
|
}
|
|
100
141
|
}
|
|
101
142
|
if (first) {
|
|
102
|
-
for (const field of options?.
|
|
143
|
+
for (const field of options?.group) {
|
|
103
144
|
group[field.key] = row[field.key];
|
|
104
145
|
}
|
|
105
146
|
}
|
|
@@ -116,17 +157,18 @@ export class ArrayEntityDataProvider {
|
|
|
116
157
|
for (const x of options.orderBy) {
|
|
117
158
|
const getValue = (row) => {
|
|
118
159
|
if (!x.field && x.operation == 'count') {
|
|
119
|
-
return row[
|
|
160
|
+
return row[GroupByCountMember];
|
|
120
161
|
}
|
|
121
162
|
else {
|
|
122
163
|
switch (x.operation) {
|
|
123
|
-
case '
|
|
124
|
-
return row[
|
|
125
|
-
case
|
|
126
|
-
return row[x.field.key]
|
|
164
|
+
case 'count':
|
|
165
|
+
return row[GroupByCountMember];
|
|
166
|
+
case undefined:
|
|
167
|
+
return row[x.field.key];
|
|
168
|
+
default:
|
|
169
|
+
return row[x.field.key][x.operation];
|
|
127
170
|
}
|
|
128
171
|
}
|
|
129
|
-
return row[x.field.key];
|
|
130
172
|
};
|
|
131
173
|
let compare = compareForSort(getValue(a), getValue(b), x.isDescending);
|
|
132
174
|
if (compare != 0)
|
|
@@ -22,8 +22,8 @@ class JsonEntityDataProvider {
|
|
|
22
22
|
this.helper = helper;
|
|
23
23
|
this.formatted = formatted;
|
|
24
24
|
}
|
|
25
|
-
|
|
26
|
-
return (this.p = this.p.then(() => this.loadEntityData((dp, save) => dp.
|
|
25
|
+
groupBy(options) {
|
|
26
|
+
return (this.p = this.p.then(() => this.loadEntityData((dp, save) => dp.groupBy(options))));
|
|
27
27
|
}
|
|
28
28
|
async loadEntityData(what) {
|
|
29
29
|
let data = [];
|
|
@@ -101,22 +101,25 @@ export class RestEntityDataProvider {
|
|
|
101
101
|
this.http = http;
|
|
102
102
|
this.entity = entity;
|
|
103
103
|
}
|
|
104
|
-
async
|
|
104
|
+
async groupBy(options) {
|
|
105
105
|
const { run } = this.buildFindRequest({
|
|
106
106
|
where: options?.where,
|
|
107
107
|
limit: options?.limit,
|
|
108
108
|
page: options?.page,
|
|
109
109
|
});
|
|
110
110
|
const body = {
|
|
111
|
-
groupBy: options?.
|
|
111
|
+
groupBy: options?.group?.map((x) => x.key),
|
|
112
112
|
sum: options?.sum?.map((x) => x.key),
|
|
113
113
|
avg: options?.avg?.map((x) => x.key),
|
|
114
|
+
min: options?.min?.map((x) => x.key),
|
|
115
|
+
max: options?.max?.map((x) => x.key),
|
|
116
|
+
distinctCount: options?.distinctCount?.map((x) => x.key),
|
|
114
117
|
orderBy: options?.orderBy?.map((x) => ({ ...x, field: x.field?.key })),
|
|
115
118
|
};
|
|
116
|
-
const result = await run('
|
|
117
|
-
if (options?.
|
|
119
|
+
const result = await run('groupBy', Object.keys(body).length > 0 ? body : undefined);
|
|
120
|
+
if (options?.group)
|
|
118
121
|
result.forEach((row) => {
|
|
119
|
-
for (const g of options.
|
|
122
|
+
for (const g of options.group) {
|
|
120
123
|
row[g.key] = g.valueConverter.fromJson(row[g.key]);
|
|
121
124
|
}
|
|
122
125
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { FilterConsumerBridgeToSqlRequest, dbNamesOfWithForceSqlExpression, isDbReadonly, } from '../filter/filter-consumer-bridge-to-sql-request.js';
|
|
2
2
|
import { Filter, customDatabaseFilterToken, } from '../filter/filter-interfaces.js';
|
|
3
3
|
import { remult as defaultRemult } from '../remult-proxy.js';
|
|
4
|
-
import {
|
|
4
|
+
import { GroupByCountMember, GroupByOperators, } from '../remult3/remult3.js';
|
|
5
5
|
import { getRepository, isAutoIncrement, } from '../remult3/RepositoryImplementation.js';
|
|
6
6
|
import { Sort } from '../sort.js';
|
|
7
7
|
import { ValueConverters } from '../valueConverters.js';
|
|
@@ -287,8 +287,8 @@ class ActualSQLEntityDataProvider {
|
|
|
287
287
|
return Number(r.rows[0].count);
|
|
288
288
|
});
|
|
289
289
|
}
|
|
290
|
-
async
|
|
291
|
-
return await
|
|
290
|
+
async groupBy(options) {
|
|
291
|
+
return await groupByImpl(options, await this.init(), this.sql.createCommand(), this.sql._getSourceSql().orderByNullsFirst, this.sql._getSourceSql().getLimitSqlSyntax);
|
|
292
292
|
}
|
|
293
293
|
async find(options) {
|
|
294
294
|
let e = await this.init();
|
|
@@ -541,15 +541,15 @@ export function getRowAfterUpdate(meta, dataProvider, data, id, operation) {
|
|
|
541
541
|
return r[0];
|
|
542
542
|
});
|
|
543
543
|
}
|
|
544
|
-
export async function
|
|
544
|
+
export async function groupByImpl(options, e, r, orderByNullFirst, limitSyntax) {
|
|
545
545
|
let select = 'select count(*) as count';
|
|
546
546
|
let groupBy = '';
|
|
547
547
|
const processResultRow = [];
|
|
548
548
|
processResultRow.push((sqlVal, theResult) => {
|
|
549
|
-
theResult[
|
|
549
|
+
theResult[GroupByCountMember] = Number(sqlVal);
|
|
550
550
|
});
|
|
551
|
-
if (options?.
|
|
552
|
-
for (const x of options?.
|
|
551
|
+
if (options?.group)
|
|
552
|
+
for (const x of options?.group) {
|
|
553
553
|
if (x.isServerExpression) {
|
|
554
554
|
}
|
|
555
555
|
else {
|
|
@@ -566,28 +566,21 @@ export async function aggregateImpl(options, e, r, orderByNullFirst, limitSyntax
|
|
|
566
566
|
theResult[x.key] = x.valueConverter.fromDb(sqlResult);
|
|
567
567
|
});
|
|
568
568
|
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
if (x.isServerExpression) {
|
|
583
|
-
}
|
|
584
|
-
else {
|
|
585
|
-
select += ', avg(' + e.$dbNameOf(x) + ') as ' + x.key + '_avg';
|
|
569
|
+
for (const operator of GroupByOperators) {
|
|
570
|
+
const fields = options?.[operator];
|
|
571
|
+
if (fields)
|
|
572
|
+
for (const x of fields) {
|
|
573
|
+
if (x.isServerExpression) {
|
|
574
|
+
}
|
|
575
|
+
else {
|
|
576
|
+
const dbName = await e.$dbNameOf(x);
|
|
577
|
+
select += `, ${aggregateSqlSyntax(operator, dbName)} as ${x.key}_${operator}`;
|
|
578
|
+
}
|
|
579
|
+
processResultRow.push((sqlResult, theResult) => {
|
|
580
|
+
theResult[x.key] = { ...theResult[x.key], [operator]: sqlResult };
|
|
581
|
+
});
|
|
586
582
|
}
|
|
587
|
-
|
|
588
|
-
theResult[x.key] = { ...theResult[x.key], avg: sqlResult };
|
|
589
|
-
});
|
|
590
|
-
}
|
|
583
|
+
}
|
|
591
584
|
select += '\n from ' + e.$entityName;
|
|
592
585
|
if (options?.where) {
|
|
593
586
|
let where = new FilterConsumerBridgeToSqlRequest(r, e);
|
|
@@ -603,21 +596,17 @@ export async function aggregateImpl(options, e, r, orderByNullFirst, limitSyntax
|
|
|
603
596
|
orderBy = ' order by ';
|
|
604
597
|
else
|
|
605
598
|
orderBy += ', ';
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
case 'avg':
|
|
616
|
-
field = 'avg(' + field + ')';
|
|
617
|
-
break;
|
|
618
|
-
}
|
|
619
|
-
orderBy += field;
|
|
599
|
+
let field = x.field && (await e.$dbNameOf(x.field));
|
|
600
|
+
switch (x.operation) {
|
|
601
|
+
case 'count':
|
|
602
|
+
field = x.operation + '(*)';
|
|
603
|
+
break;
|
|
604
|
+
case undefined:
|
|
605
|
+
break;
|
|
606
|
+
default:
|
|
607
|
+
field = aggregateSqlSyntax(x.operation, field);
|
|
620
608
|
}
|
|
609
|
+
orderBy += field;
|
|
621
610
|
if (x.isDescending)
|
|
622
611
|
orderBy += ' desc';
|
|
623
612
|
if (orderByNullFirst) {
|
|
@@ -644,4 +633,9 @@ export async function aggregateImpl(options, e, r, orderByNullFirst, limitSyntax
|
|
|
644
633
|
processResultRow.forEach((x, i) => x(sql[result.getColumnKeyInResultForIndexInSelect(i)], theResult));
|
|
645
634
|
return theResult;
|
|
646
635
|
});
|
|
636
|
+
function aggregateSqlSyntax(operator, dbName) {
|
|
637
|
+
return operator === 'distinctCount'
|
|
638
|
+
? `count (distinct ${dbName})`
|
|
639
|
+
: `${operator}( ${dbName} )`;
|
|
640
|
+
}
|
|
647
641
|
}
|
package/esm/src/remult-proxy.js
CHANGED
|
@@ -112,6 +112,11 @@ export class RemultProxy {
|
|
|
112
112
|
.remultFactory()
|
|
113
113
|
.repo(...args)
|
|
114
114
|
.find(...args2),
|
|
115
|
+
groupBy: (...args2) => self
|
|
116
|
+
.remultFactory()
|
|
117
|
+
.repo(...args)
|
|
118
|
+
//@ts-ignore
|
|
119
|
+
.groupBy(...args2),
|
|
115
120
|
aggregate: (...args2) => self
|
|
116
121
|
.remultFactory()
|
|
117
122
|
.repo(...args)
|