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 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, ClassRow, ClassEdit, RelationEdit, PropertyEdit, MaxValues, PropertyType, RelationProperty, DataProperty, RelationEditValidSides } from './types.js';
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
- }): ClassRow[];
89
- retrieve_all_classes(): ClassData[];
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', 'items', 'junctions']);
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
- retrieve_class_items({ class_id, class_name, class_data }) {
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
- // //joined+added at beginning of the query, built from relations
813
+ // joined+added at beginning of the query, built from relations
818
814
  const cte_strings = [];
819
- // //joined+added near the end of the query, built from relations
815
+ // joined+added near the end of the query, built from relations
820
816
  const cte_joins = [];
821
- // //joined+added between SELECT and FROM, built from relations
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
- for (let prop of relation_properties) {
825
- const target_selects = [];
826
- let property_junction_column_name = junction_col_name(class_id, prop.id);
827
- if (prop.relation_targets.length > 0) {
828
- for (let i = 0; i < prop.relation_targets.length; i++) {
829
- // find the side that does not match both the class and prop IDs
830
- let target = prop.relation_targets[i];
831
- if (target) {
832
- let target_junction_column_name = junction_col_name(target.class_id, target.prop_id);
833
- let junction_id = target.junction_id;
834
- let target_select = `SELECT "${property_junction_column_name}", json_object('class_id',${target.class_id},'id',"${target_junction_column_name}") AS target_data FROM junction_${junction_id}`;
835
- target_selects.push(target_select);
836
- }
837
- else {
838
- throw Error('Something went wrong trying to retrieve relationship data');
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 [class_${class_name}].* ${relation_selections.length > 0 ? ', ' + relation_selections.join(`, `) : ''}
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
- retrieve_all_classes() {
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: (_a = existing_items === null || existing_items === void 0 ? void 0 : existing_items.items) !== null && _a !== void 0 ? _a : [],
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 classes = this.class_cache.filter((cls) => blocks.some((block) => block.type == 'class' && block.thing_id == cls.id));
953
- // NOTE: could possibly add class items as well in the future
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: ClassRow[];
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.18",
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": {