goby-database 2.2.29 → 2.2.31
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 +5 -1
- package/dist/index.js +136 -47
- package/dist/types.d.ts +5 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -97,7 +97,11 @@ export default class Project {
|
|
|
97
97
|
*/
|
|
98
98
|
action_edit_relations(relations: {
|
|
99
99
|
change: 'add' | 'remove';
|
|
100
|
-
sides: [input_1: ItemRelationSide
|
|
100
|
+
sides: [input_1: ItemRelationSide & {
|
|
101
|
+
order?: number;
|
|
102
|
+
}, input_2: ItemRelationSide & {
|
|
103
|
+
order?: number;
|
|
104
|
+
}];
|
|
101
105
|
}[]): void;
|
|
102
106
|
retrieve_class_items({ class_id, class_name, class_data, pagination }: {
|
|
103
107
|
class_id: number;
|
package/dist/index.js
CHANGED
|
@@ -14,7 +14,8 @@ export default class Project {
|
|
|
14
14
|
else {
|
|
15
15
|
console.log('opened goby database');
|
|
16
16
|
}
|
|
17
|
-
//prepared statements with arguments so my code isn't as verbose elsewhere
|
|
17
|
+
// prepared statements with arguments so my code isn't as verbose elsewhere
|
|
18
|
+
// obviously can only do this for actions on determinate columns
|
|
18
19
|
this.run = {
|
|
19
20
|
begin: this.db.prepare('BEGIN IMMEDIATE'),
|
|
20
21
|
commit: this.db.prepare('COMMIT'),
|
|
@@ -598,12 +599,20 @@ export default class Project {
|
|
|
598
599
|
let id = (_a = this.db.prepare('SELECT id FROM system_junctionlist ORDER BY id DESC').get()) === null || _a === void 0 ? void 0 : _a.id;
|
|
599
600
|
if (typeof id !== 'number')
|
|
600
601
|
throw new Error('Something went wrong creating a new relationship');
|
|
602
|
+
const side_0_col = junction_col_name(sides[0].class_id, sides[0].prop_id);
|
|
603
|
+
const side_1_col = junction_col_name(sides[1].class_id, sides[1].prop_id);
|
|
604
|
+
const columns = [
|
|
605
|
+
`"${side_0_col}" INTEGER`,
|
|
606
|
+
`"${side_1_col}" INTEGER`,
|
|
607
|
+
];
|
|
608
|
+
// for each side, check if it has a prop
|
|
609
|
+
// if it does, add column for its order
|
|
610
|
+
if (defined(sides[0].prop_id))
|
|
611
|
+
columns.push(`"${side_0_col}_order" REAL`);
|
|
612
|
+
if (defined(sides[1].prop_id))
|
|
613
|
+
columns.push(`"${side_1_col}_order" REAL`);
|
|
601
614
|
// creates table
|
|
602
|
-
this.create_table('junction', id,
|
|
603
|
-
`"${junction_col_name(sides[0].class_id, sides[0].prop_id)}" INTEGER`,
|
|
604
|
-
`"${junction_col_name(sides[1].class_id, sides[1].prop_id)}" INTEGER`,
|
|
605
|
-
`date_added INTEGER`
|
|
606
|
-
]);
|
|
615
|
+
this.create_table('junction', id, columns);
|
|
607
616
|
return id;
|
|
608
617
|
}
|
|
609
618
|
transfer_connections(source, target) {
|
|
@@ -779,51 +788,98 @@ export default class Project {
|
|
|
779
788
|
input_2: junction_col_name(input_2.class_id, input_2.prop_id)
|
|
780
789
|
};
|
|
781
790
|
const junction_id = (_a = this.junction_cache.find(j => full_relation_match(j.sides, [input_1, input_2]))) === null || _a === void 0 ? void 0 : _a.id;
|
|
782
|
-
if (junction_id) {
|
|
783
|
-
if (change == 'add') {
|
|
784
|
-
const date_added = Date.now();
|
|
785
|
-
this.db.prepare(`
|
|
786
|
-
INSERT INTO junction_${junction_id}
|
|
787
|
-
("${column_names.input_1}", "${column_names.input_2}",date_added)
|
|
788
|
-
VALUES (${input_1.item_id},${input_2.item_id},${date_added})
|
|
789
|
-
`).run();
|
|
790
|
-
}
|
|
791
|
-
else if (change == 'remove') {
|
|
792
|
-
this.db.prepare(`
|
|
793
|
-
DELETE FROM junction_${junction_id}
|
|
794
|
-
WHERE "${column_names.input_1}" = ${input_1.item_id}
|
|
795
|
-
AND "${column_names.input_2}" = ${input_2.item_id}`).run();
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
else {
|
|
791
|
+
if (!defined(junction_id)) {
|
|
799
792
|
throw Error('Something went wrong - junction table for relationship not found');
|
|
800
793
|
}
|
|
794
|
+
if (change == 'add') {
|
|
795
|
+
const column_value_set = [
|
|
796
|
+
{
|
|
797
|
+
column: `"${column_names.input_1}"`,
|
|
798
|
+
value: input_1.item_id
|
|
799
|
+
},
|
|
800
|
+
{
|
|
801
|
+
column: `"${column_names.input_2}"`,
|
|
802
|
+
value: input_2.item_id
|
|
803
|
+
}
|
|
804
|
+
];
|
|
805
|
+
sides.forEach((side, s) => {
|
|
806
|
+
var _a, _b, _c;
|
|
807
|
+
// for each side, check if it has a prop id
|
|
808
|
+
if (defined(side.prop_id)) {
|
|
809
|
+
// if it does, prepare to add a column entry for it
|
|
810
|
+
const column = `"${s == 0 ? column_names.input_1 : column_names.input_2}_order"`;
|
|
811
|
+
let value = 0;
|
|
812
|
+
// check if it has an order set
|
|
813
|
+
if (defined(side.order)) {
|
|
814
|
+
// if so, use that for the column entry
|
|
815
|
+
value = side.order;
|
|
816
|
+
}
|
|
817
|
+
else {
|
|
818
|
+
// if not, you have to do a lookup of that property value for that item, and get the last order value
|
|
819
|
+
const class_data = this.lookup_class(side.class_id);
|
|
820
|
+
const item = (_a = this.retrieve_class_items({
|
|
821
|
+
class_id: side.class_id,
|
|
822
|
+
class_data,
|
|
823
|
+
pagination: {
|
|
824
|
+
item_range: [side.item_id],
|
|
825
|
+
property_range: [side.prop_id]
|
|
826
|
+
}
|
|
827
|
+
}).loaded) === null || _a === void 0 ? void 0 : _a[0];
|
|
828
|
+
const prop_name = (_b = class_data.properties.find((p) => p.id == side.prop_id)) === null || _b === void 0 ? void 0 : _b.name;
|
|
829
|
+
const prop_selections = defined(prop_name) && item ? item[`user_${prop_name}`] : [];
|
|
830
|
+
if (Array.isArray(prop_selections)) {
|
|
831
|
+
const last_item_order = (_c = prop_selections === null || prop_selections === void 0 ? void 0 : prop_selections.at(-1)) === null || _c === void 0 ? void 0 : _c.system_order;
|
|
832
|
+
if (typeof last_item_order == 'number') {
|
|
833
|
+
value = last_item_order + 1000;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
column_value_set.push({
|
|
838
|
+
column,
|
|
839
|
+
value
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
});
|
|
843
|
+
this.db.prepare(`
|
|
844
|
+
INSERT INTO junction_${junction_id}
|
|
845
|
+
(${column_value_set.map((a) => a.column).join(',')})
|
|
846
|
+
VALUES (${column_value_set.map((a) => a.value).join(',')})
|
|
847
|
+
`).run();
|
|
848
|
+
}
|
|
849
|
+
else if (change == 'remove') {
|
|
850
|
+
this.db.prepare(`
|
|
851
|
+
DELETE FROM junction_${junction_id}
|
|
852
|
+
WHERE "${column_names.input_1}" = ${input_1.item_id}
|
|
853
|
+
AND "${column_names.input_2}" = ${input_2.item_id}`).run();
|
|
854
|
+
}
|
|
801
855
|
}
|
|
802
856
|
// NOTE: should this trigger a refresh to items?
|
|
803
857
|
}
|
|
804
858
|
// MARKER: modify item retrieval
|
|
805
859
|
retrieve_class_items({ class_id, class_name, class_data, pagination = {} }) {
|
|
860
|
+
// 1. DETERMINE COLUMNS AND ROWS TO RETRIEVE ---------------------------------------------------
|
|
806
861
|
var _a, _b, _c, _d, _e;
|
|
807
862
|
const pagination_defaults = {
|
|
808
863
|
page_size: null,
|
|
809
864
|
property_range: 'all',
|
|
810
|
-
item_range: 'all'
|
|
865
|
+
item_range: 'all',
|
|
866
|
+
conditions: []
|
|
811
867
|
};
|
|
868
|
+
// set pagination rules by overriding defaults with any custom settings
|
|
812
869
|
pagination = Object.assign(Object.assign({}, pagination_defaults), pagination);
|
|
870
|
+
// get class data+name if not already passed in
|
|
813
871
|
if (class_name == undefined || class_data == undefined) {
|
|
814
872
|
class_data = this.lookup_class(class_id);
|
|
815
873
|
class_name = class_data.name;
|
|
816
874
|
}
|
|
817
875
|
;
|
|
818
|
-
|
|
819
|
-
// joined+added at beginning of the query, built from relations
|
|
820
|
-
const cte_strings = [];
|
|
821
|
-
// joined+added near the end of the query, built from relations
|
|
822
|
-
const cte_joins = [];
|
|
823
|
-
// joined+added between SELECT and FROM, built from relations
|
|
824
|
-
const relation_selections = [];
|
|
876
|
+
// gets the label prop for this class
|
|
825
877
|
const label_prop_ids = (_b = (_a = class_data.metadata.label) === null || _a === void 0 ? void 0 : _a.properties) !== null && _b !== void 0 ? _b : [];
|
|
826
|
-
|
|
878
|
+
const where_conditions = [];
|
|
879
|
+
if (pagination.item_range && pagination.item_range !== 'all') {
|
|
880
|
+
where_conditions.push(`system_id in (${pagination.item_range.join(',')})`);
|
|
881
|
+
}
|
|
882
|
+
// if a property_range is defined, first filter properties retrieved by those IDs
|
|
827
883
|
const retrieved_properties = class_data.properties.filter((prop) => {
|
|
828
884
|
if (pagination.property_range == 'all' || !pagination.property_range) {
|
|
829
885
|
return true;
|
|
@@ -838,27 +894,61 @@ export default class Project {
|
|
|
838
894
|
return true;
|
|
839
895
|
}
|
|
840
896
|
});
|
|
897
|
+
const cte_properties = [];
|
|
898
|
+
for (let condition of (pagination.conditions || [])) {
|
|
899
|
+
if (condition.name == 'under_property_max') {
|
|
900
|
+
const property = class_data.properties.find((p) => p.id == condition.property_id);
|
|
901
|
+
if ((property === null || property === void 0 ? void 0 : property.type) == 'relation' && property.max_values !== null) {
|
|
902
|
+
if (!cte_properties.some((p) => p.id == condition.property_id) && !retrieved_properties.some((p) => p.id == condition.property_id)) {
|
|
903
|
+
// if this property isn’t retrieved directly, we have to make sure a CTE is created for it, just so we can count the values
|
|
904
|
+
cte_properties.push(Object.assign(Object.assign({}, property), { cte_only: true }));
|
|
905
|
+
}
|
|
906
|
+
// add a condition that counts the items selected by this item for this property
|
|
907
|
+
where_conditions.push(`COALESCE([count_user_${property.name}],0) < ${property.max_values}`);
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
// separates these, since they are handled separately in the query
|
|
841
912
|
const relation_properties = retrieved_properties.filter(a => a.type == 'relation');
|
|
842
913
|
const data_properties = retrieved_properties.filter(a => a.type == 'data');
|
|
843
|
-
|
|
914
|
+
// 2. GENERATE SQLITE QUERY --------------------------------------------------
|
|
915
|
+
// class table name in db
|
|
916
|
+
const class_string = `[class_${class_name}]`;
|
|
917
|
+
// 2a. Handle relation properties by generating common table expressions (ctes)
|
|
918
|
+
// joined+added at beginning of the query, built from relations
|
|
919
|
+
const cte_strings = [];
|
|
920
|
+
// joined+added near the end of the query, built from relations
|
|
921
|
+
const cte_joins = [];
|
|
922
|
+
// joined+added between SELECT and FROM, built from relations
|
|
923
|
+
const relation_selections = [];
|
|
924
|
+
for (let prop of [...relation_properties, ...cte_properties]) {
|
|
844
925
|
const target_selects = [];
|
|
845
|
-
|
|
926
|
+
// name of column for this class/property in junction table
|
|
927
|
+
const property_junction_column_name = junction_col_name(class_id, prop.id);
|
|
928
|
+
// loop through each target
|
|
846
929
|
if (prop.relation_targets.length > 0) {
|
|
847
930
|
for (let i = 0; i < prop.relation_targets.length; i++) {
|
|
848
931
|
// find the side that does not match both the class and prop IDs
|
|
849
|
-
|
|
932
|
+
const target = prop.relation_targets[i];
|
|
850
933
|
const target_class = this.class_cache.find((a) => a.id == (target === null || target === void 0 ? void 0 : target.class_id));
|
|
851
934
|
if (target && target_class) {
|
|
935
|
+
// target column name in junction table
|
|
852
936
|
let target_junction_column_name = junction_col_name(target.class_id, target.prop_id);
|
|
853
|
-
//
|
|
937
|
+
// get label of label property in target prop
|
|
854
938
|
const target_label_id = (_d = (_c = target_class === null || target_class === void 0 ? void 0 : target_class.metadata) === null || _c === void 0 ? void 0 : _c.label) === null || _d === void 0 ? void 0 : _d.properties[0];
|
|
855
939
|
const target_label = target_class === null || target_class === void 0 ? void 0 : target_class.properties.find((p) => p.id == target_label_id);
|
|
856
940
|
const label_sql_string = target_label ? `,'user_${target_label.name}',target_class."user_${target_label.name}"` : '';
|
|
941
|
+
// NOTE: as mentioned elsewhere, possibly allow multiple label props
|
|
857
942
|
let junction_id = target.junction_id;
|
|
858
943
|
let target_select = `
|
|
859
944
|
SELECT
|
|
860
945
|
"${property_junction_column_name}",
|
|
861
|
-
json_object(
|
|
946
|
+
json_object(
|
|
947
|
+
'class_id',${target.class_id},
|
|
948
|
+
'system_id',junction."${target_junction_column_name}",
|
|
949
|
+
'system_order',junction."${property_junction_column_name}_order"
|
|
950
|
+
${label_sql_string}
|
|
951
|
+
) AS target_data, junction."${property_junction_column_name}_order" AS relation_order
|
|
862
952
|
FROM junction_${junction_id} AS junction
|
|
863
953
|
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
|
|
864
954
|
`;
|
|
@@ -870,40 +960,39 @@ export default class Project {
|
|
|
870
960
|
}
|
|
871
961
|
// uses built-in aggregate json function instead of group_concat craziness
|
|
872
962
|
const cte = `[${prop.id}_cte] AS (
|
|
873
|
-
SELECT "${property_junction_column_name}", json_group_array( json(target_data) ) AS [user_${prop.name}]
|
|
963
|
+
SELECT "${property_junction_column_name}", json_group_array( json(target_data) ) AS [user_${prop.name}], COUNT(1) AS [count_user_${prop.name}]
|
|
874
964
|
FROM
|
|
875
965
|
(
|
|
876
966
|
${target_selects.join(`
|
|
877
967
|
UNION
|
|
878
968
|
`)}
|
|
879
|
-
ORDER BY
|
|
969
|
+
ORDER BY relation_order
|
|
880
970
|
)
|
|
881
971
|
GROUP BY "${property_junction_column_name}"
|
|
882
972
|
|
|
883
973
|
)`;
|
|
884
974
|
cte_strings.push(cte);
|
|
885
|
-
|
|
975
|
+
if (!("cte_only" in prop && prop.cte_only))
|
|
976
|
+
relation_selections.push(`[${prop.id}_cte].[user_${prop.name}]`);
|
|
886
977
|
cte_joins.push(`LEFT JOIN [${prop.id}_cte] ON [${prop.id}_cte]."${property_junction_column_name}" = ${class_string}.system_id`);
|
|
887
978
|
}
|
|
888
979
|
else {
|
|
889
|
-
|
|
980
|
+
if (!("cte_only" in prop && prop.cte_only))
|
|
981
|
+
relation_selections.push(`'[]' AS [user_${prop.name}]`);
|
|
890
982
|
}
|
|
891
983
|
}
|
|
892
984
|
let orderby = `ORDER BY ${class_string}.system_order`;
|
|
893
985
|
const data_prop_sql_string = data_properties.length > 0 ? ', ' + data_properties.map((p) => `[user_${p.name}]`).join(',') : '';
|
|
894
986
|
const table_selection = pagination.property_range == 'all' ? `[class_${class_name}].*` : `system_id,system_order${data_prop_sql_string}`;
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
filter_by_items = `WHERE system_id in (${pagination.item_range.join(',')})`;
|
|
898
|
-
}
|
|
899
|
-
let comma_break = `,
|
|
987
|
+
const where_string = where_conditions.length > 0 ? `WHERE ${where_conditions.map((w) => `(${w})`).join(' AND ')}` : '';
|
|
988
|
+
const comma_break = `,
|
|
900
989
|
`;
|
|
901
990
|
let query = `
|
|
902
991
|
${cte_strings.length > 0 ? "WITH " + cte_strings.join(comma_break) : ''}
|
|
903
992
|
SELECT ${table_selection} ${relation_selections.length > 0 ? ', ' + relation_selections.join(`, `) : ''}
|
|
904
993
|
FROM [class_${class_name}]
|
|
905
994
|
${cte_joins.join(' ')}
|
|
906
|
-
${
|
|
995
|
+
${where_string}
|
|
907
996
|
${orderby}`;
|
|
908
997
|
// possibly elaborate this any type a little more in the future, e.g. a CellValue or SQLCellValue type that expects some wildcards
|
|
909
998
|
let items = this.db.prepare(query).all();
|
package/dist/types.d.ts
CHANGED
|
@@ -142,6 +142,10 @@ export type ClassData = {
|
|
|
142
142
|
properties: Property[];
|
|
143
143
|
};
|
|
144
144
|
export type ClassList = ClassData[];
|
|
145
|
+
type ConditionUnderPropertyMax = {
|
|
146
|
+
name: 'under_property_max';
|
|
147
|
+
property_id: number;
|
|
148
|
+
};
|
|
145
149
|
export type ItemPagination = {
|
|
146
150
|
page_size?: number | null;
|
|
147
151
|
page_range?: [start: number, end?: number];
|
|
@@ -149,6 +153,7 @@ export type ItemPagination = {
|
|
|
149
153
|
property_range?: number[] | 'slim' | 'all';
|
|
150
154
|
/** Filter by item IDs. If not specified, pulls all */
|
|
151
155
|
item_range?: number[] | 'all';
|
|
156
|
+
conditions?: ConditionUnderPropertyMax[];
|
|
152
157
|
};
|
|
153
158
|
export type PaginatedItems = ItemPagination & {
|
|
154
159
|
loaded: ClassRow[];
|