nesoi 3.3.5 → 3.3.7

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 (42) hide show
  1. package/lib/elements/blocks/job/internal/resource_job.builder.js +3 -1
  2. package/lib/elements/blocks/resource/resource.builder.js +2 -3
  3. package/lib/elements/blocks/resource/resource.js +1 -0
  4. package/lib/elements/entities/bucket/adapters/bucket_adapter.d.ts +5 -1
  5. package/lib/elements/entities/bucket/adapters/bucket_adapter.js +6 -2
  6. package/lib/elements/entities/bucket/adapters/memory.nql.d.ts +1 -1
  7. package/lib/elements/entities/bucket/adapters/memory.nql.js +55 -35
  8. package/lib/elements/entities/bucket/bucket.builder.js +1 -1
  9. package/lib/elements/entities/bucket/bucket.d.ts +4 -0
  10. package/lib/elements/entities/bucket/bucket.infer.d.ts +7 -7
  11. package/lib/elements/entities/bucket/bucket.js +14 -9
  12. package/lib/elements/entities/bucket/bucket.schema.d.ts +1 -1
  13. package/lib/elements/entities/bucket/cache/bucket_cache.js +3 -3
  14. package/lib/elements/entities/bucket/graph/bucket_graph.d.ts +13 -3
  15. package/lib/elements/entities/bucket/graph/bucket_graph.infer.d.ts +1 -1
  16. package/lib/elements/entities/bucket/graph/bucket_graph.js +23 -13
  17. package/lib/elements/entities/bucket/graph/bucket_graph_link.builder.d.ts +6 -5
  18. package/lib/elements/entities/bucket/model/bucket_model.convert.js +4 -4
  19. package/lib/elements/entities/bucket/query/nql.schema.d.ts +8 -9
  20. package/lib/elements/entities/bucket/query/nql_compiler.d.ts +1 -1
  21. package/lib/elements/entities/bucket/query/nql_compiler.js +38 -27
  22. package/lib/elements/entities/bucket/query/nql_engine.d.ts +2 -2
  23. package/lib/elements/entities/bucket/query/nql_engine.js +4 -2
  24. package/lib/elements/entities/bucket/view/bucket_view.builder.d.ts +3 -2
  25. package/lib/elements/entities/bucket/view/bucket_view.builder.js +3 -3
  26. package/lib/elements/entities/bucket/view/bucket_view.js +123 -32
  27. package/lib/elements/entities/bucket/view/bucket_view.schema.d.ts +6 -3
  28. package/lib/elements/entities/bucket/view/bucket_view.schema.js +2 -1
  29. package/lib/elements/entities/bucket/view/bucket_view_field.builder.d.ts +25 -24
  30. package/lib/elements/entities/bucket/view/bucket_view_field.builder.js +65 -25
  31. package/lib/engine/app/inline.app.js +6 -0
  32. package/lib/engine/app/native/distributed_node.app.js +6 -0
  33. package/lib/engine/app/service.d.ts +7 -0
  34. package/lib/engine/app/service.js +2 -0
  35. package/lib/engine/transaction/nodes/bucket.trx_node.d.ts +2 -2
  36. package/lib/engine/transaction/nodes/bucket.trx_node.js +6 -4
  37. package/lib/engine/transaction/nodes/bucket_query.trx_node.d.ts +2 -0
  38. package/lib/engine/transaction/nodes/bucket_query.trx_node.js +15 -4
  39. package/lib/engine/transaction/trx_engine.js +1 -1
  40. package/lib/engine/transaction/trx_node.js +3 -3
  41. package/package.json +1 -1
  42. package/tsconfig.build.tsbuildinfo +1 -1
@@ -26,8 +26,8 @@ class BucketGraph {
26
26
  * - `no_tenancy`: Don't apply tenancy rules (default: `false`)
27
27
  */
28
28
  async readLink(trx, obj, link, options) {
29
- log_1.Log.trace('bucket', this.bucketName, `Read link ${link}`);
30
- const schema = this.schema.links[link];
29
+ log_1.Log.trace('bucket', this.bucketName, `Read link ${link.name}`);
30
+ const schema = this.schema.links[link.name];
31
31
  // Make tenancy query
32
32
  const tenancy = (options?.no_tenancy)
33
33
  ? undefined
@@ -38,23 +38,29 @@ class BucketGraph {
38
38
  ...schema.query,
39
39
  '#and __tenancy__': tenancy
40
40
  };
41
- const params = [{ ...obj }];
42
41
  const page = {
43
42
  perPage: schema.many ? undefined : 1,
44
43
  };
44
+ // Params
45
+ const params = [{ ...obj }];
46
+ const param_templates = link.index.length
47
+ ? Object.fromEntries(link.index
48
+ .map((s, i) => [`$${i}`, s]))
49
+ : undefined;
45
50
  // External
46
51
  let links;
47
52
  if (schema.bucket.module !== module.name) {
48
53
  links = await trx.bucket(schema.bucket.short)
49
54
  .query(query)
50
55
  .params(params)
56
+ .param_templates(param_templates)
51
57
  .page(page);
52
58
  }
53
59
  // Internal
54
60
  else {
55
61
  const otherBucket = dependency_1.Tag.element(schema.bucket, trx);
56
62
  const adapter = otherBucket.cache || otherBucket.adapter;
57
- links = await adapter.query(trx, query, page, params);
63
+ links = await adapter.query(trx, query, page, params, param_templates ? [param_templates] : undefined);
58
64
  }
59
65
  // Empty response
60
66
  if (!schema.many && !schema.optional && !links.data.length) {
@@ -65,7 +71,7 @@ class BucketGraph {
65
71
  else {
66
72
  throw error_1.NesoiError.Bucket.Graph.RequiredLinkNotFound({
67
73
  bucket: this.bucketName,
68
- link: link,
74
+ link: link.name,
69
75
  id: obj.id
70
76
  });
71
77
  }
@@ -85,25 +91,29 @@ class BucketGraph {
85
91
  * - `no_tenancy`: Don't apply tenancy rules (default: `false`)
86
92
  */
87
93
  async readManyLinks(trx, objs, link, options) {
88
- log_1.Log.trace('bucket', this.bucketName, `Read link ${link}`);
89
- const schema = this.schema.links[link];
94
+ log_1.Log.trace('bucket', this.bucketName, `Read link ${link.name}`);
95
+ const schema = this.schema.links[link.name];
90
96
  // Make tenancy query
91
97
  const tenancy = (options?.no_tenancy)
92
98
  ? undefined
93
99
  : this.bucket.getTenancyQuery(trx);
94
- // Query
100
+ // 1st Query
95
101
  const module = trx_node_1.TrxNode.getModule(trx);
96
102
  const query = {
97
103
  ...schema.query,
98
104
  '#and __tenancy__': tenancy
99
105
  };
100
106
  const params = objs.map(obj => ({ ...obj }));
107
+ const param_templates = link.indexes.length
108
+ ? link.indexes.map(index => Object.fromEntries(index
109
+ .map((s, i) => [`$${i}`, s]))) : undefined;
101
110
  let tempAdapter;
102
111
  // External
103
112
  if (schema.bucket.module !== module.name) {
104
113
  const allLinks = await trx.bucket(schema.bucket.short)
105
114
  .query(query)
106
115
  .params(params)
116
+ .param_templates(param_templates)
107
117
  .all();
108
118
  const tempData = {};
109
119
  for (const obj of allLinks)
@@ -119,14 +129,14 @@ class BucketGraph {
119
129
  }
120
130
  else {
121
131
  const adapter = otherBucket.cache || otherBucket.adapter;
122
- const allLinks = await adapter.query(trx, query, undefined, params);
132
+ const allLinks = await adapter.query(trx, query, undefined, params, param_templates);
123
133
  const tempData = {};
124
134
  for (const obj of allLinks.data)
125
135
  tempData[obj.id] = obj;
126
136
  tempAdapter = new memory_bucket_adapter_1.MemoryBucketAdapter(otherBucket.schema, tempData);
127
137
  }
128
138
  }
129
- // Query
139
+ // 2nd Query
130
140
  const links = [];
131
141
  for (const obj of objs) {
132
142
  const result = tempAdapter instanceof bucket_cache_1.BucketCache
@@ -135,7 +145,7 @@ class BucketGraph {
135
145
  }, [{ ...obj }], undefined)
136
146
  : await tempAdapter.query(trx, schema.query, {
137
147
  perPage: schema.many ? undefined : 1,
138
- }, [{ ...obj }], undefined, {
148
+ }, [{ ...obj }], undefined, undefined, {
139
149
  module: schema.bucket.module,
140
150
  runners: {
141
151
  [tempAdapter.getQueryMeta().scope]: tempAdapter.nql
@@ -165,8 +175,8 @@ class BucketGraph {
165
175
  * - `no_tenancy`: Don't apply tenancy rules (default: `false`)
166
176
  */
167
177
  async viewLink(trx, obj, link, view, options) {
168
- log_1.Log.trace('bucket', this.bucketName, `Read link ${link}`);
169
- const schema = this.schema.links[link];
178
+ log_1.Log.trace('bucket', this.bucketName, `Read link ${link.name}`);
179
+ const schema = this.schema.links[link.name];
170
180
  const links = await this.readLink(trx, obj, link, options);
171
181
  if (!links)
172
182
  return undefined;
@@ -15,27 +15,28 @@ export declare class BucketGraphLinkFactory<Module extends $Module, SelfBucket e
15
15
  constructor(module: string);
16
16
  as(alias: string): this;
17
17
  get compose(): typeof this;
18
- one<N extends keyof Module['buckets'], Bucket extends $Bucket = Module['buckets'][N]>(bucket: N, query: NQL_Query<Module, Bucket, Fieldpaths>): BucketGraphLinkBuilder<Module, SelfBucket, Module["buckets"][N]>;
19
- many<N extends keyof Module['buckets'], Bucket extends $Bucket = Module['buckets'][N]>(bucket: N, query: NQL_Query<Module, Bucket, Fieldpaths>): BucketGraphLinkBuilder<Module, SelfBucket, Module["buckets"][N]>;
18
+ one<N extends keyof Module['buckets'], Bucket extends $Bucket = Module['buckets'][N]>(bucket: N, query: NQL_Query<Module, Bucket, Fieldpaths>): BucketGraphLinkBuilder<Module, SelfBucket, Module["buckets"][N], false>;
19
+ many<N extends keyof Module['buckets'], Bucket extends $Bucket = Module['buckets'][N]>(bucket: N, query: NQL_Query<Module, Bucket, Fieldpaths>): BucketGraphLinkBuilder<Module, SelfBucket, Module["buckets"][N], true>;
20
20
  }
21
21
  /**
22
22
  * @category Builders
23
23
  * @subcategory Entity
24
24
  * */
25
- export declare class BucketGraphLinkBuilder<Module extends $Module, SelfBucket extends $Bucket, OtherBucket extends $Bucket> {
25
+ export declare class BucketGraphLinkBuilder<Module extends $Module, SelfBucket extends $Bucket, OtherBucket extends $Bucket, Many extends boolean> {
26
26
  private bucket;
27
27
  private rel;
28
28
  private query;
29
29
  private many;
30
30
  private alias?;
31
31
  '#other': OtherBucket;
32
+ '#many': Many;
32
33
  private _optional;
33
34
  constructor(bucket: Dependency, rel: 'aggregation' | 'composition', query: NQL_AnyQuery, many: boolean, alias?: string | undefined);
34
35
  as(alias: string): this;
35
36
  get optional(): this;
36
- static build(node: BucketBuilderNode, builder: BucketGraphLinkBuilder<any, any, any>, name: string): $BucketGraphLink;
37
+ static build(node: BucketBuilderNode, builder: BucketGraphLinkBuilder<any, any, any, any>, name: string): $BucketGraphLink;
37
38
  static inferKeyOwner(query: NQL_AnyQuery): "self";
38
39
  }
39
40
  export type BucketGraphLinkBuilders = {
40
- [x: string]: BucketGraphLinkBuilder<any, any, any>;
41
+ [x: string]: BucketGraphLinkBuilder<any, any, any, any>;
41
42
  };
@@ -18,7 +18,7 @@ function convertToView(model, name, fields = model.fields, path, depth = 0) {
18
18
  const viewFields = {};
19
19
  for (const f in fields) {
20
20
  const field = fields[f];
21
- const $ = new bucket_view_field_builder_1.BucketViewFieldFactory(view);
21
+ const $ = new bucket_view_field_builder_1.BucketViewFieldFactory();
22
22
  const key = (path ? path + '.' : '')
23
23
  + field.name;
24
24
  const builder = $.model(key);
@@ -38,9 +38,9 @@ function convertToMessage(module, model, name, alias, include = [], exclude = []
38
38
  const convertField = (field) => {
39
39
  return new message_template_schema_1.$MessageTemplateField(field.type, field.name, field.alias, field.path, field.path, optional.includes(field.path) ? false : field.required, undefined, false, [], {
40
40
  enum: field.meta?.enum
41
- }, field.children ? convertFields(field.children, include, exclude, optional) : undefined);
41
+ }, field.children ? convertFields(field.children) : undefined);
42
42
  };
43
- const convertFields = (fields, include = [], exclude = [], optional = [], root = '') => {
43
+ const convertFields = (fields) => {
44
44
  const msgFields = {};
45
45
  for (const f in fields) {
46
46
  const field = fields[f];
@@ -52,7 +52,7 @@ function convertToMessage(module, model, name, alias, include = [], exclude = []
52
52
  }
53
53
  return msgFields;
54
54
  };
55
- const msgFields = convertFields(model.fields, include, exclude);
55
+ const msgFields = convertFields(model.fields);
56
56
  const template = new message_template_schema_1.$MessageTemplate(msgFields);
57
57
  return new message_schema_1.$Message(module, name, alias, template);
58
58
  }
@@ -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 = {
@@ -32,6 +32,8 @@ export type NQL_Rule = {
32
32
  static: any | any[];
33
33
  } | {
34
34
  param: string | string[];
35
+ } | {
36
+ param_with_$: string;
35
37
  } | {
36
38
  subquery: {
37
39
  bucket: $Bucket;
@@ -54,10 +56,7 @@ export type NQL_Node = NQL_Union | NQL_Intersection | NQL_Rule;
54
56
  * All operations
55
57
  */
56
58
  export type NQL_Operation = '==' | '>' | '<' | '>=' | '<=' | 'in' | 'contains' | 'contains_any' | 'present';
57
- export type NQL_Order<Querypath> = {
58
- by?: (keyof Querypath)[];
59
- dir?: ('asc' | 'desc')[];
60
- };
59
+ export type NQL_Sort<Querypath> = `${keyof Querypath & string}@${'asc' | 'desc'}` | `${keyof Querypath & string}@${'asc' | 'desc'}`[];
61
60
  export type NQL_Pagination = {
62
61
  page?: number;
63
62
  perPage?: number;
@@ -134,7 +133,7 @@ type NQL_Terms<M extends $Module, $ extends $Bucket, Parameters = {}, Querypath
134
133
  }>> = Conditions[keyof Conditions] & {
135
134
  [K in `${'#and' | '#or'}${string}`]?: NQL_Terms<M, $, Parameters>;
136
135
  } & NQL_GraphLinks<M, $, Parameters> & {
137
- '#order'?: NQL_Order<Querypath>;
136
+ '#sort'?: NQL_Sort<Querypath>;
138
137
  };
139
138
  /**
140
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' };
@@ -233,6 +239,10 @@ class NQL_RuleTree {
233
239
  if ('.' in value) {
234
240
  return { param: value['.'] };
235
241
  }
242
+ // Path Parameter
243
+ else if ('$' in value) {
244
+ return { param_with_$: value['$'] };
245
+ }
236
246
  // Sub-Query
237
247
  return { subquery: await this.parseSubQuery(value, parsedKey, meta, select) };
238
248
  }
@@ -434,7 +444,7 @@ class NQL_RuleTree {
434
444
  str += Array(d).fill(' ').join('')
435
445
  + (0, string_1.colored)(`└ ${node._debug_id || ''}[OR] `, 'lightpurple')
436
446
  + (0, string_1.colored)(`${node.meta.scope || ''} ${node.meta.avgTime || '?'}ms `, 'black')
437
- + (node.order ? ` order by ${node.order.by} ${node.order.dir}` : '')
447
+ + (node.sort ? ` sort by ${node.sort}` : '')
438
448
  + '\n';
439
449
  node.inters.forEach(inter => {
440
450
  str += this.describe(inter, d + 1);
@@ -459,7 +469,8 @@ class NQL_RuleTree {
459
469
  + `@${node.meta.schema?.name}.${node.fieldpath}${node.not ? ' not' : ''} ${node.case_i ? '~' : ''}${node.op}`
460
470
  + (('static' in node.value) ? ` ${node.value.static}`
461
471
  : ('param' in node.value) ? ` ->${node.value.param}`
462
- : ' ▼ ' + (0, string_1.colored)('(' + node.value.subquery.bucket.name + '.' + node.value.subquery.select + ')', 'brown'))
472
+ : ('param_with_$' in node.value) ? ` ->>${node.value.param_with_$}`
473
+ : ' ▼ ' + (0, string_1.colored)('(' + node.value.subquery.bucket.name + '.' + node.value.subquery.select + ')', 'brown'))
463
474
  + '\n';
464
475
  if ('subquery' in node.value) {
465
476
  str += this.describe(node.value['subquery'].union, d + 1);
@@ -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>[], 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>[], 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 = [{}], 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, 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,
@@ -4,6 +4,7 @@ import { $BucketGraph } from '../graph/bucket_graph.schema';
4
4
  import { BucketViewFieldBuilders, BucketViewFieldFactory } from './bucket_view_field.builder';
5
5
  import { $Bucket } from '../bucket.schema';
6
6
  import { $Module, $Space } from "../../..";
7
+ import { ModuleTree } from "../../../../engine/tree";
7
8
  /**
8
9
  * @category Builders
9
10
  * @subcategory Entity
@@ -13,6 +14,6 @@ export declare class BucketViewBuilder<Space extends $Space, Module extends $Mod
13
14
  private _fields;
14
15
  constructor(name: string);
15
16
  fields($: BucketViewDef<Space, Module, Bucket>): this;
16
- static build(builder: BucketViewBuilder<any, any, any>, model: $BucketModel, graph: $BucketGraph, views: $BucketViews): $BucketView;
17
+ static build(builder: BucketViewBuilder<any, any, any>, model: $BucketModel, graph: $BucketGraph, views: $BucketViews, tree: ModuleTree): $BucketView;
17
18
  }
18
- export type BucketViewDef<Space extends $Space, Module extends $Module, Bucket extends $Bucket> = ($: BucketViewFieldFactory<Space, Module, Bucket>) => BucketViewFieldBuilders;
19
+ export type BucketViewDef<Space extends $Space, Module extends $Module, Bucket extends $Bucket> = ($: BucketViewFieldFactory<Space, Module, Bucket>) => BucketViewFieldBuilders<Bucket>;
@@ -13,13 +13,13 @@ class BucketViewBuilder {
13
13
  this._fields = {};
14
14
  }
15
15
  fields($) {
16
- const fieldBuilder = new bucket_view_field_builder_1.BucketViewFieldFactory(this);
16
+ const fieldBuilder = new bucket_view_field_builder_1.BucketViewFieldFactory();
17
17
  this._fields = $(fieldBuilder);
18
18
  return this;
19
19
  }
20
20
  // Build
21
- static build(builder, model, graph, views) {
22
- const fields = bucket_view_field_builder_1.BucketViewFieldBuilder.buildFields(builder._fields, model, graph, views);
21
+ static build(builder, model, graph, views, tree) {
22
+ const fields = bucket_view_field_builder_1.BucketViewFieldBuilder.buildFields(builder._fields, model, graph, views, undefined, tree);
23
23
  const schema = new bucket_view_schema_1.$BucketView(builder.name, fields);
24
24
  schema.fields.id = new bucket_view_schema_1.$BucketViewField('id', 'model', 'id', { model: { path: 'id' } });
25
25
  return schema;
@@ -153,6 +153,7 @@ class BucketView {
153
153
  if (!node.field.children)
154
154
  return [];
155
155
  const next = [];
156
+ // subview
156
157
  for (const key in node.field.children) {
157
158
  if (key === '__raw')
158
159
  continue;
@@ -258,6 +259,12 @@ class BucketView {
258
259
  }
259
260
  }
260
261
  }
262
+ // Apply prop
263
+ for (const data of poll) {
264
+ if (node.field.prop) {
265
+ data.target[data.key] = data.target[data.key][node.field.prop];
266
+ }
267
+ }
261
268
  return poll.map(p => ({
262
269
  index: p.index,
263
270
  raw: p.raw,
@@ -271,7 +278,7 @@ class BucketView {
271
278
  async parseComputedProp(trx, node) {
272
279
  const meta = node.field.meta.computed;
273
280
  for (const entry of node.data) {
274
- entry.target[node.field.name] = await promise_1.default.solve(meta.fn({ trx, raw: entry.raw, bucket: node.bucket.schema }));
281
+ entry.target[node.field.name] = await promise_1.default.solve(meta.fn({ trx, raw: entry.raw, value: entry.value, bucket: node.bucket.schema }));
275
282
  }
276
283
  }
277
284
  /**
@@ -279,29 +286,47 @@ class BucketView {
279
286
  */
280
287
  async parseGraphProp(trx, node) {
281
288
  const meta = node.field.meta.graph;
282
- let links;
289
+ let linksObjs;
283
290
  const module = trx_node_1.TrxNode.getModule(trx);
291
+ // Step 1: Read many links from bucket
284
292
  // External
285
293
  if (node.bucket.tag.module !== module.name) {
286
- links = await trx.bucket(node.bucket.tag.short).readManyLinks(node.data.map(entry => entry.raw.id), meta.link);
294
+ linksObjs = await trx.bucket(node.bucket.tag.short).readManyLinks(node.data.map(entry => entry.raw.id), meta.path, node.data.map(entry => entry.index.map(i => i.toString())));
287
295
  }
288
296
  // Internal
289
297
  else {
290
298
  const bucket = module.buckets[node.bucket.tag.name];
291
- links = await bucket.graph.readManyLinks(trx, node.data.map(entry => entry.raw), meta.link, { silent: true });
299
+ linksObjs = await bucket.graph.readManyLinks(trx, node.data.map(entry => entry.raw), {
300
+ name: meta.link,
301
+ indexes: node.data.map(entry => entry.index.map(i => i.toString()))
302
+ }, { silent: true });
292
303
  }
293
- for (let i = 0; i < links.length; i++) {
304
+ // Step 2: Initialize target values
305
+ const link = node.bucket.schema.graph.links[meta.link];
306
+ for (let i = 0; i < linksObjs.length; i++) {
294
307
  if (meta.view) {
295
- const link = node.bucket.schema.graph.links[meta.link];
296
- node.data[i].target[node.field.name] = link.many
297
- ? []
298
- : (links[i] ? {} : undefined);
308
+ if (link.many) {
309
+ node.data[i].target[node.field.name] = [];
310
+ }
311
+ else {
312
+ node.data[i].target[node.field.name] = linksObjs[i] ? {} : undefined;
313
+ }
314
+ }
315
+ else if (node.field.prop) {
316
+ if (link.many) {
317
+ node.data[i].target[node.field.name] = linksObjs[i].map((link) => link[node.field.prop]);
318
+ }
319
+ else {
320
+ node.data[i].target[node.field.name] = linksObjs[i]?.[node.field.prop];
321
+ }
299
322
  }
300
323
  else {
301
- node.data[i].target[node.field.name] = links[i];
324
+ node.data[i].target[node.field.name] = linksObjs[i];
302
325
  }
303
326
  }
327
+ // Step 3: Build view
304
328
  let next = [];
329
+ let nextData = linksObjs;
305
330
  if (meta.view) {
306
331
  const schema = node.bucket.schema;
307
332
  const otherBucketDep = schema.graph.links[meta.link].bucket;
@@ -311,46 +336,112 @@ class BucketView {
311
336
  const view = otherBucket.schema.views[meta.view];
312
337
  const { __raw, ...v } = view.fields;
313
338
  const link = node.bucket.schema.graph.links[meta.link];
314
- let nextData;
315
339
  if (link.many) {
316
- const _links = links;
340
+ const _links = linksObjs;
317
341
  for (let i = 0; i < _links.length; i++) {
318
342
  const target = node.data[i].target[node.field.name];
319
343
  for (let j = 0; j < _links[i].length; j++) {
320
- target.push(__raw ? { ..._links[i][j] } : {});
321
- target[j].$v = meta.view;
344
+ if (node.field.prop) {
345
+ target.push(_links[i][j][node.field.prop]);
346
+ }
347
+ else {
348
+ target.push(__raw ? { ..._links[i][j] } : {});
349
+ target[j].$v = meta.view;
350
+ }
322
351
  }
323
352
  }
324
- nextData = _links.map((ll, i) => ll.map((l, j) => ({ value: l, target: node.data[i].target[node.field.name][j] }))).flat(1);
353
+ if (!node.field.prop) {
354
+ nextData = _links.map((ll, i) => ll.map((l, j) => ({ value: l, target: node.data[i].target[node.field.name][j] }))).flat(1);
355
+ }
356
+ else {
357
+ nextData = [];
358
+ }
325
359
  }
326
360
  else {
327
- const _links = links;
361
+ const _links = linksObjs;
328
362
  nextData = [];
329
363
  for (let i = 0; i < _links.length; i++) {
330
364
  if (!_links[i])
331
365
  continue;
332
- const target = node.data[i].target[node.field.name];
333
- if (__raw) {
334
- Object.assign(target, _links[i]);
366
+ if (node.field.prop) {
367
+ node.data[i].target[node.field.name] = _links[i][node.field.prop];
368
+ }
369
+ else {
370
+ const target = node.data[i].target[node.field.name];
371
+ if (__raw) {
372
+ Object.assign(target, _links[i]);
373
+ }
374
+ target.$v = meta.view;
375
+ nextData.push({
376
+ value: _links[i], target: node.data[i].target[node.field.name]
377
+ });
335
378
  }
336
- target.$v = meta.view;
337
- nextData.push({
338
- value: _links[i], target: node.data[i].target[node.field.name]
339
- });
340
379
  }
341
380
  }
342
- // TODODO: support external bucket on transitive graph
381
+ // (still step 3) Add link bucket view fields to queue
343
382
  next = [];
344
- for (const field of Object.values(v)) {
345
- const bucket = await daemon_1.Daemon.getBucketMetadata(module.daemon, otherBucketDep);
383
+ const bucket = await daemon_1.Daemon.getBucketMetadata(module.daemon, otherBucketDep);
384
+ // Next data is empty if meta.prop is defined, since there's no need to go deeper
385
+ if (nextData.length) {
386
+ for (const field of Object.values(v)) {
387
+ next.push({
388
+ bucket,
389
+ field,
390
+ data: nextData.map($ => ({
391
+ raw: $.value,
392
+ value: $.value,
393
+ index: [],
394
+ target: $.target
395
+ }))
396
+ });
397
+ }
398
+ }
399
+ }
400
+ // Step 4: Add subview fields to queue
401
+ if (node.field.children) {
402
+ // Prepare subview data
403
+ const subview_data = [];
404
+ for (let i = 0; i < linksObjs.length; i++) {
405
+ const objs = linksObjs[i];
406
+ let target;
407
+ if (link.many) {
408
+ target = [];
409
+ for (const tobj of node.data[i].target[node.field.name]) {
410
+ if ('__raw' in node.field.children) {
411
+ target.push({ ...tobj });
412
+ }
413
+ else {
414
+ target.push({ id: tobj.id });
415
+ }
416
+ }
417
+ subview_data.push(...objs.map((obj, i) => ({
418
+ obj: { ...obj },
419
+ target: target[i]
420
+ })));
421
+ }
422
+ else {
423
+ target = { id: objs.id };
424
+ if ('__raw' in node.field.children) {
425
+ Object.assign(target, node.data[i].target[node.field.name]);
426
+ }
427
+ subview_data.push({ obj: { ...objs }, target });
428
+ }
429
+ node.data[i].target[node.field.name] = target;
430
+ }
431
+ const module = trx_node_1.TrxNode.getModule(trx);
432
+ const subview_bucket = daemon_1.Daemon.getBucketMetadata(module.daemon, link.bucket);
433
+ // Add subview data to queue
434
+ for (const key in node.field.children) {
435
+ if (key === '__raw')
436
+ continue;
346
437
  next.push({
347
- bucket,
348
- field,
349
- data: nextData.map($ => ({
350
- raw: $.value,
351
- value: $.value,
438
+ bucket: subview_bucket,
439
+ field: node.field.children[key],
440
+ data: subview_data.map(data => ({
441
+ raw: data.obj,
442
+ value: data.obj,
352
443
  index: [],
353
- target: $.target
444
+ target: data.target
354
445
  }))
355
446
  });
356
447
  }