goby-database 1.0.0

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.
Files changed (3) hide show
  1. package/index.js +674 -0
  2. package/package.json +14 -0
  3. package/readme.md +7 -0
package/index.js ADDED
@@ -0,0 +1,674 @@
1
+ // import Database from 'better-sqlite3';
2
+ const Database = require('better-sqlite3');
3
+
4
+ class Project{
5
+ constructor(source){
6
+ this.db= new Database(source);
7
+
8
+ this.text_datatypes=['string','resource'];
9
+ this.real_datatypes=['number'];
10
+
11
+
12
+
13
+ //checks if goby has been initialized, initializes if not
14
+ const goby_init=this.db.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='system_root'`).get();
15
+ if(!goby_init){
16
+ console.log('initializing goby database');
17
+ this.init();
18
+ }else{
19
+ console.log('opened goby database');
20
+ }
21
+
22
+ //prepared statements with arguments so my code isn't as verbose elsewhere
23
+ this.run={
24
+ begin:this.db.prepare('BEGIN IMMEDIATE'),
25
+ commit:this.db.prepare('COMMIT'),
26
+ rollback:this.db.prepare('ROLLBACK'),
27
+ get_junctionlist:this.db.prepare('SELECT id, junction_obj(side_a, side_b) AS sides, metadata FROM system_junctionlist'),
28
+ get_class:this.db.prepare(`SELECT name, metadata FROM system_classlist WHERE id = ?`),
29
+ get_class_id:this.db.prepare(`SELECT id FROM system_classlist WHERE name = ?`),
30
+ get_all_classes:this.db.prepare(`SELECT id, name, metadata FROM system_classlist`),
31
+ save_class_meta:this.db.prepare(`UPDATE system_classlist set metadata = ? WHERE id = ?`),
32
+ match_junction:this.db.prepare(`SELECT id, side_a, side_b, metadata FROM system_junctionlist WHERE (side_a = @input_1 AND side_b = @input_2 ) OR ( side_a = @input_2 AND side_b = @input_1 )`),
33
+ fuzzy_match_junction:this.db.prepare(`SELECT id, side_a, side_b, metadata FROM system_junctionlist WHERE (side_a LIKE @input_1 AND side_b LIKE @input_2 ) OR ( side_a LIKE @input_2 AND side_b LIKE @input_1 )`)
34
+ }
35
+
36
+
37
+
38
+
39
+ this.class_cache={};
40
+ this.refresh_class_cache();
41
+ this.junction_cache=[];
42
+ this.refresh_junction_cache();
43
+ // this.junction_cache;
44
+
45
+ //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
46
+ //this enables a one-step undo.
47
+ // this.run.begin.run();
48
+
49
+ }
50
+
51
+
52
+
53
+ init(){
54
+
55
+
56
+ //System table to contain all objects in the project.
57
+ this.create_table('system','root',['id INTEGER NOT NULL PRIMARY KEY']);
58
+
59
+
60
+ //System table to contain metadata for all classes created by user
61
+ this.create_table('system','classlist',['id INTEGER NOT NULL PRIMARY KEY','name TEXT','metadata TEXT']);
62
+ //System table to contain all the junction tables and aggregate info about relations
63
+ this.create_table('system','junctionlist',['id INTEGER NOT NULL PRIMARY KEY','side_a TEXT','side_b TEXT','metadata TEXT']);
64
+ //System table to contain generated image data
65
+ this.create_table('system','images',['file_path TEXT','img_type TEXT','img BLOB']);
66
+
67
+ // [TO ADD: special junction table for root objects to reference themselves in individual relation]
68
+ this.create_table('system','junction_root',[
69
+ 'id_1 INTEGER',
70
+ 'id_2 INTEGER',
71
+ 'metadata TEXT'
72
+ ]);
73
+
74
+ this.db.function('junction_obj', (side_a, side_b) => {
75
+
76
+ let split_1=side_a.split('.');
77
+ let split_2=side_b.split('.');
78
+ let c1=`"class_id":${split_1[0]}`;
79
+ let p1=split_1[1]?`,"prop_id":${split_1[1]}`:'';
80
+ let c2=`"class_id":${split_2[0]}`;
81
+ let p2=split_2[1]?`,"prop_id":${split_2[1]}`:'';
82
+ return `[ {${c1}${p1}}, {${c2}${p2}} ]`;
83
+ });
84
+
85
+
86
+ }
87
+
88
+
89
+ refresh_class_cache(){
90
+ let class_array=this.run.get_all_classes.all();
91
+ let cache_obj={};
92
+ for(let cls of class_array){
93
+ cache_obj[cls.id]={
94
+ id:cls.id,
95
+ name:cls.name,
96
+ metadata:JSON.parse(cls.metadata)
97
+ };
98
+
99
+ }
100
+ this.class_cache=cache_obj;
101
+ }
102
+
103
+ refresh_junction_cache(){
104
+ let junction_list=this.run.get_junctionlist.all();
105
+ junction_list.map(a=>a.sides=JSON.parse(a.sides));
106
+ this.junction_cache=junction_list;
107
+ }
108
+
109
+
110
+
111
+ create_table(type,name,columns){
112
+ //type will pass in 'class', 'system', or 'junction' to use as a name prefix
113
+ //columns is an array of raw SQL column strings
114
+
115
+
116
+ let columns_string=columns.join(',');
117
+
118
+ //name in brackets to allow special characters
119
+ // validation test: what happens if there are brackets in the name?
120
+ const sqlname=type=='class'?`[class_${name}]`:`${type}_${name}`;
121
+ let create_statement=`CREATE TABLE ${sqlname}(
122
+ ${columns_string}
123
+ )`;
124
+ this.db.prepare(create_statement).run();
125
+
126
+
127
+ }
128
+
129
+ action_create_class(name,meta){
130
+ // if(this.db.inTransaction) this.run.commit.run();
131
+ // this.run.begin.run();
132
+
133
+ //a class starts with these basic columns
134
+ let columns=['system_id INTEGER UNIQUE',
135
+ 'system_order INTEGER','user_name TEXT',`FOREIGN KEY(system_id) REFERENCES system_root(id)`];
136
+
137
+ this.create_table('class',name,columns);
138
+
139
+ const table_meta={
140
+ properties:[
141
+ {
142
+ name:'name',
143
+ type:'data',
144
+ id:1,
145
+ datatype:'string',
146
+ conditions:{
147
+ max:1
148
+ },
149
+ style:{
150
+ display:true,
151
+ colwidth:4
152
+ }
153
+ }
154
+ ],
155
+ used_prop_ids:[1],
156
+ style:{
157
+ color:'#b5ffd5',
158
+ display:meta?.display || true,
159
+ position:meta?.position || [0,0]
160
+ }
161
+ };
162
+
163
+ this.db.prepare(`INSERT INTO system_classlist (name, metadata) VALUES ('${name}','${JSON.stringify(table_meta)}')`).run();
164
+ //get the id of newest value from system_classlist and return
165
+ const class_id=this.db.prepare('SELECT id FROM system_classlist ORDER BY id DESC').get().id;
166
+
167
+ this.refresh_class_cache();
168
+ this.refresh_junction_cache();
169
+
170
+ return class_id;
171
+
172
+
173
+ }
174
+
175
+
176
+ action_add_data_property(class_id,name,conditions,datatype,style){
177
+
178
+ let class_data=this.class_cache[class_id];
179
+ let class_name=class_data.name;
180
+ let class_meta=class_data.metadata;
181
+
182
+ let id=Math.max(...class_meta.used_prop_ids)+1;
183
+ class_meta.used_prop_ids.push(id);
184
+ //create JSON for storage in system_classlist
185
+ const prop_meta={ type:'data', name,conditions,style,datatype,id};
186
+
187
+ //add property to property list
188
+ class_meta.properties.push(prop_meta);
189
+
190
+ let sql_datatype='';
191
+
192
+ if(conditions.max>1||this.text_datatypes.includes(datatype)){
193
+ //multiple for data means a stringified array no matter what it is
194
+ sql_datatype='TEXT';
195
+ }else if(this.real_datatypes.includes(datatype)){
196
+ sql_datatype='REAL';
197
+ }
198
+ //create property in table
199
+ let command_string=`ALTER TABLE [class_${class_name}] ADD COLUMN [user_${name}] ${sql_datatype};`;
200
+ this.db.prepare(command_string).run();
201
+
202
+ //update metadata json for table with new property
203
+ this.run.save_class_meta.run(JSON.stringify(class_meta),class_id);
204
+ this.refresh_class_cache();
205
+
206
+
207
+ }
208
+
209
+ action_add_relation_property(class_id,name,conditions,style){
210
+ // basic property construction------------------
211
+ let class_meta=this.class_cache[class_id].metadata;
212
+
213
+ let id=Math.max(...class_meta.used_prop_ids)+1;
214
+ class_meta.used_prop_ids.push(id);
215
+ //create JSON for storage in system_classlist
216
+ const prop_meta={
217
+ type:'relation',
218
+ name,
219
+ style,
220
+ id,
221
+ targets:[],
222
+ conditions
223
+ }
224
+ //add property to property list
225
+ class_meta.properties.push(prop_meta);
226
+
227
+
228
+ this.run.save_class_meta.run(JSON.stringify(class_meta),class_id);
229
+ this.refresh_class_cache();
230
+
231
+ return id;
232
+
233
+ }
234
+
235
+
236
+ delete_property(class_id,prop_id){
237
+ //this function is meant to be used within a flow where the relations that need to change as a result of this deletion are already kept track of
238
+
239
+ let class_meta=this.class_cache[class_id].metadata;
240
+ let i=class_meta.properties.findIndex(a=>a.id==prop_id);
241
+ class_meta.properties.splice(i,1);
242
+ this.run.save_class_meta.run(JSON.stringify(class_meta),class_id.id);
243
+ this.refresh_class_cache();
244
+ }
245
+
246
+ get_junctions(){
247
+ let junction_list=this.run.get_junctionlist.all();
248
+ junction_list.map(a=>a.sides=JSON.parse(a.sides))
249
+ return junction_list;
250
+ }
251
+
252
+
253
+ action_edit_class_schema(edits){
254
+ // class_changes=[],new_junction_list
255
+ let class_changes=edits.class_changes || [];
256
+ let junction_list=edits.junction_list;
257
+
258
+ for(let change of class_changes){
259
+
260
+ if(change.action=='create'){
261
+ if(change.class_id==undefined){
262
+ change.class_id=this.action_create_class(change.class_name);
263
+ if(junction_list) junction_list.map(junction=>{
264
+ let matching=junction.sides.find(
265
+ side=>side.class_name==change.class_name);
266
+ if(matching) matching.class_id=change.class_id;
267
+ })
268
+ }
269
+
270
+ if(change.prop_name!==undefined){
271
+ //if a prop name is specified, that means
272
+ // this change involves a property
273
+ // that property needs to be created
274
+ if(change.type=='relation'){
275
+ change.prop_id=this.action_add_relation_property(change.class_id,change.prop_name,change.conditions,change.style);
276
+ junction_list.map(junction=>{
277
+ let matching=junction.sides.find(
278
+ side=>side.class_id==change.class_id&&side.prop_name==change.prop_name);
279
+ if(matching) matching.prop_id=change.prop_id;
280
+ })
281
+ }else if(change.type=='data'){
282
+ this.action_add_data_property(change.class_id,change.prop_name,change.conditions,change.datatype,change.style);
283
+ }
284
+
285
+ }
286
+ }else if(change.action=='delete'){
287
+ if(change.prop_id!==undefined){
288
+ this.delete_property(change.class_id,change.prop_id);
289
+ }else{
290
+ // delete class
291
+ }
292
+
293
+ //maybe it's worthwhile and safe to just check the junction list for any relations that include this property, and remove them/convert them if necessary
294
+ }
295
+
296
+ }
297
+
298
+
299
+ if(junction_list!==undefined) this.action_update_relations(junction_list)
300
+ //in the middle of adding update_relations to this generalized funciton
301
+
302
+ }
303
+
304
+
305
+ action_update_relations(junction_list){
306
+
307
+ let classes_meta=this.class_cache;
308
+
309
+
310
+ // STEP 1 ============================
311
+ // find all the old junctions which don't appear in the new list
312
+ // add them to a "delete_queue" to schedule them for deletion
313
+ // remove the corresponding targets from the properties they reference
314
+
315
+ let delete_queue=[];
316
+ let old_junction_list=this.get_junctions();
317
+
318
+
319
+
320
+ for(let junction of old_junction_list){
321
+
322
+ let s1=junction.sides[0]
323
+ let s2=junction.sides[1];
324
+
325
+ let matching=junction_list.find(a=>junction_match(a.sides,junction.sides));
326
+
327
+
328
+ if(matching==undefined){
329
+ if(s1.prop_id) remove_target(s1,s2);
330
+ if(s2.prop_id) remove_target(s2,s1);
331
+ delete_queue.push(junction);
332
+ }
333
+
334
+ //delete queue junctions will need to have the targets removed (or left be, if the prop target doesn't match) from their respective props
335
+
336
+ }
337
+
338
+
339
+ // STEP 2 ============================
340
+ // a) find all the junctions in the new list that don't appear in the old one
341
+ // b) this means they need to be newly created
342
+ // c) the corresponding targets need to be registered in any referenced properties
343
+ // d) we check if there are any former tables which share at least one of the same properties, and we transfer their properties
344
+
345
+
346
+ for(let junction of junction_list){
347
+ let s1=junction.sides[0];
348
+ let s2=junction.sides[1];
349
+
350
+
351
+ // a)
352
+ let matching=old_junction_list.find(a=>junction_match(a.sides,junction.sides));
353
+
354
+ if(matching==undefined){
355
+
356
+ // b)
357
+ // create the new table
358
+ let j_sides={
359
+ input_1: `${s1.class_id}.${s1.prop_id || ''}`,
360
+ input_2:`${s2.class_id}.${s2.prop_id || ''}`
361
+ }
362
+
363
+ let junction_id=this.create_junction_table(j_sides);
364
+
365
+ // c)
366
+
367
+ if(s1.prop_id) add_target(s1,s2,junction_id);
368
+ if(s2.prop_id) add_target(s2,s1,junction_id);
369
+
370
+ let j_object={
371
+ side_a:j_sides.input_1,
372
+ side_b:j_sides.input_2,
373
+ id:junction_id
374
+ };
375
+
376
+ // d)
377
+ // look for any tables in the delete pile that pair the same classes and have the same property on one side. If any exist, transfer the connections from the old tables
378
+ let partial_matches=delete_queue.filter(a=>partial_junction_match(a.sides,junction.sides));
379
+
380
+
381
+ for(let partial of partial_matches){
382
+ let p_object={
383
+ id:partial.id,
384
+ side_a:`${partial.sides[0].class_id}.${partial.sides[0].prop_id || ''}`,
385
+ side_b:`${partial.sides[1].class_id}.${partial.sides[1].prop_id || ''}`
386
+ }
387
+ this.transfer_connections(p_object,j_object);
388
+ }
389
+
390
+ }
391
+ }
392
+
393
+ // STEP 3 ============================
394
+ // delete the tables in the delete pile
395
+ for(let junction of delete_queue){
396
+ this.delete_junction_table(junction.id);
397
+ }
398
+
399
+
400
+ // STEP 4 ============================
401
+ // submit all prop updates to class_meta
402
+ let modified_classes=Object.entries(classes_meta).filter(a=>a[1].modified).map(a=>a[1]);
403
+
404
+ for(let modified of modified_classes) this.run.save_class_meta.run(JSON.stringify(modified.metadata),modified.id);
405
+
406
+
407
+ this.refresh_class_cache();
408
+
409
+ // STEP 5 (TBD) ===================
410
+ // check if the connections in the new tables follow the conditons of their corresponding properties, and remove any that don't pass muster
411
+
412
+
413
+
414
+
415
+ // ======================== utility functions ============================
416
+ function side_match(x,y){
417
+ return x.class_id==y.class_id&&x.prop_id==y.prop_id;
418
+ };
419
+
420
+
421
+ function junction_match(a,b){
422
+ // match sides completely, order doesn't matter
423
+ return (side_match(a[0],b[0])&&side_match(a[1],b[1])) ||
424
+ (side_match(a[0],b[1])&&side_match(a[1],b[0]));
425
+
426
+ }
427
+
428
+ function partial_junction_match(a,b){
429
+
430
+ // match both classes
431
+ // match at least one prop
432
+ let a0_match_i=b.findIndex(side=>a[0].class_id==side.class_id);
433
+ let a1_match_i=b.findIndex(side=>a[1].class_id==side.class_id);
434
+ if(a0_match_i>=0&&a1_match_i>=0&&a0_match_i!==a1_match_i){
435
+ return b[a0_match_i].prop_id==a[0].prop_id||
436
+ b[a1_match_i].prop_id==a[1].prop_id
437
+ }else{
438
+ return false;
439
+ }
440
+
441
+ }
442
+
443
+
444
+ function remove_target(prop,target){
445
+ let prop_class=classes_meta[prop.class_id];
446
+
447
+ let class_meta=prop_class.metadata;
448
+
449
+ let prop_meta=class_meta.properties.find(a=>a.id==prop.prop_id);
450
+
451
+ if(prop_meta){
452
+ let target_index=prop_meta.targets.findIndex(a=>side_match(a,target));
453
+ if(target_index>=0) prop_meta.targets.splice(target_index,1);
454
+ prop_class.modified=true;
455
+ }
456
+ }
457
+
458
+ function add_target(prop,target,junction_id){
459
+ let prop_class=classes_meta[prop.class_id]
460
+ let class_meta=prop_class.metadata
461
+
462
+ let prop_meta=class_meta.properties.find(a=>a.id==prop.prop_id);
463
+
464
+ if(prop_meta){
465
+ let obj={
466
+ class_id:target.class_id,
467
+ junction_id:junction_id
468
+ }
469
+ if(target.prop_id!==undefined) obj.prop_id=target.prop_id;
470
+ prop_meta.targets.push(obj)
471
+
472
+ prop_class.modified=true;
473
+
474
+ }
475
+ }
476
+
477
+
478
+ }
479
+
480
+
481
+ create_junction_table(sides){
482
+ // adds new record to junction table
483
+ this.db.prepare(`INSERT INTO system_junctionlist (side_a, side_b) VALUES ('${sides.input_1}','${sides.input_2}')`).run();
484
+
485
+ //gets id of new record
486
+ let id=this.db.prepare('SELECT id FROM system_junctionlist ORDER BY id DESC').get().id;
487
+
488
+ // creates table
489
+ this.create_table('junction',id,[
490
+ `"${sides.input_1}" INTEGER`,
491
+ `"${sides.input_2}" INTEGER`
492
+ ]);
493
+
494
+ return id;
495
+ }
496
+
497
+
498
+ transfer_connections(source,target){
499
+
500
+ let source_relations=this.db.prepare(`SELECT * FROM junction_${source.id}`).all();
501
+
502
+ //have to match source sides to target sides in order to determine junction order
503
+ let source_sides=source.side_a.split('.')[0] == target.side_a.split('.')[0]?
504
+ [source.side_a,source.side_b]:[source.side_b,source.side_a];
505
+
506
+ for(let relation of source_relations){
507
+
508
+ this.db.prepare(`INSERT INTO junction_${target.id}("${target.side_a}","${target.side_b}")
509
+ VALUES(${ relation[source_sides[0]]},${ relation[source_sides[1]]})`).run();
510
+ }
511
+ }
512
+
513
+ delete_junction_table(id){
514
+ this.db.prepare(`DELETE FROM system_junctionlist WHERE id = ${id}`).run();
515
+ this.db.prepare(`DROP TABLE junction_${id}`).run();
516
+ }
517
+
518
+ check_conditions(class_id,prop_id,targets,conditions){
519
+ /* conditions={
520
+ max: integer or undefined,
521
+ filters:[
522
+
523
+ ],
524
+ rules:[
525
+
526
+ ]
527
+ }
528
+
529
+ for now not gonna deal with filters or rules, just going to check max
530
+ */
531
+
532
+ let cls=this.retrieve_class(class_id);
533
+ let prop_name='user_'+cls.metadata.properties.find(a=>a.id==prop_id).name;
534
+ for(let object of cls.objects){
535
+ let prop_values=object[prop_name];
536
+ console.log(prop_name,prop_values);
537
+ }
538
+
539
+ // this is going to require writing the new retrieval function.
540
+
541
+
542
+ }
543
+
544
+ action_save(){
545
+ if(this.db.inTransaction) this.run.commit.run();
546
+ this.db.close();
547
+ }
548
+
549
+
550
+ action_add_row(class_id,class_name){
551
+ //first add new row to root and get id
552
+ if(class_name==undefined) class_name=this.class_cache[class_id].name;
553
+ // console.log(class_name)
554
+ this.db.prepare('INSERT INTO system_root VALUES (null)').run();
555
+ const root_id=this.db.prepare('SELECT id FROM system_root ORDER BY id DESC').get().id;
556
+
557
+ //get the last item in class table order and use it to get the order for the new item
558
+ const last_order=this.db.prepare(`SELECT system_order FROM [class_${class_name}] ORDER BY system_order DESC`).get();
559
+ const new_order=last_order?last_order.system_order+1:1;
560
+
561
+ this.db.prepare(`INSERT INTO [class_${class_name}] (system_id, system_order) VALUES (${root_id},${new_order})`).run();
562
+
563
+ return root_id;
564
+ }
565
+
566
+ action_make_relation(input_1,input_2){
567
+ /* input = {
568
+ class_id: INT,
569
+ prop_id: INT,
570
+ object_id: INT
571
+ }
572
+ */
573
+
574
+ let sides={
575
+ input_1:`${input_1.class_id}.${input_1.prop_id || ''}`,
576
+ input_2:`${input_2.class_id}.${input_2.prop_id || ''}`
577
+ }
578
+ let junction_id=this.run.match_junction.get(sides)?.id;
579
+ this.db.prepare(`INSERT INTO junction_${junction_id} ("${sides.input_1}", "${sides.input_2}") VALUES (${input_1.object_id},${input_2.object_id})`).run();
580
+
581
+ }
582
+
583
+
584
+ retrieve_class(class_id,class_name,class_meta){
585
+ if(class_name==undefined){
586
+ let class_data=this.class_cache[class_id];
587
+ // console.log(class_data)
588
+ class_name=class_data.name;
589
+ class_meta=class_data.metadata;
590
+ };
591
+
592
+ const class_string=`[class_${class_name}]`;
593
+
594
+ // //joined+added at beginning of the query, built from relations
595
+ const cte_strings=[];
596
+
597
+ // //joined+added near the end of the query, built from relations
598
+ const cte_joins=[];
599
+
600
+ // //joined+added between SELECT and FROM, built from relations
601
+ const relation_selections=[];
602
+
603
+ let relation_properties=class_meta.properties.filter(a=>a.type=='relation');
604
+
605
+ for (let prop of relation_properties){
606
+ const target_selects=[];
607
+ const target_joins=[];
608
+ let p_side=`${class_id}.${prop.id}`;
609
+ let first=prop.targets[0];
610
+
611
+ for(let i = 0; i < prop.targets.length; i++){
612
+ let target=prop.targets[i];
613
+ let t_side=`${target.class_id}.${target.prop_id || ''}`;
614
+
615
+ let target_select=`
616
+ SELECT "${p_side}", json_object('target_id','${target.class_id}','id',"${t_side}") AS json_object
617
+ FROM junction_${target.junction_id}`
618
+ target_selects.push(target_select);
619
+ }
620
+
621
+ const cte=`[${prop.id}_cte] AS (
622
+ SELECT "${p_side}", ('[' || GROUP_CONCAT(json_object,',') || ']') AS [user_${prop.name}]
623
+ FROM (${target_selects.join(' UNION ')})
624
+ GROUP BY "${p_side}"
625
+ )`
626
+
627
+ cte_strings.push(cte);
628
+ relation_selections.push(`[${prop.id}_cte].[user_${prop.name}]`);
629
+ cte_joins.push(`LEFT JOIN [${prop.id}_cte] ON [${prop.id}_cte]."${p_side}" = ${class_string}.system_id`)
630
+
631
+ }
632
+
633
+ let orderby=`ORDER BY ${class_string}.system_order`;
634
+
635
+ let query=`
636
+ ${cte_strings.length>0?"WITH "+cte_strings.join(','):''}
637
+ SELECT [class_${class_name}].* ${cte_strings.length>0?', '+relation_selections.join(`, `):''}
638
+ FROM [class_${class_name}]
639
+ ${cte_joins.join(' ')}
640
+ ${orderby}`;
641
+
642
+ let objects=this.db.prepare(query).all();
643
+
644
+ let stringified_properties=class_meta.properties.filter(a=>a.type=='relation'||a.conditions?.max>1);
645
+ objects.map(row=>{
646
+ for (let prop of stringified_properties){
647
+ row['user_'+prop.name]=JSON.parse(row['user_'+prop.name]);
648
+ }
649
+ })
650
+ return {
651
+ objects,
652
+ metadata:class_meta,
653
+ name:class_name
654
+ };
655
+ }
656
+
657
+ retrieve_all_classes(){
658
+ const classes_data=this.run.get_all_classes.all();
659
+ // console.log(classes)
660
+ let classes=[];
661
+ for(let cls of classes_data){
662
+ classes.push(this.retrieve_class(cls.id,cls.name,JSON.parse(cls.metadata)));
663
+ }
664
+
665
+ return classes;
666
+ }
667
+
668
+
669
+ }
670
+
671
+ // export default Project;
672
+ module.exports=Project;
673
+
674
+ // export default Project;
package/package.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "goby-database",
3
+ "version": "1.0.0",
4
+ "description": "This will hold the core better-sqlite3-powered application for creating and modifying goby databases",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "author": "Nico Chilla",
10
+ "license": "ISC",
11
+ "dependencies": {
12
+ "better-sqlite3": "^8.5.0"
13
+ }
14
+ }
package/readme.md ADDED
@@ -0,0 +1,7 @@
1
+ I intend for this to be the core module Goby uses to represent and manipulate the data structure of a project, which is stored as an SQLite database
2
+
3
+ Directory:
4
+ * `notes.md` is my running notes doc for the project.
5
+ * `index.js` will be main script (warning, lots of currently non-functional unfinished code)
6
+ * `sandbox.js` will be a kind of test suite
7
+ * `outdated_v0.json` is the first version I made of this module, used in the version of goby that I put together for my undergraduate thesis. I’m pulling a lot of the design of the new module from here, but there are a few big conceptual changes, particularly around how relation properties will work and be supported by junction tables in the back-end architecture.