nesoi 3.3.17 → 3.3.19
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/lib/elements/entities/bucket/adapters/browserdb.service.js +3 -2
- package/lib/elements/entities/bucket/adapters/bucket_adapter.d.ts +4 -4
- package/lib/elements/entities/bucket/adapters/bucket_adapter.js +1 -0
- package/lib/elements/entities/bucket/adapters/memory.nql.js +14 -1
- package/lib/elements/entities/bucket/adapters/rest.bucket_adapter.d.ts +46 -0
- package/lib/elements/entities/bucket/adapters/rest.bucket_adapter.js +248 -0
- package/lib/elements/entities/bucket/adapters/rest.nql.d.ts +15 -0
- package/lib/elements/entities/bucket/adapters/rest.nql.js +33 -0
- package/lib/elements/entities/bucket/bucket.config.d.ts +3 -3
- package/lib/elements/entities/bucket/bucket.js +7 -6
- package/lib/elements/entities/bucket/cache/bucket_cache.d.ts +35 -2
- package/lib/elements/entities/bucket/cache/bucket_cache.js +65 -9
- package/lib/elements/entities/bucket/graph/bucket_graph.js +5 -4
- package/lib/elements/entities/bucket/query/nql_compiler.d.ts +3 -0
- package/lib/elements/entities/bucket/query/nql_compiler.js +62 -1
- package/lib/elements/entities/bucket/view/bucket_view.js +4 -2
- package/lib/engine/dependency.js +4 -0
- package/lib/engine/transaction/trx.d.ts +11 -0
- package/lib/engine/transaction/trx.js +33 -0
- package/lib/engine/transaction/trx_node.d.ts +3 -2
- package/lib/engine/transaction/trx_node.js +15 -4
- package/package.json +1 -1
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -68,8 +68,9 @@ class BrowserDBService extends service_1.Service {
|
|
|
68
68
|
return async (trx, fn, services) => {
|
|
69
69
|
const module = trx.engine.getModule();
|
|
70
70
|
const db = await BrowserDBService.db(services[service], module);
|
|
71
|
-
const refNames = Object.
|
|
72
|
-
.
|
|
71
|
+
const refNames = Object.entries(module.buckets)
|
|
72
|
+
.filter(([_, val]) => val.adapter instanceof browserdb_bucket_adapter_1.BrowserDBBucketAdapter)
|
|
73
|
+
.map(([key]) => key.includes('::')
|
|
73
74
|
? key
|
|
74
75
|
: `${module.name}::${key}`);
|
|
75
76
|
const trxData = {};
|
|
@@ -17,11 +17,11 @@ export type BucketAdapterConfig = {
|
|
|
17
17
|
* @category Adapters
|
|
18
18
|
* @subcategory Entity
|
|
19
19
|
* */
|
|
20
|
-
export declare abstract class BucketAdapter<Obj extends NesoiObj> {
|
|
20
|
+
export declare abstract class BucketAdapter<Obj extends NesoiObj, Config extends BucketAdapterConfig = BucketAdapterConfig> {
|
|
21
21
|
protected schema: $Bucket;
|
|
22
22
|
nql: NQLRunner;
|
|
23
|
-
config:
|
|
24
|
-
constructor(schema: $Bucket, nql: NQLRunner, config?: Partial<
|
|
23
|
+
config: Config;
|
|
24
|
+
constructor(schema: $Bucket, nql: NQLRunner, config?: Partial<Config>);
|
|
25
25
|
/**
|
|
26
26
|
* **DANGEROUS!**
|
|
27
27
|
* This should only be used on inner adapters of bucket caches.
|
|
@@ -158,4 +158,4 @@ export declare abstract class BucketAdapter<Obj extends NesoiObj> {
|
|
|
158
158
|
*/
|
|
159
159
|
getUpdateEpoch(obj: Obj): number;
|
|
160
160
|
}
|
|
161
|
-
export type AnyBucketAdapter = BucketAdapter<any>;
|
|
161
|
+
export type AnyBucketAdapter = BucketAdapter<any, any>;
|
|
@@ -33,10 +33,23 @@ class MemoryNQLRunner extends nql_engine_1.NQLRunner {
|
|
|
33
33
|
if (part.union.sort?.length) {
|
|
34
34
|
const sort = part.union.sort;
|
|
35
35
|
output.sort((a, b) => {
|
|
36
|
+
let fallback = 0;
|
|
36
37
|
for (let i = 0; i < sort.length; i++) {
|
|
37
38
|
const s = sort[i];
|
|
38
39
|
const a_val = tree_1.Tree.get(a, s.key);
|
|
39
40
|
const b_val = tree_1.Tree.get(b, s.key);
|
|
41
|
+
if (a_val == null) {
|
|
42
|
+
if (b_val == null)
|
|
43
|
+
return 0;
|
|
44
|
+
fallback = -1;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
else if (b_val == null) {
|
|
48
|
+
if (a_val == null)
|
|
49
|
+
return 0;
|
|
50
|
+
fallback = 1;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
40
53
|
if (a_val instanceof datetime_1.NesoiDatetime) {
|
|
41
54
|
let d = 0;
|
|
42
55
|
if (b_val instanceof datetime_1.NesoiDatetime)
|
|
@@ -90,7 +103,7 @@ class MemoryNQLRunner extends nql_engine_1.NQLRunner {
|
|
|
90
103
|
}
|
|
91
104
|
}
|
|
92
105
|
}
|
|
93
|
-
return
|
|
106
|
+
return fallback;
|
|
94
107
|
});
|
|
95
108
|
}
|
|
96
109
|
let totalItems = undefined;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { BucketAdapter, BucketAdapterConfig } from './bucket_adapter';
|
|
2
|
+
import { ObjWithOptionalId } from "../../../../engine/data/obj";
|
|
3
|
+
import { AnyTrxNode } from "../../../../engine/transaction/trx_node";
|
|
4
|
+
import { $Bucket } from "../../..";
|
|
5
|
+
import { BucketCacheSync } from '../cache/bucket_cache';
|
|
6
|
+
export type RESTBucketAdapterConfig = {
|
|
7
|
+
base_url: string;
|
|
8
|
+
auth?: string;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* @category Adapters
|
|
12
|
+
* @subcategory Entity
|
|
13
|
+
* */
|
|
14
|
+
export declare class RESTBucketAdapter<B extends $Bucket, Obj extends B['#data']> extends BucketAdapter<Obj, RESTBucketAdapterConfig & BucketAdapterConfig> {
|
|
15
|
+
schema: B;
|
|
16
|
+
constructor(schema: B, config?: RESTBucketAdapterConfig);
|
|
17
|
+
getQueryMeta(): {
|
|
18
|
+
scope: string;
|
|
19
|
+
avgTime: number;
|
|
20
|
+
};
|
|
21
|
+
fetch(trx: AnyTrxNode, url: string, options?: RequestInit): Promise<{
|
|
22
|
+
data: Obj | Obj[];
|
|
23
|
+
}>;
|
|
24
|
+
protected deleteEverything(trx: AnyTrxNode): Promise<void>;
|
|
25
|
+
index(trx: AnyTrxNode): Promise<Obj[]>;
|
|
26
|
+
get(trx: AnyTrxNode, id: Obj['id']): Promise<Obj | undefined>;
|
|
27
|
+
create(trx: AnyTrxNode, obj: ObjWithOptionalId<Obj>): Promise<Obj>;
|
|
28
|
+
createMany(trx: AnyTrxNode, objs: ObjWithOptionalId<Obj>[]): Promise<Obj[]>;
|
|
29
|
+
replace(trx: AnyTrxNode, obj: ObjWithOptionalId<Obj>): Promise<Obj>;
|
|
30
|
+
replaceMany(trx: AnyTrxNode, objs: ObjWithOptionalId<Obj>[]): Promise<Obj[]>;
|
|
31
|
+
patch(trx: AnyTrxNode, obj: ObjWithOptionalId<Obj>): Promise<Obj>;
|
|
32
|
+
patchMany(trx: AnyTrxNode, objs: ObjWithOptionalId<Obj>[]): Promise<Obj[]>;
|
|
33
|
+
put(trx: AnyTrxNode, obj: ObjWithOptionalId<Obj>): Promise<Obj>;
|
|
34
|
+
putMany(trx: AnyTrxNode, objs: ObjWithOptionalId<Obj>[]): Promise<Obj[]>;
|
|
35
|
+
delete(trx: AnyTrxNode, id: Obj['id']): Promise<void>;
|
|
36
|
+
deleteMany(trx: AnyTrxNode, ids: Obj['id'][]): Promise<void>;
|
|
37
|
+
syncOne(trx: AnyTrxNode, id: Obj['id'], lastObjUpdateEpoch: number): Promise<null | 'deleted' | BucketCacheSync<Obj>>;
|
|
38
|
+
syncOneAndPast(trx: AnyTrxNode, id: Obj['id'], lastUpdateEpoch: number): Promise<null | 'deleted' | BucketCacheSync<Obj>[]>;
|
|
39
|
+
syncAll(trx: AnyTrxNode, lastHash?: string, lastUpdateEpoch?: number): Promise<null | {
|
|
40
|
+
sync: BucketCacheSync<Obj>[];
|
|
41
|
+
hash: string;
|
|
42
|
+
updateEpoch: number;
|
|
43
|
+
reset: boolean;
|
|
44
|
+
}>;
|
|
45
|
+
}
|
|
46
|
+
export type AnyRESTBucketAdapter = RESTBucketAdapter<any, any>;
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RESTBucketAdapter = void 0;
|
|
4
|
+
const bucket_adapter_1 = require("./bucket_adapter");
|
|
5
|
+
const rest_nql_1 = require("./rest.nql");
|
|
6
|
+
const log_1 = require("../../../../engine/util/log");
|
|
7
|
+
/**
|
|
8
|
+
* @category Adapters
|
|
9
|
+
* @subcategory Entity
|
|
10
|
+
* */
|
|
11
|
+
class RESTBucketAdapter extends bucket_adapter_1.BucketAdapter {
|
|
12
|
+
schema;
|
|
13
|
+
constructor(schema, config) {
|
|
14
|
+
const nql = new rest_nql_1.RESTNQLRunner();
|
|
15
|
+
super(schema, nql, config);
|
|
16
|
+
this.schema = schema;
|
|
17
|
+
nql.bind(this);
|
|
18
|
+
}
|
|
19
|
+
getQueryMeta() {
|
|
20
|
+
return {
|
|
21
|
+
scope: `rest.${this.schema.name}`,
|
|
22
|
+
avgTime: 10
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/*
|
|
26
|
+
Fetch
|
|
27
|
+
*/
|
|
28
|
+
async fetch(trx, url, options) {
|
|
29
|
+
const token = this.config.auth
|
|
30
|
+
? await trx.token(this.config.auth)
|
|
31
|
+
: undefined;
|
|
32
|
+
const input = this.config.base_url + url;
|
|
33
|
+
return fetch(input, {
|
|
34
|
+
...options,
|
|
35
|
+
headers: {
|
|
36
|
+
...options?.headers,
|
|
37
|
+
Authorization: token ? `Bearer ${token}` : undefined,
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
.then(async (response) => ({
|
|
41
|
+
status: response.status,
|
|
42
|
+
headers: response.headers,
|
|
43
|
+
body: await response.json().catch(() => null)
|
|
44
|
+
}))
|
|
45
|
+
.then((response) => {
|
|
46
|
+
// if (response.status === 401) {
|
|
47
|
+
// // ...
|
|
48
|
+
// }
|
|
49
|
+
return response.body;
|
|
50
|
+
})
|
|
51
|
+
.catch((e) => {
|
|
52
|
+
log_1.Log.error('rest', '', e, e);
|
|
53
|
+
// ...
|
|
54
|
+
throw e;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
/* Dangerous, used on cache only */
|
|
58
|
+
async deleteEverything(trx) {
|
|
59
|
+
throw new Error('Not implemented');
|
|
60
|
+
}
|
|
61
|
+
/* Read operations */
|
|
62
|
+
async index(trx) {
|
|
63
|
+
const res = await this.fetch(trx, '/', {
|
|
64
|
+
method: 'GET'
|
|
65
|
+
});
|
|
66
|
+
return res.data;
|
|
67
|
+
}
|
|
68
|
+
async get(trx, id) {
|
|
69
|
+
const res = await this.fetch(trx, '/' + id, {
|
|
70
|
+
method: 'GET'
|
|
71
|
+
});
|
|
72
|
+
return res.data;
|
|
73
|
+
}
|
|
74
|
+
/* Write Operations */
|
|
75
|
+
async create(trx, obj) {
|
|
76
|
+
const res = await this.fetch(trx, '/', {
|
|
77
|
+
method: 'POST',
|
|
78
|
+
body: JSON.stringify(obj)
|
|
79
|
+
});
|
|
80
|
+
return res.data;
|
|
81
|
+
}
|
|
82
|
+
async createMany(trx, objs) {
|
|
83
|
+
const out = [];
|
|
84
|
+
for (const obj of objs) {
|
|
85
|
+
out.push(await this.create(trx, obj));
|
|
86
|
+
}
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
async replace(trx, obj) {
|
|
90
|
+
if (!obj.id) {
|
|
91
|
+
throw new Error(`Object with id ${obj.id} not found for replace`);
|
|
92
|
+
}
|
|
93
|
+
const host_obj = await this.get(trx, obj.id);
|
|
94
|
+
if (!host_obj) {
|
|
95
|
+
throw new Error(`Object with id ${obj.id} not found for replace`);
|
|
96
|
+
}
|
|
97
|
+
const res = await this.fetch(trx, '/' + obj.id, {
|
|
98
|
+
method: 'PUT',
|
|
99
|
+
body: JSON.stringify(obj)
|
|
100
|
+
});
|
|
101
|
+
return res.data;
|
|
102
|
+
}
|
|
103
|
+
async replaceMany(trx, objs) {
|
|
104
|
+
const out = [];
|
|
105
|
+
for (const obj of objs) {
|
|
106
|
+
const output = await this.replace(trx, obj);
|
|
107
|
+
out.push(output);
|
|
108
|
+
}
|
|
109
|
+
return Promise.resolve(out);
|
|
110
|
+
}
|
|
111
|
+
async patch(trx, obj) {
|
|
112
|
+
const res = await this.fetch(trx, '/' + obj.id, {
|
|
113
|
+
method: 'PATCH',
|
|
114
|
+
body: JSON.stringify(obj)
|
|
115
|
+
});
|
|
116
|
+
return res.data;
|
|
117
|
+
}
|
|
118
|
+
async patchMany(trx, objs) {
|
|
119
|
+
const out = [];
|
|
120
|
+
for (const obj of objs) {
|
|
121
|
+
const output = await this.patch(trx, obj);
|
|
122
|
+
out.push(output);
|
|
123
|
+
}
|
|
124
|
+
return Promise.resolve(out);
|
|
125
|
+
}
|
|
126
|
+
async put(trx, obj) {
|
|
127
|
+
const res = await this.fetch(trx, '/' + obj.id, {
|
|
128
|
+
method: 'PUT',
|
|
129
|
+
body: JSON.stringify(obj)
|
|
130
|
+
});
|
|
131
|
+
return res.data;
|
|
132
|
+
}
|
|
133
|
+
async putMany(trx, objs) {
|
|
134
|
+
const out = [];
|
|
135
|
+
for (const obj of objs) {
|
|
136
|
+
const output = await this.put(trx, obj);
|
|
137
|
+
out.push(output);
|
|
138
|
+
}
|
|
139
|
+
return Promise.resolve(out);
|
|
140
|
+
}
|
|
141
|
+
async delete(trx, id) {
|
|
142
|
+
await this.fetch(trx, '/' + id, {
|
|
143
|
+
method: 'DELETE'
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
async deleteMany(trx, ids) {
|
|
147
|
+
for (const id of ids) {
|
|
148
|
+
await this.delete(trx, id);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/* Cache Operations */
|
|
152
|
+
async syncOne(trx, id, lastObjUpdateEpoch) {
|
|
153
|
+
// TODO
|
|
154
|
+
return null;
|
|
155
|
+
// // 1. Check if object was deleted
|
|
156
|
+
// const obj = await this.get(trx, id);
|
|
157
|
+
// if (!obj) {
|
|
158
|
+
// return 'deleted' as const;
|
|
159
|
+
// }
|
|
160
|
+
// // 2. Check if object was updated
|
|
161
|
+
// const updateEpoch = this.getUpdateEpoch(obj);
|
|
162
|
+
// const hasObjUpdated = updateEpoch > lastObjUpdateEpoch;
|
|
163
|
+
// if (!hasObjUpdated) {
|
|
164
|
+
// return null;
|
|
165
|
+
// }
|
|
166
|
+
// // 3. Return updated object and epoch
|
|
167
|
+
// return {
|
|
168
|
+
// obj,
|
|
169
|
+
// updateEpoch
|
|
170
|
+
// };
|
|
171
|
+
}
|
|
172
|
+
async syncOneAndPast(trx, id, lastUpdateEpoch) {
|
|
173
|
+
// TODO
|
|
174
|
+
return null;
|
|
175
|
+
// // 1. Check if object was deleted
|
|
176
|
+
// const obj = await this.get(trx, id);
|
|
177
|
+
// if (!obj) {
|
|
178
|
+
// return 'deleted' as const;
|
|
179
|
+
// }
|
|
180
|
+
// // 2. Check if object was updated
|
|
181
|
+
// const objUpdateEpoch = this.getUpdateEpoch(obj);
|
|
182
|
+
// const hasObjUpdated = objUpdateEpoch > lastUpdateEpoch;
|
|
183
|
+
// if (!hasObjUpdated) {
|
|
184
|
+
// return null;
|
|
185
|
+
// }
|
|
186
|
+
// // 3. Return all objects updated and the max epoch
|
|
187
|
+
// let updateEpoch = 0;
|
|
188
|
+
// const changed = (Object.values(this.data) as Obj[])
|
|
189
|
+
// .map(obj => {
|
|
190
|
+
// const epoch = this.getUpdateEpoch(obj);
|
|
191
|
+
// if (epoch > updateEpoch) {
|
|
192
|
+
// updateEpoch = epoch;
|
|
193
|
+
// }
|
|
194
|
+
// return { obj, updateEpoch: epoch };
|
|
195
|
+
// })
|
|
196
|
+
// .filter(obj => obj.updateEpoch > lastUpdateEpoch);
|
|
197
|
+
// if (!changed.length) {
|
|
198
|
+
// return null;
|
|
199
|
+
// }
|
|
200
|
+
// return changed;
|
|
201
|
+
}
|
|
202
|
+
async syncAll(trx, lastHash, lastUpdateEpoch = 0) {
|
|
203
|
+
// TODO
|
|
204
|
+
return null;
|
|
205
|
+
// // 1. Hash the current ids
|
|
206
|
+
// const idStr = Object.keys(this.data).sort().join('');
|
|
207
|
+
// const hash = Hash.string(idStr);
|
|
208
|
+
// // 2. If hash changed, return a reset sync with all objects
|
|
209
|
+
// if (hash !== lastHash) {
|
|
210
|
+
// let updateEpoch = 0;
|
|
211
|
+
// const sync = (Object.values(this.data) as Obj[])
|
|
212
|
+
// .map(obj => {
|
|
213
|
+
// const epoch = this.getUpdateEpoch(obj);
|
|
214
|
+
// if (epoch > updateEpoch) {
|
|
215
|
+
// updateEpoch = epoch;
|
|
216
|
+
// }
|
|
217
|
+
// return { obj, updateEpoch: epoch };
|
|
218
|
+
// });
|
|
219
|
+
// return {
|
|
220
|
+
// sync,
|
|
221
|
+
// hash,
|
|
222
|
+
// updateEpoch,
|
|
223
|
+
// reset: true
|
|
224
|
+
// };
|
|
225
|
+
// }
|
|
226
|
+
// // 3. Find the data that changed and return it
|
|
227
|
+
// let updateEpoch = 0;
|
|
228
|
+
// const sync = (Object.values(this.data) as Obj[])
|
|
229
|
+
// .map(obj => {
|
|
230
|
+
// const epoch = this.getUpdateEpoch(obj);
|
|
231
|
+
// if (epoch > updateEpoch) {
|
|
232
|
+
// updateEpoch = epoch;
|
|
233
|
+
// }
|
|
234
|
+
// return { obj, updateEpoch: epoch };
|
|
235
|
+
// })
|
|
236
|
+
// .filter(obj => obj.updateEpoch > lastUpdateEpoch);
|
|
237
|
+
// if (!sync.length) {
|
|
238
|
+
// return null;
|
|
239
|
+
// }
|
|
240
|
+
// return {
|
|
241
|
+
// sync,
|
|
242
|
+
// hash,
|
|
243
|
+
// updateEpoch,
|
|
244
|
+
// reset: false
|
|
245
|
+
// };
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
exports.RESTBucketAdapter = RESTBucketAdapter;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { NQL_Result, NQLRunner } from '../query/nql_engine';
|
|
2
|
+
import { AnyTrxNode } from "../../../../engine/transaction/trx_node";
|
|
3
|
+
import { NQL_Pagination, NQL_Part } from '../query/nql.schema';
|
|
4
|
+
import { AnyRESTBucketAdapter } from './rest.bucket_adapter';
|
|
5
|
+
type Obj = Record<string, any>;
|
|
6
|
+
/**
|
|
7
|
+
* @category NQL
|
|
8
|
+
* */
|
|
9
|
+
export declare class RESTNQLRunner extends NQLRunner {
|
|
10
|
+
protected adapter?: AnyRESTBucketAdapter;
|
|
11
|
+
constructor();
|
|
12
|
+
bind(adapter: AnyRESTBucketAdapter): void;
|
|
13
|
+
run(trx: AnyTrxNode, part: NQL_Part, params: Obj[], param_templates: Record<string, string>[], pagination?: NQL_Pagination): Promise<NQL_Result>;
|
|
14
|
+
}
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RESTNQLRunner = void 0;
|
|
4
|
+
const nql_engine_1 = require("../query/nql_engine");
|
|
5
|
+
const nql_compiler_1 = require("../query/nql_compiler");
|
|
6
|
+
/**
|
|
7
|
+
* @category NQL
|
|
8
|
+
* */
|
|
9
|
+
class RESTNQLRunner extends nql_engine_1.NQLRunner {
|
|
10
|
+
adapter;
|
|
11
|
+
constructor() {
|
|
12
|
+
super();
|
|
13
|
+
}
|
|
14
|
+
bind(adapter) {
|
|
15
|
+
this.adapter = adapter;
|
|
16
|
+
}
|
|
17
|
+
async run(trx, part, params, param_templates, pagination) {
|
|
18
|
+
if (!this.adapter) {
|
|
19
|
+
throw new Error('No adapter bound to NQL Runner');
|
|
20
|
+
}
|
|
21
|
+
const query = nql_compiler_1.NQL_Decompiler.decompile(part, params, param_templates);
|
|
22
|
+
const res = await this.adapter.fetch(trx, '/query', {
|
|
23
|
+
method: 'POST',
|
|
24
|
+
body: JSON.stringify({
|
|
25
|
+
query,
|
|
26
|
+
page: pagination?.page,
|
|
27
|
+
perPage: pagination?.perPage,
|
|
28
|
+
})
|
|
29
|
+
});
|
|
30
|
+
return res.data;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.RESTNQLRunner = RESTNQLRunner;
|
|
@@ -18,16 +18,16 @@ export type BucketConfig<M extends $Module, B extends $Bucket, Services extends
|
|
|
18
18
|
* - **past**: Update/delete the object and update all objects modified before it, then return
|
|
19
19
|
* - **all**: Update/reset the cache, then return
|
|
20
20
|
*/
|
|
21
|
-
get?: 'one' | 'past' | 'all';
|
|
21
|
+
get?: 'eager' | 'one' | 'past' | 'all';
|
|
22
22
|
/** Cache mode for `index`:
|
|
23
23
|
* - **all**: Update/reset the cache, then return
|
|
24
24
|
*/
|
|
25
|
-
index?: 'all';
|
|
25
|
+
index?: 'eager' | 'all';
|
|
26
26
|
/** Cache mode for `query`:
|
|
27
27
|
* - **incremental**: Query ids only, then query data for modified entries only, save them and return
|
|
28
28
|
* - **all**: Update/reset the cache, then query the inner adapter and return
|
|
29
29
|
*/
|
|
30
|
-
query?: 'incremental' | 'all';
|
|
30
|
+
query?: 'eager' | 'incremental' | 'all';
|
|
31
31
|
};
|
|
32
32
|
};
|
|
33
33
|
};
|
|
@@ -13,6 +13,7 @@ const tree_1 = require("../../../engine/data/tree");
|
|
|
13
13
|
const crypto_1 = require("../../../engine/util/crypto");
|
|
14
14
|
const bucket_model_schema_1 = require("./model/bucket_model.schema");
|
|
15
15
|
const trash_1 = require("../../../engine/data/trash");
|
|
16
|
+
const trx_1 = require("../../../engine/transaction/trx");
|
|
16
17
|
/**
|
|
17
18
|
* **This should only be used inside a `#composition` of a bucket `create`** to refer to the parent id, which doesn't exist yet.
|
|
18
19
|
*
|
|
@@ -88,7 +89,7 @@ class Bucket {
|
|
|
88
89
|
: this.getTenancyQuery(trx);
|
|
89
90
|
let raw;
|
|
90
91
|
// With Tenancy
|
|
91
|
-
const adapter = this.cache || this.adapter;
|
|
92
|
+
const adapter = await trx_1.Trx.getCache(trx, this) || this.cache || this.adapter;
|
|
92
93
|
if (tenancy) {
|
|
93
94
|
const result = await adapter.query(trx, {
|
|
94
95
|
id,
|
|
@@ -127,9 +128,9 @@ class Bucket {
|
|
|
127
128
|
: this.getTenancyQuery(trx);
|
|
128
129
|
let raws;
|
|
129
130
|
// With Tenancy
|
|
130
|
-
const adapter = this.cache || this.adapter;
|
|
131
|
+
const adapter = await trx_1.Trx.getCache(trx, this) || this.cache || this.adapter;
|
|
131
132
|
if (tenancy) {
|
|
132
|
-
const result = await adapter.query(trx, tenancy, undefined, options?.query_view ?
|
|
133
|
+
const result = await adapter.query(trx, tenancy, undefined, undefined, undefined, options?.query_view ? { view: options?.query_view } : undefined);
|
|
133
134
|
raws = result.data;
|
|
134
135
|
}
|
|
135
136
|
// Without Tenancy
|
|
@@ -432,7 +433,7 @@ class Bucket {
|
|
|
432
433
|
// Read old object, if safe, to check if it exists
|
|
433
434
|
let oldObj;
|
|
434
435
|
if (!options?.unsafe) {
|
|
435
|
-
const adapter = this.cache || this.adapter;
|
|
436
|
+
const adapter = await trx_1.Trx.getCache(trx, this) || this.cache || this.adapter;
|
|
436
437
|
// With Tenancy
|
|
437
438
|
if (tenancy) {
|
|
438
439
|
const result = await adapter.query(trx, {
|
|
@@ -620,7 +621,7 @@ class Bucket {
|
|
|
620
621
|
await trx.bucket(link.bucket.short).unsafe.deleteMany(linked.map((l) => l.id));
|
|
621
622
|
}
|
|
622
623
|
else {
|
|
623
|
-
await trx.bucket(link.bucket.short).delete(linked.id);
|
|
624
|
+
await trx.bucket(link.bucket.short).unsafe.delete(linked.id);
|
|
624
625
|
}
|
|
625
626
|
}
|
|
626
627
|
// Delete the object itself
|
|
@@ -727,7 +728,7 @@ class Bucket {
|
|
|
727
728
|
pagination.perPage = undefined;
|
|
728
729
|
}
|
|
729
730
|
// Query
|
|
730
|
-
const adapter = this.cache || this.adapter;
|
|
731
|
+
const adapter = await trx_1.Trx.getCache(trx, this) || this.cache || this.adapter;
|
|
731
732
|
const result = await adapter.query(trx, query, pagination, options?.params, options?.param_templates);
|
|
732
733
|
if (!result.data.length)
|
|
733
734
|
return result;
|
|
@@ -32,17 +32,50 @@ export declare class BucketCache<Obj extends NesoiObj> {
|
|
|
32
32
|
private outerAdapter;
|
|
33
33
|
constructor(bucket: AnyBucket, config: NonNullable<BucketConfig<any, any, any>['cache']>);
|
|
34
34
|
get(trx: AnyTrxNode, id: NesoiObj['id']): Promise<any>;
|
|
35
|
-
index(trx: AnyTrxNode): Promise<
|
|
36
|
-
|
|
35
|
+
index(trx: AnyTrxNode): Promise<{
|
|
36
|
+
id: Obj["id"];
|
|
37
|
+
}[]>;
|
|
38
|
+
query<MetadataOnly extends boolean>(trx: AnyTrxNode, query: NQL_AnyQuery, pagination?: NQL_Pagination, params?: Record<string, any>[], param_templates?: Record<string, string>[], config?: {
|
|
37
39
|
view?: string;
|
|
38
40
|
metadataOnly?: MetadataOnly;
|
|
39
41
|
}): Promise<NQL_Result<MetadataOnly extends true ? {
|
|
40
42
|
id: Obj['id'];
|
|
41
43
|
[x: string]: any;
|
|
42
44
|
} : Obj>>;
|
|
45
|
+
/**
|
|
46
|
+
* Update inner adapter with data from outer adapter.
|
|
47
|
+
*/
|
|
48
|
+
sync(trx: AnyTrxNode): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* If doesnt exist on inner adapter, read.
|
|
51
|
+
* If it exists, check if it's updated (query metadata).
|
|
52
|
+
*
|
|
53
|
+
* [get.one]
|
|
54
|
+
* - reduces transit payload for data that doesn't change much
|
|
55
|
+
*/
|
|
43
56
|
private syncOne;
|
|
57
|
+
/**
|
|
58
|
+
* If doesnt exist on inner adapter, read.
|
|
59
|
+
* If it exists, read past.
|
|
60
|
+
*
|
|
61
|
+
* [get.past]
|
|
62
|
+
* - reduces transit payload for data that changes a little
|
|
63
|
+
*/
|
|
44
64
|
private syncOneAndPast;
|
|
65
|
+
/**
|
|
66
|
+
* Compare ids hash with outer. If it matches, read updated data.
|
|
67
|
+
* If not, hard resync.
|
|
68
|
+
*
|
|
69
|
+
* [get.all, index.all, query.all]
|
|
70
|
+
* - reduces transit payload for data that's not often deleted
|
|
71
|
+
*/
|
|
45
72
|
private syncAll;
|
|
73
|
+
/**
|
|
74
|
+
* Query metadata from outer, then query data from outer - only newer than inner data.
|
|
75
|
+
*
|
|
76
|
+
* [query.incremental]
|
|
77
|
+
* - reduces transit payload for data that doesn't change much
|
|
78
|
+
*/
|
|
46
79
|
private syncQuery;
|
|
47
80
|
}
|
|
48
81
|
export type AnyBucketCache = BucketCache<any>;
|
|
@@ -47,6 +47,14 @@ class BucketCache {
|
|
|
47
47
|
}
|
|
48
48
|
async get(trx, id) {
|
|
49
49
|
const mode = this.config?.mode?.get;
|
|
50
|
+
if (mode === 'eager') {
|
|
51
|
+
log_1.Log.debug('bucket', this.bucket.schema.name, `CACHE get.eager, ${id}`);
|
|
52
|
+
const sync = await this.innerAdapter.get(trx, id);
|
|
53
|
+
if (!sync)
|
|
54
|
+
return undefined;
|
|
55
|
+
const { __update_epoch, __sync_epoch, ...obj } = sync;
|
|
56
|
+
return obj;
|
|
57
|
+
}
|
|
50
58
|
if (mode === 'one') {
|
|
51
59
|
const { action, sync } = await this.syncOne(trx, id);
|
|
52
60
|
log_1.Log.debug('bucket', this.bucket.schema.name, `CACHE get.one, ${action}`);
|
|
@@ -76,21 +84,33 @@ class BucketCache {
|
|
|
76
84
|
}
|
|
77
85
|
async index(trx) {
|
|
78
86
|
const mode = this.config?.mode?.index;
|
|
79
|
-
|
|
87
|
+
let data;
|
|
88
|
+
if (mode === 'eager') {
|
|
89
|
+
log_1.Log.debug('bucket', this.bucket.schema.name, 'CACHE index.eager');
|
|
90
|
+
data = await this.innerAdapter.index(trx);
|
|
91
|
+
}
|
|
92
|
+
else if (mode === 'all') {
|
|
80
93
|
const { action, sync } = await this.syncAll(trx);
|
|
81
94
|
log_1.Log.debug('bucket', this.bucket.schema.name, `CACHE index.all, ${action}`);
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
return data;
|
|
95
|
+
data = sync;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
throw new Error(`Invalid index cache mode '${mode}'`);
|
|
88
99
|
}
|
|
89
|
-
|
|
100
|
+
const out = [];
|
|
101
|
+
for (const e of data) {
|
|
102
|
+
const { __update_epoch, __sync_epoch, ...obj } = e;
|
|
103
|
+
out.push(obj);
|
|
104
|
+
}
|
|
105
|
+
return out;
|
|
90
106
|
}
|
|
91
|
-
async query(trx, query, pagination, params, config) {
|
|
107
|
+
async query(trx, query, pagination, params, param_templates, config) {
|
|
92
108
|
const mode = this.config?.mode?.query;
|
|
93
109
|
let data;
|
|
110
|
+
if (mode === 'eager') {
|
|
111
|
+
log_1.Log.debug('bucket', this.bucket.schema.name, 'CACHE index.eager');
|
|
112
|
+
data = await this.innerAdapter.query(trx, query, pagination, params, param_templates, config);
|
|
113
|
+
}
|
|
94
114
|
if (mode === 'incremental') {
|
|
95
115
|
const { action, sync } = await this.syncQuery(trx, query, pagination, params);
|
|
96
116
|
log_1.Log.debug('bucket', this.bucket.schema.name, `CACHE query.incremental, ${action}`);
|
|
@@ -125,6 +145,22 @@ class BucketCache {
|
|
|
125
145
|
return { data };
|
|
126
146
|
}
|
|
127
147
|
/* Cache modes */
|
|
148
|
+
/**
|
|
149
|
+
* Update inner adapter with data from outer adapter.
|
|
150
|
+
*/
|
|
151
|
+
async sync(trx) {
|
|
152
|
+
const objects = await this.outerAdapter.index(trx);
|
|
153
|
+
const entries = objects.map(obj => new BucketCacheEntry(obj, this.outerAdapter.getUpdateEpoch(obj), this.lastSyncEpoch));
|
|
154
|
+
await this.innerAdapter.deleteEverything(trx);
|
|
155
|
+
await this.innerAdapter.putMany(trx, entries);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* If doesnt exist on inner adapter, read.
|
|
159
|
+
* If it exists, check if it's updated (query metadata).
|
|
160
|
+
*
|
|
161
|
+
* [get.one]
|
|
162
|
+
* - reduces transit payload for data that doesn't change much
|
|
163
|
+
*/
|
|
128
164
|
async syncOne(trx, id) {
|
|
129
165
|
let localObj = await this.innerAdapter.get(trx, id);
|
|
130
166
|
if (!localObj) {
|
|
@@ -148,6 +184,13 @@ class BucketCache {
|
|
|
148
184
|
await this.innerAdapter.put(trx, localObj);
|
|
149
185
|
return { action: 'update', sync: localObj };
|
|
150
186
|
}
|
|
187
|
+
/**
|
|
188
|
+
* If doesnt exist on inner adapter, read.
|
|
189
|
+
* If it exists, read past.
|
|
190
|
+
*
|
|
191
|
+
* [get.past]
|
|
192
|
+
* - reduces transit payload for data that changes a little
|
|
193
|
+
*/
|
|
151
194
|
async syncOneAndPast(trx, id) {
|
|
152
195
|
const localObj = await this.innerAdapter.get(trx, id);
|
|
153
196
|
if (!localObj) {
|
|
@@ -170,6 +213,13 @@ class BucketCache {
|
|
|
170
213
|
await this.innerAdapter.putMany(trx, entries);
|
|
171
214
|
return { action: 'update', sync: entries.find(e => e.id === id) };
|
|
172
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* Compare ids hash with outer. If it matches, read updated data.
|
|
218
|
+
* If not, hard resync.
|
|
219
|
+
*
|
|
220
|
+
* [get.all, index.all, query.all]
|
|
221
|
+
* - reduces transit payload for data that's not often deleted
|
|
222
|
+
*/
|
|
173
223
|
async syncAll(trx) {
|
|
174
224
|
const sync = await this.outerAdapter.syncAll(trx, this.lastHash, this.lastUpdateEpoch);
|
|
175
225
|
if (sync === null) {
|
|
@@ -188,6 +238,12 @@ class BucketCache {
|
|
|
188
238
|
await this.innerAdapter.putMany(trx, entries);
|
|
189
239
|
return { action: 'update', sync: entries };
|
|
190
240
|
}
|
|
241
|
+
/**
|
|
242
|
+
* Query metadata from outer, then query data from outer - only newer than inner data.
|
|
243
|
+
*
|
|
244
|
+
* [query.incremental]
|
|
245
|
+
* - reduces transit payload for data that doesn't change much
|
|
246
|
+
*/
|
|
191
247
|
async syncQuery(trx, query, pagination, params) {
|
|
192
248
|
// 1. Query id and epoch from outer adapter
|
|
193
249
|
const outerMetadata = await this.outerAdapter.query(trx, query, pagination, params, undefined, {
|