remult 3.4.0-next.1 → 3.4.0-next.10
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/SseSubscriptionServer.d.ts +2 -0
- package/SseSubscriptionServer.js +40 -0
- package/esm/SseSubscriptionServer.js +13 -0
- package/esm/index.js +7 -6
- package/esm/internals.js +2 -0
- package/esm/remult-d1.js +1 -0
- package/esm/remult-knex/index.js +149 -66
- package/esm/remult-mongo.js +35 -20
- package/esm/remult-sqlite-core.js +1 -0
- package/esm/server/remult-api-server.js +15 -14
- package/esm/src/data-api.js +60 -38
- package/esm/src/data-interfaces.js +4 -4
- package/esm/src/data-providers/array-entity-data-provider.js +14 -2
- package/esm/src/data-providers/in-memory-database.js +12 -1
- package/esm/src/data-providers/indexed-db-data-provider.js +844 -0
- package/esm/src/data-providers/json-data-provider.js +2 -2
- package/esm/src/data-providers/rest-data-provider.js +11 -12
- package/esm/src/data-providers/sql-database.js +302 -172
- package/esm/src/filter/filter-consumer-bridge-to-sql-request.js +35 -34
- package/esm/src/filter/walk-filter.js +29 -0
- package/esm/src/live-query/LiveQueryClient.js +53 -25
- package/esm/src/live-query/SseSubscriptionClient.js +49 -20
- package/esm/src/live-query/SubscriptionChannel.js +34 -10
- package/esm/src/remult3/Fields.js +12 -2
- package/esm/src/remult3/RepositoryImplementation.js +169 -71
- package/esm/src/remult3/remult3.js +2 -2
- package/esm/src/sort.js +0 -3
- package/esm/src/valueConverters.js +11 -0
- package/index.d.ts +32 -17
- package/index.js +5 -2
- package/internals.d.ts +4 -0
- package/internals.js +6 -1
- package/package.json +1 -1
- package/remult-d1.d.ts +1 -0
- package/remult-d1.js +1 -0
- package/remult-knex/index.d.ts +3 -2
- package/remult-knex/index.js +381 -142
- package/remult-mongo.js +103 -52
- package/remult-sqlite-core.d.ts +1 -0
- package/remult-sqlite-core.js +1 -0
- package/server/remult-api-server.d.ts +12 -0
- package/server/remult-api-server.js +19 -39
- package/src/column-interfaces.d.ts +5 -1
- package/src/data-api.d.ts +6 -5
- package/src/data-api.js +152 -121
- package/src/data-interfaces.d.ts +9 -3
- package/src/data-interfaces.js +4 -4
- package/src/data-providers/array-entity-data-provider.d.ts +2 -2
- package/src/data-providers/array-entity-data-provider.js +92 -14
- package/src/data-providers/in-memory-database.d.ts +5 -2
- package/src/data-providers/in-memory-database.js +22 -1
- package/src/data-providers/indexed-db-data-provider.d.ts +61 -0
- package/src/data-providers/indexed-db-data-provider.js +1459 -0
- package/src/data-providers/json-data-provider.js +2 -2
- package/src/data-providers/rest-data-provider.d.ts +2 -3
- package/src/data-providers/rest-data-provider.js +18 -17
- package/src/data-providers/sql-database.d.ts +57 -5
- package/src/data-providers/sql-database.js +587 -307
- package/src/entity.d.ts +12 -0
- package/src/filter/filter-consumer-bridge-to-sql-request.d.ts +8 -3
- package/src/filter/filter-consumer-bridge-to-sql-request.js +40 -47
- package/src/filter/walk-filter.d.ts +3 -0
- package/src/filter/walk-filter.js +56 -0
- package/src/live-query/LiveQueryClient.js +87 -37
- package/src/live-query/SseSubscriptionClient.js +75 -44
- package/src/live-query/SubscriptionChannel.d.ts +12 -0
- package/src/live-query/SubscriptionChannel.js +36 -11
- package/src/live-query/SubscriptionServer.d.ts +4 -0
- package/src/remult3/Fields.js +6 -0
- package/src/remult3/RepositoryImplementation.d.ts +12 -5
- package/src/remult3/RepositoryImplementation.js +553 -338
- package/src/remult3/remult3.d.ts +7 -5
- package/src/remult3/remult3.js +2 -2
- package/src/remult3/repository-internals.d.ts +4 -2
- package/src/sort.js +0 -3
- package/src/sql-command.d.ts +7 -0
- package/src/standard-schema/index.d.ts +1 -1
- package/src/valueConverters.d.ts +4 -1
- package/src/valueConverters.js +13 -0
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { GenericResponse } from './server/index.js';
|
|
2
|
+
import type { SubscriptionServerRouteApi } from './server/remult-api-server.js';
|
|
2
3
|
import type { Remult } from './src/context.js';
|
|
3
4
|
import type { SubscriptionServer } from './src/live-query/SubscriptionServer.js';
|
|
4
5
|
/** Keep below typical proxy idle (30s) and Heroku H15 (55s). */
|
|
5
6
|
export declare const ssePingMs = 15000;
|
|
6
7
|
export declare class SseSubscriptionServer implements SubscriptionServer {
|
|
7
8
|
private canUserConnectToChannel?;
|
|
9
|
+
initApiServer(api: SubscriptionServerRouteApi): void;
|
|
8
10
|
constructor(canUserConnectToChannel?: ((channel: string, remult: Remult) => boolean) | undefined);
|
|
9
11
|
publishMessage<T>(channel: string, message: any): Promise<void>;
|
|
10
12
|
}
|
package/SseSubscriptionServer.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.clientConnection = exports.SseSubscriptionServer = exports.ssePingMs = void 0;
|
|
4
4
|
var tslib_1 = require("tslib");
|
|
5
5
|
var SseSubscriptionClient_js_1 = require("./src/live-query/SseSubscriptionClient.js");
|
|
6
|
+
var SubscriptionChannel_js_1 = require("./src/live-query/SubscriptionChannel.js");
|
|
6
7
|
/** Keep below typical proxy idle (30s) and Heroku H15 (55s). */
|
|
7
8
|
exports.ssePingMs = 15000;
|
|
8
9
|
var SseSubscriptionServer = /** @class */ (function () {
|
|
@@ -18,6 +19,45 @@ var SseSubscriptionServer = /** @class */ (function () {
|
|
|
18
19
|
this.canUserConnectToChannel = function () { return true; };
|
|
19
20
|
}
|
|
20
21
|
}
|
|
22
|
+
SseSubscriptionServer.prototype.initApiServer = function (api) {
|
|
23
|
+
var _this = this;
|
|
24
|
+
var streamPath = '/' + SubscriptionChannel_js_1.streamUrl;
|
|
25
|
+
api.addRoute(streamPath, 'get', function (_a) { return tslib_1.__awaiter(_this, [_a], void 0, function (_b) {
|
|
26
|
+
var req = _b.req, origRes = _b.origRes;
|
|
27
|
+
return tslib_1.__generator(this, function (_c) {
|
|
28
|
+
this.openHttpServerStream(req, origRes);
|
|
29
|
+
return [2 /*return*/];
|
|
30
|
+
});
|
|
31
|
+
}); });
|
|
32
|
+
api.addRoute(streamPath + '/subscribe', 'post', function (_a) { return tslib_1.__awaiter(_this, [_a], void 0, function (_b) {
|
|
33
|
+
var _c;
|
|
34
|
+
var remult = _b.remult, res = _b.res, getBody = _b.getBody;
|
|
35
|
+
return tslib_1.__generator(this, function (_d) {
|
|
36
|
+
switch (_d.label) {
|
|
37
|
+
case 0:
|
|
38
|
+
_c = this.subscribeToChannel;
|
|
39
|
+
return [4 /*yield*/, getBody()];
|
|
40
|
+
case 1:
|
|
41
|
+
_c.apply(this, [_d.sent(), res, remult]);
|
|
42
|
+
return [2 /*return*/];
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}); });
|
|
46
|
+
api.addRoute(streamPath + '/unsubscribe', 'post', function (_a) { return tslib_1.__awaiter(_this, [_a], void 0, function (_b) {
|
|
47
|
+
var _c;
|
|
48
|
+
var remult = _b.remult, res = _b.res, getBody = _b.getBody;
|
|
49
|
+
return tslib_1.__generator(this, function (_d) {
|
|
50
|
+
switch (_d.label) {
|
|
51
|
+
case 0:
|
|
52
|
+
_c = this.subscribeToChannel;
|
|
53
|
+
return [4 /*yield*/, getBody()];
|
|
54
|
+
case 1:
|
|
55
|
+
_c.apply(this, [_d.sent(), res, remult, true]);
|
|
56
|
+
return [2 /*return*/];
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}); });
|
|
60
|
+
};
|
|
21
61
|
//@internal
|
|
22
62
|
SseSubscriptionServer.prototype.subscribeToChannel = function (_a, res, remult, remove) {
|
|
23
63
|
var e_1, _b;
|
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
import { ConnectionNotFoundError } from './src/live-query/SseSubscriptionClient.js';
|
|
2
|
+
import { streamUrl } from './src/live-query/SubscriptionChannel.js';
|
|
2
3
|
/** Keep below typical proxy idle (30s) and Heroku H15 (55s). */
|
|
3
4
|
export const ssePingMs = 15_000;
|
|
4
5
|
export class SseSubscriptionServer {
|
|
5
6
|
canUserConnectToChannel;
|
|
7
|
+
initApiServer(api) {
|
|
8
|
+
const streamPath = '/' + streamUrl;
|
|
9
|
+
api.addRoute(streamPath, 'get', async ({ req, origRes }) => {
|
|
10
|
+
this.openHttpServerStream(req, origRes);
|
|
11
|
+
});
|
|
12
|
+
api.addRoute(streamPath + '/subscribe', 'post', async ({ remult, res, getBody }) => {
|
|
13
|
+
this.subscribeToChannel(await getBody(), res, remult);
|
|
14
|
+
});
|
|
15
|
+
api.addRoute(streamPath + '/unsubscribe', 'post', async ({ remult, res, getBody }) => {
|
|
16
|
+
this.subscribeToChannel(await getBody(), res, remult, true);
|
|
17
|
+
});
|
|
18
|
+
}
|
|
6
19
|
//@internal
|
|
7
20
|
subscribeToChannel({ channel, clientId }, res, remult, remove = false) {
|
|
8
21
|
for (const c of this.connections) {
|
package/esm/index.js
CHANGED
|
@@ -9,31 +9,32 @@ export { LabelTransformer } from './src/remult3/RepositoryImplementation.js';
|
|
|
9
9
|
export const CaptionTransformer = LabelTransformer;
|
|
10
10
|
export { Entity } from './src/remult3/entity.js';
|
|
11
11
|
export { getEntityRef } from './src/remult3/getEntityRef.js';
|
|
12
|
-
export { Field, Fields, Relations
|
|
12
|
+
export { Field, Fields, Relations } from './src/remult3/Fields.js';
|
|
13
13
|
export { IdEntity } from './src/remult3/IdEntity.js';
|
|
14
14
|
export { describeClass, describeBackendMethods, describeEntity, } from './src/remult3/classDescribers.js';
|
|
15
|
-
export { EntityError
|
|
15
|
+
export { EntityError } from './src/data-interfaces.js'; //V
|
|
16
16
|
export { RestDataProvider } from './src/data-providers/rest-data-provider.js'; //V
|
|
17
17
|
export { InMemoryDataProvider } from './src/data-providers/in-memory-database.js'; //V
|
|
18
18
|
export { SqlDatabase } from './src/data-providers/sql-database.js'; //V
|
|
19
19
|
export { CustomSqlFilterBuilder, dbNamesOf, } from './src/filter/filter-consumer-bridge-to-sql-request.js';
|
|
20
|
-
export { JsonDataProvider
|
|
20
|
+
export { JsonDataProvider } from './src/data-providers/json-data-provider.js'; //V
|
|
21
21
|
export { JsonEntityOpfsStorage } from './src/data-providers/json-entity-opfs-storage.js';
|
|
22
22
|
export { JsonEntityIndexedDbStorage } from './src/data-providers/json-entity-indexed-db-data-provider.js';
|
|
23
|
+
export { IndexedDbDataProvider, IndexedDbIndexBuilder, } from './src/data-providers/indexed-db-data-provider.js';
|
|
23
24
|
//export * from './src/data-api'; //reconsider if to make internal
|
|
24
25
|
export { Controller, BackendMethod, ProgressListener, ForbiddenError, } from './src/server-action.js';
|
|
25
26
|
export { Allow, Remult, withRemult, isBackend, EventSource, } from './src/context.js';
|
|
26
27
|
export { Sort } from './src/sort.js';
|
|
27
28
|
export { CompoundIdField } from './src/CompoundIdField.js';
|
|
28
|
-
export { Filter
|
|
29
|
+
export { Filter } from './src/filter/filter-interfaces.js';
|
|
29
30
|
export { UrlBuilder } from './urlBuilder.js';
|
|
30
31
|
export { Validators, valueValidator, createValidator, createValidatorWithArgs, createValueValidatorWithArgs, createValueValidator, } from './src/validators.js';
|
|
31
32
|
export { ValueConverters } from './src/valueConverters.js';
|
|
32
33
|
export { remult } from './src/remult-proxy.js';
|
|
33
34
|
import { remult } from './src/remult-proxy.js';
|
|
34
35
|
//export { getId } from './src/remult3/getId';
|
|
35
|
-
export { InMemoryLiveQueryStorage
|
|
36
|
-
export { SubscriptionChannel
|
|
36
|
+
export { InMemoryLiveQueryStorage } from './src/live-query/SubscriptionServer.js';
|
|
37
|
+
export { SubscriptionChannel } from './src/live-query/SubscriptionChannel.js';
|
|
37
38
|
/**
|
|
38
39
|
* A convenient shortcut function to quickly obtain a repository for a specific entity type in Remult.
|
|
39
40
|
*
|
package/esm/internals.js
CHANGED
|
@@ -4,8 +4,10 @@ export { getRelationInfo, getRelationFieldInfo, } from './src/remult3/relationIn
|
|
|
4
4
|
export { decorateColumnSettings, controllerRefImpl, getControllerRef, fieldOptionsEnricher, } from './src/remult3/RepositoryImplementation.js';
|
|
5
5
|
export { getEntitySettings } from './src/remult3/getEntityRef.js';
|
|
6
6
|
export { __updateEntityBasedOnWhere } from './src/filter/filter-interfaces.js';
|
|
7
|
+
export { omitFilterField, walkFilter } from './src/filter/walk-filter.js';
|
|
7
8
|
export const actionInfo = remultStatic.actionInfo;
|
|
8
9
|
export { isOfType } from './src/isOfType.js';
|
|
9
10
|
export { flags } from './src/remult3/remult3.js';
|
|
11
|
+
export { SseSubscriptionClient } from './src/live-query/SseSubscriptionClient.js';
|
|
10
12
|
export { DataProviderPromiseWrapper } from './src/data-interfaces.js';
|
|
11
13
|
export { pagedQueryResult } from './src/remult3/pagedQueryResult.js';
|
package/esm/remult-d1.js
CHANGED
package/esm/remult-knex/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { CustomSqlFilterBuilder, dbNamesOfWithForceSqlExpression, } from '../src/filter/filter-consumer-bridge-to-sql-request.js';
|
|
1
|
+
import { CustomSqlFilterBuilder, dbNamesOfWithForceSqlExpression, escapeLike, LIKE_ESCAPE_SQL, } from '../src/filter/filter-consumer-bridge-to-sql-request.js';
|
|
2
2
|
import { dbNamesOf, isDbReadonly, shouldCreateEntity, shouldNotCreateField, } from '../src/filter/filter-consumer-bridge-to-sql-request.js';
|
|
3
3
|
import { customDatabaseFilterToken, Filter, } from '../src/filter/filter-interfaces.js';
|
|
4
4
|
import { remult as remultContext } from '../src/remult-proxy.js';
|
|
5
5
|
import { getRepository, isAutoIncrement, } from '../src/remult3/RepositoryImplementation.js';
|
|
6
6
|
import { Sort } from '../src/sort.js';
|
|
7
|
-
import { ValueConverters } from '../src/valueConverters.js';
|
|
7
|
+
import { fromDbColumn, toDbColumn, ValueConverters, } from '../src/valueConverters.js';
|
|
8
8
|
import { getRepositoryInternals } from '../src/remult3/repository-internals.js';
|
|
9
|
-
import { groupByImpl, getRowAfterUpdate, } from '../src/data-providers/sql-database.js';
|
|
9
|
+
import { groupByImpl, getRowAfterUpdate, jsonColumnStorageOf, } from '../src/data-providers/sql-database.js';
|
|
10
10
|
export class KnexDataProvider {
|
|
11
11
|
knex;
|
|
12
12
|
constructor(knex) {
|
|
@@ -46,29 +46,14 @@ export class KnexDataProvider {
|
|
|
46
46
|
return r.knex;
|
|
47
47
|
}
|
|
48
48
|
wrapIdentifier = (x) => this.knex.ref(x) + '';
|
|
49
|
-
|
|
49
|
+
get jsonColumnStorage() {
|
|
50
50
|
if (!supportsJsonLoadingAndSaving(this.knex))
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
fromDb: ValueConverters.JsonString.fromDb,
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
else if (this.knex.client.config.client === 'mysql2') {
|
|
62
|
-
for (const f of entity.fields.toArray()) {
|
|
63
|
-
if (f.valueConverter.fieldTypeInDb === 'json') {
|
|
64
|
-
//@ts-ignore
|
|
65
|
-
f.valueConverter = {
|
|
66
|
-
...f.valueConverter,
|
|
67
|
-
toDb: ValueConverters.JsonString.toDb,
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
51
|
+
return 'text';
|
|
52
|
+
if (this.knex.client.config.client === 'mysql2')
|
|
53
|
+
return 'textOnWrite';
|
|
54
|
+
return 'native';
|
|
55
|
+
}
|
|
56
|
+
getEntityDataProvider(entity) {
|
|
72
57
|
return new KnexEntityDataProvider(entity, this.knex, this.wrapIdentifier, this);
|
|
73
58
|
}
|
|
74
59
|
async transaction(action) {
|
|
@@ -93,7 +78,7 @@ export class KnexDataProvider {
|
|
|
93
78
|
if (!wrapIdentifier)
|
|
94
79
|
wrapIdentifier = (x) => x;
|
|
95
80
|
const repo = getRepository(entity);
|
|
96
|
-
var b = new FilterConsumerBridgeToKnexRequest(await dbNamesOfWithForceSqlExpression(repo.metadata, wrapIdentifier), wrapIdentifier ?? wrapIdentifier);
|
|
81
|
+
var b = new FilterConsumerBridgeToKnexRequest(await dbNamesOfWithForceSqlExpression(repo.metadata, wrapIdentifier), wrapIdentifier ?? wrapIdentifier, jsonColumnStorageOf(repo));
|
|
97
82
|
b._addWhere = false;
|
|
98
83
|
await (await getRepositoryInternals(repo)._translateWhereToFilter(condition)).__applyToConsumer(b);
|
|
99
84
|
let r = await b.resolveWhere();
|
|
@@ -119,7 +104,7 @@ class KnexEntityDataProvider {
|
|
|
119
104
|
async groupBy(options) {
|
|
120
105
|
return groupByImpl(options, await dbNamesOf(this.entity, this.dp.wrapIdentifier), this.dp.createCommand(), false, (limit, offset) => this.knex.client.config.client?.includes('mssql')
|
|
121
106
|
? ' offset ' + offset + ' rows fetch next ' + limit + ' rows only'
|
|
122
|
-
: ' limit ' + limit + ' offset ' + offset);
|
|
107
|
+
: ' limit ' + limit + ' offset ' + offset, this.dp.jsonColumnStorage);
|
|
123
108
|
}
|
|
124
109
|
getEntityFrom(e) {
|
|
125
110
|
if (this.entity.options.sqlExpression)
|
|
@@ -128,7 +113,7 @@ class KnexEntityDataProvider {
|
|
|
128
113
|
}
|
|
129
114
|
async count(where) {
|
|
130
115
|
const e = await this.init();
|
|
131
|
-
const br = new FilterConsumerBridgeToKnexRequest(e, this.rawSqlWrapIdentifier);
|
|
116
|
+
const br = new FilterConsumerBridgeToKnexRequest(e, this.rawSqlWrapIdentifier, this.dp.jsonColumnStorage);
|
|
132
117
|
where.__applyToConsumer(br);
|
|
133
118
|
let r = await br.resolveWhere();
|
|
134
119
|
const result = await this.getEntityFrom(e)
|
|
@@ -162,7 +147,7 @@ class KnexEntityDataProvider {
|
|
|
162
147
|
}
|
|
163
148
|
let query = this.getEntityFrom(e).select(cols);
|
|
164
149
|
if (options?.where) {
|
|
165
|
-
const br = new FilterConsumerBridgeToKnexRequest(e, this.rawSqlWrapIdentifier);
|
|
150
|
+
const br = new FilterConsumerBridgeToKnexRequest(e, this.rawSqlWrapIdentifier, this.dp.jsonColumnStorage);
|
|
166
151
|
options.where.__applyToConsumer(br);
|
|
167
152
|
let r = await br.resolveWhere();
|
|
168
153
|
query.where((b) => r.forEach((y) => y(b)));
|
|
@@ -188,7 +173,7 @@ class KnexEntityDataProvider {
|
|
|
188
173
|
for (let m in y) {
|
|
189
174
|
let field = colKeys[i++];
|
|
190
175
|
try {
|
|
191
|
-
result[field.key] = field
|
|
176
|
+
result[field.key] = fromDbColumn(field, y[m], this.dp.jsonColumnStorage);
|
|
192
177
|
}
|
|
193
178
|
catch (err) {
|
|
194
179
|
throw new Error('Failed to load from db:' + field.key + '\r\n' + err);
|
|
@@ -212,14 +197,14 @@ class KnexEntityDataProvider {
|
|
|
212
197
|
}
|
|
213
198
|
async update(id, data, options) {
|
|
214
199
|
const e = await this.init();
|
|
215
|
-
let f = new FilterConsumerBridgeToKnexRequest(e, this.rawSqlWrapIdentifier);
|
|
200
|
+
let f = new FilterConsumerBridgeToKnexRequest(e, this.rawSqlWrapIdentifier, this.dp.jsonColumnStorage);
|
|
216
201
|
Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(id)).__applyToConsumer(f);
|
|
217
202
|
let updateObject = {};
|
|
218
203
|
for (const x of this.entity.fields) {
|
|
219
204
|
if (isDbReadonly(x, e)) {
|
|
220
205
|
}
|
|
221
206
|
else if (data[x.key] !== undefined) {
|
|
222
|
-
let v = translateValueAndHandleArrayAndHandleArray(x, data[x.key]);
|
|
207
|
+
let v = translateValueAndHandleArrayAndHandleArray(x, data[x.key], this.dp.jsonColumnStorage);
|
|
223
208
|
if (v !== undefined) {
|
|
224
209
|
let key = await e.$dbNameOf(x);
|
|
225
210
|
updateObject[key] = v;
|
|
@@ -234,23 +219,91 @@ class KnexEntityDataProvider {
|
|
|
234
219
|
return undefined;
|
|
235
220
|
return getRowAfterUpdate(this.entity, this, data, id, 'update');
|
|
236
221
|
}
|
|
237
|
-
async delete(
|
|
222
|
+
async delete(ids) {
|
|
223
|
+
if (ids.length === 0)
|
|
224
|
+
return;
|
|
238
225
|
const e = await this.init();
|
|
239
|
-
let f = new FilterConsumerBridgeToKnexRequest(e, this.rawSqlWrapIdentifier);
|
|
240
|
-
Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(
|
|
226
|
+
let f = new FilterConsumerBridgeToKnexRequest(e, this.rawSqlWrapIdentifier, this.dp.jsonColumnStorage);
|
|
227
|
+
Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(...ids)).__applyToConsumer(f);
|
|
241
228
|
let where = await f.resolveWhere();
|
|
242
229
|
await this.getEntityFrom(e)
|
|
243
230
|
.delete()
|
|
244
231
|
.where((b) => where.forEach((w) => w(b)));
|
|
245
232
|
}
|
|
246
233
|
async insert(data, options) {
|
|
234
|
+
if (data.length === 0)
|
|
235
|
+
return [];
|
|
236
|
+
if (isMysqlKnex(this.knex) &&
|
|
237
|
+
isAutoIncrement(this.entity.idMetadata.field) &&
|
|
238
|
+
options?.select !== 'none') {
|
|
239
|
+
const result = [];
|
|
240
|
+
for (const row of data)
|
|
241
|
+
result.push(await this.insertOne(row, options));
|
|
242
|
+
return result;
|
|
243
|
+
}
|
|
244
|
+
const e = await this.init();
|
|
245
|
+
const batches = splitByMaxParameters(data, (row) => countKnexInsertBindParameters(this.entity, e, row, this.dp.jsonColumnStorage), knexMaxParametersInOneSqlStatement(this.knex));
|
|
246
|
+
const result = [];
|
|
247
|
+
for (const batch of batches)
|
|
248
|
+
result.push(...(await this.insertBatch(batch, e, options)));
|
|
249
|
+
return result;
|
|
250
|
+
}
|
|
251
|
+
async insertBatch(batch, e, options) {
|
|
252
|
+
const cols = [];
|
|
253
|
+
for (const x of this.entity.fields) {
|
|
254
|
+
if (isDbReadonly(x, e))
|
|
255
|
+
continue;
|
|
256
|
+
if (batch.some((row) => translateValueAndHandleArrayAndHandleArray(x, row[x.key], this.dp.jsonColumnStorage) != undefined))
|
|
257
|
+
cols.push(x);
|
|
258
|
+
}
|
|
259
|
+
const insertObjects = [];
|
|
260
|
+
for (const row of batch) {
|
|
261
|
+
const insertObject = {};
|
|
262
|
+
for (const x of cols) {
|
|
263
|
+
const v = translateValueAndHandleArrayAndHandleArray(x, row[x.key], this.dp.jsonColumnStorage);
|
|
264
|
+
const key = await e.$dbNameOf(x);
|
|
265
|
+
if (v != undefined)
|
|
266
|
+
insertObject[key] = v;
|
|
267
|
+
else if (x.allowNull)
|
|
268
|
+
insertObject[key] = null;
|
|
269
|
+
else
|
|
270
|
+
insertObject[key] = this.knex.raw('DEFAULT');
|
|
271
|
+
}
|
|
272
|
+
insertObjects.push(insertObject);
|
|
273
|
+
}
|
|
274
|
+
let insert = this.getEntityFrom(e).insert(insertObjects);
|
|
275
|
+
const wantRows = options?.select !== 'none';
|
|
276
|
+
if (isAutoIncrement(this.entity.idMetadata.field) && wantRows) {
|
|
277
|
+
const result = await insert.returning(this.entity.idMetadata.field.dbName);
|
|
278
|
+
const ids = result.map((r) => r && typeof r === 'object' ? r[this.entity.idMetadata.field.dbName] : r);
|
|
279
|
+
return this.loadRowsByIds(ids);
|
|
280
|
+
}
|
|
281
|
+
await insert;
|
|
282
|
+
if (!wantRows)
|
|
283
|
+
return batch.map(() => undefined);
|
|
284
|
+
return this.loadRowsByIds(batch.map((row) => this.entity.idMetadata.getId(row)));
|
|
285
|
+
}
|
|
286
|
+
async loadRowsByIds(ids) {
|
|
287
|
+
const found = await this.find({
|
|
288
|
+
where: Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(...ids)),
|
|
289
|
+
});
|
|
290
|
+
const byId = new Map(found.map((row) => [this.entity.idMetadata.getId(row) + '', row]));
|
|
291
|
+
return ids.map((id) => {
|
|
292
|
+
const row = byId.get(id + '');
|
|
293
|
+
if (!row)
|
|
294
|
+
throw new Error(`Failed to insert row - result contained ${found.length} rows`);
|
|
295
|
+
return row;
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
//@internal
|
|
299
|
+
async insertOne(data, options) {
|
|
247
300
|
const e = await this.init();
|
|
248
301
|
let insertObject = {};
|
|
249
302
|
for (const x of this.entity.fields) {
|
|
250
303
|
if (isDbReadonly(x, e)) {
|
|
251
304
|
}
|
|
252
305
|
else {
|
|
253
|
-
let v = translateValueAndHandleArrayAndHandleArray(x, data[x.key]);
|
|
306
|
+
let v = translateValueAndHandleArrayAndHandleArray(x, data[x.key], this.dp.jsonColumnStorage);
|
|
254
307
|
if (v != undefined) {
|
|
255
308
|
let key = await e.$dbNameOf(x);
|
|
256
309
|
insertObject[key] = v;
|
|
@@ -286,6 +339,7 @@ class KnexEntityDataProvider {
|
|
|
286
339
|
class FilterConsumerBridgeToKnexRequest {
|
|
287
340
|
innerNameProvider;
|
|
288
341
|
rawSqlWrapIdentifier;
|
|
342
|
+
jsonColumnStorage;
|
|
289
343
|
_addWhere = true;
|
|
290
344
|
promises = [];
|
|
291
345
|
result = [];
|
|
@@ -299,9 +353,10 @@ class FilterConsumerBridgeToKnexRequest {
|
|
|
299
353
|
}
|
|
300
354
|
return this.result;
|
|
301
355
|
}
|
|
302
|
-
constructor(innerNameProvider, rawSqlWrapIdentifier) {
|
|
356
|
+
constructor(innerNameProvider, rawSqlWrapIdentifier, jsonColumnStorage) {
|
|
303
357
|
this.innerNameProvider = innerNameProvider;
|
|
304
358
|
this.rawSqlWrapIdentifier = rawSqlWrapIdentifier;
|
|
359
|
+
this.jsonColumnStorage = jsonColumnStorage;
|
|
305
360
|
}
|
|
306
361
|
custom(key, customItem) {
|
|
307
362
|
throw new Error('Custom filter should be translated before it gets here');
|
|
@@ -310,7 +365,7 @@ class FilterConsumerBridgeToKnexRequest {
|
|
|
310
365
|
this.promises.push((async () => {
|
|
311
366
|
const result = [];
|
|
312
367
|
for (const element of orElements) {
|
|
313
|
-
let f = new FilterConsumerBridgeToKnexRequest(this.innerNameProvider, this.rawSqlWrapIdentifier);
|
|
368
|
+
let f = new FilterConsumerBridgeToKnexRequest(this.innerNameProvider, this.rawSqlWrapIdentifier, this.jsonColumnStorage);
|
|
314
369
|
f._addWhere = false;
|
|
315
370
|
element.__applyToConsumer(f);
|
|
316
371
|
let where = await f.resolveWhere();
|
|
@@ -331,7 +386,7 @@ class FilterConsumerBridgeToKnexRequest {
|
|
|
331
386
|
}
|
|
332
387
|
not(element) {
|
|
333
388
|
this.promises.push((async () => {
|
|
334
|
-
let f = new FilterConsumerBridgeToKnexRequest(this.innerNameProvider, this.rawSqlWrapIdentifier);
|
|
389
|
+
let f = new FilterConsumerBridgeToKnexRequest(this.innerNameProvider, this.rawSqlWrapIdentifier, this.jsonColumnStorage);
|
|
335
390
|
f._addWhere = false;
|
|
336
391
|
element.__applyToConsumer(f);
|
|
337
392
|
let where = await f.resolveWhere();
|
|
@@ -353,7 +408,7 @@ class FilterConsumerBridgeToKnexRequest {
|
|
|
353
408
|
this.result.push((b) => b.whereNotNull(this.innerNameProvider.$dbNameOf(col)));
|
|
354
409
|
}
|
|
355
410
|
isIn(col, val) {
|
|
356
|
-
this.result.push((knex) => knex.whereIn(this.innerNameProvider.$dbNameOf(col), val.map((x) => translateValueAndHandleArrayAndHandleArray(col, x))));
|
|
411
|
+
this.result.push((knex) => knex.whereIn(this.innerNameProvider.$dbNameOf(col), val.map((x) => translateValueAndHandleArrayAndHandleArray(col, x, this.jsonColumnStorage))));
|
|
357
412
|
}
|
|
358
413
|
isEqualTo(col, val) {
|
|
359
414
|
this.add(col, val, '=');
|
|
@@ -374,39 +429,27 @@ class FilterConsumerBridgeToKnexRequest {
|
|
|
374
429
|
this.add(col, val, '<');
|
|
375
430
|
}
|
|
376
431
|
containsCaseInsensitive(col, val) {
|
|
377
|
-
this.
|
|
378
|
-
b.client.ref(this.innerNameProvider.$dbNameOf(col)) +
|
|
379
|
-
") like lower ('%" +
|
|
380
|
-
val.replace(/'/g, "''") +
|
|
381
|
-
"%')"));
|
|
382
|
-
this.promises.push((async () => { })());
|
|
432
|
+
this.like(col, '%' + escapeLike(val) + '%');
|
|
383
433
|
}
|
|
384
434
|
notContainsCaseInsensitive(col, val) {
|
|
385
|
-
this.
|
|
386
|
-
b.client.ref(this.innerNameProvider.$dbNameOf(col)) +
|
|
387
|
-
") like lower ('%" +
|
|
388
|
-
val.replace(/'/g, "''") +
|
|
389
|
-
"%')"));
|
|
390
|
-
this.promises.push((async () => { })());
|
|
435
|
+
this.like(col, '%' + escapeLike(val) + '%', 'not ');
|
|
391
436
|
}
|
|
392
437
|
startsWithCaseInsensitive(col, val) {
|
|
393
|
-
this.
|
|
394
|
-
b.client.ref(this.innerNameProvider.$dbNameOf(col)) +
|
|
395
|
-
") like lower ('" +
|
|
396
|
-
val.replace(/'/g, "''") +
|
|
397
|
-
"%')"));
|
|
398
|
-
this.promises.push((async () => { })());
|
|
438
|
+
this.like(col, escapeLike(val) + '%');
|
|
399
439
|
}
|
|
400
440
|
endsWithCaseInsensitive(col, val) {
|
|
401
|
-
this.
|
|
441
|
+
this.like(col, '%' + escapeLike(val));
|
|
442
|
+
}
|
|
443
|
+
like(col, pattern, not = '') {
|
|
444
|
+
this.result.push((b) => b.whereRaw(not +
|
|
445
|
+
'lower (' +
|
|
402
446
|
b.client.ref(this.innerNameProvider.$dbNameOf(col)) +
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
"')"));
|
|
447
|
+
') like lower (?)' +
|
|
448
|
+
LIKE_ESCAPE_SQL, [pattern]));
|
|
406
449
|
this.promises.push((async () => { })());
|
|
407
450
|
}
|
|
408
451
|
add(col, val, operator) {
|
|
409
|
-
this.result.push((b) => b.where(this.innerNameProvider.$dbNameOf(col), operator, translateValueAndHandleArrayAndHandleArray(col, val)));
|
|
452
|
+
this.result.push((b) => b.where(this.innerNameProvider.$dbNameOf(col), operator, translateValueAndHandleArrayAndHandleArray(col, val, this.jsonColumnStorage)));
|
|
410
453
|
}
|
|
411
454
|
databaseCustom(databaseCustom) {
|
|
412
455
|
this.promises.push((async () => {
|
|
@@ -415,7 +458,7 @@ class FilterConsumerBridgeToKnexRequest {
|
|
|
415
458
|
}
|
|
416
459
|
if (databaseCustom?.buildSql) {
|
|
417
460
|
let r = new KnexCommandHelper();
|
|
418
|
-
const item = new CustomSqlFilterBuilder(r, this.rawSqlWrapIdentifier);
|
|
461
|
+
const item = new CustomSqlFilterBuilder(r, this.rawSqlWrapIdentifier, this.jsonColumnStorage);
|
|
419
462
|
let sql = await databaseCustom.buildSql(item);
|
|
420
463
|
if (typeof sql !== 'string')
|
|
421
464
|
sql = item.sql;
|
|
@@ -591,8 +634,48 @@ export async function createKnexDataProvider(config) {
|
|
|
591
634
|
let result = new KnexDataProvider(k);
|
|
592
635
|
return result;
|
|
593
636
|
}
|
|
594
|
-
|
|
595
|
-
|
|
637
|
+
const defaultMaxParametersInOneSqlStatement = 2000;
|
|
638
|
+
function isMysqlKnex(knex) {
|
|
639
|
+
const client = knex.client.config.client;
|
|
640
|
+
return client === 'mysql2' || client === 'mysql';
|
|
641
|
+
}
|
|
642
|
+
function knexMaxParametersInOneSqlStatement(knex) {
|
|
643
|
+
const client = String(knex.client.config.client ?? '');
|
|
644
|
+
if (client.includes('sqlite'))
|
|
645
|
+
return 999;
|
|
646
|
+
return defaultMaxParametersInOneSqlStatement;
|
|
647
|
+
}
|
|
648
|
+
function splitByMaxParameters(items, parametersInItem, maxParameters) {
|
|
649
|
+
const batches = [];
|
|
650
|
+
let batch = [];
|
|
651
|
+
let params = 0;
|
|
652
|
+
for (const item of items) {
|
|
653
|
+
const n = parametersInItem(item);
|
|
654
|
+
if (batch.length > 0 && params + n > maxParameters) {
|
|
655
|
+
batches.push(batch);
|
|
656
|
+
batch = [];
|
|
657
|
+
params = 0;
|
|
658
|
+
}
|
|
659
|
+
batch.push(item);
|
|
660
|
+
params += n;
|
|
661
|
+
}
|
|
662
|
+
if (batch.length)
|
|
663
|
+
batches.push(batch);
|
|
664
|
+
return batches;
|
|
665
|
+
}
|
|
666
|
+
function countKnexInsertBindParameters(entity, e, row, storage) {
|
|
667
|
+
let n = 0;
|
|
668
|
+
for (const x of entity.fields) {
|
|
669
|
+
if (isDbReadonly(x, e))
|
|
670
|
+
continue;
|
|
671
|
+
if (translateValueAndHandleArrayAndHandleArray(x, row[x.key], storage) !=
|
|
672
|
+
undefined)
|
|
673
|
+
n++;
|
|
674
|
+
}
|
|
675
|
+
return n;
|
|
676
|
+
}
|
|
677
|
+
function translateValueAndHandleArrayAndHandleArray(field, val, storage) {
|
|
678
|
+
let result = toDbColumn(field, val, storage);
|
|
596
679
|
if (Array.isArray(result))
|
|
597
680
|
return JSON.stringify(result);
|
|
598
681
|
return result;
|
package/esm/remult-mongo.js
CHANGED
|
@@ -119,6 +119,8 @@ class MongoEntityDataProvider {
|
|
|
119
119
|
translateToDb(row, nameProvider) {
|
|
120
120
|
let result = {};
|
|
121
121
|
for (const col of this.entity.fields) {
|
|
122
|
+
if (col.dbReadOnly || col.isServerExpression)
|
|
123
|
+
continue;
|
|
122
124
|
let val = toDb(col, row[col.key]);
|
|
123
125
|
if (val === null)
|
|
124
126
|
val = NULL;
|
|
@@ -255,7 +257,7 @@ class MongoEntityDataProvider {
|
|
|
255
257
|
for (const f of this.entity.fields) {
|
|
256
258
|
if (!f.dbReadOnly && !f.isServerExpression) {
|
|
257
259
|
if (keys.includes(f.key)) {
|
|
258
|
-
newR[f
|
|
260
|
+
newR[e.$dbNameOf(f)] = toDb(f, data[f.key]);
|
|
259
261
|
}
|
|
260
262
|
}
|
|
261
263
|
}
|
|
@@ -266,22 +268,31 @@ class MongoEntityDataProvider {
|
|
|
266
268
|
return undefined;
|
|
267
269
|
return getRowAfterUpdate(this.entity, this, data, id, 'update');
|
|
268
270
|
}
|
|
269
|
-
async delete(
|
|
271
|
+
async delete(ids) {
|
|
272
|
+
if (ids.length === 0)
|
|
273
|
+
return;
|
|
270
274
|
const { e, collection } = await this.collection();
|
|
271
275
|
let f = new FilterConsumerBridgeToMongo(e);
|
|
272
|
-
Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(
|
|
273
|
-
await collection.
|
|
276
|
+
Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(...ids)).__applyToConsumer(f);
|
|
277
|
+
await collection.deleteMany(await f.resolveWhere(), {
|
|
274
278
|
session: this.session,
|
|
275
279
|
});
|
|
276
280
|
}
|
|
277
281
|
async insert(data, options) {
|
|
282
|
+
if (data.length === 0)
|
|
283
|
+
return [];
|
|
278
284
|
let { collection, e } = await this.collection();
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
285
|
+
const docs = [];
|
|
286
|
+
for (const row of data)
|
|
287
|
+
docs.push(await this.translateToDb(row, e));
|
|
288
|
+
const r = await collection.insertMany(docs, { session: this.session });
|
|
282
289
|
if (options?.select === 'none')
|
|
283
|
-
return undefined;
|
|
284
|
-
|
|
290
|
+
return data.map(() => undefined);
|
|
291
|
+
const result = [];
|
|
292
|
+
for (let i = 0; i < data.length; i++) {
|
|
293
|
+
result.push(await this.translateFromDb(await collection.findOne({ _id: r.insertedIds[i] }, { session: this.session }), e));
|
|
294
|
+
}
|
|
295
|
+
return result;
|
|
285
296
|
}
|
|
286
297
|
async collection() {
|
|
287
298
|
const e = await dbNamesOf(this.entity);
|
|
@@ -377,23 +388,27 @@ class FilterConsumerBridgeToMongo {
|
|
|
377
388
|
this.add(col, val, '$lt');
|
|
378
389
|
}
|
|
379
390
|
containsCaseInsensitive(col, val) {
|
|
380
|
-
this.
|
|
391
|
+
this.addRegex(col, val);
|
|
381
392
|
}
|
|
382
393
|
notContainsCaseInsensitive(col, val) {
|
|
383
|
-
this.
|
|
384
|
-
[this.nameProvider.$dbNameOf(col)]: {
|
|
385
|
-
$not: {
|
|
386
|
-
$regex: isNull(val) ? val : toDb(col, val),
|
|
387
|
-
$options: 'i',
|
|
388
|
-
},
|
|
389
|
-
},
|
|
390
|
-
}));
|
|
394
|
+
this.addRegex(col, val, '', '', true);
|
|
391
395
|
}
|
|
392
396
|
startsWithCaseInsensitive(col, val) {
|
|
393
|
-
this.
|
|
397
|
+
this.addRegex(col, val, '^');
|
|
394
398
|
}
|
|
395
399
|
endsWithCaseInsensitive(col, val) {
|
|
396
|
-
this.
|
|
400
|
+
this.addRegex(col, val, '', '$');
|
|
401
|
+
}
|
|
402
|
+
addRegex(col, val, start = '', end = '', negate = false) {
|
|
403
|
+
this.result.push(() => {
|
|
404
|
+
const regex = {
|
|
405
|
+
$regex: start + toDb(col, val).replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + end,
|
|
406
|
+
$options: 'i',
|
|
407
|
+
};
|
|
408
|
+
return {
|
|
409
|
+
[this.nameProvider.$dbNameOf(col)]: negate ? { $not: regex } : regex,
|
|
410
|
+
};
|
|
411
|
+
});
|
|
397
412
|
}
|
|
398
413
|
add(col, val, operator, moreOptions) {
|
|
399
414
|
this.result.push(() => ({
|
|
@@ -13,6 +13,7 @@ export class SqliteCoreDataProvider {
|
|
|
13
13
|
this.doesNotSupportReturningSyntaxOnlyForUpdate = doesNotSupportReturningSyntaxOnlyForUpdate;
|
|
14
14
|
}
|
|
15
15
|
orderByNullsFirst;
|
|
16
|
+
maxParametersInOneSqlStatement = 999;
|
|
16
17
|
getLimitSqlSyntax(limit, offset) {
|
|
17
18
|
return ' limit ' + limit + ' offset ' + offset;
|
|
18
19
|
}
|