goby-database 2.2.29 → 2.2.30
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.js +54 -23
- package/dist/types.d.ts +5 -0
- package/package.json +1 -1
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'),
|
|
@@ -803,27 +804,29 @@ export default class Project {
|
|
|
803
804
|
}
|
|
804
805
|
// MARKER: modify item retrieval
|
|
805
806
|
retrieve_class_items({ class_id, class_name, class_data, pagination = {} }) {
|
|
807
|
+
// 1. DETERMINE COLUMNS AND ROWS TO RETRIEVE ---------------------------------------------------
|
|
806
808
|
var _a, _b, _c, _d, _e;
|
|
807
809
|
const pagination_defaults = {
|
|
808
810
|
page_size: null,
|
|
809
811
|
property_range: 'all',
|
|
810
|
-
item_range: 'all'
|
|
812
|
+
item_range: 'all',
|
|
813
|
+
conditions: []
|
|
811
814
|
};
|
|
815
|
+
// set pagination rules by overriding defaults with any custom settings
|
|
812
816
|
pagination = Object.assign(Object.assign({}, pagination_defaults), pagination);
|
|
817
|
+
// get class data+name if not already passed in
|
|
813
818
|
if (class_name == undefined || class_data == undefined) {
|
|
814
819
|
class_data = this.lookup_class(class_id);
|
|
815
820
|
class_name = class_data.name;
|
|
816
821
|
}
|
|
817
822
|
;
|
|
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 = [];
|
|
823
|
+
// gets the label prop for this class
|
|
825
824
|
const label_prop_ids = (_b = (_a = class_data.metadata.label) === null || _a === void 0 ? void 0 : _a.properties) !== null && _b !== void 0 ? _b : [];
|
|
826
|
-
|
|
825
|
+
const where_conditions = [];
|
|
826
|
+
if (pagination.item_range && pagination.item_range !== 'all') {
|
|
827
|
+
where_conditions.push(`system_id in (${pagination.item_range.join(',')})`);
|
|
828
|
+
}
|
|
829
|
+
// if a property_range is defined, first filter properties retrieved by those IDs
|
|
827
830
|
const retrieved_properties = class_data.properties.filter((prop) => {
|
|
828
831
|
if (pagination.property_range == 'all' || !pagination.property_range) {
|
|
829
832
|
return true;
|
|
@@ -838,22 +841,51 @@ export default class Project {
|
|
|
838
841
|
return true;
|
|
839
842
|
}
|
|
840
843
|
});
|
|
844
|
+
const cte_properties = [];
|
|
845
|
+
for (let condition of (pagination.conditions || [])) {
|
|
846
|
+
if (condition.name == 'under_property_max') {
|
|
847
|
+
const property = class_data.properties.find((p) => p.id == condition.property_id);
|
|
848
|
+
if ((property === null || property === void 0 ? void 0 : property.type) == 'relation' && property.max_values !== null) {
|
|
849
|
+
if (!cte_properties.some((p) => p.id == condition.property_id) && !retrieved_properties.some((p) => p.id == condition.property_id)) {
|
|
850
|
+
// 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
|
|
851
|
+
cte_properties.push(Object.assign(Object.assign({}, property), { cte_only: true }));
|
|
852
|
+
}
|
|
853
|
+
// add a condition that counts the items selected by this item for this property
|
|
854
|
+
where_conditions.push(`COALESCE([count_user_${property.name}],0) < ${property.max_values}`);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
// separates these, since they are handled separately in the query
|
|
841
859
|
const relation_properties = retrieved_properties.filter(a => a.type == 'relation');
|
|
842
860
|
const data_properties = retrieved_properties.filter(a => a.type == 'data');
|
|
843
|
-
|
|
861
|
+
// 2. GENERATE SQLITE QUERY --------------------------------------------------
|
|
862
|
+
// class table name in db
|
|
863
|
+
const class_string = `[class_${class_name}]`;
|
|
864
|
+
// 2a. Handle relation properties by generating common table expressions (ctes)
|
|
865
|
+
// joined+added at beginning of the query, built from relations
|
|
866
|
+
const cte_strings = [];
|
|
867
|
+
// joined+added near the end of the query, built from relations
|
|
868
|
+
const cte_joins = [];
|
|
869
|
+
// joined+added between SELECT and FROM, built from relations
|
|
870
|
+
const relation_selections = [];
|
|
871
|
+
for (let prop of [...relation_properties, ...cte_properties]) {
|
|
844
872
|
const target_selects = [];
|
|
845
|
-
|
|
873
|
+
// name of column for this class/property in junction table
|
|
874
|
+
const property_junction_column_name = junction_col_name(class_id, prop.id);
|
|
875
|
+
// loop through each target
|
|
846
876
|
if (prop.relation_targets.length > 0) {
|
|
847
877
|
for (let i = 0; i < prop.relation_targets.length; i++) {
|
|
848
878
|
// find the side that does not match both the class and prop IDs
|
|
849
|
-
|
|
879
|
+
const target = prop.relation_targets[i];
|
|
850
880
|
const target_class = this.class_cache.find((a) => a.id == (target === null || target === void 0 ? void 0 : target.class_id));
|
|
851
881
|
if (target && target_class) {
|
|
882
|
+
// target column name in junction table
|
|
852
883
|
let target_junction_column_name = junction_col_name(target.class_id, target.prop_id);
|
|
853
|
-
//
|
|
884
|
+
// get label of label property in target prop
|
|
854
885
|
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
886
|
const target_label = target_class === null || target_class === void 0 ? void 0 : target_class.properties.find((p) => p.id == target_label_id);
|
|
856
887
|
const label_sql_string = target_label ? `,'user_${target_label.name}',target_class."user_${target_label.name}"` : '';
|
|
888
|
+
// NOTE: as mentioned elsewhere, possibly allow multiple label props
|
|
857
889
|
let junction_id = target.junction_id;
|
|
858
890
|
let target_select = `
|
|
859
891
|
SELECT
|
|
@@ -870,7 +902,7 @@ export default class Project {
|
|
|
870
902
|
}
|
|
871
903
|
// uses built-in aggregate json function instead of group_concat craziness
|
|
872
904
|
const cte = `[${prop.id}_cte] AS (
|
|
873
|
-
SELECT "${property_junction_column_name}", json_group_array( json(target_data) ) AS [user_${prop.name}]
|
|
905
|
+
SELECT "${property_junction_column_name}", json_group_array( json(target_data) ) AS [user_${prop.name}], COUNT(1) AS [count_user_${prop.name}]
|
|
874
906
|
FROM
|
|
875
907
|
(
|
|
876
908
|
${target_selects.join(`
|
|
@@ -882,28 +914,27 @@ export default class Project {
|
|
|
882
914
|
|
|
883
915
|
)`;
|
|
884
916
|
cte_strings.push(cte);
|
|
885
|
-
|
|
917
|
+
if (!("cte_only" in prop && prop.cte_only))
|
|
918
|
+
relation_selections.push(`[${prop.id}_cte].[user_${prop.name}]`);
|
|
886
919
|
cte_joins.push(`LEFT JOIN [${prop.id}_cte] ON [${prop.id}_cte]."${property_junction_column_name}" = ${class_string}.system_id`);
|
|
887
920
|
}
|
|
888
921
|
else {
|
|
889
|
-
|
|
922
|
+
if (!("cte_only" in prop && prop.cte_only))
|
|
923
|
+
relation_selections.push(`'[]' AS [user_${prop.name}]`);
|
|
890
924
|
}
|
|
891
925
|
}
|
|
892
926
|
let orderby = `ORDER BY ${class_string}.system_order`;
|
|
893
927
|
const data_prop_sql_string = data_properties.length > 0 ? ', ' + data_properties.map((p) => `[user_${p.name}]`).join(',') : '';
|
|
894
928
|
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 = `,
|
|
929
|
+
const where_string = where_conditions.length > 0 ? `WHERE ${where_conditions.map((w) => `(${w})`).join(' AND ')}` : '';
|
|
930
|
+
const comma_break = `,
|
|
900
931
|
`;
|
|
901
932
|
let query = `
|
|
902
933
|
${cte_strings.length > 0 ? "WITH " + cte_strings.join(comma_break) : ''}
|
|
903
934
|
SELECT ${table_selection} ${relation_selections.length > 0 ? ', ' + relation_selections.join(`, `) : ''}
|
|
904
935
|
FROM [class_${class_name}]
|
|
905
936
|
${cte_joins.join(' ')}
|
|
906
|
-
${
|
|
937
|
+
${where_string}
|
|
907
938
|
${orderby}`;
|
|
908
939
|
// possibly elaborate this any type a little more in the future, e.g. a CellValue or SQLCellValue type that expects some wildcards
|
|
909
940
|
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[];
|