goby-database 1.0.8 → 2.0.9

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/index.js DELETED
@@ -1,840 +0,0 @@
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
- this.db.function('junction_obj', (side_a, side_b) => {
23
-
24
- let split_1=side_a.split('.');
25
- let split_2=side_b.split('.');
26
- let c1=`"class_id":${split_1[0]}`;
27
- let p1=split_1[1]?`,"prop_id":${split_1[1]}`:'';
28
- let c2=`"class_id":${split_2[0]}`;
29
- let p2=split_2[1]?`,"prop_id":${split_2[1]}`:'';
30
- return `[ {${c1}${p1}}, {${c2}${p2}} ]`;
31
- });
32
-
33
- //prepared statements with arguments so my code isn't as verbose elsewhere
34
- this.run={
35
- begin:this.db.prepare('BEGIN IMMEDIATE'),
36
- commit:this.db.prepare('COMMIT'),
37
- rollback:this.db.prepare('ROLLBACK'),
38
- create_item:this.db.prepare('INSERT INTO system_root(type,value) VALUES (@type, @value)'),
39
- get_junctionlist:this.db.prepare('SELECT id, junction_obj(side_a, side_b) AS sides, metadata FROM system_junctionlist'),
40
- get_class:this.db.prepare(`SELECT name, metadata FROM system_classlist WHERE id = ?`),
41
- get_class_id:this.db.prepare(`SELECT id FROM system_classlist WHERE name = ?`),
42
- get_all_classes:this.db.prepare(`SELECT id, name, metadata FROM system_classlist`),
43
- save_class_meta:this.db.prepare(`UPDATE system_classlist set metadata = ? WHERE id = ?`),
44
- update_window:this.db.prepare(`UPDATE system_windows set open = @open, type=@type, metadata = @meta WHERE id = @id`),
45
- create_window: this.db.prepare(`INSERT INTO system_windows (type,open, metadata) VALUES (@type, @open, @meta)`),
46
- get_windows:this.db.prepare(`SELECT id, type, open, metadata FROM system_windows`),
47
- 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 )`),
48
- 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 )`)
49
- }
50
-
51
-
52
-
53
-
54
- this.class_cache={};
55
- this.refresh_class_cache();
56
- this.junction_cache=[];
57
- this.refresh_junction_cache();
58
- // this.junction_cache;
59
-
60
- //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
61
- //this enables a one-step undo.
62
- // this.run.begin.run();
63
-
64
- }
65
-
66
- get_latest_id(table_name){
67
- let id=this.db.prepare(`SELECT last_insert_rowid() AS id FROM ${table_name}`).get().id;
68
- return id;
69
- }
70
-
71
-
72
-
73
- init(){
74
-
75
-
76
- //System table to contain all items in the project.
77
- this.create_table('system','root',[
78
- 'id INTEGER NOT NULL PRIMARY KEY',
79
- 'type TEXT',
80
- 'value TEXT'
81
- ]);
82
-
83
-
84
- //System table to contain metadata for all classes created by user
85
- this.create_table('system','classlist',['id INTEGER NOT NULL PRIMARY KEY','name TEXT','metadata TEXT']);
86
- //System table to contain all the junction tables and aggregate info about relations
87
- this.create_table('system','junctionlist',['id INTEGER NOT NULL PRIMARY KEY','side_a TEXT','side_b TEXT','metadata TEXT']);
88
- //System table to contain generated image data
89
- this.create_table('system','images',['file_path TEXT','img_type TEXT','img BLOB']);
90
-
91
- this.create_table('system','windows',[
92
- 'id INTEGER NOT NULL PRIMARY KEY',
93
- 'type TEXT',
94
- 'open INTEGER',
95
- 'metadata TEXT'
96
- ]);
97
-
98
-
99
- // let home_id=this.action_config_window('home',0,{pos:[null,null], size:[540,400]})
100
- // let hopper_id=this.action_config_window('hopper',0,{pos:[null,null], size:[300,400]})
101
- // console.log('home:',home_id,'hopper:',hopper_id)
102
- this.db.prepare(`INSERT INTO system_windows
103
- (type, open, metadata)
104
- VALUES
105
- ('home',0,'${JSON.stringify({pos:[null,null], size:[540,400]})}'),
106
- ('hopper',0,'${JSON.stringify({pos:[null,null], size:[300,400]})}')`).run();
107
-
108
- // [TO ADD: special junction table for root items to reference themselves in individual relation]
109
- this.create_table('system','junction_root',[
110
- 'id_1 INTEGER',
111
- 'id_2 INTEGER',
112
- 'metadata TEXT'
113
- ]);
114
-
115
-
116
-
117
-
118
-
119
- }
120
-
121
-
122
- refresh_class_cache(){
123
- let class_array=this.run.get_all_classes.all();
124
- let cache_obj={};
125
- for(let cls of class_array){
126
- cache_obj[cls.id]={
127
- id:cls.id,
128
- name:cls.name,
129
- metadata:JSON.parse(cls.metadata)
130
- };
131
-
132
- }
133
- this.class_cache=cache_obj;
134
- }
135
-
136
- refresh_junction_cache(){
137
- let junction_list=this.run.get_junctionlist.all();
138
- junction_list.map(a=>a.sides=JSON.parse(a.sides));
139
- this.junction_cache=junction_list;
140
- }
141
-
142
-
143
-
144
- create_table(type,name,columns){
145
- //type will pass in 'class', 'system', or 'junction' to use as a name prefix
146
- //columns is an array of raw SQL column strings
147
-
148
-
149
- let columns_string=columns.join(',');
150
-
151
- //name in brackets to allow special characters
152
- // validation test: what happens if there are brackets in the name?
153
- const sqlname=type=='class'?`[class_${name}]`:`${type}_${name}`;
154
- let create_statement=`CREATE TABLE ${sqlname}(
155
- ${columns_string}
156
- )`;
157
- this.db.prepare(create_statement).run();
158
-
159
-
160
- }
161
-
162
- action_create_class(name,meta){
163
- // if(this.db.inTransaction) this.run.commit.run();
164
- // this.run.begin.run();
165
-
166
- //a class starts with these basic columns
167
- let columns=['system_id INTEGER UNIQUE',
168
- 'system_order INTEGER','user_name TEXT',`FOREIGN KEY(system_id) REFERENCES system_root(id)`];
169
-
170
- this.create_table('class',name,columns);
171
-
172
- const class_meta={
173
- properties:[
174
- {
175
- name:'name',
176
- type:'data',
177
- id:1,
178
- datatype:'string',
179
- conditions:{
180
- max:1
181
- },
182
- style:{
183
- display:true,
184
- colwidth:4
185
- }
186
- }
187
- ],
188
- used_prop_ids:[1],
189
- style:{
190
- color:'#b5ffd5',
191
- display:meta?.display || true,
192
- position:meta?.position || [0,0]
193
- }
194
- };
195
-
196
- this.db.prepare(`INSERT INTO system_classlist (name, metadata) VALUES ('${name}','${JSON.stringify(class_meta)}')`).run();
197
- //get the id of newest value from system_classlist and return
198
- const class_id=this.db.prepare('SELECT id FROM system_classlist ORDER BY id DESC').get().id;
199
-
200
- this.refresh_class_cache();
201
- this.refresh_junction_cache();
202
-
203
- return class_id;
204
-
205
-
206
- }
207
-
208
-
209
- action_add_data_property(class_id,name,conditions,datatype,style){
210
-
211
- let class_data=this.class_cache[class_id];
212
- let class_name=class_data.name;
213
- let class_meta=class_data.metadata;
214
-
215
- let id=Math.max(...class_meta.used_prop_ids)+1;
216
- class_meta.used_prop_ids.push(id);
217
- //create JSON for storage in system_classlist
218
- const prop_meta={ type:'data', name,conditions,style,datatype,id};
219
-
220
- //add property to property list
221
- class_meta.properties.push(prop_meta);
222
-
223
- let sql_datatype='';
224
-
225
- if(conditions.max>1||this.text_datatypes.includes(datatype)){
226
- //multiple for data means a stringified array no matter what it is
227
- sql_datatype='TEXT';
228
- }else if(this.real_datatypes.includes(datatype)){
229
- sql_datatype='REAL';
230
- }
231
- //create property in table
232
- let command_string=`ALTER TABLE [class_${class_name}] ADD COLUMN [user_${name}] ${sql_datatype};`;
233
- this.db.prepare(command_string).run();
234
-
235
- //update metadata json for table with new property
236
- this.run.save_class_meta.run(JSON.stringify(class_meta),class_id);
237
- this.refresh_class_cache();
238
-
239
-
240
- }
241
-
242
- action_add_relation_property(class_id,name,conditions,style){
243
- // basic property construction------------------
244
- let class_meta=this.class_cache[class_id].metadata;
245
-
246
- let id=Math.max(...class_meta.used_prop_ids)+1;
247
- class_meta.used_prop_ids.push(id);
248
- //create JSON for storage in system_classlist
249
- const prop_meta={
250
- type:'relation',
251
- name,
252
- style,
253
- id,
254
- targets:[],
255
- conditions
256
- }
257
- //add property to property list
258
- class_meta.properties.push(prop_meta);
259
-
260
-
261
- this.run.save_class_meta.run(JSON.stringify(class_meta),class_id);
262
- this.refresh_class_cache();
263
-
264
- return id;
265
-
266
- }
267
-
268
-
269
- delete_property(class_id,prop_id){
270
- //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
271
-
272
- let class_meta=this.class_cache[class_id].metadata;
273
- let i=class_meta.properties.findIndex(a=>a.id==prop_id);
274
- class_meta.properties.splice(i,1);
275
- this.run.save_class_meta.run(JSON.stringify(class_meta),class_id.id);
276
- this.refresh_class_cache();
277
- }
278
-
279
- get_junctions(){
280
- let junction_list=this.run.get_junctionlist.all();
281
- junction_list.map(a=>a.sides=JSON.parse(a.sides))
282
- return junction_list;
283
- }
284
-
285
-
286
- action_edit_class_schema(edits){
287
- // class_changes=[],new_junction_list
288
- let class_changes=edits.class_changes || [];
289
- let junction_list=edits.junction_list;
290
-
291
- for(let change of class_changes){
292
-
293
- if(change.action=='create'){
294
- if(change.class_id==undefined){
295
- change.class_id=this.action_create_class(change.class_name);
296
- if(junction_list) junction_list.map(junction=>{
297
- let matching=junction.sides.find(
298
- side=>side.class_name==change.class_name);
299
- if(matching) matching.class_id=change.class_id;
300
- })
301
- }
302
-
303
- if(change.prop_name!==undefined){
304
- //if a prop name is specified, that means
305
- // this change involves a property
306
- // that property needs to be created
307
- if(change.type=='relation'){
308
- change.prop_id=this.action_add_relation_property(change.class_id,change.prop_name,change.conditions,change.style);
309
- junction_list.map(junction=>{
310
- let matching=junction.sides.find(
311
- side=>side.class_id==change.class_id&&side.prop_name==change.prop_name);
312
- if(matching) matching.prop_id=change.prop_id;
313
- })
314
- }else if(change.type=='data'){
315
- this.action_add_data_property(change.class_id,change.prop_name,change.conditions,change.datatype,change.style);
316
- }
317
-
318
- }
319
- }else if(change.action=='delete'){
320
- if(change.prop_id!==undefined){
321
- this.delete_property(change.class_id,change.prop_id);
322
- }else{
323
- // delete class
324
- }
325
-
326
- //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
327
- }
328
-
329
- }
330
-
331
-
332
- if(junction_list!==undefined) this.action_update_relations(junction_list)
333
- //in the middle of adding update_relations to this generalized funciton
334
-
335
- }
336
-
337
-
338
- action_update_relations(junction_list){
339
-
340
- let classes_meta=this.class_cache;
341
-
342
-
343
- // STEP 1 ============================
344
- // find all the old junctions which don't appear in the new list
345
- // add them to a "delete_queue" to schedule them for deletion
346
- // remove the corresponding targets from the properties they reference
347
-
348
- let delete_queue=[];
349
- let old_junction_list=this.get_junctions();
350
-
351
-
352
-
353
- for(let junction of old_junction_list){
354
-
355
- let s1=junction.sides[0]
356
- let s2=junction.sides[1];
357
-
358
- let matching=junction_list.find(a=>junction_match(a.sides,junction.sides));
359
-
360
-
361
- if(matching==undefined){
362
- if(s1.prop_id) remove_target(s1,s2);
363
- if(s2.prop_id) remove_target(s2,s1);
364
- delete_queue.push(junction);
365
- }
366
-
367
- //delete queue junctions will need to have the targets removed (or left be, if the prop target doesn't match) from their respective props
368
-
369
- }
370
-
371
-
372
- // STEP 2 ============================
373
- // a) find all the junctions in the new list that don't appear in the old one
374
- // b) this means they need to be newly created
375
- // c) the corresponding targets need to be registered in any referenced properties
376
- // d) we check if there are any former tables which share at least one of the same properties, and we transfer their properties
377
-
378
-
379
- for(let junction of junction_list){
380
- let s1=junction.sides[0];
381
- let s2=junction.sides[1];
382
-
383
-
384
- // a)
385
- let matching=old_junction_list.find(a=>junction_match(a.sides,junction.sides));
386
-
387
- if(matching==undefined){
388
-
389
- // b)
390
- // create the new table
391
- let j_sides={
392
- input_1: `${s1.class_id}.${s1.prop_id || ''}`,
393
- input_2:`${s2.class_id}.${s2.prop_id || ''}`
394
- }
395
-
396
- let junction_id=this.create_junction_table(j_sides);
397
-
398
- // c)
399
-
400
- if(s1.prop_id) add_target(s1,s2,junction_id);
401
- if(s2.prop_id) add_target(s2,s1,junction_id);
402
-
403
- let j_object={
404
- side_a:j_sides.input_1,
405
- side_b:j_sides.input_2,
406
- id:junction_id
407
- };
408
-
409
- // d)
410
- // 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
411
- let partial_matches=delete_queue.filter(a=>partial_junction_match(a.sides,junction.sides));
412
-
413
-
414
- for(let partial of partial_matches){
415
- let p_object={
416
- id:partial.id,
417
- side_a:`${partial.sides[0].class_id}.${partial.sides[0].prop_id || ''}`,
418
- side_b:`${partial.sides[1].class_id}.${partial.sides[1].prop_id || ''}`
419
- }
420
- this.transfer_connections(p_object,j_object);
421
- }
422
-
423
- }
424
- }
425
-
426
- // STEP 3 ============================
427
- // delete the tables in the delete pile
428
- for(let junction of delete_queue){
429
- this.delete_junction_table(junction.id);
430
- }
431
-
432
-
433
- // STEP 4 ============================
434
- // submit all prop updates to class_meta
435
- let modified_classes=Object.entries(classes_meta).filter(a=>a[1].modified).map(a=>a[1]);
436
-
437
- for(let modified of modified_classes) this.run.save_class_meta.run(JSON.stringify(modified.metadata),modified.id);
438
-
439
-
440
- this.refresh_class_cache();
441
-
442
- // STEP 5 (TBD) ===================
443
- // check if the connections in the new tables follow the conditons of their corresponding properties, and remove any that don't pass muster
444
-
445
-
446
-
447
-
448
- // ======================== utility functions ============================
449
- function side_match(x,y){
450
- return x.class_id==y.class_id&&x.prop_id==y.prop_id;
451
- };
452
-
453
-
454
- function junction_match(a,b){
455
- // match sides completely, order doesn't matter
456
- return (side_match(a[0],b[0])&&side_match(a[1],b[1])) ||
457
- (side_match(a[0],b[1])&&side_match(a[1],b[0]));
458
-
459
- }
460
-
461
- function partial_junction_match(a,b){
462
-
463
- // match both classes
464
- // match at least one prop
465
- let a0_match_i=b.findIndex(side=>a[0].class_id==side.class_id);
466
- let a1_match_i=b.findIndex(side=>a[1].class_id==side.class_id);
467
- if(a0_match_i>=0&&a1_match_i>=0&&a0_match_i!==a1_match_i){
468
- return b[a0_match_i].prop_id==a[0].prop_id||
469
- b[a1_match_i].prop_id==a[1].prop_id
470
- }else{
471
- return false;
472
- }
473
-
474
- }
475
-
476
-
477
- function remove_target(prop,target){
478
- let prop_class=classes_meta[prop.class_id];
479
-
480
- let class_meta=prop_class.metadata;
481
-
482
- let prop_meta=class_meta.properties.find(a=>a.id==prop.prop_id);
483
-
484
- if(prop_meta){
485
- let target_index=prop_meta.targets.findIndex(a=>side_match(a,target));
486
- if(target_index>=0) prop_meta.targets.splice(target_index,1);
487
- prop_class.modified=true;
488
- }
489
- }
490
-
491
- function add_target(prop,target,junction_id){
492
- let prop_class=classes_meta[prop.class_id]
493
- let class_meta=prop_class.metadata
494
-
495
- let prop_meta=class_meta.properties.find(a=>a.id==prop.prop_id);
496
-
497
- if(prop_meta){
498
- let obj={
499
- class_id:target.class_id,
500
- junction_id:junction_id
501
- }
502
- if(target.prop_id!==undefined) obj.prop_id=target.prop_id;
503
- prop_meta.targets.push(obj)
504
-
505
- prop_class.modified=true;
506
-
507
- }
508
- }
509
-
510
-
511
- }
512
-
513
-
514
- create_junction_table(sides){
515
- // adds new record to junction table
516
- this.db.prepare(`INSERT INTO system_junctionlist (side_a, side_b) VALUES ('${sides.input_1}','${sides.input_2}')`).run();
517
-
518
- //gets id of new record
519
- let id=this.db.prepare('SELECT id FROM system_junctionlist ORDER BY id DESC').get().id;
520
-
521
- // creates table
522
- this.create_table('junction',id,[
523
- `"${sides.input_1}" INTEGER`,
524
- `"${sides.input_2}" INTEGER`
525
- ]);
526
-
527
- return id;
528
- }
529
-
530
-
531
- transfer_connections(source,target){
532
-
533
- let source_relations=this.db.prepare(`SELECT * FROM junction_${source.id}`).all();
534
-
535
- //have to match source sides to target sides in order to determine junction order
536
- let source_sides=source.side_a.split('.')[0] == target.side_a.split('.')[0]?
537
- [source.side_a,source.side_b]:[source.side_b,source.side_a];
538
-
539
- for(let relation of source_relations){
540
-
541
- this.db.prepare(`INSERT INTO junction_${target.id}("${target.side_a}","${target.side_b}")
542
- VALUES(${ relation[source_sides[0]]},${ relation[source_sides[1]]})`).run();
543
- }
544
- }
545
-
546
- delete_junction_table(id){
547
- this.db.prepare(`DELETE FROM system_junctionlist WHERE id = ${id}`).run();
548
- this.db.prepare(`DROP TABLE junction_${id}`).run();
549
- }
550
-
551
- check_conditions(class_id,prop_id,targets,conditions){
552
- /* conditions={
553
- max: integer or undefined,
554
- filters:[
555
-
556
- ],
557
- rules:[
558
-
559
- ]
560
- }
561
-
562
- for now not gonna deal with filters or rules, just going to check max
563
- */
564
-
565
- let cls=this.retrieve_class(class_id);
566
- let prop_name='user_'+cls.metadata.properties.find(a=>a.id==prop_id).name;
567
- for(let item of cls.items){
568
- let prop_values=item[prop_name];
569
- console.log(prop_name,prop_values);
570
- }
571
-
572
- // this is going to require writing the new retrieval function.
573
-
574
-
575
- }
576
-
577
- action_save(){
578
- if(this.db.inTransaction) this.run.commit.run();
579
- this.db.close();
580
- }
581
-
582
- action_create_item_in_root({type=null,value=''}){
583
- // this.db.prepare('INSERT INTO system_root VALUES (null)').run();
584
- this.run.create_item.run({type,value});
585
- let id=this.db.prepare('SELECT id FROM system_root ORDER BY id DESC').get().id;
586
- return id;
587
- }
588
-
589
- action_delete_item_from_root(id){
590
- this.db.prepare(`DELETE FROM system_root WHERE id = ${id}`).run();
591
- }
592
-
593
- action_set_root_item_value(id,value){
594
- this.db.prepare(`UPDATE system_root set value = ? WHERE id = ?`).run(value,id);
595
- }
596
-
597
- action_add_row(class_id,class_name){
598
- //first add new row to root and get id
599
- if(class_name==undefined) class_name=this.class_cache[class_id].name;
600
-
601
- // note for future: instead of letting class_id be undefined, locate it
602
- const root_id=this.action_create_item_in_root({type:class_id!==undefined?'class_'+class_id:null});
603
-
604
- //get the last item in class table order and use it to get the order for the new item
605
- const last_order=this.db.prepare(`SELECT system_order FROM [class_${class_name}] ORDER BY system_order DESC`).get();
606
- const new_order=last_order?last_order.system_order+1:1;
607
-
608
- this.db.prepare(`INSERT INTO [class_${class_name}] (system_id, system_order) VALUES (${root_id},${new_order})`).run();
609
-
610
- return root_id;
611
- }
612
-
613
- action_make_relation(input_1,input_2){
614
- /* input = {
615
- class_id: INT,
616
- prop_id: INT,
617
- item_id: INT
618
- }
619
- */
620
-
621
- let sides={
622
- input_1:`${input_1.class_id}.${input_1.prop_id || ''}`,
623
- input_2:`${input_2.class_id}.${input_2.prop_id || ''}`
624
- }
625
- let junction_id=this.run.match_junction.get(sides)?.id;
626
- this.db.prepare(`INSERT INTO junction_${junction_id} ("${sides.input_1}", "${sides.input_2}") VALUES (${input_1.item_id},${input_2.item_id})`).run();
627
-
628
- }
629
-
630
-
631
- retrieve_class(class_id,class_name,class_meta){
632
- if(class_name==undefined){
633
- let class_data=this.class_cache[class_id];
634
- // console.log(class_data)
635
- class_name=class_data.name;
636
- class_meta=class_data.metadata;
637
- };
638
-
639
- const class_string=`[class_${class_name}]`;
640
-
641
- // //joined+added at beginning of the query, built from relations
642
- const cte_strings=[];
643
-
644
- // //joined+added near the end of the query, built from relations
645
- const cte_joins=[];
646
-
647
- // //joined+added between SELECT and FROM, built from relations
648
- const relation_selections=[];
649
-
650
- let relation_properties=class_meta.properties.filter(a=>a.type=='relation');
651
-
652
- for (let prop of relation_properties){
653
- const target_selects=[];
654
- const target_joins=[];
655
- let p_side=`${class_id}.${prop.id}`;
656
- let first=prop.targets[0];
657
-
658
- for(let i = 0; i < prop.targets.length; i++){
659
- let target=prop.targets[i];
660
- let t_side=`${target.class_id}.${target.prop_id || ''}`;
661
-
662
- let target_select=`
663
- SELECT "${p_side}", json_object('target_id','${target.class_id}','id',"${t_side}") AS json_object
664
- FROM junction_${target.junction_id}`
665
- target_selects.push(target_select);
666
- }
667
-
668
- const cte=`[${prop.id}_cte] AS (
669
- SELECT "${p_side}", ('[' || GROUP_CONCAT(json_object,',') || ']') AS [user_${prop.name}]
670
- FROM (${target_selects.join(' UNION ')})
671
- GROUP BY "${p_side}"
672
- )`
673
-
674
- cte_strings.push(cte);
675
- relation_selections.push(`[${prop.id}_cte].[user_${prop.name}]`);
676
- cte_joins.push(`LEFT JOIN [${prop.id}_cte] ON [${prop.id}_cte]."${p_side}" = ${class_string}.system_id`)
677
-
678
- }
679
-
680
- let orderby=`ORDER BY ${class_string}.system_order`;
681
-
682
- let query=`
683
- ${cte_strings.length>0?"WITH "+cte_strings.join(','):''}
684
- SELECT [class_${class_name}].* ${cte_strings.length>0?', '+relation_selections.join(`, `):''}
685
- FROM [class_${class_name}]
686
- ${cte_joins.join(' ')}
687
- ${orderby}`;
688
-
689
- let items=this.db.prepare(query).all();
690
-
691
- let stringified_properties=class_meta.properties.filter(a=>a.type=='relation'||a.conditions?.max>1);
692
- items.map(row=>{
693
- for (let prop of stringified_properties){
694
- row['user_'+prop.name]=JSON.parse(row['user_'+prop.name]);
695
- }
696
- })
697
- return {
698
- items,
699
- metadata:class_meta,
700
- name:class_name
701
- };
702
- }
703
-
704
- retrieve_all_classes(){
705
- const classes_data=this.run.get_all_classes.all();
706
- // console.log(classes)
707
- let classes=[];
708
- for(let cls of classes_data){
709
- classes.push(this.retrieve_class(cls.id,cls.name,JSON.parse(cls.metadata)));
710
- }
711
-
712
- return classes;
713
- }
714
-
715
- retrieve_windows(){
716
- let windows=this.run.get_windows.all();
717
- windows.map(a=>a.metadata=JSON.parse(a.metadata));
718
- return windows;
719
- }
720
-
721
- retrieve_workspace_contents(id){
722
- // get the workspace table
723
- let blocks=this.db.prepare(`SELECT * FROM workspace_${id}`).all();
724
- for(let block of blocks) block.properties=JSON.parse(block.properties);
725
- // get any relevant root items
726
- let items=this.db.prepare(`SELECT system_root.*
727
- FROM system_root
728
- LEFT JOIN workspace_${id}
729
- ON system_root.id = workspace_${id}.concept_id
730
- WHERE workspace_${id}.type = 'item';
731
- `).all();
732
- // get any relevant classes (going to hold off from this for now)
733
-
734
- return {
735
- blocks,
736
- items
737
- }
738
- }
739
-
740
- action_config_window(type,open,meta={pos:[null,null], size:[1000,700]},id){
741
- if(id!==undefined){
742
- this.run.update_window.run({
743
- id,
744
- open,
745
- type,
746
- meta:JSON.stringify(meta)
747
- })
748
- }else{
749
-
750
- let id=this.create_workspace(open,meta)
751
-
752
- return id;
753
- }
754
-
755
- }
756
-
757
-
758
- create_workspace(open,meta){
759
-
760
- this.run.create_window.run({
761
- type:'workspace',
762
- open,
763
- meta:JSON.stringify(meta)
764
- })
765
-
766
- let id=this.get_latest_id('system_windows')
767
-
768
- this.create_table('workspace',id,[
769
- 'block_id INTEGER NOT NULL PRIMARY KEY',
770
- 'type TEXT',
771
- 'properties TEXT',
772
- 'concept_id INTEGER'
773
- ]);
774
-
775
-
776
-
777
- return id;
778
- }
779
-
780
- action_create_workspace_block({workspace_id,type,properties,concept_id}){
781
- // should return block id
782
- this.db.prepare(`INSERT INTO workspace_${workspace_id}(type,properties,concept_id) VALUES (@type,@properties,@concept_id)`).run({
783
- type,
784
- properties:JSON.stringify(properties),
785
- concept_id
786
- });
787
- let block_id=this.db.prepare(`SELECT block_id FROM workspace_${workspace_id} ORDER BY block_id DESC`).get().id;
788
- return block_id;
789
-
790
- }
791
-
792
- action_remove_workspace_block({workspace_id,block_id}){
793
- this.db.prepare(`DELETE FROM workspace_${workspace_id} WHERE block_id = ${block_id}`).run();
794
- }
795
-
796
- action_create_and_add_to_workspace(workspace_id,blocktype,block_properties,concept_data){
797
- let concept_id;
798
- // concept creation
799
- switch(blocktype){
800
- case 'item':
801
- let {
802
- value:item_value,
803
- type:item_type
804
- } = concept_data;
805
- concept_id=this.action_create_item_in_root({type:item_type,value:item_value});
806
- break;
807
- // add cases for class and anything else in the future
808
- }
809
-
810
- let block_id=this.action_create_workspace_block({
811
- workspace_id,
812
- type:blocktype,
813
- properties:block_properties,
814
- concept_id
815
- })
816
-
817
- return {
818
- concept_id,
819
- block_id
820
- }
821
- // should return the block id and item id
822
- }
823
-
824
- action_remove_from_workspace_and_delete(workspace_id,block_id,blocktype,concept_id){
825
- this.action_remove_workspace_block({workspace_id,block_id});
826
- switch(blocktype){
827
- case 'item':
828
- this.action_delete_item_from_root(concept_id);
829
- break;
830
- }
831
- }
832
-
833
- }
834
-
835
-
836
-
837
- // export default Project;
838
- module.exports=Project;
839
-
840
- // export default Project;