goby-database 2.1.18 → 2.1.20
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/index.d.ts +11 -8
- package/dist/index.js +96 -61
- package/dist/types.d.ts +12 -1
- package/package.json +2 -5
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Database as DatabaseType, Statement } from 'better-sqlite3';
|
|
2
|
-
import type { SQLTableType, SQLClassListRow, SQLJunctonListRow, JunctionSides, JunctionList, ClassList, Property, DataType, ItemRelationSide, SQLApplicationWindow, ApplicationWindow, WorkspaceBlock, ClassData,
|
|
2
|
+
import type { SQLTableType, SQLClassListRow, SQLJunctonListRow, JunctionSides, JunctionList, ClassList, Property, DataType, ItemRelationSide, SQLApplicationWindow, ApplicationWindow, WorkspaceBlock, ClassData, ClassEdit, RelationEdit, PropertyEdit, MaxValues, PropertyType, RelationProperty, DataProperty, RelationEditValidSides, ItemPagination, PaginatedItems } from './types.js';
|
|
3
3
|
export default class Project {
|
|
4
4
|
db: DatabaseType;
|
|
5
5
|
run: {
|
|
@@ -19,10 +19,6 @@ export default class Project {
|
|
|
19
19
|
}>;
|
|
20
20
|
};
|
|
21
21
|
class_cache: ClassList;
|
|
22
|
-
item_cache: {
|
|
23
|
-
class_id: number;
|
|
24
|
-
items: ClassRow[];
|
|
25
|
-
}[];
|
|
26
22
|
junction_cache: JunctionList;
|
|
27
23
|
constructor(source: string);
|
|
28
24
|
get_latest_table_row_id(table_name: string): number | null;
|
|
@@ -81,12 +77,19 @@ export default class Project {
|
|
|
81
77
|
value: any;
|
|
82
78
|
}[]): void;
|
|
83
79
|
action_make_relations(relations: [input_1: ItemRelationSide, input_2: ItemRelationSide][]): void;
|
|
84
|
-
retrieve_class_items({ class_id, class_name, class_data }: {
|
|
80
|
+
retrieve_class_items({ class_id, class_name, class_data, pagination }: {
|
|
85
81
|
class_id: number;
|
|
86
82
|
class_name?: string;
|
|
87
83
|
class_data?: ClassData;
|
|
88
|
-
|
|
89
|
-
|
|
84
|
+
pagination?: ItemPagination;
|
|
85
|
+
}): PaginatedItems;
|
|
86
|
+
retrieve_all_classes(include?: {
|
|
87
|
+
all_items?: ItemPagination;
|
|
88
|
+
items_by_class?: {
|
|
89
|
+
class_id: number;
|
|
90
|
+
pagination: ItemPagination;
|
|
91
|
+
}[];
|
|
92
|
+
}): ClassData[];
|
|
90
93
|
parse_sql_prop(class_id: number, sql_prop: {
|
|
91
94
|
id: number;
|
|
92
95
|
type: PropertyType;
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,6 @@ const real_data_types = ['number'];
|
|
|
6
6
|
export default class Project {
|
|
7
7
|
constructor(source) {
|
|
8
8
|
this.class_cache = [];
|
|
9
|
-
this.item_cache = [];
|
|
10
9
|
this.junction_cache = [];
|
|
11
10
|
this.db = new Database(source);
|
|
12
11
|
//checks if goby has been initialized, initializes if not
|
|
@@ -52,7 +51,7 @@ export default class Project {
|
|
|
52
51
|
create_window: this.db.prepare(`INSERT INTO system_windows (type,open, metadata) VALUES (@type, @open, @meta)`),
|
|
53
52
|
get_windows: this.db.prepare(`SELECT id, type, open, metadata FROM system_windows`)
|
|
54
53
|
};
|
|
55
|
-
this.refresh_caches(['classlist', '
|
|
54
|
+
this.refresh_caches(['classlist', 'junctions']);
|
|
56
55
|
// commenting this out until I figure out my transaction / one-step-undo functionality
|
|
57
56
|
//if I understand transactions correctly, a new one will begin with every user action while committing the one before, meaning I'll need to have the first begin here
|
|
58
57
|
// this.run.begin.run();
|
|
@@ -108,18 +107,6 @@ export default class Project {
|
|
|
108
107
|
if (caches.includes('classlist')) {
|
|
109
108
|
this.class_cache = this.retrieve_all_classes();
|
|
110
109
|
}
|
|
111
|
-
if (caches.includes('items')) {
|
|
112
|
-
let refreshed_items = [];
|
|
113
|
-
for (let class_data of this.class_cache) {
|
|
114
|
-
let items = this.retrieve_class_items({ class_id: class_data.id });
|
|
115
|
-
refreshed_items.push({
|
|
116
|
-
class_id: class_data.id,
|
|
117
|
-
items
|
|
118
|
-
});
|
|
119
|
-
class_data.items = items;
|
|
120
|
-
}
|
|
121
|
-
this.item_cache = refreshed_items;
|
|
122
|
-
}
|
|
123
110
|
if (caches.includes('junctions')) {
|
|
124
111
|
this.junction_cache = this.get_junctions();
|
|
125
112
|
}
|
|
@@ -149,6 +136,10 @@ export default class Project {
|
|
|
149
136
|
const class_meta = {
|
|
150
137
|
style: {
|
|
151
138
|
color: '#b5ffd5'
|
|
139
|
+
},
|
|
140
|
+
label: {
|
|
141
|
+
// TODO: build functionality to change label property in the future
|
|
142
|
+
properties: [1]
|
|
152
143
|
}
|
|
153
144
|
};
|
|
154
145
|
// create entry for class in classlist
|
|
@@ -705,7 +696,6 @@ export default class Project {
|
|
|
705
696
|
//get the last item in class table order and use it to get the order for the new item
|
|
706
697
|
const new_order = this.get_next_order(`[class_${class_name}]`);
|
|
707
698
|
this.db.prepare(`INSERT INTO [class_${class_name}] (system_id, system_order) VALUES (${root_id},${new_order})`).run();
|
|
708
|
-
this.refresh_caches(['items']);
|
|
709
699
|
return root_id;
|
|
710
700
|
}
|
|
711
701
|
get_next_order(table_name) {
|
|
@@ -740,7 +730,6 @@ export default class Project {
|
|
|
740
730
|
const set_statements = sql_column_inserts.map((p) => `${p.column_name} = ?`).join(',');
|
|
741
731
|
const insert_statement = `UPDATE [class_${class_data.name}] SET ${set_statements} WHERE system_id=${item_id}`;
|
|
742
732
|
this.db.prepare(insert_statement).run(params);
|
|
743
|
-
this.refresh_caches(['items']);
|
|
744
733
|
function validate(input, data_type, max_values) {
|
|
745
734
|
const multiple = max_values == null || max_values > 1;
|
|
746
735
|
const values = multiple ? input : [input];
|
|
@@ -804,65 +793,93 @@ export default class Project {
|
|
|
804
793
|
throw Error('Something went wrong - junction table for relationship not found');
|
|
805
794
|
}
|
|
806
795
|
}
|
|
807
|
-
this.refresh_caches(['items']);
|
|
808
796
|
// NOTE: should this trigger a refresh to items?
|
|
809
797
|
}
|
|
810
|
-
|
|
798
|
+
// MARKER: modify item retrieval
|
|
799
|
+
retrieve_class_items({ class_id, class_name, class_data, pagination = {} }) {
|
|
800
|
+
var _a, _b, _c, _d;
|
|
801
|
+
const pagination_defaults = {
|
|
802
|
+
page_size: null,
|
|
803
|
+
property_range: 'all'
|
|
804
|
+
};
|
|
805
|
+
pagination = Object.assign(Object.assign({}, pagination_defaults), pagination);
|
|
806
|
+
const slim = pagination.property_range == 'slim';
|
|
811
807
|
if (class_name == undefined || class_data == undefined) {
|
|
812
808
|
class_data = this.lookup_class(class_id);
|
|
813
809
|
class_name = class_data.name;
|
|
814
810
|
}
|
|
815
811
|
;
|
|
816
812
|
const class_string = `[class_${class_name}]`;
|
|
817
|
-
//
|
|
813
|
+
// joined+added at beginning of the query, built from relations
|
|
818
814
|
const cte_strings = [];
|
|
819
|
-
//
|
|
815
|
+
// joined+added near the end of the query, built from relations
|
|
820
816
|
const cte_joins = [];
|
|
821
|
-
//
|
|
817
|
+
// joined+added between SELECT and FROM, built from relations
|
|
822
818
|
const relation_selections = [];
|
|
819
|
+
// NOTE: in the future, if a property_range is defined, first filter class_data.properties by those IDs
|
|
823
820
|
let relation_properties = class_data.properties.filter(a => a.type == 'relation');
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
821
|
+
if (!slim) {
|
|
822
|
+
for (let prop of relation_properties) {
|
|
823
|
+
const target_selects = [];
|
|
824
|
+
let property_junction_column_name = junction_col_name(class_id, prop.id);
|
|
825
|
+
if (prop.relation_targets.length > 0) {
|
|
826
|
+
for (let i = 0; i < prop.relation_targets.length; i++) {
|
|
827
|
+
// find the side that does not match both the class and prop IDs
|
|
828
|
+
let target = prop.relation_targets[i];
|
|
829
|
+
const target_class = this.class_cache.find((a) => a.id == (target === null || target === void 0 ? void 0 : target.class_id));
|
|
830
|
+
if (target && target_class) {
|
|
831
|
+
let target_junction_column_name = junction_col_name(target.class_id, target.prop_id);
|
|
832
|
+
// NOTE: as mentioned elsewhere, possibly allow multiple label props
|
|
833
|
+
const target_label_id = (_b = (_a = target_class === null || target_class === void 0 ? void 0 : target_class.metadata) === null || _a === void 0 ? void 0 : _a.label) === null || _b === void 0 ? void 0 : _b.properties[0];
|
|
834
|
+
const target_label = target_class === null || target_class === void 0 ? void 0 : target_class.properties.find((p) => p.id == target_label_id);
|
|
835
|
+
const label_sql_string = target_label ? `,'user_${target_label.name}',target_class."user_${target_label.name}"` : '';
|
|
836
|
+
let junction_id = target.junction_id;
|
|
837
|
+
let target_select = `
|
|
838
|
+
SELECT
|
|
839
|
+
"${property_junction_column_name}",
|
|
840
|
+
json_object('class_id',${target.class_id},'id',junction."${target_junction_column_name}"${label_sql_string}) AS target_data
|
|
841
|
+
FROM junction_${junction_id} AS junction
|
|
842
|
+
LEFT JOIN "class_${target_class === null || target_class === void 0 ? void 0 : target_class.name}" AS target_class ON junction."${target_junction_column_name}" = target_class.system_id
|
|
843
|
+
`;
|
|
844
|
+
target_selects.push(target_select);
|
|
845
|
+
}
|
|
846
|
+
else {
|
|
847
|
+
throw Error('Something went wrong trying to retrieve relationship data');
|
|
848
|
+
}
|
|
839
849
|
}
|
|
850
|
+
// uses built-in aggregate json function instead of group_concat craziness
|
|
851
|
+
const cte = `[${prop.id}_cte] AS (
|
|
852
|
+
SELECT "${property_junction_column_name}", json_group_array( json(target_data) ) AS [user_${prop.name}]
|
|
853
|
+
FROM
|
|
854
|
+
(
|
|
855
|
+
${target_selects.join(`
|
|
856
|
+
UNION
|
|
857
|
+
`)}
|
|
858
|
+
)
|
|
859
|
+
GROUP BY "${property_junction_column_name}"
|
|
860
|
+
)`;
|
|
861
|
+
cte_strings.push(cte);
|
|
862
|
+
relation_selections.push(`[${prop.id}_cte].[user_${prop.name}]`);
|
|
863
|
+
cte_joins.push(`LEFT JOIN [${prop.id}_cte] ON [${prop.id}_cte]."${property_junction_column_name}" = ${class_string}.system_id`);
|
|
864
|
+
}
|
|
865
|
+
else {
|
|
866
|
+
relation_selections.push(`'[]' AS [user_${prop.name}]`);
|
|
840
867
|
}
|
|
841
|
-
// uses built-in aggregate json function instead of group_concat craziness
|
|
842
|
-
const cte = `[${prop.id}_cte] AS (
|
|
843
|
-
SELECT "${property_junction_column_name}", json_group_array( json(target_data) ) AS [user_${prop.name}]
|
|
844
|
-
FROM
|
|
845
|
-
(
|
|
846
|
-
${target_selects.join(`
|
|
847
|
-
UNION
|
|
848
|
-
`)}
|
|
849
|
-
)
|
|
850
|
-
GROUP BY "${property_junction_column_name}"
|
|
851
|
-
)`;
|
|
852
|
-
cte_strings.push(cte);
|
|
853
|
-
relation_selections.push(`[${prop.id}_cte].[user_${prop.name}]`);
|
|
854
|
-
cte_joins.push(`LEFT JOIN [${prop.id}_cte] ON [${prop.id}_cte]."${property_junction_column_name}" = ${class_string}.system_id`);
|
|
855
|
-
}
|
|
856
|
-
else {
|
|
857
|
-
relation_selections.push(`'[]' AS [user_${prop.name}]`);
|
|
858
868
|
}
|
|
859
869
|
}
|
|
860
870
|
let orderby = `ORDER BY ${class_string}.system_order`;
|
|
871
|
+
let table_selection = `[class_${class_name}].*`;
|
|
872
|
+
if (slim) {
|
|
873
|
+
const label_prop_ids = (_d = (_c = class_data.metadata.label) === null || _c === void 0 ? void 0 : _c.properties) !== null && _d !== void 0 ? _d : [];
|
|
874
|
+
const label_props = class_data.properties.filter((p) => label_prop_ids.includes(p.id));
|
|
875
|
+
const label_prop_sql_string = label_props.map((p) => `[user_${p.name}]`).join(',');
|
|
876
|
+
table_selection = `system_id,system_order,${label_prop_sql_string}`;
|
|
877
|
+
}
|
|
861
878
|
let comma_break = `,
|
|
862
879
|
`;
|
|
863
880
|
let query = `
|
|
864
881
|
${cte_strings.length > 0 ? "WITH " + cte_strings.join(comma_break) : ''}
|
|
865
|
-
SELECT
|
|
882
|
+
SELECT ${table_selection} ${relation_selections.length > 0 ? ', ' + relation_selections.join(`, `) : ''}
|
|
866
883
|
FROM [class_${class_name}]
|
|
867
884
|
${cte_joins.join(' ')}
|
|
868
885
|
${orderby}`;
|
|
@@ -879,19 +896,31 @@ export default class Project {
|
|
|
879
896
|
}
|
|
880
897
|
}
|
|
881
898
|
});
|
|
882
|
-
return items;
|
|
899
|
+
return Object.assign(Object.assign({}, pagination), { loaded: items });
|
|
883
900
|
}
|
|
884
|
-
|
|
901
|
+
// include:{
|
|
902
|
+
// class_id:number;
|
|
903
|
+
// pagination:ItemPagination
|
|
904
|
+
// }[] = []
|
|
905
|
+
// MARKER: modify item retrieval
|
|
906
|
+
retrieve_all_classes(include = {}) {
|
|
885
907
|
const classes_data = this.run.get_all_classes.all();
|
|
886
908
|
return classes_data.map(({ id, name, metadata }) => {
|
|
887
|
-
var _a;
|
|
888
|
-
let existing_items = this.item_cache.find((itemlist) => itemlist.class_id == id);
|
|
909
|
+
var _a, _b, _c;
|
|
889
910
|
let properties_sql = this.db.prepare(`SELECT * FROM class_${id}_properties`).all() || [];
|
|
890
911
|
let properties = properties_sql.map((sql_prop) => this.parse_sql_prop(id, sql_prop));
|
|
912
|
+
const pagination = (_a = include.all_items) !== null && _a !== void 0 ? _a : (_c = (_b = include.items_by_class) === null || _b === void 0 ? void 0 : _b.find(((a) => a.class_id == id))) === null || _c === void 0 ? void 0 : _c.pagination;
|
|
913
|
+
const items = pagination ? this.retrieve_class_items({
|
|
914
|
+
class_id: id,
|
|
915
|
+
class_name: name,
|
|
916
|
+
pagination
|
|
917
|
+
}) : {
|
|
918
|
+
loaded: []
|
|
919
|
+
};
|
|
891
920
|
return {
|
|
892
921
|
id,
|
|
893
922
|
name,
|
|
894
|
-
items
|
|
923
|
+
items,
|
|
895
924
|
properties,
|
|
896
925
|
metadata: JSON.parse(metadata)
|
|
897
926
|
};
|
|
@@ -948,9 +977,15 @@ export default class Project {
|
|
|
948
977
|
ON system_root.id = workspace_${id}.thing_id
|
|
949
978
|
WHERE workspace_${id}.type = 'item';
|
|
950
979
|
`).all();
|
|
980
|
+
// MARKER: modify item retrieval
|
|
951
981
|
// get any relevant classes
|
|
952
|
-
const
|
|
953
|
-
|
|
982
|
+
const items_by_class = blocks.filter((b) => b.type == 'class').map((b) => ({
|
|
983
|
+
class_id: b.thing_id,
|
|
984
|
+
pagination: {
|
|
985
|
+
page_size: null
|
|
986
|
+
}
|
|
987
|
+
}));
|
|
988
|
+
const classes = this.retrieve_all_classes({ items_by_class });
|
|
954
989
|
return {
|
|
955
990
|
blocks,
|
|
956
991
|
items,
|
package/dist/types.d.ts
CHANGED
|
@@ -96,6 +96,9 @@ export type ClassMetadata = {
|
|
|
96
96
|
style: {
|
|
97
97
|
color?: string;
|
|
98
98
|
};
|
|
99
|
+
label?: {
|
|
100
|
+
properties: number[];
|
|
101
|
+
};
|
|
99
102
|
};
|
|
100
103
|
export type PropertyType = 'data' | 'relation';
|
|
101
104
|
export type BinaryBoolean = 0 | 1;
|
|
@@ -132,10 +135,18 @@ export type ClassData = {
|
|
|
132
135
|
id: number;
|
|
133
136
|
name: string;
|
|
134
137
|
metadata: ClassMetadata;
|
|
135
|
-
items:
|
|
138
|
+
items: PaginatedItems;
|
|
136
139
|
properties: Property[];
|
|
137
140
|
};
|
|
138
141
|
export type ClassList = ClassData[];
|
|
142
|
+
export type ItemPagination = {
|
|
143
|
+
page_size?: number | null;
|
|
144
|
+
page_range?: [start: number, end?: number];
|
|
145
|
+
property_range?: number[] | 'slim' | 'all';
|
|
146
|
+
};
|
|
147
|
+
export type PaginatedItems = ItemPagination & {
|
|
148
|
+
loaded: ClassRow[];
|
|
149
|
+
};
|
|
139
150
|
export type JunctionSides = [RelationshipSide, RelationshipSide];
|
|
140
151
|
export type JunctionTable = {
|
|
141
152
|
id: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "goby-database",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.20",
|
|
4
4
|
"description": "This will hold the core better-sqlite3-powered application for creating and modifying goby databases",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"files": [
|
|
@@ -10,10 +10,7 @@
|
|
|
10
10
|
"!dist/utils.js.map",
|
|
11
11
|
"!dist/sandbox.js",
|
|
12
12
|
"!dist/sandbox.d.ts",
|
|
13
|
-
"!dist/sandbox.js.map"
|
|
14
|
-
"!dist/unit-tests.js",
|
|
15
|
-
"!dist/unit-tests.d.ts",
|
|
16
|
-
"!dist/unit-tests.js.map"
|
|
13
|
+
"!dist/sandbox.js.map"
|
|
17
14
|
],
|
|
18
15
|
"types": "dist/index.d.ts",
|
|
19
16
|
"scripts": {
|