@rljson/db 0.0.6 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/db.d.ts +27 -3
- package/dist/db.js +622 -205
- package/package.json +13 -13
package/dist/db.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Io } from '@rljson/io';
|
|
2
2
|
import { Json } from '@rljson/json';
|
|
3
|
-
import { Insert, InsertHistoryRow, InsertHistoryTimeId, Ref, Rljson, Route } from '@rljson/rljson';
|
|
3
|
+
import { Insert, InsertHistoryRow, InsertHistoryTimeId, Ref, Rljson, Route, TableKey } from '@rljson/rljson';
|
|
4
4
|
import { Controller, ControllerRefs } from './controller/controller.ts';
|
|
5
5
|
import { Core } from './core.ts';
|
|
6
6
|
import { Join } from './join/join.ts';
|
|
@@ -33,7 +33,21 @@ export declare class Db {
|
|
|
33
33
|
* @throws {Error} If the route is not valid or if any controller cannot be created
|
|
34
34
|
*/
|
|
35
35
|
get(route: Route, where: string | Json): Promise<Rljson>;
|
|
36
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Resolves the route and returns corresponding data for any segment of the route,
|
|
38
|
+
* matching recursive filters and where clauses
|
|
39
|
+
*
|
|
40
|
+
* @param route - The route to get data from
|
|
41
|
+
* @param where - The recursive filtering key/value pairs to apply to the data
|
|
42
|
+
* @param controllers - The controllers to use for fetching data
|
|
43
|
+
* @param filter - Optional filter to apply to the data at the current route segment
|
|
44
|
+
* @returns - An Rljson object matching the route and filters
|
|
45
|
+
*/
|
|
46
|
+
_get(route: Route, where: string | Json, controllers: Record<string, Controller<any, any>>, filter?: Array<{
|
|
47
|
+
tableKey: TableKey;
|
|
48
|
+
columnKey?: string;
|
|
49
|
+
ref: Ref;
|
|
50
|
+
}>): Promise<Rljson>;
|
|
37
51
|
/**
|
|
38
52
|
* Get the reference (hash) of a route segment, considering default refs and insertHistory refs
|
|
39
53
|
* @param segment - The route segment to get the reference for
|
|
@@ -45,6 +59,15 @@ export declare class Db {
|
|
|
45
59
|
* @param rljson - The Rljson to join data for
|
|
46
60
|
*/
|
|
47
61
|
join(columnSelection: ColumnSelection, cakeKey: string, cakeRef: Ref): Promise<Join>;
|
|
62
|
+
private _resolveComponentProperties;
|
|
63
|
+
/**
|
|
64
|
+
* Resolve a component's columns, including referenced components
|
|
65
|
+
*
|
|
66
|
+
* @param baseRoute - The base route for the component
|
|
67
|
+
* @param componentKey - The component's table key
|
|
68
|
+
* @returns - The resolved column configurations, column infos, and object map
|
|
69
|
+
*/
|
|
70
|
+
private _resolveComponent;
|
|
48
71
|
/**
|
|
49
72
|
* Fetches data for the given ColumnSelection
|
|
50
73
|
* @param columnSelection - The ColumnSelection to fetch data for
|
|
@@ -58,7 +81,8 @@ export declare class Db {
|
|
|
58
81
|
*/
|
|
59
82
|
insert(insert: Insert<any>, options?: {
|
|
60
83
|
skipNotification?: boolean;
|
|
61
|
-
|
|
84
|
+
skipHistory?: boolean;
|
|
85
|
+
}): Promise<InsertHistoryRow<any>[]>;
|
|
62
86
|
/**
|
|
63
87
|
* Recursively runs controllers based on the route of the Insert
|
|
64
88
|
* @param insert - The Insert to run
|
package/dist/db.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { equals, merge } from "@rljson/json";
|
|
2
2
|
import { timeId, createInsertHistoryTableCfg, Validate, BaseValidator, Route, validateInsert, isTimeId } from "@rljson/rljson";
|
|
3
|
+
import { traverse } from "object-traversal";
|
|
3
4
|
import { rmhsh, hsh, Hash, hip } from "@rljson/hash";
|
|
4
5
|
import { IoMem } from "@rljson/io";
|
|
5
6
|
import { compileExpression } from "filtrex";
|
|
@@ -87,7 +88,7 @@ class CakeController extends BaseController {
|
|
|
87
88
|
if (this._table._type !== "cakes") {
|
|
88
89
|
throw new Error(`Table ${this._tableKey} is not of type cakes.`);
|
|
89
90
|
}
|
|
90
|
-
if (this._refs && this._refs.base) {
|
|
91
|
+
if (this._refs && this._refs.base && this._refs.base.length > 0) {
|
|
91
92
|
const {
|
|
92
93
|
[this._tableKey]: { _data: baseCakes }
|
|
93
94
|
} = await this._core.readRow(this._tableKey, this._refs.base);
|
|
@@ -102,6 +103,7 @@ class CakeController extends BaseController {
|
|
|
102
103
|
sliceIdsTable: cake.sliceIdsTable,
|
|
103
104
|
sliceIdsRow: cake.sliceIdsRow
|
|
104
105
|
};
|
|
106
|
+
this._baseLayers = rmhsh(cake.layers);
|
|
105
107
|
}
|
|
106
108
|
}
|
|
107
109
|
async getChildRefs(where, filter) {
|
|
@@ -126,8 +128,20 @@ class CakeController extends BaseController {
|
|
|
126
128
|
if (!command.startsWith("add")) {
|
|
127
129
|
throw new Error(`Command ${command} is not supported by CakeController.`);
|
|
128
130
|
}
|
|
131
|
+
if (this._refs?.base) delete this._refs.base;
|
|
132
|
+
const normalizedValue = {};
|
|
133
|
+
for (const [layerTable, layerRef] of Object.entries(
|
|
134
|
+
value
|
|
135
|
+
)) {
|
|
136
|
+
if (Array.isArray(layerRef) && layerRef.length > 1) {
|
|
137
|
+
throw new Error(
|
|
138
|
+
`CakeController insert: Layer ref for table ${layerTable} cannot be an array of size > 1. No 1:n relations supported.`
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
normalizedValue[layerTable] = Array.isArray(layerRef) ? layerRef[0] : layerRef;
|
|
142
|
+
}
|
|
129
143
|
const cake = {
|
|
130
|
-
layers: { ...this._baseLayers, ...
|
|
144
|
+
layers: { ...this._baseLayers, ...normalizedValue },
|
|
131
145
|
...refs || this._refs
|
|
132
146
|
};
|
|
133
147
|
const rlJson = { [this._tableKey]: { _data: [cake] } };
|
|
@@ -141,7 +155,7 @@ class CakeController extends BaseController {
|
|
|
141
155
|
//Unique id/timestamp
|
|
142
156
|
timeId: timeId()
|
|
143
157
|
};
|
|
144
|
-
return result;
|
|
158
|
+
return [result];
|
|
145
159
|
}
|
|
146
160
|
async get(where, filter) {
|
|
147
161
|
if (typeof where === "string") {
|
|
@@ -196,18 +210,49 @@ class ComponentController extends BaseController {
|
|
|
196
210
|
if (!!refs) {
|
|
197
211
|
throw new Error(`Refs are not supported on ComponentController.`);
|
|
198
212
|
}
|
|
199
|
-
const
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
213
|
+
const values = [];
|
|
214
|
+
const referencedValues = /* @__PURE__ */ new Map();
|
|
215
|
+
for (const [k, v] of Object.entries(value)) {
|
|
216
|
+
if (Array.isArray(v)) {
|
|
217
|
+
for (const possibleRef of v) {
|
|
218
|
+
if (typeof possibleRef === "object" && possibleRef.hasOwnProperty("_ref") && possibleRef.hasOwnProperty("_value")) {
|
|
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 = {
|
|
245
|
+
//Ref to component
|
|
246
|
+
[this._tableKey + "Ref"]: hsh(component)._hash,
|
|
247
|
+
//Data from edit
|
|
248
|
+
route: "",
|
|
249
|
+
origin,
|
|
250
|
+
//Unique id/timestamp
|
|
251
|
+
timeId: timeId()
|
|
252
|
+
};
|
|
253
|
+
results.push(result);
|
|
254
|
+
}
|
|
255
|
+
return results;
|
|
211
256
|
}
|
|
212
257
|
// ...........................................................................
|
|
213
258
|
/**
|
|
@@ -536,12 +581,23 @@ class LayerController extends BaseController {
|
|
|
536
581
|
`Command ${command} is not supported by LayerController.`
|
|
537
582
|
);
|
|
538
583
|
}
|
|
584
|
+
const normalizedValue = {};
|
|
585
|
+
for (const [sliceId, compRef] of Object.entries(
|
|
586
|
+
value
|
|
587
|
+
)) {
|
|
588
|
+
if (Array.isArray(compRef) && compRef.length > 1) {
|
|
589
|
+
throw new Error(
|
|
590
|
+
`LayerController insert: Component ref for slice ${sliceId} cannot be an array of size > 1. No 1:n relations supported.`
|
|
591
|
+
);
|
|
592
|
+
}
|
|
593
|
+
normalizedValue[sliceId] = Array.isArray(compRef) ? compRef[0] : compRef;
|
|
594
|
+
}
|
|
539
595
|
const layer = command.startsWith("add") === true ? {
|
|
540
|
-
add:
|
|
596
|
+
add: normalizedValue,
|
|
541
597
|
...refs || this._refs
|
|
542
598
|
} : {
|
|
543
599
|
add: {},
|
|
544
|
-
remove:
|
|
600
|
+
remove: normalizedValue,
|
|
545
601
|
...refs || this._refs
|
|
546
602
|
};
|
|
547
603
|
const rlJson = { [this._tableKey]: { _data: [layer] } };
|
|
@@ -555,7 +611,7 @@ class LayerController extends BaseController {
|
|
|
555
611
|
//Unique id/timestamp
|
|
556
612
|
timeId: timeId()
|
|
557
613
|
};
|
|
558
|
-
return result;
|
|
614
|
+
return [result];
|
|
559
615
|
}
|
|
560
616
|
async get(where, filter) {
|
|
561
617
|
if (typeof where === "string") {
|
|
@@ -975,44 +1031,134 @@ class ColumnSelection {
|
|
|
975
1031
|
static exampleCarsColumnSelection() {
|
|
976
1032
|
return new ColumnSelection([
|
|
977
1033
|
{
|
|
978
|
-
route: "carCake/carGeneralLayer/carGeneral/brand",
|
|
979
1034
|
key: "brand",
|
|
1035
|
+
route: "carCake/carGeneralLayer/carGeneral/brand",
|
|
980
1036
|
alias: "brand",
|
|
981
|
-
titleShort: "Brand",
|
|
982
1037
|
titleLong: "Car Brand",
|
|
983
|
-
|
|
1038
|
+
titleShort: "Brand",
|
|
1039
|
+
type: "string",
|
|
1040
|
+
_hash: ""
|
|
984
1041
|
},
|
|
985
1042
|
{
|
|
986
|
-
route: "carCake/carGeneralLayer/carGeneral/type",
|
|
987
1043
|
key: "type",
|
|
1044
|
+
route: "carCake/carGeneralLayer/carGeneral/type",
|
|
988
1045
|
alias: "type",
|
|
989
|
-
titleShort: "Type",
|
|
990
1046
|
titleLong: "Car Type",
|
|
991
|
-
|
|
1047
|
+
titleShort: "Type",
|
|
1048
|
+
type: "string",
|
|
1049
|
+
_hash: ""
|
|
1050
|
+
},
|
|
1051
|
+
{
|
|
1052
|
+
key: "serviceIntervals",
|
|
1053
|
+
route: "carCake/carGeneralLayer/carGeneral/serviceIntervals",
|
|
1054
|
+
alias: "serviceIntervals",
|
|
1055
|
+
titleLong: "Car Service Intervals",
|
|
1056
|
+
titleShort: "Service Intervals",
|
|
1057
|
+
type: "jsonValue",
|
|
1058
|
+
_hash: ""
|
|
992
1059
|
},
|
|
993
1060
|
{
|
|
994
|
-
route: "carCake/carGeneralLayer/carGeneral/isElectric",
|
|
995
1061
|
key: "isElectric",
|
|
1062
|
+
route: "carCake/carGeneralLayer/carGeneral/isElectric",
|
|
996
1063
|
alias: "isElectric",
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
type: "boolean"
|
|
1064
|
+
titleLong: "Is Electric Car",
|
|
1065
|
+
titleShort: "Electric",
|
|
1066
|
+
type: "boolean",
|
|
1067
|
+
_hash: ""
|
|
1000
1068
|
},
|
|
1001
1069
|
{
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
alias: "
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
type: "
|
|
1070
|
+
key: "carDimensions/height",
|
|
1071
|
+
route: "carCake/carTechnicalLayer/carTechnical/carDimensions/height",
|
|
1072
|
+
alias: "height",
|
|
1073
|
+
titleLong: "Car Height",
|
|
1074
|
+
titleShort: "Height",
|
|
1075
|
+
type: "number",
|
|
1076
|
+
_hash: ""
|
|
1008
1077
|
},
|
|
1009
1078
|
{
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
alias: "
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
type: "
|
|
1079
|
+
key: "carDimensions/width",
|
|
1080
|
+
route: "carCake/carTechnicalLayer/carTechnical/carDimensions/width",
|
|
1081
|
+
alias: "width",
|
|
1082
|
+
titleLong: "Car Width",
|
|
1083
|
+
titleShort: "Width",
|
|
1084
|
+
type: "number",
|
|
1085
|
+
_hash: ""
|
|
1086
|
+
},
|
|
1087
|
+
{
|
|
1088
|
+
key: "carDimensions/length",
|
|
1089
|
+
route: "carCake/carTechnicalLayer/carTechnical/carDimensions/length",
|
|
1090
|
+
alias: "length",
|
|
1091
|
+
titleLong: "Car Length",
|
|
1092
|
+
titleShort: "Length",
|
|
1093
|
+
type: "number",
|
|
1094
|
+
_hash: ""
|
|
1095
|
+
},
|
|
1096
|
+
{
|
|
1097
|
+
key: "engine",
|
|
1098
|
+
route: "carCake/carTechnicalLayer/carTechnical/engine",
|
|
1099
|
+
alias: "engine",
|
|
1100
|
+
titleLong: "Car Engine",
|
|
1101
|
+
titleShort: "Engine",
|
|
1102
|
+
type: "string",
|
|
1103
|
+
_hash: ""
|
|
1104
|
+
},
|
|
1105
|
+
{
|
|
1106
|
+
key: "repairedByWorkshop",
|
|
1107
|
+
route: "carCake/carTechnicalLayer/carTechnical/repairedByWorkshop",
|
|
1108
|
+
alias: "repairedByWorkshop",
|
|
1109
|
+
titleLong: "Was Repaired By Workshop",
|
|
1110
|
+
titleShort: "Repaired By Workshop",
|
|
1111
|
+
type: "boolean",
|
|
1112
|
+
_hash: ""
|
|
1113
|
+
}
|
|
1114
|
+
]);
|
|
1115
|
+
}
|
|
1116
|
+
static exampleCarsColumnSelectionOnlySomeColumns() {
|
|
1117
|
+
return new ColumnSelection([
|
|
1118
|
+
{
|
|
1119
|
+
key: "brand",
|
|
1120
|
+
route: "carCake/carGeneralLayer/carGeneral/brand",
|
|
1121
|
+
alias: "brand",
|
|
1122
|
+
titleLong: "Car Brand",
|
|
1123
|
+
titleShort: "Brand",
|
|
1124
|
+
type: "string",
|
|
1125
|
+
_hash: ""
|
|
1126
|
+
},
|
|
1127
|
+
{
|
|
1128
|
+
key: "type",
|
|
1129
|
+
route: "carCake/carGeneralLayer/carGeneral/type",
|
|
1130
|
+
alias: "type",
|
|
1131
|
+
titleLong: "Car Type",
|
|
1132
|
+
titleShort: "Type",
|
|
1133
|
+
type: "string",
|
|
1134
|
+
_hash: ""
|
|
1135
|
+
},
|
|
1136
|
+
{
|
|
1137
|
+
key: "serviceIntervals",
|
|
1138
|
+
route: "carCake/carGeneralLayer/carGeneral/serviceIntervals",
|
|
1139
|
+
alias: "serviceIntervals",
|
|
1140
|
+
titleLong: "Car Service Intervals",
|
|
1141
|
+
titleShort: "Service Intervals",
|
|
1142
|
+
type: "jsonValue",
|
|
1143
|
+
_hash: ""
|
|
1144
|
+
},
|
|
1145
|
+
{
|
|
1146
|
+
key: "isElectric",
|
|
1147
|
+
route: "carCake/carGeneralLayer/carGeneral/isElectric",
|
|
1148
|
+
alias: "isElectric",
|
|
1149
|
+
titleLong: "Is Electric Car",
|
|
1150
|
+
titleShort: "Electric",
|
|
1151
|
+
type: "boolean",
|
|
1152
|
+
_hash: ""
|
|
1153
|
+
},
|
|
1154
|
+
{
|
|
1155
|
+
key: "carDimensions/length",
|
|
1156
|
+
route: "carCake/carTechnicalLayer/carTechnical/carDimensions/length",
|
|
1157
|
+
alias: "length",
|
|
1158
|
+
titleLong: "Car Length",
|
|
1159
|
+
titleShort: "Length",
|
|
1160
|
+
type: "number",
|
|
1161
|
+
_hash: ""
|
|
1016
1162
|
}
|
|
1017
1163
|
]);
|
|
1018
1164
|
}
|
|
@@ -1493,8 +1639,25 @@ class RowFilterProcessor {
|
|
|
1493
1639
|
}
|
|
1494
1640
|
for (const i of remainingIndices) {
|
|
1495
1641
|
const cellValue = join.value(i, columnIndex);
|
|
1496
|
-
if (
|
|
1497
|
-
|
|
1642
|
+
if (Array.isArray(cellValue)) {
|
|
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 {
|
|
1658
|
+
if (filter.matches(cellValue)) {
|
|
1659
|
+
result.push(i);
|
|
1660
|
+
}
|
|
1498
1661
|
}
|
|
1499
1662
|
}
|
|
1500
1663
|
return result;
|
|
@@ -1545,7 +1708,7 @@ class RowFilterProcessor {
|
|
|
1545
1708
|
const route = item.route;
|
|
1546
1709
|
if (availableRoutes.includes(route) === false) {
|
|
1547
1710
|
throw new Error(
|
|
1548
|
-
`RowFilterProcessor: Error while applying filter to
|
|
1711
|
+
`RowFilterProcessor: Error while applying filter to join: There is a column filter for route "${route}", but the join does not have a column with this route.
|
|
1549
1712
|
|
|
1550
1713
|
Available routes:
|
|
1551
1714
|
${availableRoutes.map((a) => `- ${a}`).join("\n")}`
|
|
@@ -1555,8 +1718,9 @@ ${availableRoutes.map((a) => `- ${a}`).join("\n")}`
|
|
|
1555
1718
|
}
|
|
1556
1719
|
}
|
|
1557
1720
|
class Join {
|
|
1558
|
-
constructor(baseRows, _baseColumnSelection) {
|
|
1721
|
+
constructor(baseRows, _baseColumnSelection, _objectMap) {
|
|
1559
1722
|
this._baseColumnSelection = _baseColumnSelection;
|
|
1723
|
+
this._objectMap = _objectMap;
|
|
1560
1724
|
this._base = this._hashedRows(baseRows);
|
|
1561
1725
|
}
|
|
1562
1726
|
_base = {};
|
|
@@ -1686,16 +1850,30 @@ class Join {
|
|
|
1686
1850
|
* Returns insert Object of the join
|
|
1687
1851
|
*/
|
|
1688
1852
|
insert() {
|
|
1689
|
-
const cakeInserts = this.
|
|
1690
|
-
const
|
|
1691
|
-
const
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1853
|
+
const cakeInserts = this._insertCakeObjects(this.data);
|
|
1854
|
+
const cakeInsertsMergedOfLayerRoutes = /* @__PURE__ */ new Map();
|
|
1855
|
+
for (const { [this.cakeRoute.root.tableKey]: cakeInsert } of cakeInserts) {
|
|
1856
|
+
const cakeInsertRoute = cakeInsert.route.flat;
|
|
1857
|
+
if (!cakeInsertsMergedOfLayerRoutes.has(cakeInsertRoute)) {
|
|
1858
|
+
cakeInsertsMergedOfLayerRoutes.set(cakeInsertRoute, cakeInsert.value);
|
|
1859
|
+
} else {
|
|
1860
|
+
const existingValue = cakeInsertsMergedOfLayerRoutes.get(
|
|
1861
|
+
cakeInsertRoute
|
|
1862
|
+
);
|
|
1863
|
+
const mergedValue = merge(existingValue, cakeInsert.value);
|
|
1864
|
+
cakeInsertsMergedOfLayerRoutes.set(cakeInsertRoute, mergedValue);
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
const results = [];
|
|
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;
|
|
1699
1877
|
}
|
|
1700
1878
|
// ...........................................................................
|
|
1701
1879
|
/**
|
|
@@ -1739,7 +1917,12 @@ class Join {
|
|
|
1739
1917
|
*/
|
|
1740
1918
|
get layerRoutes() {
|
|
1741
1919
|
return Array.from(
|
|
1742
|
-
new Set(
|
|
1920
|
+
new Set(
|
|
1921
|
+
Object.values(this.columnSelection.columns).map((c) => [
|
|
1922
|
+
Route.fromFlat(c.route).top,
|
|
1923
|
+
Route.fromFlat(c.route).deeper(1).top
|
|
1924
|
+
]).map((segments) => new Route(segments).flat)
|
|
1925
|
+
)
|
|
1743
1926
|
).map((r) => Route.fromFlat(r));
|
|
1744
1927
|
}
|
|
1745
1928
|
// ...........................................................................
|
|
@@ -1748,7 +1931,11 @@ class Join {
|
|
|
1748
1931
|
*/
|
|
1749
1932
|
get cakeRoute() {
|
|
1750
1933
|
const cakeRoute = Array.from(
|
|
1751
|
-
new Set(
|
|
1934
|
+
new Set(
|
|
1935
|
+
Object.values(this.columnSelection.columns).map(
|
|
1936
|
+
(c) => Route.fromFlat(c.route).top.tableKey
|
|
1937
|
+
)
|
|
1938
|
+
)
|
|
1752
1939
|
).map((r) => Route.fromFlat(r));
|
|
1753
1940
|
if (cakeRoute.length !== 1) {
|
|
1754
1941
|
throw new Error(
|
|
@@ -1851,7 +2038,7 @@ class Join {
|
|
|
1851
2038
|
* @param rows - The join rows
|
|
1852
2039
|
* @returns The cake insert objects
|
|
1853
2040
|
*/
|
|
1854
|
-
|
|
2041
|
+
_insertCakeObjects(rows) {
|
|
1855
2042
|
const cakeInsertObjects = [];
|
|
1856
2043
|
const cakeRoute = this.cakeRoute;
|
|
1857
2044
|
for (const [sliceId, row] of Object.entries(rows)) {
|
|
@@ -1894,13 +2081,14 @@ class Join {
|
|
|
1894
2081
|
for (const [compRouteFlat, compInsertObj] of Object.entries(
|
|
1895
2082
|
insertComponentObjects
|
|
1896
2083
|
)) {
|
|
2084
|
+
if (!compInsertObj._somethingToInsert) continue;
|
|
1897
2085
|
const compRoute = Route.fromFlat(compRouteFlat);
|
|
1898
2086
|
if (layerRoute.includes(compRoute)) {
|
|
1899
2087
|
const layerInsertObj = {};
|
|
1900
2088
|
layerInsertObj[layerRoute.root.tableKey] = {
|
|
1901
|
-
route:
|
|
2089
|
+
route: Route.fromFlat(compRouteFlat),
|
|
1902
2090
|
value: {
|
|
1903
|
-
[sliceId]: compInsertObj
|
|
2091
|
+
[sliceId]: compInsertObj
|
|
1904
2092
|
}
|
|
1905
2093
|
};
|
|
1906
2094
|
layerInsertObjects.push(layerInsertObj);
|
|
@@ -1918,28 +2106,60 @@ class Join {
|
|
|
1918
2106
|
* @returns
|
|
1919
2107
|
*/
|
|
1920
2108
|
_insertComponentObjects(sliceId, insertColumns) {
|
|
1921
|
-
const componentRoutes = this.componentRoutes;
|
|
1922
2109
|
const columns = this._mergeInsertRow(sliceId, insertColumns);
|
|
2110
|
+
return this._denormalizeComponentInserts(columns, this._objectMap || {});
|
|
2111
|
+
}
|
|
2112
|
+
_denormalizeComponentInserts(columns, objectMap, refTableKey) {
|
|
1923
2113
|
const result = {};
|
|
1924
|
-
for (const
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
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
|
+
};
|
|
1937
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
|
+
};
|
|
1938
2162
|
}
|
|
1939
|
-
result[compRoute.flat] = {
|
|
1940
|
-
route: compRoute,
|
|
1941
|
-
value: compChanged ? compInsert : null
|
|
1942
|
-
};
|
|
1943
2163
|
}
|
|
1944
2164
|
return result;
|
|
1945
2165
|
}
|
|
@@ -2097,70 +2317,114 @@ class Db {
|
|
|
2097
2317
|
return data;
|
|
2098
2318
|
}
|
|
2099
2319
|
}
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2320
|
+
// ...........................................................................
|
|
2321
|
+
/**
|
|
2322
|
+
* Resolves the route and returns corresponding data for any segment of the route,
|
|
2323
|
+
* matching recursive filters and where clauses
|
|
2324
|
+
*
|
|
2325
|
+
* @param route - The route to get data from
|
|
2326
|
+
* @param where - The recursive filtering key/value pairs to apply to the data
|
|
2327
|
+
* @param controllers - The controllers to use for fetching data
|
|
2328
|
+
* @param filter - Optional filter to apply to the data at the current route segment
|
|
2329
|
+
* @returns - An Rljson object matching the route and filters
|
|
2330
|
+
*/
|
|
2331
|
+
async _get(route, where, controllers, filter) {
|
|
2332
|
+
const nodeTableKey = route.top.tableKey;
|
|
2333
|
+
const nodeRoute = route;
|
|
2334
|
+
const nodeRouteRef = await this._getReferenceOfRouteSegment(nodeRoute.top);
|
|
2335
|
+
const nodeController = controllers[nodeTableKey];
|
|
2336
|
+
let nodeWhere = typeof where === "object" ? { ...where } : where;
|
|
2337
|
+
if (!route.isRoot && typeof nodeWhere === "object") {
|
|
2338
|
+
delete nodeWhere[nodeRoute.deeper().top.tableKey];
|
|
2339
|
+
}
|
|
2340
|
+
nodeWhere = nodeWhere ? typeof nodeWhere === "string" ? { _hash: nodeWhere } : nodeWhere : {};
|
|
2341
|
+
if (nodeRouteRef && nodeRouteRef.length > 0)
|
|
2342
|
+
nodeWhere = { ...nodeWhere, ...{ _hash: nodeRouteRef } };
|
|
2343
|
+
delete nodeWhere["_through"];
|
|
2344
|
+
delete nodeWhere["_tableKey"];
|
|
2345
|
+
const {
|
|
2346
|
+
[nodeTableKey]: { _data: nodeRows, _type: nodeType, _hash: nodeHash }
|
|
2347
|
+
} = await nodeController.get(nodeWhere);
|
|
2348
|
+
const nodeRowsFiltered = [];
|
|
2349
|
+
if (filter && filter.length > 0) {
|
|
2350
|
+
for (const f of filter) {
|
|
2351
|
+
if (f.tableKey !== nodeTableKey) continue;
|
|
2352
|
+
for (const nodeRow of nodeRows) {
|
|
2353
|
+
if (nodeRow._hash === f.ref) {
|
|
2354
|
+
nodeRowsFiltered.push(nodeRow);
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
} else {
|
|
2359
|
+
nodeRowsFiltered.push(...nodeRows);
|
|
2360
|
+
}
|
|
2361
|
+
const node = {
|
|
2362
|
+
[nodeTableKey]: {
|
|
2363
|
+
_data: nodeRowsFiltered,
|
|
2364
|
+
_type: nodeType,
|
|
2365
|
+
_hash: nodeHash
|
|
2366
|
+
}
|
|
2367
|
+
};
|
|
2368
|
+
if (route.isRoot) {
|
|
2369
|
+
if (route.hasPropertyKey) {
|
|
2370
|
+
return this.isolatePropertyFromComponents(node, route.propertyKey);
|
|
2371
|
+
}
|
|
2372
|
+
return node;
|
|
2373
|
+
}
|
|
2374
|
+
const childrenRoute = route.deeper();
|
|
2375
|
+
const childrenTableKey = childrenRoute.top.tableKey;
|
|
2376
|
+
const childrenWhere = typeof where === "object" ? where[childrenTableKey] ?? {} : {};
|
|
2377
|
+
const childrenThroughProperty = childrenWhere?._through;
|
|
2378
|
+
const nodeChildrenArray = [];
|
|
2379
|
+
const nodeRowsMatchingChildrenRefs = /* @__PURE__ */ new Map();
|
|
2380
|
+
for (let i = 0; i < nodeRowsFiltered.length; i++) {
|
|
2381
|
+
const nodeRow = nodeRowsFiltered[i];
|
|
2382
|
+
const childrenRefs = await nodeController.getChildRefs(
|
|
2383
|
+
nodeRow._hash
|
|
2124
2384
|
);
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2385
|
+
const rowChildren = await this._get(
|
|
2386
|
+
childrenRoute,
|
|
2387
|
+
childrenWhere,
|
|
2388
|
+
controllers,
|
|
2389
|
+
childrenRefs
|
|
2390
|
+
);
|
|
2391
|
+
if (rowChildren[childrenTableKey]._data.length === 0) continue;
|
|
2392
|
+
nodeChildrenArray.push(rowChildren);
|
|
2393
|
+
if (childrenThroughProperty) {
|
|
2394
|
+
const childrenHashes = rowChildren[childrenTableKey]._data.map(
|
|
2395
|
+
(rc) => rc._hash
|
|
2132
2396
|
);
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
const
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
);
|
|
2144
|
-
if (includesChild) {
|
|
2145
|
-
filteredParentRows.set(
|
|
2146
|
-
row["_hash"],
|
|
2147
|
-
row
|
|
2148
|
-
);
|
|
2397
|
+
for (const nr of nodeRowsFiltered) {
|
|
2398
|
+
{
|
|
2399
|
+
const throughHashesInRowCouldBeArray = nr[childrenThroughProperty];
|
|
2400
|
+
const throughHashesInRow = Array.isArray(
|
|
2401
|
+
throughHashesInRowCouldBeArray
|
|
2402
|
+
) ? throughHashesInRowCouldBeArray : [throughHashesInRowCouldBeArray];
|
|
2403
|
+
for (const th of throughHashesInRow) {
|
|
2404
|
+
if (childrenHashes.includes(th)) {
|
|
2405
|
+
nodeRowsMatchingChildrenRefs.set(nr._hash, nr);
|
|
2406
|
+
}
|
|
2149
2407
|
}
|
|
2150
2408
|
}
|
|
2151
2409
|
}
|
|
2152
2410
|
}
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2411
|
+
}
|
|
2412
|
+
const nodeChildren = merge(...nodeChildrenArray);
|
|
2413
|
+
if (childrenThroughProperty) {
|
|
2414
|
+
const matchedNodeRows = Array.from(nodeRowsMatchingChildrenRefs.values());
|
|
2415
|
+
return {
|
|
2416
|
+
...node,
|
|
2417
|
+
...{
|
|
2418
|
+
[nodeTableKey]: {
|
|
2419
|
+
_data: matchedNodeRows,
|
|
2420
|
+
_type: nodeType,
|
|
2421
|
+
_hash: nodeHash
|
|
2158
2422
|
}
|
|
2159
|
-
}
|
|
2423
|
+
},
|
|
2424
|
+
...nodeChildren
|
|
2160
2425
|
};
|
|
2161
|
-
return merge(parentWithFilteredRows, ...children);
|
|
2162
2426
|
}
|
|
2163
|
-
return
|
|
2427
|
+
return { ...node, ...nodeChildren };
|
|
2164
2428
|
}
|
|
2165
2429
|
// ...........................................................................
|
|
2166
2430
|
/**
|
|
@@ -2204,6 +2468,11 @@ class Db {
|
|
|
2204
2468
|
);
|
|
2205
2469
|
layers.set(layerKey, layer);
|
|
2206
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
|
+
}
|
|
2207
2476
|
const mergedSliceIds = /* @__PURE__ */ new Set();
|
|
2208
2477
|
for (const layer of layers.values()) {
|
|
2209
2478
|
const sliceIdsTable = layer.sliceIdsTable;
|
|
@@ -2219,52 +2488,59 @@ class Db {
|
|
|
2219
2488
|
}
|
|
2220
2489
|
const columnCfgs = /* @__PURE__ */ new Map();
|
|
2221
2490
|
const columnInfos = /* @__PURE__ */ new Map();
|
|
2491
|
+
let objectMap = {};
|
|
2222
2492
|
for (const [layerKey, layer] of layers.entries()) {
|
|
2223
2493
|
const componentKey = layer.componentsTable;
|
|
2224
|
-
const
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
columnInfo.push({
|
|
2232
|
-
...colCfg,
|
|
2233
|
-
alias: `${colCfg.key}`,
|
|
2234
|
-
route: Route.fromFlat(
|
|
2235
|
-
`/${cakeKey}/${layerKey}/${componentKey}/${colCfg.key}`
|
|
2236
|
-
).flat.slice(1),
|
|
2237
|
-
titleShort: colCfg.key,
|
|
2238
|
-
titleLong: colCfg.key
|
|
2239
|
-
});
|
|
2240
|
-
}
|
|
2241
|
-
columnInfos.set(componentKey, columnInfo);
|
|
2242
|
-
columnCfgs.set(componentKey, columnCfg);
|
|
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);
|
|
2243
2501
|
}
|
|
2244
2502
|
const rowMap = /* @__PURE__ */ new Map();
|
|
2503
|
+
const joinColumnInfos = /* @__PURE__ */ new Map();
|
|
2245
2504
|
for (const sliceId of mergedSliceIds) {
|
|
2246
|
-
|
|
2505
|
+
const sliceIdRow = [];
|
|
2247
2506
|
for (const [layerKey, layer] of layers.entries()) {
|
|
2248
2507
|
const layerRef = layer._hash;
|
|
2249
2508
|
const componentKey = layer.componentsTable;
|
|
2250
2509
|
const componentRef = layer.add[sliceId];
|
|
2251
2510
|
const componentsTable = data[componentKey];
|
|
2252
|
-
const
|
|
2511
|
+
const rowComponentProperties = componentsTable._data.find(
|
|
2253
2512
|
(r) => r._hash === componentRef
|
|
2254
2513
|
);
|
|
2255
|
-
const
|
|
2514
|
+
const resolvedProperties = this._resolveComponentProperties(
|
|
2515
|
+
rowComponentProperties,
|
|
2516
|
+
objectMap,
|
|
2517
|
+
data
|
|
2518
|
+
);
|
|
2256
2519
|
const joinColumns = [];
|
|
2257
|
-
for (
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
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
|
+
}
|
|
2266
2542
|
}
|
|
2267
|
-
sliceIdRow
|
|
2543
|
+
sliceIdRow.push(...joinColumns);
|
|
2268
2544
|
}
|
|
2269
2545
|
rowMap.set(sliceId, sliceIdRow);
|
|
2270
2546
|
}
|
|
@@ -2274,9 +2550,111 @@ class Db {
|
|
|
2274
2550
|
[sliceId]: joinColumns
|
|
2275
2551
|
});
|
|
2276
2552
|
}
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
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
|
|
2624
|
+
);
|
|
2625
|
+
objectMap[colCfg.key] = {
|
|
2626
|
+
_tableKey: colCfg.ref.tableKey,
|
|
2627
|
+
...columnCfgsAndInfosForRef.objectMap
|
|
2628
|
+
};
|
|
2629
|
+
const columnCfgsForRef = columnCfgsAndInfosForRef.columnCfgs.map(
|
|
2630
|
+
(cc) => ({
|
|
2631
|
+
...cc,
|
|
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
|
+
});
|
|
2656
|
+
}
|
|
2657
|
+
return { columnCfgs, columnInfos, objectMap };
|
|
2280
2658
|
}
|
|
2281
2659
|
// ...........................................................................
|
|
2282
2660
|
/**
|
|
@@ -2286,8 +2664,13 @@ class Db {
|
|
|
2286
2664
|
async _getBaseDataForColumnSelection(columnSelection) {
|
|
2287
2665
|
const uniqueComponentRoutes = /* @__PURE__ */ new Set();
|
|
2288
2666
|
for (const colInfo of columnSelection.columns) {
|
|
2289
|
-
const
|
|
2290
|
-
|
|
2667
|
+
const componentRoute = Route.fromFlat(colInfo.route);
|
|
2668
|
+
const isolatedComponentRoute = await this.isolatePropertyKeyFromRoute(
|
|
2669
|
+
componentRoute
|
|
2670
|
+
);
|
|
2671
|
+
uniqueComponentRoutes.add(
|
|
2672
|
+
isolatedComponentRoute.toRouteWithoutProperty().flat
|
|
2673
|
+
);
|
|
2291
2674
|
}
|
|
2292
2675
|
const data = {};
|
|
2293
2676
|
for (const compRouteFlat of uniqueComponentRoutes) {
|
|
@@ -2326,7 +2709,7 @@ ${JSON.stringify(errors, null, 2)}`
|
|
|
2326
2709
|
* @throws {Error} If the route is not valid or if any controller cannot be created
|
|
2327
2710
|
*/
|
|
2328
2711
|
async _insert(insert, route, runFns, options) {
|
|
2329
|
-
let
|
|
2712
|
+
let results;
|
|
2330
2713
|
let tableKey;
|
|
2331
2714
|
const segment = route.segment(0);
|
|
2332
2715
|
tableKey = segment.tableKey;
|
|
@@ -2351,36 +2734,63 @@ ${JSON.stringify(errors, null, 2)}`
|
|
|
2351
2734
|
for (const k of childKeys) {
|
|
2352
2735
|
const childValue = insert.value[k];
|
|
2353
2736
|
const childInsert = { ...insert, value: childValue };
|
|
2354
|
-
const
|
|
2737
|
+
const childResults = await this._insert(
|
|
2738
|
+
childInsert,
|
|
2739
|
+
childRoute,
|
|
2740
|
+
runFns
|
|
2741
|
+
);
|
|
2355
2742
|
const childRefKey = childRoute.top.tableKey + "Ref";
|
|
2356
|
-
const
|
|
2357
|
-
|
|
2743
|
+
const childRefArray = childResults.map(
|
|
2744
|
+
(childResult) => childResult[childRefKey]
|
|
2745
|
+
);
|
|
2746
|
+
childRefs[k] = childRefArray;
|
|
2358
2747
|
}
|
|
2359
2748
|
const runFn = runFns[tableKey];
|
|
2360
|
-
|
|
2361
|
-
...await runFn(
|
|
2749
|
+
results = [
|
|
2750
|
+
...(await runFn(
|
|
2362
2751
|
insert.command,
|
|
2363
2752
|
{
|
|
2364
2753
|
...insert.value,
|
|
2365
2754
|
...childRefs
|
|
2366
2755
|
},
|
|
2367
2756
|
insert.origin
|
|
2368
|
-
),
|
|
2369
|
-
|
|
2370
|
-
};
|
|
2757
|
+
)).map((r) => ({ ...r, ...{ previous } }))
|
|
2758
|
+
];
|
|
2371
2759
|
} else {
|
|
2372
2760
|
tableKey = route.root.tableKey;
|
|
2373
2761
|
const runFn = runFns[tableKey];
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2762
|
+
const insertValue = insert.value;
|
|
2763
|
+
for (const [propertyKey, propertyValue] of Object.entries(insert.value)) {
|
|
2764
|
+
if (propertyValue && typeof propertyValue === "object" && !!propertyValue._tableKey) {
|
|
2765
|
+
const referenceRoute = propertyValue._tableKey;
|
|
2766
|
+
delete propertyValue._tableKey;
|
|
2767
|
+
const referenceInsert = {
|
|
2768
|
+
command: insert.command,
|
|
2769
|
+
route: referenceRoute,
|
|
2770
|
+
value: propertyValue
|
|
2771
|
+
};
|
|
2772
|
+
const referencesWritten = (await this._insert(
|
|
2773
|
+
referenceInsert,
|
|
2774
|
+
Route.fromFlat(referenceRoute),
|
|
2775
|
+
runFns
|
|
2776
|
+
)).map((h) => h[referenceRoute + "Ref"]);
|
|
2777
|
+
insertValue[propertyKey] = referencesWritten.length === 1 ? referencesWritten[0] : referencesWritten;
|
|
2778
|
+
}
|
|
2779
|
+
}
|
|
2780
|
+
results = [
|
|
2781
|
+
...(await runFn(insert.command, insertValue, insert.origin)).map(
|
|
2782
|
+
(r) => ({ ...r, previous })
|
|
2783
|
+
)
|
|
2784
|
+
];
|
|
2378
2785
|
}
|
|
2379
|
-
result
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2786
|
+
for (const result of results) {
|
|
2787
|
+
result.route = insert.route;
|
|
2788
|
+
if (!options?.skipHistory)
|
|
2789
|
+
await this._writeInsertHistory(tableKey, result);
|
|
2790
|
+
if (!options?.skipNotification)
|
|
2791
|
+
this.notify.notify(Route.fromFlat(insert.route), result);
|
|
2792
|
+
}
|
|
2793
|
+
return results;
|
|
2384
2794
|
}
|
|
2385
2795
|
// ...........................................................................
|
|
2386
2796
|
/**
|
|
@@ -2411,6 +2821,14 @@ ${JSON.stringify(errors, null, 2)}`
|
|
|
2411
2821
|
const controllers = await this._indexedControllers(
|
|
2412
2822
|
Route.fromFlat(Insert2.route)
|
|
2413
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
|
+
}
|
|
2414
2832
|
const runFns = {};
|
|
2415
2833
|
for (const tableKey of Object.keys(controllers)) {
|
|
2416
2834
|
runFns[tableKey] = controllers[tableKey].insert.bind(
|
|
@@ -2500,15 +2918,9 @@ ${JSON.stringify(errors, null, 2)}`
|
|
|
2500
2918
|
if (options.sorted) {
|
|
2501
2919
|
const dumpedTable = await this.core.dumpTable(insertHistoryTable);
|
|
2502
2920
|
const tableData = dumpedTable[insertHistoryTable]._data;
|
|
2503
|
-
tableData.sort(
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
if (options.ascending) {
|
|
2507
|
-
return aTime.localeCompare(bTime);
|
|
2508
|
-
} else {
|
|
2509
|
-
return bTime.localeCompare(aTime);
|
|
2510
|
-
}
|
|
2511
|
-
});
|
|
2921
|
+
tableData.sort(
|
|
2922
|
+
(a, b) => options.ascending ? a.timeId.localeCompare(b.timeId) : b.timeId.localeCompare(a.timeId)
|
|
2923
|
+
);
|
|
2512
2924
|
return {
|
|
2513
2925
|
[insertHistoryTable]: { _data: tableData, _type: "insertHistory" }
|
|
2514
2926
|
};
|
|
@@ -2591,15 +3003,20 @@ ${JSON.stringify(errors, null, 2)}`
|
|
|
2591
3003
|
* @returns A route with extracted property key
|
|
2592
3004
|
*/
|
|
2593
3005
|
async isolatePropertyKeyFromRoute(route) {
|
|
2594
|
-
const
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
const
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
3006
|
+
const segmentLength = route.segments.length;
|
|
3007
|
+
let propertyKey = "";
|
|
3008
|
+
let result = route;
|
|
3009
|
+
for (let i = segmentLength; i > 0; i--) {
|
|
3010
|
+
const segment = route.segments[i - 1];
|
|
3011
|
+
const tableKey = segment.tableKey;
|
|
3012
|
+
const tableExists = await this._io.tableExists(tableKey);
|
|
3013
|
+
if (!tableExists) {
|
|
3014
|
+
propertyKey = propertyKey.length > 0 ? segment.tableKey + "/" + propertyKey : segment.tableKey;
|
|
3015
|
+
result = result.upper();
|
|
3016
|
+
result.propertyKey = propertyKey;
|
|
3017
|
+
}
|
|
3018
|
+
}
|
|
3019
|
+
return result;
|
|
2603
3020
|
}
|
|
2604
3021
|
// ...........................................................................
|
|
2605
3022
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rljson/db",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"description": "A high level interface to read and write RLJSON data from and into a database",
|
|
5
5
|
"homepage": "https://github.com/rljson/db",
|
|
6
6
|
"bugs": "https://github.com/rljson/db/issues",
|
|
@@ -20,31 +20,31 @@
|
|
|
20
20
|
],
|
|
21
21
|
"type": "module",
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@types/node": "^24.10.
|
|
24
|
-
"@typescript-eslint/eslint-plugin": "^8.46.
|
|
25
|
-
"@typescript-eslint/parser": "^8.46.
|
|
26
|
-
"@vitest/coverage-v8": "^4.0.
|
|
23
|
+
"@types/node": "^24.10.1",
|
|
24
|
+
"@typescript-eslint/eslint-plugin": "^8.46.4",
|
|
25
|
+
"@typescript-eslint/parser": "^8.46.4",
|
|
26
|
+
"@vitest/coverage-v8": "^4.0.8",
|
|
27
27
|
"cross-env": "^10.1.0",
|
|
28
28
|
"eslint": "^9.39.1",
|
|
29
29
|
"eslint-plugin-jsdoc": "^61.1.12",
|
|
30
|
-
"eslint-plugin-tsdoc": "^0.
|
|
30
|
+
"eslint-plugin-tsdoc": "^0.5.0",
|
|
31
31
|
"globals": "^16.5.0",
|
|
32
32
|
"jsdoc": "^4.0.5",
|
|
33
|
-
"read-pkg": "^
|
|
33
|
+
"read-pkg": "^10.0.0",
|
|
34
34
|
"typescript": "~5.9.3",
|
|
35
|
-
"typescript-eslint": "^8.46.
|
|
36
|
-
"vite": "^7.2.
|
|
37
|
-
"vite-node": "^
|
|
35
|
+
"typescript-eslint": "^8.46.4",
|
|
36
|
+
"vite": "^7.2.2",
|
|
37
|
+
"vite-node": "^5.0.0",
|
|
38
38
|
"vite-plugin-dts": "^4.5.4",
|
|
39
39
|
"vite-tsconfig-paths": "^5.1.4",
|
|
40
|
-
"vitest": "^4.0.
|
|
40
|
+
"vitest": "^4.0.8",
|
|
41
41
|
"vitest-dom": "^0.1.1"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@rljson/hash": "^0.0.17",
|
|
45
|
-
"@rljson/io": "^0.0.
|
|
45
|
+
"@rljson/io": "^0.0.55",
|
|
46
46
|
"@rljson/json": "^0.0.23",
|
|
47
|
-
"@rljson/rljson": "^0.0.
|
|
47
|
+
"@rljson/rljson": "^0.0.69",
|
|
48
48
|
"@rljson/validate": "^0.0.11",
|
|
49
49
|
"filtrex": "^3.1.0",
|
|
50
50
|
"object-traversal": "^1.0.1",
|