@rljson/db 0.0.6 → 0.0.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/dist/db.d.ts +39 -28
- package/dist/db.js +1457 -506
- package/package.json +14 -14
- package/dist/cars-example.d.ts +0 -41
package/dist/db.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import { equals, merge } from "@rljson/json";
|
|
2
|
-
import { timeId, createInsertHistoryTableCfg, Validate, BaseValidator, Route, validateInsert, isTimeId } from "@rljson/rljson";
|
|
3
1
|
import { rmhsh, hsh, Hash, hip } from "@rljson/hash";
|
|
2
|
+
import { equals, merge } from "@rljson/json";
|
|
3
|
+
import { timeId, createInsertHistoryTableCfg, Validate, BaseValidator, Route, isTimeId } from "@rljson/rljson";
|
|
4
4
|
import { IoMem } from "@rljson/io";
|
|
5
|
+
import { traverse } from "object-traversal";
|
|
5
6
|
import { compileExpression } from "filtrex";
|
|
6
7
|
class BaseController {
|
|
7
8
|
constructor(_core, _tableKey) {
|
|
8
9
|
this._core = _core;
|
|
9
10
|
this._tableKey = _tableKey;
|
|
10
11
|
}
|
|
12
|
+
_contentType;
|
|
13
|
+
_tableCfg;
|
|
11
14
|
// ...........................................................................
|
|
12
15
|
/**
|
|
13
16
|
* Retrieves the current state of the table.
|
|
@@ -66,6 +69,28 @@ class BaseController {
|
|
|
66
69
|
});
|
|
67
70
|
return rows;
|
|
68
71
|
}
|
|
72
|
+
// ...........................................................................
|
|
73
|
+
/**
|
|
74
|
+
* Gets the content type of the controller.
|
|
75
|
+
* @returns The content type managed by the controller.
|
|
76
|
+
*/
|
|
77
|
+
/* v8 ignore next -- @preserve */
|
|
78
|
+
contentType() {
|
|
79
|
+
return this._contentType ?? "components";
|
|
80
|
+
}
|
|
81
|
+
// ...........................................................................
|
|
82
|
+
/**
|
|
83
|
+
* Gets the table configuration of the controller.
|
|
84
|
+
* @returns The table configuration managed by the controller.
|
|
85
|
+
*/
|
|
86
|
+
tableCfg() {
|
|
87
|
+
if (!this._tableCfg) {
|
|
88
|
+
throw new Error(
|
|
89
|
+
`TableCfg for controller ${this._tableKey} is not initialized.`
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
return this._tableCfg;
|
|
93
|
+
}
|
|
69
94
|
}
|
|
70
95
|
class CakeController extends BaseController {
|
|
71
96
|
constructor(_core, _tableKey, _refs) {
|
|
@@ -73,6 +98,7 @@ class CakeController extends BaseController {
|
|
|
73
98
|
this._core = _core;
|
|
74
99
|
this._tableKey = _tableKey;
|
|
75
100
|
this._refs = _refs;
|
|
101
|
+
this._contentType = "cakes";
|
|
76
102
|
}
|
|
77
103
|
_table = null;
|
|
78
104
|
_baseLayers = {};
|
|
@@ -87,7 +113,8 @@ class CakeController extends BaseController {
|
|
|
87
113
|
if (this._table._type !== "cakes") {
|
|
88
114
|
throw new Error(`Table ${this._tableKey} is not of type cakes.`);
|
|
89
115
|
}
|
|
90
|
-
|
|
116
|
+
this._tableCfg = await this._core.tableCfg(this._tableKey);
|
|
117
|
+
if (this._refs && this._refs.base && this._refs.base !== void 0 && this._refs.base.length > 0) {
|
|
91
118
|
const {
|
|
92
119
|
[this._tableKey]: { _data: baseCakes }
|
|
93
120
|
} = await this._core.readRow(this._tableKey, this._refs.base);
|
|
@@ -98,10 +125,13 @@ class CakeController extends BaseController {
|
|
|
98
125
|
this._baseLayers = rmhsh(baseCake.layers);
|
|
99
126
|
} else {
|
|
100
127
|
const cake = this._table._data[0];
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
128
|
+
if (!!cake) {
|
|
129
|
+
this._refs = {
|
|
130
|
+
sliceIdsTable: cake.sliceIdsTable,
|
|
131
|
+
sliceIdsRow: cake.sliceIdsRow
|
|
132
|
+
};
|
|
133
|
+
this._baseLayers = rmhsh(cake.layers);
|
|
134
|
+
}
|
|
105
135
|
}
|
|
106
136
|
}
|
|
107
137
|
async getChildRefs(where, filter) {
|
|
@@ -126,8 +156,21 @@ class CakeController extends BaseController {
|
|
|
126
156
|
if (!command.startsWith("add")) {
|
|
127
157
|
throw new Error(`Command ${command} is not supported by CakeController.`);
|
|
128
158
|
}
|
|
159
|
+
if (this._refs?.base) delete this._refs.base;
|
|
160
|
+
const normalizedValue = {};
|
|
161
|
+
for (const [layerTable, layerRef] of Object.entries(
|
|
162
|
+
value.layers
|
|
163
|
+
)) {
|
|
164
|
+
if (Array.isArray(layerRef) && layerRef.length > 1) {
|
|
165
|
+
throw new Error(
|
|
166
|
+
`CakeController insert: Layer ref for table ${layerTable} cannot be an array of size > 1. No 1:n relations supported.`
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
normalizedValue[layerTable] = Array.isArray(layerRef) ? layerRef[0] : layerRef;
|
|
170
|
+
}
|
|
129
171
|
const cake = {
|
|
130
|
-
|
|
172
|
+
...value,
|
|
173
|
+
layers: { ...this._baseLayers, ...normalizedValue },
|
|
131
174
|
...refs || this._refs
|
|
132
175
|
};
|
|
133
176
|
const rlJson = { [this._tableKey]: { _data: [cake] } };
|
|
@@ -141,7 +184,7 @@ class CakeController extends BaseController {
|
|
|
141
184
|
//Unique id/timestamp
|
|
142
185
|
timeId: timeId()
|
|
143
186
|
};
|
|
144
|
-
return result;
|
|
187
|
+
return [result];
|
|
145
188
|
}
|
|
146
189
|
async get(where, filter) {
|
|
147
190
|
if (typeof where === "string") {
|
|
@@ -152,7 +195,7 @@ class CakeController extends BaseController {
|
|
|
152
195
|
return Promise.resolve({});
|
|
153
196
|
}
|
|
154
197
|
}
|
|
155
|
-
filterRow(row, key, value) {
|
|
198
|
+
async filterRow(row, key, value) {
|
|
156
199
|
const cake = row;
|
|
157
200
|
for (const [layerKey, layerRef] of Object.entries(cake.layers)) {
|
|
158
201
|
if (layerKey === key && layerRef === value) {
|
|
@@ -168,8 +211,14 @@ class ComponentController extends BaseController {
|
|
|
168
211
|
this._core = _core;
|
|
169
212
|
this._tableKey = _tableKey;
|
|
170
213
|
this._refs = _refs;
|
|
214
|
+
this._contentType = "components";
|
|
171
215
|
}
|
|
172
|
-
|
|
216
|
+
_allowedContentTypes = [
|
|
217
|
+
"components",
|
|
218
|
+
"edits",
|
|
219
|
+
"editHistory",
|
|
220
|
+
"multiEdits"
|
|
221
|
+
];
|
|
173
222
|
_resolvedColumns = null;
|
|
174
223
|
_refTableKeyToColumnKeyMap = null;
|
|
175
224
|
async init() {
|
|
@@ -178,7 +227,7 @@ class ComponentController extends BaseController {
|
|
|
178
227
|
}
|
|
179
228
|
const rljson = await this._core.dumpTable(this._tableKey);
|
|
180
229
|
const table = rljson[this._tableKey];
|
|
181
|
-
if (table._type
|
|
230
|
+
if (this._allowedContentTypes.indexOf(table._type) === -1) {
|
|
182
231
|
throw new Error(`Table ${this._tableKey} is not of type components.`);
|
|
183
232
|
}
|
|
184
233
|
this._tableCfg = await this._core.tableCfg(this._tableKey);
|
|
@@ -197,17 +246,20 @@ class ComponentController extends BaseController {
|
|
|
197
246
|
throw new Error(`Refs are not supported on ComponentController.`);
|
|
198
247
|
}
|
|
199
248
|
const component = value;
|
|
249
|
+
delete component._somethingToInsert;
|
|
200
250
|
const rlJson = { [this._tableKey]: { _data: [component] } };
|
|
201
251
|
await this._core.import(rlJson);
|
|
202
|
-
return
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
252
|
+
return [
|
|
253
|
+
{
|
|
254
|
+
//Ref to component
|
|
255
|
+
[this._tableKey + "Ref"]: hsh(component)._hash,
|
|
256
|
+
//Data from edit
|
|
257
|
+
route: "",
|
|
258
|
+
origin,
|
|
259
|
+
//Unique id/timestamp
|
|
260
|
+
timeId: timeId()
|
|
261
|
+
}
|
|
262
|
+
];
|
|
211
263
|
}
|
|
212
264
|
// ...........................................................................
|
|
213
265
|
/**
|
|
@@ -242,6 +294,20 @@ class ComponentController extends BaseController {
|
|
|
242
294
|
columnKey: propertyKey,
|
|
243
295
|
ref: refItem
|
|
244
296
|
});
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
if (typeof refItem === "object" && refItem !== null) {
|
|
300
|
+
const cakeReference = refItem;
|
|
301
|
+
childRefs.set(
|
|
302
|
+
`${childRefTableKey}|${propertyKey}|${cakeReference.ref}|${cakeReference.sliceIds?.join(",")}`,
|
|
303
|
+
{
|
|
304
|
+
tableKey: childRefTableKey,
|
|
305
|
+
columnKey: propertyKey,
|
|
306
|
+
ref: cakeReference.ref,
|
|
307
|
+
sliceIds: cakeReference.sliceIds
|
|
308
|
+
}
|
|
309
|
+
);
|
|
310
|
+
continue;
|
|
245
311
|
}
|
|
246
312
|
}
|
|
247
313
|
continue;
|
|
@@ -274,7 +340,7 @@ class ComponentController extends BaseController {
|
|
|
274
340
|
const column = Object.keys(refWhere)[0];
|
|
275
341
|
const refValue = refWhere[column];
|
|
276
342
|
for (const row of tableData) {
|
|
277
|
-
if (this.filterRow(row, column, refValue)) {
|
|
343
|
+
if (await this.filterRow(row, column, refValue)) {
|
|
278
344
|
consolidatedRows.set(row._hash, row);
|
|
279
345
|
}
|
|
280
346
|
}
|
|
@@ -473,7 +539,7 @@ class ComponentController extends BaseController {
|
|
|
473
539
|
return this._core.readRows(table, where);
|
|
474
540
|
}
|
|
475
541
|
}
|
|
476
|
-
filterRow(row, key, value) {
|
|
542
|
+
async filterRow(row, key, value) {
|
|
477
543
|
for (const [propertyKey, propertyValue] of Object.entries(row)) {
|
|
478
544
|
if (propertyKey === key && equals(propertyValue, value)) {
|
|
479
545
|
return true;
|
|
@@ -488,12 +554,131 @@ class ComponentController extends BaseController {
|
|
|
488
554
|
return false;
|
|
489
555
|
}
|
|
490
556
|
}
|
|
557
|
+
class SliceIdController extends BaseController {
|
|
558
|
+
constructor(_core, _tableKey, _refs) {
|
|
559
|
+
super(_core, _tableKey);
|
|
560
|
+
this._core = _core;
|
|
561
|
+
this._tableKey = _tableKey;
|
|
562
|
+
this._refs = _refs;
|
|
563
|
+
this._contentType = "sliceIds";
|
|
564
|
+
}
|
|
565
|
+
async init() {
|
|
566
|
+
if (this._tableKey.endsWith("SliceId") === false) {
|
|
567
|
+
throw new Error(
|
|
568
|
+
`Table ${this._tableKey} is not supported by SliceIdController.`
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
const rljson = await this._core.dumpTable(this._tableKey);
|
|
572
|
+
const table = rljson[this._tableKey];
|
|
573
|
+
if (table._type !== "sliceIds") {
|
|
574
|
+
throw new Error(`Table ${this._tableKey} is not of type sliceIds.`);
|
|
575
|
+
}
|
|
576
|
+
this._tableCfg = await this._core.tableCfg(this._tableKey);
|
|
577
|
+
if (this._refs && this._refs.base) {
|
|
578
|
+
const {
|
|
579
|
+
[this._tableKey]: { _data: SliceIds2 }
|
|
580
|
+
} = await this._core.readRow(this._tableKey, this._refs.base);
|
|
581
|
+
if (SliceIds2.length === 0) {
|
|
582
|
+
throw new Error(`Base sliceId ${this._refs.base} does not exist.`);
|
|
583
|
+
}
|
|
584
|
+
} else {
|
|
585
|
+
const sliceId = table._data[0];
|
|
586
|
+
if (!!sliceId) {
|
|
587
|
+
this._refs = {
|
|
588
|
+
base: sliceId.base
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
async insert(command, value, origin, refs) {
|
|
594
|
+
if (!command.startsWith("add") && !command.startsWith("remove")) {
|
|
595
|
+
throw new Error(
|
|
596
|
+
`Command ${command} is not supported by SliceIdController.`
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
const sliceIds = command.startsWith("add") === true ? {
|
|
600
|
+
add: value,
|
|
601
|
+
...refs || this._refs
|
|
602
|
+
} : {
|
|
603
|
+
add: [],
|
|
604
|
+
remove: value,
|
|
605
|
+
...refs || this._refs
|
|
606
|
+
};
|
|
607
|
+
const rlJson = { [this._tableKey]: { _data: [sliceIds] } };
|
|
608
|
+
await this._core.import(rlJson);
|
|
609
|
+
const result = {
|
|
610
|
+
//Ref to component
|
|
611
|
+
[this._tableKey + "Ref"]: hsh(sliceIds)._hash,
|
|
612
|
+
//Data from edit
|
|
613
|
+
route: "",
|
|
614
|
+
origin,
|
|
615
|
+
//Unique id/timestamp
|
|
616
|
+
timeId: timeId()
|
|
617
|
+
};
|
|
618
|
+
return [result];
|
|
619
|
+
}
|
|
620
|
+
async get(where, filter) {
|
|
621
|
+
if (typeof where === "string") {
|
|
622
|
+
return this._getByHash(where, filter);
|
|
623
|
+
} else {
|
|
624
|
+
return this._getByWhere(where, filter);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
async resolveBaseSliceIds(sliceIds) {
|
|
628
|
+
const add = /* @__PURE__ */ new Set();
|
|
629
|
+
const remove = /* @__PURE__ */ new Set();
|
|
630
|
+
if (!!sliceIds.base) {
|
|
631
|
+
const baseSliceIds = await this.get(sliceIds.base);
|
|
632
|
+
if (!baseSliceIds[this._tableKey]?._data?.[0]) {
|
|
633
|
+
throw new Error(`Base sliceIds ${sliceIds.base} does not exist.`);
|
|
634
|
+
}
|
|
635
|
+
if (baseSliceIds[this._tableKey]._data.length > 1) {
|
|
636
|
+
throw new Error(
|
|
637
|
+
`Base sliceIds ${sliceIds.base} has more than one entry.`
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
const baseSliceId = baseSliceIds[this._tableKey]._data[0];
|
|
641
|
+
const resolvedBaseSliceIds = await this.resolveBaseSliceIds(baseSliceId);
|
|
642
|
+
for (const sliceId of resolvedBaseSliceIds.add) {
|
|
643
|
+
add.add(sliceId);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
for (const sliceId of sliceIds.add) {
|
|
647
|
+
add.add(sliceId);
|
|
648
|
+
}
|
|
649
|
+
if (!!sliceIds.remove)
|
|
650
|
+
for (const sliceId of sliceIds.remove) {
|
|
651
|
+
remove.add(sliceId);
|
|
652
|
+
}
|
|
653
|
+
for (const sliceId of remove.values()) {
|
|
654
|
+
if (add.has(sliceId)) {
|
|
655
|
+
add.delete(sliceId);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
return { add: Array.from(add) };
|
|
659
|
+
}
|
|
660
|
+
/* v8 ignore next -- @preserve */
|
|
661
|
+
async getChildRefs() {
|
|
662
|
+
return [];
|
|
663
|
+
}
|
|
664
|
+
async filterRow(row, _, value) {
|
|
665
|
+
const sliceIds = row;
|
|
666
|
+
const sliceId = value;
|
|
667
|
+
for (const sId of Object.values(sliceIds.add)) {
|
|
668
|
+
if (sliceId === sId) {
|
|
669
|
+
return true;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
return false;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
491
675
|
class LayerController extends BaseController {
|
|
492
676
|
constructor(_core, _tableKey, _refs) {
|
|
493
677
|
super(_core, _tableKey);
|
|
494
678
|
this._core = _core;
|
|
495
679
|
this._tableKey = _tableKey;
|
|
496
680
|
this._refs = _refs;
|
|
681
|
+
this._contentType = "layers";
|
|
497
682
|
}
|
|
498
683
|
async init() {
|
|
499
684
|
if (this._tableKey.endsWith("Layer") === false) {
|
|
@@ -506,6 +691,7 @@ class LayerController extends BaseController {
|
|
|
506
691
|
if (table._type !== "layers") {
|
|
507
692
|
throw new Error(`Table ${this._tableKey} is not of type layers.`);
|
|
508
693
|
}
|
|
694
|
+
this._tableCfg = await this._core.tableCfg(this._tableKey);
|
|
509
695
|
if (this._refs && this._refs.base) {
|
|
510
696
|
const {
|
|
511
697
|
[this._tableKey]: { _data: baseLayers }
|
|
@@ -523,11 +709,13 @@ class LayerController extends BaseController {
|
|
|
523
709
|
}
|
|
524
710
|
} else {
|
|
525
711
|
const layer = table._data[0];
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
712
|
+
if (!!layer) {
|
|
713
|
+
this._refs = {
|
|
714
|
+
sliceIdsTable: layer.sliceIdsTable,
|
|
715
|
+
sliceIdsTableRow: layer.sliceIdsTableRow,
|
|
716
|
+
componentsTable: layer.componentsTable
|
|
717
|
+
};
|
|
718
|
+
}
|
|
531
719
|
}
|
|
532
720
|
}
|
|
533
721
|
async insert(command, value, origin, refs) {
|
|
@@ -536,13 +724,30 @@ class LayerController extends BaseController {
|
|
|
536
724
|
`Command ${command} is not supported by LayerController.`
|
|
537
725
|
);
|
|
538
726
|
}
|
|
539
|
-
const
|
|
540
|
-
|
|
541
|
-
|
|
727
|
+
const isAdd = command.startsWith("add");
|
|
728
|
+
const normalizedValue = {};
|
|
729
|
+
for (const [sliceId, compRef] of isAdd ? Object.entries(value.add) : Object.entries(value.remove)) {
|
|
730
|
+
if (Array.isArray(compRef) && compRef.length > 1) {
|
|
731
|
+
throw new Error(
|
|
732
|
+
`LayerController insert: Component ref for slice ${sliceId} cannot be an array of size > 1. No 1:n relations supported.`
|
|
733
|
+
);
|
|
734
|
+
}
|
|
735
|
+
normalizedValue[sliceId] = Array.isArray(compRef) ? compRef[0] : compRef;
|
|
736
|
+
}
|
|
737
|
+
const layer = isAdd ? {
|
|
738
|
+
...value,
|
|
739
|
+
...{
|
|
740
|
+
add: normalizedValue,
|
|
741
|
+
remove: {}
|
|
742
|
+
},
|
|
743
|
+
...refs
|
|
542
744
|
} : {
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
745
|
+
...value,
|
|
746
|
+
...{
|
|
747
|
+
remove: normalizedValue,
|
|
748
|
+
add: {}
|
|
749
|
+
},
|
|
750
|
+
...refs
|
|
546
751
|
};
|
|
547
752
|
const rlJson = { [this._tableKey]: { _data: [layer] } };
|
|
548
753
|
await this._core.import(rlJson);
|
|
@@ -555,7 +760,7 @@ class LayerController extends BaseController {
|
|
|
555
760
|
//Unique id/timestamp
|
|
556
761
|
timeId: timeId()
|
|
557
762
|
};
|
|
558
|
-
return result;
|
|
763
|
+
return [result];
|
|
559
764
|
}
|
|
560
765
|
async get(where, filter) {
|
|
561
766
|
if (typeof where === "string") {
|
|
@@ -564,25 +769,108 @@ class LayerController extends BaseController {
|
|
|
564
769
|
return this._getByWhere(where, filter);
|
|
565
770
|
}
|
|
566
771
|
}
|
|
772
|
+
async resolveBaseLayer(layer) {
|
|
773
|
+
const add = /* @__PURE__ */ new Map();
|
|
774
|
+
const sliceIds = /* @__PURE__ */ new Set();
|
|
775
|
+
if (!!layer.base) {
|
|
776
|
+
const baseLayer = await this.get(layer.base);
|
|
777
|
+
if (!baseLayer[this._tableKey]?._data?.[0]) {
|
|
778
|
+
throw new Error(`Base layer ${layer.base} does not exist.`);
|
|
779
|
+
}
|
|
780
|
+
if (baseLayer[this._tableKey]._data.length > 1) {
|
|
781
|
+
throw new Error(
|
|
782
|
+
`Base layer ${layer.base} resolving not possible. Not unique.`
|
|
783
|
+
);
|
|
784
|
+
}
|
|
785
|
+
const baseLayerData = rmhsh(baseLayer[this._tableKey]._data[0]);
|
|
786
|
+
const baseLayerResolved = await this.resolveBaseLayer(baseLayerData);
|
|
787
|
+
for (const [sliceId, compRef] of Object.entries(baseLayerResolved.add)) {
|
|
788
|
+
if (sliceId.startsWith("_")) continue;
|
|
789
|
+
add.set(sliceId, compRef);
|
|
790
|
+
}
|
|
791
|
+
for (const sliceId of baseLayerResolved.sliceIds) {
|
|
792
|
+
sliceIds.add(sliceId);
|
|
793
|
+
}
|
|
794
|
+
const baseLayerSliceIdsTable = baseLayerData.sliceIdsTable;
|
|
795
|
+
const baseLayerSliceIdsRow = baseLayerData.sliceIdsTableRow;
|
|
796
|
+
const {
|
|
797
|
+
[baseLayerSliceIdsTable]: { _data: baseLayerSliceIds }
|
|
798
|
+
} = await this._core.readRow(
|
|
799
|
+
baseLayerSliceIdsTable,
|
|
800
|
+
baseLayerSliceIdsRow
|
|
801
|
+
);
|
|
802
|
+
for (const sIds of baseLayerSliceIds) {
|
|
803
|
+
const sliceIdController = new SliceIdController(
|
|
804
|
+
this._core,
|
|
805
|
+
baseLayerSliceIdsTable
|
|
806
|
+
);
|
|
807
|
+
const resolvedSliceIds = await sliceIdController.resolveBaseSliceIds(
|
|
808
|
+
sIds
|
|
809
|
+
);
|
|
810
|
+
for (const sId of resolvedSliceIds.add) {
|
|
811
|
+
if (sId.startsWith("_")) continue;
|
|
812
|
+
sliceIds.add(sId);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
const {
|
|
817
|
+
[layer.sliceIdsTable]: { _data: layerSliceIds }
|
|
818
|
+
} = await this._core.readRow(layer.sliceIdsTable, layer.sliceIdsTableRow);
|
|
819
|
+
if (!layerSliceIds || layerSliceIds.length === 0) {
|
|
820
|
+
throw new Error(
|
|
821
|
+
`Layer sliceIds ${layer.sliceIdsTableRow} does not exist.`
|
|
822
|
+
);
|
|
823
|
+
}
|
|
824
|
+
if (layerSliceIds.length > 1) {
|
|
825
|
+
throw new Error(
|
|
826
|
+
`Layer sliceIds ${layer.sliceIdsTableRow} has more than one entry.`
|
|
827
|
+
);
|
|
828
|
+
}
|
|
829
|
+
const layerSliceId = layerSliceIds[0];
|
|
830
|
+
for (const sId of layerSliceId.add) {
|
|
831
|
+
if (sId.startsWith("_")) continue;
|
|
832
|
+
sliceIds.add(sId);
|
|
833
|
+
}
|
|
834
|
+
if (!!layerSliceId.remove)
|
|
835
|
+
for (const sId of Object.keys(layerSliceId.remove)) {
|
|
836
|
+
if (sliceIds.has(sId)) {
|
|
837
|
+
sliceIds.delete(sId);
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
for (const [sliceId, compRef] of Object.entries(layer.add)) {
|
|
841
|
+
if (sliceId.startsWith("_")) continue;
|
|
842
|
+
add.set(sliceId, compRef);
|
|
843
|
+
}
|
|
844
|
+
if (!!layer.remove)
|
|
845
|
+
for (const sliceId of Object.keys(layer.remove)) {
|
|
846
|
+
if (add.has(sliceId)) {
|
|
847
|
+
add.delete(sliceId);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
return { add: Object.fromEntries(add), sliceIds: Array.from(sliceIds) };
|
|
851
|
+
}
|
|
567
852
|
async getChildRefs(where, filter) {
|
|
568
853
|
const { [this._tableKey]: table } = await this.get(where, filter);
|
|
569
854
|
const childRefs = [];
|
|
570
855
|
for (const row of table._data) {
|
|
571
856
|
const layer = row;
|
|
572
|
-
|
|
857
|
+
const resolvedLayer = await this.resolveBaseLayer(layer);
|
|
858
|
+
for (const [sliceId, ref] of Object.entries(resolvedLayer.add)) {
|
|
573
859
|
if (sliceId.startsWith("_")) continue;
|
|
574
860
|
childRefs.push({
|
|
575
861
|
tableKey: layer.componentsTable,
|
|
576
|
-
ref
|
|
862
|
+
ref,
|
|
863
|
+
sliceIds: [sliceId]
|
|
577
864
|
});
|
|
578
865
|
}
|
|
579
866
|
}
|
|
580
867
|
return childRefs;
|
|
581
868
|
}
|
|
582
|
-
filterRow(row, _, value) {
|
|
869
|
+
async filterRow(row, _, value) {
|
|
583
870
|
const layer = row;
|
|
584
871
|
const compRef = value;
|
|
585
|
-
|
|
872
|
+
const resolvedLayer = await this.resolveBaseLayer(layer);
|
|
873
|
+
for (const componentRef of Object.values(resolvedLayer.add)) {
|
|
586
874
|
if (componentRef === compRef) {
|
|
587
875
|
return true;
|
|
588
876
|
}
|
|
@@ -597,11 +885,21 @@ const createController = async (type, core, tableKey, refs) => {
|
|
|
597
885
|
ctrl = new LayerController(core, tableKey, refs);
|
|
598
886
|
break;
|
|
599
887
|
case "components":
|
|
888
|
+
case "edits":
|
|
889
|
+
case "editHistory":
|
|
890
|
+
case "multiEdits":
|
|
600
891
|
ctrl = new ComponentController(core, tableKey, refs);
|
|
601
892
|
break;
|
|
602
893
|
case "cakes":
|
|
603
894
|
ctrl = new CakeController(core, tableKey, refs);
|
|
604
895
|
break;
|
|
896
|
+
case "sliceIds":
|
|
897
|
+
ctrl = new SliceIdController(
|
|
898
|
+
core,
|
|
899
|
+
tableKey,
|
|
900
|
+
refs
|
|
901
|
+
);
|
|
902
|
+
break;
|
|
605
903
|
default:
|
|
606
904
|
throw new Error(`Controller for type ${type} is not implemented yet.`);
|
|
607
905
|
}
|
|
@@ -665,7 +963,7 @@ class Core {
|
|
|
665
963
|
const validate = new Validate();
|
|
666
964
|
validate.addValidator(new BaseValidator());
|
|
667
965
|
const result = await validate.run(data);
|
|
668
|
-
if ((result.hasErrors || result.base && result.base.hasErrors) && !result.base.refsNotFound) {
|
|
966
|
+
if ((result.hasErrors || result.base && result.base.hasErrors) && !result.base.refsNotFound && !result.base.layerBasesNotFound) {
|
|
669
967
|
throw new Error(
|
|
670
968
|
"The imported rljson data is not valid:\n" + JSON.stringify(result, null, 2)
|
|
671
969
|
);
|
|
@@ -711,6 +1009,152 @@ class Core {
|
|
|
711
1009
|
return this._io.readRows({ table, where });
|
|
712
1010
|
}
|
|
713
1011
|
}
|
|
1012
|
+
const inject = (tree, path, value) => {
|
|
1013
|
+
for (let i = 0; i < path.length; i++) {
|
|
1014
|
+
const segment = path[i];
|
|
1015
|
+
if (i === path.length - 1) {
|
|
1016
|
+
tree[segment] = value;
|
|
1017
|
+
delete tree["_hash"];
|
|
1018
|
+
} else {
|
|
1019
|
+
if (!tree[segment]) {
|
|
1020
|
+
tree[segment] = {};
|
|
1021
|
+
}
|
|
1022
|
+
tree = tree[segment];
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
};
|
|
1026
|
+
const isolate = (tree, path, preservedKeys = []) => {
|
|
1027
|
+
if (path.length === 0) {
|
|
1028
|
+
return Array.isArray(tree) ? [] : {};
|
|
1029
|
+
}
|
|
1030
|
+
if (tree == null) {
|
|
1031
|
+
return null;
|
|
1032
|
+
}
|
|
1033
|
+
const [currentKey, ...remainingPath] = path;
|
|
1034
|
+
const result = Array.isArray(tree) ? [] : {};
|
|
1035
|
+
if (!Array.isArray(tree)) {
|
|
1036
|
+
for (const key in tree) {
|
|
1037
|
+
if (typeof key === "string" && key.startsWith("_") || preservedKeys.includes(key)) {
|
|
1038
|
+
result[key] = tree[key];
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
if (!(currentKey in tree)) {
|
|
1043
|
+
return result;
|
|
1044
|
+
}
|
|
1045
|
+
const currentValue = tree[currentKey];
|
|
1046
|
+
if (remainingPath.length === 0) {
|
|
1047
|
+
if (Array.isArray(result)) {
|
|
1048
|
+
result[currentKey] = currentValue;
|
|
1049
|
+
} else {
|
|
1050
|
+
result[currentKey] = currentValue;
|
|
1051
|
+
}
|
|
1052
|
+
} else {
|
|
1053
|
+
const isolatedChild = isolate(currentValue, remainingPath, preservedKeys);
|
|
1054
|
+
const hasContent = Array.isArray(isolatedChild) ? isolatedChild.length > 0 : Object.keys(isolatedChild).length > 0;
|
|
1055
|
+
if (hasContent || isolatedChild === null) {
|
|
1056
|
+
if (Array.isArray(result)) {
|
|
1057
|
+
result[currentKey] = isolatedChild;
|
|
1058
|
+
} else {
|
|
1059
|
+
result[currentKey] = isolatedChild;
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
return result;
|
|
1064
|
+
};
|
|
1065
|
+
const mergeTrees = (trees) => {
|
|
1066
|
+
if (!trees || trees.length === 0) {
|
|
1067
|
+
return {};
|
|
1068
|
+
}
|
|
1069
|
+
let result = {};
|
|
1070
|
+
for (const { tree } of trees) {
|
|
1071
|
+
if (tree != null) {
|
|
1072
|
+
result = mergeStructures(result, tree);
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
const pathValues = [];
|
|
1076
|
+
for (const { tree, path } of trees) {
|
|
1077
|
+
if (tree == null) continue;
|
|
1078
|
+
let current = tree;
|
|
1079
|
+
let pathExists = true;
|
|
1080
|
+
for (const key of path) {
|
|
1081
|
+
if (current == null || !(key in current)) {
|
|
1082
|
+
pathExists = false;
|
|
1083
|
+
break;
|
|
1084
|
+
}
|
|
1085
|
+
current = current[key];
|
|
1086
|
+
}
|
|
1087
|
+
if (pathExists && current != null) {
|
|
1088
|
+
pathValues.push({ path, value: current });
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
const pathGroups = /* @__PURE__ */ new Map();
|
|
1092
|
+
for (const { path, value } of pathValues) {
|
|
1093
|
+
const pathKey = JSON.stringify(path);
|
|
1094
|
+
if (!pathGroups.has(pathKey)) {
|
|
1095
|
+
pathGroups.set(pathKey, []);
|
|
1096
|
+
}
|
|
1097
|
+
pathGroups.get(pathKey).push(value);
|
|
1098
|
+
}
|
|
1099
|
+
for (const [pathKey, values] of pathGroups) {
|
|
1100
|
+
const path = JSON.parse(pathKey);
|
|
1101
|
+
let mergedValue = void 0;
|
|
1102
|
+
for (const value of values) {
|
|
1103
|
+
if (value == null) continue;
|
|
1104
|
+
if (mergedValue === void 0) {
|
|
1105
|
+
mergedValue = value;
|
|
1106
|
+
continue;
|
|
1107
|
+
}
|
|
1108
|
+
if (Array.isArray(mergedValue) && Array.isArray(value)) {
|
|
1109
|
+
mergedValue = [...mergedValue, ...value];
|
|
1110
|
+
} else if (!Array.isArray(mergedValue) && !Array.isArray(value)) {
|
|
1111
|
+
mergedValue = {
|
|
1112
|
+
...mergedValue,
|
|
1113
|
+
...value
|
|
1114
|
+
};
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
let current = result;
|
|
1118
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
1119
|
+
const key = path[i];
|
|
1120
|
+
if (current == null || !(key in current)) {
|
|
1121
|
+
current[key] = typeof path[i + 1] === "number" ? [] : {};
|
|
1122
|
+
}
|
|
1123
|
+
current = current[key];
|
|
1124
|
+
}
|
|
1125
|
+
if (path.length > 0) {
|
|
1126
|
+
current[path[path.length - 1]] = mergedValue;
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
return result;
|
|
1130
|
+
};
|
|
1131
|
+
function mergeStructures(target, source) {
|
|
1132
|
+
if (source == null) return target;
|
|
1133
|
+
if (target == null) return source;
|
|
1134
|
+
if (Array.isArray(target) && Array.isArray(source)) {
|
|
1135
|
+
const result = [...target];
|
|
1136
|
+
for (let i = 0; i < source.length; i++) {
|
|
1137
|
+
if (result[i] === void 0) {
|
|
1138
|
+
result[i] = source[i];
|
|
1139
|
+
} else {
|
|
1140
|
+
result[i] = mergeStructures(result[i], source[i]);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
return result;
|
|
1144
|
+
}
|
|
1145
|
+
if (typeof target === "object" && typeof source === "object" && !Array.isArray(target) && !Array.isArray(source) && target !== null && source !== null) {
|
|
1146
|
+
const result = { ...target };
|
|
1147
|
+
for (const key in source) {
|
|
1148
|
+
if (key in result) {
|
|
1149
|
+
result[key] = mergeStructures(result[key], source[key]);
|
|
1150
|
+
} else {
|
|
1151
|
+
result[key] = source[key];
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
return result;
|
|
1155
|
+
}
|
|
1156
|
+
return source;
|
|
1157
|
+
}
|
|
714
1158
|
class ColumnSelection {
|
|
715
1159
|
constructor(columns) {
|
|
716
1160
|
this._throwOnWrongAlias(columns);
|
|
@@ -975,53 +1419,156 @@ class ColumnSelection {
|
|
|
975
1419
|
static exampleCarsColumnSelection() {
|
|
976
1420
|
return new ColumnSelection([
|
|
977
1421
|
{
|
|
978
|
-
route: "carCake/carGeneralLayer/carGeneral/brand",
|
|
979
1422
|
key: "brand",
|
|
1423
|
+
route: "carCake/carGeneralLayer/carGeneral/brand",
|
|
980
1424
|
alias: "brand",
|
|
981
|
-
titleShort: "Brand",
|
|
982
1425
|
titleLong: "Car Brand",
|
|
983
|
-
|
|
1426
|
+
titleShort: "Brand",
|
|
1427
|
+
type: "string",
|
|
1428
|
+
_hash: ""
|
|
984
1429
|
},
|
|
985
1430
|
{
|
|
986
|
-
route: "carCake/carGeneralLayer/carGeneral/type",
|
|
987
1431
|
key: "type",
|
|
1432
|
+
route: "carCake/carGeneralLayer/carGeneral/type",
|
|
988
1433
|
alias: "type",
|
|
989
|
-
titleShort: "Type",
|
|
990
1434
|
titleLong: "Car Type",
|
|
991
|
-
|
|
1435
|
+
titleShort: "Type",
|
|
1436
|
+
type: "string",
|
|
1437
|
+
_hash: ""
|
|
1438
|
+
},
|
|
1439
|
+
{
|
|
1440
|
+
key: "serviceIntervals",
|
|
1441
|
+
route: "carCake/carGeneralLayer/carGeneral/serviceIntervals",
|
|
1442
|
+
alias: "serviceIntervals",
|
|
1443
|
+
titleLong: "Car Service Intervals",
|
|
1444
|
+
titleShort: "Service Intervals",
|
|
1445
|
+
type: "jsonValue",
|
|
1446
|
+
_hash: ""
|
|
992
1447
|
},
|
|
993
1448
|
{
|
|
994
|
-
route: "carCake/carGeneralLayer/carGeneral/isElectric",
|
|
995
1449
|
key: "isElectric",
|
|
1450
|
+
route: "carCake/carGeneralLayer/carGeneral/isElectric",
|
|
996
1451
|
alias: "isElectric",
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
type: "boolean"
|
|
1452
|
+
titleLong: "Is Electric Car",
|
|
1453
|
+
titleShort: "Electric",
|
|
1454
|
+
type: "boolean",
|
|
1455
|
+
_hash: ""
|
|
1456
|
+
},
|
|
1457
|
+
{
|
|
1458
|
+
key: "carDimensions/height",
|
|
1459
|
+
route: "carCake/carTechnicalLayer/carTechnical/carDimensions/height",
|
|
1460
|
+
alias: "height",
|
|
1461
|
+
titleLong: "Car Height",
|
|
1462
|
+
titleShort: "Height",
|
|
1463
|
+
type: "number",
|
|
1464
|
+
_hash: ""
|
|
1465
|
+
},
|
|
1466
|
+
{
|
|
1467
|
+
key: "carDimensions/width",
|
|
1468
|
+
route: "carCake/carTechnicalLayer/carTechnical/carDimensions/width",
|
|
1469
|
+
alias: "width",
|
|
1470
|
+
titleLong: "Car Width",
|
|
1471
|
+
titleShort: "Width",
|
|
1472
|
+
type: "number",
|
|
1473
|
+
_hash: ""
|
|
1474
|
+
},
|
|
1475
|
+
{
|
|
1476
|
+
key: "carDimensions/length",
|
|
1477
|
+
route: "carCake/carTechnicalLayer/carTechnical/carDimensions/length",
|
|
1478
|
+
alias: "length",
|
|
1479
|
+
titleLong: "Car Length",
|
|
1480
|
+
titleShort: "Length",
|
|
1481
|
+
type: "number",
|
|
1482
|
+
_hash: ""
|
|
1000
1483
|
},
|
|
1001
1484
|
{
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
alias: "
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
type: "string"
|
|
1485
|
+
key: "engine",
|
|
1486
|
+
route: "carCake/carTechnicalLayer/carTechnical/engine",
|
|
1487
|
+
alias: "engine",
|
|
1488
|
+
titleLong: "Car Engine",
|
|
1489
|
+
titleShort: "Engine",
|
|
1490
|
+
type: "string",
|
|
1491
|
+
_hash: ""
|
|
1008
1492
|
},
|
|
1009
1493
|
{
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
alias: "
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
type: "
|
|
1494
|
+
key: "repairedByWorkshop",
|
|
1495
|
+
route: "carCake/carTechnicalLayer/carTechnical/repairedByWorkshop",
|
|
1496
|
+
alias: "repairedByWorkshop",
|
|
1497
|
+
titleLong: "Was Repaired By Workshop",
|
|
1498
|
+
titleShort: "Repaired By Workshop",
|
|
1499
|
+
type: "boolean",
|
|
1500
|
+
_hash: ""
|
|
1016
1501
|
}
|
|
1017
1502
|
]);
|
|
1018
1503
|
}
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1504
|
+
static exampleCarsDeeplyNestedColumnSelection() {
|
|
1505
|
+
return new ColumnSelection([
|
|
1506
|
+
{
|
|
1507
|
+
key: "brand",
|
|
1508
|
+
route: "catalogCake/catalogSeriesLayer/catalogSeries/seriesCake/seriesCarsLayer/seriesCars/carCake/carGeneralLayer/carGeneral/brand",
|
|
1509
|
+
alias: "brand",
|
|
1510
|
+
titleLong: "Car Brand",
|
|
1511
|
+
titleShort: "Brand",
|
|
1512
|
+
type: "string",
|
|
1513
|
+
_hash: ""
|
|
1514
|
+
}
|
|
1515
|
+
]);
|
|
1022
1516
|
}
|
|
1023
|
-
|
|
1024
|
-
|
|
1517
|
+
static exampleCarsColumnSelectionOnlySomeColumns() {
|
|
1518
|
+
return new ColumnSelection([
|
|
1519
|
+
{
|
|
1520
|
+
key: "brand",
|
|
1521
|
+
route: "carCake/carGeneralLayer/carGeneral/brand",
|
|
1522
|
+
alias: "brand",
|
|
1523
|
+
titleLong: "Car Brand",
|
|
1524
|
+
titleShort: "Brand",
|
|
1525
|
+
type: "string",
|
|
1526
|
+
_hash: ""
|
|
1527
|
+
},
|
|
1528
|
+
{
|
|
1529
|
+
key: "type",
|
|
1530
|
+
route: "carCake/carGeneralLayer/carGeneral/type",
|
|
1531
|
+
alias: "type",
|
|
1532
|
+
titleLong: "Car Type",
|
|
1533
|
+
titleShort: "Type",
|
|
1534
|
+
type: "string",
|
|
1535
|
+
_hash: ""
|
|
1536
|
+
},
|
|
1537
|
+
{
|
|
1538
|
+
key: "serviceIntervals",
|
|
1539
|
+
route: "carCake/carGeneralLayer/carGeneral/serviceIntervals",
|
|
1540
|
+
alias: "serviceIntervals",
|
|
1541
|
+
titleLong: "Car Service Intervals",
|
|
1542
|
+
titleShort: "Service Intervals",
|
|
1543
|
+
type: "jsonValue",
|
|
1544
|
+
_hash: ""
|
|
1545
|
+
},
|
|
1546
|
+
{
|
|
1547
|
+
key: "isElectric",
|
|
1548
|
+
route: "carCake/carGeneralLayer/carGeneral/isElectric",
|
|
1549
|
+
alias: "isElectric",
|
|
1550
|
+
titleLong: "Is Electric Car",
|
|
1551
|
+
titleShort: "Electric",
|
|
1552
|
+
type: "boolean",
|
|
1553
|
+
_hash: ""
|
|
1554
|
+
},
|
|
1555
|
+
{
|
|
1556
|
+
key: "carDimensions/length",
|
|
1557
|
+
route: "carCake/carTechnicalLayer/carTechnical/carDimensions/length",
|
|
1558
|
+
alias: "length",
|
|
1559
|
+
titleLong: "Car Length",
|
|
1560
|
+
titleShort: "Length",
|
|
1561
|
+
type: "number",
|
|
1562
|
+
_hash: ""
|
|
1563
|
+
}
|
|
1564
|
+
]);
|
|
1565
|
+
}
|
|
1566
|
+
// ...........................................................................
|
|
1567
|
+
static empty() {
|
|
1568
|
+
return new ColumnSelection([]);
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
const trueValues = ["t", "j", "y"];
|
|
1025
1572
|
const falseValues = ["n", "f"];
|
|
1026
1573
|
const parseBooleanSearch = (search) => {
|
|
1027
1574
|
if (typeof search == "undefined" || search == null) {
|
|
@@ -1492,9 +2039,11 @@ class RowFilterProcessor {
|
|
|
1492
2039
|
return remainingIndices;
|
|
1493
2040
|
}
|
|
1494
2041
|
for (const i of remainingIndices) {
|
|
1495
|
-
const
|
|
1496
|
-
|
|
1497
|
-
|
|
2042
|
+
const cellValues = join.value(i, columnIndex);
|
|
2043
|
+
for (const cellValue of cellValues) {
|
|
2044
|
+
if (filter.matches(cellValue)) {
|
|
2045
|
+
result.push(i);
|
|
2046
|
+
}
|
|
1498
2047
|
}
|
|
1499
2048
|
}
|
|
1500
2049
|
return result;
|
|
@@ -1532,9 +2081,11 @@ class RowFilterProcessor {
|
|
|
1532
2081
|
if (applyTo[r]) {
|
|
1533
2082
|
continue;
|
|
1534
2083
|
}
|
|
1535
|
-
const
|
|
1536
|
-
|
|
1537
|
-
|
|
2084
|
+
const cellValues = join.value(r, columnIndex);
|
|
2085
|
+
for (const cellValue of cellValues) {
|
|
2086
|
+
if (filter.matches(cellValue)) {
|
|
2087
|
+
applyTo[r] = true;
|
|
2088
|
+
}
|
|
1538
2089
|
}
|
|
1539
2090
|
}
|
|
1540
2091
|
}
|
|
@@ -1545,7 +2096,7 @@ class RowFilterProcessor {
|
|
|
1545
2096
|
const route = item.route;
|
|
1546
2097
|
if (availableRoutes.includes(route) === false) {
|
|
1547
2098
|
throw new Error(
|
|
1548
|
-
`RowFilterProcessor: Error while applying filter to
|
|
2099
|
+
`RowFilterProcessor: Error while applying filter to join: There is a column filter for route "${route}", but the join does not have a column with this route.
|
|
1549
2100
|
|
|
1550
2101
|
Available routes:
|
|
1551
2102
|
${availableRoutes.map((a) => `- ${a}`).join("\n")}`
|
|
@@ -1554,13 +2105,22 @@ ${availableRoutes.map((a) => `- ${a}`).join("\n")}`
|
|
|
1554
2105
|
}
|
|
1555
2106
|
}
|
|
1556
2107
|
}
|
|
2108
|
+
const joinPreserveKeys = [
|
|
2109
|
+
"sliceIdsTable",
|
|
2110
|
+
"sliceIdsRow",
|
|
2111
|
+
/*'base',*/
|
|
2112
|
+
"sliceIdsTable",
|
|
2113
|
+
"sliceIdsTableRow",
|
|
2114
|
+
"componentsTable"
|
|
2115
|
+
];
|
|
1557
2116
|
class Join {
|
|
1558
|
-
constructor(baseRows, _baseColumnSelection) {
|
|
1559
|
-
this._baseColumnSelection = _baseColumnSelection;
|
|
1560
|
-
this._base = this._hashedRows(baseRows);
|
|
1561
|
-
}
|
|
1562
2117
|
_base = {};
|
|
2118
|
+
_baseColumnSelection;
|
|
1563
2119
|
_processes = [];
|
|
2120
|
+
constructor(rows, columnSelection) {
|
|
2121
|
+
this._base = this._hashedRows(rows);
|
|
2122
|
+
this._baseColumnSelection = columnSelection;
|
|
2123
|
+
}
|
|
1564
2124
|
// ...........................................................................
|
|
1565
2125
|
/**
|
|
1566
2126
|
* Applies a filter to the join and returns the filtered view
|
|
@@ -1589,14 +2149,62 @@ class Join {
|
|
|
1589
2149
|
const data = {};
|
|
1590
2150
|
for (const [sliceId, joinRowH] of Object.entries(this.data)) {
|
|
1591
2151
|
const cols = [...joinRowH.columns];
|
|
2152
|
+
const insertCols = [];
|
|
1592
2153
|
for (const col of cols) {
|
|
1593
|
-
|
|
1594
|
-
col
|
|
1595
|
-
|
|
2154
|
+
const insertCol = {
|
|
2155
|
+
...col
|
|
2156
|
+
//inserts: col.inserts ? [...col.inserts] : [],
|
|
2157
|
+
};
|
|
2158
|
+
if (Route.fromFlat(setValue.route).equalsWithoutRefs(col.route)) {
|
|
2159
|
+
for (const cell of col.value.cell) {
|
|
2160
|
+
if (cell.path.length === 0) {
|
|
2161
|
+
throw new Error(
|
|
2162
|
+
`Join: Error while applying SetValue: Cannot set value for column without paths. Route: ${setValue.route.toString()}.`
|
|
2163
|
+
);
|
|
2164
|
+
}
|
|
2165
|
+
if (cell.path.length > 1) {
|
|
2166
|
+
throw new Error(
|
|
2167
|
+
`Join: Error while applying SetValue: Cannot set value for multiple paths in one cell. Found paths: [${cell.path.join(", ")}] for route: ${setValue.route.toString()}.`
|
|
2168
|
+
);
|
|
2169
|
+
}
|
|
2170
|
+
const cellInsertTree = isolate(
|
|
2171
|
+
{ ...col.value.tree },
|
|
2172
|
+
cell.path[0],
|
|
2173
|
+
joinPreserveKeys
|
|
2174
|
+
);
|
|
2175
|
+
inject(cellInsertTree, cell.path[0], setValue.value);
|
|
2176
|
+
const propertyKey = cell.path[0].slice(-1)[0];
|
|
2177
|
+
const insert = {
|
|
2178
|
+
cell: [
|
|
2179
|
+
{
|
|
2180
|
+
...cell,
|
|
2181
|
+
...{ value: setValue.value },
|
|
2182
|
+
...{
|
|
2183
|
+
row: {
|
|
2184
|
+
...cell.row,
|
|
2185
|
+
...{ [propertyKey]: setValue.value }
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
],
|
|
2190
|
+
tree: cellInsertTree,
|
|
2191
|
+
rljson: col.value.rljson
|
|
2192
|
+
};
|
|
2193
|
+
if (insert) {
|
|
2194
|
+
if (insertCol.inserts) insertCol.inserts.push(insert);
|
|
2195
|
+
else insertCol.inserts = [insert];
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
insertCols.push(insertCol);
|
|
1596
2200
|
}
|
|
1597
2201
|
data[sliceId] = {
|
|
1598
|
-
rowHash: Hash.default.calcHash(
|
|
1599
|
-
|
|
2202
|
+
rowHash: Hash.default.calcHash(
|
|
2203
|
+
insertCols.map(
|
|
2204
|
+
(col) => col.value.cell.flatMap((c) => c.value)
|
|
2205
|
+
)
|
|
2206
|
+
),
|
|
2207
|
+
columns: insertCols
|
|
1600
2208
|
};
|
|
1601
2209
|
}
|
|
1602
2210
|
const process = {
|
|
@@ -1639,15 +2247,15 @@ class Join {
|
|
|
1639
2247
|
const data = {};
|
|
1640
2248
|
for (let i2 = 0; i2 < this.rowCount; i2++) {
|
|
1641
2249
|
const [sliceId, row] = Object.entries(this.data)[i2];
|
|
1642
|
-
const
|
|
2250
|
+
const cols = [];
|
|
1643
2251
|
for (let j = 0; j < masterColumnIndices.length; j++) {
|
|
1644
|
-
|
|
2252
|
+
cols.push(row.columns[masterColumnIndices[j]]);
|
|
1645
2253
|
}
|
|
1646
2254
|
data[sliceId] = {
|
|
1647
2255
|
rowHash: Hash.default.calcHash(
|
|
1648
|
-
|
|
2256
|
+
cols.map((col) => col.value.cell.flatMap((c) => c.value))
|
|
1649
2257
|
),
|
|
1650
|
-
columns:
|
|
2258
|
+
columns: cols
|
|
1651
2259
|
};
|
|
1652
2260
|
}
|
|
1653
2261
|
const process = {
|
|
@@ -1686,16 +2294,51 @@ class Join {
|
|
|
1686
2294
|
* Returns insert Object of the join
|
|
1687
2295
|
*/
|
|
1688
2296
|
insert() {
|
|
1689
|
-
const
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
2297
|
+
const inserts = [];
|
|
2298
|
+
for (let i = 0; i < this.columnCount; i++) {
|
|
2299
|
+
const colInserts = [];
|
|
2300
|
+
for (const row of Object.values(this.data)) {
|
|
2301
|
+
const col = row.columns[i];
|
|
2302
|
+
if (col.inserts && col.inserts.length > 0) {
|
|
2303
|
+
for (const insert of col.inserts) {
|
|
2304
|
+
for (const cell of insert.cell) {
|
|
2305
|
+
const tree = insert.tree;
|
|
2306
|
+
const path = cell.path;
|
|
2307
|
+
inject(tree, path[0].slice(0, -1), cell.row);
|
|
2308
|
+
colInserts.push({
|
|
2309
|
+
route: col.route,
|
|
2310
|
+
tree,
|
|
2311
|
+
path: path[0]
|
|
2312
|
+
});
|
|
2313
|
+
}
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2316
|
+
}
|
|
2317
|
+
if (colInserts.length === 0) continue;
|
|
2318
|
+
const routes = colInserts.map((ins) => ins.route.flat);
|
|
2319
|
+
const uniqueRoute = Array.from(new Set(routes));
|
|
2320
|
+
if (uniqueRoute.length > 1) {
|
|
2321
|
+
throw new Error(
|
|
2322
|
+
`Join: Error while generating insert: Multiple different routes found in inserts: ${uniqueRoute.map((r) => r.toString()).join(", ")}. Cannot generate single insert object.`
|
|
2323
|
+
);
|
|
2324
|
+
}
|
|
2325
|
+
const merged = mergeTrees(
|
|
2326
|
+
colInserts.map((ins) => ({
|
|
2327
|
+
tree: ins.tree,
|
|
2328
|
+
path: ins.path.slice(0, -1)
|
|
2329
|
+
}))
|
|
2330
|
+
);
|
|
2331
|
+
traverse(merged, ({ parent, key, value }) => {
|
|
2332
|
+
if (key == "_data" && Array.isArray(value) && value.length > 0) {
|
|
2333
|
+
parent[key] = value.filter((v) => !!v);
|
|
2334
|
+
}
|
|
2335
|
+
});
|
|
2336
|
+
inserts.push({
|
|
2337
|
+
route: Route.fromFlat(uniqueRoute[0]).toRouteWithProperty(),
|
|
2338
|
+
tree: merged
|
|
2339
|
+
});
|
|
2340
|
+
}
|
|
2341
|
+
return inserts;
|
|
1699
2342
|
}
|
|
1700
2343
|
// ...........................................................................
|
|
1701
2344
|
/**
|
|
@@ -1739,7 +2382,12 @@ class Join {
|
|
|
1739
2382
|
*/
|
|
1740
2383
|
get layerRoutes() {
|
|
1741
2384
|
return Array.from(
|
|
1742
|
-
new Set(
|
|
2385
|
+
new Set(
|
|
2386
|
+
Object.values(this.columnSelection.columns).map((c) => [
|
|
2387
|
+
Route.fromFlat(c.route).top,
|
|
2388
|
+
Route.fromFlat(c.route).deeper(1).top
|
|
2389
|
+
]).map((segments) => new Route(segments).flat)
|
|
2390
|
+
)
|
|
1743
2391
|
).map((r) => Route.fromFlat(r));
|
|
1744
2392
|
}
|
|
1745
2393
|
// ...........................................................................
|
|
@@ -1748,7 +2396,11 @@ class Join {
|
|
|
1748
2396
|
*/
|
|
1749
2397
|
get cakeRoute() {
|
|
1750
2398
|
const cakeRoute = Array.from(
|
|
1751
|
-
new Set(
|
|
2399
|
+
new Set(
|
|
2400
|
+
Object.values(this.columnSelection.columns).map(
|
|
2401
|
+
(c) => Route.fromFlat(c.route).top.tableKey
|
|
2402
|
+
)
|
|
2403
|
+
)
|
|
1752
2404
|
).map((r) => Route.fromFlat(r));
|
|
1753
2405
|
if (cakeRoute.length !== 1) {
|
|
1754
2406
|
throw new Error(
|
|
@@ -1833,7 +2485,11 @@ class Join {
|
|
|
1833
2485
|
const dataColRoute = dataCol.route;
|
|
1834
2486
|
return colInfoRoute.equalsWithoutRefs(dataColRoute);
|
|
1835
2487
|
});
|
|
1836
|
-
|
|
2488
|
+
const insertValue = joinCol && joinCol.inserts ? joinCol.inserts.flatMap(
|
|
2489
|
+
(con) => con.cell.flatMap((c) => c.value)
|
|
2490
|
+
) ?? null : null;
|
|
2491
|
+
const baseValue = joinCol && joinCol.value.cell ? joinCol.value.cell.flatMap((c) => c.value) ?? null : null;
|
|
2492
|
+
row.push(insertValue ?? baseValue);
|
|
1837
2493
|
}
|
|
1838
2494
|
result.push(row);
|
|
1839
2495
|
}
|
|
@@ -1842,133 +2498,6 @@ class Join {
|
|
|
1842
2498
|
static empty() {
|
|
1843
2499
|
return new Join({}, ColumnSelection.empty());
|
|
1844
2500
|
}
|
|
1845
|
-
//#############################################################
|
|
1846
|
-
// ############# Private Methods ##############################
|
|
1847
|
-
//#############################################################
|
|
1848
|
-
// ...........................................................................
|
|
1849
|
-
/**
|
|
1850
|
-
* Builds cake insert objects from the given join rows
|
|
1851
|
-
* @param rows - The join rows
|
|
1852
|
-
* @returns The cake insert objects
|
|
1853
|
-
*/
|
|
1854
|
-
_insertCakeObject(rows) {
|
|
1855
|
-
const cakeInsertObjects = [];
|
|
1856
|
-
const cakeRoute = this.cakeRoute;
|
|
1857
|
-
for (const [sliceId, row] of Object.entries(rows)) {
|
|
1858
|
-
const layerInsertObjectList = this._insertLayerObjects(
|
|
1859
|
-
sliceId,
|
|
1860
|
-
row.columns
|
|
1861
|
-
);
|
|
1862
|
-
for (const layerInsertObject of layerInsertObjectList) {
|
|
1863
|
-
const cakeInsertObject = {};
|
|
1864
|
-
for (const [layerRoute, layerInsertObj] of Object.entries(
|
|
1865
|
-
layerInsertObject
|
|
1866
|
-
)) {
|
|
1867
|
-
cakeInsertObject[cakeRoute.root.tableKey] = {
|
|
1868
|
-
route: layerInsertObj.route,
|
|
1869
|
-
value: {
|
|
1870
|
-
[layerRoute]: layerInsertObj.value
|
|
1871
|
-
}
|
|
1872
|
-
};
|
|
1873
|
-
}
|
|
1874
|
-
cakeInsertObjects.push(cakeInsertObject);
|
|
1875
|
-
}
|
|
1876
|
-
}
|
|
1877
|
-
return cakeInsertObjects;
|
|
1878
|
-
}
|
|
1879
|
-
// ...........................................................................
|
|
1880
|
-
/**
|
|
1881
|
-
* Wraps component insert objects into layer insert objects
|
|
1882
|
-
* @param sliceId - The slice id
|
|
1883
|
-
* @param componentInsertObjects - The component insert objects
|
|
1884
|
-
* @returns
|
|
1885
|
-
*/
|
|
1886
|
-
_insertLayerObjects(sliceId, insertRow) {
|
|
1887
|
-
const layerRoutes = this.layerRoutes;
|
|
1888
|
-
const layerInsertObjects = [];
|
|
1889
|
-
const insertComponentObjects = this._insertComponentObjects(
|
|
1890
|
-
sliceId,
|
|
1891
|
-
insertRow
|
|
1892
|
-
);
|
|
1893
|
-
for (const layerRoute of layerRoutes) {
|
|
1894
|
-
for (const [compRouteFlat, compInsertObj] of Object.entries(
|
|
1895
|
-
insertComponentObjects
|
|
1896
|
-
)) {
|
|
1897
|
-
const compRoute = Route.fromFlat(compRouteFlat);
|
|
1898
|
-
if (layerRoute.includes(compRoute)) {
|
|
1899
|
-
const layerInsertObj = {};
|
|
1900
|
-
layerInsertObj[layerRoute.root.tableKey] = {
|
|
1901
|
-
route: compInsertObj.route,
|
|
1902
|
-
value: {
|
|
1903
|
-
[sliceId]: compInsertObj.value
|
|
1904
|
-
}
|
|
1905
|
-
};
|
|
1906
|
-
layerInsertObjects.push(layerInsertObj);
|
|
1907
|
-
} else {
|
|
1908
|
-
continue;
|
|
1909
|
-
}
|
|
1910
|
-
}
|
|
1911
|
-
}
|
|
1912
|
-
return layerInsertObjects;
|
|
1913
|
-
}
|
|
1914
|
-
// ...........................................................................
|
|
1915
|
-
/**
|
|
1916
|
-
* Merges columns into component insert objects
|
|
1917
|
-
* @param insertColumns - The columns to merge
|
|
1918
|
-
* @returns
|
|
1919
|
-
*/
|
|
1920
|
-
_insertComponentObjects(sliceId, insertColumns) {
|
|
1921
|
-
const componentRoutes = this.componentRoutes;
|
|
1922
|
-
const columns = this._mergeInsertRow(sliceId, insertColumns);
|
|
1923
|
-
const result = {};
|
|
1924
|
-
for (const compRoute of componentRoutes) {
|
|
1925
|
-
let compChanged = false;
|
|
1926
|
-
const compInsert = {};
|
|
1927
|
-
for (const c of columns) {
|
|
1928
|
-
if (compRoute.includes(c.route)) {
|
|
1929
|
-
if (c.insert !== null) {
|
|
1930
|
-
compChanged = true;
|
|
1931
|
-
compInsert[c.route.propertyKey] = c.insert;
|
|
1932
|
-
} else {
|
|
1933
|
-
compInsert[c.route.propertyKey] = c.value;
|
|
1934
|
-
}
|
|
1935
|
-
} else {
|
|
1936
|
-
continue;
|
|
1937
|
-
}
|
|
1938
|
-
}
|
|
1939
|
-
result[compRoute.flat] = {
|
|
1940
|
-
route: compRoute,
|
|
1941
|
-
value: compChanged ? compInsert : null
|
|
1942
|
-
};
|
|
1943
|
-
}
|
|
1944
|
-
return result;
|
|
1945
|
-
}
|
|
1946
|
-
// ...........................................................................
|
|
1947
|
-
/**
|
|
1948
|
-
* Merges the insert values into the base row
|
|
1949
|
-
* @param sliceId - The slice id
|
|
1950
|
-
* @param insertRow - The insert row
|
|
1951
|
-
* @returns The merged join row
|
|
1952
|
-
*/
|
|
1953
|
-
_mergeInsertRow(sliceId, insertRow) {
|
|
1954
|
-
const baseColumns = this._base[sliceId].columns;
|
|
1955
|
-
const mergedRow = [];
|
|
1956
|
-
for (const baseCol of baseColumns) {
|
|
1957
|
-
const insertCol = insertRow.find(
|
|
1958
|
-
(col) => col.route.equalsWithoutRefs(baseCol.route)
|
|
1959
|
-
);
|
|
1960
|
-
if (insertCol) {
|
|
1961
|
-
mergedRow.push({
|
|
1962
|
-
route: baseCol.route,
|
|
1963
|
-
value: baseCol.value,
|
|
1964
|
-
insert: insertCol.insert
|
|
1965
|
-
});
|
|
1966
|
-
} else {
|
|
1967
|
-
mergedRow.push(baseCol);
|
|
1968
|
-
}
|
|
1969
|
-
}
|
|
1970
|
-
return mergedRow;
|
|
1971
|
-
}
|
|
1972
2501
|
// ...........................................................................
|
|
1973
2502
|
/**
|
|
1974
2503
|
* Hashes the given join rows. If insert value is present, it is used for hashing.
|
|
@@ -1980,13 +2509,15 @@ class Join {
|
|
|
1980
2509
|
const sliceIds = Object.keys(rows);
|
|
1981
2510
|
const hashedRows = {};
|
|
1982
2511
|
for (const sliceId of sliceIds) {
|
|
1983
|
-
const
|
|
2512
|
+
const cols = rows[sliceId];
|
|
1984
2513
|
const rowHash = Hash.default.calcHash(
|
|
1985
|
-
|
|
2514
|
+
cols.map(
|
|
2515
|
+
(col) => col.inserts?.flatMap((con) => con.cell.flatMap((c) => c.value)) ?? col.value.cell ? col.value.cell.flatMap((c) => c.value) : []
|
|
2516
|
+
)
|
|
1986
2517
|
);
|
|
1987
2518
|
hashedRows[sliceId] = {
|
|
1988
2519
|
rowHash,
|
|
1989
|
-
columns
|
|
2520
|
+
columns: cols
|
|
1990
2521
|
};
|
|
1991
2522
|
}
|
|
1992
2523
|
return hashedRows;
|
|
@@ -2056,6 +2587,25 @@ class Notify {
|
|
|
2056
2587
|
return this._callbacks.get(route.flat) || [];
|
|
2057
2588
|
}
|
|
2058
2589
|
}
|
|
2590
|
+
const makeUniqueArrayByHash = (arr) => {
|
|
2591
|
+
const seen = /* @__PURE__ */ new Map();
|
|
2592
|
+
const result = [];
|
|
2593
|
+
for (const item of arr) {
|
|
2594
|
+
if (!seen.has(item._hash)) {
|
|
2595
|
+
seen.set(item._hash, item);
|
|
2596
|
+
result.push(item);
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
return result;
|
|
2600
|
+
};
|
|
2601
|
+
const makeUnique = (rljson) => {
|
|
2602
|
+
traverse(rljson, ({ parent, key, value }) => {
|
|
2603
|
+
if (key == "_data" && Array.isArray(value)) {
|
|
2604
|
+
parent[key] = makeUniqueArrayByHash(value);
|
|
2605
|
+
}
|
|
2606
|
+
});
|
|
2607
|
+
return rljson;
|
|
2608
|
+
};
|
|
2059
2609
|
class Db {
|
|
2060
2610
|
/**
|
|
2061
2611
|
* Constructor
|
|
@@ -2080,87 +2630,444 @@ class Db {
|
|
|
2080
2630
|
* Get data from a route with optional filtering
|
|
2081
2631
|
* @param route - The route to get data from
|
|
2082
2632
|
* @param where - Optional filter to apply to the data
|
|
2633
|
+
* @param filter - Optional filter to apply to child entries in related tables
|
|
2634
|
+
* @param sliceIds - Optional slice IDs to filter the data
|
|
2083
2635
|
* @returns An array of Rljson objects matching the route and filter
|
|
2084
2636
|
* @throws {Error} If the route is not valid or if any controller cannot be created
|
|
2085
2637
|
*/
|
|
2086
|
-
async get(route, where) {
|
|
2638
|
+
async get(route, where, filter, sliceIds) {
|
|
2087
2639
|
if (!route.isValid) throw new Error(`Route ${route.flat} is not valid.`);
|
|
2088
2640
|
const isolatedRoute = await this.isolatePropertyKeyFromRoute(route);
|
|
2089
|
-
const
|
|
2641
|
+
const controllers = await this.indexedControllers(isolatedRoute);
|
|
2642
|
+
const data = await this._get(
|
|
2643
|
+
isolatedRoute,
|
|
2644
|
+
where,
|
|
2645
|
+
controllers,
|
|
2646
|
+
filter,
|
|
2647
|
+
sliceIds
|
|
2648
|
+
);
|
|
2649
|
+
const dataWithControllers = {
|
|
2650
|
+
...data,
|
|
2651
|
+
...{ controllers }
|
|
2652
|
+
};
|
|
2653
|
+
return dataWithControllers;
|
|
2654
|
+
}
|
|
2655
|
+
// ...........................................................................
|
|
2656
|
+
/**
|
|
2657
|
+
* Resolves the route and returns corresponding data for any segment of the route,
|
|
2658
|
+
* matching recursive filters and where clauses
|
|
2659
|
+
*
|
|
2660
|
+
* @param route - The route to get data from
|
|
2661
|
+
* @param where - The recursive filtering key/value pairs to apply to the data
|
|
2662
|
+
* @param controllers - The controllers to use for fetching data
|
|
2663
|
+
* @param filter - Optional filter to apply to the data at the current route segment
|
|
2664
|
+
* @param sliceIds - Optional slice IDs to filter the data at the current route segment
|
|
2665
|
+
* @returns - An Rljson object matching the route and filters
|
|
2666
|
+
*/
|
|
2667
|
+
async _get(route, where, controllers, filter, sliceIds, routeAccumulator) {
|
|
2668
|
+
const params = {
|
|
2669
|
+
route: route.flat,
|
|
2670
|
+
where,
|
|
2671
|
+
filter,
|
|
2672
|
+
sliceIds,
|
|
2673
|
+
routeAccumulator: routeAccumulator ? routeAccumulator.flat : ""
|
|
2674
|
+
};
|
|
2675
|
+
const cacheHash = hsh(rmhsh(params))._hash;
|
|
2090
2676
|
const isCached = this._cache.has(cacheHash);
|
|
2091
2677
|
if (isCached) {
|
|
2092
2678
|
return this._cache.get(cacheHash);
|
|
2093
|
-
}
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
const
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
const
|
|
2122
|
-
|
|
2123
|
-
{
|
|
2679
|
+
}
|
|
2680
|
+
const nodeTableKey = route.top.tableKey;
|
|
2681
|
+
const nodeRoute = route;
|
|
2682
|
+
const nodeRouteRef = await this._getReferenceOfRouteSegment(nodeRoute.top);
|
|
2683
|
+
const nodeController = controllers[nodeTableKey];
|
|
2684
|
+
const nodeSliceIds = nodeRoute.top.sliceIds ?? sliceIds;
|
|
2685
|
+
let nodeWhere = typeof where === "object" ? { ...where } : where;
|
|
2686
|
+
if (!route.isRoot && typeof nodeWhere === "object") {
|
|
2687
|
+
delete nodeWhere[nodeRoute.deeper().top.tableKey];
|
|
2688
|
+
}
|
|
2689
|
+
nodeWhere = nodeWhere ? typeof nodeWhere === "string" ? { _hash: nodeWhere } : nodeWhere : {};
|
|
2690
|
+
if (nodeRouteRef && nodeRouteRef.length > 0)
|
|
2691
|
+
nodeWhere = { _hash: nodeRouteRef };
|
|
2692
|
+
delete nodeWhere["_through"];
|
|
2693
|
+
delete nodeWhere["_tableKey"];
|
|
2694
|
+
const {
|
|
2695
|
+
[nodeTableKey]: { _data: nodeRows, _type: nodeType, _hash: nodeHash }
|
|
2696
|
+
} = await nodeController.get(nodeWhere);
|
|
2697
|
+
const nodeColumnCfgs = nodeController.tableCfg().columns;
|
|
2698
|
+
const nodeRowsFiltered = [];
|
|
2699
|
+
for (const nodeRow of nodeRows) {
|
|
2700
|
+
const filterActive = filter && filter.length > 0;
|
|
2701
|
+
const sliceIdActive = nodeSliceIds && nodeSliceIds.length > 0;
|
|
2702
|
+
if (!filterActive && !sliceIdActive) {
|
|
2703
|
+
nodeRowsFiltered.push(nodeRow);
|
|
2704
|
+
continue;
|
|
2705
|
+
}
|
|
2706
|
+
let filterResult = false;
|
|
2707
|
+
const filterProperties = [];
|
|
2708
|
+
if (filterActive) {
|
|
2709
|
+
for (const f of filter) {
|
|
2710
|
+
if (f.tableKey !== nodeTableKey) continue;
|
|
2711
|
+
if (nodeRow._hash === f.ref) {
|
|
2712
|
+
filterProperties.push(f);
|
|
2713
|
+
filterResult = true;
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
} else {
|
|
2717
|
+
filterResult = true;
|
|
2718
|
+
}
|
|
2719
|
+
let sliceIdResult = false;
|
|
2720
|
+
if (sliceIdActive) {
|
|
2721
|
+
switch (nodeType) {
|
|
2722
|
+
case "cakes":
|
|
2723
|
+
const cake = nodeRow;
|
|
2724
|
+
const cakeSliceIds = await this._resolveSliceIds(
|
|
2725
|
+
cake.sliceIdsTable,
|
|
2726
|
+
cake.sliceIdsRow
|
|
2727
|
+
);
|
|
2728
|
+
const cakeMatchesSliceIds = nodeSliceIds.filter(
|
|
2729
|
+
(sId) => cakeSliceIds.includes(sId)
|
|
2730
|
+
);
|
|
2731
|
+
if (cakeMatchesSliceIds.length > 0) sliceIdResult = true;
|
|
2732
|
+
break;
|
|
2733
|
+
case "layers":
|
|
2734
|
+
const layer = nodeRow;
|
|
2735
|
+
const layerSliceIds = await this._resolveSliceIds(
|
|
2736
|
+
layer.sliceIdsTable,
|
|
2737
|
+
layer.sliceIdsTableRow
|
|
2738
|
+
);
|
|
2739
|
+
const layerMatchesSliceIds = nodeSliceIds.filter(
|
|
2740
|
+
(sId) => layerSliceIds.includes(sId)
|
|
2741
|
+
);
|
|
2742
|
+
if (layerMatchesSliceIds.length > 0) sliceIdResult = true;
|
|
2743
|
+
break;
|
|
2744
|
+
case "components":
|
|
2745
|
+
if (filterProperties.length > 0) {
|
|
2746
|
+
const componentSliceIds = filterProperties.flatMap(
|
|
2747
|
+
(f) => f.sliceIds
|
|
2748
|
+
);
|
|
2749
|
+
const componentMatchesSliceIds = nodeSliceIds.filter(
|
|
2750
|
+
(sId) => componentSliceIds.includes(sId)
|
|
2751
|
+
);
|
|
2752
|
+
if (componentMatchesSliceIds.length > 0) {
|
|
2753
|
+
sliceIdResult = true;
|
|
2754
|
+
}
|
|
2755
|
+
}
|
|
2756
|
+
break;
|
|
2757
|
+
/* v8 ignore next -- @preserve */
|
|
2758
|
+
default:
|
|
2759
|
+
sliceIdResult = true;
|
|
2760
|
+
break;
|
|
2761
|
+
}
|
|
2762
|
+
} else {
|
|
2763
|
+
sliceIdResult = true;
|
|
2764
|
+
}
|
|
2765
|
+
if (filterResult && sliceIdResult) nodeRowsFiltered.push(nodeRow);
|
|
2766
|
+
}
|
|
2767
|
+
const node = {
|
|
2768
|
+
[nodeTableKey]: {
|
|
2769
|
+
_data: nodeRowsFiltered,
|
|
2770
|
+
_type: nodeType,
|
|
2771
|
+
_hash: nodeHash
|
|
2772
|
+
}
|
|
2773
|
+
};
|
|
2774
|
+
const nodeValue = node[nodeTableKey]._data.filter(
|
|
2775
|
+
(v) => v !== void 0 && v !== null
|
|
2776
|
+
);
|
|
2777
|
+
if (route.isRoot) {
|
|
2778
|
+
if (route.hasPropertyKey) {
|
|
2779
|
+
const isolatedNode = this.isolatePropertyFromComponents(
|
|
2780
|
+
node,
|
|
2781
|
+
route.propertyKey
|
|
2782
|
+
);
|
|
2783
|
+
const result3 = {
|
|
2784
|
+
rljson: isolatedNode,
|
|
2785
|
+
tree: { [nodeTableKey]: node[nodeTableKey] },
|
|
2786
|
+
cell: nodeValue.map(
|
|
2787
|
+
(v, idx) => ({
|
|
2788
|
+
value: v[route.propertyKey] ?? null,
|
|
2789
|
+
row: v,
|
|
2790
|
+
route: Route.fromFlat(
|
|
2791
|
+
(routeAccumulator ? routeAccumulator.flat : nodeTableKey) + (nodeHash ? `@${nodeHash}` : "") + `/${route.propertyKey}`
|
|
2792
|
+
).toRouteWithProperty(),
|
|
2793
|
+
path: [[nodeTableKey, "_data", idx, route.propertyKey]]
|
|
2794
|
+
})
|
|
2795
|
+
)
|
|
2796
|
+
};
|
|
2797
|
+
this._cache.set(cacheHash, result3);
|
|
2798
|
+
return result3;
|
|
2799
|
+
}
|
|
2800
|
+
const result2 = {
|
|
2801
|
+
rljson: node,
|
|
2802
|
+
tree: { [nodeTableKey]: node[nodeTableKey] },
|
|
2803
|
+
cell: nodeValue.map(
|
|
2804
|
+
(v, idx) => ({
|
|
2805
|
+
value: v[route.propertyKey] ?? null,
|
|
2806
|
+
row: v,
|
|
2807
|
+
route: Route.fromFlat(
|
|
2808
|
+
(routeAccumulator ? routeAccumulator.flat : nodeTableKey) + (nodeHash ? `@${nodeHash}` : "")
|
|
2809
|
+
),
|
|
2810
|
+
path: [[nodeTableKey, "_data", idx]]
|
|
2811
|
+
})
|
|
2812
|
+
)
|
|
2813
|
+
};
|
|
2814
|
+
this._cache.set(cacheHash, result2);
|
|
2815
|
+
return result2;
|
|
2816
|
+
}
|
|
2817
|
+
const childrenRoute = route.deeper();
|
|
2818
|
+
const childrenTableKey = childrenRoute.top.tableKey;
|
|
2819
|
+
const childrenWhere = typeof where === "object" ? where[childrenTableKey] ?? {} : {};
|
|
2820
|
+
const childrenThroughProperty = childrenWhere?._through;
|
|
2821
|
+
const nodeChildrenArray = [];
|
|
2822
|
+
const nodeRowsMatchingChildrenRefs = /* @__PURE__ */ new Map();
|
|
2823
|
+
for (let i = 0; i < nodeRowsFiltered.length; i++) {
|
|
2824
|
+
const nodeRow = nodeRowsFiltered[i];
|
|
2825
|
+
const nodeRowObj = { ...{}, ...nodeRow };
|
|
2826
|
+
const childrenRefs = await nodeController.getChildRefs(
|
|
2827
|
+
nodeRow._hash
|
|
2124
2828
|
);
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2829
|
+
const childrenRefTypes = /* @__PURE__ */ new Map();
|
|
2830
|
+
const childrenRefSliceIds = /* @__PURE__ */ new Set();
|
|
2831
|
+
for (const cr of childrenRefs) {
|
|
2832
|
+
if (!!cr.columnKey) {
|
|
2833
|
+
const childrenRefColumnCfg = nodeColumnCfgs.find(
|
|
2834
|
+
(c) => c.key === cr.columnKey
|
|
2835
|
+
);
|
|
2836
|
+
if (childrenRefColumnCfg) {
|
|
2837
|
+
childrenRefTypes.set(
|
|
2838
|
+
childrenRefColumnCfg.key,
|
|
2839
|
+
childrenRefColumnCfg.ref?.type ?? ""
|
|
2840
|
+
);
|
|
2841
|
+
}
|
|
2842
|
+
if (cr.sliceIds && cr.sliceIds.length > 0) {
|
|
2843
|
+
for (const sId of cr.sliceIds) {
|
|
2844
|
+
childrenRefSliceIds.add(sId);
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
}
|
|
2849
|
+
if (childrenRefTypes.size > 1) {
|
|
2850
|
+
throw new Error(
|
|
2851
|
+
`Db._get: Multiple reference types found for children of table ${nodeTableKey}.`
|
|
2132
2852
|
);
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2853
|
+
}
|
|
2854
|
+
const cakeIsReferenced = childrenRefTypes.size === 1 && [...childrenRefTypes.values()][0] === "cakes";
|
|
2855
|
+
const componentIsReferenced = childrenRefTypes.size === 1 && nodeType === "components" && [...childrenRefTypes.values()][0] === "components";
|
|
2856
|
+
const childrenSliceIds = cakeIsReferenced ? [...childrenRefSliceIds] : componentIsReferenced ? void 0 : nodeSliceIds;
|
|
2857
|
+
const {
|
|
2858
|
+
rljson: rowChildrenRljson,
|
|
2859
|
+
tree: rowChildrenTree,
|
|
2860
|
+
cell: rowChildrenCell
|
|
2861
|
+
} = await this._get(
|
|
2862
|
+
childrenRoute,
|
|
2863
|
+
childrenWhere,
|
|
2864
|
+
controllers,
|
|
2865
|
+
childrenRefs,
|
|
2866
|
+
childrenSliceIds,
|
|
2867
|
+
Route.fromFlat(
|
|
2868
|
+
(routeAccumulator ? routeAccumulator.flat : nodeTableKey) + (nodeHash ? `@${nodeHash}` : "") + "/" + childrenTableKey
|
|
2869
|
+
)
|
|
2870
|
+
);
|
|
2871
|
+
if (cakeIsReferenced) {
|
|
2872
|
+
const refKey = [...childrenRefTypes.keys()][0];
|
|
2873
|
+
nodeRowObj[refKey] = rowChildrenTree;
|
|
2874
|
+
}
|
|
2875
|
+
if (rowChildrenRljson[childrenTableKey]._data.length === 0) continue;
|
|
2876
|
+
nodeChildrenArray.push(rowChildrenRljson);
|
|
2877
|
+
if (childrenThroughProperty) {
|
|
2878
|
+
const resolvedChildrenHashes = rowChildrenRljson[childrenTableKey]._data.map((rc) => rc._hash);
|
|
2879
|
+
for (const nr of nodeRowsFiltered) {
|
|
2880
|
+
{
|
|
2881
|
+
const throughHashesInRowCouldBeArray = nr[childrenThroughProperty];
|
|
2882
|
+
const throughHashesInRow = Array.isArray(
|
|
2883
|
+
throughHashesInRowCouldBeArray
|
|
2884
|
+
) ? throughHashesInRowCouldBeArray : [throughHashesInRowCouldBeArray];
|
|
2885
|
+
for (const th of throughHashesInRow) {
|
|
2886
|
+
if (resolvedChildrenHashes.includes(th)) {
|
|
2887
|
+
nodeRowObj[childrenThroughProperty] = {
|
|
2888
|
+
...rowChildrenTree[childrenTableKey],
|
|
2889
|
+
...{ _tableKey: childrenTableKey }
|
|
2890
|
+
};
|
|
2891
|
+
}
|
|
2892
|
+
}
|
|
2893
|
+
}
|
|
2894
|
+
}
|
|
2895
|
+
}
|
|
2896
|
+
const resolvedChildren = rowChildrenRljson[childrenTableKey]._data;
|
|
2897
|
+
const childrenRefsOfRow = childrenRefs.filter(
|
|
2898
|
+
(cr) => cr.tableKey == childrenTableKey
|
|
2899
|
+
);
|
|
2900
|
+
const matchingChildrenRefs = childrenRefsOfRow.filter(
|
|
2901
|
+
(cr) => !!resolvedChildren.find((ch) => cr.ref === ch._hash)
|
|
2902
|
+
);
|
|
2903
|
+
if (nodeType === "layers") {
|
|
2904
|
+
const compChildrenTrees = rowChildrenTree[childrenTableKey]._data;
|
|
2905
|
+
const compChildrenPaths = rowChildrenCell.map((c) => c.path);
|
|
2906
|
+
const components = compChildrenTrees.map((c, idx) => {
|
|
2907
|
+
return {
|
|
2908
|
+
tree: c,
|
|
2909
|
+
path: compChildrenPaths.filter((p) => p[0][2] == idx)
|
|
2910
|
+
};
|
|
2911
|
+
});
|
|
2912
|
+
const layerTreesAndPaths = components.map(({ tree: comp, path }) => {
|
|
2913
|
+
const sliceIds2 = matchingChildrenRefs.find(
|
|
2914
|
+
(cr) => cr.ref === comp._hash
|
|
2915
|
+
)?.sliceIds;
|
|
2916
|
+
if (!sliceIds2 || sliceIds2.length === 0) {
|
|
2917
|
+
throw new Error(
|
|
2918
|
+
`Db._get: No sliceIds found for component ${comp._hash} of layer ${nodeRow._hash}.`
|
|
2143
2919
|
);
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2920
|
+
}
|
|
2921
|
+
if (sliceIds2.length > 1) {
|
|
2922
|
+
throw new Error(
|
|
2923
|
+
`Db._get: Multiple sliceIds found for component ${comp._hash} of layer ${nodeRow._hash}.`
|
|
2924
|
+
);
|
|
2925
|
+
}
|
|
2926
|
+
const sliceId = sliceIds2[0];
|
|
2927
|
+
const pathsForSliceId = [
|
|
2928
|
+
...path.map((p) => p[0]).map((p) => {
|
|
2929
|
+
const newPath = [...p];
|
|
2930
|
+
newPath[2] = 0;
|
|
2931
|
+
return ["add", sliceId, ...newPath];
|
|
2932
|
+
})
|
|
2933
|
+
];
|
|
2934
|
+
return {
|
|
2935
|
+
[sliceId]: {
|
|
2936
|
+
tree: {
|
|
2937
|
+
[childrenTableKey]: {
|
|
2938
|
+
...{ _data: [comp] },
|
|
2939
|
+
...{ _type: "components" }
|
|
2940
|
+
}
|
|
2941
|
+
},
|
|
2942
|
+
path: pathsForSliceId
|
|
2149
2943
|
}
|
|
2944
|
+
};
|
|
2945
|
+
});
|
|
2946
|
+
const layer = layerTreesAndPaths.flatMap(
|
|
2947
|
+
(ltap) => Object.entries(ltap).map(([sliceId, { tree }]) => ({
|
|
2948
|
+
[sliceId]: tree
|
|
2949
|
+
}))
|
|
2950
|
+
).reduce((a, b) => ({ ...a, ...b }), {});
|
|
2951
|
+
const paths = layerTreesAndPaths.map(
|
|
2952
|
+
(ltap) => Object.values(ltap)[0].path
|
|
2953
|
+
);
|
|
2954
|
+
nodeRowsMatchingChildrenRefs.set(nodeRow._hash, {
|
|
2955
|
+
rljson: nodeRow,
|
|
2956
|
+
tree: {
|
|
2957
|
+
...nodeRowObj,
|
|
2958
|
+
add: { ...nodeRowObj.add, ...layer }
|
|
2959
|
+
},
|
|
2960
|
+
cell: rowChildrenCell.map(
|
|
2961
|
+
(c, idx) => ({
|
|
2962
|
+
...c,
|
|
2963
|
+
...{
|
|
2964
|
+
path: [paths.flat()[idx]]
|
|
2965
|
+
}
|
|
2966
|
+
})
|
|
2967
|
+
)
|
|
2968
|
+
});
|
|
2969
|
+
} else if (nodeType === "cakes") {
|
|
2970
|
+
nodeRowsMatchingChildrenRefs.set(nodeRow._hash, {
|
|
2971
|
+
rljson: nodeRow,
|
|
2972
|
+
tree: {
|
|
2973
|
+
...nodeRowObj,
|
|
2974
|
+
layers: { ...nodeRowObj.layers, ...rowChildrenTree }
|
|
2975
|
+
},
|
|
2976
|
+
cell: rowChildrenCell.map((c) => ({
|
|
2977
|
+
...c,
|
|
2978
|
+
...{
|
|
2979
|
+
path: c.path.map((p) => ["layers", ...p])
|
|
2980
|
+
}
|
|
2981
|
+
}))
|
|
2982
|
+
});
|
|
2983
|
+
} else if (nodeType === "components") {
|
|
2984
|
+
if (rowChildrenTree && Object.keys(rowChildrenTree).length > 0) {
|
|
2985
|
+
const columnReferenceMap = nodeColumnCfgs.filter(
|
|
2986
|
+
(c) => c.ref && ["components", "cakes"].includes(c.ref.type)
|
|
2987
|
+
).reduce((acc, curr) => {
|
|
2988
|
+
acc.set(curr.key, curr.ref.tableKey);
|
|
2989
|
+
return acc;
|
|
2990
|
+
}, /* @__PURE__ */ new Map());
|
|
2991
|
+
const resolvedRefs = {};
|
|
2992
|
+
for (const [colKey, childTableKey] of columnReferenceMap) {
|
|
2993
|
+
const tree = {
|
|
2994
|
+
...rowChildrenTree[childTableKey],
|
|
2995
|
+
...{ _tableKey: childTableKey }
|
|
2996
|
+
};
|
|
2997
|
+
const cell = rowChildrenCell.map((c) => ({
|
|
2998
|
+
...c,
|
|
2999
|
+
...{
|
|
3000
|
+
path: c.path.filter((p) => p[0] === childTableKey).map((p) => [colKey, ...p.slice(1)])
|
|
3001
|
+
}
|
|
3002
|
+
}));
|
|
3003
|
+
resolvedRefs[colKey] = { tree, cell };
|
|
2150
3004
|
}
|
|
3005
|
+
const resolvedProperties = Object.entries(resolvedRefs).map(([colKey, { tree }]) => ({
|
|
3006
|
+
[colKey]: tree
|
|
3007
|
+
})).reduce((a, b) => ({ ...a, ...b }), {});
|
|
3008
|
+
const resolvedTree = {
|
|
3009
|
+
...nodeRowObj,
|
|
3010
|
+
...resolvedProperties
|
|
3011
|
+
};
|
|
3012
|
+
const resolvedCell = Object.values(resolvedRefs).map((r) => r.cell).flat();
|
|
3013
|
+
nodeRowsMatchingChildrenRefs.set(nodeRow._hash, {
|
|
3014
|
+
rljson: nodeRow,
|
|
3015
|
+
tree: resolvedTree,
|
|
3016
|
+
cell: resolvedCell
|
|
3017
|
+
});
|
|
3018
|
+
} else {
|
|
3019
|
+
nodeRowsMatchingChildrenRefs.set(nodeRow._hash, {
|
|
3020
|
+
rljson: nodeRow,
|
|
3021
|
+
tree: { ...nodeRowObj },
|
|
3022
|
+
cell: rowChildrenCell
|
|
3023
|
+
});
|
|
2151
3024
|
}
|
|
3025
|
+
} else {
|
|
3026
|
+
throw new Error(
|
|
3027
|
+
`Db._get: Unsupported node type ${nodeType} for getting children.`
|
|
3028
|
+
);
|
|
2152
3029
|
}
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
3030
|
+
}
|
|
3031
|
+
const nodeChildren = makeUnique(
|
|
3032
|
+
merge(...nodeChildrenArray)
|
|
3033
|
+
);
|
|
3034
|
+
const matchedNodeRows = Array.from(nodeRowsMatchingChildrenRefs.values());
|
|
3035
|
+
const result = {
|
|
3036
|
+
rljson: {
|
|
3037
|
+
...node,
|
|
3038
|
+
...{
|
|
3039
|
+
[nodeTableKey]: {
|
|
3040
|
+
...{
|
|
3041
|
+
_data: matchedNodeRows.map((mr) => mr.rljson),
|
|
3042
|
+
_type: nodeType
|
|
3043
|
+
},
|
|
3044
|
+
...{
|
|
3045
|
+
...nodeHash ? { _hash: nodeHash } : {}
|
|
3046
|
+
}
|
|
3047
|
+
}
|
|
3048
|
+
},
|
|
3049
|
+
...nodeChildren
|
|
3050
|
+
},
|
|
3051
|
+
tree: {
|
|
3052
|
+
[nodeTableKey]: {
|
|
3053
|
+
...{
|
|
3054
|
+
_data: matchedNodeRows.map((mr) => mr.tree),
|
|
3055
|
+
_type: nodeType
|
|
3056
|
+
},
|
|
2156
3057
|
...{
|
|
2157
|
-
|
|
3058
|
+
...nodeHash ? { _hash: nodeHash } : {}
|
|
2158
3059
|
}
|
|
2159
3060
|
}
|
|
2160
|
-
}
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
3061
|
+
},
|
|
3062
|
+
cell: matchedNodeRows.map(
|
|
3063
|
+
(mr, idx) => mr.cell.map((c) => ({
|
|
3064
|
+
...c,
|
|
3065
|
+
...{ path: c.path.map((p) => [nodeTableKey, "_data", idx, ...p]) }
|
|
3066
|
+
}))
|
|
3067
|
+
).flat()
|
|
3068
|
+
};
|
|
3069
|
+
this._cache.set(cacheHash, result);
|
|
3070
|
+
return result;
|
|
2164
3071
|
}
|
|
2165
3072
|
// ...........................................................................
|
|
2166
3073
|
/**
|
|
@@ -2187,115 +3094,66 @@ class Db {
|
|
|
2187
3094
|
* @param rljson - The Rljson to join data for
|
|
2188
3095
|
*/
|
|
2189
3096
|
async join(columnSelection, cakeKey, cakeRef) {
|
|
2190
|
-
const
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
3097
|
+
const {
|
|
3098
|
+
tree: { [cakeKey]: cakesTable }
|
|
3099
|
+
} = await this.get(Route.fromFlat(`${cakeKey}@${cakeRef}`), {});
|
|
3100
|
+
const cakes = cakesTable._data;
|
|
3101
|
+
if (cakes.length === 0) {
|
|
2194
3102
|
throw new Error(
|
|
2195
3103
|
`Db.join: Cake with ref "${cakeRef}" not found in cake table "${cakeKey}".`
|
|
2196
3104
|
);
|
|
2197
3105
|
}
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
const layersTable = data[layerKey];
|
|
2202
|
-
const layer = layersTable._data.find(
|
|
2203
|
-
(l) => l._hash === cake.layers[layerKey]
|
|
3106
|
+
if (cakes.length > 1) {
|
|
3107
|
+
throw new Error(
|
|
3108
|
+
`Db.join: Multiple cakes with ref "${cakeRef}" found in cake table "${cakeKey}".`
|
|
2204
3109
|
);
|
|
2205
|
-
layers.set(layerKey, layer);
|
|
2206
|
-
}
|
|
2207
|
-
const mergedSliceIds = /* @__PURE__ */ new Set();
|
|
2208
|
-
for (const layer of layers.values()) {
|
|
2209
|
-
const sliceIdsTable = layer.sliceIdsTable;
|
|
2210
|
-
const sliceIdsTableRow = layer.sliceIdsTableRow;
|
|
2211
|
-
const {
|
|
2212
|
-
[sliceIdsTable]: { _data: sliceIds }
|
|
2213
|
-
} = await this.core.readRows(sliceIdsTable, { _hash: sliceIdsTableRow });
|
|
2214
|
-
for (const sid of sliceIds) {
|
|
2215
|
-
for (const s of sid.add) {
|
|
2216
|
-
mergedSliceIds.add(s);
|
|
2217
|
-
}
|
|
2218
|
-
}
|
|
2219
3110
|
}
|
|
2220
|
-
const
|
|
2221
|
-
const
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
const
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
titleShort: colCfg.key,
|
|
2238
|
-
titleLong: colCfg.key
|
|
2239
|
-
});
|
|
2240
|
-
}
|
|
2241
|
-
columnInfos.set(componentKey, columnInfo);
|
|
2242
|
-
columnCfgs.set(componentKey, columnCfg);
|
|
2243
|
-
}
|
|
2244
|
-
const rowMap = /* @__PURE__ */ new Map();
|
|
2245
|
-
for (const sliceId of mergedSliceIds) {
|
|
2246
|
-
let sliceIdRow = [];
|
|
2247
|
-
for (const [layerKey, layer] of layers.entries()) {
|
|
2248
|
-
const layerRef = layer._hash;
|
|
2249
|
-
const componentKey = layer.componentsTable;
|
|
2250
|
-
const componentRef = layer.add[sliceId];
|
|
2251
|
-
const componentsTable = data[componentKey];
|
|
2252
|
-
const component = componentsTable._data.find(
|
|
2253
|
-
(r) => r._hash === componentRef
|
|
3111
|
+
const cake = cakes[0];
|
|
3112
|
+
const sliceIds = await this._resolveSliceIds(
|
|
3113
|
+
cake.sliceIdsTable,
|
|
3114
|
+
cake.sliceIdsRow
|
|
3115
|
+
);
|
|
3116
|
+
const rows = {};
|
|
3117
|
+
for (const sliceId of sliceIds) {
|
|
3118
|
+
const row = [];
|
|
3119
|
+
for (const columnInfo of columnSelection.columns) {
|
|
3120
|
+
const columnRoute = Route.fromFlat(
|
|
3121
|
+
columnInfo.route
|
|
3122
|
+
).toRouteWithProperty();
|
|
3123
|
+
const columnContainer = await this.get(
|
|
3124
|
+
columnRoute,
|
|
3125
|
+
cakeRef,
|
|
3126
|
+
void 0,
|
|
3127
|
+
[sliceId]
|
|
2254
3128
|
);
|
|
2255
|
-
const
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
`${cakeKey}@${cakeRef}/${layerKey}@${layerRef}/${componentKey}@${componentRef}/${columnCfg.key}`
|
|
2262
|
-
).toRouteWithProperty(),
|
|
2263
|
-
value: component[columnCfg.key] ?? null,
|
|
2264
|
-
insert: null
|
|
2265
|
-
});
|
|
2266
|
-
}
|
|
2267
|
-
sliceIdRow = [...sliceIdRow, ...joinColumns];
|
|
3129
|
+
const column = {
|
|
3130
|
+
route: columnRoute,
|
|
3131
|
+
value: columnContainer,
|
|
3132
|
+
inserts: null
|
|
3133
|
+
};
|
|
3134
|
+
row.push(column);
|
|
2268
3135
|
}
|
|
2269
|
-
|
|
3136
|
+
rows[sliceId] = row;
|
|
2270
3137
|
}
|
|
2271
|
-
|
|
2272
|
-
for (const [sliceId, joinColumns] of rowMap.entries()) {
|
|
2273
|
-
Object.assign(joinRows, {
|
|
2274
|
-
[sliceId]: joinColumns
|
|
2275
|
-
});
|
|
2276
|
-
}
|
|
2277
|
-
const joinColumnInfos = Array.from(columnInfos.values()).flat();
|
|
2278
|
-
const joinColumnSelection = new ColumnSelection(joinColumnInfos);
|
|
2279
|
-
return new Join(joinRows, joinColumnSelection).select(columnSelection);
|
|
3138
|
+
return new Join(rows, columnSelection);
|
|
2280
3139
|
}
|
|
2281
3140
|
// ...........................................................................
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
Object.assign(data, componentData);
|
|
3141
|
+
async _resolveSliceIds(sliceIdTable, sliceIdRow) {
|
|
3142
|
+
const sliceIdController = new SliceIdController(this.core, sliceIdTable);
|
|
3143
|
+
sliceIdController.init();
|
|
3144
|
+
const resolvedSliceIds = /* @__PURE__ */ new Set();
|
|
3145
|
+
const {
|
|
3146
|
+
[sliceIdTable]: { _data: sliceIds }
|
|
3147
|
+
} = await sliceIdController.get(sliceIdRow);
|
|
3148
|
+
for (const sliceId of sliceIds) {
|
|
3149
|
+
const baseSliceIds = await sliceIdController.resolveBaseSliceIds(
|
|
3150
|
+
sliceId
|
|
3151
|
+
);
|
|
3152
|
+
for (const sId of baseSliceIds.add) {
|
|
3153
|
+
resolvedSliceIds.add(sId);
|
|
3154
|
+
}
|
|
2297
3155
|
}
|
|
2298
|
-
return
|
|
3156
|
+
return Array.from(resolvedSliceIds);
|
|
2299
3157
|
}
|
|
2300
3158
|
// ...........................................................................
|
|
2301
3159
|
/**
|
|
@@ -2304,17 +3162,15 @@ class Db {
|
|
|
2304
3162
|
* @returns The result of the Insert as an InsertHistoryRow
|
|
2305
3163
|
* @throws {Error} If the Insert is not valid or if any controller cannot be created
|
|
2306
3164
|
*/
|
|
2307
|
-
async insert(
|
|
2308
|
-
const
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
${JSON.stringify(errors, null, 2)}`
|
|
2315
|
-
);
|
|
3165
|
+
async insert(route, tree, options) {
|
|
3166
|
+
const controllers = await this.indexedControllers(
|
|
3167
|
+
Route.fromFlat(route.flatWithoutRefs)
|
|
3168
|
+
);
|
|
3169
|
+
const runFns = {};
|
|
3170
|
+
for (const [tableKey, controller] of Object.entries(controllers)) {
|
|
3171
|
+
runFns[tableKey] = controller.insert.bind(controller);
|
|
2316
3172
|
}
|
|
2317
|
-
return this._insert(
|
|
3173
|
+
return this._insert(route, tree, runFns, options);
|
|
2318
3174
|
}
|
|
2319
3175
|
// ...........................................................................
|
|
2320
3176
|
/**
|
|
@@ -2325,62 +3181,192 @@ ${JSON.stringify(errors, null, 2)}`
|
|
|
2325
3181
|
* @returns The result of the Insert
|
|
2326
3182
|
* @throws {Error} If the route is not valid or if any controller cannot be created
|
|
2327
3183
|
*/
|
|
2328
|
-
async _insert(
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
const
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
3184
|
+
async _insert(route, tree, runFns, options) {
|
|
3185
|
+
const results = [];
|
|
3186
|
+
const nodeRoute = route;
|
|
3187
|
+
const nodeSegment = nodeRoute.segment(0);
|
|
3188
|
+
const nodeTableKey = nodeSegment.tableKey;
|
|
3189
|
+
const nodeTree = tree[nodeTableKey];
|
|
3190
|
+
const nodeType = nodeTree._type;
|
|
3191
|
+
if (nodeTree._data.length === 0) {
|
|
3192
|
+
throw new Error(
|
|
3193
|
+
`Db._insert: No data found for table "${nodeTableKey}" in route "${route.flat}".`
|
|
3194
|
+
);
|
|
3195
|
+
}
|
|
3196
|
+
const previousHash = nodeSegment[nodeTableKey + "Ref"] ?? null;
|
|
3197
|
+
const previousTimeId = nodeSegment[nodeTableKey + "InsertHistoryRef"] ?? null;
|
|
3198
|
+
const previous = previousHash ? await this.getTimeIdsForRef(nodeTableKey, previousHash) : previousTimeId ? [previousTimeId] : [];
|
|
3199
|
+
if (!nodeRoute.isRoot) {
|
|
3200
|
+
const childRoute = nodeRoute.deeper(1);
|
|
3201
|
+
const childTableKey = childRoute.top.tableKey;
|
|
3202
|
+
if (nodeType === "cakes") {
|
|
3203
|
+
const cakes = nodeTree._data;
|
|
3204
|
+
if (cakes.length > 1) ;
|
|
3205
|
+
const cake = cakes[0];
|
|
3206
|
+
const childTree = cake.layers[childTableKey];
|
|
3207
|
+
const childResults = await this._insert(
|
|
3208
|
+
childRoute,
|
|
3209
|
+
{ [childTableKey]: childTree },
|
|
3210
|
+
runFns
|
|
2343
3211
|
);
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
...
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
}
|
|
3212
|
+
if (childResults.length > 1) {
|
|
3213
|
+
throw new Error(
|
|
3214
|
+
`Db._insert: Multiple inserts returned for child table "${childTableKey}" when inserting into cake table "${nodeTableKey}". Only single child inserts are supported.`
|
|
3215
|
+
);
|
|
3216
|
+
}
|
|
3217
|
+
const childResult = childResults[0];
|
|
3218
|
+
const insertValue = {
|
|
3219
|
+
...cake,
|
|
3220
|
+
...{
|
|
3221
|
+
layers: {
|
|
3222
|
+
...cake.layers,
|
|
3223
|
+
...{
|
|
3224
|
+
[childTableKey]: childResult[childTableKey + "Ref"]
|
|
3225
|
+
}
|
|
3226
|
+
}
|
|
3227
|
+
}
|
|
3228
|
+
};
|
|
3229
|
+
const runFn = runFns[nodeTableKey];
|
|
3230
|
+
const result = await runFn("add", rmhsh(insertValue), "db.insert");
|
|
3231
|
+
results.push(
|
|
3232
|
+
...result.map((r) => ({
|
|
3233
|
+
...r,
|
|
3234
|
+
...{ previous },
|
|
3235
|
+
...{ route: route.flat }
|
|
3236
|
+
}))
|
|
3237
|
+
);
|
|
3238
|
+
}
|
|
3239
|
+
if (nodeType === "layers") {
|
|
3240
|
+
const layers = nodeTree._data;
|
|
3241
|
+
for (const layer of layers) {
|
|
3242
|
+
const layerInsert = {};
|
|
3243
|
+
for (const [sliceId, componentTree] of Object.entries(layer.add)) {
|
|
3244
|
+
if (sliceId === "_hash") continue;
|
|
3245
|
+
const writtenComponents = await this._insert(
|
|
3246
|
+
childRoute,
|
|
3247
|
+
componentTree,
|
|
3248
|
+
runFns
|
|
3249
|
+
);
|
|
3250
|
+
if (writtenComponents.length > 1) {
|
|
3251
|
+
throw new Error(
|
|
3252
|
+
`Db._insert: Multiple components written for layer "${layer._hash}" and sliceId "${sliceId}" is currently not supported.`
|
|
3253
|
+
);
|
|
3254
|
+
}
|
|
3255
|
+
const writtenComponent = writtenComponents[0];
|
|
3256
|
+
if (!writtenComponent || !writtenComponent[childTableKey + "Ref"]) {
|
|
3257
|
+
throw new Error(
|
|
3258
|
+
`Db._insert: No component reference returned for layer "${layer._hash}" and sliceId "${sliceId}".`
|
|
3259
|
+
);
|
|
3260
|
+
}
|
|
3261
|
+
layerInsert[sliceId] = writtenComponent[childTableKey + "Ref"];
|
|
3262
|
+
}
|
|
3263
|
+
const runFn = runFns[nodeTableKey];
|
|
3264
|
+
const result = await runFn(
|
|
3265
|
+
"add",
|
|
3266
|
+
rmhsh({
|
|
3267
|
+
...layer,
|
|
3268
|
+
...{ add: layerInsert }
|
|
3269
|
+
}),
|
|
3270
|
+
"db.insert"
|
|
3271
|
+
);
|
|
3272
|
+
results.push(
|
|
3273
|
+
...result.map((r) => ({
|
|
3274
|
+
...r,
|
|
3275
|
+
...{ previous },
|
|
3276
|
+
...{ route: route.flat }
|
|
3277
|
+
}))
|
|
3278
|
+
);
|
|
3279
|
+
}
|
|
3280
|
+
}
|
|
3281
|
+
if (["components", "edits", "multiEdits"].includes(
|
|
3282
|
+
nodeType
|
|
3283
|
+
)) {
|
|
3284
|
+
const runFn = runFns[nodeTableKey];
|
|
3285
|
+
const components = nodeTree._data;
|
|
3286
|
+
for (const component of components) {
|
|
3287
|
+
const resolvedComponent = { ...component };
|
|
3288
|
+
for (const [property, value] of Object.entries(component)) {
|
|
3289
|
+
if (value.hasOwnProperty("_tableKey") && value._tableKey === childTableKey) {
|
|
3290
|
+
const writtenReferences = await this._insert(
|
|
3291
|
+
childRoute,
|
|
3292
|
+
{ [childTableKey]: value },
|
|
3293
|
+
runFns
|
|
3294
|
+
);
|
|
3295
|
+
resolvedComponent[property] = writtenReferences.map(
|
|
3296
|
+
(wr) => wr[childTableKey + "Ref"]
|
|
3297
|
+
);
|
|
3298
|
+
}
|
|
3299
|
+
}
|
|
3300
|
+
const result = await runFn(
|
|
3301
|
+
"add",
|
|
3302
|
+
rmhsh(resolvedComponent),
|
|
3303
|
+
"db.insert"
|
|
3304
|
+
);
|
|
3305
|
+
results.push(
|
|
3306
|
+
...result.map((r) => ({
|
|
3307
|
+
...r,
|
|
3308
|
+
...{ previous },
|
|
3309
|
+
...{ route: route.flat }
|
|
3310
|
+
}))
|
|
3311
|
+
);
|
|
3312
|
+
}
|
|
3313
|
+
}
|
|
2371
3314
|
} else {
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
3315
|
+
const runFn = runFns[nodeTableKey];
|
|
3316
|
+
if (["components", "edits", "multiEdits"].includes(
|
|
3317
|
+
nodeType
|
|
3318
|
+
)) {
|
|
3319
|
+
const components = rmhsh(
|
|
3320
|
+
tree[nodeTableKey]
|
|
3321
|
+
);
|
|
3322
|
+
for (const component of components._data) {
|
|
3323
|
+
if (!component) continue;
|
|
3324
|
+
delete component._tableKey;
|
|
3325
|
+
delete component._type;
|
|
3326
|
+
const result = await runFn("add", component, "db.insert");
|
|
3327
|
+
results.push(
|
|
3328
|
+
...result.map((r) => ({
|
|
3329
|
+
...r,
|
|
3330
|
+
...{ previous },
|
|
3331
|
+
...{ route: route.flat }
|
|
3332
|
+
}))
|
|
3333
|
+
);
|
|
3334
|
+
}
|
|
3335
|
+
}
|
|
3336
|
+
if (nodeType === "layers") {
|
|
3337
|
+
const layers = rmhsh(tree[nodeTableKey]);
|
|
3338
|
+
for (const layer of layers._data) {
|
|
3339
|
+
const result = await runFn("add", layer, "db.insert");
|
|
3340
|
+
results.push(
|
|
3341
|
+
...result.map((r) => ({
|
|
3342
|
+
...r,
|
|
3343
|
+
...{ previous },
|
|
3344
|
+
...{ route: route.flat }
|
|
3345
|
+
}))
|
|
3346
|
+
);
|
|
3347
|
+
}
|
|
3348
|
+
}
|
|
3349
|
+
if (nodeType === "cakes") {
|
|
3350
|
+
const cakes = rmhsh(tree[nodeTableKey]);
|
|
3351
|
+
for (const cake of cakes._data) {
|
|
3352
|
+
const result = await runFn("add", cake, "db.insert");
|
|
3353
|
+
results.push(
|
|
3354
|
+
...result.map((r) => ({
|
|
3355
|
+
...r,
|
|
3356
|
+
...{ previous },
|
|
3357
|
+
...{ route: route.flat }
|
|
3358
|
+
}))
|
|
3359
|
+
);
|
|
3360
|
+
}
|
|
3361
|
+
}
|
|
2378
3362
|
}
|
|
2379
|
-
result
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
3363
|
+
for (const result of results) {
|
|
3364
|
+
if (!options?.skipHistory)
|
|
3365
|
+
await this._writeInsertHistory(nodeTableKey, result);
|
|
3366
|
+
if (!options?.skipNotification)
|
|
3367
|
+
this.notify.notify(Route.fromFlat(result.route), result);
|
|
3368
|
+
}
|
|
3369
|
+
return results;
|
|
2384
3370
|
}
|
|
2385
3371
|
// ...........................................................................
|
|
2386
3372
|
/**
|
|
@@ -2401,40 +3387,6 @@ ${JSON.stringify(errors, null, 2)}`
|
|
|
2401
3387
|
this.notify.unregister(route, callback);
|
|
2402
3388
|
}
|
|
2403
3389
|
// ...........................................................................
|
|
2404
|
-
/**
|
|
2405
|
-
* Resolves an Insert by returning the run functions of all controllers involved in the Insert's route
|
|
2406
|
-
* @param Insert - The Insert to resolve
|
|
2407
|
-
* @returns A record of controller run functions, keyed by table name
|
|
2408
|
-
* @throws {Error} If the route is not valid or if any controller cannot be created
|
|
2409
|
-
*/
|
|
2410
|
-
async _resolveInsert(Insert2) {
|
|
2411
|
-
const controllers = await this._indexedControllers(
|
|
2412
|
-
Route.fromFlat(Insert2.route)
|
|
2413
|
-
);
|
|
2414
|
-
const runFns = {};
|
|
2415
|
-
for (const tableKey of Object.keys(controllers)) {
|
|
2416
|
-
runFns[tableKey] = controllers[tableKey].insert.bind(
|
|
2417
|
-
controllers[tableKey]
|
|
2418
|
-
);
|
|
2419
|
-
}
|
|
2420
|
-
return runFns;
|
|
2421
|
-
}
|
|
2422
|
-
// ...........................................................................
|
|
2423
|
-
/**
|
|
2424
|
-
* Returns the keys of child refs in a value based on a route
|
|
2425
|
-
* @param value - The value to check
|
|
2426
|
-
* @returns An array of keys of child refs in the value
|
|
2427
|
-
*/
|
|
2428
|
-
_childKeys(value) {
|
|
2429
|
-
const keys = Object.keys(value);
|
|
2430
|
-
const childKeys = [];
|
|
2431
|
-
for (const k of keys) {
|
|
2432
|
-
if (typeof value[k] !== "object") continue;
|
|
2433
|
-
childKeys.push(k);
|
|
2434
|
-
}
|
|
2435
|
-
return childKeys;
|
|
2436
|
-
}
|
|
2437
|
-
// ...........................................................................
|
|
2438
3390
|
/**
|
|
2439
3391
|
* Get a controller for a specific table
|
|
2440
3392
|
* @param tableKey - The key of the table to get the controller for
|
|
@@ -2451,7 +3403,7 @@ ${JSON.stringify(errors, null, 2)}`
|
|
|
2451
3403
|
return createController(contentType, this.core, tableKey, refs);
|
|
2452
3404
|
}
|
|
2453
3405
|
// ...........................................................................
|
|
2454
|
-
async
|
|
3406
|
+
async indexedControllers(route) {
|
|
2455
3407
|
const controllers = {};
|
|
2456
3408
|
const isolatedRoute = await this.isolatePropertyKeyFromRoute(route);
|
|
2457
3409
|
for (let i = 0; i < isolatedRoute.segments.length; i++) {
|
|
@@ -2500,15 +3452,9 @@ ${JSON.stringify(errors, null, 2)}`
|
|
|
2500
3452
|
if (options.sorted) {
|
|
2501
3453
|
const dumpedTable = await this.core.dumpTable(insertHistoryTable);
|
|
2502
3454
|
const tableData = dumpedTable[insertHistoryTable]._data;
|
|
2503
|
-
tableData.sort(
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
if (options.ascending) {
|
|
2507
|
-
return aTime.localeCompare(bTime);
|
|
2508
|
-
} else {
|
|
2509
|
-
return bTime.localeCompare(aTime);
|
|
2510
|
-
}
|
|
2511
|
-
});
|
|
3455
|
+
tableData.sort(
|
|
3456
|
+
(a, b) => options.ascending ? a.timeId.localeCompare(b.timeId) : b.timeId.localeCompare(a.timeId)
|
|
3457
|
+
);
|
|
2512
3458
|
return {
|
|
2513
3459
|
[insertHistoryTable]: { _data: tableData, _type: "insertHistory" }
|
|
2514
3460
|
};
|
|
@@ -2591,15 +3537,20 @@ ${JSON.stringify(errors, null, 2)}`
|
|
|
2591
3537
|
* @returns A route with extracted property key
|
|
2592
3538
|
*/
|
|
2593
3539
|
async isolatePropertyKeyFromRoute(route) {
|
|
2594
|
-
const
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
const
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
3540
|
+
const segmentLength = route.segments.length;
|
|
3541
|
+
let propertyKey = "";
|
|
3542
|
+
let result = route;
|
|
3543
|
+
for (let i = segmentLength; i > 0; i--) {
|
|
3544
|
+
const segment = route.segments[i - 1];
|
|
3545
|
+
const tableKey = segment.tableKey;
|
|
3546
|
+
const tableExists = await this._io.tableExists(tableKey);
|
|
3547
|
+
if (!tableExists) {
|
|
3548
|
+
propertyKey = propertyKey.length > 0 ? segment.tableKey + "/" + propertyKey : segment.tableKey;
|
|
3549
|
+
result = result.upper();
|
|
3550
|
+
result.propertyKey = propertyKey;
|
|
3551
|
+
}
|
|
3552
|
+
}
|
|
3553
|
+
return result;
|
|
2603
3554
|
}
|
|
2604
3555
|
// ...........................................................................
|
|
2605
3556
|
/**
|