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.
Files changed (39) hide show
  1. package/lib/bundler/distributed/stages/6_dump_cli_stage.js +0 -1
  2. package/lib/bundler/monolyth/stages/6_dump_cli_stage.js +0 -1
  3. package/lib/elements/blocks/resource/resource.builder.js +1 -2
  4. package/lib/elements/blocks/resource/resource.js +10 -0
  5. package/lib/elements/entities/bucket/adapters/bucket_adapter.d.ts +5 -1
  6. package/lib/elements/entities/bucket/adapters/bucket_adapter.js +6 -2
  7. package/lib/elements/entities/bucket/adapters/memory.nql.d.ts +1 -1
  8. package/lib/elements/entities/bucket/adapters/memory.nql.js +42 -42
  9. package/lib/elements/entities/bucket/bucket.d.ts +1 -1
  10. package/lib/elements/entities/bucket/bucket.js +6 -1
  11. package/lib/elements/entities/bucket/graph/bucket_graph.d.ts +1 -1
  12. package/lib/elements/entities/bucket/graph/bucket_graph.js +8 -8
  13. package/lib/elements/entities/bucket/query/nql.schema.d.ts +7 -10
  14. package/lib/elements/entities/bucket/query/nql_compiler.d.ts +1 -1
  15. package/lib/elements/entities/bucket/query/nql_compiler.js +34 -28
  16. package/lib/elements/entities/bucket/query/nql_engine.d.ts +2 -2
  17. package/lib/elements/entities/bucket/query/nql_engine.js +4 -2
  18. package/lib/engine/app/app.config.d.ts +7 -1
  19. package/lib/engine/app/app.config.js +20 -0
  20. package/lib/engine/app/app.d.ts +3 -1
  21. package/lib/engine/app/inline.app.d.ts +8 -2
  22. package/lib/engine/app/inline.app.js +33 -34
  23. package/lib/engine/app/native/browser.app.d.ts +1 -0
  24. package/lib/engine/app/native/browser.app.js +1 -1
  25. package/lib/engine/app/native/distributed_node.app.js +1 -6
  26. package/lib/engine/app/native/monolyth.app.d.ts +1 -0
  27. package/lib/engine/app/native/monolyth.app.js +1 -1
  28. package/lib/engine/app/service.d.ts +4 -10
  29. package/lib/engine/app/service.js +1 -3
  30. package/lib/engine/cli/script.d.ts +97 -0
  31. package/lib/engine/cli/script.js +422 -0
  32. package/lib/engine/transaction/nodes/bucket_query.trx_node.d.ts +2 -2
  33. package/lib/engine/transaction/nodes/bucket_query.trx_node.js +8 -8
  34. package/lib/engine/util/dotenv.d.ts +5 -6
  35. package/lib/engine/util/dotenv.js +14 -12
  36. package/package.json +1 -1
  37. package/tsconfig.build.tsbuildinfo +1 -1
  38. package/tools/dotenv.d.ts +0 -1
  39. 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
- orderBy: $.string.optional,
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>[], path_params?: Record<string, string>[], config?: {
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, path_params, config,
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, path_params, view, custom?.runners);
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[], path_params: Obj[], pagination?: NQL_Pagination): Promise<{
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, path_params, pagination) {
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, path_params);
28
+ response = await this.filter(part, data, params, param_templates);
29
29
  }
30
30
  let output = Object.values(response);
31
- if (part.union.order) {
32
- const by = part.union.order.by.length
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 < by.length; i++) {
37
- const key = by[i];
38
- if (typeof a[key] == 'number') {
39
- if (typeof b[key] !== 'number')
40
- throw new Error(`Cannot compare number and ${typeof b[key]}`);
41
- let d = a[key] - b[key];
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 (part.union.order.dir[i] === 'desc') {
43
+ if (s.dir === 'desc') {
44
44
  d *= -1;
45
45
  }
46
46
  return d;
47
47
  }
48
48
  }
49
- else if (typeof a[key] == 'string') {
50
- if (typeof b[key] !== 'string')
51
- throw new Error(`Cannot compare string and ${typeof b[key]}`);
52
- let d = a[key].localeCompare(b[key]);
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 (part.union.order.dir[i] === 'desc') {
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 || 10);
71
- const b = a + (pagination.perPage || 10);
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, path_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, path_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, path_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, path_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, path_params);
114
+ out = _union(rule, remaining, params, param_templates);
115
115
  }
116
116
  // <Rule>
117
117
  else {
118
- out = _rule(rule, remaining, params, path_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, path_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 (path_params.length) {
136
- for (const path_param of path_params) {
137
- combos.push({ params: param, path_params: path_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, path_params: {} });
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.path_params);
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, path_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, path_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(path_params).length ? [path_params] : []);
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[p]);
196
+ queryValue = rule.value.param.map(p => tree_1.Tree.get(params, p));
197
197
  }
198
198
  else {
199
- queryValue = params[rule.value.param];
199
+ queryValue = tree_1.Tree.get(params, rule.value.param);
200
200
  }
201
201
  }
202
- else if ('path_param' in rule.value) {
203
- let path = rule.value.path_param;
204
- for (const key in path_params) {
205
- path = path.replace(new RegExp(key, 'g'), path_params[key]);
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, path_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
- path_params?: Record<string, any>[];
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?.path_params);
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
- path_params?: Record<string, any>;
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 path_params = link.index.length
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
- .path_params(path_params)
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, path_params ? [path_params] : undefined);
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 path_params = link.indexes.length
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
- .path_params(path_params)
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, path_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
- order?: {
15
- by: string[];
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
- path_param: string;
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 NQL_Order<Querypath> = {
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
- '#order'?: NQL_Order<Querypath>;
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 parseOrder;
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 === 'order') {
88
- union.order = await this.parseOrder(bucket, value);
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
- parseOrder(bucket, value) {
97
- let by = value['by'];
98
- if (by) {
99
- for (const key of by) {
100
- if (Object.values(bucket.meta).includes(key))
101
- continue;
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
- else {
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 === '#order') {
133
- return { type: 'order' };
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 { path_param: value['$'] };
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.order ? ` order by ${node.order.by} ${node.order.dir}` : '')
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
- : ('path_param' in node.value) ? ` ->>${node.value.path_param}`
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>[], path_params: Record<string, any>[], pagination?: NQL_Pagination, view?: $BucketView): Promise<NQL_Result>;
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>[], path_params?: Record<string, any>[], view?: $BucketView, customRunners?: Record<string, NQLRunner>): Promise<NQL_Result>;
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 = [{}], path_params = [], view, customRunners) {
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, path_params, pagination, view);
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,