remult 0.27.19 → 0.27.20-next.1
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/index.js +1 -0
- package/esm/server/remult-api-server.js +1 -1
- package/esm/src/data-api.js +34 -7
- package/esm/src/data-providers/json-data-provider.js +4 -2
- package/esm/src/data-providers/json-entity-indexed-db-data-provider.js +55 -0
- package/esm/src/data-providers/rest-data-provider.js +18 -9
- package/esm/src/remult-proxy.js +2 -2
- package/esm/src/remult3/QueryResultImpl.js +148 -0
- package/esm/src/remult3/RepositoryImplementation.js +26 -131
- package/esm/src/remult3/remult3.js +4 -22
- package/index.d.ts +1 -0
- package/index.js +3 -1
- package/package.json +1 -1
- package/server/remult-api-server.js +6 -3
- package/src/data-api.d.ts +7 -3
- package/src/data-api.js +74 -35
- package/src/data-interfaces.d.ts +4 -0
- package/src/data-providers/json-data-provider.d.ts +3 -2
- package/src/data-providers/json-data-provider.js +4 -2
- package/src/data-providers/json-entity-indexed-db-data-provider.d.ts +9 -0
- package/src/data-providers/json-entity-indexed-db-data-provider.js +97 -0
- package/src/data-providers/rest-data-provider.d.ts +5 -0
- package/src/data-providers/rest-data-provider.js +22 -13
- package/src/remult-proxy.js +4 -9
- package/src/remult3/QueryResultImpl.d.ts +16 -0
- package/src/remult3/QueryResultImpl.js +269 -0
- package/src/remult3/RepositoryImplementation.d.ts +7 -6
- package/src/remult3/RepositoryImplementation.js +68 -251
- package/src/remult3/remult3.d.ts +62 -23
- package/src/remult3/remult3.js +4 -22
package/esm/index.js
CHANGED
|
@@ -11,6 +11,7 @@ export { SqlDatabase } from './src/data-providers/sql-database.js'; //V
|
|
|
11
11
|
export { CustomSqlFilterBuilder, dbNamesOf, } from './src/filter/filter-consumer-bridge-to-sql-request.js';
|
|
12
12
|
export { JsonDataProvider, } from './src/data-providers/json-data-provider.js'; //V
|
|
13
13
|
export { JsonEntityOpfsStorage } from './src/data-providers/json-entity-opfs-storage.js';
|
|
14
|
+
export { JsonEntityIndexedDbStorage } from './src/data-providers/json-entity-indexed-db-data-provider.js';
|
|
14
15
|
//export * from './src/data-api'; //reconsider if to make internal
|
|
15
16
|
export { Controller, BackendMethod, ProgressListener, ForbiddenError, } from './src/server-action.js';
|
|
16
17
|
export { Allow, Remult, withRemult, isBackend, EventSource, } from './src/context.js';
|
|
@@ -258,7 +258,7 @@ export class RemultServerImplementation {
|
|
|
258
258
|
if (this.options.logApiEndPoints)
|
|
259
259
|
console.info('[remult] ' + myRoute);
|
|
260
260
|
r.route(myRoute)
|
|
261
|
-
.get(this.process((c, req, res, orig) => dataApiFactory(c).httpGet(res, req, () => this.serializeContext(c))))
|
|
261
|
+
.get(this.process(async (c, req, res, orig) => dataApiFactory(c).httpGet(res, req, () => this.serializeContext(c))))
|
|
262
262
|
.put(this.process(async (c, req, res, _, __, orig) => dataApiFactory(c).updateManyThroughPutRequest(res, req, await this.coreOptions.getRequestBody(orig))))
|
|
263
263
|
.delete(this.process(async (c, req, res, _, __, orig) => dataApiFactory(c).deleteMany(res, req, undefined)))
|
|
264
264
|
.post(this.process(async (c, req, res, _, __, orig) => dataApiFactory(c).httpPost(res, req, await this.coreOptions.getRequestBody(orig), () => this.serializeContext(c))));
|
package/esm/src/data-api.js
CHANGED
|
@@ -20,7 +20,7 @@ export class DataApi {
|
|
|
20
20
|
case 'count':
|
|
21
21
|
return this.count(res, req, undefined);
|
|
22
22
|
case 'groupBy':
|
|
23
|
-
return this.groupBy(
|
|
23
|
+
return res.success(this.groupBy(req, undefined));
|
|
24
24
|
}
|
|
25
25
|
return this.getArray(res, req, undefined);
|
|
26
26
|
}
|
|
@@ -46,7 +46,7 @@ export class DataApi {
|
|
|
46
46
|
validateWhereInBody();
|
|
47
47
|
return this.count(res, req, body);
|
|
48
48
|
case 'groupBy':
|
|
49
|
-
return this.groupBy(
|
|
49
|
+
return res.success(this.groupBy(req, body));
|
|
50
50
|
case 'deleteMany':
|
|
51
51
|
validateWhereInBody();
|
|
52
52
|
return this.deleteMany(res, req, body);
|
|
@@ -57,10 +57,35 @@ export class DataApi {
|
|
|
57
57
|
await this.remult.liveQueryStorage.remove(body.id);
|
|
58
58
|
res.success('ok');
|
|
59
59
|
return;
|
|
60
|
+
case 'query':
|
|
61
|
+
return res.success(this.query(res, req, body));
|
|
60
62
|
default:
|
|
61
63
|
return this.post(res, body);
|
|
62
64
|
}
|
|
63
65
|
}
|
|
66
|
+
async query(response, request, body) {
|
|
67
|
+
if (!this.repository.metadata.apiReadAllowed) {
|
|
68
|
+
response.forbidden();
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
let { aggregate, ...rest } = body;
|
|
73
|
+
let [{ r }, [aggregates]] = await Promise.all([
|
|
74
|
+
this.getArrayImpl(request, rest),
|
|
75
|
+
this.groupBy(request, aggregate),
|
|
76
|
+
]);
|
|
77
|
+
return {
|
|
78
|
+
items: r,
|
|
79
|
+
aggregates,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
if (err.isForbiddenError)
|
|
84
|
+
response.forbidden();
|
|
85
|
+
else
|
|
86
|
+
response.error(err, this.repository.metadata);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
64
89
|
static defaultGetLimit = 0;
|
|
65
90
|
async get(response, id) {
|
|
66
91
|
if (!this.repository.metadata.apiReadAllowed) {
|
|
@@ -92,6 +117,7 @@ export class DataApi {
|
|
|
92
117
|
for await (const x of this.repository.query({
|
|
93
118
|
where,
|
|
94
119
|
include: this.includeNone(),
|
|
120
|
+
aggregate: undefined,
|
|
95
121
|
})) {
|
|
96
122
|
await this.actualDelete(x);
|
|
97
123
|
deleted++;
|
|
@@ -103,7 +129,7 @@ export class DataApi {
|
|
|
103
129
|
response.error(err, this.repository.metadata);
|
|
104
130
|
}
|
|
105
131
|
}
|
|
106
|
-
async groupBy(
|
|
132
|
+
async groupBy(request, body) {
|
|
107
133
|
let findOptions = await this.findOptionsFromRequest(request, body);
|
|
108
134
|
let orderBy = {};
|
|
109
135
|
if (body?.orderBy) {
|
|
@@ -146,9 +172,9 @@ export class DataApi {
|
|
|
146
172
|
x[f] = this.repository.fields.find(f).valueConverter.toJson(x[f]);
|
|
147
173
|
}
|
|
148
174
|
});
|
|
149
|
-
|
|
175
|
+
return result;
|
|
150
176
|
}
|
|
151
|
-
async getArrayImpl(
|
|
177
|
+
async getArrayImpl(request, body) {
|
|
152
178
|
let findOptions = await this.findOptionsFromRequest(request, body);
|
|
153
179
|
const r = await this.repository.find(findOptions).then(async (r) => {
|
|
154
180
|
return await Promise.all(r.map(async (y) => this.repository.getEntityRef(y).toApiJson()));
|
|
@@ -224,7 +250,7 @@ export class DataApi {
|
|
|
224
250
|
return;
|
|
225
251
|
}
|
|
226
252
|
try {
|
|
227
|
-
const { r } = await this.getArrayImpl(
|
|
253
|
+
const { r } = await this.getArrayImpl(request, body);
|
|
228
254
|
response.success(r);
|
|
229
255
|
}
|
|
230
256
|
catch (err) {
|
|
@@ -240,7 +266,7 @@ export class DataApi {
|
|
|
240
266
|
return;
|
|
241
267
|
}
|
|
242
268
|
try {
|
|
243
|
-
const r = await this.getArrayImpl(
|
|
269
|
+
const r = await this.getArrayImpl(request, body);
|
|
244
270
|
const data = {
|
|
245
271
|
requestJson: await serializeContext(),
|
|
246
272
|
findOptionsJson: findOptionsToJson(r.findOptions, this.repository.metadata),
|
|
@@ -345,6 +371,7 @@ export class DataApi {
|
|
|
345
371
|
for await (const x of this.repository.query({
|
|
346
372
|
where,
|
|
347
373
|
include: this.includeNone(),
|
|
374
|
+
aggregate: undefined,
|
|
348
375
|
})) {
|
|
349
376
|
await this.actualUpdate(x, body.set);
|
|
350
377
|
updated++;
|
|
@@ -30,9 +30,11 @@ class JsonEntityDataProvider {
|
|
|
30
30
|
let dbName = await this.entity.dbName;
|
|
31
31
|
let s = await this.helper.getItem(dbName);
|
|
32
32
|
if (s)
|
|
33
|
-
data = JSON.parse(s);
|
|
33
|
+
data = this.helper.supportsRawJson ? s : JSON.parse(s);
|
|
34
34
|
let dp = new ArrayEntityDataProvider(this.entity, () => data);
|
|
35
|
-
return what(dp, async () => await this.helper.setItem(dbName,
|
|
35
|
+
return what(dp, async () => await this.helper.setItem(dbName, this.helper.supportsRawJson
|
|
36
|
+
? data
|
|
37
|
+
: JSON.stringify(data, undefined, this.formatted ? 2 : undefined)));
|
|
36
38
|
}
|
|
37
39
|
p = Promise.resolve();
|
|
38
40
|
find(options) {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export class JsonEntityIndexedDbStorage {
|
|
2
|
+
dbName;
|
|
3
|
+
storeName;
|
|
4
|
+
constructor(dbName = 'db', storeName = 'jsonStore') {
|
|
5
|
+
this.dbName = dbName;
|
|
6
|
+
this.storeName = storeName;
|
|
7
|
+
}
|
|
8
|
+
supportsRawJson = true;
|
|
9
|
+
//@internal
|
|
10
|
+
db;
|
|
11
|
+
async getItem(entityDbName) {
|
|
12
|
+
return new Promise(async (resolve, reject) => {
|
|
13
|
+
const transaction = (await this.init()).transaction([this.storeName], 'readonly');
|
|
14
|
+
const store = transaction.objectStore(this.storeName);
|
|
15
|
+
const request = store.get(entityDbName);
|
|
16
|
+
request.onerror = (event) => reject(request.error);
|
|
17
|
+
request.onsuccess = (event) => {
|
|
18
|
+
if (request.result) {
|
|
19
|
+
resolve(request.result);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
resolve(null);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
//@internal
|
|
28
|
+
async init() {
|
|
29
|
+
if (!this.db) {
|
|
30
|
+
this.db = await new Promise((resolve, reject) => {
|
|
31
|
+
let db;
|
|
32
|
+
const request = indexedDB.open(this.dbName, 1);
|
|
33
|
+
request.onerror = (event) => reject(request.error);
|
|
34
|
+
request.onsuccess = (event) => {
|
|
35
|
+
db = request.result;
|
|
36
|
+
resolve(db);
|
|
37
|
+
};
|
|
38
|
+
request.onupgradeneeded = (event) => {
|
|
39
|
+
db = request.result;
|
|
40
|
+
db.createObjectStore(this.storeName);
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return this.db;
|
|
45
|
+
}
|
|
46
|
+
async setItem(entityDbName, json) {
|
|
47
|
+
return new Promise(async (resolve, reject) => {
|
|
48
|
+
const transaction = (await this.init()).transaction([this.storeName], 'readwrite');
|
|
49
|
+
const store = transaction.objectStore(this.storeName);
|
|
50
|
+
const request = store.put(json, entityDbName);
|
|
51
|
+
request.onerror = (event) => reject(request.error);
|
|
52
|
+
request.onsuccess = (event) => resolve();
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -103,21 +103,19 @@ export class RestEntityDataProvider {
|
|
|
103
103
|
this.http = http;
|
|
104
104
|
this.entity = entity;
|
|
105
105
|
}
|
|
106
|
+
query(options, aggregateOptions) {
|
|
107
|
+
const r = this.buildFindRequest(options);
|
|
108
|
+
return r.run('query', {
|
|
109
|
+
aggregate: this.buildAggregateOptions(aggregateOptions),
|
|
110
|
+
});
|
|
111
|
+
}
|
|
106
112
|
async groupBy(options) {
|
|
107
113
|
const { run } = this.buildFindRequest({
|
|
108
114
|
where: options?.where,
|
|
109
115
|
limit: options?.limit,
|
|
110
116
|
page: options?.page,
|
|
111
117
|
});
|
|
112
|
-
const body =
|
|
113
|
-
groupBy: options?.group?.map((x) => x.key),
|
|
114
|
-
sum: options?.sum?.map((x) => x.key),
|
|
115
|
-
avg: options?.avg?.map((x) => x.key),
|
|
116
|
-
min: options?.min?.map((x) => x.key),
|
|
117
|
-
max: options?.max?.map((x) => x.key),
|
|
118
|
-
distinctCount: options?.distinctCount?.map((x) => x.key),
|
|
119
|
-
orderBy: options?.orderBy?.map((x) => ({ ...x, field: x.field?.key })),
|
|
120
|
-
};
|
|
118
|
+
const body = this.buildAggregateOptions(options);
|
|
121
119
|
const result = await run('groupBy', Object.keys(body).length > 0 ? body : undefined);
|
|
122
120
|
if (options?.group)
|
|
123
121
|
result.forEach((row) => {
|
|
@@ -127,6 +125,17 @@ export class RestEntityDataProvider {
|
|
|
127
125
|
});
|
|
128
126
|
return result;
|
|
129
127
|
}
|
|
128
|
+
buildAggregateOptions(options) {
|
|
129
|
+
return {
|
|
130
|
+
groupBy: options?.group?.map((x) => x.key),
|
|
131
|
+
sum: options?.sum?.map((x) => x.key),
|
|
132
|
+
avg: options?.avg?.map((x) => x.key),
|
|
133
|
+
min: options?.min?.map((x) => x.key),
|
|
134
|
+
max: options?.max?.map((x) => x.key),
|
|
135
|
+
distinctCount: options?.distinctCount?.map((x) => x.key),
|
|
136
|
+
orderBy: options?.orderBy?.map((x) => ({ ...x, field: x.field?.key })),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
130
139
|
translateFromJson(row) {
|
|
131
140
|
let result = {};
|
|
132
141
|
for (const col of this.entity.fields) {
|
package/esm/src/remult-proxy.js
CHANGED
|
@@ -161,10 +161,10 @@ export class RemultProxy {
|
|
|
161
161
|
get metadata() {
|
|
162
162
|
return remultStatic.remultFactory().repo(...args).metadata;
|
|
163
163
|
},
|
|
164
|
-
query: (
|
|
164
|
+
query: (options) => self
|
|
165
165
|
.remultFactory()
|
|
166
166
|
.repo(...args)
|
|
167
|
-
.query(
|
|
167
|
+
.query(options),
|
|
168
168
|
save: (args2) => self
|
|
169
169
|
.remultFactory()
|
|
170
170
|
.repo(...args)
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { queryConfig } from '../context.js';
|
|
2
|
+
import { Sort } from '../sort.js';
|
|
3
|
+
import { RelationLoader } from './relation-loader.js';
|
|
4
|
+
export class QueryResultImpl {
|
|
5
|
+
options;
|
|
6
|
+
repo;
|
|
7
|
+
constructor(options, repo) {
|
|
8
|
+
this.options = options;
|
|
9
|
+
this.repo = repo;
|
|
10
|
+
if (!this.options)
|
|
11
|
+
this.options = {};
|
|
12
|
+
if (!this.options.pageSize) {
|
|
13
|
+
this.options.pageSize = queryConfig.defaultPageSize;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
_count = undefined;
|
|
17
|
+
_aggregates;
|
|
18
|
+
async getPage(page) {
|
|
19
|
+
if ((page ?? 0) < 1)
|
|
20
|
+
page = 1;
|
|
21
|
+
return this.repo.find({
|
|
22
|
+
where: this.options.where,
|
|
23
|
+
orderBy: this.options.orderBy,
|
|
24
|
+
limit: this.options.pageSize,
|
|
25
|
+
page: page,
|
|
26
|
+
load: this.options.load,
|
|
27
|
+
include: this.options.include,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
async count() {
|
|
31
|
+
if (this._count === undefined)
|
|
32
|
+
this._count = await this.repo.count(this.options.where);
|
|
33
|
+
return this._count;
|
|
34
|
+
}
|
|
35
|
+
async forEach(what) {
|
|
36
|
+
let i = 0;
|
|
37
|
+
for await (const x of this) {
|
|
38
|
+
await what(x);
|
|
39
|
+
i++;
|
|
40
|
+
}
|
|
41
|
+
return i;
|
|
42
|
+
}
|
|
43
|
+
async paginator(pNextPageFilter) {
|
|
44
|
+
this.options.orderBy = Sort.createUniqueEntityOrderBy(this.repo.metadata, this.options.orderBy);
|
|
45
|
+
let options = {
|
|
46
|
+
where: {
|
|
47
|
+
$and: [this.options.where, pNextPageFilter],
|
|
48
|
+
},
|
|
49
|
+
orderBy: this.options.orderBy,
|
|
50
|
+
limit: this.options.pageSize,
|
|
51
|
+
load: this.options.load,
|
|
52
|
+
include: this.options.include,
|
|
53
|
+
};
|
|
54
|
+
let getItems = () => this.repo.find(options);
|
|
55
|
+
if (this._aggregates === undefined /* &&
|
|
56
|
+
(isOfType<QueryOptions<entityType, any, any, any, any, any>>(
|
|
57
|
+
this.options,
|
|
58
|
+
'aggregate',
|
|
59
|
+
))*/) {
|
|
60
|
+
let agg = this.options.aggregate;
|
|
61
|
+
if (!this.repo._dataProvider.isProxy) {
|
|
62
|
+
let itemsPromise = getItems();
|
|
63
|
+
getItems = async () => {
|
|
64
|
+
this._aggregates = await this.repo.aggregate({
|
|
65
|
+
...agg,
|
|
66
|
+
where: this.options.where,
|
|
67
|
+
});
|
|
68
|
+
return itemsPromise;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
const loader = new RelationLoader();
|
|
73
|
+
getItems = () => this.repo
|
|
74
|
+
._rawFind(options, false, loader, async (opt) => {
|
|
75
|
+
const r = await this.repo._edp.query(opt, await this.repo.__buildGroupByOptions(agg));
|
|
76
|
+
this._aggregates = r.aggregates;
|
|
77
|
+
return r.items;
|
|
78
|
+
})
|
|
79
|
+
.then(async (y) => {
|
|
80
|
+
await loader.resolveAll();
|
|
81
|
+
return y;
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
let items = await getItems();
|
|
86
|
+
let nextPage = () => {
|
|
87
|
+
throw new Error('no more pages');
|
|
88
|
+
};
|
|
89
|
+
let hasNextPage = items.length == this.options.pageSize;
|
|
90
|
+
if (hasNextPage) {
|
|
91
|
+
let nextPageFilter = await this.repo._createAfterFilter(this.options.orderBy, items[items.length - 1]);
|
|
92
|
+
nextPage = () => this.paginator(nextPageFilter);
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
count: () => this.count(),
|
|
96
|
+
hasNextPage,
|
|
97
|
+
items,
|
|
98
|
+
nextPage,
|
|
99
|
+
//@ts-ignore
|
|
100
|
+
aggregates: this._aggregates,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
[Symbol.asyncIterator]() {
|
|
104
|
+
if (!this.options.where) {
|
|
105
|
+
this.options.where = {};
|
|
106
|
+
}
|
|
107
|
+
let ob = this.options.orderBy;
|
|
108
|
+
this.options.orderBy = Sort.createUniqueEntityOrderBy(this.repo.metadata, ob);
|
|
109
|
+
let itemIndex = -1;
|
|
110
|
+
let currentPage = undefined;
|
|
111
|
+
let itStrategy;
|
|
112
|
+
let j = 0;
|
|
113
|
+
itStrategy = async () => {
|
|
114
|
+
if (this.options.progress) {
|
|
115
|
+
this.options.progress.progress(j++ / (await this.count()));
|
|
116
|
+
}
|
|
117
|
+
if (currentPage === undefined || itemIndex == currentPage.items.length) {
|
|
118
|
+
if (currentPage && !currentPage.hasNextPage)
|
|
119
|
+
return { value: undefined, done: true };
|
|
120
|
+
let prev = currentPage;
|
|
121
|
+
if (currentPage)
|
|
122
|
+
currentPage = await currentPage.nextPage();
|
|
123
|
+
else
|
|
124
|
+
currentPage = await this.paginator();
|
|
125
|
+
itemIndex = 0;
|
|
126
|
+
if (currentPage.items.length == 0) {
|
|
127
|
+
return { value: undefined, done: true };
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
if (prev?.items.length ?? 0 > 0) {
|
|
131
|
+
if (this.repo.getEntityRef(prev.items[0]).getId() ==
|
|
132
|
+
this.repo.getEntityRef(currentPage.items[0]).getId())
|
|
133
|
+
throw new Error('pagination failure, returned same first row');
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (itemIndex < currentPage.items.length)
|
|
138
|
+
return { value: currentPage.items[itemIndex++], done: false };
|
|
139
|
+
return { done: true, value: undefined };
|
|
140
|
+
};
|
|
141
|
+
return {
|
|
142
|
+
next: async () => {
|
|
143
|
+
let r = itStrategy();
|
|
144
|
+
return r;
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { LookupColumn, makeTitle } from '../column.js';
|
|
2
2
|
import { CompoundIdField } from '../CompoundIdField.js';
|
|
3
|
-
import { Remult, isBackend
|
|
3
|
+
import { Remult, isBackend } from '../context.js';
|
|
4
4
|
import { Filter } from '../filter/filter-interfaces.js';
|
|
5
5
|
import { Sort } from '../sort.js';
|
|
6
6
|
import { GroupByForApiKey, } from './remult3.js';
|
|
@@ -18,6 +18,7 @@ import { remultStatic } from '../remult-static.js';
|
|
|
18
18
|
import { Validators } from '../validators.js';
|
|
19
19
|
import { addValidator } from './addValidator.js';
|
|
20
20
|
import { isOfType } from '../isOfType.js';
|
|
21
|
+
import { QueryResultImpl } from './QueryResultImpl.js';
|
|
21
22
|
//import { remult } from "../remult-proxy";
|
|
22
23
|
let classValidatorValidate = undefined;
|
|
23
24
|
// import ("class-validator".toString())
|
|
@@ -132,6 +133,23 @@ export class RepositoryImplementation {
|
|
|
132
133
|
return (await this.groupBy(options))[0];
|
|
133
134
|
}
|
|
134
135
|
async groupBy(options) {
|
|
136
|
+
var dpOptions = await this.__buildGroupByOptions(options);
|
|
137
|
+
const result = await this._edp.groupBy(dpOptions);
|
|
138
|
+
//@ts-ignore
|
|
139
|
+
if (!options?.[GroupByForApiKey] && options.group) {
|
|
140
|
+
const loaderOptions = {
|
|
141
|
+
include: {},
|
|
142
|
+
};
|
|
143
|
+
for (const key of options.group) {
|
|
144
|
+
loaderOptions.include[key] = true;
|
|
145
|
+
}
|
|
146
|
+
const loader = new RelationLoader();
|
|
147
|
+
await this._populateRelationsForFields(dpOptions.group, loaderOptions, result, loader);
|
|
148
|
+
await loader.resolveAll();
|
|
149
|
+
}
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
async __buildGroupByOptions(options) {
|
|
135
153
|
let findOptions = await this._buildEntityDataProviderFindOptions({
|
|
136
154
|
...options,
|
|
137
155
|
});
|
|
@@ -185,20 +203,7 @@ export class RepositoryImplementation {
|
|
|
185
203
|
}
|
|
186
204
|
}
|
|
187
205
|
}
|
|
188
|
-
|
|
189
|
-
//@ts-ignore
|
|
190
|
-
if (!options?.[GroupByForApiKey] && options.group) {
|
|
191
|
-
const loaderOptions = {
|
|
192
|
-
include: {},
|
|
193
|
-
};
|
|
194
|
-
for (const key of options.group) {
|
|
195
|
-
loaderOptions.include[key] = true;
|
|
196
|
-
}
|
|
197
|
-
const loader = new RelationLoader();
|
|
198
|
-
await this._populateRelationsForFields(dpOptions.group, loaderOptions, result, loader);
|
|
199
|
-
await loader.resolveAll();
|
|
200
|
-
}
|
|
201
|
-
return result;
|
|
206
|
+
return dpOptions;
|
|
202
207
|
}
|
|
203
208
|
_idCache = new Map();
|
|
204
209
|
_getCachedById(id, doNotLoadIfNotFound) {
|
|
@@ -352,7 +357,7 @@ export class RepositoryImplementation {
|
|
|
352
357
|
}
|
|
353
358
|
else {
|
|
354
359
|
let updated = 0;
|
|
355
|
-
for await (const item of this.query({ where })) {
|
|
360
|
+
for await (const item of this.query({ where, aggregate: undefined })) {
|
|
356
361
|
assign(item, set);
|
|
357
362
|
await getEntityRef(item).save();
|
|
358
363
|
updated++;
|
|
@@ -465,7 +470,7 @@ export class RepositoryImplementation {
|
|
|
465
470
|
},
|
|
466
471
|
};
|
|
467
472
|
}
|
|
468
|
-
async _rawFind(options, skipOrderByAndLimit = false, loader) {
|
|
473
|
+
async _rawFind(options, skipOrderByAndLimit = false, loader, actualFind) {
|
|
469
474
|
if (!options)
|
|
470
475
|
options = {};
|
|
471
476
|
if (this._defaultFindOptions) {
|
|
@@ -477,13 +482,13 @@ export class RepositoryImplementation {
|
|
|
477
482
|
delete opt.limit;
|
|
478
483
|
}
|
|
479
484
|
Remult.onFind(this._info, options);
|
|
480
|
-
const rawRows = await
|
|
485
|
+
const rawRows = await actualFind(opt);
|
|
481
486
|
let result = await this._loadManyToOneForManyRows(rawRows, options, loader);
|
|
482
487
|
return result;
|
|
483
488
|
}
|
|
484
489
|
async find(options, skipOrderByAndLimit = false) {
|
|
485
490
|
const loader = new RelationLoader();
|
|
486
|
-
const result = await this._rawFind(options, skipOrderByAndLimit, loader);
|
|
491
|
+
const result = await this._rawFind(options, skipOrderByAndLimit, loader, (x) => this._edp.find(x));
|
|
487
492
|
await loader.resolveAll();
|
|
488
493
|
return result;
|
|
489
494
|
}
|
|
@@ -578,7 +583,7 @@ export class RepositoryImplementation {
|
|
|
578
583
|
loader
|
|
579
584
|
.load({
|
|
580
585
|
entityType,
|
|
581
|
-
find: (options) => toRepo._rawFind(options, false, loader),
|
|
586
|
+
find: (options) => toRepo._rawFind(options, false, loader, (o) => toRepo._edp.find(o)),
|
|
582
587
|
metadata: toRepo.metadata,
|
|
583
588
|
}, findOptions)
|
|
584
589
|
.then((result) => {
|
|
@@ -729,7 +734,7 @@ export class RepositoryImplementation {
|
|
|
729
734
|
}
|
|
730
735
|
else {
|
|
731
736
|
let deleted = 0;
|
|
732
|
-
for await (const item of this.query({ where })) {
|
|
737
|
+
for await (const item of this.query({ where, aggregate: undefined })) {
|
|
733
738
|
await getEntityRef(item).delete();
|
|
734
739
|
deleted++;
|
|
735
740
|
}
|
|
@@ -2380,116 +2385,6 @@ export class ControllerBase {
|
|
|
2380
2385
|
return getControllerRef(this, this.remult);
|
|
2381
2386
|
}
|
|
2382
2387
|
}
|
|
2383
|
-
class QueryResultImpl {
|
|
2384
|
-
options;
|
|
2385
|
-
repo;
|
|
2386
|
-
constructor(options, repo) {
|
|
2387
|
-
this.options = options;
|
|
2388
|
-
this.repo = repo;
|
|
2389
|
-
if (!this.options)
|
|
2390
|
-
this.options = {};
|
|
2391
|
-
if (!this.options.pageSize) {
|
|
2392
|
-
this.options.pageSize = queryConfig.defaultPageSize;
|
|
2393
|
-
}
|
|
2394
|
-
}
|
|
2395
|
-
_count = undefined;
|
|
2396
|
-
async getPage(page) {
|
|
2397
|
-
if ((page ?? 0) < 1)
|
|
2398
|
-
page = 1;
|
|
2399
|
-
return this.repo.find({
|
|
2400
|
-
where: this.options.where,
|
|
2401
|
-
orderBy: this.options.orderBy,
|
|
2402
|
-
limit: this.options.pageSize,
|
|
2403
|
-
page: page,
|
|
2404
|
-
load: this.options.load,
|
|
2405
|
-
include: this.options.include,
|
|
2406
|
-
});
|
|
2407
|
-
}
|
|
2408
|
-
async count() {
|
|
2409
|
-
if (this._count === undefined)
|
|
2410
|
-
this._count = await this.repo.count(this.options.where);
|
|
2411
|
-
return this._count;
|
|
2412
|
-
}
|
|
2413
|
-
async forEach(what) {
|
|
2414
|
-
let i = 0;
|
|
2415
|
-
for await (const x of this) {
|
|
2416
|
-
await what(x);
|
|
2417
|
-
i++;
|
|
2418
|
-
}
|
|
2419
|
-
return i;
|
|
2420
|
-
}
|
|
2421
|
-
async paginator(pNextPageFilter) {
|
|
2422
|
-
this.options.orderBy = Sort.createUniqueEntityOrderBy(this.repo.metadata, this.options.orderBy);
|
|
2423
|
-
let items = await this.repo.find({
|
|
2424
|
-
where: {
|
|
2425
|
-
$and: [this.options.where, pNextPageFilter],
|
|
2426
|
-
},
|
|
2427
|
-
orderBy: this.options.orderBy,
|
|
2428
|
-
limit: this.options.pageSize,
|
|
2429
|
-
load: this.options.load,
|
|
2430
|
-
include: this.options.include,
|
|
2431
|
-
});
|
|
2432
|
-
let nextPage = () => {
|
|
2433
|
-
throw new Error('no more pages');
|
|
2434
|
-
};
|
|
2435
|
-
let hasNextPage = items.length == this.options.pageSize;
|
|
2436
|
-
if (hasNextPage) {
|
|
2437
|
-
let nextPageFilter = await this.repo._createAfterFilter(this.options.orderBy, items[items.length - 1]);
|
|
2438
|
-
nextPage = () => this.paginator(nextPageFilter);
|
|
2439
|
-
}
|
|
2440
|
-
return {
|
|
2441
|
-
count: () => this.count(),
|
|
2442
|
-
hasNextPage,
|
|
2443
|
-
items,
|
|
2444
|
-
nextPage,
|
|
2445
|
-
};
|
|
2446
|
-
}
|
|
2447
|
-
[Symbol.asyncIterator]() {
|
|
2448
|
-
if (!this.options.where) {
|
|
2449
|
-
this.options.where = {};
|
|
2450
|
-
}
|
|
2451
|
-
let ob = this.options.orderBy;
|
|
2452
|
-
this.options.orderBy = Sort.createUniqueEntityOrderBy(this.repo.metadata, ob);
|
|
2453
|
-
let itemIndex = -1;
|
|
2454
|
-
let currentPage = undefined;
|
|
2455
|
-
let itStrategy;
|
|
2456
|
-
let j = 0;
|
|
2457
|
-
itStrategy = async () => {
|
|
2458
|
-
if (this.options.progress) {
|
|
2459
|
-
this.options.progress.progress(j++ / (await this.count()));
|
|
2460
|
-
}
|
|
2461
|
-
if (currentPage === undefined || itemIndex == currentPage.items.length) {
|
|
2462
|
-
if (currentPage && !currentPage.hasNextPage)
|
|
2463
|
-
return { value: undefined, done: true };
|
|
2464
|
-
let prev = currentPage;
|
|
2465
|
-
if (currentPage)
|
|
2466
|
-
currentPage = await currentPage.nextPage();
|
|
2467
|
-
else
|
|
2468
|
-
currentPage = await this.paginator();
|
|
2469
|
-
itemIndex = 0;
|
|
2470
|
-
if (currentPage.items.length == 0) {
|
|
2471
|
-
return { value: undefined, done: true };
|
|
2472
|
-
}
|
|
2473
|
-
else {
|
|
2474
|
-
if (prev?.items.length ?? 0 > 0) {
|
|
2475
|
-
if (this.repo.getEntityRef(prev.items[0]).getId() ==
|
|
2476
|
-
this.repo.getEntityRef(currentPage.items[0]).getId())
|
|
2477
|
-
throw new Error('pagination failure, returned same first row');
|
|
2478
|
-
}
|
|
2479
|
-
}
|
|
2480
|
-
}
|
|
2481
|
-
if (itemIndex < currentPage.items.length)
|
|
2482
|
-
return { value: currentPage.items[itemIndex++], done: false };
|
|
2483
|
-
return { done: true, value: undefined };
|
|
2484
|
-
};
|
|
2485
|
-
return {
|
|
2486
|
-
next: async () => {
|
|
2487
|
-
let r = itStrategy();
|
|
2488
|
-
return r;
|
|
2489
|
-
},
|
|
2490
|
-
};
|
|
2491
|
-
}
|
|
2492
|
-
}
|
|
2493
2388
|
class cacheEntityInfo {
|
|
2494
2389
|
value = {};
|
|
2495
2390
|
promise;
|