nesoi 3.3.6 → 3.3.8
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/bundler/distributed/stages/6_dump_cli_stage.js +0 -1
- package/lib/bundler/monolyth/stages/6_dump_cli_stage.js +0 -1
- package/lib/elements/blocks/resource/resource.builder.js +1 -2
- package/lib/elements/blocks/resource/resource.js +10 -0
- package/lib/elements/entities/bucket/adapters/bucket_adapter.d.ts +5 -1
- package/lib/elements/entities/bucket/adapters/bucket_adapter.js +6 -2
- package/lib/elements/entities/bucket/adapters/memory.nql.d.ts +1 -1
- package/lib/elements/entities/bucket/adapters/memory.nql.js +42 -42
- package/lib/elements/entities/bucket/bucket.d.ts +1 -1
- package/lib/elements/entities/bucket/bucket.js +6 -1
- package/lib/elements/entities/bucket/graph/bucket_graph.d.ts +1 -1
- package/lib/elements/entities/bucket/graph/bucket_graph.js +8 -8
- package/lib/elements/entities/bucket/query/nql.schema.d.ts +7 -10
- package/lib/elements/entities/bucket/query/nql_compiler.d.ts +1 -1
- package/lib/elements/entities/bucket/query/nql_compiler.js +34 -28
- package/lib/elements/entities/bucket/query/nql_engine.d.ts +2 -2
- package/lib/elements/entities/bucket/query/nql_engine.js +4 -2
- package/lib/engine/app/app.config.d.ts +7 -1
- package/lib/engine/app/app.config.js +20 -0
- package/lib/engine/app/app.d.ts +3 -1
- package/lib/engine/app/inline.app.d.ts +8 -2
- package/lib/engine/app/inline.app.js +33 -34
- package/lib/engine/app/native/browser.app.d.ts +1 -0
- package/lib/engine/app/native/browser.app.js +1 -1
- package/lib/engine/app/native/distributed_node.app.js +1 -6
- package/lib/engine/app/native/monolyth.app.d.ts +1 -0
- package/lib/engine/app/native/monolyth.app.js +1 -1
- package/lib/engine/app/service.d.ts +4 -10
- package/lib/engine/app/service.js +1 -3
- package/lib/engine/cli/script.d.ts +97 -0
- package/lib/engine/cli/script.js +422 -0
- package/lib/engine/transaction/nodes/bucket_query.trx_node.d.ts +2 -2
- package/lib/engine/transaction/nodes/bucket_query.trx_node.js +8 -8
- package/lib/engine/util/dotenv.d.ts +5 -6
- package/lib/engine/util/dotenv.js +14 -12
- package/package.json +1 -1
- package/tsconfig.build.tsbuildinfo +1 -1
- package/tools/dotenv.d.ts +0 -1
- package/tools/dotenv.js +0 -4
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
// Log.info('compiler', 'monolyth', 'Dumping cli.js file to build/bin folder...')
|
|
21
21
|
// const { dirs } = this.monolyth;
|
|
22
22
|
// let str = '';
|
|
23
|
-
// str += 'require("nesoi/tools/dotenv");\n';
|
|
24
23
|
// str += 'const app = require(\'../app\').default\n';
|
|
25
24
|
// str += 'const { Log } = require(\'nesoi/lib/engine/util/log\');\n';
|
|
26
25
|
// str += 'Log.level = \'debug\';\n'
|
|
@@ -53,7 +53,6 @@ class DumpCLIStage {
|
|
|
53
53
|
log_1.Log.info('compiler', 'monolyth', 'Dumping cli.js file to build/bin folder...');
|
|
54
54
|
const { dirs } = this.bundler;
|
|
55
55
|
let str = '';
|
|
56
|
-
str += 'require("nesoi/tools/dotenv");\n';
|
|
57
56
|
str += 'const app = require(\'../app\').default\n';
|
|
58
57
|
str += 'const { Log } = require(\'nesoi/lib/engine/util/log\');\n';
|
|
59
58
|
str += 'Log.level = \'debug\';\n';
|
|
@@ -42,8 +42,7 @@ class ResourceBuilder extends block_builder_1.BlockBuilder {
|
|
|
42
42
|
id: $.string_or_number.optional,
|
|
43
43
|
perPage: $.int.default(10),
|
|
44
44
|
page: $.int.default(0),
|
|
45
|
-
|
|
46
|
-
orderDesc: $.boolean.default(false)
|
|
45
|
+
sort: $.list($.literal(/.@(asc|desc)/)).optional
|
|
47
46
|
}))
|
|
48
47
|
.prepare(resource_job_1.ResourceJob.prepareMsgData);
|
|
49
48
|
this._inlineNodes.push(new dependency_1.BuilderNode({
|
|
@@ -4,6 +4,8 @@ exports.Resource = void 0;
|
|
|
4
4
|
const block_1 = require("../block");
|
|
5
5
|
const trx_node_1 = require("../../../engine/transaction/trx_node");
|
|
6
6
|
const error_1 = require("../../../engine/data/error");
|
|
7
|
+
const dependency_1 = require("../../../engine/dependency");
|
|
8
|
+
const daemon_1 = require("../../../engine/daemon");
|
|
7
9
|
/**
|
|
8
10
|
* @category Elements
|
|
9
11
|
* @subcategory Block
|
|
@@ -76,12 +78,20 @@ class Resource extends block_1.Block {
|
|
|
76
78
|
/* Implementations */
|
|
77
79
|
static view($) {
|
|
78
80
|
const scope = $.job.scope;
|
|
81
|
+
// TODO: sort
|
|
79
82
|
return $.msg.id
|
|
80
83
|
? $.trx.bucket(scope.bucket).viewOneOrFail($.msg.id, $.msg.view)
|
|
81
84
|
: $.trx.bucket(scope.bucket).viewAll($.msg.view);
|
|
82
85
|
}
|
|
83
86
|
static query($) {
|
|
84
87
|
const scope = $.job.scope;
|
|
88
|
+
// Default sorting
|
|
89
|
+
if (!('#sort' in $.msg.query) || !$.msg.query['#sort']?.length) {
|
|
90
|
+
const module = trx_node_1.TrxNode.getModule($.trx);
|
|
91
|
+
const tag = dependency_1.Tag.fromNameOrShort(scope.module, 'bucket', scope.bucket);
|
|
92
|
+
const { meta } = daemon_1.Daemon.getBucketMetadata(module.daemon, tag);
|
|
93
|
+
$.msg.query['#sort'] = `${meta.updated_at}@desc`;
|
|
94
|
+
}
|
|
85
95
|
return $.trx.bucket(scope.bucket)
|
|
86
96
|
.viewQuery($.msg.query, $.msg.view).page({
|
|
87
97
|
page: $.msg.page,
|
|
@@ -136,8 +136,12 @@ export declare abstract class BucketAdapter<Obj extends NesoiObj> {
|
|
|
136
136
|
};
|
|
137
137
|
/**
|
|
138
138
|
* Return the results of a query
|
|
139
|
+
* - `pagination`: Limits the number of results.
|
|
140
|
+
* - `perPage`: If 0, returns no results (useful with config.metadataOnly). If -1, returns all results.
|
|
141
|
+
* - `params`: Objects to be used when filling param values. The query returns objects matching *any* of the param objects.
|
|
142
|
+
* - `param_templates`: Path parameter replacements to be used when filling param_with_$ values. The query returns objects matching *any* of the param objects.
|
|
139
143
|
*/
|
|
140
|
-
query<MetadataOnly extends boolean>(trx: AnyTrxNode, query: NQL_AnyQuery, pagination?: NQL_Pagination, params?: Record<string, any>[],
|
|
144
|
+
query<MetadataOnly extends boolean>(trx: AnyTrxNode, query: NQL_AnyQuery, pagination?: NQL_Pagination, params?: Record<string, any>[], param_templates?: Record<string, string>[], config?: {
|
|
141
145
|
view?: string;
|
|
142
146
|
metadataOnly?: MetadataOnly;
|
|
143
147
|
}, custom?: {
|
|
@@ -24,8 +24,12 @@ class BucketAdapter {
|
|
|
24
24
|
/* Generic Implementation */
|
|
25
25
|
/**
|
|
26
26
|
* Return the results of a query
|
|
27
|
+
* - `pagination`: Limits the number of results.
|
|
28
|
+
* - `perPage`: If 0, returns no results (useful with config.metadataOnly). If -1, returns all results.
|
|
29
|
+
* - `params`: Objects to be used when filling param values. The query returns objects matching *any* of the param objects.
|
|
30
|
+
* - `param_templates`: Path parameter replacements to be used when filling param_with_$ values. The query returns objects matching *any* of the param objects.
|
|
27
31
|
*/
|
|
28
|
-
async query(trx, query, pagination, params,
|
|
32
|
+
async query(trx, query, pagination, params, param_templates, config,
|
|
29
33
|
// When running a temporary local memory adapter,
|
|
30
34
|
// these are required
|
|
31
35
|
custom) {
|
|
@@ -33,7 +37,7 @@ class BucketAdapter {
|
|
|
33
37
|
const moduleName = custom?.module || module.name;
|
|
34
38
|
const compiled = await nql_compiler_1.NQL_Compiler.build(module.daemon, moduleName, this.schema.name, query, custom?.metadata);
|
|
35
39
|
const view = config?.view ? this.schema.views[config.view] : undefined;
|
|
36
|
-
const result = await module.nql.run(trx, compiled, pagination, params,
|
|
40
|
+
const result = await module.nql.run(trx, compiled, pagination, params, param_templates, view, custom?.runners);
|
|
37
41
|
if (config?.metadataOnly) {
|
|
38
42
|
result.data = result.data.map(obj => ({
|
|
39
43
|
id: obj.id,
|
|
@@ -10,7 +10,7 @@ export declare class MemoryNQLRunner extends NQLRunner {
|
|
|
10
10
|
protected adapter?: AnyMemoryBucketAdapter;
|
|
11
11
|
constructor();
|
|
12
12
|
bind(adapter: AnyMemoryBucketAdapter): void;
|
|
13
|
-
run(trx: AnyTrxNode, part: NQL_Part, params: Obj[],
|
|
13
|
+
run(trx: AnyTrxNode, part: NQL_Part, params: Obj[], param_templates: Record<string, string>[], pagination?: NQL_Pagination): Promise<{
|
|
14
14
|
data: any[];
|
|
15
15
|
totalItems: number | undefined;
|
|
16
16
|
page: number | undefined;
|
|
@@ -13,7 +13,7 @@ class MemoryNQLRunner extends nql_engine_1.NQLRunner {
|
|
|
13
13
|
bind(adapter) {
|
|
14
14
|
this.adapter = adapter;
|
|
15
15
|
}
|
|
16
|
-
async run(trx, part, params,
|
|
16
|
+
async run(trx, part, params, param_templates, pagination) {
|
|
17
17
|
if (!this.adapter) {
|
|
18
18
|
throw new Error('No adapter bound to NQL Runner');
|
|
19
19
|
}
|
|
@@ -25,33 +25,33 @@ class MemoryNQLRunner extends nql_engine_1.NQLRunner {
|
|
|
25
25
|
}
|
|
26
26
|
// Non-empty query
|
|
27
27
|
else {
|
|
28
|
-
response = await this.filter(part, data, params,
|
|
28
|
+
response = await this.filter(part, data, params, param_templates);
|
|
29
29
|
}
|
|
30
30
|
let output = Object.values(response);
|
|
31
|
-
if (part.union.
|
|
32
|
-
const
|
|
33
|
-
? part.union.order.by
|
|
34
|
-
: [this.adapter.config.meta.updated_at];
|
|
31
|
+
if (part.union.sort?.length) {
|
|
32
|
+
const sort = part.union.sort;
|
|
35
33
|
output.sort((a, b) => {
|
|
36
|
-
for (let i = 0; i <
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
34
|
+
for (let i = 0; i < sort.length; i++) {
|
|
35
|
+
const s = sort[i];
|
|
36
|
+
const a_val = tree_1.Tree.get(a, s.key);
|
|
37
|
+
const b_val = tree_1.Tree.get(b, s.key);
|
|
38
|
+
if (typeof a_val == 'number') {
|
|
39
|
+
if (typeof b_val !== 'number')
|
|
40
|
+
throw new Error(`Cannot compare number and ${typeof b_val}`);
|
|
41
|
+
let d = a_val - b_val;
|
|
42
42
|
if (d !== 0) {
|
|
43
|
-
if (
|
|
43
|
+
if (s.dir === 'desc') {
|
|
44
44
|
d *= -1;
|
|
45
45
|
}
|
|
46
46
|
return d;
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
|
-
else if (typeof
|
|
50
|
-
if (typeof
|
|
51
|
-
throw new Error(`Cannot compare string and ${typeof
|
|
52
|
-
let d =
|
|
49
|
+
else if (typeof a_val == 'string') {
|
|
50
|
+
if (typeof b_val !== 'string')
|
|
51
|
+
throw new Error(`Cannot compare string and ${typeof b_val}`);
|
|
52
|
+
let d = a_val.localeCompare(b_val);
|
|
53
53
|
if (d !== 0) {
|
|
54
|
-
if (
|
|
54
|
+
if (s.dir === 'desc') {
|
|
55
55
|
d *= -1;
|
|
56
56
|
}
|
|
57
57
|
return d;
|
|
@@ -67,8 +67,8 @@ class MemoryNQLRunner extends nql_engine_1.NQLRunner {
|
|
|
67
67
|
totalItems = output.length;
|
|
68
68
|
}
|
|
69
69
|
if (pagination.page !== undefined || pagination.perPage !== undefined) {
|
|
70
|
-
const a = ((pagination.page || 1) - 1) * (pagination.perPage
|
|
71
|
-
const b = a + (pagination.perPage
|
|
70
|
+
const a = ((pagination.page || 1) - 1) * (pagination.perPage ?? 10);
|
|
71
|
+
const b = a + (pagination.perPage ?? 10);
|
|
72
72
|
output = output.slice(a, b);
|
|
73
73
|
}
|
|
74
74
|
}
|
|
@@ -84,16 +84,16 @@ class MemoryNQLRunner extends nql_engine_1.NQLRunner {
|
|
|
84
84
|
* testing objects unnecessarily. Returns a dict of results by id.
|
|
85
85
|
* @returns A dict of results by id
|
|
86
86
|
*/
|
|
87
|
-
filter(part, objs, params,
|
|
87
|
+
filter(part, objs, params, param_templates) {
|
|
88
88
|
// Accumulate results from n intersections,
|
|
89
89
|
// avoiding a re-check of already matched objects.
|
|
90
|
-
const _union = (union, objs, params,
|
|
90
|
+
const _union = (union, objs, params, param_templates) => {
|
|
91
91
|
const out = {};
|
|
92
92
|
const remaining = { ...objs };
|
|
93
93
|
for (const inter of union.inters) {
|
|
94
94
|
if (Object.keys(remaining).length === 0)
|
|
95
95
|
break;
|
|
96
|
-
const interOut = _inter(inter, remaining, params,
|
|
96
|
+
const interOut = _inter(inter, remaining, params, param_templates);
|
|
97
97
|
Object.assign(out, interOut);
|
|
98
98
|
for (const k in interOut) {
|
|
99
99
|
delete remaining[k];
|
|
@@ -103,7 +103,7 @@ class MemoryNQLRunner extends nql_engine_1.NQLRunner {
|
|
|
103
103
|
};
|
|
104
104
|
// Sieves results from n unions or rules,
|
|
105
105
|
// avoiding a re-check of already filtered-out objects.
|
|
106
|
-
const _inter = (inter, objs, params,
|
|
106
|
+
const _inter = (inter, objs, params, param_templates) => {
|
|
107
107
|
let out = {};
|
|
108
108
|
const remaining = { ...objs };
|
|
109
109
|
for (const rule of inter.rules) {
|
|
@@ -111,11 +111,11 @@ class MemoryNQLRunner extends nql_engine_1.NQLRunner {
|
|
|
111
111
|
break;
|
|
112
112
|
// <Union>
|
|
113
113
|
if ('inters' in rule) {
|
|
114
|
-
out = _union(rule, remaining, params,
|
|
114
|
+
out = _union(rule, remaining, params, param_templates);
|
|
115
115
|
}
|
|
116
116
|
// <Rule>
|
|
117
117
|
else {
|
|
118
|
-
out = _rule(rule, remaining, params,
|
|
118
|
+
out = _rule(rule, remaining, params, param_templates);
|
|
119
119
|
}
|
|
120
120
|
for (const k in remaining) {
|
|
121
121
|
if (!(k in out)) {
|
|
@@ -125,24 +125,24 @@ class MemoryNQLRunner extends nql_engine_1.NQLRunner {
|
|
|
125
125
|
}
|
|
126
126
|
return out;
|
|
127
127
|
};
|
|
128
|
-
const _rule = (rule, objs, params,
|
|
128
|
+
const _rule = (rule, objs, params, param_templates) => {
|
|
129
129
|
const out = {};
|
|
130
130
|
for (const id in objs) {
|
|
131
131
|
const obj = objs[id];
|
|
132
132
|
let match = false;
|
|
133
133
|
const combos = [];
|
|
134
134
|
for (const param of params) {
|
|
135
|
-
if (
|
|
136
|
-
for (const
|
|
137
|
-
combos.push({ params: param,
|
|
135
|
+
if (param_templates.length) {
|
|
136
|
+
for (const template of param_templates) {
|
|
137
|
+
combos.push({ params: param, param_template: template });
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
else {
|
|
141
|
-
combos.push({ params: param,
|
|
141
|
+
combos.push({ params: param, param_template: {} });
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
144
|
for (const combo of combos) {
|
|
145
|
-
match = _obj(rule, obj, combo.params, combo.
|
|
145
|
+
match = _obj(rule, obj, combo.params, combo.param_template);
|
|
146
146
|
if (match)
|
|
147
147
|
break;
|
|
148
148
|
}
|
|
@@ -160,7 +160,7 @@ class MemoryNQLRunner extends nql_engine_1.NQLRunner {
|
|
|
160
160
|
}
|
|
161
161
|
return out;
|
|
162
162
|
};
|
|
163
|
-
const _obj = (rule, obj, params,
|
|
163
|
+
const _obj = (rule, obj, params, param_template) => {
|
|
164
164
|
const fieldValue = tree_1.Tree.get(obj, rule.fieldpath);
|
|
165
165
|
// Value is undefined, only 'present' rule applies
|
|
166
166
|
if (fieldValue === undefined) {
|
|
@@ -172,7 +172,7 @@ class MemoryNQLRunner extends nql_engine_1.NQLRunner {
|
|
|
172
172
|
// Fieldpath is a spread, apply rule to each item
|
|
173
173
|
if (rule.fieldpath.includes('.#')) {
|
|
174
174
|
for (const item of fieldValue) {
|
|
175
|
-
if (_obj(rule, item, params,
|
|
175
|
+
if (_obj(rule, item, params, param_template))
|
|
176
176
|
return true;
|
|
177
177
|
}
|
|
178
178
|
return false;
|
|
@@ -180,7 +180,7 @@ class MemoryNQLRunner extends nql_engine_1.NQLRunner {
|
|
|
180
180
|
let queryValue;
|
|
181
181
|
// Value is a subquery, run union
|
|
182
182
|
if ('subquery' in rule.value) {
|
|
183
|
-
const subOut = _union(rule.value.subquery.union, objs, [params], Object.keys(
|
|
183
|
+
const subOut = _union(rule.value.subquery.union, objs, [params], Object.keys(param_template).length ? [param_template] : []);
|
|
184
184
|
const subList = Object.values(subOut);
|
|
185
185
|
// Subquery operator is for a list, filter
|
|
186
186
|
if (rule.op === 'in' || rule.op === 'contains_any') {
|
|
@@ -193,16 +193,16 @@ class MemoryNQLRunner extends nql_engine_1.NQLRunner {
|
|
|
193
193
|
}
|
|
194
194
|
else if ('param' in rule.value) {
|
|
195
195
|
if (Array.isArray(rule.value.param)) {
|
|
196
|
-
queryValue = rule.value.param.map(p => params
|
|
196
|
+
queryValue = rule.value.param.map(p => tree_1.Tree.get(params, p));
|
|
197
197
|
}
|
|
198
198
|
else {
|
|
199
|
-
queryValue = params
|
|
199
|
+
queryValue = tree_1.Tree.get(params, rule.value.param);
|
|
200
200
|
}
|
|
201
201
|
}
|
|
202
|
-
else if ('
|
|
203
|
-
let path = rule.value.
|
|
204
|
-
for (const key in
|
|
205
|
-
path = path.replace(new RegExp(key, 'g'),
|
|
202
|
+
else if ('param_with_$' in rule.value) {
|
|
203
|
+
let path = rule.value.param_with_$;
|
|
204
|
+
for (const key in param_template) {
|
|
205
|
+
path = path.replace(new RegExp(key.replace('$', '\\$'), 'g'), param_template[key]);
|
|
206
206
|
}
|
|
207
207
|
queryValue = tree_1.Tree.get(params, path);
|
|
208
208
|
}
|
|
@@ -296,7 +296,7 @@ class MemoryNQLRunner extends nql_engine_1.NQLRunner {
|
|
|
296
296
|
}
|
|
297
297
|
return false;
|
|
298
298
|
};
|
|
299
|
-
return _union(part.union, objs, params,
|
|
299
|
+
return _union(part.union, objs, params, param_templates);
|
|
300
300
|
}
|
|
301
301
|
}
|
|
302
302
|
exports.MemoryNQLRunner = MemoryNQLRunner;
|
|
@@ -206,7 +206,7 @@ export declare class Bucket<M extends $Module, $ extends $Bucket> {
|
|
|
206
206
|
query<V extends ViewName<$>, Obj extends ViewObj<$, V>>(trx: AnyTrxNode, query: NQL_AnyQuery, pagination?: NQL_Pagination, view?: V, options?: {
|
|
207
207
|
no_tenancy?: boolean;
|
|
208
208
|
params?: Record<string, any>[];
|
|
209
|
-
|
|
209
|
+
param_templates?: Record<string, any>[];
|
|
210
210
|
}): Promise<NQL_Result<Obj>>;
|
|
211
211
|
/**
|
|
212
212
|
* Add `created_by`, `created_at`, `updated_by` and `updated_at` fields to object
|
|
@@ -712,9 +712,14 @@ class Bucket {
|
|
|
712
712
|
...query,
|
|
713
713
|
'#and __tenancy__': tenancy
|
|
714
714
|
};
|
|
715
|
+
// Pagination
|
|
716
|
+
if (pagination) {
|
|
717
|
+
if (pagination.perPage && pagination.perPage < 0)
|
|
718
|
+
pagination.perPage = undefined;
|
|
719
|
+
}
|
|
715
720
|
// Query
|
|
716
721
|
const adapter = this.cache || this.adapter;
|
|
717
|
-
const result = await adapter.query(trx, query, pagination, options?.params, options?.
|
|
722
|
+
const result = await adapter.query(trx, query, pagination, options?.params, options?.param_templates);
|
|
718
723
|
if (!result.data.length)
|
|
719
724
|
return result;
|
|
720
725
|
// Encryption
|
|
@@ -52,7 +52,7 @@ export declare class BucketGraph<M extends $Module, $ extends $Bucket> {
|
|
|
52
52
|
}, view: V, options?: {
|
|
53
53
|
silent?: boolean;
|
|
54
54
|
no_tenancy?: boolean;
|
|
55
|
-
|
|
55
|
+
param_templates?: Record<string, any>;
|
|
56
56
|
}): Promise<Obj | Obj[] | undefined>;
|
|
57
57
|
/**
|
|
58
58
|
* Return true if the link resolves to at least one object
|
|
@@ -43,7 +43,7 @@ class BucketGraph {
|
|
|
43
43
|
};
|
|
44
44
|
// Params
|
|
45
45
|
const params = [{ ...obj }];
|
|
46
|
-
const
|
|
46
|
+
const param_templates = link.index.length
|
|
47
47
|
? Object.fromEntries(link.index
|
|
48
48
|
.map((s, i) => [`$${i}`, s]))
|
|
49
49
|
: undefined;
|
|
@@ -53,14 +53,14 @@ class BucketGraph {
|
|
|
53
53
|
links = await trx.bucket(schema.bucket.short)
|
|
54
54
|
.query(query)
|
|
55
55
|
.params(params)
|
|
56
|
-
.
|
|
56
|
+
.param_templates(param_templates)
|
|
57
57
|
.page(page);
|
|
58
58
|
}
|
|
59
59
|
// Internal
|
|
60
60
|
else {
|
|
61
61
|
const otherBucket = dependency_1.Tag.element(schema.bucket, trx);
|
|
62
62
|
const adapter = otherBucket.cache || otherBucket.adapter;
|
|
63
|
-
links = await adapter.query(trx, query, page, params,
|
|
63
|
+
links = await adapter.query(trx, query, page, params, param_templates ? [param_templates] : undefined);
|
|
64
64
|
}
|
|
65
65
|
// Empty response
|
|
66
66
|
if (!schema.many && !schema.optional && !links.data.length) {
|
|
@@ -97,14 +97,14 @@ class BucketGraph {
|
|
|
97
97
|
const tenancy = (options?.no_tenancy)
|
|
98
98
|
? undefined
|
|
99
99
|
: this.bucket.getTenancyQuery(trx);
|
|
100
|
-
// Query
|
|
100
|
+
// 1st Query
|
|
101
101
|
const module = trx_node_1.TrxNode.getModule(trx);
|
|
102
102
|
const query = {
|
|
103
103
|
...schema.query,
|
|
104
104
|
'#and __tenancy__': tenancy
|
|
105
105
|
};
|
|
106
106
|
const params = objs.map(obj => ({ ...obj }));
|
|
107
|
-
const
|
|
107
|
+
const param_templates = link.indexes.length
|
|
108
108
|
? link.indexes.map(index => Object.fromEntries(index
|
|
109
109
|
.map((s, i) => [`$${i}`, s]))) : undefined;
|
|
110
110
|
let tempAdapter;
|
|
@@ -113,7 +113,7 @@ class BucketGraph {
|
|
|
113
113
|
const allLinks = await trx.bucket(schema.bucket.short)
|
|
114
114
|
.query(query)
|
|
115
115
|
.params(params)
|
|
116
|
-
.
|
|
116
|
+
.param_templates(param_templates)
|
|
117
117
|
.all();
|
|
118
118
|
const tempData = {};
|
|
119
119
|
for (const obj of allLinks)
|
|
@@ -129,14 +129,14 @@ class BucketGraph {
|
|
|
129
129
|
}
|
|
130
130
|
else {
|
|
131
131
|
const adapter = otherBucket.cache || otherBucket.adapter;
|
|
132
|
-
const allLinks = await adapter.query(trx, query, undefined, params,
|
|
132
|
+
const allLinks = await adapter.query(trx, query, undefined, params, param_templates);
|
|
133
133
|
const tempData = {};
|
|
134
134
|
for (const obj of allLinks.data)
|
|
135
135
|
tempData[obj.id] = obj;
|
|
136
136
|
tempAdapter = new memory_bucket_adapter_1.MemoryBucketAdapter(otherBucket.schema, tempData);
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
|
-
// Query
|
|
139
|
+
// 2nd Query
|
|
140
140
|
const links = [];
|
|
141
141
|
for (const obj of objs) {
|
|
142
142
|
const result = tempAdapter instanceof bucket_cache_1.BucketCache
|
|
@@ -11,10 +11,10 @@ export type NQL_QueryMeta = {
|
|
|
11
11
|
export type NQL_Union = {
|
|
12
12
|
meta: NQL_QueryMeta;
|
|
13
13
|
inters: NQL_Intersection[];
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
dir: ('asc' | 'desc')
|
|
17
|
-
};
|
|
14
|
+
sort?: {
|
|
15
|
+
key: string;
|
|
16
|
+
dir: ('asc' | 'desc');
|
|
17
|
+
}[];
|
|
18
18
|
_debug_id?: number;
|
|
19
19
|
};
|
|
20
20
|
export type NQL_Intersection = {
|
|
@@ -33,7 +33,7 @@ export type NQL_Rule = {
|
|
|
33
33
|
} | {
|
|
34
34
|
param: string | string[];
|
|
35
35
|
} | {
|
|
36
|
-
|
|
36
|
+
param_with_$: string;
|
|
37
37
|
} | {
|
|
38
38
|
subquery: {
|
|
39
39
|
bucket: $Bucket;
|
|
@@ -56,10 +56,7 @@ export type NQL_Node = NQL_Union | NQL_Intersection | NQL_Rule;
|
|
|
56
56
|
* All operations
|
|
57
57
|
*/
|
|
58
58
|
export type NQL_Operation = '==' | '>' | '<' | '>=' | '<=' | 'in' | 'contains' | 'contains_any' | 'present';
|
|
59
|
-
export type
|
|
60
|
-
by?: (keyof Querypath)[];
|
|
61
|
-
dir?: ('asc' | 'desc')[];
|
|
62
|
-
};
|
|
59
|
+
export type NQL_Sort<Querypath> = `${keyof Querypath & string}@${'asc' | 'desc'}` | `${keyof Querypath & string}@${'asc' | 'desc'}`[];
|
|
63
60
|
export type NQL_Pagination = {
|
|
64
61
|
page?: number;
|
|
65
62
|
perPage?: number;
|
|
@@ -136,7 +133,7 @@ type NQL_Terms<M extends $Module, $ extends $Bucket, Parameters = {}, Querypath
|
|
|
136
133
|
}>> = Conditions[keyof Conditions] & {
|
|
137
134
|
[K in `${'#and' | '#or'}${string}`]?: NQL_Terms<M, $, Parameters>;
|
|
138
135
|
} & NQL_GraphLinks<M, $, Parameters> & {
|
|
139
|
-
'#
|
|
136
|
+
'#sort'?: NQL_Sort<Querypath>;
|
|
140
137
|
};
|
|
141
138
|
/**
|
|
142
139
|
* NQL Dynamic Query Type
|
|
@@ -16,7 +16,7 @@ export declare class NQL_RuleTree {
|
|
|
16
16
|
constructor(daemon: AnyDaemon, module: string, bucketName: string, query: NQL_AnyQuery, customMetadata?: BucketMetadata | undefined, debug?: boolean);
|
|
17
17
|
parse(): Promise<void>;
|
|
18
18
|
private parseUnion;
|
|
19
|
-
private
|
|
19
|
+
private parseSort;
|
|
20
20
|
private parseKey;
|
|
21
21
|
private parseOp;
|
|
22
22
|
private parseValue;
|
|
@@ -84,8 +84,8 @@ class NQL_RuleTree {
|
|
|
84
84
|
const subInter = await this.parseUnion(bucket, value, select);
|
|
85
85
|
union.inters.push({ meta: {}, rules: [subInter] });
|
|
86
86
|
}
|
|
87
|
-
else if (parsedKey.type === '
|
|
88
|
-
union.
|
|
87
|
+
else if (parsedKey.type === 'sort') {
|
|
88
|
+
union.sort = await this.parseSort(bucket, value);
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
if (!union.inters[0]?.rules.length) {
|
|
@@ -93,12 +93,28 @@ class NQL_RuleTree {
|
|
|
93
93
|
}
|
|
94
94
|
return union;
|
|
95
95
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
96
|
+
parseSort(bucket, value) {
|
|
97
|
+
if (!Array.isArray(value))
|
|
98
|
+
value = [value];
|
|
99
|
+
if (value.some(v => typeof v !== 'string')) {
|
|
100
|
+
throw new Error('Invalid sort parameter. Should be a string or array of strings.');
|
|
101
|
+
}
|
|
102
|
+
const sort = [];
|
|
103
|
+
for (const v of value) {
|
|
104
|
+
let key, vdir;
|
|
105
|
+
if (v.endsWith('@asc')) {
|
|
106
|
+
key = v.split('@asc')[0];
|
|
107
|
+
vdir = 'asc';
|
|
108
|
+
}
|
|
109
|
+
else if (v.endsWith('@desc')) {
|
|
110
|
+
key = v.split('@desc')[0];
|
|
111
|
+
vdir = 'desc';
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
throw new Error(`Invalid query sort direction '${key}', string must end with '@asc' or '@desc'`);
|
|
115
|
+
}
|
|
116
|
+
const is_metadata_field = Object.values(bucket.meta).includes(key);
|
|
117
|
+
if (!is_metadata_field) {
|
|
102
118
|
const fields = bucket_model_schema_1.$BucketModel.getField(bucket.schema.model, key);
|
|
103
119
|
if (!fields.length) {
|
|
104
120
|
throw new Error(`Field '${key}' not found on bucket '${bucket.schema.name}'`);
|
|
@@ -111,26 +127,16 @@ class NQL_RuleTree {
|
|
|
111
127
|
}
|
|
112
128
|
}
|
|
113
129
|
}
|
|
130
|
+
sort.push({
|
|
131
|
+
key,
|
|
132
|
+
dir: vdir
|
|
133
|
+
});
|
|
114
134
|
}
|
|
115
|
-
|
|
116
|
-
by = [];
|
|
117
|
-
}
|
|
118
|
-
let dir = value['dir'];
|
|
119
|
-
if (dir) {
|
|
120
|
-
for (const key of dir) {
|
|
121
|
-
if (key !== 'asc' && key !== 'desc') {
|
|
122
|
-
throw new Error(`Invalid query order direction '${key}', expected 'asc'|'desc'`);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
else {
|
|
127
|
-
dir = [];
|
|
128
|
-
}
|
|
129
|
-
return { by, dir: dir };
|
|
135
|
+
return sort;
|
|
130
136
|
}
|
|
131
137
|
async parseKey(bucket, key) {
|
|
132
|
-
if (key === '#
|
|
133
|
-
return { type: '
|
|
138
|
+
if (key === '#sort') {
|
|
139
|
+
return { type: 'sort' };
|
|
134
140
|
}
|
|
135
141
|
else if (key.startsWith('#and')) {
|
|
136
142
|
return { type: 'and' };
|
|
@@ -235,7 +241,7 @@ class NQL_RuleTree {
|
|
|
235
241
|
}
|
|
236
242
|
// Path Parameter
|
|
237
243
|
else if ('$' in value) {
|
|
238
|
-
return {
|
|
244
|
+
return { param_with_$: value['$'] };
|
|
239
245
|
}
|
|
240
246
|
// Sub-Query
|
|
241
247
|
return { subquery: await this.parseSubQuery(value, parsedKey, meta, select) };
|
|
@@ -438,7 +444,7 @@ class NQL_RuleTree {
|
|
|
438
444
|
str += Array(d).fill(' ').join('')
|
|
439
445
|
+ (0, string_1.colored)(`└ ${node._debug_id || ''}[OR] `, 'lightpurple')
|
|
440
446
|
+ (0, string_1.colored)(`${node.meta.scope || ''} ${node.meta.avgTime || '?'}ms `, 'black')
|
|
441
|
-
+ (node.
|
|
447
|
+
+ (node.sort ? ` sort by ${node.sort}` : '')
|
|
442
448
|
+ '\n';
|
|
443
449
|
node.inters.forEach(inter => {
|
|
444
450
|
str += this.describe(inter, d + 1);
|
|
@@ -463,7 +469,7 @@ class NQL_RuleTree {
|
|
|
463
469
|
+ `@${node.meta.schema?.name}.${node.fieldpath}${node.not ? ' not' : ''} ${node.case_i ? '~' : ''}${node.op}`
|
|
464
470
|
+ (('static' in node.value) ? ` ${node.value.static}`
|
|
465
471
|
: ('param' in node.value) ? ` ->${node.value.param}`
|
|
466
|
-
: ('
|
|
472
|
+
: ('param_with_$' in node.value) ? ` ->>${node.value.param_with_$}`
|
|
467
473
|
: ' ▼ ' + (0, string_1.colored)('(' + node.value.subquery.bucket.name + '.' + node.value.subquery.select + ')', 'brown'))
|
|
468
474
|
+ '\n';
|
|
469
475
|
if ('subquery' in node.value) {
|
|
@@ -15,7 +15,7 @@ export type NQL_Result<T = Obj> = {
|
|
|
15
15
|
* @category NQL
|
|
16
16
|
* */
|
|
17
17
|
export declare abstract class NQLRunner {
|
|
18
|
-
abstract run(trx: AnyTrxNode, part: NQL_Part, params: Record<string, any>[],
|
|
18
|
+
abstract run(trx: AnyTrxNode, part: NQL_Part, params: Record<string, any>[], param_templates: Record<string, string>[], pagination?: NQL_Pagination, view?: $BucketView): Promise<NQL_Result>;
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
21
|
* @category NQL
|
|
@@ -24,7 +24,7 @@ export declare class NQL_Engine {
|
|
|
24
24
|
private module;
|
|
25
25
|
private runners;
|
|
26
26
|
constructor(module: AnyModule);
|
|
27
|
-
run(trx: AnyTrxNode, query: NQL_CompiledQuery, pagination?: NQL_Pagination, params?: Record<string, any>[],
|
|
27
|
+
run(trx: AnyTrxNode, query: NQL_CompiledQuery, pagination?: NQL_Pagination, params?: Record<string, any>[], param_templates?: Record<string, string>[], view?: $BucketView, customRunners?: Record<string, NQLRunner>): Promise<NQL_Result>;
|
|
28
28
|
linkExternal(bucket: AnyBucket): void;
|
|
29
29
|
}
|
|
30
30
|
export {};
|
|
@@ -22,9 +22,11 @@ class NQL_Engine {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
|
-
async run(trx, query, pagination, params = [{}],
|
|
25
|
+
async run(trx, query, pagination, params = [{}], param_templates = [], view, customRunners) {
|
|
26
26
|
if (!params.length)
|
|
27
27
|
params = [{}];
|
|
28
|
+
if (!param_templates.length)
|
|
29
|
+
param_templates = [{}];
|
|
28
30
|
let result = {
|
|
29
31
|
data: []
|
|
30
32
|
};
|
|
@@ -36,7 +38,7 @@ class NQL_Engine {
|
|
|
36
38
|
? { ...this.runners, ...customRunners }
|
|
37
39
|
: this.runners;
|
|
38
40
|
const _runner = runners[part.union.meta.scope];
|
|
39
|
-
const out = await _runner.run(trx, part, params,
|
|
41
|
+
const out = await _runner.run(trx, part, params, param_templates, pagination, view);
|
|
40
42
|
result = out;
|
|
41
43
|
// Part failed, return
|
|
42
44
|
// Failure here is only when a single value is expected,
|