@rljson/db 0.0.7 → 0.0.9
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 +72 -43
- package/dist/db.js +1334 -679
- package/dist/notify.d.ts +1 -2
- package/package.json +11 -11
- package/dist/cars-example.d.ts +0 -41
package/dist/db.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import { equals, merge } from "@rljson/json";
|
|
2
|
-
import { timeId, createInsertHistoryTableCfg, Validate, BaseValidator, Route, validateInsert, isTimeId } from "@rljson/rljson";
|
|
3
|
-
import { traverse } from "object-traversal";
|
|
4
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";
|
|
5
4
|
import { IoMem } from "@rljson/io";
|
|
5
|
+
import { traverse } from "object-traversal";
|
|
6
6
|
import { compileExpression } from "filtrex";
|
|
7
7
|
class BaseController {
|
|
8
8
|
constructor(_core, _tableKey) {
|
|
9
9
|
this._core = _core;
|
|
10
10
|
this._tableKey = _tableKey;
|
|
11
11
|
}
|
|
12
|
+
_contentType;
|
|
13
|
+
_tableCfg;
|
|
12
14
|
// ...........................................................................
|
|
13
15
|
/**
|
|
14
16
|
* Retrieves the current state of the table.
|
|
@@ -67,6 +69,28 @@ class BaseController {
|
|
|
67
69
|
});
|
|
68
70
|
return rows;
|
|
69
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
|
+
}
|
|
70
94
|
}
|
|
71
95
|
class CakeController extends BaseController {
|
|
72
96
|
constructor(_core, _tableKey, _refs) {
|
|
@@ -74,6 +98,7 @@ class CakeController extends BaseController {
|
|
|
74
98
|
this._core = _core;
|
|
75
99
|
this._tableKey = _tableKey;
|
|
76
100
|
this._refs = _refs;
|
|
101
|
+
this._contentType = "cakes";
|
|
77
102
|
}
|
|
78
103
|
_table = null;
|
|
79
104
|
_baseLayers = {};
|
|
@@ -88,7 +113,8 @@ class CakeController extends BaseController {
|
|
|
88
113
|
if (this._table._type !== "cakes") {
|
|
89
114
|
throw new Error(`Table ${this._tableKey} is not of type cakes.`);
|
|
90
115
|
}
|
|
91
|
-
|
|
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) {
|
|
92
118
|
const {
|
|
93
119
|
[this._tableKey]: { _data: baseCakes }
|
|
94
120
|
} = await this._core.readRow(this._tableKey, this._refs.base);
|
|
@@ -99,11 +125,13 @@ class CakeController extends BaseController {
|
|
|
99
125
|
this._baseLayers = rmhsh(baseCake.layers);
|
|
100
126
|
} else {
|
|
101
127
|
const cake = this._table._data[0];
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
128
|
+
if (!!cake) {
|
|
129
|
+
this._refs = {
|
|
130
|
+
sliceIdsTable: cake.sliceIdsTable,
|
|
131
|
+
sliceIdsRow: cake.sliceIdsRow
|
|
132
|
+
};
|
|
133
|
+
this._baseLayers = rmhsh(cake.layers);
|
|
134
|
+
}
|
|
107
135
|
}
|
|
108
136
|
}
|
|
109
137
|
async getChildRefs(where, filter) {
|
|
@@ -131,7 +159,7 @@ class CakeController extends BaseController {
|
|
|
131
159
|
if (this._refs?.base) delete this._refs.base;
|
|
132
160
|
const normalizedValue = {};
|
|
133
161
|
for (const [layerTable, layerRef] of Object.entries(
|
|
134
|
-
value
|
|
162
|
+
value.layers
|
|
135
163
|
)) {
|
|
136
164
|
if (Array.isArray(layerRef) && layerRef.length > 1) {
|
|
137
165
|
throw new Error(
|
|
@@ -141,6 +169,7 @@ class CakeController extends BaseController {
|
|
|
141
169
|
normalizedValue[layerTable] = Array.isArray(layerRef) ? layerRef[0] : layerRef;
|
|
142
170
|
}
|
|
143
171
|
const cake = {
|
|
172
|
+
...value,
|
|
144
173
|
layers: { ...this._baseLayers, ...normalizedValue },
|
|
145
174
|
...refs || this._refs
|
|
146
175
|
};
|
|
@@ -166,7 +195,7 @@ class CakeController extends BaseController {
|
|
|
166
195
|
return Promise.resolve({});
|
|
167
196
|
}
|
|
168
197
|
}
|
|
169
|
-
filterRow(row, key, value) {
|
|
198
|
+
async filterRow(row, key, value) {
|
|
170
199
|
const cake = row;
|
|
171
200
|
for (const [layerKey, layerRef] of Object.entries(cake.layers)) {
|
|
172
201
|
if (layerKey === key && layerRef === value) {
|
|
@@ -182,8 +211,15 @@ class ComponentController extends BaseController {
|
|
|
182
211
|
this._core = _core;
|
|
183
212
|
this._tableKey = _tableKey;
|
|
184
213
|
this._refs = _refs;
|
|
185
|
-
|
|
186
|
-
|
|
214
|
+
this._contentType = "components";
|
|
215
|
+
}
|
|
216
|
+
_allowedContentTypes = [
|
|
217
|
+
"components",
|
|
218
|
+
"edits",
|
|
219
|
+
"editHistory",
|
|
220
|
+
"multiEdits",
|
|
221
|
+
"head"
|
|
222
|
+
];
|
|
187
223
|
_resolvedColumns = null;
|
|
188
224
|
_refTableKeyToColumnKeyMap = null;
|
|
189
225
|
async init() {
|
|
@@ -192,7 +228,7 @@ class ComponentController extends BaseController {
|
|
|
192
228
|
}
|
|
193
229
|
const rljson = await this._core.dumpTable(this._tableKey);
|
|
194
230
|
const table = rljson[this._tableKey];
|
|
195
|
-
if (table._type
|
|
231
|
+
if (this._allowedContentTypes.indexOf(table._type) === -1) {
|
|
196
232
|
throw new Error(`Table ${this._tableKey} is not of type components.`);
|
|
197
233
|
}
|
|
198
234
|
this._tableCfg = await this._core.tableCfg(this._tableKey);
|
|
@@ -210,38 +246,12 @@ class ComponentController extends BaseController {
|
|
|
210
246
|
if (!!refs) {
|
|
211
247
|
throw new Error(`Refs are not supported on ComponentController.`);
|
|
212
248
|
}
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const ref = possibleRef._ref;
|
|
220
|
-
const val = possibleRef._value;
|
|
221
|
-
if (!referencedValues.has(ref))
|
|
222
|
-
referencedValues.set(ref, { [k]: val });
|
|
223
|
-
else {
|
|
224
|
-
const existing = referencedValues.get(ref);
|
|
225
|
-
referencedValues.set(ref, { ...{ [k]: val }, ...existing });
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
if (referencedValues.size > 0) {
|
|
232
|
-
for (const refValue of referencedValues.values()) {
|
|
233
|
-
values.push({ ...value, ...refValue });
|
|
234
|
-
}
|
|
235
|
-
} else {
|
|
236
|
-
values.push(value);
|
|
237
|
-
}
|
|
238
|
-
const components = values;
|
|
239
|
-
const results = [];
|
|
240
|
-
for (const component of components) {
|
|
241
|
-
delete component._somethingToInsert;
|
|
242
|
-
const rlJson = { [this._tableKey]: { _data: [component] } };
|
|
243
|
-
await this._core.import(rlJson);
|
|
244
|
-
const result = {
|
|
249
|
+
const component = value;
|
|
250
|
+
delete component._somethingToInsert;
|
|
251
|
+
const rlJson = { [this._tableKey]: { _data: [component] } };
|
|
252
|
+
await this._core.import(rlJson);
|
|
253
|
+
return [
|
|
254
|
+
{
|
|
245
255
|
//Ref to component
|
|
246
256
|
[this._tableKey + "Ref"]: hsh(component)._hash,
|
|
247
257
|
//Data from edit
|
|
@@ -249,10 +259,8 @@ class ComponentController extends BaseController {
|
|
|
249
259
|
origin,
|
|
250
260
|
//Unique id/timestamp
|
|
251
261
|
timeId: timeId()
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
}
|
|
255
|
-
return results;
|
|
262
|
+
}
|
|
263
|
+
];
|
|
256
264
|
}
|
|
257
265
|
// ...........................................................................
|
|
258
266
|
/**
|
|
@@ -287,6 +295,20 @@ class ComponentController extends BaseController {
|
|
|
287
295
|
columnKey: propertyKey,
|
|
288
296
|
ref: refItem
|
|
289
297
|
});
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
if (typeof refItem === "object" && refItem !== null) {
|
|
301
|
+
const cakeReference = refItem;
|
|
302
|
+
childRefs.set(
|
|
303
|
+
`${childRefTableKey}|${propertyKey}|${cakeReference.ref}|${cakeReference.sliceIds?.join(",")}`,
|
|
304
|
+
{
|
|
305
|
+
tableKey: childRefTableKey,
|
|
306
|
+
columnKey: propertyKey,
|
|
307
|
+
ref: cakeReference.ref,
|
|
308
|
+
sliceIds: cakeReference.sliceIds
|
|
309
|
+
}
|
|
310
|
+
);
|
|
311
|
+
continue;
|
|
290
312
|
}
|
|
291
313
|
}
|
|
292
314
|
continue;
|
|
@@ -319,7 +341,7 @@ class ComponentController extends BaseController {
|
|
|
319
341
|
const column = Object.keys(refWhere)[0];
|
|
320
342
|
const refValue = refWhere[column];
|
|
321
343
|
for (const row of tableData) {
|
|
322
|
-
if (this.filterRow(row, column, refValue)) {
|
|
344
|
+
if (await this.filterRow(row, column, refValue)) {
|
|
323
345
|
consolidatedRows.set(row._hash, row);
|
|
324
346
|
}
|
|
325
347
|
}
|
|
@@ -518,7 +540,7 @@ class ComponentController extends BaseController {
|
|
|
518
540
|
return this._core.readRows(table, where);
|
|
519
541
|
}
|
|
520
542
|
}
|
|
521
|
-
filterRow(row, key, value) {
|
|
543
|
+
async filterRow(row, key, value) {
|
|
522
544
|
for (const [propertyKey, propertyValue] of Object.entries(row)) {
|
|
523
545
|
if (propertyKey === key && equals(propertyValue, value)) {
|
|
524
546
|
return true;
|
|
@@ -533,12 +555,131 @@ class ComponentController extends BaseController {
|
|
|
533
555
|
return false;
|
|
534
556
|
}
|
|
535
557
|
}
|
|
558
|
+
class SliceIdController extends BaseController {
|
|
559
|
+
constructor(_core, _tableKey, _refs) {
|
|
560
|
+
super(_core, _tableKey);
|
|
561
|
+
this._core = _core;
|
|
562
|
+
this._tableKey = _tableKey;
|
|
563
|
+
this._refs = _refs;
|
|
564
|
+
this._contentType = "sliceIds";
|
|
565
|
+
}
|
|
566
|
+
async init() {
|
|
567
|
+
if (this._tableKey.endsWith("SliceId") === false) {
|
|
568
|
+
throw new Error(
|
|
569
|
+
`Table ${this._tableKey} is not supported by SliceIdController.`
|
|
570
|
+
);
|
|
571
|
+
}
|
|
572
|
+
const rljson = await this._core.dumpTable(this._tableKey);
|
|
573
|
+
const table = rljson[this._tableKey];
|
|
574
|
+
if (table._type !== "sliceIds") {
|
|
575
|
+
throw new Error(`Table ${this._tableKey} is not of type sliceIds.`);
|
|
576
|
+
}
|
|
577
|
+
this._tableCfg = await this._core.tableCfg(this._tableKey);
|
|
578
|
+
if (this._refs && this._refs.base) {
|
|
579
|
+
const {
|
|
580
|
+
[this._tableKey]: { _data: SliceIds2 }
|
|
581
|
+
} = await this._core.readRow(this._tableKey, this._refs.base);
|
|
582
|
+
if (SliceIds2.length === 0) {
|
|
583
|
+
throw new Error(`Base sliceId ${this._refs.base} does not exist.`);
|
|
584
|
+
}
|
|
585
|
+
} else {
|
|
586
|
+
const sliceId = table._data[0];
|
|
587
|
+
if (!!sliceId) {
|
|
588
|
+
this._refs = {
|
|
589
|
+
base: sliceId.base
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
async insert(command, value, origin, refs) {
|
|
595
|
+
if (!command.startsWith("add") && !command.startsWith("remove")) {
|
|
596
|
+
throw new Error(
|
|
597
|
+
`Command ${command} is not supported by SliceIdController.`
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
const sliceIds = command.startsWith("add") === true ? {
|
|
601
|
+
add: value,
|
|
602
|
+
...refs || this._refs
|
|
603
|
+
} : {
|
|
604
|
+
add: [],
|
|
605
|
+
remove: value,
|
|
606
|
+
...refs || this._refs
|
|
607
|
+
};
|
|
608
|
+
const rlJson = { [this._tableKey]: { _data: [sliceIds] } };
|
|
609
|
+
await this._core.import(rlJson);
|
|
610
|
+
const result = {
|
|
611
|
+
//Ref to component
|
|
612
|
+
[this._tableKey + "Ref"]: hsh(sliceIds)._hash,
|
|
613
|
+
//Data from edit
|
|
614
|
+
route: "",
|
|
615
|
+
origin,
|
|
616
|
+
//Unique id/timestamp
|
|
617
|
+
timeId: timeId()
|
|
618
|
+
};
|
|
619
|
+
return [result];
|
|
620
|
+
}
|
|
621
|
+
async get(where, filter) {
|
|
622
|
+
if (typeof where === "string") {
|
|
623
|
+
return this._getByHash(where, filter);
|
|
624
|
+
} else {
|
|
625
|
+
return this._getByWhere(where, filter);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
async resolveBaseSliceIds(sliceIds) {
|
|
629
|
+
const add = /* @__PURE__ */ new Set();
|
|
630
|
+
const remove = /* @__PURE__ */ new Set();
|
|
631
|
+
if (!!sliceIds.base) {
|
|
632
|
+
const baseSliceIds = await this.get(sliceIds.base);
|
|
633
|
+
if (!baseSliceIds[this._tableKey]?._data?.[0]) {
|
|
634
|
+
throw new Error(`Base sliceIds ${sliceIds.base} does not exist.`);
|
|
635
|
+
}
|
|
636
|
+
if (baseSliceIds[this._tableKey]._data.length > 1) {
|
|
637
|
+
throw new Error(
|
|
638
|
+
`Base sliceIds ${sliceIds.base} has more than one entry.`
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
const baseSliceId = baseSliceIds[this._tableKey]._data[0];
|
|
642
|
+
const resolvedBaseSliceIds = await this.resolveBaseSliceIds(baseSliceId);
|
|
643
|
+
for (const sliceId of resolvedBaseSliceIds.add) {
|
|
644
|
+
add.add(sliceId);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
for (const sliceId of sliceIds.add) {
|
|
648
|
+
add.add(sliceId);
|
|
649
|
+
}
|
|
650
|
+
if (!!sliceIds.remove)
|
|
651
|
+
for (const sliceId of sliceIds.remove) {
|
|
652
|
+
remove.add(sliceId);
|
|
653
|
+
}
|
|
654
|
+
for (const sliceId of remove.values()) {
|
|
655
|
+
if (add.has(sliceId)) {
|
|
656
|
+
add.delete(sliceId);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
return { add: Array.from(add) };
|
|
660
|
+
}
|
|
661
|
+
/* v8 ignore next -- @preserve */
|
|
662
|
+
async getChildRefs() {
|
|
663
|
+
return [];
|
|
664
|
+
}
|
|
665
|
+
async filterRow(row, _, value) {
|
|
666
|
+
const sliceIds = row;
|
|
667
|
+
const sliceId = value;
|
|
668
|
+
for (const sId of Object.values(sliceIds.add)) {
|
|
669
|
+
if (sliceId === sId) {
|
|
670
|
+
return true;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
return false;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
536
676
|
class LayerController extends BaseController {
|
|
537
677
|
constructor(_core, _tableKey, _refs) {
|
|
538
678
|
super(_core, _tableKey);
|
|
539
679
|
this._core = _core;
|
|
540
680
|
this._tableKey = _tableKey;
|
|
541
681
|
this._refs = _refs;
|
|
682
|
+
this._contentType = "layers";
|
|
542
683
|
}
|
|
543
684
|
async init() {
|
|
544
685
|
if (this._tableKey.endsWith("Layer") === false) {
|
|
@@ -551,6 +692,7 @@ class LayerController extends BaseController {
|
|
|
551
692
|
if (table._type !== "layers") {
|
|
552
693
|
throw new Error(`Table ${this._tableKey} is not of type layers.`);
|
|
553
694
|
}
|
|
695
|
+
this._tableCfg = await this._core.tableCfg(this._tableKey);
|
|
554
696
|
if (this._refs && this._refs.base) {
|
|
555
697
|
const {
|
|
556
698
|
[this._tableKey]: { _data: baseLayers }
|
|
@@ -568,11 +710,13 @@ class LayerController extends BaseController {
|
|
|
568
710
|
}
|
|
569
711
|
} else {
|
|
570
712
|
const layer = table._data[0];
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
713
|
+
if (!!layer) {
|
|
714
|
+
this._refs = {
|
|
715
|
+
sliceIdsTable: layer.sliceIdsTable,
|
|
716
|
+
sliceIdsTableRow: layer.sliceIdsTableRow,
|
|
717
|
+
componentsTable: layer.componentsTable
|
|
718
|
+
};
|
|
719
|
+
}
|
|
576
720
|
}
|
|
577
721
|
}
|
|
578
722
|
async insert(command, value, origin, refs) {
|
|
@@ -581,10 +725,9 @@ class LayerController extends BaseController {
|
|
|
581
725
|
`Command ${command} is not supported by LayerController.`
|
|
582
726
|
);
|
|
583
727
|
}
|
|
728
|
+
const isAdd = command.startsWith("add");
|
|
584
729
|
const normalizedValue = {};
|
|
585
|
-
for (const [sliceId, compRef] of Object.entries(
|
|
586
|
-
value
|
|
587
|
-
)) {
|
|
730
|
+
for (const [sliceId, compRef] of isAdd ? Object.entries(value.add) : Object.entries(value.remove)) {
|
|
588
731
|
if (Array.isArray(compRef) && compRef.length > 1) {
|
|
589
732
|
throw new Error(
|
|
590
733
|
`LayerController insert: Component ref for slice ${sliceId} cannot be an array of size > 1. No 1:n relations supported.`
|
|
@@ -592,13 +735,20 @@ class LayerController extends BaseController {
|
|
|
592
735
|
}
|
|
593
736
|
normalizedValue[sliceId] = Array.isArray(compRef) ? compRef[0] : compRef;
|
|
594
737
|
}
|
|
595
|
-
const layer =
|
|
596
|
-
|
|
597
|
-
...
|
|
738
|
+
const layer = isAdd ? {
|
|
739
|
+
...value,
|
|
740
|
+
...{
|
|
741
|
+
add: normalizedValue,
|
|
742
|
+
remove: {}
|
|
743
|
+
},
|
|
744
|
+
...refs
|
|
598
745
|
} : {
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
746
|
+
...value,
|
|
747
|
+
...{
|
|
748
|
+
remove: normalizedValue,
|
|
749
|
+
add: {}
|
|
750
|
+
},
|
|
751
|
+
...refs
|
|
602
752
|
};
|
|
603
753
|
const rlJson = { [this._tableKey]: { _data: [layer] } };
|
|
604
754
|
await this._core.import(rlJson);
|
|
@@ -620,25 +770,108 @@ class LayerController extends BaseController {
|
|
|
620
770
|
return this._getByWhere(where, filter);
|
|
621
771
|
}
|
|
622
772
|
}
|
|
773
|
+
async resolveBaseLayer(layer) {
|
|
774
|
+
const add = /* @__PURE__ */ new Map();
|
|
775
|
+
const sliceIds = /* @__PURE__ */ new Set();
|
|
776
|
+
if (!!layer.base) {
|
|
777
|
+
const baseLayer = await this.get(layer.base);
|
|
778
|
+
if (!baseLayer[this._tableKey]?._data?.[0]) {
|
|
779
|
+
throw new Error(`Base layer ${layer.base} does not exist.`);
|
|
780
|
+
}
|
|
781
|
+
if (baseLayer[this._tableKey]._data.length > 1) {
|
|
782
|
+
throw new Error(
|
|
783
|
+
`Base layer ${layer.base} resolving not possible. Not unique.`
|
|
784
|
+
);
|
|
785
|
+
}
|
|
786
|
+
const baseLayerData = rmhsh(baseLayer[this._tableKey]._data[0]);
|
|
787
|
+
const baseLayerResolved = await this.resolveBaseLayer(baseLayerData);
|
|
788
|
+
for (const [sliceId, compRef] of Object.entries(baseLayerResolved.add)) {
|
|
789
|
+
if (sliceId.startsWith("_")) continue;
|
|
790
|
+
add.set(sliceId, compRef);
|
|
791
|
+
}
|
|
792
|
+
for (const sliceId of baseLayerResolved.sliceIds) {
|
|
793
|
+
sliceIds.add(sliceId);
|
|
794
|
+
}
|
|
795
|
+
const baseLayerSliceIdsTable = baseLayerData.sliceIdsTable;
|
|
796
|
+
const baseLayerSliceIdsRow = baseLayerData.sliceIdsTableRow;
|
|
797
|
+
const {
|
|
798
|
+
[baseLayerSliceIdsTable]: { _data: baseLayerSliceIds }
|
|
799
|
+
} = await this._core.readRow(
|
|
800
|
+
baseLayerSliceIdsTable,
|
|
801
|
+
baseLayerSliceIdsRow
|
|
802
|
+
);
|
|
803
|
+
for (const sIds of baseLayerSliceIds) {
|
|
804
|
+
const sliceIdController = new SliceIdController(
|
|
805
|
+
this._core,
|
|
806
|
+
baseLayerSliceIdsTable
|
|
807
|
+
);
|
|
808
|
+
const resolvedSliceIds = await sliceIdController.resolveBaseSliceIds(
|
|
809
|
+
sIds
|
|
810
|
+
);
|
|
811
|
+
for (const sId of resolvedSliceIds.add) {
|
|
812
|
+
if (sId.startsWith("_")) continue;
|
|
813
|
+
sliceIds.add(sId);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
const {
|
|
818
|
+
[layer.sliceIdsTable]: { _data: layerSliceIds }
|
|
819
|
+
} = await this._core.readRow(layer.sliceIdsTable, layer.sliceIdsTableRow);
|
|
820
|
+
if (!layerSliceIds || layerSliceIds.length === 0) {
|
|
821
|
+
throw new Error(
|
|
822
|
+
`Layer sliceIds ${layer.sliceIdsTableRow} does not exist.`
|
|
823
|
+
);
|
|
824
|
+
}
|
|
825
|
+
if (layerSliceIds.length > 1) {
|
|
826
|
+
throw new Error(
|
|
827
|
+
`Layer sliceIds ${layer.sliceIdsTableRow} has more than one entry.`
|
|
828
|
+
);
|
|
829
|
+
}
|
|
830
|
+
const layerSliceId = layerSliceIds[0];
|
|
831
|
+
for (const sId of layerSliceId.add) {
|
|
832
|
+
if (sId.startsWith("_")) continue;
|
|
833
|
+
sliceIds.add(sId);
|
|
834
|
+
}
|
|
835
|
+
if (!!layerSliceId.remove)
|
|
836
|
+
for (const sId of Object.keys(layerSliceId.remove)) {
|
|
837
|
+
if (sliceIds.has(sId)) {
|
|
838
|
+
sliceIds.delete(sId);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
for (const [sliceId, compRef] of Object.entries(layer.add)) {
|
|
842
|
+
if (sliceId.startsWith("_")) continue;
|
|
843
|
+
add.set(sliceId, compRef);
|
|
844
|
+
}
|
|
845
|
+
if (!!layer.remove)
|
|
846
|
+
for (const sliceId of Object.keys(layer.remove)) {
|
|
847
|
+
if (add.has(sliceId)) {
|
|
848
|
+
add.delete(sliceId);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
return { add: Object.fromEntries(add), sliceIds: Array.from(sliceIds) };
|
|
852
|
+
}
|
|
623
853
|
async getChildRefs(where, filter) {
|
|
624
854
|
const { [this._tableKey]: table } = await this.get(where, filter);
|
|
625
855
|
const childRefs = [];
|
|
626
856
|
for (const row of table._data) {
|
|
627
857
|
const layer = row;
|
|
628
|
-
|
|
858
|
+
const resolvedLayer = await this.resolveBaseLayer(layer);
|
|
859
|
+
for (const [sliceId, ref] of Object.entries(resolvedLayer.add)) {
|
|
629
860
|
if (sliceId.startsWith("_")) continue;
|
|
630
861
|
childRefs.push({
|
|
631
862
|
tableKey: layer.componentsTable,
|
|
632
|
-
ref
|
|
863
|
+
ref,
|
|
864
|
+
sliceIds: [sliceId]
|
|
633
865
|
});
|
|
634
866
|
}
|
|
635
867
|
}
|
|
636
868
|
return childRefs;
|
|
637
869
|
}
|
|
638
|
-
filterRow(row, _, value) {
|
|
870
|
+
async filterRow(row, _, value) {
|
|
639
871
|
const layer = row;
|
|
640
872
|
const compRef = value;
|
|
641
|
-
|
|
873
|
+
const resolvedLayer = await this.resolveBaseLayer(layer);
|
|
874
|
+
for (const componentRef of Object.values(resolvedLayer.add)) {
|
|
642
875
|
if (componentRef === compRef) {
|
|
643
876
|
return true;
|
|
644
877
|
}
|
|
@@ -653,11 +886,22 @@ const createController = async (type, core, tableKey, refs) => {
|
|
|
653
886
|
ctrl = new LayerController(core, tableKey, refs);
|
|
654
887
|
break;
|
|
655
888
|
case "components":
|
|
889
|
+
case "edits":
|
|
890
|
+
case "editHistory":
|
|
891
|
+
case "multiEdits":
|
|
892
|
+
case "head":
|
|
656
893
|
ctrl = new ComponentController(core, tableKey, refs);
|
|
657
894
|
break;
|
|
658
895
|
case "cakes":
|
|
659
896
|
ctrl = new CakeController(core, tableKey, refs);
|
|
660
897
|
break;
|
|
898
|
+
case "sliceIds":
|
|
899
|
+
ctrl = new SliceIdController(
|
|
900
|
+
core,
|
|
901
|
+
tableKey,
|
|
902
|
+
refs
|
|
903
|
+
);
|
|
904
|
+
break;
|
|
661
905
|
default:
|
|
662
906
|
throw new Error(`Controller for type ${type} is not implemented yet.`);
|
|
663
907
|
}
|
|
@@ -721,7 +965,7 @@ class Core {
|
|
|
721
965
|
const validate = new Validate();
|
|
722
966
|
validate.addValidator(new BaseValidator());
|
|
723
967
|
const result = await validate.run(data);
|
|
724
|
-
if ((result.hasErrors || result.base && result.base.hasErrors) && !result.base.refsNotFound) {
|
|
968
|
+
if ((result.hasErrors || result.base && result.base.hasErrors) && !result.base.refsNotFound && !result.base.layerBasesNotFound) {
|
|
725
969
|
throw new Error(
|
|
726
970
|
"The imported rljson data is not valid:\n" + JSON.stringify(result, null, 2)
|
|
727
971
|
);
|
|
@@ -767,6 +1011,152 @@ class Core {
|
|
|
767
1011
|
return this._io.readRows({ table, where });
|
|
768
1012
|
}
|
|
769
1013
|
}
|
|
1014
|
+
const inject = (tree, path, value) => {
|
|
1015
|
+
for (let i = 0; i < path.length; i++) {
|
|
1016
|
+
const segment = path[i];
|
|
1017
|
+
if (i === path.length - 1) {
|
|
1018
|
+
tree[segment] = value;
|
|
1019
|
+
delete tree["_hash"];
|
|
1020
|
+
} else {
|
|
1021
|
+
if (!tree[segment]) {
|
|
1022
|
+
tree[segment] = {};
|
|
1023
|
+
}
|
|
1024
|
+
tree = tree[segment];
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
};
|
|
1028
|
+
const isolate = (tree, path, preservedKeys = []) => {
|
|
1029
|
+
if (path.length === 0) {
|
|
1030
|
+
return Array.isArray(tree) ? [] : {};
|
|
1031
|
+
}
|
|
1032
|
+
if (tree == null) {
|
|
1033
|
+
return null;
|
|
1034
|
+
}
|
|
1035
|
+
const [currentKey, ...remainingPath] = path;
|
|
1036
|
+
const result = Array.isArray(tree) ? [] : {};
|
|
1037
|
+
if (!Array.isArray(tree)) {
|
|
1038
|
+
for (const key in tree) {
|
|
1039
|
+
if (typeof key === "string" && key.startsWith("_") || preservedKeys.includes(key)) {
|
|
1040
|
+
result[key] = tree[key];
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
if (!(currentKey in tree)) {
|
|
1045
|
+
return result;
|
|
1046
|
+
}
|
|
1047
|
+
const currentValue = tree[currentKey];
|
|
1048
|
+
if (remainingPath.length === 0) {
|
|
1049
|
+
if (Array.isArray(result)) {
|
|
1050
|
+
result[currentKey] = currentValue;
|
|
1051
|
+
} else {
|
|
1052
|
+
result[currentKey] = currentValue;
|
|
1053
|
+
}
|
|
1054
|
+
} else {
|
|
1055
|
+
const isolatedChild = isolate(currentValue, remainingPath, preservedKeys);
|
|
1056
|
+
const hasContent = Array.isArray(isolatedChild) ? isolatedChild.length > 0 : Object.keys(isolatedChild).length > 0;
|
|
1057
|
+
if (hasContent || isolatedChild === null) {
|
|
1058
|
+
if (Array.isArray(result)) {
|
|
1059
|
+
result[currentKey] = isolatedChild;
|
|
1060
|
+
} else {
|
|
1061
|
+
result[currentKey] = isolatedChild;
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
return result;
|
|
1066
|
+
};
|
|
1067
|
+
const mergeTrees = (trees) => {
|
|
1068
|
+
if (!trees || trees.length === 0) {
|
|
1069
|
+
return {};
|
|
1070
|
+
}
|
|
1071
|
+
let result = {};
|
|
1072
|
+
for (const { tree } of trees) {
|
|
1073
|
+
if (tree != null) {
|
|
1074
|
+
result = mergeStructures(result, tree);
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
const pathValues = [];
|
|
1078
|
+
for (const { tree, path } of trees) {
|
|
1079
|
+
if (tree == null) continue;
|
|
1080
|
+
let current = tree;
|
|
1081
|
+
let pathExists = true;
|
|
1082
|
+
for (const key of path) {
|
|
1083
|
+
if (current == null || !(key in current)) {
|
|
1084
|
+
pathExists = false;
|
|
1085
|
+
break;
|
|
1086
|
+
}
|
|
1087
|
+
current = current[key];
|
|
1088
|
+
}
|
|
1089
|
+
if (pathExists && current != null) {
|
|
1090
|
+
pathValues.push({ path, value: current });
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
const pathGroups = /* @__PURE__ */ new Map();
|
|
1094
|
+
for (const { path, value } of pathValues) {
|
|
1095
|
+
const pathKey = JSON.stringify(path);
|
|
1096
|
+
if (!pathGroups.has(pathKey)) {
|
|
1097
|
+
pathGroups.set(pathKey, []);
|
|
1098
|
+
}
|
|
1099
|
+
pathGroups.get(pathKey).push(value);
|
|
1100
|
+
}
|
|
1101
|
+
for (const [pathKey, values] of pathGroups) {
|
|
1102
|
+
const path = JSON.parse(pathKey);
|
|
1103
|
+
let mergedValue = void 0;
|
|
1104
|
+
for (const value of values) {
|
|
1105
|
+
if (value == null) continue;
|
|
1106
|
+
if (mergedValue === void 0) {
|
|
1107
|
+
mergedValue = value;
|
|
1108
|
+
continue;
|
|
1109
|
+
}
|
|
1110
|
+
if (Array.isArray(mergedValue) && Array.isArray(value)) {
|
|
1111
|
+
mergedValue = [...mergedValue, ...value];
|
|
1112
|
+
} else if (!Array.isArray(mergedValue) && !Array.isArray(value)) {
|
|
1113
|
+
mergedValue = {
|
|
1114
|
+
...mergedValue,
|
|
1115
|
+
...value
|
|
1116
|
+
};
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
let current = result;
|
|
1120
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
1121
|
+
const key = path[i];
|
|
1122
|
+
if (current == null || !(key in current)) {
|
|
1123
|
+
current[key] = typeof path[i + 1] === "number" ? [] : {};
|
|
1124
|
+
}
|
|
1125
|
+
current = current[key];
|
|
1126
|
+
}
|
|
1127
|
+
if (path.length > 0) {
|
|
1128
|
+
current[path[path.length - 1]] = mergedValue;
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
return result;
|
|
1132
|
+
};
|
|
1133
|
+
function mergeStructures(target, source) {
|
|
1134
|
+
if (source == null) return target;
|
|
1135
|
+
if (target == null) return source;
|
|
1136
|
+
if (Array.isArray(target) && Array.isArray(source)) {
|
|
1137
|
+
const result = [...target];
|
|
1138
|
+
for (let i = 0; i < source.length; i++) {
|
|
1139
|
+
if (result[i] === void 0) {
|
|
1140
|
+
result[i] = source[i];
|
|
1141
|
+
} else {
|
|
1142
|
+
result[i] = mergeStructures(result[i], source[i]);
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
return result;
|
|
1146
|
+
}
|
|
1147
|
+
if (typeof target === "object" && typeof source === "object" && !Array.isArray(target) && !Array.isArray(source) && target !== null && source !== null) {
|
|
1148
|
+
const result = { ...target };
|
|
1149
|
+
for (const key in source) {
|
|
1150
|
+
if (key in result) {
|
|
1151
|
+
result[key] = mergeStructures(result[key], source[key]);
|
|
1152
|
+
} else {
|
|
1153
|
+
result[key] = source[key];
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
return result;
|
|
1157
|
+
}
|
|
1158
|
+
return source;
|
|
1159
|
+
}
|
|
770
1160
|
class ColumnSelection {
|
|
771
1161
|
constructor(columns) {
|
|
772
1162
|
this._throwOnWrongAlias(columns);
|
|
@@ -1113,6 +1503,19 @@ class ColumnSelection {
|
|
|
1113
1503
|
}
|
|
1114
1504
|
]);
|
|
1115
1505
|
}
|
|
1506
|
+
static exampleCarsDeeplyNestedColumnSelection() {
|
|
1507
|
+
return new ColumnSelection([
|
|
1508
|
+
{
|
|
1509
|
+
key: "brand",
|
|
1510
|
+
route: "catalogCake/catalogSeriesLayer/catalogSeries/seriesCake/seriesCarsLayer/seriesCars/carCake/carGeneralLayer/carGeneral/brand",
|
|
1511
|
+
alias: "brand",
|
|
1512
|
+
titleLong: "Car Brand",
|
|
1513
|
+
titleShort: "Brand",
|
|
1514
|
+
type: "string",
|
|
1515
|
+
_hash: ""
|
|
1516
|
+
}
|
|
1517
|
+
]);
|
|
1518
|
+
}
|
|
1116
1519
|
static exampleCarsColumnSelectionOnlySomeColumns() {
|
|
1117
1520
|
return new ColumnSelection([
|
|
1118
1521
|
{
|
|
@@ -1638,23 +2041,8 @@ class RowFilterProcessor {
|
|
|
1638
2041
|
return remainingIndices;
|
|
1639
2042
|
}
|
|
1640
2043
|
for (const i of remainingIndices) {
|
|
1641
|
-
const
|
|
1642
|
-
|
|
1643
|
-
for (const v of cellValue) {
|
|
1644
|
-
if (typeof v === "object" && v !== null && v.hasOwnProperty("_value")) {
|
|
1645
|
-
const matchValue = v._value;
|
|
1646
|
-
if (filter.matches(matchValue)) {
|
|
1647
|
-
result.push(i);
|
|
1648
|
-
break;
|
|
1649
|
-
}
|
|
1650
|
-
} else {
|
|
1651
|
-
if (filter.matches(v)) {
|
|
1652
|
-
result.push(i);
|
|
1653
|
-
break;
|
|
1654
|
-
}
|
|
1655
|
-
}
|
|
1656
|
-
}
|
|
1657
|
-
} else {
|
|
2044
|
+
const cellValues = join.value(i, columnIndex);
|
|
2045
|
+
for (const cellValue of cellValues) {
|
|
1658
2046
|
if (filter.matches(cellValue)) {
|
|
1659
2047
|
result.push(i);
|
|
1660
2048
|
}
|
|
@@ -1695,9 +2083,11 @@ class RowFilterProcessor {
|
|
|
1695
2083
|
if (applyTo[r]) {
|
|
1696
2084
|
continue;
|
|
1697
2085
|
}
|
|
1698
|
-
const
|
|
1699
|
-
|
|
1700
|
-
|
|
2086
|
+
const cellValues = join.value(r, columnIndex);
|
|
2087
|
+
for (const cellValue of cellValues) {
|
|
2088
|
+
if (filter.matches(cellValue)) {
|
|
2089
|
+
applyTo[r] = true;
|
|
2090
|
+
}
|
|
1701
2091
|
}
|
|
1702
2092
|
}
|
|
1703
2093
|
}
|
|
@@ -1717,14 +2107,22 @@ ${availableRoutes.map((a) => `- ${a}`).join("\n")}`
|
|
|
1717
2107
|
}
|
|
1718
2108
|
}
|
|
1719
2109
|
}
|
|
2110
|
+
const joinPreserveKeys = [
|
|
2111
|
+
"sliceIdsTable",
|
|
2112
|
+
"sliceIdsRow",
|
|
2113
|
+
/*'base',*/
|
|
2114
|
+
"sliceIdsTable",
|
|
2115
|
+
"sliceIdsTableRow",
|
|
2116
|
+
"componentsTable"
|
|
2117
|
+
];
|
|
1720
2118
|
class Join {
|
|
1721
|
-
constructor(baseRows, _baseColumnSelection, _objectMap) {
|
|
1722
|
-
this._baseColumnSelection = _baseColumnSelection;
|
|
1723
|
-
this._objectMap = _objectMap;
|
|
1724
|
-
this._base = this._hashedRows(baseRows);
|
|
1725
|
-
}
|
|
1726
2119
|
_base = {};
|
|
2120
|
+
_baseColumnSelection;
|
|
1727
2121
|
_processes = [];
|
|
2122
|
+
constructor(rows, columnSelection) {
|
|
2123
|
+
this._base = this._hashedRows(rows);
|
|
2124
|
+
this._baseColumnSelection = columnSelection;
|
|
2125
|
+
}
|
|
1728
2126
|
// ...........................................................................
|
|
1729
2127
|
/**
|
|
1730
2128
|
* Applies a filter to the join and returns the filtered view
|
|
@@ -1753,14 +2151,62 @@ class Join {
|
|
|
1753
2151
|
const data = {};
|
|
1754
2152
|
for (const [sliceId, joinRowH] of Object.entries(this.data)) {
|
|
1755
2153
|
const cols = [...joinRowH.columns];
|
|
2154
|
+
const insertCols = [];
|
|
1756
2155
|
for (const col of cols) {
|
|
1757
|
-
|
|
1758
|
-
col
|
|
1759
|
-
|
|
2156
|
+
const insertCol = {
|
|
2157
|
+
...col
|
|
2158
|
+
//inserts: col.inserts ? [...col.inserts] : [],
|
|
2159
|
+
};
|
|
2160
|
+
if (Route.fromFlat(setValue.route).equalsWithoutRefs(col.route)) {
|
|
2161
|
+
for (const cell of col.value.cell) {
|
|
2162
|
+
if (cell.path.length === 0) {
|
|
2163
|
+
throw new Error(
|
|
2164
|
+
`Join: Error while applying SetValue: Cannot set value for column without paths. Route: ${setValue.route.toString()}.`
|
|
2165
|
+
);
|
|
2166
|
+
}
|
|
2167
|
+
if (cell.path.length > 1) {
|
|
2168
|
+
throw new Error(
|
|
2169
|
+
`Join: Error while applying SetValue: Cannot set value for multiple paths in one cell. Found paths: [${cell.path.join(", ")}] for route: ${setValue.route.toString()}.`
|
|
2170
|
+
);
|
|
2171
|
+
}
|
|
2172
|
+
const cellInsertTree = isolate(
|
|
2173
|
+
{ ...col.value.tree },
|
|
2174
|
+
cell.path[0],
|
|
2175
|
+
joinPreserveKeys
|
|
2176
|
+
);
|
|
2177
|
+
inject(cellInsertTree, cell.path[0], setValue.value);
|
|
2178
|
+
const propertyKey = cell.path[0].slice(-1)[0];
|
|
2179
|
+
const insert = {
|
|
2180
|
+
cell: [
|
|
2181
|
+
{
|
|
2182
|
+
...cell,
|
|
2183
|
+
...{ value: setValue.value },
|
|
2184
|
+
...{
|
|
2185
|
+
row: {
|
|
2186
|
+
...cell.row,
|
|
2187
|
+
...{ [propertyKey]: setValue.value }
|
|
2188
|
+
}
|
|
2189
|
+
}
|
|
2190
|
+
}
|
|
2191
|
+
],
|
|
2192
|
+
tree: cellInsertTree,
|
|
2193
|
+
rljson: col.value.rljson
|
|
2194
|
+
};
|
|
2195
|
+
if (insert) {
|
|
2196
|
+
if (insertCol.inserts) insertCol.inserts.push(insert);
|
|
2197
|
+
else insertCol.inserts = [insert];
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2201
|
+
insertCols.push(insertCol);
|
|
1760
2202
|
}
|
|
1761
2203
|
data[sliceId] = {
|
|
1762
|
-
rowHash: Hash.default.calcHash(
|
|
1763
|
-
|
|
2204
|
+
rowHash: Hash.default.calcHash(
|
|
2205
|
+
insertCols.map(
|
|
2206
|
+
(col) => col.value.cell.flatMap((c) => c.value)
|
|
2207
|
+
)
|
|
2208
|
+
),
|
|
2209
|
+
columns: insertCols
|
|
1764
2210
|
};
|
|
1765
2211
|
}
|
|
1766
2212
|
const process = {
|
|
@@ -1803,15 +2249,15 @@ class Join {
|
|
|
1803
2249
|
const data = {};
|
|
1804
2250
|
for (let i2 = 0; i2 < this.rowCount; i2++) {
|
|
1805
2251
|
const [sliceId, row] = Object.entries(this.data)[i2];
|
|
1806
|
-
const
|
|
2252
|
+
const cols = [];
|
|
1807
2253
|
for (let j = 0; j < masterColumnIndices.length; j++) {
|
|
1808
|
-
|
|
2254
|
+
cols.push(row.columns[masterColumnIndices[j]]);
|
|
1809
2255
|
}
|
|
1810
2256
|
data[sliceId] = {
|
|
1811
2257
|
rowHash: Hash.default.calcHash(
|
|
1812
|
-
|
|
2258
|
+
cols.map((col) => col.value.cell.flatMap((c) => c.value))
|
|
1813
2259
|
),
|
|
1814
|
-
columns:
|
|
2260
|
+
columns: cols
|
|
1815
2261
|
};
|
|
1816
2262
|
}
|
|
1817
2263
|
const process = {
|
|
@@ -1850,30 +2296,51 @@ class Join {
|
|
|
1850
2296
|
* Returns insert Object of the join
|
|
1851
2297
|
*/
|
|
1852
2298
|
insert() {
|
|
1853
|
-
const
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
const
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
2299
|
+
const inserts = [];
|
|
2300
|
+
for (let i = 0; i < this.columnCount; i++) {
|
|
2301
|
+
const colInserts = [];
|
|
2302
|
+
for (const row of Object.values(this.data)) {
|
|
2303
|
+
const col = row.columns[i];
|
|
2304
|
+
if (col.inserts && col.inserts.length > 0) {
|
|
2305
|
+
for (const insert of col.inserts) {
|
|
2306
|
+
for (const cell of insert.cell) {
|
|
2307
|
+
const tree = insert.tree;
|
|
2308
|
+
const path = cell.path;
|
|
2309
|
+
inject(tree, path[0].slice(0, -1), cell.row);
|
|
2310
|
+
colInserts.push({
|
|
2311
|
+
route: col.route,
|
|
2312
|
+
tree,
|
|
2313
|
+
path: path[0]
|
|
2314
|
+
});
|
|
2315
|
+
}
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
if (colInserts.length === 0) continue;
|
|
2320
|
+
const routes = colInserts.map((ins) => ins.route.flat);
|
|
2321
|
+
const uniqueRoute = Array.from(new Set(routes));
|
|
2322
|
+
if (uniqueRoute.length > 1) {
|
|
2323
|
+
throw new Error(
|
|
2324
|
+
`Join: Error while generating insert: Multiple different routes found in inserts: ${uniqueRoute.map((r) => r.toString()).join(", ")}. Cannot generate single insert object.`
|
|
1862
2325
|
);
|
|
1863
|
-
const mergedValue = merge(existingValue, cakeInsert.value);
|
|
1864
|
-
cakeInsertsMergedOfLayerRoutes.set(cakeInsertRoute, mergedValue);
|
|
1865
2326
|
}
|
|
2327
|
+
const merged = mergeTrees(
|
|
2328
|
+
colInserts.map((ins) => ({
|
|
2329
|
+
tree: ins.tree,
|
|
2330
|
+
path: ins.path.slice(0, -1)
|
|
2331
|
+
}))
|
|
2332
|
+
);
|
|
2333
|
+
traverse(merged, ({ parent, key, value }) => {
|
|
2334
|
+
if (key == "_data" && Array.isArray(value) && value.length > 0) {
|
|
2335
|
+
parent[key] = value.filter((v) => !!v);
|
|
2336
|
+
}
|
|
2337
|
+
});
|
|
2338
|
+
inserts.push({
|
|
2339
|
+
route: Route.fromFlat(uniqueRoute[0]).toRouteWithProperty(),
|
|
2340
|
+
tree: merged
|
|
2341
|
+
});
|
|
1866
2342
|
}
|
|
1867
|
-
|
|
1868
|
-
for (const [route, value] of cakeInsertsMergedOfLayerRoutes) {
|
|
1869
|
-
const insert = {
|
|
1870
|
-
command: "add",
|
|
1871
|
-
route,
|
|
1872
|
-
value
|
|
1873
|
-
};
|
|
1874
|
-
results.push(insert);
|
|
1875
|
-
}
|
|
1876
|
-
return results;
|
|
2343
|
+
return inserts;
|
|
1877
2344
|
}
|
|
1878
2345
|
// ...........................................................................
|
|
1879
2346
|
/**
|
|
@@ -2020,7 +2487,11 @@ class Join {
|
|
|
2020
2487
|
const dataColRoute = dataCol.route;
|
|
2021
2488
|
return colInfoRoute.equalsWithoutRefs(dataColRoute);
|
|
2022
2489
|
});
|
|
2023
|
-
|
|
2490
|
+
const insertValue = joinCol && joinCol.inserts ? joinCol.inserts.flatMap(
|
|
2491
|
+
(con) => con.cell.flatMap((c) => c.value)
|
|
2492
|
+
) ?? null : null;
|
|
2493
|
+
const baseValue = joinCol && joinCol.value.cell ? joinCol.value.cell.flatMap((c) => c.value) ?? null : null;
|
|
2494
|
+
row.push(insertValue ?? baseValue);
|
|
2024
2495
|
}
|
|
2025
2496
|
result.push(row);
|
|
2026
2497
|
}
|
|
@@ -2029,184 +2500,26 @@ class Join {
|
|
|
2029
2500
|
static empty() {
|
|
2030
2501
|
return new Join({}, ColumnSelection.empty());
|
|
2031
2502
|
}
|
|
2032
|
-
//#############################################################
|
|
2033
|
-
// ############# Private Methods ##############################
|
|
2034
|
-
//#############################################################
|
|
2035
2503
|
// ...........................................................................
|
|
2036
2504
|
/**
|
|
2037
|
-
*
|
|
2038
|
-
*
|
|
2039
|
-
* @
|
|
2505
|
+
* Hashes the given join rows. If insert value is present, it is used for hashing.
|
|
2506
|
+
*
|
|
2507
|
+
* @param rows The join rows to hash
|
|
2508
|
+
* @returns The hashed join rows
|
|
2040
2509
|
*/
|
|
2041
|
-
|
|
2042
|
-
const
|
|
2043
|
-
const
|
|
2044
|
-
for (const
|
|
2045
|
-
const
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
const cakeInsertObject = {};
|
|
2051
|
-
for (const [layerRoute, layerInsertObj] of Object.entries(
|
|
2052
|
-
layerInsertObject
|
|
2053
|
-
)) {
|
|
2054
|
-
cakeInsertObject[cakeRoute.root.tableKey] = {
|
|
2055
|
-
route: layerInsertObj.route,
|
|
2056
|
-
value: {
|
|
2057
|
-
[layerRoute]: layerInsertObj.value
|
|
2058
|
-
}
|
|
2059
|
-
};
|
|
2060
|
-
}
|
|
2061
|
-
cakeInsertObjects.push(cakeInsertObject);
|
|
2062
|
-
}
|
|
2063
|
-
}
|
|
2064
|
-
return cakeInsertObjects;
|
|
2065
|
-
}
|
|
2066
|
-
// ...........................................................................
|
|
2067
|
-
/**
|
|
2068
|
-
* Wraps component insert objects into layer insert objects
|
|
2069
|
-
* @param sliceId - The slice id
|
|
2070
|
-
* @param componentInsertObjects - The component insert objects
|
|
2071
|
-
* @returns
|
|
2072
|
-
*/
|
|
2073
|
-
_insertLayerObjects(sliceId, insertRow) {
|
|
2074
|
-
const layerRoutes = this.layerRoutes;
|
|
2075
|
-
const layerInsertObjects = [];
|
|
2076
|
-
const insertComponentObjects = this._insertComponentObjects(
|
|
2077
|
-
sliceId,
|
|
2078
|
-
insertRow
|
|
2079
|
-
);
|
|
2080
|
-
for (const layerRoute of layerRoutes) {
|
|
2081
|
-
for (const [compRouteFlat, compInsertObj] of Object.entries(
|
|
2082
|
-
insertComponentObjects
|
|
2083
|
-
)) {
|
|
2084
|
-
if (!compInsertObj._somethingToInsert) continue;
|
|
2085
|
-
const compRoute = Route.fromFlat(compRouteFlat);
|
|
2086
|
-
if (layerRoute.includes(compRoute)) {
|
|
2087
|
-
const layerInsertObj = {};
|
|
2088
|
-
layerInsertObj[layerRoute.root.tableKey] = {
|
|
2089
|
-
route: Route.fromFlat(compRouteFlat),
|
|
2090
|
-
value: {
|
|
2091
|
-
[sliceId]: compInsertObj
|
|
2092
|
-
}
|
|
2093
|
-
};
|
|
2094
|
-
layerInsertObjects.push(layerInsertObj);
|
|
2095
|
-
} else {
|
|
2096
|
-
continue;
|
|
2097
|
-
}
|
|
2098
|
-
}
|
|
2099
|
-
}
|
|
2100
|
-
return layerInsertObjects;
|
|
2101
|
-
}
|
|
2102
|
-
// ...........................................................................
|
|
2103
|
-
/**
|
|
2104
|
-
* Merges columns into component insert objects
|
|
2105
|
-
* @param insertColumns - The columns to merge
|
|
2106
|
-
* @returns
|
|
2107
|
-
*/
|
|
2108
|
-
_insertComponentObjects(sliceId, insertColumns) {
|
|
2109
|
-
const columns = this._mergeInsertRow(sliceId, insertColumns);
|
|
2110
|
-
return this._denormalizeComponentInserts(columns, this._objectMap || {});
|
|
2111
|
-
}
|
|
2112
|
-
_denormalizeComponentInserts(columns, objectMap, refTableKey) {
|
|
2113
|
-
const result = {};
|
|
2114
|
-
for (const [propertyKey, propertyValue] of Object.entries(objectMap)) {
|
|
2115
|
-
if (typeof propertyValue === "object") {
|
|
2116
|
-
const refObjectMap = {};
|
|
2117
|
-
const refTableKey2 = propertyValue._tableKey;
|
|
2118
|
-
for (const [refKey, refValue] of Object.entries(
|
|
2119
|
-
propertyValue
|
|
2120
|
-
)) {
|
|
2121
|
-
if (refKey === "_tableKey") continue;
|
|
2122
|
-
refObjectMap[refTableKey2 + "/" + refKey] = refValue;
|
|
2123
|
-
}
|
|
2124
|
-
const refInsert = this._denormalizeComponentInserts(
|
|
2125
|
-
columns,
|
|
2126
|
-
refObjectMap,
|
|
2127
|
-
refTableKey2
|
|
2128
|
-
);
|
|
2129
|
-
for (const [refRoute, refObject] of Object.entries(refInsert)) {
|
|
2130
|
-
const insertObj = { [propertyKey]: refObject };
|
|
2131
|
-
result[refRoute] = {
|
|
2132
|
-
...result[refRoute],
|
|
2133
|
-
...insertObj,
|
|
2134
|
-
...{
|
|
2135
|
-
_somethingToInsert: result[refRoute]._somethingToInsert || refObject._somethingToInsert
|
|
2136
|
-
}
|
|
2137
|
-
};
|
|
2138
|
-
}
|
|
2139
|
-
} else {
|
|
2140
|
-
let compKey = Route.fromFlat(propertyValue).upper().flat;
|
|
2141
|
-
compKey = refTableKey ? compKey.replace(`/${refTableKey}`, "") : compKey;
|
|
2142
|
-
const refPropertyKey = refTableKey ? propertyKey.replace(`${refTableKey}/`, "") : propertyKey;
|
|
2143
|
-
if (!result[compKey]) result[compKey] = {};
|
|
2144
|
-
if (refTableKey && !result[compKey]._tableKey)
|
|
2145
|
-
result[compKey]._tableKey = refTableKey;
|
|
2146
|
-
const propValue = columns.find((col) => {
|
|
2147
|
-
return col.route.propertyKey === propertyKey;
|
|
2148
|
-
});
|
|
2149
|
-
if (!propValue) {
|
|
2150
|
-
throw new Error(
|
|
2151
|
-
`Join._denormalizeComponentInserts: Could not find column value for property key "${propertyKey}".`
|
|
2152
|
-
);
|
|
2153
|
-
}
|
|
2154
|
-
const somethingToInsert = !!propValue.insert;
|
|
2155
|
-
result[compKey] = {
|
|
2156
|
-
...result[compKey],
|
|
2157
|
-
[refPropertyKey]: propValue.insert ?? propValue.value,
|
|
2158
|
-
...{
|
|
2159
|
-
_somethingToInsert: result[compKey]._somethingToInsert || somethingToInsert
|
|
2160
|
-
}
|
|
2161
|
-
};
|
|
2162
|
-
}
|
|
2163
|
-
}
|
|
2164
|
-
return result;
|
|
2165
|
-
}
|
|
2166
|
-
// ...........................................................................
|
|
2167
|
-
/**
|
|
2168
|
-
* Merges the insert values into the base row
|
|
2169
|
-
* @param sliceId - The slice id
|
|
2170
|
-
* @param insertRow - The insert row
|
|
2171
|
-
* @returns The merged join row
|
|
2172
|
-
*/
|
|
2173
|
-
_mergeInsertRow(sliceId, insertRow) {
|
|
2174
|
-
const baseColumns = this._base[sliceId].columns;
|
|
2175
|
-
const mergedRow = [];
|
|
2176
|
-
for (const baseCol of baseColumns) {
|
|
2177
|
-
const insertCol = insertRow.find(
|
|
2178
|
-
(col) => col.route.equalsWithoutRefs(baseCol.route)
|
|
2179
|
-
);
|
|
2180
|
-
if (insertCol) {
|
|
2181
|
-
mergedRow.push({
|
|
2182
|
-
route: baseCol.route,
|
|
2183
|
-
value: baseCol.value,
|
|
2184
|
-
insert: insertCol.insert
|
|
2185
|
-
});
|
|
2186
|
-
} else {
|
|
2187
|
-
mergedRow.push(baseCol);
|
|
2188
|
-
}
|
|
2189
|
-
}
|
|
2190
|
-
return mergedRow;
|
|
2191
|
-
}
|
|
2192
|
-
// ...........................................................................
|
|
2193
|
-
/**
|
|
2194
|
-
* Hashes the given join rows. If insert value is present, it is used for hashing.
|
|
2195
|
-
*
|
|
2196
|
-
* @param rows The join rows to hash
|
|
2197
|
-
* @returns The hashed join rows
|
|
2198
|
-
*/
|
|
2199
|
-
_hashedRows(rows) {
|
|
2200
|
-
const sliceIds = Object.keys(rows);
|
|
2201
|
-
const hashedRows = {};
|
|
2202
|
-
for (const sliceId of sliceIds) {
|
|
2203
|
-
const columns = rows[sliceId];
|
|
2204
|
-
const rowHash = Hash.default.calcHash(
|
|
2205
|
-
columns.map((col) => col.insert ?? col.value)
|
|
2510
|
+
_hashedRows(rows) {
|
|
2511
|
+
const sliceIds = Object.keys(rows);
|
|
2512
|
+
const hashedRows = {};
|
|
2513
|
+
for (const sliceId of sliceIds) {
|
|
2514
|
+
const cols = rows[sliceId];
|
|
2515
|
+
const rowHash = Hash.default.calcHash(
|
|
2516
|
+
cols.map(
|
|
2517
|
+
(col) => col.inserts?.flatMap((con) => con.cell.flatMap((c) => c.value)) ?? col.value.cell ? col.value.cell.flatMap((c) => c.value) : []
|
|
2518
|
+
)
|
|
2206
2519
|
);
|
|
2207
2520
|
hashedRows[sliceId] = {
|
|
2208
2521
|
rowHash,
|
|
2209
|
-
columns
|
|
2522
|
+
columns: cols
|
|
2210
2523
|
};
|
|
2211
2524
|
}
|
|
2212
2525
|
return hashedRows;
|
|
@@ -2253,9 +2566,12 @@ class Notify {
|
|
|
2253
2566
|
notify(route, insertHistoryRow) {
|
|
2254
2567
|
const callbacks = this._callbacks.get(route.flat);
|
|
2255
2568
|
if (callbacks) {
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2569
|
+
Promise.all(callbacks.map((cb) => cb(insertHistoryRow))).catch((err) => {
|
|
2570
|
+
console.error(
|
|
2571
|
+
`Error notifying callbacks for route ${route.flat}:`,
|
|
2572
|
+
err
|
|
2573
|
+
);
|
|
2574
|
+
});
|
|
2259
2575
|
}
|
|
2260
2576
|
}
|
|
2261
2577
|
// ...........................................................................
|
|
@@ -2276,6 +2592,25 @@ class Notify {
|
|
|
2276
2592
|
return this._callbacks.get(route.flat) || [];
|
|
2277
2593
|
}
|
|
2278
2594
|
}
|
|
2595
|
+
const makeUniqueArrayByHash = (arr) => {
|
|
2596
|
+
const seen = /* @__PURE__ */ new Map();
|
|
2597
|
+
const result = [];
|
|
2598
|
+
for (const item of arr) {
|
|
2599
|
+
if (!seen.has(item._hash)) {
|
|
2600
|
+
seen.set(item._hash, item);
|
|
2601
|
+
result.push(item);
|
|
2602
|
+
}
|
|
2603
|
+
}
|
|
2604
|
+
return result;
|
|
2605
|
+
};
|
|
2606
|
+
const makeUnique = (rljson) => {
|
|
2607
|
+
traverse(rljson, ({ parent, key, value }) => {
|
|
2608
|
+
if (key == "_data" && Array.isArray(value)) {
|
|
2609
|
+
parent[key] = makeUniqueArrayByHash(value);
|
|
2610
|
+
}
|
|
2611
|
+
});
|
|
2612
|
+
return rljson;
|
|
2613
|
+
};
|
|
2279
2614
|
class Db {
|
|
2280
2615
|
/**
|
|
2281
2616
|
* Constructor
|
|
@@ -2300,22 +2635,27 @@ class Db {
|
|
|
2300
2635
|
* Get data from a route with optional filtering
|
|
2301
2636
|
* @param route - The route to get data from
|
|
2302
2637
|
* @param where - Optional filter to apply to the data
|
|
2638
|
+
* @param filter - Optional filter to apply to child entries in related tables
|
|
2639
|
+
* @param sliceIds - Optional slice IDs to filter the data
|
|
2303
2640
|
* @returns An array of Rljson objects matching the route and filter
|
|
2304
2641
|
* @throws {Error} If the route is not valid or if any controller cannot be created
|
|
2305
2642
|
*/
|
|
2306
|
-
async get(route, where) {
|
|
2643
|
+
async get(route, where, filter, sliceIds) {
|
|
2307
2644
|
if (!route.isValid) throw new Error(`Route ${route.flat} is not valid.`);
|
|
2308
2645
|
const isolatedRoute = await this.isolatePropertyKeyFromRoute(route);
|
|
2309
|
-
const
|
|
2310
|
-
const
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2646
|
+
const controllers = await this.indexedControllers(isolatedRoute);
|
|
2647
|
+
const data = await this._get(
|
|
2648
|
+
isolatedRoute,
|
|
2649
|
+
where,
|
|
2650
|
+
controllers,
|
|
2651
|
+
filter,
|
|
2652
|
+
sliceIds
|
|
2653
|
+
);
|
|
2654
|
+
const dataWithControllers = {
|
|
2655
|
+
...data,
|
|
2656
|
+
...{ controllers }
|
|
2657
|
+
};
|
|
2658
|
+
return dataWithControllers;
|
|
2319
2659
|
}
|
|
2320
2660
|
// ...........................................................................
|
|
2321
2661
|
/**
|
|
@@ -2326,37 +2666,108 @@ class Db {
|
|
|
2326
2666
|
* @param where - The recursive filtering key/value pairs to apply to the data
|
|
2327
2667
|
* @param controllers - The controllers to use for fetching data
|
|
2328
2668
|
* @param filter - Optional filter to apply to the data at the current route segment
|
|
2669
|
+
* @param sliceIds - Optional slice IDs to filter the data at the current route segment
|
|
2329
2670
|
* @returns - An Rljson object matching the route and filters
|
|
2330
2671
|
*/
|
|
2331
|
-
async _get(route, where, controllers, filter) {
|
|
2672
|
+
async _get(route, where, controllers, filter, sliceIds, routeAccumulator) {
|
|
2673
|
+
const params = {
|
|
2674
|
+
route: route.flat,
|
|
2675
|
+
where,
|
|
2676
|
+
filter,
|
|
2677
|
+
sliceIds,
|
|
2678
|
+
routeAccumulator: routeAccumulator ? routeAccumulator.flat : ""
|
|
2679
|
+
};
|
|
2680
|
+
const cacheHash = hsh(rmhsh(params))._hash;
|
|
2681
|
+
const isCached = this._cache.has(cacheHash);
|
|
2682
|
+
if (isCached) {
|
|
2683
|
+
return this._cache.get(cacheHash);
|
|
2684
|
+
}
|
|
2332
2685
|
const nodeTableKey = route.top.tableKey;
|
|
2333
2686
|
const nodeRoute = route;
|
|
2334
2687
|
const nodeRouteRef = await this._getReferenceOfRouteSegment(nodeRoute.top);
|
|
2335
2688
|
const nodeController = controllers[nodeTableKey];
|
|
2689
|
+
const nodeSliceIds = nodeRoute.top.sliceIds ?? sliceIds;
|
|
2336
2690
|
let nodeWhere = typeof where === "object" ? { ...where } : where;
|
|
2337
2691
|
if (!route.isRoot && typeof nodeWhere === "object") {
|
|
2338
2692
|
delete nodeWhere[nodeRoute.deeper().top.tableKey];
|
|
2339
2693
|
}
|
|
2340
2694
|
nodeWhere = nodeWhere ? typeof nodeWhere === "string" ? { _hash: nodeWhere } : nodeWhere : {};
|
|
2341
2695
|
if (nodeRouteRef && nodeRouteRef.length > 0)
|
|
2342
|
-
nodeWhere = {
|
|
2696
|
+
nodeWhere = { _hash: nodeRouteRef };
|
|
2343
2697
|
delete nodeWhere["_through"];
|
|
2344
2698
|
delete nodeWhere["_tableKey"];
|
|
2345
2699
|
const {
|
|
2346
2700
|
[nodeTableKey]: { _data: nodeRows, _type: nodeType, _hash: nodeHash }
|
|
2347
2701
|
} = await nodeController.get(nodeWhere);
|
|
2702
|
+
const nodeColumnCfgs = nodeController.tableCfg().columns;
|
|
2348
2703
|
const nodeRowsFiltered = [];
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2704
|
+
for (const nodeRow of nodeRows) {
|
|
2705
|
+
const filterActive = filter && filter.length > 0;
|
|
2706
|
+
const sliceIdActive = nodeSliceIds && nodeSliceIds.length > 0;
|
|
2707
|
+
if (!filterActive && !sliceIdActive) {
|
|
2708
|
+
nodeRowsFiltered.push(nodeRow);
|
|
2709
|
+
continue;
|
|
2710
|
+
}
|
|
2711
|
+
let filterResult = false;
|
|
2712
|
+
const filterProperties = [];
|
|
2713
|
+
if (filterActive) {
|
|
2714
|
+
for (const f of filter) {
|
|
2715
|
+
if (f.tableKey !== nodeTableKey) continue;
|
|
2353
2716
|
if (nodeRow._hash === f.ref) {
|
|
2354
|
-
|
|
2717
|
+
filterProperties.push(f);
|
|
2718
|
+
filterResult = true;
|
|
2355
2719
|
}
|
|
2356
2720
|
}
|
|
2721
|
+
} else {
|
|
2722
|
+
filterResult = true;
|
|
2723
|
+
}
|
|
2724
|
+
let sliceIdResult = false;
|
|
2725
|
+
if (sliceIdActive) {
|
|
2726
|
+
switch (nodeType) {
|
|
2727
|
+
case "cakes":
|
|
2728
|
+
const cake = nodeRow;
|
|
2729
|
+
const cakeSliceIds = await this._resolveSliceIds(
|
|
2730
|
+
cake.sliceIdsTable,
|
|
2731
|
+
cake.sliceIdsRow
|
|
2732
|
+
);
|
|
2733
|
+
const cakeMatchesSliceIds = nodeSliceIds.filter(
|
|
2734
|
+
(sId) => cakeSliceIds.includes(sId)
|
|
2735
|
+
);
|
|
2736
|
+
if (cakeMatchesSliceIds.length > 0) sliceIdResult = true;
|
|
2737
|
+
break;
|
|
2738
|
+
case "layers":
|
|
2739
|
+
const layer = nodeRow;
|
|
2740
|
+
const layerSliceIds = await this._resolveSliceIds(
|
|
2741
|
+
layer.sliceIdsTable,
|
|
2742
|
+
layer.sliceIdsTableRow
|
|
2743
|
+
);
|
|
2744
|
+
const layerMatchesSliceIds = nodeSliceIds.filter(
|
|
2745
|
+
(sId) => layerSliceIds.includes(sId)
|
|
2746
|
+
);
|
|
2747
|
+
if (layerMatchesSliceIds.length > 0) sliceIdResult = true;
|
|
2748
|
+
break;
|
|
2749
|
+
case "components":
|
|
2750
|
+
if (filterProperties.length > 0) {
|
|
2751
|
+
const componentSliceIds = filterProperties.flatMap(
|
|
2752
|
+
(f) => f.sliceIds
|
|
2753
|
+
);
|
|
2754
|
+
const componentMatchesSliceIds = nodeSliceIds.filter(
|
|
2755
|
+
(sId) => componentSliceIds.includes(sId)
|
|
2756
|
+
);
|
|
2757
|
+
if (componentMatchesSliceIds.length > 0) {
|
|
2758
|
+
sliceIdResult = true;
|
|
2759
|
+
}
|
|
2760
|
+
}
|
|
2761
|
+
break;
|
|
2762
|
+
/* v8 ignore next -- @preserve */
|
|
2763
|
+
default:
|
|
2764
|
+
sliceIdResult = true;
|
|
2765
|
+
break;
|
|
2766
|
+
}
|
|
2767
|
+
} else {
|
|
2768
|
+
sliceIdResult = true;
|
|
2357
2769
|
}
|
|
2358
|
-
|
|
2359
|
-
nodeRowsFiltered.push(...nodeRows);
|
|
2770
|
+
if (filterResult && sliceIdResult) nodeRowsFiltered.push(nodeRow);
|
|
2360
2771
|
}
|
|
2361
2772
|
const node = {
|
|
2362
2773
|
[nodeTableKey]: {
|
|
@@ -2365,11 +2776,48 @@ class Db {
|
|
|
2365
2776
|
_hash: nodeHash
|
|
2366
2777
|
}
|
|
2367
2778
|
};
|
|
2779
|
+
const nodeValue = node[nodeTableKey]._data.filter(
|
|
2780
|
+
(v) => v !== void 0 && v !== null
|
|
2781
|
+
);
|
|
2368
2782
|
if (route.isRoot) {
|
|
2369
2783
|
if (route.hasPropertyKey) {
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2784
|
+
const isolatedNode = this.isolatePropertyFromComponents(
|
|
2785
|
+
node,
|
|
2786
|
+
route.propertyKey
|
|
2787
|
+
);
|
|
2788
|
+
const result3 = {
|
|
2789
|
+
rljson: isolatedNode,
|
|
2790
|
+
tree: { [nodeTableKey]: node[nodeTableKey] },
|
|
2791
|
+
cell: nodeValue.map(
|
|
2792
|
+
(v, idx) => ({
|
|
2793
|
+
value: v[route.propertyKey] ?? null,
|
|
2794
|
+
row: v,
|
|
2795
|
+
route: Route.fromFlat(
|
|
2796
|
+
(routeAccumulator ? routeAccumulator.flat : nodeTableKey) + (nodeHash ? `@${nodeHash}` : "") + `/${route.propertyKey}`
|
|
2797
|
+
).toRouteWithProperty(),
|
|
2798
|
+
path: [[nodeTableKey, "_data", idx, route.propertyKey]]
|
|
2799
|
+
})
|
|
2800
|
+
)
|
|
2801
|
+
};
|
|
2802
|
+
this._cache.set(cacheHash, result3);
|
|
2803
|
+
return result3;
|
|
2804
|
+
}
|
|
2805
|
+
const result2 = {
|
|
2806
|
+
rljson: node,
|
|
2807
|
+
tree: { [nodeTableKey]: node[nodeTableKey] },
|
|
2808
|
+
cell: nodeValue.map(
|
|
2809
|
+
(v, idx) => ({
|
|
2810
|
+
value: v[route.propertyKey] ?? null,
|
|
2811
|
+
row: v,
|
|
2812
|
+
route: Route.fromFlat(
|
|
2813
|
+
(routeAccumulator ? routeAccumulator.flat : nodeTableKey) + (nodeHash ? `@${nodeHash}` : "")
|
|
2814
|
+
),
|
|
2815
|
+
path: [[nodeTableKey, "_data", idx]]
|
|
2816
|
+
})
|
|
2817
|
+
)
|
|
2818
|
+
};
|
|
2819
|
+
this._cache.set(cacheHash, result2);
|
|
2820
|
+
return result2;
|
|
2373
2821
|
}
|
|
2374
2822
|
const childrenRoute = route.deeper();
|
|
2375
2823
|
const childrenTableKey = childrenRoute.top.tableKey;
|
|
@@ -2379,21 +2827,60 @@ class Db {
|
|
|
2379
2827
|
const nodeRowsMatchingChildrenRefs = /* @__PURE__ */ new Map();
|
|
2380
2828
|
for (let i = 0; i < nodeRowsFiltered.length; i++) {
|
|
2381
2829
|
const nodeRow = nodeRowsFiltered[i];
|
|
2830
|
+
const nodeRowObj = { ...{}, ...nodeRow };
|
|
2382
2831
|
const childrenRefs = await nodeController.getChildRefs(
|
|
2383
2832
|
nodeRow._hash
|
|
2384
2833
|
);
|
|
2385
|
-
const
|
|
2834
|
+
const childrenRefTypes = /* @__PURE__ */ new Map();
|
|
2835
|
+
const childrenRefSliceIds = /* @__PURE__ */ new Set();
|
|
2836
|
+
for (const cr of childrenRefs) {
|
|
2837
|
+
if (!!cr.columnKey) {
|
|
2838
|
+
const childrenRefColumnCfg = nodeColumnCfgs.find(
|
|
2839
|
+
(c) => c.key === cr.columnKey
|
|
2840
|
+
);
|
|
2841
|
+
if (childrenRefColumnCfg) {
|
|
2842
|
+
childrenRefTypes.set(
|
|
2843
|
+
childrenRefColumnCfg.key,
|
|
2844
|
+
childrenRefColumnCfg.ref?.type ?? ""
|
|
2845
|
+
);
|
|
2846
|
+
}
|
|
2847
|
+
if (cr.sliceIds && cr.sliceIds.length > 0) {
|
|
2848
|
+
for (const sId of cr.sliceIds) {
|
|
2849
|
+
childrenRefSliceIds.add(sId);
|
|
2850
|
+
}
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
if (childrenRefTypes.size > 1) {
|
|
2855
|
+
throw new Error(
|
|
2856
|
+
`Db._get: Multiple reference types found for children of table ${nodeTableKey}.`
|
|
2857
|
+
);
|
|
2858
|
+
}
|
|
2859
|
+
const cakeIsReferenced = childrenRefTypes.size === 1 && [...childrenRefTypes.values()][0] === "cakes";
|
|
2860
|
+
const componentIsReferenced = childrenRefTypes.size === 1 && nodeType === "components" && [...childrenRefTypes.values()][0] === "components";
|
|
2861
|
+
const childrenSliceIds = cakeIsReferenced ? [...childrenRefSliceIds] : componentIsReferenced ? void 0 : nodeSliceIds;
|
|
2862
|
+
const {
|
|
2863
|
+
rljson: rowChildrenRljson,
|
|
2864
|
+
tree: rowChildrenTree,
|
|
2865
|
+
cell: rowChildrenCell
|
|
2866
|
+
} = await this._get(
|
|
2386
2867
|
childrenRoute,
|
|
2387
2868
|
childrenWhere,
|
|
2388
2869
|
controllers,
|
|
2389
|
-
childrenRefs
|
|
2870
|
+
childrenRefs,
|
|
2871
|
+
childrenSliceIds,
|
|
2872
|
+
Route.fromFlat(
|
|
2873
|
+
(routeAccumulator ? routeAccumulator.flat : nodeTableKey) + (nodeHash ? `@${nodeHash}` : "") + "/" + childrenTableKey
|
|
2874
|
+
)
|
|
2390
2875
|
);
|
|
2391
|
-
if (
|
|
2392
|
-
|
|
2876
|
+
if (cakeIsReferenced) {
|
|
2877
|
+
const refKey = [...childrenRefTypes.keys()][0];
|
|
2878
|
+
nodeRowObj[refKey] = rowChildrenTree;
|
|
2879
|
+
}
|
|
2880
|
+
if (rowChildrenRljson[childrenTableKey]._data.length === 0) continue;
|
|
2881
|
+
nodeChildrenArray.push(rowChildrenRljson);
|
|
2393
2882
|
if (childrenThroughProperty) {
|
|
2394
|
-
const
|
|
2395
|
-
(rc) => rc._hash
|
|
2396
|
-
);
|
|
2883
|
+
const resolvedChildrenHashes = rowChildrenRljson[childrenTableKey]._data.map((rc) => rc._hash);
|
|
2397
2884
|
for (const nr of nodeRowsFiltered) {
|
|
2398
2885
|
{
|
|
2399
2886
|
const throughHashesInRowCouldBeArray = nr[childrenThroughProperty];
|
|
@@ -2401,30 +2888,191 @@ class Db {
|
|
|
2401
2888
|
throughHashesInRowCouldBeArray
|
|
2402
2889
|
) ? throughHashesInRowCouldBeArray : [throughHashesInRowCouldBeArray];
|
|
2403
2890
|
for (const th of throughHashesInRow) {
|
|
2404
|
-
if (
|
|
2405
|
-
|
|
2891
|
+
if (resolvedChildrenHashes.includes(th)) {
|
|
2892
|
+
nodeRowObj[childrenThroughProperty] = {
|
|
2893
|
+
...rowChildrenTree[childrenTableKey],
|
|
2894
|
+
...{ _tableKey: childrenTableKey }
|
|
2895
|
+
};
|
|
2896
|
+
}
|
|
2897
|
+
}
|
|
2898
|
+
}
|
|
2899
|
+
}
|
|
2900
|
+
}
|
|
2901
|
+
const resolvedChildren = rowChildrenRljson[childrenTableKey]._data;
|
|
2902
|
+
const childrenRefsOfRow = childrenRefs.filter(
|
|
2903
|
+
(cr) => cr.tableKey == childrenTableKey
|
|
2904
|
+
);
|
|
2905
|
+
const matchingChildrenRefs = childrenRefsOfRow.filter(
|
|
2906
|
+
(cr) => !!resolvedChildren.find((ch) => cr.ref === ch._hash)
|
|
2907
|
+
);
|
|
2908
|
+
if (nodeType === "layers") {
|
|
2909
|
+
const compChildrenTrees = rowChildrenTree[childrenTableKey]._data;
|
|
2910
|
+
const compChildrenPaths = rowChildrenCell.map((c) => c.path);
|
|
2911
|
+
const components = compChildrenTrees.map((c, idx) => {
|
|
2912
|
+
return {
|
|
2913
|
+
tree: c,
|
|
2914
|
+
path: compChildrenPaths.filter((p) => p[0][2] == idx)
|
|
2915
|
+
};
|
|
2916
|
+
});
|
|
2917
|
+
const layerTreesAndPaths = components.map(({ tree: comp, path }) => {
|
|
2918
|
+
const sliceIds2 = matchingChildrenRefs.find(
|
|
2919
|
+
(cr) => cr.ref === comp._hash
|
|
2920
|
+
)?.sliceIds;
|
|
2921
|
+
if (!sliceIds2 || sliceIds2.length === 0) {
|
|
2922
|
+
throw new Error(
|
|
2923
|
+
`Db._get: No sliceIds found for component ${comp._hash} of layer ${nodeRow._hash}.`
|
|
2924
|
+
);
|
|
2925
|
+
}
|
|
2926
|
+
if (sliceIds2.length > 1) {
|
|
2927
|
+
throw new Error(
|
|
2928
|
+
`Db._get: Multiple sliceIds found for component ${comp._hash} of layer ${nodeRow._hash}.`
|
|
2929
|
+
);
|
|
2930
|
+
}
|
|
2931
|
+
const sliceId = sliceIds2[0];
|
|
2932
|
+
const pathsForSliceId = [
|
|
2933
|
+
...path.map((p) => p[0]).map((p) => {
|
|
2934
|
+
const newPath = [...p];
|
|
2935
|
+
newPath[2] = 0;
|
|
2936
|
+
return ["add", sliceId, ...newPath];
|
|
2937
|
+
})
|
|
2938
|
+
];
|
|
2939
|
+
return {
|
|
2940
|
+
[sliceId]: {
|
|
2941
|
+
tree: {
|
|
2942
|
+
[childrenTableKey]: {
|
|
2943
|
+
...{ _data: [comp] },
|
|
2944
|
+
...{ _type: "components" }
|
|
2945
|
+
}
|
|
2946
|
+
},
|
|
2947
|
+
path: pathsForSliceId
|
|
2948
|
+
}
|
|
2949
|
+
};
|
|
2950
|
+
});
|
|
2951
|
+
const layer = layerTreesAndPaths.flatMap(
|
|
2952
|
+
(ltap) => Object.entries(ltap).map(([sliceId, { tree }]) => ({
|
|
2953
|
+
[sliceId]: tree
|
|
2954
|
+
}))
|
|
2955
|
+
).reduce((a, b) => ({ ...a, ...b }), {});
|
|
2956
|
+
const paths = layerTreesAndPaths.map(
|
|
2957
|
+
(ltap) => Object.values(ltap)[0].path
|
|
2958
|
+
);
|
|
2959
|
+
nodeRowsMatchingChildrenRefs.set(nodeRow._hash, {
|
|
2960
|
+
rljson: nodeRow,
|
|
2961
|
+
tree: {
|
|
2962
|
+
...nodeRowObj,
|
|
2963
|
+
add: { ...nodeRowObj.add, ...layer }
|
|
2964
|
+
},
|
|
2965
|
+
cell: rowChildrenCell.map(
|
|
2966
|
+
(c, idx) => ({
|
|
2967
|
+
...c,
|
|
2968
|
+
...{
|
|
2969
|
+
path: [paths.flat()[idx]]
|
|
2406
2970
|
}
|
|
2971
|
+
})
|
|
2972
|
+
)
|
|
2973
|
+
});
|
|
2974
|
+
} else if (nodeType === "cakes") {
|
|
2975
|
+
nodeRowsMatchingChildrenRefs.set(nodeRow._hash, {
|
|
2976
|
+
rljson: nodeRow,
|
|
2977
|
+
tree: {
|
|
2978
|
+
...nodeRowObj,
|
|
2979
|
+
layers: { ...nodeRowObj.layers, ...rowChildrenTree }
|
|
2980
|
+
},
|
|
2981
|
+
cell: rowChildrenCell.map((c) => ({
|
|
2982
|
+
...c,
|
|
2983
|
+
...{
|
|
2984
|
+
path: c.path.map((p) => ["layers", ...p])
|
|
2407
2985
|
}
|
|
2986
|
+
}))
|
|
2987
|
+
});
|
|
2988
|
+
} else if (nodeType === "components") {
|
|
2989
|
+
if (rowChildrenTree && Object.keys(rowChildrenTree).length > 0) {
|
|
2990
|
+
const columnReferenceMap = nodeColumnCfgs.filter(
|
|
2991
|
+
(c) => c.ref && ["components", "cakes"].includes(c.ref.type)
|
|
2992
|
+
).reduce((acc, curr) => {
|
|
2993
|
+
acc.set(curr.key, curr.ref.tableKey);
|
|
2994
|
+
return acc;
|
|
2995
|
+
}, /* @__PURE__ */ new Map());
|
|
2996
|
+
const resolvedRefs = {};
|
|
2997
|
+
for (const [colKey, childTableKey] of columnReferenceMap) {
|
|
2998
|
+
const tree = {
|
|
2999
|
+
...rowChildrenTree[childTableKey],
|
|
3000
|
+
...{ _tableKey: childTableKey }
|
|
3001
|
+
};
|
|
3002
|
+
const cell = rowChildrenCell.map((c) => ({
|
|
3003
|
+
...c,
|
|
3004
|
+
...{
|
|
3005
|
+
path: c.path.filter((p) => p[0] === childTableKey).map((p) => [colKey, ...p.slice(1)])
|
|
3006
|
+
}
|
|
3007
|
+
}));
|
|
3008
|
+
resolvedRefs[colKey] = { tree, cell };
|
|
2408
3009
|
}
|
|
3010
|
+
const resolvedProperties = Object.entries(resolvedRefs).map(([colKey, { tree }]) => ({
|
|
3011
|
+
[colKey]: tree
|
|
3012
|
+
})).reduce((a, b) => ({ ...a, ...b }), {});
|
|
3013
|
+
const resolvedTree = {
|
|
3014
|
+
...nodeRowObj,
|
|
3015
|
+
...resolvedProperties
|
|
3016
|
+
};
|
|
3017
|
+
const resolvedCell = Object.values(resolvedRefs).map((r) => r.cell).flat();
|
|
3018
|
+
nodeRowsMatchingChildrenRefs.set(nodeRow._hash, {
|
|
3019
|
+
rljson: nodeRow,
|
|
3020
|
+
tree: resolvedTree,
|
|
3021
|
+
cell: resolvedCell
|
|
3022
|
+
});
|
|
3023
|
+
} else {
|
|
3024
|
+
nodeRowsMatchingChildrenRefs.set(nodeRow._hash, {
|
|
3025
|
+
rljson: nodeRow,
|
|
3026
|
+
tree: { ...nodeRowObj },
|
|
3027
|
+
cell: rowChildrenCell
|
|
3028
|
+
});
|
|
2409
3029
|
}
|
|
3030
|
+
} else {
|
|
3031
|
+
throw new Error(
|
|
3032
|
+
`Db._get: Unsupported node type ${nodeType} for getting children.`
|
|
3033
|
+
);
|
|
2410
3034
|
}
|
|
2411
3035
|
}
|
|
2412
|
-
const nodeChildren =
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
3036
|
+
const nodeChildren = makeUnique(
|
|
3037
|
+
merge(...nodeChildrenArray)
|
|
3038
|
+
);
|
|
3039
|
+
const matchedNodeRows = Array.from(nodeRowsMatchingChildrenRefs.values());
|
|
3040
|
+
const result = {
|
|
3041
|
+
rljson: {
|
|
2416
3042
|
...node,
|
|
2417
3043
|
...{
|
|
2418
3044
|
[nodeTableKey]: {
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
3045
|
+
...{
|
|
3046
|
+
_data: matchedNodeRows.map((mr) => mr.rljson),
|
|
3047
|
+
_type: nodeType
|
|
3048
|
+
},
|
|
3049
|
+
...{
|
|
3050
|
+
...nodeHash ? { _hash: nodeHash } : {}
|
|
3051
|
+
}
|
|
2422
3052
|
}
|
|
2423
3053
|
},
|
|
2424
3054
|
...nodeChildren
|
|
2425
|
-
}
|
|
2426
|
-
|
|
2427
|
-
|
|
3055
|
+
},
|
|
3056
|
+
tree: {
|
|
3057
|
+
[nodeTableKey]: {
|
|
3058
|
+
...{
|
|
3059
|
+
_data: matchedNodeRows.map((mr) => mr.tree),
|
|
3060
|
+
_type: nodeType
|
|
3061
|
+
},
|
|
3062
|
+
...{
|
|
3063
|
+
...nodeHash ? { _hash: nodeHash } : {}
|
|
3064
|
+
}
|
|
3065
|
+
}
|
|
3066
|
+
},
|
|
3067
|
+
cell: matchedNodeRows.map(
|
|
3068
|
+
(mr, idx) => mr.cell.map((c) => ({
|
|
3069
|
+
...c,
|
|
3070
|
+
...{ path: c.path.map((p) => [nodeTableKey, "_data", idx, ...p]) }
|
|
3071
|
+
}))
|
|
3072
|
+
).flat()
|
|
3073
|
+
};
|
|
3074
|
+
this._cache.set(cacheHash, result);
|
|
3075
|
+
return result;
|
|
2428
3076
|
}
|
|
2429
3077
|
// ...........................................................................
|
|
2430
3078
|
/**
|
|
@@ -2451,234 +3099,66 @@ class Db {
|
|
|
2451
3099
|
* @param rljson - The Rljson to join data for
|
|
2452
3100
|
*/
|
|
2453
3101
|
async join(columnSelection, cakeKey, cakeRef) {
|
|
2454
|
-
const
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
3102
|
+
const {
|
|
3103
|
+
tree: { [cakeKey]: cakesTable }
|
|
3104
|
+
} = await this.get(Route.fromFlat(`${cakeKey}@${cakeRef}`), {});
|
|
3105
|
+
const cakes = cakesTable._data;
|
|
3106
|
+
if (cakes.length === 0) {
|
|
2458
3107
|
throw new Error(
|
|
2459
3108
|
`Db.join: Cake with ref "${cakeRef}" not found in cake table "${cakeKey}".`
|
|
2460
3109
|
);
|
|
2461
3110
|
}
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
const layersTable = data[layerKey];
|
|
2466
|
-
const layer = layersTable._data.find(
|
|
2467
|
-
(l) => l._hash === cake.layers[layerKey]
|
|
3111
|
+
if (cakes.length > 1) {
|
|
3112
|
+
throw new Error(
|
|
3113
|
+
`Db.join: Multiple cakes with ref "${cakeRef}" found in cake table "${cakeKey}".`
|
|
2468
3114
|
);
|
|
2469
|
-
layers.set(layerKey, layer);
|
|
2470
|
-
}
|
|
2471
|
-
const components = /* @__PURE__ */ new Map();
|
|
2472
|
-
for (const [tableKey, table] of Object.entries(data)) {
|
|
2473
|
-
if (table._type !== "components") continue;
|
|
2474
|
-
components.set(tableKey, table);
|
|
2475
3115
|
}
|
|
2476
|
-
const
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
const componentKey = layer.componentsTable;
|
|
2494
|
-
const componentResolved = await this._resolveComponent(
|
|
2495
|
-
`${cakeKey}/${layerKey}/`,
|
|
2496
|
-
componentKey
|
|
2497
|
-
);
|
|
2498
|
-
objectMap = { ...objectMap, ...componentResolved.objectMap };
|
|
2499
|
-
columnInfos.set(componentKey, componentResolved.columnInfos);
|
|
2500
|
-
columnCfgs.set(componentKey, componentResolved.columnCfgs);
|
|
2501
|
-
}
|
|
2502
|
-
const rowMap = /* @__PURE__ */ new Map();
|
|
2503
|
-
const joinColumnInfos = /* @__PURE__ */ new Map();
|
|
2504
|
-
for (const sliceId of mergedSliceIds) {
|
|
2505
|
-
const sliceIdRow = [];
|
|
2506
|
-
for (const [layerKey, layer] of layers.entries()) {
|
|
2507
|
-
const layerRef = layer._hash;
|
|
2508
|
-
const componentKey = layer.componentsTable;
|
|
2509
|
-
const componentRef = layer.add[sliceId];
|
|
2510
|
-
const componentsTable = data[componentKey];
|
|
2511
|
-
const rowComponentProperties = componentsTable._data.find(
|
|
2512
|
-
(r) => r._hash === componentRef
|
|
2513
|
-
);
|
|
2514
|
-
const resolvedProperties = this._resolveComponentProperties(
|
|
2515
|
-
rowComponentProperties,
|
|
2516
|
-
objectMap,
|
|
2517
|
-
data
|
|
2518
|
-
);
|
|
2519
|
-
const joinColumns = [];
|
|
2520
|
-
for (const [
|
|
2521
|
-
resolvedPropertyKey,
|
|
2522
|
-
resolvedPropertyValue
|
|
2523
|
-
] of Object.entries(resolvedProperties)) {
|
|
2524
|
-
const propertyRoute = cakeKey + "/" + layerKey + "/" + componentKey + "/" + resolvedPropertyKey;
|
|
2525
|
-
const propertyColumnInfo = columnInfos.get(componentKey).find((cI) => cI.route === propertyRoute);
|
|
2526
|
-
if (!!propertyColumnInfo) {
|
|
2527
|
-
const joinColumnRoute = Route.fromFlat(
|
|
2528
|
-
`${cakeKey}@${cakeRef}/${layerKey}@${layerRef}/${componentKey}@${componentRef}`
|
|
2529
|
-
);
|
|
2530
|
-
joinColumnRoute.propertyKey = resolvedPropertyKey;
|
|
2531
|
-
joinColumnInfos.set(resolvedPropertyKey, {
|
|
2532
|
-
...propertyColumnInfo,
|
|
2533
|
-
key: resolvedPropertyKey,
|
|
2534
|
-
route: joinColumnRoute.flatWithoutRefs.slice(1)
|
|
2535
|
-
});
|
|
2536
|
-
joinColumns.push({
|
|
2537
|
-
route: joinColumnRoute,
|
|
2538
|
-
value: resolvedPropertyValue ?? null,
|
|
2539
|
-
insert: null
|
|
2540
|
-
});
|
|
2541
|
-
}
|
|
2542
|
-
}
|
|
2543
|
-
sliceIdRow.push(...joinColumns);
|
|
2544
|
-
}
|
|
2545
|
-
rowMap.set(sliceId, sliceIdRow);
|
|
2546
|
-
}
|
|
2547
|
-
const joinRows = {};
|
|
2548
|
-
for (const [sliceId, joinColumns] of rowMap.entries()) {
|
|
2549
|
-
Object.assign(joinRows, {
|
|
2550
|
-
[sliceId]: joinColumns
|
|
2551
|
-
});
|
|
2552
|
-
}
|
|
2553
|
-
return new Join(
|
|
2554
|
-
joinRows,
|
|
2555
|
-
new ColumnSelection(Array.from(joinColumnInfos.values())),
|
|
2556
|
-
objectMap
|
|
2557
|
-
).select(columnSelection);
|
|
2558
|
-
}
|
|
2559
|
-
_resolveComponentProperties(componentData, objectMapOrRoute, baseData) {
|
|
2560
|
-
let result = {};
|
|
2561
|
-
for (const [propertyKey, propertyObjectMap] of Object.entries(
|
|
2562
|
-
objectMapOrRoute
|
|
2563
|
-
)) {
|
|
2564
|
-
if (propertyKey === "_tableKey") continue;
|
|
2565
|
-
if (typeof propertyObjectMap === "object") {
|
|
2566
|
-
const refs = Array.isArray(componentData[propertyKey]) ? componentData[propertyKey] : [componentData[propertyKey]];
|
|
2567
|
-
const refTableKey = propertyObjectMap["_tableKey"];
|
|
2568
|
-
const refCompTable = baseData[refTableKey];
|
|
2569
|
-
if (!refCompTable) continue;
|
|
2570
|
-
const prefixedResolvedRefCompData = {};
|
|
2571
|
-
for (const ref of refs) {
|
|
2572
|
-
const refCompData = refCompTable._data.find((r) => r._hash === ref);
|
|
2573
|
-
if (refCompData) {
|
|
2574
|
-
const resolvedRefCompData = this._resolveComponentProperties(
|
|
2575
|
-
refCompData,
|
|
2576
|
-
propertyObjectMap,
|
|
2577
|
-
baseData
|
|
2578
|
-
);
|
|
2579
|
-
for (const [refPropKey, value] of Object.entries(
|
|
2580
|
-
resolvedRefCompData
|
|
2581
|
-
)) {
|
|
2582
|
-
if (!prefixedResolvedRefCompData[`${refTableKey}/${refPropKey}`]) {
|
|
2583
|
-
prefixedResolvedRefCompData[`${refTableKey}/${refPropKey}`] = [];
|
|
2584
|
-
}
|
|
2585
|
-
prefixedResolvedRefCompData[`${refTableKey}/${refPropKey}`].push({
|
|
2586
|
-
_ref: ref,
|
|
2587
|
-
_value: value
|
|
2588
|
-
});
|
|
2589
|
-
}
|
|
2590
|
-
}
|
|
2591
|
-
result = {
|
|
2592
|
-
...result,
|
|
2593
|
-
...prefixedResolvedRefCompData
|
|
2594
|
-
};
|
|
2595
|
-
}
|
|
2596
|
-
}
|
|
2597
|
-
result = {
|
|
2598
|
-
...result,
|
|
2599
|
-
...{ [propertyKey]: componentData[propertyKey] }
|
|
2600
|
-
};
|
|
2601
|
-
}
|
|
2602
|
-
return result;
|
|
2603
|
-
}
|
|
2604
|
-
// ...........................................................................
|
|
2605
|
-
/**
|
|
2606
|
-
* Resolve a component's columns, including referenced components
|
|
2607
|
-
*
|
|
2608
|
-
* @param baseRoute - The base route for the component
|
|
2609
|
-
* @param componentKey - The component's table key
|
|
2610
|
-
* @returns - The resolved column configurations, column infos, and object map
|
|
2611
|
-
*/
|
|
2612
|
-
async _resolveComponent(baseRoute, componentKey) {
|
|
2613
|
-
const { columns: colCfgs } = await this.core.tableCfg(componentKey);
|
|
2614
|
-
const objectMap = {};
|
|
2615
|
-
const columnCfgs = [];
|
|
2616
|
-
const columnInfos = [];
|
|
2617
|
-
for (let i = 0; i < colCfgs.length; i++) {
|
|
2618
|
-
if (colCfgs[i].key === "_hash") continue;
|
|
2619
|
-
const colCfg = colCfgs[i];
|
|
2620
|
-
if (colCfg.ref) {
|
|
2621
|
-
const columnCfgsAndInfosForRef = await this._resolveComponent(
|
|
2622
|
-
baseRoute + `/${componentKey}`,
|
|
2623
|
-
colCfg.ref.tableKey
|
|
3116
|
+
const cake = cakes[0];
|
|
3117
|
+
const sliceIds = await this._resolveSliceIds(
|
|
3118
|
+
cake.sliceIdsTable,
|
|
3119
|
+
cake.sliceIdsRow
|
|
3120
|
+
);
|
|
3121
|
+
const rows = {};
|
|
3122
|
+
for (const sliceId of sliceIds) {
|
|
3123
|
+
const row = [];
|
|
3124
|
+
for (const columnInfo of columnSelection.columns) {
|
|
3125
|
+
const columnRoute = Route.fromFlat(
|
|
3126
|
+
columnInfo.route
|
|
3127
|
+
).toRouteWithProperty();
|
|
3128
|
+
const columnContainer = await this.get(
|
|
3129
|
+
columnRoute,
|
|
3130
|
+
cakeRef,
|
|
3131
|
+
void 0,
|
|
3132
|
+
[sliceId]
|
|
2624
3133
|
);
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
3134
|
+
const column = {
|
|
3135
|
+
route: columnRoute,
|
|
3136
|
+
value: columnContainer,
|
|
3137
|
+
inserts: null
|
|
2628
3138
|
};
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
key: colCfg.ref.tableKey + "/" + cc.key
|
|
2633
|
-
})
|
|
2634
|
-
);
|
|
2635
|
-
const columnInfosForRef = columnCfgsAndInfosForRef.columnInfos.map(
|
|
2636
|
-
(cc) => ({
|
|
2637
|
-
...cc,
|
|
2638
|
-
key: colCfg.ref.tableKey + "/" + cc.key
|
|
2639
|
-
})
|
|
2640
|
-
);
|
|
2641
|
-
columnCfgs.push(...columnCfgsForRef);
|
|
2642
|
-
columnInfos.push(...columnInfosForRef);
|
|
2643
|
-
}
|
|
2644
|
-
const columnRoute = Route.fromFlat(
|
|
2645
|
-
baseRoute.length > 0 ? `${baseRoute}/${componentKey}/${colCfg.key}` : `/${componentKey}/${colCfg.key}`
|
|
2646
|
-
).flat.slice(1);
|
|
2647
|
-
if (!objectMap[colCfg.key]) objectMap[colCfg.key] = columnRoute;
|
|
2648
|
-
columnCfgs.push(colCfg);
|
|
2649
|
-
columnInfos.push({
|
|
2650
|
-
...colCfg,
|
|
2651
|
-
alias: `${colCfg.key}`,
|
|
2652
|
-
route: columnRoute,
|
|
2653
|
-
titleShort: colCfg.key,
|
|
2654
|
-
titleLong: colCfg.key
|
|
2655
|
-
});
|
|
3139
|
+
row.push(column);
|
|
3140
|
+
}
|
|
3141
|
+
rows[sliceId] = row;
|
|
2656
3142
|
}
|
|
2657
|
-
return
|
|
3143
|
+
return new Join(rows, columnSelection);
|
|
2658
3144
|
}
|
|
2659
3145
|
// ...........................................................................
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
const
|
|
2669
|
-
|
|
2670
|
-
);
|
|
2671
|
-
uniqueComponentRoutes.add(
|
|
2672
|
-
isolatedComponentRoute.toRouteWithoutProperty().flat
|
|
3146
|
+
async _resolveSliceIds(sliceIdTable, sliceIdRow) {
|
|
3147
|
+
const sliceIdController = new SliceIdController(this.core, sliceIdTable);
|
|
3148
|
+
sliceIdController.init();
|
|
3149
|
+
const resolvedSliceIds = /* @__PURE__ */ new Set();
|
|
3150
|
+
const {
|
|
3151
|
+
[sliceIdTable]: { _data: sliceIds }
|
|
3152
|
+
} = await sliceIdController.get(sliceIdRow);
|
|
3153
|
+
for (const sliceId of sliceIds) {
|
|
3154
|
+
const baseSliceIds = await sliceIdController.resolveBaseSliceIds(
|
|
3155
|
+
sliceId
|
|
2673
3156
|
);
|
|
3157
|
+
for (const sId of baseSliceIds.add) {
|
|
3158
|
+
resolvedSliceIds.add(sId);
|
|
3159
|
+
}
|
|
2674
3160
|
}
|
|
2675
|
-
|
|
2676
|
-
for (const compRouteFlat of uniqueComponentRoutes) {
|
|
2677
|
-
const uniqueComponentRoute = Route.fromFlat(compRouteFlat);
|
|
2678
|
-
const componentData = await this.get(uniqueComponentRoute, {});
|
|
2679
|
-
Object.assign(data, componentData);
|
|
2680
|
-
}
|
|
2681
|
-
return data;
|
|
3161
|
+
return Array.from(resolvedSliceIds);
|
|
2682
3162
|
}
|
|
2683
3163
|
// ...........................................................................
|
|
2684
3164
|
/**
|
|
@@ -2687,17 +3167,15 @@ class Db {
|
|
|
2687
3167
|
* @returns The result of the Insert as an InsertHistoryRow
|
|
2688
3168
|
* @throws {Error} If the Insert is not valid or if any controller cannot be created
|
|
2689
3169
|
*/
|
|
2690
|
-
async insert(
|
|
2691
|
-
const
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
${JSON.stringify(errors, null, 2)}`
|
|
2698
|
-
);
|
|
3170
|
+
async insert(route, tree, options) {
|
|
3171
|
+
const controllers = await this.indexedControllers(
|
|
3172
|
+
Route.fromFlat(route.flatWithoutRefs)
|
|
3173
|
+
);
|
|
3174
|
+
const runFns = {};
|
|
3175
|
+
for (const [tableKey, controller] of Object.entries(controllers)) {
|
|
3176
|
+
runFns[tableKey] = controller.insert.bind(controller);
|
|
2699
3177
|
}
|
|
2700
|
-
return this._insert(
|
|
3178
|
+
return this._insert(route, tree, runFns, options);
|
|
2701
3179
|
}
|
|
2702
3180
|
// ...........................................................................
|
|
2703
3181
|
/**
|
|
@@ -2708,87 +3186,198 @@ ${JSON.stringify(errors, null, 2)}`
|
|
|
2708
3186
|
* @returns The result of the Insert
|
|
2709
3187
|
* @throws {Error} If the route is not valid or if any controller cannot be created
|
|
2710
3188
|
*/
|
|
2711
|
-
async _insert(
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
const
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
if (Route.segmentHasDefaultRef(segment)) {
|
|
2723
|
-
const timeIds = await this.getTimeIdsForRef(
|
|
2724
|
-
tableKey,
|
|
2725
|
-
Route.segmentRef(segment)
|
|
2726
|
-
);
|
|
2727
|
-
previous = [...previous, ...timeIds];
|
|
2728
|
-
}
|
|
3189
|
+
async _insert(route, tree, runFns, options) {
|
|
3190
|
+
const results = [];
|
|
3191
|
+
const nodeRoute = route;
|
|
3192
|
+
const nodeSegment = nodeRoute.segment(0);
|
|
3193
|
+
const nodeTableKey = nodeSegment.tableKey;
|
|
3194
|
+
const nodeTree = tree[nodeTableKey];
|
|
3195
|
+
const nodeType = nodeTree._type;
|
|
3196
|
+
if (nodeTree._data.length === 0) {
|
|
3197
|
+
throw new Error(
|
|
3198
|
+
`Db._insert: No data found for table "${nodeTableKey}" in route "${route.flat}".`
|
|
3199
|
+
);
|
|
2729
3200
|
}
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
3201
|
+
const previousHash = nodeSegment[nodeTableKey + "Ref"] ?? null;
|
|
3202
|
+
const previousTimeId = nodeSegment[nodeTableKey + "InsertHistoryRef"] ?? null;
|
|
3203
|
+
const previous = previousHash ? await this.getTimeIdsForRef(nodeTableKey, previousHash) : previousTimeId ? [previousTimeId] : [];
|
|
3204
|
+
if (!nodeRoute.isRoot) {
|
|
3205
|
+
const childRoute = nodeRoute.deeper(1);
|
|
3206
|
+
const childTableKey = childRoute.top.tableKey;
|
|
3207
|
+
if (nodeType === "cakes") {
|
|
3208
|
+
const cakes = nodeTree._data;
|
|
3209
|
+
if (cakes.length > 1) ;
|
|
3210
|
+
const cake = cakes[0];
|
|
3211
|
+
const childTree = cake.layers[childTableKey];
|
|
2737
3212
|
const childResults = await this._insert(
|
|
2738
|
-
childInsert,
|
|
2739
3213
|
childRoute,
|
|
3214
|
+
{ [childTableKey]: childTree },
|
|
2740
3215
|
runFns
|
|
2741
3216
|
);
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
3217
|
+
if (childResults.length > 1) {
|
|
3218
|
+
throw new Error(
|
|
3219
|
+
`Db._insert: Multiple inserts returned for child table "${childTableKey}" when inserting into cake table "${nodeTableKey}". Only single child inserts are supported.`
|
|
3220
|
+
);
|
|
3221
|
+
}
|
|
3222
|
+
const childResult = childResults[0];
|
|
3223
|
+
const insertValue = {
|
|
3224
|
+
...cake,
|
|
3225
|
+
...{
|
|
3226
|
+
layers: {
|
|
3227
|
+
...cake.layers,
|
|
3228
|
+
...{
|
|
3229
|
+
[childTableKey]: childResult[childTableKey + "Ref"]
|
|
3230
|
+
}
|
|
3231
|
+
}
|
|
3232
|
+
}
|
|
3233
|
+
};
|
|
3234
|
+
const runFn = runFns[nodeTableKey];
|
|
3235
|
+
const result = await runFn("add", rmhsh(insertValue), "db.insert");
|
|
3236
|
+
results.push(
|
|
3237
|
+
...result.map((r) => ({
|
|
3238
|
+
...r,
|
|
3239
|
+
...{ previous },
|
|
3240
|
+
...{ route: route.flat }
|
|
3241
|
+
}))
|
|
2745
3242
|
);
|
|
2746
|
-
childRefs[k] = childRefArray;
|
|
2747
3243
|
}
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
{
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
3244
|
+
if (nodeType === "layers") {
|
|
3245
|
+
const layers = nodeTree._data;
|
|
3246
|
+
for (const layer of layers) {
|
|
3247
|
+
const layerInsert = {};
|
|
3248
|
+
for (const [sliceId, componentTree] of Object.entries(layer.add)) {
|
|
3249
|
+
if (sliceId === "_hash") continue;
|
|
3250
|
+
const writtenComponents = await this._insert(
|
|
3251
|
+
childRoute,
|
|
3252
|
+
componentTree,
|
|
3253
|
+
runFns
|
|
3254
|
+
);
|
|
3255
|
+
if (writtenComponents.length > 1) {
|
|
3256
|
+
throw new Error(
|
|
3257
|
+
`Db._insert: Multiple components written for layer "${layer._hash}" and sliceId "${sliceId}" is currently not supported.`
|
|
3258
|
+
);
|
|
3259
|
+
}
|
|
3260
|
+
const writtenComponent = writtenComponents[0];
|
|
3261
|
+
if (!writtenComponent || !writtenComponent[childTableKey + "Ref"]) {
|
|
3262
|
+
throw new Error(
|
|
3263
|
+
`Db._insert: No component reference returned for layer "${layer._hash}" and sliceId "${sliceId}".`
|
|
3264
|
+
);
|
|
3265
|
+
}
|
|
3266
|
+
layerInsert[sliceId] = writtenComponent[childTableKey + "Ref"];
|
|
3267
|
+
}
|
|
3268
|
+
const runFn = runFns[nodeTableKey];
|
|
3269
|
+
const result = await runFn(
|
|
3270
|
+
"add",
|
|
3271
|
+
rmhsh({
|
|
3272
|
+
...layer,
|
|
3273
|
+
...{ add: layerInsert }
|
|
3274
|
+
}),
|
|
3275
|
+
"db.insert"
|
|
3276
|
+
);
|
|
3277
|
+
results.push(
|
|
3278
|
+
...result.map((r) => ({
|
|
3279
|
+
...r,
|
|
3280
|
+
...{ previous },
|
|
3281
|
+
...{ route: route.flat }
|
|
3282
|
+
}))
|
|
3283
|
+
);
|
|
3284
|
+
}
|
|
3285
|
+
}
|
|
3286
|
+
if ([
|
|
3287
|
+
"components",
|
|
3288
|
+
"edits",
|
|
3289
|
+
"multiEdits",
|
|
3290
|
+
"editHistory",
|
|
3291
|
+
"head"
|
|
3292
|
+
].includes(nodeType)) {
|
|
3293
|
+
const runFn = runFns[nodeTableKey];
|
|
3294
|
+
const components = nodeTree._data;
|
|
3295
|
+
for (const component of components) {
|
|
3296
|
+
const resolvedComponent = { ...component };
|
|
3297
|
+
for (const [property, value] of Object.entries(component)) {
|
|
3298
|
+
if (value.hasOwnProperty("_tableKey") && value._tableKey === childTableKey) {
|
|
3299
|
+
const writtenReferences = await this._insert(
|
|
3300
|
+
childRoute,
|
|
3301
|
+
{ [childTableKey]: value },
|
|
3302
|
+
runFns
|
|
3303
|
+
);
|
|
3304
|
+
resolvedComponent[property] = writtenReferences.map(
|
|
3305
|
+
(wr) => wr[childTableKey + "Ref"]
|
|
3306
|
+
);
|
|
3307
|
+
}
|
|
3308
|
+
}
|
|
3309
|
+
const result = await runFn(
|
|
3310
|
+
"add",
|
|
3311
|
+
rmhsh(resolvedComponent),
|
|
3312
|
+
"db.insert"
|
|
3313
|
+
);
|
|
3314
|
+
results.push(
|
|
3315
|
+
...result.map((r) => ({
|
|
3316
|
+
...r,
|
|
3317
|
+
...{ previous },
|
|
3318
|
+
...{ route: route.flat }
|
|
3319
|
+
}))
|
|
3320
|
+
);
|
|
3321
|
+
}
|
|
3322
|
+
}
|
|
2759
3323
|
} else {
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
3324
|
+
const runFn = runFns[nodeTableKey];
|
|
3325
|
+
if ([
|
|
3326
|
+
"components",
|
|
3327
|
+
"edits",
|
|
3328
|
+
"multiEdits",
|
|
3329
|
+
"editHistory",
|
|
3330
|
+
"head"
|
|
3331
|
+
].includes(nodeType)) {
|
|
3332
|
+
const components = rmhsh(
|
|
3333
|
+
tree[nodeTableKey]
|
|
3334
|
+
);
|
|
3335
|
+
for (const component of components._data) {
|
|
3336
|
+
if (!component) continue;
|
|
3337
|
+
delete component._tableKey;
|
|
3338
|
+
delete component._type;
|
|
3339
|
+
const result = await runFn("add", component, "db.insert");
|
|
3340
|
+
results.push(
|
|
3341
|
+
...result.map((r) => ({
|
|
3342
|
+
...r,
|
|
3343
|
+
...{ previous },
|
|
3344
|
+
...{ route: route.flat }
|
|
3345
|
+
}))
|
|
3346
|
+
);
|
|
3347
|
+
}
|
|
3348
|
+
}
|
|
3349
|
+
if (nodeType === "layers") {
|
|
3350
|
+
const layers = rmhsh(tree[nodeTableKey]);
|
|
3351
|
+
for (const layer of layers._data) {
|
|
3352
|
+
const result = await runFn("add", layer, "db.insert");
|
|
3353
|
+
results.push(
|
|
3354
|
+
...result.map((r) => ({
|
|
3355
|
+
...r,
|
|
3356
|
+
...{ previous },
|
|
3357
|
+
...{ route: route.flat }
|
|
3358
|
+
}))
|
|
3359
|
+
);
|
|
3360
|
+
}
|
|
3361
|
+
}
|
|
3362
|
+
if (nodeType === "cakes") {
|
|
3363
|
+
const cakes = rmhsh(tree[nodeTableKey]);
|
|
3364
|
+
for (const cake of cakes._data) {
|
|
3365
|
+
const result = await runFn("add", cake, "db.insert");
|
|
3366
|
+
results.push(
|
|
3367
|
+
...result.map((r) => ({
|
|
3368
|
+
...r,
|
|
3369
|
+
...{ previous },
|
|
3370
|
+
...{ route: route.flat }
|
|
3371
|
+
}))
|
|
3372
|
+
);
|
|
2778
3373
|
}
|
|
2779
3374
|
}
|
|
2780
|
-
results = [
|
|
2781
|
-
...(await runFn(insert.command, insertValue, insert.origin)).map(
|
|
2782
|
-
(r) => ({ ...r, previous })
|
|
2783
|
-
)
|
|
2784
|
-
];
|
|
2785
3375
|
}
|
|
2786
3376
|
for (const result of results) {
|
|
2787
|
-
result.route = insert.route;
|
|
2788
3377
|
if (!options?.skipHistory)
|
|
2789
|
-
await this._writeInsertHistory(
|
|
3378
|
+
await this._writeInsertHistory(nodeTableKey, result);
|
|
2790
3379
|
if (!options?.skipNotification)
|
|
2791
|
-
this.notify.notify(Route.fromFlat(
|
|
3380
|
+
this.notify.notify(Route.fromFlat(result.route), result);
|
|
2792
3381
|
}
|
|
2793
3382
|
return results;
|
|
2794
3383
|
}
|
|
@@ -2811,48 +3400,6 @@ ${JSON.stringify(errors, null, 2)}`
|
|
|
2811
3400
|
this.notify.unregister(route, callback);
|
|
2812
3401
|
}
|
|
2813
3402
|
// ...........................................................................
|
|
2814
|
-
/**
|
|
2815
|
-
* Resolves an Insert by returning the run functions of all controllers involved in the Insert's route
|
|
2816
|
-
* @param Insert - The Insert to resolve
|
|
2817
|
-
* @returns A record of controller run functions, keyed by table name
|
|
2818
|
-
* @throws {Error} If the route is not valid or if any controller cannot be created
|
|
2819
|
-
*/
|
|
2820
|
-
async _resolveInsert(Insert2) {
|
|
2821
|
-
const controllers = await this._indexedControllers(
|
|
2822
|
-
Route.fromFlat(Insert2.route)
|
|
2823
|
-
);
|
|
2824
|
-
const referencedComponentTableKeys = /* @__PURE__ */ new Set();
|
|
2825
|
-
traverse(Insert2.value, ({ key, parent }) => {
|
|
2826
|
-
if (key == "_tableKey")
|
|
2827
|
-
referencedComponentTableKeys.add(parent[key]);
|
|
2828
|
-
});
|
|
2829
|
-
for (const tableKey of referencedComponentTableKeys) {
|
|
2830
|
-
controllers[tableKey] ??= await this.getController(tableKey);
|
|
2831
|
-
}
|
|
2832
|
-
const runFns = {};
|
|
2833
|
-
for (const tableKey of Object.keys(controllers)) {
|
|
2834
|
-
runFns[tableKey] = controllers[tableKey].insert.bind(
|
|
2835
|
-
controllers[tableKey]
|
|
2836
|
-
);
|
|
2837
|
-
}
|
|
2838
|
-
return runFns;
|
|
2839
|
-
}
|
|
2840
|
-
// ...........................................................................
|
|
2841
|
-
/**
|
|
2842
|
-
* Returns the keys of child refs in a value based on a route
|
|
2843
|
-
* @param value - The value to check
|
|
2844
|
-
* @returns An array of keys of child refs in the value
|
|
2845
|
-
*/
|
|
2846
|
-
_childKeys(value) {
|
|
2847
|
-
const keys = Object.keys(value);
|
|
2848
|
-
const childKeys = [];
|
|
2849
|
-
for (const k of keys) {
|
|
2850
|
-
if (typeof value[k] !== "object") continue;
|
|
2851
|
-
childKeys.push(k);
|
|
2852
|
-
}
|
|
2853
|
-
return childKeys;
|
|
2854
|
-
}
|
|
2855
|
-
// ...........................................................................
|
|
2856
3403
|
/**
|
|
2857
3404
|
* Get a controller for a specific table
|
|
2858
3405
|
* @param tableKey - The key of the table to get the controller for
|
|
@@ -2869,7 +3416,7 @@ ${JSON.stringify(errors, null, 2)}`
|
|
|
2869
3416
|
return createController(contentType, this.core, tableKey, refs);
|
|
2870
3417
|
}
|
|
2871
3418
|
// ...........................................................................
|
|
2872
|
-
async
|
|
3419
|
+
async indexedControllers(route) {
|
|
2873
3420
|
const controllers = {};
|
|
2874
3421
|
const isolatedRoute = await this.isolatePropertyKeyFromRoute(route);
|
|
2875
3422
|
for (let i = 0; i < isolatedRoute.segments.length; i++) {
|
|
@@ -2901,6 +3448,114 @@ ${JSON.stringify(errors, null, 2)}`
|
|
|
2901
3448
|
});
|
|
2902
3449
|
}
|
|
2903
3450
|
// ...........................................................................
|
|
3451
|
+
/**
|
|
3452
|
+
* Add a head revision for a cake
|
|
3453
|
+
* @param cakeKey - The cake table key
|
|
3454
|
+
* @param cakeRef - The cake reference
|
|
3455
|
+
*/
|
|
3456
|
+
async addHeadRevision(cakeKey, cakeRef) {
|
|
3457
|
+
const cakeHeadKey = cakeKey + "Heads";
|
|
3458
|
+
const cakeHeadController = await this.getController(cakeHeadKey);
|
|
3459
|
+
return await cakeHeadController.insert("add", {
|
|
3460
|
+
cakeRef,
|
|
3461
|
+
timeId: timeId(),
|
|
3462
|
+
_hash: ""
|
|
3463
|
+
});
|
|
3464
|
+
}
|
|
3465
|
+
// ...........................................................................
|
|
3466
|
+
/**
|
|
3467
|
+
* Add a multiEdit
|
|
3468
|
+
* @param cakeKey - The cake table key
|
|
3469
|
+
* @param multiEdit - The multiEdit to add
|
|
3470
|
+
*/
|
|
3471
|
+
async addMultiEdit(cakeKey, multiEdit) {
|
|
3472
|
+
return this.insert(
|
|
3473
|
+
Route.fromFlat(cakeKey + "MultiEdits"),
|
|
3474
|
+
{
|
|
3475
|
+
[cakeKey + "MultiEdits"]: {
|
|
3476
|
+
_data: [multiEdit],
|
|
3477
|
+
_type: "multiEdits"
|
|
3478
|
+
}
|
|
3479
|
+
},
|
|
3480
|
+
{ skipHistory: true }
|
|
3481
|
+
);
|
|
3482
|
+
}
|
|
3483
|
+
// ...........................................................................
|
|
3484
|
+
/**
|
|
3485
|
+
* Get multiEdits
|
|
3486
|
+
* @param cakeKey - The cake table key
|
|
3487
|
+
* @param where - The where clause to filter multiEdits
|
|
3488
|
+
*/
|
|
3489
|
+
async getMultiEdits(cakeKey, where) {
|
|
3490
|
+
const multiEditController = await this.getController(
|
|
3491
|
+
cakeKey + "MultiEdits"
|
|
3492
|
+
);
|
|
3493
|
+
const { [cakeKey + "MultiEdits"]: result } = await multiEditController.get(
|
|
3494
|
+
where
|
|
3495
|
+
);
|
|
3496
|
+
return result._data;
|
|
3497
|
+
}
|
|
3498
|
+
// ...........................................................................
|
|
3499
|
+
/**
|
|
3500
|
+
* Add an edit
|
|
3501
|
+
* @param cakeKey - The cake table key
|
|
3502
|
+
* @param edit - The edit to add
|
|
3503
|
+
*/
|
|
3504
|
+
async addEdit(cakeKey, edit) {
|
|
3505
|
+
return this.insert(
|
|
3506
|
+
Route.fromFlat(cakeKey + "Edits"),
|
|
3507
|
+
{
|
|
3508
|
+
[cakeKey + "Edits"]: {
|
|
3509
|
+
_data: [edit],
|
|
3510
|
+
_type: "edits"
|
|
3511
|
+
}
|
|
3512
|
+
},
|
|
3513
|
+
{ skipHistory: true }
|
|
3514
|
+
);
|
|
3515
|
+
}
|
|
3516
|
+
// ...........................................................................
|
|
3517
|
+
/**
|
|
3518
|
+
* Get edits
|
|
3519
|
+
* @param cakeKey - The cake table key
|
|
3520
|
+
* @param where - The where clause to filter edits
|
|
3521
|
+
*/
|
|
3522
|
+
async getEdits(cakeKey, where) {
|
|
3523
|
+
const editController = await this.getController(cakeKey + "Edits");
|
|
3524
|
+
const { [cakeKey + "Edits"]: result } = await editController.get(where);
|
|
3525
|
+
return result._data;
|
|
3526
|
+
}
|
|
3527
|
+
// ...........................................................................
|
|
3528
|
+
/**
|
|
3529
|
+
* Add an edit history entry
|
|
3530
|
+
* @param cakeKey - The cake table key
|
|
3531
|
+
* @param editHistory - The edit history entry to add
|
|
3532
|
+
*/
|
|
3533
|
+
async addEditHistory(cakeKey, editHistory) {
|
|
3534
|
+
return this.insert(
|
|
3535
|
+
Route.fromFlat(cakeKey + "EditHistory"),
|
|
3536
|
+
{
|
|
3537
|
+
[cakeKey + "EditHistory"]: {
|
|
3538
|
+
_data: [editHistory],
|
|
3539
|
+
_type: "editHistory"
|
|
3540
|
+
}
|
|
3541
|
+
},
|
|
3542
|
+
{ skipHistory: true }
|
|
3543
|
+
);
|
|
3544
|
+
}
|
|
3545
|
+
// ...........................................................................
|
|
3546
|
+
/**
|
|
3547
|
+
* Get edit history entries
|
|
3548
|
+
* @param cakeKey - The cake table key
|
|
3549
|
+
* @param where - The where clause to filter edit history entries
|
|
3550
|
+
*/
|
|
3551
|
+
async getEditHistories(cakeKey, where) {
|
|
3552
|
+
const editHistoryController = await this.getController(
|
|
3553
|
+
cakeKey + "EditHistory"
|
|
3554
|
+
);
|
|
3555
|
+
const { [cakeKey + "EditHistory"]: result } = await editHistoryController.get(where);
|
|
3556
|
+
return result._data;
|
|
3557
|
+
}
|
|
3558
|
+
// ...........................................................................
|
|
2904
3559
|
/**
|
|
2905
3560
|
* Get the InsertHistory of a table
|
|
2906
3561
|
* @param table - The table to get the InsertHistory for
|