masterrecord 0.1.4 → 0.2.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.
- package/Entity/entityTrackerModel.js +7 -3
- package/MIGRATIONS.md +178 -0
- package/Migrations/cli.js +3 -3
- package/Migrations/migrationMySQLQuery.js +18 -8
- package/Migrations/migrationSQLiteQuery.js +30 -3
- package/Migrations/schema.js +201 -16
- package/QueryLanguage/queryMethods.js +45 -58
- package/QueryLanguage/queryScript.js +102 -35
- package/SQLLiteEngine.js +158 -61
- package/Tools.js +74 -29
- package/context.js +191 -60
- package/deleteManager.js +3 -3
- package/insertManager.js +128 -34
- package/masterrecord_all_files.txt +4646 -0
- package/mySQLEngine.js +159 -44
- package/package.json +5 -5
- package/readme.md +3 -1
|
@@ -0,0 +1,4646 @@
|
|
|
1
|
+
/*
|
|
2
|
+
|
|
3
|
+
:binary
|
|
4
|
+
:boolean
|
|
5
|
+
:date
|
|
6
|
+
:datetime
|
|
7
|
+
:decimal
|
|
8
|
+
:float
|
|
9
|
+
:integer
|
|
10
|
+
:bigint
|
|
11
|
+
:primary_key
|
|
12
|
+
:references
|
|
13
|
+
:string
|
|
14
|
+
:text
|
|
15
|
+
:time
|
|
16
|
+
:timestamp
|
|
17
|
+
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
// version 0.0.5
|
|
21
|
+
class EntityModel {
|
|
22
|
+
|
|
23
|
+
constructor(name){
|
|
24
|
+
this.obj = {
|
|
25
|
+
name: name,
|
|
26
|
+
type: null,
|
|
27
|
+
relationshipType: null,
|
|
28
|
+
typeSize : null,
|
|
29
|
+
primary : null,
|
|
30
|
+
default : null,
|
|
31
|
+
virtual : null,
|
|
32
|
+
foreignKey : null,
|
|
33
|
+
nullable : true, // no
|
|
34
|
+
unique : false,
|
|
35
|
+
auto : false,
|
|
36
|
+
cascadeOnDelete : true,
|
|
37
|
+
lazyLoading : true,
|
|
38
|
+
isNavigational : false,
|
|
39
|
+
skipGetFunction :false,
|
|
40
|
+
valueConversion : true
|
|
41
|
+
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
type(type, size){
|
|
46
|
+
this.obj.type = type;
|
|
47
|
+
this.obj.typeSize = size;
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
string(){
|
|
52
|
+
this.obj.type = "string";
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
integer(){
|
|
57
|
+
this.obj.type = "integer";
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
time(){
|
|
62
|
+
this.obj.type = "time";
|
|
63
|
+
return this;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
boolean(){
|
|
67
|
+
this.obj.type = "boolean";
|
|
68
|
+
return this;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// maxLength(amount){
|
|
72
|
+
// this.obj.maxLength = amount;
|
|
73
|
+
// return this;
|
|
74
|
+
// }
|
|
75
|
+
|
|
76
|
+
// will stop cascade delete which means it will stop not auto delete relationship
|
|
77
|
+
stopCascadeOnDelete(){
|
|
78
|
+
this.obj.cascadeOnDelete = false;
|
|
79
|
+
return this;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// is this obj a primary key
|
|
83
|
+
primary(){
|
|
84
|
+
this.obj.primary = true;
|
|
85
|
+
this.obj.nullable = false;
|
|
86
|
+
this.obj.unique = true;
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// allows ablity to get back primaryKey on insert automaticlly return on insert
|
|
91
|
+
auto(){
|
|
92
|
+
this.obj.auto = true;
|
|
93
|
+
return this;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// sets the default value in the DB
|
|
97
|
+
default(value){
|
|
98
|
+
this.obj.default = value;
|
|
99
|
+
return this;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
get(func){
|
|
103
|
+
this.obj.get = func;
|
|
104
|
+
return this;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
set(func){
|
|
108
|
+
this.obj.set = func;
|
|
109
|
+
return this;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
unique(){
|
|
113
|
+
this.obj.unique = true; // yes
|
|
114
|
+
return this;
|
|
115
|
+
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// this means that it can be an empty field
|
|
119
|
+
nullable(){
|
|
120
|
+
this.obj.nullable = true; // yes
|
|
121
|
+
return this;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
notNullable(){
|
|
125
|
+
this.obj.nullable = false; // no
|
|
126
|
+
return this;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
//allows you to stop lazy loading because lazy loading is added by default
|
|
130
|
+
lazyLoadingOff(){
|
|
131
|
+
this.obj.lazyLoading = false;
|
|
132
|
+
return this;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
valueConversion(bool){
|
|
136
|
+
this.obj.valueConversion = bool;
|
|
137
|
+
return this;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// allows you to add a virtual object that will skipped from being used as sql objects
|
|
141
|
+
virtual(){
|
|
142
|
+
this.obj.virtual = true;
|
|
143
|
+
return this;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
hasMany(foreignTable, foreignKey){
|
|
147
|
+
if(foreignKey === undefined){
|
|
148
|
+
foreignKey = `${this.obj.name.toLowerCase()}_id`;
|
|
149
|
+
}
|
|
150
|
+
this.obj.relationshipType = "hasMany";
|
|
151
|
+
this.obj.type = "hasMany";
|
|
152
|
+
this.obj.foreignTable = foreignTable;
|
|
153
|
+
this.obj.foreignKey = foreignKey;
|
|
154
|
+
this.obj.isNavigational = true;
|
|
155
|
+
this.obj.nullable = false;
|
|
156
|
+
return this;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// DB must have a record or exception will be thrown unless set to nullable
|
|
160
|
+
hasOne(foreignTable, foreignKey){
|
|
161
|
+
if(foreignKey === undefined){
|
|
162
|
+
foreignKey = `${this.obj.name.toLowerCase()}_id`;
|
|
163
|
+
}
|
|
164
|
+
this.obj.relationshipType = "hasOne";
|
|
165
|
+
this.obj.type = "hasOne";
|
|
166
|
+
this.obj.foreignTable = foreignTable;
|
|
167
|
+
this.obj.foreignKey = foreignKey;
|
|
168
|
+
this.obj.isNavigational = true;
|
|
169
|
+
this.obj.nullable = false;
|
|
170
|
+
return this;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// will do a inner join with foreignKey
|
|
174
|
+
//hasManyThrough("Tagging", "tag_id") ----- if foreignKey is not provided use the name of the object_id
|
|
175
|
+
hasManyThrough(foreignTable, foreignKey ){
|
|
176
|
+
if(foreignKey === undefined){
|
|
177
|
+
foreignKey = `${this.obj.name.toLowerCase()}_id`;
|
|
178
|
+
};
|
|
179
|
+
this.obj.relationshipType = "hasManyThrough";
|
|
180
|
+
this.obj.type = "hasManyThrough";
|
|
181
|
+
this.obj.foreignTable = foreignTable;// if joinKey is undefined then use name of object.
|
|
182
|
+
this.obj.foreignKey = foreignKey; // Foreign Key table
|
|
183
|
+
this.obj.isNavigational = true;
|
|
184
|
+
return this;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// will get info
|
|
188
|
+
belongsTo(foreignTable, foreignKey){
|
|
189
|
+
|
|
190
|
+
if(foreignKey === undefined){
|
|
191
|
+
foreignKey = `${foreignTable.toLowerCase()}_id`;
|
|
192
|
+
}
|
|
193
|
+
// will use table name to find forien key
|
|
194
|
+
this.obj.type = "integer";
|
|
195
|
+
this.obj.relationshipType = "belongsTo";
|
|
196
|
+
|
|
197
|
+
this.obj.foreignTable = foreignTable; // this is the table name of the current table if diffrent from the object name
|
|
198
|
+
this.obj.foreignKey = foreignKey; // this is the table name of the joining table
|
|
199
|
+
this.obj.nullable = false; // this means it cannot be null
|
|
200
|
+
return this
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
foreignKey(foreignKey){
|
|
204
|
+
this.obj.foreignKey = foreignKey;
|
|
205
|
+
this.obj.nullable = false;
|
|
206
|
+
return this
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
foreignTable(foreignTable){
|
|
210
|
+
this.obj.foreignTable = foreignTable;
|
|
211
|
+
this.obj.nullable = false;
|
|
212
|
+
return this
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
module.exports = EntityModel;
|
|
216
|
+
** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/Entity/entityModel.js **
|
|
217
|
+
// version 0.0.3
|
|
218
|
+
|
|
219
|
+
var modelDB = require('./entityModel');
|
|
220
|
+
|
|
221
|
+
// creates new instance if entity model and calls inner functions to build out a valid entity
|
|
222
|
+
class EntityModelBuilder {
|
|
223
|
+
|
|
224
|
+
static create(model){
|
|
225
|
+
if(model.name === undefined){
|
|
226
|
+
throw "dbset model declaired incorrectly. Check you dbset models for code errors."
|
|
227
|
+
}
|
|
228
|
+
var mod = new model(); //create new instance of Entity Model
|
|
229
|
+
var obj = {};
|
|
230
|
+
var methodNamesArray = Object.getOwnPropertyNames( mod.__proto__ );
|
|
231
|
+
var constructorIndex = methodNamesArray.indexOf("constructor");
|
|
232
|
+
// remove contructor method
|
|
233
|
+
if (constructorIndex > -1) {
|
|
234
|
+
methodNamesArray.splice(constructorIndex, 1);
|
|
235
|
+
}
|
|
236
|
+
// loop through all method names in the entity model
|
|
237
|
+
for (var i = 0; i < methodNamesArray.length; i++) {
|
|
238
|
+
let MDB = new modelDB(model.name); // create a new instance of entity Model class
|
|
239
|
+
mod[methodNamesArray[i]](MDB);
|
|
240
|
+
this.cleanNull(MDB.obj); // remove objects that are null or undefined
|
|
241
|
+
if(Object.keys(MDB.obj).length === 0){
|
|
242
|
+
MDB.obj.virtual = true;
|
|
243
|
+
}
|
|
244
|
+
MDB.obj.name = methodNamesArray[i];
|
|
245
|
+
obj[methodNamesArray[i]] = MDB.obj;
|
|
246
|
+
}
|
|
247
|
+
return obj;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
static cleanNull(obj) {
|
|
251
|
+
for (var propName in obj) {
|
|
252
|
+
if (obj[propName] === null) {
|
|
253
|
+
delete obj[propName];
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
module.exports = EntityModelBuilder;
|
|
261
|
+
** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/Entity/entityModelBuilder.js **
|
|
262
|
+
|
|
263
|
+
// version : 0.0.7
|
|
264
|
+
var tools = require('../Tools');
|
|
265
|
+
class EntityTrackerModel {
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
// entity states https://docs.microsoft.com/en-us/dotnet/api/system.data.entitystate?view=netframework-4.7.2
|
|
269
|
+
|
|
270
|
+
// start tracking model
|
|
271
|
+
build(dataModel, currentEntity, context){
|
|
272
|
+
var $that = this;
|
|
273
|
+
var modelClass = this.buildObject(); // build entity with models
|
|
274
|
+
modelClass.__proto__ = {};
|
|
275
|
+
const modelFields = Object.entries(dataModel); /// return array of objects
|
|
276
|
+
modelClass.__entity = currentEntity;
|
|
277
|
+
modelClass.__name = currentEntity.__name;
|
|
278
|
+
modelClass.__context = context;
|
|
279
|
+
this.buildRelationshipModels(modelClass, currentEntity, dataModel);
|
|
280
|
+
|
|
281
|
+
// loop through data model fields
|
|
282
|
+
for (const [modelField, modelFieldValue] of modelFields) {
|
|
283
|
+
|
|
284
|
+
// set the value dynamiclly
|
|
285
|
+
if(!$that._isRelationship(currentEntity[modelField])){
|
|
286
|
+
// current entity has a value then add
|
|
287
|
+
modelClass["__proto__"]["_" + modelField] = modelFieldValue;
|
|
288
|
+
|
|
289
|
+
Object.defineProperty(modelClass,modelField, {
|
|
290
|
+
set: function(value) {
|
|
291
|
+
modelClass.__state = "modified";
|
|
292
|
+
modelClass.__dirtyFields.push(modelField);
|
|
293
|
+
if(typeof currentEntity[modelField].set === "function"){
|
|
294
|
+
this["__proto__"]["_" + modelField] = currentEntity[modelField].set(value);
|
|
295
|
+
}else{
|
|
296
|
+
// Then it will add name to dirty fields
|
|
297
|
+
this["__proto__"]["_" + modelField] = value;
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
get:function(){
|
|
301
|
+
// TODO: fix only when updating
|
|
302
|
+
if(currentEntity[modelField]){
|
|
303
|
+
if(!currentEntity[modelField].skipGetFunction){
|
|
304
|
+
if(typeof currentEntity[modelField].get === "function"){
|
|
305
|
+
return currentEntity[modelField].get(this["__proto__"]["_" + modelField]);
|
|
306
|
+
}else{
|
|
307
|
+
return this["__proto__"]["_" + modelField];
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}else{
|
|
311
|
+
return this["__proto__"]["_" + modelField];
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
return modelClass;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
buildObject(){
|
|
323
|
+
return {
|
|
324
|
+
__ID : Math.floor((Math.random() * 100000) + 1),
|
|
325
|
+
__dirtyFields : [],
|
|
326
|
+
__state : "track",
|
|
327
|
+
__entity : null,
|
|
328
|
+
__context : null,
|
|
329
|
+
__name : null
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
_isRelationship(entity){
|
|
334
|
+
if(entity){
|
|
335
|
+
if(entity.type === "hasOne" || entity.type === "hasMany" || entity.relationshipType === "belongsTo" || entity.type === "hasManyThrough"){
|
|
336
|
+
return true;
|
|
337
|
+
}
|
|
338
|
+
else{
|
|
339
|
+
return false;
|
|
340
|
+
}
|
|
341
|
+
}else{
|
|
342
|
+
return false;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
buildRelationshipModels(modelClass, currentEntity, currentModel){
|
|
347
|
+
var $that = this;
|
|
348
|
+
// loop though current entity and add only relationship models to this list
|
|
349
|
+
const entityFields = Object.entries(currentEntity);
|
|
350
|
+
for (const [entityField, entityFieldValue] of entityFields) { // loop through entity values
|
|
351
|
+
|
|
352
|
+
if($that._isRelationship(currentEntity[entityField])){
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
Object.defineProperty(modelClass, entityField, {
|
|
356
|
+
set: function(value) {
|
|
357
|
+
if(typeof value === "string" || typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" ){
|
|
358
|
+
modelClass.__state = "modified";
|
|
359
|
+
modelClass.__dirtyFields.push(entityField);
|
|
360
|
+
modelClass.__context.__track(modelClass);
|
|
361
|
+
}
|
|
362
|
+
this["__proto__"]["_" + entityField] = value;
|
|
363
|
+
},
|
|
364
|
+
get : function(){
|
|
365
|
+
var ent = tools.findEntity(entityField, this.__context);
|
|
366
|
+
if(!ent){
|
|
367
|
+
var parentEntity = tools.findEntity(this.__name, this.__context);
|
|
368
|
+
if(parentEntity){
|
|
369
|
+
ent = tools.findEntity(parentEntity.__entity[entityField].foreignTable, this.__context);
|
|
370
|
+
if(!ent){
|
|
371
|
+
return `Error - Entity ${parentEntity.__entity[entityField].foreignTable} not found. Please check your context for proper name.`
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
else{
|
|
375
|
+
return `Error - Entity ${parentEntity} not found. Please check your context for proper name.`
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
if(currentEntity[entityField].relationshipType === "belongsTo"){
|
|
381
|
+
if(currentEntity[entityField].lazyLoading){
|
|
382
|
+
// TODO: UPDATE THIS CODE TO USE SOMETHING ELSE - THIS WILL NOT WORK WHEN USING DIFFERENT DATABASES BECAUSE THIS IS USING SQLITE CODE.
|
|
383
|
+
|
|
384
|
+
var name = currentEntity[entityField].foreignKey;
|
|
385
|
+
var priKey = tools.getPrimaryKeyObject(ent.__entity);
|
|
386
|
+
|
|
387
|
+
//var idValue = currentEntity[entityField].foreignKey;
|
|
388
|
+
var currentValue = this.__proto__[`_${name}`];
|
|
389
|
+
var val = this["__proto__"]["_"+entityField];
|
|
390
|
+
var modelValue = null;
|
|
391
|
+
if(!val){
|
|
392
|
+
modelValue = ent.where(`r => r.${priKey} == ${ currentValue }`).single();
|
|
393
|
+
|
|
394
|
+
}
|
|
395
|
+
else{
|
|
396
|
+
modelValue = val;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
this[entityField] = modelValue;
|
|
400
|
+
}
|
|
401
|
+
else{
|
|
402
|
+
return this["__proto__"]["_" + entityField];
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
else{
|
|
406
|
+
// user.tags = gets all tags related to user
|
|
407
|
+
// tag.users = get all users related to tags
|
|
408
|
+
if(currentEntity[entityField].lazyLoading){
|
|
409
|
+
var priKey = tools.getPrimaryKeyObject(this.__entity);
|
|
410
|
+
var entityName = currentEntity[entityField].foreignTable === undefined ? entityField : currentEntity[entityField].foreignTable;
|
|
411
|
+
var tableName = "";
|
|
412
|
+
if(entityName){
|
|
413
|
+
switch(currentEntity[entityField].type){
|
|
414
|
+
// TODO: move the SQL generation part to the SQL builder so that we can later on use many diffrent types of SQL databases.
|
|
415
|
+
case "hasManyThrough" :
|
|
416
|
+
try{
|
|
417
|
+
var joiningEntity = this.__context[tools.capitalize(entityName)];
|
|
418
|
+
var entityFieldJoinName = currentEntity[entityField].foreignTable === undefined? entityField : currentEntity[entityField].foreignTable;
|
|
419
|
+
var thirdEntity = this.__context[tools.capitalize(entityFieldJoinName)];
|
|
420
|
+
var firstJoiningID = joiningEntity.__entity[this.__entity.__name].foreignTable;
|
|
421
|
+
var secondJoiningID = joiningEntity.__entity[entityField].foreignTable;
|
|
422
|
+
if(firstJoiningID && secondJoiningID )
|
|
423
|
+
{
|
|
424
|
+
var modelValue = ent.include(`p => p.${entityFieldJoinName}.select(j => j.${joiningEntity.__entity[this.__entity.__name].foreignKey})`).include(`p =>p.${this.__entity.__name}`).where(`r =>r.${this.__entity.__name}.${priKey} = ${this[priKey]}`).toList();
|
|
425
|
+
// var modelQuery = `select ${selectParameter} from ${this.__entity.__name} INNER JOIN ${entityName} ON ${this.__entity.__name}.${priKey} = ${entityName}.${firstJoiningID} INNER JOIN ${entityField} ON ${entityField}.${joinTablePriKey} = ${entityName}.${secondJoiningID} WHERE ${this.__entity.__name}.${priKey} = ${ this[priKey]}`;
|
|
426
|
+
// var modelValue = ent.raw(modelQuery).toList();
|
|
427
|
+
this[entityField] = modelValue;
|
|
428
|
+
}
|
|
429
|
+
else{
|
|
430
|
+
return "Joining table must declaire joining table names"
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
catch(error){
|
|
434
|
+
return error;
|
|
435
|
+
}
|
|
436
|
+
/*
|
|
437
|
+
select * from User
|
|
438
|
+
INNER JOIN Tagging ON User.id = Tagging.user_id
|
|
439
|
+
INNER JOIN Tag ON Tag.id = Tagging.tag_id
|
|
440
|
+
WHERE Tagging.user_id = 13
|
|
441
|
+
*/
|
|
442
|
+
break;
|
|
443
|
+
case "hasOne" :
|
|
444
|
+
var entityName = tools.findForeignTable(this.__entity.__name, ent.__entity);
|
|
445
|
+
if(entityName){
|
|
446
|
+
tableName = entityName.foreignKey;
|
|
447
|
+
}
|
|
448
|
+
else{
|
|
449
|
+
return `Error - Entity ${ent.__entity.__name} has no property named ${this.__entity.__name}`;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
//var jj = ent.raw(`select * from ${entityName} where ${tableName} = ${ this[priKey] }`).single();
|
|
453
|
+
var modelValue = ent.where(`r => r.${tableName} == ${this[priKey]}`).single();
|
|
454
|
+
this[entityField] = modelValue;
|
|
455
|
+
break;
|
|
456
|
+
case "hasMany" :
|
|
457
|
+
var entityName = tools.findForeignTable(this.__entity.__name, ent.__entity);
|
|
458
|
+
if(entityName){
|
|
459
|
+
tableName = entityName.foreignKey;
|
|
460
|
+
}
|
|
461
|
+
else{
|
|
462
|
+
return `Error - Entity ${ent.__entity.__name} has no property named ${this.__entity.__name}`;
|
|
463
|
+
}
|
|
464
|
+
//var modelValue = ent.raw(`select * from ${entityName} where ${tableName} = ${ this[priKey] }`).toList();
|
|
465
|
+
var modelValue = ent.where(`r => r.${tableName} == ${this[priKey]}`).toList();
|
|
466
|
+
this[entityField] = modelValue;
|
|
467
|
+
break;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
else{
|
|
471
|
+
return "Entity name must be defined"
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
else{
|
|
475
|
+
return this["__proto__"]["_" + entityField];
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
return this["__proto__"]["_" + entityField];
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
if(currentEntity[entityField].relationshipType === "belongsTo"){
|
|
485
|
+
// check if entity has a value if so then return that value
|
|
486
|
+
if(currentModel[entityField]){
|
|
487
|
+
modelClass[entityField] = currentModel[entityField];
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
module.exports = EntityTrackerModel
|
|
499
|
+
** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/Entity/entityTrackerModel.js **
|
|
500
|
+
|
|
501
|
+
// https://github.com/kriasoft/node-sqlite
|
|
502
|
+
// https://www.learnentityframeworkcore.com/dbset/deleting-data
|
|
503
|
+
// version 1.0.20
|
|
504
|
+
var context = require("./context");
|
|
505
|
+
var schema = require("./Migrations/schema");
|
|
506
|
+
|
|
507
|
+
class masterrecord{
|
|
508
|
+
|
|
509
|
+
constructor(){
|
|
510
|
+
this.context = context;
|
|
511
|
+
this.schema = schema;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
module.exports = new masterrecord();
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
/*
|
|
521
|
+
|
|
522
|
+
//Create new standard
|
|
523
|
+
var standard = new Standard();
|
|
524
|
+
standard.StandardName = "Standard1";
|
|
525
|
+
|
|
526
|
+
//create three new teachers
|
|
527
|
+
var teacher1 = new Teacher();
|
|
528
|
+
teacher1.TeacherName = "New Teacher1";
|
|
529
|
+
|
|
530
|
+
var teacher2 = new Teacher();
|
|
531
|
+
teacher2.TeacherName = "New Teacher2";
|
|
532
|
+
|
|
533
|
+
var teacher3 = new Teacher();
|
|
534
|
+
teacher3.TeacherName = "New Teacher3";
|
|
535
|
+
|
|
536
|
+
//add teachers for new standard
|
|
537
|
+
standard.Teachers.Add(teacher1);
|
|
538
|
+
standard.Teachers.Add(teacher2);
|
|
539
|
+
standard.Teachers.Add(teacher3);
|
|
540
|
+
|
|
541
|
+
using (var dbCtx = new SchoolDBEntities())
|
|
542
|
+
{
|
|
543
|
+
//add standard entity into standards entitySet
|
|
544
|
+
dbCtx.Standards.Add(standard);
|
|
545
|
+
//Save whole entity graph to the database
|
|
546
|
+
dbCtx.SaveChanges();
|
|
547
|
+
}
|
|
548
|
+
*/
|
|
549
|
+
|
|
550
|
+
** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/MasterRecord.js **
|
|
551
|
+
#!/usr/bin/env node
|
|
552
|
+
|
|
553
|
+
// version 0.0.4
|
|
554
|
+
// https://docs.microsoft.com/en-us/ef/ef6/modeling/code-first/migrations/
|
|
555
|
+
// how to add environment variables on cli call example - master=development masterrecord add-migration auth authContext
|
|
556
|
+
|
|
557
|
+
const program = require('commander');
|
|
558
|
+
let fs = require('fs');
|
|
559
|
+
let path = require('path');
|
|
560
|
+
var Migration = require('./migrations');
|
|
561
|
+
var globSearch = require("glob");
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
const [,, ...args] = process.argv
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
program
|
|
569
|
+
.version('0.0.3', '-v, --version', '0.0.3')
|
|
570
|
+
.description('A ORM framework that facilitates the creation and use of business objects whose data requires persistent storage to a database');
|
|
571
|
+
|
|
572
|
+
// Instructions : to run command you must go to main project folder is located and run the command using the context file name.
|
|
573
|
+
program
|
|
574
|
+
.command('enable-migrations <contextFileName>')
|
|
575
|
+
.alias('em')
|
|
576
|
+
.description('Enables the migration in your project by creating a configuration class called ContextSnapShot.json')
|
|
577
|
+
.action(function(contextFileName){
|
|
578
|
+
|
|
579
|
+
var migration = new Migration();
|
|
580
|
+
// location of folder where command is being executed..
|
|
581
|
+
var executedLocation = process.cwd();
|
|
582
|
+
// find context file from main folder location
|
|
583
|
+
var contextInstance = migration.findContext(executedLocation, contextFileName);
|
|
584
|
+
var context = new contextInstance.context();
|
|
585
|
+
var snap = {
|
|
586
|
+
file : contextInstance.fileLocation,
|
|
587
|
+
executedLocation : executedLocation,
|
|
588
|
+
context : context,
|
|
589
|
+
contextEntities : [],
|
|
590
|
+
contextFileName: contextFileName.toLowerCase()
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
migration.createSnapShot(snap);
|
|
594
|
+
console.log("Migration enabled")
|
|
595
|
+
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
// program
|
|
599
|
+
// .command('create-database <contextFileName> <dbName>')
|
|
600
|
+
// .alias('cd')
|
|
601
|
+
// .description('allows you to create a database')
|
|
602
|
+
// .action(function(contextFileName, dbName){
|
|
603
|
+
// var executedLocation = process.cwd();
|
|
604
|
+
// contextFileName = contextFileName.toLowerCase();
|
|
605
|
+
|
|
606
|
+
// try{
|
|
607
|
+
// // find context file from main folder location
|
|
608
|
+
// // find context file from main folder location
|
|
609
|
+
// var search = `${executedLocation}/**/*${contextFileName}_contextSnapShot.json`;
|
|
610
|
+
// var files = globSearch.sync(search, executedLocation);
|
|
611
|
+
// var file = files[0];
|
|
612
|
+
|
|
613
|
+
// if(file){
|
|
614
|
+
// var contextSnapshot = require(file);
|
|
615
|
+
// var context = require(contextSnapshot.contextLocation);
|
|
616
|
+
// var newSchema = new schema(context);
|
|
617
|
+
// newSchema.createDatabase(dbName);
|
|
618
|
+
// }
|
|
619
|
+
// else{
|
|
620
|
+
// console.log("Error - Cannot read or find Context file");
|
|
621
|
+
// }
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
// }catch (e){
|
|
625
|
+
// console.log("Error - Cannot read or find file ", e);
|
|
626
|
+
// }
|
|
627
|
+
// console.log("Database Created");
|
|
628
|
+
|
|
629
|
+
// });
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
// Instructions : to run command you must go to folder where migration file is located.
|
|
633
|
+
program
|
|
634
|
+
.command('add-migration <name> <contextFileName>')
|
|
635
|
+
.alias('am')
|
|
636
|
+
.action(function(name, contextFileName){
|
|
637
|
+
var executedLocation = process.cwd();
|
|
638
|
+
contextFileName = contextFileName.toLowerCase();
|
|
639
|
+
var migration = new Migration();
|
|
640
|
+
try{
|
|
641
|
+
// find context file from main folder location
|
|
642
|
+
var search = `${executedLocation}/**/*${contextFileName}_contextSnapShot.json`;
|
|
643
|
+
var files = globSearch.sync(search, executedLocation);
|
|
644
|
+
var file = files[0];
|
|
645
|
+
if(file){
|
|
646
|
+
var contextSnapshot = require(files[0]);
|
|
647
|
+
var context = require(contextSnapshot.contextLocation);
|
|
648
|
+
var contextInstance = new context();
|
|
649
|
+
var cleanEntities = migration.cleanEntities(contextInstance.__entities);
|
|
650
|
+
var newEntity = migration.template(name, contextSnapshot.schema, cleanEntities);
|
|
651
|
+
var migrationDate = Date.now();
|
|
652
|
+
var file = `${contextSnapshot.migrationFolder}/${migrationDate}_${name}_migration.js`
|
|
653
|
+
fs.writeFile(file, newEntity, 'utf8', function (err) {
|
|
654
|
+
if (err) return console.log("--- Error running cammand, re-run command add-migration ---- ", err);
|
|
655
|
+
});
|
|
656
|
+
console.log(`${name} migration file created`);
|
|
657
|
+
}
|
|
658
|
+
else{
|
|
659
|
+
console.log("Error - Cannot read or find Context file");
|
|
660
|
+
}
|
|
661
|
+
}catch (e){
|
|
662
|
+
console.log("Error - Cannot read or find file ", e);
|
|
663
|
+
}
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
program
|
|
667
|
+
.command('update-database <contextFileName>')
|
|
668
|
+
.alias('ud')
|
|
669
|
+
.description('Apply pending migrations to database - up method call')
|
|
670
|
+
.action(function(contextFileName){
|
|
671
|
+
var executedLocation = process.cwd();
|
|
672
|
+
contextFileName = contextFileName.toLowerCase();
|
|
673
|
+
var migration = new Migration();
|
|
674
|
+
try{
|
|
675
|
+
// find context file from main folder location
|
|
676
|
+
var search = `${executedLocation}/**/*${contextFileName}_contextSnapShot.json`;
|
|
677
|
+
var files = globSearch.sync(search, executedLocation);
|
|
678
|
+
var file = files[0];
|
|
679
|
+
if(file){
|
|
680
|
+
var contextSnapshot = require(file);
|
|
681
|
+
var searchMigration = `${contextSnapshot.migrationFolder}/**/*_migration.js`;
|
|
682
|
+
var migrationFiles = globSearch.sync(searchMigration, contextSnapshot.migrationFolder);
|
|
683
|
+
if( migrationFiles){
|
|
684
|
+
|
|
685
|
+
// find newest migration file
|
|
686
|
+
// THIS DOESNT WORK BUG - hack to fix this I just take the last file which I am asuming is the newest one
|
|
687
|
+
var mFiles = migrationFiles.sort(function(x, y){
|
|
688
|
+
return new Date(x.timestamp) < new Date(y.timestamp) ? 1 : -1
|
|
689
|
+
});
|
|
690
|
+
/** ENd of BUG */
|
|
691
|
+
|
|
692
|
+
var mFile = mFiles[mFiles.length -1];
|
|
693
|
+
|
|
694
|
+
var migrationProjectFile = require(mFile);
|
|
695
|
+
var context = require(contextSnapshot.contextLocation);
|
|
696
|
+
var contextInstance = new context();
|
|
697
|
+
var newMigrationProjectInstance = new migrationProjectFile(context);
|
|
698
|
+
|
|
699
|
+
var tableObj = migration.buildUpObject(contextSnapshot.schema, contextInstance.__entities);
|
|
700
|
+
|
|
701
|
+
var cleanEntities = migration.cleanEntities(contextInstance.__entities);
|
|
702
|
+
newMigrationProjectInstance.up(tableObj);
|
|
703
|
+
|
|
704
|
+
var snap = {
|
|
705
|
+
file : contextSnapshot.contextLocation,
|
|
706
|
+
executedLocation : executedLocation,
|
|
707
|
+
context : contextInstance,
|
|
708
|
+
contextEntities : cleanEntities,
|
|
709
|
+
contextFileName: contextFileName
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
migration.createSnapShot(snap);
|
|
713
|
+
console.log("database updated");
|
|
714
|
+
}
|
|
715
|
+
else{
|
|
716
|
+
console.log("Error - Cannot read or find migration file");
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
}
|
|
720
|
+
else{
|
|
721
|
+
console.log("Error - Cannot read or find Context file");
|
|
722
|
+
}
|
|
723
|
+
}catch (e){
|
|
724
|
+
console.log("Error - Cannot read or find file ", e);
|
|
725
|
+
}
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
|
|
729
|
+
program
|
|
730
|
+
.command('update-database-restart <contextFileName>')
|
|
731
|
+
.alias('udr')
|
|
732
|
+
.description('Apply pending migrations to database - up method call')
|
|
733
|
+
.action(function(contextFileName){
|
|
734
|
+
var executedLocation = process.cwd();
|
|
735
|
+
contextFileName = contextFileName.toLowerCase();
|
|
736
|
+
var migration = new Migration();
|
|
737
|
+
try{
|
|
738
|
+
// find context file from main folder location
|
|
739
|
+
var search = `${executedLocation}/**/*${contextFileName}_contextSnapShot.json`;
|
|
740
|
+
var files = globSearch.sync(search, executedLocation);
|
|
741
|
+
var file = files[0];
|
|
742
|
+
if(file){
|
|
743
|
+
var contextSnapshot = require(file);
|
|
744
|
+
var searchMigration = `${contextSnapshot.migrationFolder}/**/*_migration.js`;
|
|
745
|
+
var migrationFiles = globSearch.sync(searchMigration, contextSnapshot.migrationFolder);
|
|
746
|
+
if( migrationFiles){
|
|
747
|
+
|
|
748
|
+
|
|
749
|
+
// organize by time
|
|
750
|
+
var mFiles = migrationFiles.sort(function(x, y){
|
|
751
|
+
return new Date(x.timestamp) < new Date(y.timestamp) ? 1 : -1;
|
|
752
|
+
});
|
|
753
|
+
var context = require(contextSnapshot.contextLocation);
|
|
754
|
+
|
|
755
|
+
for (let i = 0; i < mFiles.length; i++) {
|
|
756
|
+
var file = mFiles[i];
|
|
757
|
+
var migrationProjectFile = require(file);
|
|
758
|
+
|
|
759
|
+
var contextInstance = new context();
|
|
760
|
+
var newMigrationProjectInstance = new migrationProjectFile(context);
|
|
761
|
+
var tableObj = migration.buildUpObject(contextSnapshot.schema, contextInstance.__entities);
|
|
762
|
+
var cleanEntities = migration.cleanEntities(contextInstance.__entities);
|
|
763
|
+
newMigrationProjectInstance.up(tableObj);
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
|
|
767
|
+
var snap = {
|
|
768
|
+
file : contextSnapshot.contextLocation,
|
|
769
|
+
executedLocation : executedLocation,
|
|
770
|
+
context : contextInstance,
|
|
771
|
+
contextEntities : cleanEntities,
|
|
772
|
+
contextFileName: contextFileName
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
migration.createSnapShot(snap);
|
|
776
|
+
console.log("database updated");
|
|
777
|
+
}
|
|
778
|
+
else{
|
|
779
|
+
console.log("Error - Cannot read or find migration file");
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
}
|
|
783
|
+
else{
|
|
784
|
+
console.log("Error - Cannot read or find Context file");
|
|
785
|
+
}
|
|
786
|
+
}catch (e){
|
|
787
|
+
console.log("Error - Cannot read or find file ", e);
|
|
788
|
+
}
|
|
789
|
+
});
|
|
790
|
+
|
|
791
|
+
|
|
792
|
+
program
|
|
793
|
+
.command('get-migrations <contextFileName>')
|
|
794
|
+
.alias('gm')
|
|
795
|
+
.description('Get a list of migration file names using the context')
|
|
796
|
+
.action(function(contextFileName){
|
|
797
|
+
var executedLocation = process.cwd();
|
|
798
|
+
contextFileName = contextFileName.toLowerCase();
|
|
799
|
+
var search = `${executedLocation}/**/*${contextFileName}_contextSnapShot.json`;
|
|
800
|
+
var files = globSearch.sync(search, executedLocation);
|
|
801
|
+
var file = files[0];
|
|
802
|
+
if(file){
|
|
803
|
+
var contextSnapshot = require(file);
|
|
804
|
+
var searchMigration = `${contextSnapshot.migrationFolder}/**/*_migration.js`;
|
|
805
|
+
var migrationFiles = globSearch.sync(searchMigration, contextSnapshot.migrationFolder);
|
|
806
|
+
if( migrationFiles){
|
|
807
|
+
return migrationFiles;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
else{
|
|
811
|
+
console.log("Error - Cannot read or find Context file");
|
|
812
|
+
}
|
|
813
|
+
});
|
|
814
|
+
|
|
815
|
+
// we will find the migration folder inside the nearest app folder if no migration folder is location is added
|
|
816
|
+
program
|
|
817
|
+
.command('update-database-target <migrationFileName>')
|
|
818
|
+
.alias('udt')
|
|
819
|
+
.description('Apply pending migrations to database - down method call')
|
|
820
|
+
.action(function(migrationFileName){
|
|
821
|
+
// this will call all the down methods until it gets to the one your looking for. First it needs to validate that there is such a file.
|
|
822
|
+
// TODO:
|
|
823
|
+
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
|
|
827
|
+
|
|
828
|
+
program.parse(process.argv);
|
|
829
|
+
** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/Migrations/cli.js **
|
|
830
|
+
|
|
831
|
+
// verison 0.0.2
|
|
832
|
+
class migrationMySQLQuery {
|
|
833
|
+
|
|
834
|
+
#tempTableName = "_temp_alter_column_update"
|
|
835
|
+
|
|
836
|
+
#getTableColumns(table){
|
|
837
|
+
var columnList = [];
|
|
838
|
+
for (var key in table) {
|
|
839
|
+
if(typeof table[key] === "object"){
|
|
840
|
+
columnList.push(table[key].name);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
return columnList.join(',');;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
#columnMapping(table){
|
|
847
|
+
/*
|
|
848
|
+
var mapping = {
|
|
849
|
+
"name": "id", // if this changes then call rename column
|
|
850
|
+
"type": "integer", // if this changes then call altercolumn
|
|
851
|
+
"primary": false, // is primary key
|
|
852
|
+
"nullable": false, // is nullable
|
|
853
|
+
"unique": true, // vlaue has to be uniqe
|
|
854
|
+
"auto": true, // sets the value to AUTOINCREMENT
|
|
855
|
+
"cascadeOnDelete": true,
|
|
856
|
+
"lazyLoading": true,
|
|
857
|
+
"isNavigational": false
|
|
858
|
+
|
|
859
|
+
}
|
|
860
|
+
*/
|
|
861
|
+
// name TEXT NOT NULL,
|
|
862
|
+
|
|
863
|
+
var auto = table.auto ? " AUTO_INCREMENT":"";
|
|
864
|
+
var primaryKey = table.primary ? " PRIMARY KEY" : "";
|
|
865
|
+
var nullName = table.nullable ? "" : " NOT NULL";
|
|
866
|
+
var unique = table.unique ? " UNIQUE" : "";
|
|
867
|
+
var type = this.typeManager(table.type);
|
|
868
|
+
var tableName = table.name;
|
|
869
|
+
var defaultValue = "";
|
|
870
|
+
if(table.default != null){
|
|
871
|
+
|
|
872
|
+
defaultValue = ` DEFAULT ${this.boolType(table.default)}`
|
|
873
|
+
}
|
|
874
|
+
if(table.relationshipType === 'belongsTo'){
|
|
875
|
+
tableName = table.foreignKey;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
return `${tableName} ${type}${nullName}${defaultValue}${unique}${primaryKey}${auto}`;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
boolType(type){
|
|
882
|
+
switch(type) {
|
|
883
|
+
case "true":
|
|
884
|
+
return "1"
|
|
885
|
+
break;
|
|
886
|
+
case "false":
|
|
887
|
+
return "0"
|
|
888
|
+
break;
|
|
889
|
+
case true:
|
|
890
|
+
return "1"
|
|
891
|
+
break;
|
|
892
|
+
case false:
|
|
893
|
+
return "0"
|
|
894
|
+
break;
|
|
895
|
+
default:
|
|
896
|
+
return type;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
typeManager(type){
|
|
901
|
+
switch(type) {
|
|
902
|
+
case "string":
|
|
903
|
+
return "VARCHAR(255)"
|
|
904
|
+
break;
|
|
905
|
+
case "text":
|
|
906
|
+
return "TEXT"
|
|
907
|
+
break;
|
|
908
|
+
case "float":
|
|
909
|
+
return "fLOAT(24)"
|
|
910
|
+
break;
|
|
911
|
+
case "decimal":
|
|
912
|
+
return "DECIMAL"
|
|
913
|
+
break;
|
|
914
|
+
case "datetime":
|
|
915
|
+
return "DATETIME"
|
|
916
|
+
break;
|
|
917
|
+
case "timestamp":
|
|
918
|
+
return "TIMESTAMP"
|
|
919
|
+
break;
|
|
920
|
+
case "date":
|
|
921
|
+
return "DATE"
|
|
922
|
+
break;
|
|
923
|
+
case "time":
|
|
924
|
+
return "TIME"
|
|
925
|
+
break;
|
|
926
|
+
case "boolean":
|
|
927
|
+
return "TINYINT"
|
|
928
|
+
break;
|
|
929
|
+
case "integer":
|
|
930
|
+
return "INTEGER"
|
|
931
|
+
break;
|
|
932
|
+
case "binary":
|
|
933
|
+
return "BLOB"
|
|
934
|
+
break;
|
|
935
|
+
case "blob":
|
|
936
|
+
return "BLOB"
|
|
937
|
+
break;
|
|
938
|
+
case "json":
|
|
939
|
+
return "JSON"
|
|
940
|
+
break;
|
|
941
|
+
}
|
|
942
|
+
// :string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
// table is the altered field
|
|
946
|
+
alterColumn( table){
|
|
947
|
+
|
|
948
|
+
if(table){
|
|
949
|
+
|
|
950
|
+
return `ALTER TABLE ${table.tableName} MODIFY COLUMN ${this.#columnMapping(table.table)} `;
|
|
951
|
+
}
|
|
952
|
+
else{
|
|
953
|
+
console.log("table information is null");
|
|
954
|
+
return null;
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
alterNullable(table){
|
|
959
|
+
// check if has value
|
|
960
|
+
if(typeof table.changes.nullable !== 'undefined'){
|
|
961
|
+
// if it does we want to add that to the alter statment
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
|
|
966
|
+
addColum(table){
|
|
967
|
+
return `ALTER TABLE ${table.tableName}
|
|
968
|
+
ADD ${table.name} ${table.realDataType}`;
|
|
969
|
+
|
|
970
|
+
/*
|
|
971
|
+
column definations
|
|
972
|
+
NULL
|
|
973
|
+
TEXT. The value is a text string, stored using the database encoding (UTF-8, UTF-16BE or UTF-16LE).
|
|
974
|
+
BLOB. The value is a blob of data, stored exactly as it was input
|
|
975
|
+
INTEGER,
|
|
976
|
+
real
|
|
977
|
+
*/
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
dropColumn(table){
|
|
981
|
+
/*
|
|
982
|
+
COLUMNS CANNOT BE DROPPED - RULES
|
|
983
|
+
has unique constraint
|
|
984
|
+
is indexed
|
|
985
|
+
appears in a view
|
|
986
|
+
*/
|
|
987
|
+
return `ALTER TABLE ${table.tableName} DROP COLUMN ${table.name}`;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
insertInto(name, table){
|
|
991
|
+
return `INSERT INTO ${name} (${this.#getTableColumns(table)})
|
|
992
|
+
SELECT ${this.#getTableColumns(table)} FROM ${this.#tempTableName}`;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
createTable(table){
|
|
996
|
+
|
|
997
|
+
var queryVar = "";
|
|
998
|
+
//console.log("Dsfdsfdsf---------", table)
|
|
999
|
+
for (var key in table) {
|
|
1000
|
+
if(typeof table[key] === "object"){
|
|
1001
|
+
|
|
1002
|
+
if(table[key].type !== "hasOne" && table[key].type !== "hasMany" && table[key].type !== "hasManyThrough"){
|
|
1003
|
+
queryVar += `${this.#columnMapping(table[key])}, `;
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
var completeQuery = `CREATE TABLE ${table.__name} (${queryVar.replace(/,\s*$/, "")});`;
|
|
1009
|
+
return completeQuery;
|
|
1010
|
+
|
|
1011
|
+
/*
|
|
1012
|
+
INTEGER PRIMARY KEY AUTOINCREMENT
|
|
1013
|
+
all these are equal to interger
|
|
1014
|
+
INT
|
|
1015
|
+
INTEGER
|
|
1016
|
+
TINYINT
|
|
1017
|
+
SMALLINT
|
|
1018
|
+
MEDIUMINT
|
|
1019
|
+
BIGINT
|
|
1020
|
+
UNSIGNED BIG INT
|
|
1021
|
+
INT2
|
|
1022
|
+
INT8
|
|
1023
|
+
*/
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
|
|
1027
|
+
dropTable(name){
|
|
1028
|
+
return `DROP TABLE ${name}`
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
renameTable(table){
|
|
1032
|
+
return `ALTER TABLE ${table.tableName} RENAME TO ${table.newName}`;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
renameColumn(table){
|
|
1036
|
+
return `ALTER TABLE ${table.tableName} RENAME COLUMN ${table.name} TO ${table.newName}`
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
|
|
1043
|
+
module.exports = migrationMySQLQuery;
|
|
1044
|
+
|
|
1045
|
+
/**
|
|
1046
|
+
*
|
|
1047
|
+
*
|
|
1048
|
+
*
|
|
1049
|
+
*
|
|
1050
|
+
MySQL Data Types NATIVE_DATABASE_TYPES = {
|
|
1051
|
+
primary_key: "bigint auto_increment PRIMARY KEY",
|
|
1052
|
+
string: { name: "varchar", limit: 255 },
|
|
1053
|
+
text: { name: "text" },
|
|
1054
|
+
integer: { name: "int", limit: 4 },
|
|
1055
|
+
float: { name: "float", limit: 24 },
|
|
1056
|
+
decimal: { name: "decimal" },
|
|
1057
|
+
datetime: { name: "datetime" },
|
|
1058
|
+
timestamp: { name: "timestamp" },
|
|
1059
|
+
time: { name: "time" },
|
|
1060
|
+
date: { name: "date" },
|
|
1061
|
+
binary: { name: "blob" },
|
|
1062
|
+
blob: { name: "blob" },
|
|
1063
|
+
boolean: { name: "tinyint", limit: 1 },
|
|
1064
|
+
json: { name: "json" },
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
|
|
1068
|
+
PostgreSQL Data Types NATIVE_DATABASE_TYPES = {
|
|
1069
|
+
primary_key: "bigserial primary key",
|
|
1070
|
+
string: { name: "character varying" },
|
|
1071
|
+
text: { name: "text" },
|
|
1072
|
+
integer: { name: "integer", limit: 4 },
|
|
1073
|
+
float: { name: "float" },
|
|
1074
|
+
decimal: { name: "decimal" },
|
|
1075
|
+
datetime: {}, # set dynamically based on datetime_type
|
|
1076
|
+
timestamp: { name: "timestamp" },
|
|
1077
|
+
timestamptz: { name: "timestamptz" },
|
|
1078
|
+
time: { name: "time" },
|
|
1079
|
+
date: { name: "date" },
|
|
1080
|
+
daterange: { name: "daterange" },
|
|
1081
|
+
numrange: { name: "numrange" },
|
|
1082
|
+
tsrange: { name: "tsrange" },
|
|
1083
|
+
tstzrange: { name: "tstzrange" },
|
|
1084
|
+
int4range: { name: "int4range" },
|
|
1085
|
+
int8range: { name: "int8range" },
|
|
1086
|
+
binary: { name: "bytea" },
|
|
1087
|
+
boolean: { name: "boolean" },
|
|
1088
|
+
xml: { name: "xml" },
|
|
1089
|
+
tsvector: { name: "tsvector" },
|
|
1090
|
+
hstore: { name: "hstore" },
|
|
1091
|
+
inet: { name: "inet" },
|
|
1092
|
+
cidr: { name: "cidr" },
|
|
1093
|
+
macaddr: { name: "macaddr" },
|
|
1094
|
+
uuid: { name: "uuid" },
|
|
1095
|
+
json: { name: "json" },
|
|
1096
|
+
jsonb: { name: "jsonb" },
|
|
1097
|
+
ltree: { name: "ltree" },
|
|
1098
|
+
citext: { name: "citext" },
|
|
1099
|
+
point: { name: "point" },
|
|
1100
|
+
line: { name: "line" },
|
|
1101
|
+
lseg: { name: "lseg" },
|
|
1102
|
+
box: { name: "box" },
|
|
1103
|
+
path: { name: "path" },
|
|
1104
|
+
polygon: { name: "polygon" },
|
|
1105
|
+
circle: { name: "circle" },
|
|
1106
|
+
bit: { name: "bit" },
|
|
1107
|
+
bit_varying: { name: "bit varying" },
|
|
1108
|
+
money: { name: "money" },
|
|
1109
|
+
interval: { name: "interval" },
|
|
1110
|
+
oid: { name: "oid" },
|
|
1111
|
+
}
|
|
1112
|
+
*/
|
|
1113
|
+
|
|
1114
|
+
|
|
1115
|
+
/****
|
|
1116
|
+
*
|
|
1117
|
+
*
|
|
1118
|
+
* console.log("sdfdsfdsf", this.#tempTableName);
|
|
1119
|
+
return `ALTER TABLE ${table.tableName} MODIFY COLUMN NOT NULL`
|
|
1120
|
+
TODO -- We need to find a way build the alter query based on the data that is changed
|
|
1121
|
+
//ALTER TABLE MyTable MODIFY COLUMN comment BIGINT NOT NULL;
|
|
1122
|
+
if(table){
|
|
1123
|
+
table.newName = this.#tempTableName;
|
|
1124
|
+
//console.log("----------------------", table)
|
|
1125
|
+
return {
|
|
1126
|
+
1 : this.renameTable(table),
|
|
1127
|
+
2 : this.createTable(fullTable),
|
|
1128
|
+
3 : this.insertInto(table.tableName, fullTable),
|
|
1129
|
+
4 : this.dropTable(this.#tempTableName)
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
else{
|
|
1133
|
+
console.log("table information is null")
|
|
1134
|
+
}
|
|
1135
|
+
*/
|
|
1136
|
+
** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/Migrations/migrationMySQLQuery.js **
|
|
1137
|
+
|
|
1138
|
+
// verison 0.0.5
|
|
1139
|
+
class migrationSQLiteQuery {
|
|
1140
|
+
|
|
1141
|
+
#tempTableName = "_temp_alter_column_update"
|
|
1142
|
+
|
|
1143
|
+
#getTableColumns(table){
|
|
1144
|
+
var columnList = [];
|
|
1145
|
+
for (var key in table) {
|
|
1146
|
+
if(typeof table[key] === "object"){
|
|
1147
|
+
columnList.push(table[key].name);
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
return columnList.join(',');;
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
#columnMapping(table){
|
|
1154
|
+
/*
|
|
1155
|
+
var mapping = {
|
|
1156
|
+
"name": "id", // if this chnages then call rename column
|
|
1157
|
+
"type": "integer", // if this changes then call altercolumn
|
|
1158
|
+
"primary": false, // is primary key
|
|
1159
|
+
"nullable": false, // is nullable
|
|
1160
|
+
"unique": true, // vlaue has to be uniqe
|
|
1161
|
+
"auto": true, // sets the value to AUTOINCREMENT
|
|
1162
|
+
"cascadeOnDelete": true,
|
|
1163
|
+
"lazyLoading": true,
|
|
1164
|
+
"isNavigational": false
|
|
1165
|
+
|
|
1166
|
+
}
|
|
1167
|
+
*/
|
|
1168
|
+
// name TEXT NOT NULL,
|
|
1169
|
+
|
|
1170
|
+
var auto = table.auto ? " AUTOINCREMENT":"";
|
|
1171
|
+
var primaryKey = table.primary ? " PRIMARY KEY" : "";
|
|
1172
|
+
var nullName = table.nullable ? "" : " NOT NULL";
|
|
1173
|
+
var unique = table.unique ? " UNIQUE" : "";
|
|
1174
|
+
var type = this.#typeManager(table.type);
|
|
1175
|
+
|
|
1176
|
+
return `${table.name} ${type}${nullName}${unique}${primaryKey}${auto}`;
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
#typeManager(type){
|
|
1180
|
+
switch(type) {
|
|
1181
|
+
case "string":
|
|
1182
|
+
return "TEXT"
|
|
1183
|
+
break;
|
|
1184
|
+
case "time":
|
|
1185
|
+
return "TEXT"
|
|
1186
|
+
break;
|
|
1187
|
+
case "boolean":
|
|
1188
|
+
return "INTEGER"
|
|
1189
|
+
break;
|
|
1190
|
+
case "integer":
|
|
1191
|
+
return "INTEGER"
|
|
1192
|
+
break;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
alterColumn(fullTable, table){
|
|
1198
|
+
if(table){
|
|
1199
|
+
table.newName = this.#tempTableName;
|
|
1200
|
+
return {
|
|
1201
|
+
1 : this.renameTable(table),
|
|
1202
|
+
2 : this.createTable(fullTable),
|
|
1203
|
+
3 : this.insertInto(table.tableName, fullTable),
|
|
1204
|
+
4 : this.dropTable(this.#tempTableName)
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
else{
|
|
1208
|
+
console.log("table information is null")
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
|
|
1213
|
+
addColum(table){
|
|
1214
|
+
return `ALTER TABLE ${table.tableName}
|
|
1215
|
+
ADD COLUMN ${table.name}`;
|
|
1216
|
+
|
|
1217
|
+
/*
|
|
1218
|
+
column definations
|
|
1219
|
+
NULL
|
|
1220
|
+
TEXT. The value is a text string, stored using the database encoding (UTF-8, UTF-16BE or UTF-16LE).
|
|
1221
|
+
BLOB. The value is a blob of data, stored exactly as it was input
|
|
1222
|
+
INTEGER,
|
|
1223
|
+
real
|
|
1224
|
+
*/
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
dropColumn(table){
|
|
1228
|
+
/*
|
|
1229
|
+
COLUMNS CANNOT BE DROPPED - RULES
|
|
1230
|
+
has unique constraint
|
|
1231
|
+
is indexed
|
|
1232
|
+
appears in a view
|
|
1233
|
+
*/
|
|
1234
|
+
return `ALTER TABLE ${table.tableName} DROP COLUMN ${table.name}`
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
insertInto(name, table){
|
|
1238
|
+
return `INSERT INTO ${name} (${this.#getTableColumns(table)})
|
|
1239
|
+
SELECT ${this.#getTableColumns(table)} FROM ${this.#tempTableName}`;
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
createTable(table){
|
|
1243
|
+
var queryVar = "";
|
|
1244
|
+
for (var key in table) {
|
|
1245
|
+
if(typeof table[key] === "object"){
|
|
1246
|
+
queryVar += `${this.#columnMapping(table[key])}, `;
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
return `CREATE TABLE ${table.__name} (${queryVar.replace(/,\s*$/, "")});`;
|
|
1251
|
+
|
|
1252
|
+
/*
|
|
1253
|
+
INTEGER PRIMARY KEY AUTOINCREMENT
|
|
1254
|
+
all these are equal to interger
|
|
1255
|
+
INT
|
|
1256
|
+
INTEGER
|
|
1257
|
+
TINYINT
|
|
1258
|
+
SMALLINT
|
|
1259
|
+
MEDIUMINT
|
|
1260
|
+
BIGINT
|
|
1261
|
+
UNSIGNED BIG INT
|
|
1262
|
+
INT2
|
|
1263
|
+
INT8
|
|
1264
|
+
*/
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
|
|
1268
|
+
dropTable(name){
|
|
1269
|
+
return `DROP TABLE ${name}`
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
renameTable(table){
|
|
1273
|
+
return `ALTER TABLE ${table.tableName} RENAME TO ${table.newName}`;
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
renameColumn(table){
|
|
1277
|
+
return `ALTER TABLE ${table.tableName} RENAME COLUMN ${table.name} TO ${table.newName}`
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
|
|
1284
|
+
module.exports = migrationSQLiteQuery;
|
|
1285
|
+
|
|
1286
|
+
|
|
1287
|
+
/*
|
|
1288
|
+
ADDING NEW COLUMN SQLITE
|
|
1289
|
+
There are some restrictions on the new column:
|
|
1290
|
+
The new column cannot have a UNIQUE or PRIMARY KEY constraint.
|
|
1291
|
+
If the new column has a NOT NULL constraint, you must specify a default value for the column other than a NULL value.
|
|
1292
|
+
The new column cannot have a default of CURRENT_TIMESTAMP, CURRENT_DATE, and CURRENT_TIME, or an expression.
|
|
1293
|
+
If the new column is a foreign key and the foreign key constraint check is enabled, the new column must accept a default value NULL.
|
|
1294
|
+
|
|
1295
|
+
*/
|
|
1296
|
+
|
|
1297
|
+
/*
|
|
1298
|
+
|
|
1299
|
+
DROPING A COLUMN SQLITE
|
|
1300
|
+
Possible reasons why the DROP COLUMN command can fail include:
|
|
1301
|
+
|
|
1302
|
+
The column is a PRIMARY KEY or part of one.
|
|
1303
|
+
The column has a UNIQUE constraint.
|
|
1304
|
+
The column is indexed.
|
|
1305
|
+
The column is named in the WHERE clause of a partial index.
|
|
1306
|
+
The column is named in a table or column CHECK constraint not associated with the column being dropped.
|
|
1307
|
+
The column is used in a foreign key constraint.
|
|
1308
|
+
The column is used in the expression of a generated column.
|
|
1309
|
+
The column appears in a trigger or view.
|
|
1310
|
+
|
|
1311
|
+
*/
|
|
1312
|
+
** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/Migrations/migrationSQLiteQuery.js **
|
|
1313
|
+
|
|
1314
|
+
|
|
1315
|
+
// https://channel9.msdn.com/Blogs/EF/Migrations-Under-the-Hood
|
|
1316
|
+
// version 0.0.3
|
|
1317
|
+
|
|
1318
|
+
const os = require('os');
|
|
1319
|
+
class MigrationTemplate {
|
|
1320
|
+
|
|
1321
|
+
constructor(name) {
|
|
1322
|
+
this.name = name;
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
#up = ''
|
|
1326
|
+
#down = ''
|
|
1327
|
+
|
|
1328
|
+
get(){
|
|
1329
|
+
return `
|
|
1330
|
+
|
|
1331
|
+
var masterrecord = require('masterrecord');
|
|
1332
|
+
|
|
1333
|
+
class ${this.name} extends masterrecord.schema {
|
|
1334
|
+
constructor(context){
|
|
1335
|
+
super(context);
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
up(table){
|
|
1339
|
+
this.init(table);
|
|
1340
|
+
${this.#up}
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
down(table){
|
|
1344
|
+
this.init(table);
|
|
1345
|
+
${this.#down}
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
module.exports = ${this.name};
|
|
1349
|
+
`
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
alterColumn(type, name, parent){
|
|
1353
|
+
if(type === "up"){
|
|
1354
|
+
this.#up += os.EOL + ` this.alterColumn(table.${name});`
|
|
1355
|
+
}
|
|
1356
|
+
else{
|
|
1357
|
+
this.#down += os.EOL + ` this.alterColumn(table.${name});`
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
createTable(type, name){
|
|
1361
|
+
if(type === "up"){
|
|
1362
|
+
this.#up += os.EOL + ` this.createTable(table.${name});`
|
|
1363
|
+
}
|
|
1364
|
+
else{
|
|
1365
|
+
this.#down += os.EOL + ` this.createTable(table.${name});`
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
addColumn(type, name, parent){
|
|
1370
|
+
if(type === "up"){
|
|
1371
|
+
this.#up += os.EOL + ` this.addColumn(table.${name});`
|
|
1372
|
+
}
|
|
1373
|
+
else{
|
|
1374
|
+
this.#down += os.EOL + ` this.addColumn(table.${name});`
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
//this.addColumn(table.${parent}.${name});`
|
|
1378
|
+
|
|
1379
|
+
dropTable(type, name){
|
|
1380
|
+
if(type === "up"){
|
|
1381
|
+
this.#down += os.EOL + ` this.droptable(table.${name});`
|
|
1382
|
+
}
|
|
1383
|
+
else{
|
|
1384
|
+
this.#down += os.EOL + ` this.droptable(table.${name});`
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
dropColumn(type, name, parent){
|
|
1389
|
+
if(type === "up"){
|
|
1390
|
+
this.#up += os.EOL + ` this.dropColumn(table.${name});`
|
|
1391
|
+
}
|
|
1392
|
+
else{
|
|
1393
|
+
this.#down += os.EOL + ` this.dropColumn(table.${name});`
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
module.exports = MigrationTemplate;
|
|
1400
|
+
|
|
1401
|
+
|
|
1402
|
+
** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/Migrations/migrationTemplate.js **
|
|
1403
|
+
// version 0.0.8
|
|
1404
|
+
// learn more about seeding info - https://www.pauric.blog/Database-Updates-and-Migrations-with-Entity-Framework/
|
|
1405
|
+
|
|
1406
|
+
var fs = require('fs');
|
|
1407
|
+
var diff = require("deep-object-diff");
|
|
1408
|
+
var MigrationTemplate = require("./migrationTemplate");
|
|
1409
|
+
var globSearch = require("glob");
|
|
1410
|
+
|
|
1411
|
+
// https://blog.tekspace.io/code-first-multiple-db-context-migration/
|
|
1412
|
+
|
|
1413
|
+
// node masterrecord add-migration josh C:\Users\rbatista\Downloads\kollege\freshmen\app\models\context
|
|
1414
|
+
class Migrations{
|
|
1415
|
+
|
|
1416
|
+
#organizeSchemaByTables(oldSchema, newSchema){
|
|
1417
|
+
var tables = []
|
|
1418
|
+
if(oldSchema.length === 0){
|
|
1419
|
+
newSchema.forEach(function (item, index) {
|
|
1420
|
+
var table = {
|
|
1421
|
+
name: item["__name"],
|
|
1422
|
+
new :item,
|
|
1423
|
+
old : {},
|
|
1424
|
+
newColumns : [],
|
|
1425
|
+
newTables : [],
|
|
1426
|
+
deletedColumns : [],
|
|
1427
|
+
updatedColumns : []
|
|
1428
|
+
}
|
|
1429
|
+
tables.push(table);
|
|
1430
|
+
});
|
|
1431
|
+
}
|
|
1432
|
+
else{
|
|
1433
|
+
newSchema.forEach(function (item, index) {
|
|
1434
|
+
var table = {
|
|
1435
|
+
name: item["__name"],
|
|
1436
|
+
old: null,
|
|
1437
|
+
new :item,
|
|
1438
|
+
newColumns : [],
|
|
1439
|
+
newTables : [],
|
|
1440
|
+
deletedColumns : [],
|
|
1441
|
+
updatedColumns : []
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
oldSchema.forEach(function (oldItem, index) {
|
|
1445
|
+
var oldItemName = oldItem["__name"];
|
|
1446
|
+
if(table.name === oldItemName){
|
|
1447
|
+
table.old = oldItem;
|
|
1448
|
+
tables.push(table);
|
|
1449
|
+
}
|
|
1450
|
+
});
|
|
1451
|
+
|
|
1452
|
+
});
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
return tables;
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
#findDeletedColumns(tables){
|
|
1459
|
+
tables.forEach(function (item, index) {
|
|
1460
|
+
var deletedColumn = null;
|
|
1461
|
+
if(item.new && item.old){
|
|
1462
|
+
Object.keys(item.old).forEach(function (key) {
|
|
1463
|
+
var value = item.old[key].name;
|
|
1464
|
+
deletedColumn = null;
|
|
1465
|
+
Object.keys(item.new).forEach(function (newKey) {
|
|
1466
|
+
var newValue = item.new[newKey].name;
|
|
1467
|
+
if(value === newValue){
|
|
1468
|
+
deletedColumn = value;
|
|
1469
|
+
}
|
|
1470
|
+
});
|
|
1471
|
+
if(deletedColumn === null){
|
|
1472
|
+
item.deletedColumns.push(value);
|
|
1473
|
+
}
|
|
1474
|
+
});
|
|
1475
|
+
}
|
|
1476
|
+
});
|
|
1477
|
+
return tables;
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
#findUpdatedColumns(tables){
|
|
1481
|
+
|
|
1482
|
+
|
|
1483
|
+
tables.forEach(function (item, index) {
|
|
1484
|
+
|
|
1485
|
+
var UD = diff.updatedDiff(item.old, item.new);
|
|
1486
|
+
const isEmpty = Object.keys(UD).length === 0;
|
|
1487
|
+
if(!isEmpty){
|
|
1488
|
+
for (var key in UD) {
|
|
1489
|
+
var tableChanges = {
|
|
1490
|
+
changes : UD[key],
|
|
1491
|
+
table : item.new[key],
|
|
1492
|
+
tableName : item.name
|
|
1493
|
+
};
|
|
1494
|
+
item.updatedColumns.push(tableChanges);
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
});
|
|
1499
|
+
return tables;
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
#findNewColumns(tables){
|
|
1503
|
+
tables.forEach(function (item, index) {
|
|
1504
|
+
if(item.new && item.old){
|
|
1505
|
+
Object.keys(item.new).forEach(function (key) {
|
|
1506
|
+
if(typeof item.new[key] === "object"){
|
|
1507
|
+
var value = item.new[key].name;
|
|
1508
|
+
var columnNotFound = false;
|
|
1509
|
+
Object.keys(item.old).forEach(function (oldKey) {
|
|
1510
|
+
if(typeof item.old[oldKey] === "object"){
|
|
1511
|
+
var oldValue = item.old[oldKey].name;
|
|
1512
|
+
if(value === oldValue){
|
|
1513
|
+
columnNotFound = true;
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
});
|
|
1517
|
+
|
|
1518
|
+
if(columnNotFound === false){
|
|
1519
|
+
// this means it did not find the column
|
|
1520
|
+
|
|
1521
|
+
if(item.new[key].type !== "hasOne" && item.new[key].type !== "hasMany" && item.new[key].type !== "hasManyThrough"){
|
|
1522
|
+
// if you have to create a new table no need to create the columns
|
|
1523
|
+
if(item.newTables.length === 0){
|
|
1524
|
+
item.newColumns.push(value);
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
});
|
|
1532
|
+
}
|
|
1533
|
+
else{
|
|
1534
|
+
console.log("Table object has no old or new values");
|
|
1535
|
+
}
|
|
1536
|
+
});
|
|
1537
|
+
return tables;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
#findNewTables(tables){
|
|
1541
|
+
// find new tables
|
|
1542
|
+
tables.forEach(function (item, index) {
|
|
1543
|
+
if(item.new && item.old){
|
|
1544
|
+
if(Object.keys(item.old).length === 0){
|
|
1545
|
+
item.newTables.push(item);
|
|
1546
|
+
}
|
|
1547
|
+
}else{
|
|
1548
|
+
console.log("Cannot find NEW or and Old Objects");
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
});
|
|
1552
|
+
return tables;
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
// build table to build new migration snapshot
|
|
1556
|
+
#buildMigrationObject(oldSchema, newSchema){
|
|
1557
|
+
|
|
1558
|
+
var tables = this.#organizeSchemaByTables(oldSchema, newSchema);
|
|
1559
|
+
|
|
1560
|
+
tables = this.#findNewTables(tables);
|
|
1561
|
+
tables = this.#findNewColumns(tables);
|
|
1562
|
+
tables = this.#findDeletedColumns(tables);
|
|
1563
|
+
tables = this.#findUpdatedColumns(tables);
|
|
1564
|
+
return tables;
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
|
|
1568
|
+
|
|
1569
|
+
findContext(executedLocation, contextFileName){
|
|
1570
|
+
var search = `${executedLocation}/**/*${contextFileName}.js`
|
|
1571
|
+
var files = globSearch.sync(search, executedLocation);
|
|
1572
|
+
var file = files[0];
|
|
1573
|
+
var context = require(file);
|
|
1574
|
+
return {
|
|
1575
|
+
context : context,
|
|
1576
|
+
fileLocation : file
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
// remove hasMany and hasOne and hasManyThrough
|
|
1581
|
+
cleanEntities(entities){
|
|
1582
|
+
var newEntity = [];
|
|
1583
|
+
for (let i = 0; i < entities.length; i++) {
|
|
1584
|
+
var entity = entities[i];
|
|
1585
|
+
var newObj = {}
|
|
1586
|
+
|
|
1587
|
+
for (let key in entity) {
|
|
1588
|
+
if (entity.hasOwnProperty(key)) {
|
|
1589
|
+
|
|
1590
|
+
if(entity[key].type !== "hasOne" && entity[key].type !== "hasMany" && entity[key].type !== "hasManyThrough"){
|
|
1591
|
+
// if(entity[key].relationshipType == "belongsTo" ){
|
|
1592
|
+
// entity[key].name = entity[key].foreignKey;
|
|
1593
|
+
// }
|
|
1594
|
+
newObj[key] = entity[key];
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
newEntity.push(newObj);
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
return newEntity;
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
createSnapShot(snap){
|
|
1605
|
+
|
|
1606
|
+
var dbFolder = `${snap.executedLocation}/db`;
|
|
1607
|
+
if (!fs.existsSync(dbFolder)){
|
|
1608
|
+
fs.mkdirSync(dbFolder);
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
var migrationsDirectory = `${snap.executedLocation}/db/migrations`;
|
|
1612
|
+
if (!fs.existsSync(migrationsDirectory)){
|
|
1613
|
+
fs.mkdirSync(migrationsDirectory);
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
var content = {
|
|
1617
|
+
contextLocation: snap.file,
|
|
1618
|
+
migrationFolder: `${snap.executedLocation}/db/migrations`,
|
|
1619
|
+
snapShotLocation: `${snap.executedLocation}/db/migrations/${snap.contextFileName}_contextSnapShot.json`,
|
|
1620
|
+
schema : snap.contextEntities
|
|
1621
|
+
};
|
|
1622
|
+
|
|
1623
|
+
const jsonContent = JSON.stringify(content, null, 2);
|
|
1624
|
+
try{
|
|
1625
|
+
// will replace the whole file if it exist
|
|
1626
|
+
fs.writeFileSync(`${migrationsDirectory}/${snap.contextFileName}_contextSnapShot.json`, jsonContent);
|
|
1627
|
+
}catch (e){
|
|
1628
|
+
console.log("Cannot write file ", e);
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
// validate if schema has changed based on new and old
|
|
1633
|
+
buildUpObject(oldSchema, newSchema){
|
|
1634
|
+
var tableObj = {}
|
|
1635
|
+
var tables = this.#buildMigrationObject(oldSchema, newSchema);
|
|
1636
|
+
|
|
1637
|
+
tables.forEach(function (item, index) {
|
|
1638
|
+
// add new columns for table
|
|
1639
|
+
var columnInfo = tables[index];
|
|
1640
|
+
|
|
1641
|
+
item.newTables.forEach(function (column, ind) {
|
|
1642
|
+
tableObj[item.name] = columnInfo.new;
|
|
1643
|
+
});
|
|
1644
|
+
|
|
1645
|
+
item.newColumns.forEach(function (column, ind) {
|
|
1646
|
+
columnInfo.new[column].tableName = item.name;
|
|
1647
|
+
tableObj[column] = columnInfo.new[column];
|
|
1648
|
+
});
|
|
1649
|
+
|
|
1650
|
+
item.deletedColumns.forEach(function (column, ind) {
|
|
1651
|
+
columnInfo.old[column].tableName = item.name;
|
|
1652
|
+
tableObj[column] = columnInfo.old[column];
|
|
1653
|
+
});
|
|
1654
|
+
|
|
1655
|
+
item.updatedColumns.forEach(function (column, ind) {
|
|
1656
|
+
tableObj[column.table.name] = column;
|
|
1657
|
+
});
|
|
1658
|
+
|
|
1659
|
+
if(item.new === null){
|
|
1660
|
+
columnInfo.old.tableName = item.name;
|
|
1661
|
+
tableObj["new"] = columnInfo.old;
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
tableObj.___table = item;
|
|
1665
|
+
});
|
|
1666
|
+
return tableObj;
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
template(name, oldSchema, newSchema){
|
|
1670
|
+
var MT = new MigrationTemplate(name);
|
|
1671
|
+
var tables = this.#buildMigrationObject(oldSchema, newSchema);
|
|
1672
|
+
|
|
1673
|
+
tables.forEach(function (item, index) {
|
|
1674
|
+
if(item.old === null){
|
|
1675
|
+
MT.createTable("up", column, item.name);
|
|
1676
|
+
MT.dropTable("down", column, item.name);
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
if(item.new === null){
|
|
1680
|
+
MT.dropTable("up", column, item.name);
|
|
1681
|
+
MT.createTable("down", column, item.name);
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
item.newTables.forEach(function (column, ind) {
|
|
1685
|
+
MT.createTable("up", item.name);
|
|
1686
|
+
MT.dropTable("down", item.name);
|
|
1687
|
+
});
|
|
1688
|
+
|
|
1689
|
+
// add new columns for table
|
|
1690
|
+
item.newColumns.forEach(function (column, index) {
|
|
1691
|
+
MT.addColumn("up", column, item.name);
|
|
1692
|
+
MT.dropColumn("down", column, item.name);
|
|
1693
|
+
});
|
|
1694
|
+
|
|
1695
|
+
item.deletedColumns.forEach(function (column, index) {
|
|
1696
|
+
MT.dropColumn("up", column, item.name);
|
|
1697
|
+
MT.addColumn("down",column, item.name);
|
|
1698
|
+
});
|
|
1699
|
+
|
|
1700
|
+
item.updatedColumns.forEach(function (column, index) {
|
|
1701
|
+
const isEmpty = Object.keys(column).length === 0;
|
|
1702
|
+
if(!isEmpty){
|
|
1703
|
+
MT.alterColumn("up", column.table.name, item.name);
|
|
1704
|
+
MT.alterColumn("down", column.table.name, item.name);
|
|
1705
|
+
}
|
|
1706
|
+
});
|
|
1707
|
+
|
|
1708
|
+
});
|
|
1709
|
+
|
|
1710
|
+
return MT.get();
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
module.exports = Migrations;
|
|
1717
|
+
** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/Migrations/migrations.js **
|
|
1718
|
+
// version 0.0.4
|
|
1719
|
+
class schema{
|
|
1720
|
+
|
|
1721
|
+
constructor(context){
|
|
1722
|
+
this.context = new context();
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
|
|
1726
|
+
init(table){
|
|
1727
|
+
if(table){
|
|
1728
|
+
this.fullTable = table.___table;
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
// create obj to convert into create sql
|
|
1733
|
+
addColumn(table){
|
|
1734
|
+
// todo need to work on add column for mysql
|
|
1735
|
+
if(table){
|
|
1736
|
+
if(this.context.isSQLite){
|
|
1737
|
+
var sqliteQuery = require("./migrationSQLiteQuery");
|
|
1738
|
+
var queryBuilder = new sqliteQuery();
|
|
1739
|
+
var queryObj = queryBuilder.alterColumn(this.fullTable.new, table);
|
|
1740
|
+
for (var key in queryObj) {
|
|
1741
|
+
var query = queryObj[key];
|
|
1742
|
+
this.context._execute(query);
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
if(this.context.isMySQL){
|
|
1747
|
+
var sqlquery = require("./migrationMySQLQuery");
|
|
1748
|
+
var queryBuilder = new sqlquery();
|
|
1749
|
+
table.realDataType = queryBuilder.typeManager(table.type);
|
|
1750
|
+
var query = queryBuilder.addColum(table);
|
|
1751
|
+
this.context._execute(query);
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
// add column to database
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1758
|
+
dropColumn(table){
|
|
1759
|
+
if(table){
|
|
1760
|
+
if(this.fullTable){
|
|
1761
|
+
// drop column
|
|
1762
|
+
if(this.context.isSQLite){
|
|
1763
|
+
var sqliteQuery = require("./migrationSQLiteQuery");
|
|
1764
|
+
var queryBuilder = new sqliteQuery();
|
|
1765
|
+
var query = queryBuilder.dropColumn(table);
|
|
1766
|
+
this.context._execute(query);
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
if(this.context.isMySQL){
|
|
1770
|
+
var sqlquery = require("./migrationMySQLQuery");
|
|
1771
|
+
var queryBuilder = new sqlquery();
|
|
1772
|
+
var query = queryBuilder.dropColumn(table);
|
|
1773
|
+
this.context._execute(query);
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1776
|
+
}else{
|
|
1777
|
+
console.log("Must call the addTable function.");
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
createTable(table){
|
|
1783
|
+
|
|
1784
|
+
if(table){
|
|
1785
|
+
if(this.context.isSQLite){
|
|
1786
|
+
var sqliteQuery = require("./migrationSQLiteQuery");
|
|
1787
|
+
var queryBuilder = new sqliteQuery();
|
|
1788
|
+
var query = queryBuilder.createTable(table);
|
|
1789
|
+
this.context._execute(query);
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
if(this.context.isMySQL){
|
|
1793
|
+
var sqlquery = require("./migrationMySQLQuery");
|
|
1794
|
+
var queryBuilder = new sqlquery();
|
|
1795
|
+
var query = queryBuilder.createTable(table);
|
|
1796
|
+
this.context._execute(query);
|
|
1797
|
+
}
|
|
1798
|
+
}else{
|
|
1799
|
+
console.log("Table that your trying to create is undefined. PLease check if there are any changes that need to be made");
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1803
|
+
|
|
1804
|
+
dropTable(table){
|
|
1805
|
+
if(table){
|
|
1806
|
+
if(this.context.isSQLite){
|
|
1807
|
+
var sqliteQuery = require("./migrationSQLiteQuery");
|
|
1808
|
+
var queryBuilder = new sqliteQuery();
|
|
1809
|
+
var query = queryBuilder.dropTable(table.__name);
|
|
1810
|
+
this.context._execute(query);
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
if(this.context.isMySQL){
|
|
1814
|
+
var sqlquery = require("./migrationMySQLQuery");
|
|
1815
|
+
var queryBuilder = new sqlquery();
|
|
1816
|
+
var query = queryBuilder.dropTable(table.__name);
|
|
1817
|
+
this.context._execute(query);
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
|
|
1823
|
+
//"dbo.People", "Location"
|
|
1824
|
+
alterColumn(table){
|
|
1825
|
+
if(table){
|
|
1826
|
+
if(this.fullTable){
|
|
1827
|
+
if(this.context.isSQLite){
|
|
1828
|
+
var sqliteQuery = require("./migrationSQLiteQuery");
|
|
1829
|
+
var queryBuilder = new sqliteQuery();
|
|
1830
|
+
var queryObj = queryBuilder.alterColumn(this.fullTable.new, table);
|
|
1831
|
+
for (var key in queryObj) {
|
|
1832
|
+
var query = queryObj[key];
|
|
1833
|
+
this.context._execute(query);
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1837
|
+
if(this.context.isMySQL){
|
|
1838
|
+
var sqlquery = require("./migrationMySQLQuery");
|
|
1839
|
+
var queryBuilder = new sqlquery();
|
|
1840
|
+
var query = queryBuilder.alterColumn(table);
|
|
1841
|
+
this.context._execute(query);
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
}else{
|
|
1845
|
+
console.log("Must call the addTable function.");
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
|
|
1850
|
+
renameColumn(){
|
|
1851
|
+
// TODO
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
seed(){
|
|
1855
|
+
// TODO
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
|
|
1861
|
+
module.exports = schema;
|
|
1862
|
+
** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/Migrations/schema.js **
|
|
1863
|
+
|
|
1864
|
+
// version 0.0.13
|
|
1865
|
+
var entityTrackerModel = require('masterrecord/Entity/entityTrackerModel');
|
|
1866
|
+
var tools = require('masterrecord/Tools');
|
|
1867
|
+
var queryScript = require('masterrecord/QueryLanguage/queryScript');
|
|
1868
|
+
|
|
1869
|
+
class queryMethods{
|
|
1870
|
+
|
|
1871
|
+
constructor(entity, context) {
|
|
1872
|
+
this.__entity = entity;
|
|
1873
|
+
this.__context = context;
|
|
1874
|
+
this.__queryObject = new queryScript();
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
// build a single entity
|
|
1878
|
+
__singleEntityBuilder(dataModel){
|
|
1879
|
+
var $that = this;
|
|
1880
|
+
if(dataModel){
|
|
1881
|
+
var ent = new entityTrackerModel();
|
|
1882
|
+
var mod = ent.build(dataModel, $that.__entity, $that.__context);
|
|
1883
|
+
mod.__state = "track";
|
|
1884
|
+
$that.__context.__track(mod);
|
|
1885
|
+
return mod;
|
|
1886
|
+
}else{
|
|
1887
|
+
return null;
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
// build multiple entities
|
|
1892
|
+
__multipleEntityBuilder(entityValue){
|
|
1893
|
+
var $that = this;
|
|
1894
|
+
var listArray = [];
|
|
1895
|
+
if(entityValue){
|
|
1896
|
+
for(let i = 0; i < entityValue.length; i++){
|
|
1897
|
+
listArray.push($that.__singleEntityBuilder(entityValue[i]));
|
|
1898
|
+
}
|
|
1899
|
+
return listArray;
|
|
1900
|
+
}else{
|
|
1901
|
+
return null;
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
__reset(){
|
|
1906
|
+
this.__queryObject.reset();
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
|
|
1910
|
+
// do join on two tables = inner join
|
|
1911
|
+
join(){
|
|
1912
|
+
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
groupBy(){
|
|
1916
|
+
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
// do join on two tables = inner join
|
|
1920
|
+
_____leftJoin(){
|
|
1921
|
+
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
______orderByCount(query, ...args){
|
|
1925
|
+
var str = query.toString();
|
|
1926
|
+
if(args){
|
|
1927
|
+
for(let argument in args){
|
|
1928
|
+
var item = args[argument];
|
|
1929
|
+
str = str.replace("$$", item);
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
this.__queryObject.orderByCount(str, this.__entity.__name);
|
|
1933
|
+
return this;
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
______orderByCountDescending(query, ...args){
|
|
1937
|
+
var str = query.toString();
|
|
1938
|
+
if(args){
|
|
1939
|
+
for(let argument in args){
|
|
1940
|
+
var item = args[argument];
|
|
1941
|
+
str = str.replace("$$", item);
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
this.__queryObject.orderByCountDesc(str, this.__entity.__name);
|
|
1945
|
+
return this;
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
orderBy(query, ...args){
|
|
1949
|
+
var str = query.toString();
|
|
1950
|
+
if(args){
|
|
1951
|
+
for(let argument in args){
|
|
1952
|
+
var item = args[argument];
|
|
1953
|
+
str = str.replace("$$", item);
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
this.__queryObject.orderBy(str, this.__entity.__name);
|
|
1957
|
+
return this;
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1960
|
+
orderByDescending(query, ...args){
|
|
1961
|
+
var str = query.toString();
|
|
1962
|
+
if(args){
|
|
1963
|
+
for(let argument in args){
|
|
1964
|
+
var item = args[argument];
|
|
1965
|
+
str = str.replace("$$", item);
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
this.__queryObject.orderByDesc(str, this.__entity.__name);
|
|
1969
|
+
return this;
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
raw(query){
|
|
1973
|
+
this.__queryObject.raw(query);
|
|
1974
|
+
return this;
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
/* WHERE and AND work together its a way to add to the WHERE CLAUSE DYNAMICALLY */
|
|
1978
|
+
and(query, ...args){
|
|
1979
|
+
var str = query.toString();
|
|
1980
|
+
if(args){
|
|
1981
|
+
for(let argument in args){
|
|
1982
|
+
var item = args[argument];
|
|
1983
|
+
str = str.replace("$$", item);
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
this.__queryObject.and(str, this.__entity.__name);
|
|
1987
|
+
return this;
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
where(query, ...args){
|
|
1991
|
+
var str = query.toString();
|
|
1992
|
+
if(args){
|
|
1993
|
+
for(let argument in args){
|
|
1994
|
+
var item = args[argument];
|
|
1995
|
+
str = str.replace("$$", item);
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
this.__queryObject.where(str, this.__entity.__name);
|
|
2000
|
+
return this;
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
// when you dont want to use lazy loading and want it called at that moment
|
|
2004
|
+
//Eagerly loading
|
|
2005
|
+
include(query, ...args){
|
|
2006
|
+
var str = query.toString();
|
|
2007
|
+
if(args){
|
|
2008
|
+
for(let argument in args){
|
|
2009
|
+
var item = args[argument];
|
|
2010
|
+
str = str.replace("$$", item);
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
this.__queryObject.include(str, this.__entity.__name);
|
|
2014
|
+
return this;
|
|
2015
|
+
}
|
|
2016
|
+
|
|
2017
|
+
// only takes a array of selected items
|
|
2018
|
+
select(query, ...args){
|
|
2019
|
+
var str = query.toString();
|
|
2020
|
+
if(args){
|
|
2021
|
+
for(let argument in args){
|
|
2022
|
+
var item = args[argument];
|
|
2023
|
+
str = str.replace("$$", item);
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
this.__queryObject.select(str, this.__entity.__name);
|
|
2027
|
+
return this;
|
|
2028
|
+
}
|
|
2029
|
+
|
|
2030
|
+
take(number){
|
|
2031
|
+
this.__queryObject.script.take = number;
|
|
2032
|
+
return this;
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
skip(number){
|
|
2036
|
+
this.__queryObject.script.skip = number;
|
|
2037
|
+
return this;
|
|
2038
|
+
}
|
|
2039
|
+
|
|
2040
|
+
|
|
2041
|
+
// ------------------------------- FUNCTIONS THAT MAKE THE SQL CALL START FROM HERE ON -----------------------------------------------------
|
|
2042
|
+
// ---------------------------------------------------------------------------------------------------------------------------------------
|
|
2043
|
+
|
|
2044
|
+
count(query, ...args){
|
|
2045
|
+
if(query){
|
|
2046
|
+
var str = query.toString();
|
|
2047
|
+
if(args){
|
|
2048
|
+
for(let argument in args){
|
|
2049
|
+
var item = args[argument];
|
|
2050
|
+
str = str.replace("$$", item);
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
this.__queryObject.count(str, this.__entity.__name);
|
|
2054
|
+
}
|
|
2055
|
+
|
|
2056
|
+
if(this.__context.isSQLite){
|
|
2057
|
+
// trying to match string select and relace with select Count(*);
|
|
2058
|
+
var entityValue = this.__context._SQLEngine.getCount(this.__queryObject, this.__entity, this.__context);
|
|
2059
|
+
var val = entityValue[Object.keys(entityValue)[0]];
|
|
2060
|
+
this.__reset();
|
|
2061
|
+
return val;
|
|
2062
|
+
}
|
|
2063
|
+
|
|
2064
|
+
if(this.__context.isMySQL){
|
|
2065
|
+
// trying to match string select and relace with select Count(*);
|
|
2066
|
+
var entityValue = this.__context._SQLEngine.getCount(this.__queryObject, this.__entity, this.__context);
|
|
2067
|
+
var val = entityValue[Object.keys(entityValue)[0]];
|
|
2068
|
+
this.__reset();
|
|
2069
|
+
return val;
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
|
|
2073
|
+
single(){
|
|
2074
|
+
// If no clauses were used before single(), seed defaults so SQL is valid
|
|
2075
|
+
if(this.__queryObject.script.entityMap.length === 0){
|
|
2076
|
+
this.__queryObject.skipClause(this.__entity.__name);
|
|
2077
|
+
this.__queryObject.script.take = 1;
|
|
2078
|
+
}
|
|
2079
|
+
|
|
2080
|
+
if(this.__context.isSQLite){
|
|
2081
|
+
var entityValue = this.__context._SQLEngine.get(this.__queryObject.script, this.__entity, this.__context);
|
|
2082
|
+
var sing = this.__singleEntityBuilder(entityValue);
|
|
2083
|
+
this.__reset();
|
|
2084
|
+
return sing;
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
if(this.__context.isMySQL){
|
|
2088
|
+
var entityValue = this.__context._SQLEngine.get(this.__queryObject.script, this.__entity, this.__context);
|
|
2089
|
+
var sing = this.__singleEntityBuilder(entityValue[0]);
|
|
2090
|
+
this.__reset();
|
|
2091
|
+
return sing;
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
toList(){
|
|
2096
|
+
if(this.__context.isSQLite){
|
|
2097
|
+
if(this.__queryObject.script.entityMap.length === 0){
|
|
2098
|
+
this.__queryObject.skipClause( this.__entity.__name);
|
|
2099
|
+
if(!this.__queryObject.script.take || this.__queryObject.script.take === 0){
|
|
2100
|
+
this.__queryObject.script.take = 1000;
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
var entityValue = this.__context._SQLEngine.all(this.__queryObject.script, this.__entity, this.__context);
|
|
2104
|
+
var toLi = this.__multipleEntityBuilder(entityValue);
|
|
2105
|
+
this.__reset();
|
|
2106
|
+
return toLi;
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2109
|
+
if(this.__context.isMySQL){
|
|
2110
|
+
if(this.__queryObject.script.entityMap.length === 0){
|
|
2111
|
+
this.__queryObject.skipClause( this.__entity.__name);
|
|
2112
|
+
if(!this.__queryObject.script.take || this.__queryObject.script.take === 0){
|
|
2113
|
+
this.__queryObject.script.take = 1000;
|
|
2114
|
+
}
|
|
2115
|
+
}
|
|
2116
|
+
var entityValue = this.__context._SQLEngine.all(this.__queryObject.script, this.__entity, this.__context);
|
|
2117
|
+
var toLi = this.__multipleEntityBuilder(entityValue);
|
|
2118
|
+
this.__reset();
|
|
2119
|
+
return toLi;
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
// ------------------------------- FUNCTIONS THAT UPDATE SQL START FROM HERE -----------------------------------------------------
|
|
2124
|
+
// ---------------------------------------------------------------------------------------------------------------------------------------
|
|
2125
|
+
add(entityValue){
|
|
2126
|
+
entityValue.__state = "insert";
|
|
2127
|
+
entityValue.__entity = this.__entity;
|
|
2128
|
+
entityValue.__context = this.__context;
|
|
2129
|
+
entityValue.__name = this.__entity.__name;
|
|
2130
|
+
this.__context.__track(entityValue);
|
|
2131
|
+
}
|
|
2132
|
+
|
|
2133
|
+
remove(entityValue){
|
|
2134
|
+
entityValue.__state = "delete";
|
|
2135
|
+
entityValue.__entity = this.__entity;
|
|
2136
|
+
entityValue.__context = this.__context;
|
|
2137
|
+
}
|
|
2138
|
+
|
|
2139
|
+
removeRange(entityValues){
|
|
2140
|
+
for (const property in entityValues) {
|
|
2141
|
+
entityValues[property].__state = "delete";
|
|
2142
|
+
entityValues[property].__entity = this.__entity;
|
|
2143
|
+
entityValues[property].__context = this.__context;
|
|
2144
|
+
}
|
|
2145
|
+
}
|
|
2146
|
+
|
|
2147
|
+
track(entityValue){
|
|
2148
|
+
entityValue.__state = "track";
|
|
2149
|
+
tools.clearAllProto(entityValue);
|
|
2150
|
+
entityValue.__entity = this.__entity;
|
|
2151
|
+
entityValue.__context = this.__context;
|
|
2152
|
+
this.__context.__track(entityValue);
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
|
|
2156
|
+
module.exports = queryMethods;
|
|
2157
|
+
|
|
2158
|
+
|
|
2159
|
+
** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/QueryLanguage/queryMethods.js **
|
|
2160
|
+
// version 0.0.5
|
|
2161
|
+
|
|
2162
|
+
const LOG_OPERATORS_REGEX = /(\|\|)|(&&)/;
|
|
2163
|
+
var tools = require('../Tools');
|
|
2164
|
+
|
|
2165
|
+
class queryScript{
|
|
2166
|
+
|
|
2167
|
+
constructor(){ }
|
|
2168
|
+
|
|
2169
|
+
script = {
|
|
2170
|
+
select : false,
|
|
2171
|
+
where: false,
|
|
2172
|
+
and : [],
|
|
2173
|
+
include : [],
|
|
2174
|
+
raw: false,
|
|
2175
|
+
entity : "",
|
|
2176
|
+
entityMap : [],
|
|
2177
|
+
take : 0,
|
|
2178
|
+
skip: 0,
|
|
2179
|
+
orderBy : false,
|
|
2180
|
+
orderByDesc : false,
|
|
2181
|
+
parentName : ""
|
|
2182
|
+
};
|
|
2183
|
+
|
|
2184
|
+
|
|
2185
|
+
reset(){
|
|
2186
|
+
this.script = {
|
|
2187
|
+
select : false,
|
|
2188
|
+
where: false,
|
|
2189
|
+
and : [],
|
|
2190
|
+
include : [],
|
|
2191
|
+
raw: false,
|
|
2192
|
+
entity : "",
|
|
2193
|
+
entityMap : [],
|
|
2194
|
+
take : 0,
|
|
2195
|
+
skip: 0,
|
|
2196
|
+
orderBy : false,
|
|
2197
|
+
orderByDesc : false
|
|
2198
|
+
};
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
getScript(){
|
|
2202
|
+
return this.script;
|
|
2203
|
+
}
|
|
2204
|
+
|
|
2205
|
+
raw(query){
|
|
2206
|
+
this.script.raw = query;
|
|
2207
|
+
return this.script;
|
|
2208
|
+
}
|
|
2209
|
+
|
|
2210
|
+
orderBy(text, entityName){
|
|
2211
|
+
this.buildScript(text, "orderBy", this.script, entityName);
|
|
2212
|
+
return this.script;
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
orderByDesc(text, entityName){
|
|
2216
|
+
this.buildScript(text, "orderByDesc", this.script, entityName);
|
|
2217
|
+
return this.script;
|
|
2218
|
+
}
|
|
2219
|
+
|
|
2220
|
+
and(text, entityName){
|
|
2221
|
+
this.buildScript(text, "and", this.script, entityName);
|
|
2222
|
+
return this.script;
|
|
2223
|
+
}
|
|
2224
|
+
|
|
2225
|
+
where(text, entityName){
|
|
2226
|
+
this.buildScript(text, "where", this.script, entityName);
|
|
2227
|
+
return this.script;
|
|
2228
|
+
}
|
|
2229
|
+
|
|
2230
|
+
// this gets called when you skip a where clause
|
|
2231
|
+
skipClause(entityName){
|
|
2232
|
+
//this.buildScript(text, "skipClause", this.script, entityName);
|
|
2233
|
+
this.script.entityMap.push({
|
|
2234
|
+
name: entityName,
|
|
2235
|
+
entity : "ran"
|
|
2236
|
+
});
|
|
2237
|
+
this.script.parentName = entityName;
|
|
2238
|
+
return this.script;
|
|
2239
|
+
}
|
|
2240
|
+
|
|
2241
|
+
include(text, entityName){
|
|
2242
|
+
this.buildScript(text, "include", this.script, entityName);
|
|
2243
|
+
return this.script;
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2246
|
+
select(text, entityName){
|
|
2247
|
+
this.buildScript(text, "select", this.script, entityName);
|
|
2248
|
+
return this.script;
|
|
2249
|
+
}
|
|
2250
|
+
|
|
2251
|
+
count(text, entityName){
|
|
2252
|
+
this.buildScript(text, "count", this.script, entityName);
|
|
2253
|
+
return this.script;
|
|
2254
|
+
}
|
|
2255
|
+
|
|
2256
|
+
buildScript(text, type, obj, entityName){
|
|
2257
|
+
|
|
2258
|
+
var cachedExpr = {}; // this function will just get the high level
|
|
2259
|
+
|
|
2260
|
+
/// first we find all the groups in the query string
|
|
2261
|
+
var querySections = this.getFunctionsInQuery(text);
|
|
2262
|
+
|
|
2263
|
+
// remove spaces from query and get the Entity
|
|
2264
|
+
cachedExpr.entity = this.getEntity(text);
|
|
2265
|
+
|
|
2266
|
+
|
|
2267
|
+
if(!this.isMapped(entityName, obj.entityMap)){
|
|
2268
|
+
|
|
2269
|
+
obj.entityMap.push({
|
|
2270
|
+
name: entityName,
|
|
2271
|
+
entity : cachedExpr.entity
|
|
2272
|
+
});
|
|
2273
|
+
}
|
|
2274
|
+
|
|
2275
|
+
// attach the entity name to the main Object
|
|
2276
|
+
obj.entity = cachedExpr.entity
|
|
2277
|
+
|
|
2278
|
+
cachedExpr[entityName] = this.splitGroupsByLogicalOperators(querySections.query);
|
|
2279
|
+
|
|
2280
|
+
// lets break the string into a list of functions
|
|
2281
|
+
this.buildFields(text, cachedExpr);
|
|
2282
|
+
|
|
2283
|
+
if(type === "include"){
|
|
2284
|
+
if(cachedExpr.selectFields){
|
|
2285
|
+
if(!this.isMapped(cachedExpr.selectFields[0], obj.entityMap)){
|
|
2286
|
+
obj.entityMap.push({
|
|
2287
|
+
name: tools.capitalizeFirstLetter(cachedExpr.selectFields[0]),
|
|
2288
|
+
entity : tools.getRandomLetter(1, obj.entityMap)
|
|
2289
|
+
});
|
|
2290
|
+
}
|
|
2291
|
+
};
|
|
2292
|
+
}
|
|
2293
|
+
|
|
2294
|
+
this.describeExpressionParts(cachedExpr[entityName]);
|
|
2295
|
+
this.describeExpressionPartsFunctions(cachedExpr[entityName], querySections.functions);
|
|
2296
|
+
if(type === "include" || type === "and"){
|
|
2297
|
+
obj[type].push(cachedExpr);
|
|
2298
|
+
}
|
|
2299
|
+
else{
|
|
2300
|
+
obj[type] = cachedExpr;
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2303
|
+
obj.parentName = entityName;
|
|
2304
|
+
return cachedExpr;
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2307
|
+
|
|
2308
|
+
|
|
2309
|
+
// look and grab all fields
|
|
2310
|
+
buildFields(text, desc){
|
|
2311
|
+
var match = text.match(/^([\w\d$_]+?)\s*=>((?:\{\sreturn\s)?[\s\S]*(?:\})?)/);
|
|
2312
|
+
if(match){
|
|
2313
|
+
const entity = match[1];
|
|
2314
|
+
let exprStr = match[2];
|
|
2315
|
+
const fields = [];
|
|
2316
|
+
|
|
2317
|
+
exprStr.replace(new RegExp(entity + "\\.([\\w_]+)", "g"), function (_, field) {
|
|
2318
|
+
if (!fields.includes(field)) fields.push(field);
|
|
2319
|
+
});
|
|
2320
|
+
|
|
2321
|
+
desc.expr = exprStr.trim();
|
|
2322
|
+
desc.selectFields = fields;
|
|
2323
|
+
return desc;
|
|
2324
|
+
}
|
|
2325
|
+
else{
|
|
2326
|
+
return null;
|
|
2327
|
+
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
|
|
2331
|
+
MATCH_ENTITY_REGEXP(entityName) {
|
|
2332
|
+
return new RegExp("(^|[^\\w\\d])" + entityName + "[ \\.\\)]");
|
|
2333
|
+
}
|
|
2334
|
+
|
|
2335
|
+
OPERATORS_REGEX(entityName){
|
|
2336
|
+
return new RegExp("(?:^|[^\\w\\d])" + entityName
|
|
2337
|
+
+ "\\.((?:\\.?[\\w\\d_\\$]+)+)(?:\\((.*?)\\))?(?:\\s*(>|<|(?:===)|(?:!==)|(?:==)|(?:!=)|(?:=)|(?:<=)|(?:>=)|(?:in))\\s*(.*))?")
|
|
2338
|
+
}
|
|
2339
|
+
|
|
2340
|
+
|
|
2341
|
+
splitGroupsByLogicalOperators(text) {
|
|
2342
|
+
let parts = {}, tmp;
|
|
2343
|
+
var part = {query : text, name : "query"}
|
|
2344
|
+
|
|
2345
|
+
//tmp = this.splitByLogicalOperators(part.query, entityRegExp)
|
|
2346
|
+
tmp = this.extractInside(part.query, part.name);
|
|
2347
|
+
if(tmp){
|
|
2348
|
+
part.inside = tmp;
|
|
2349
|
+
parts[part.name] = part;
|
|
2350
|
+
}
|
|
2351
|
+
else{
|
|
2352
|
+
part.inside = part.query;
|
|
2353
|
+
parts[part.name] = part;
|
|
2354
|
+
}
|
|
2355
|
+
part.inside = part.inside.replaceAll("&&", "and");
|
|
2356
|
+
part.query = part.query.replaceAll("&&", "and");
|
|
2357
|
+
part.inside = part.inside.replaceAll("||", "or");
|
|
2358
|
+
part.query = part.query.replaceAll("||", "or");
|
|
2359
|
+
|
|
2360
|
+
return parts;
|
|
2361
|
+
}
|
|
2362
|
+
|
|
2363
|
+
|
|
2364
|
+
// find all functions from querySections
|
|
2365
|
+
getFunctionsInQuery(text) {
|
|
2366
|
+
|
|
2367
|
+
const regex = /(?<=\.(?=[A-z]+\())([^(]+)\((.+?)\)(?!\))/g;
|
|
2368
|
+
let m;
|
|
2369
|
+
const items = [];
|
|
2370
|
+
|
|
2371
|
+
while ((m = regex.exec(text)) !== null) {
|
|
2372
|
+
// This is necessary to avoid infinite loops with zero-width matches
|
|
2373
|
+
if (m.index === regex.lastIndex) {
|
|
2374
|
+
regex.lastIndex++;
|
|
2375
|
+
}
|
|
2376
|
+
|
|
2377
|
+
const [, fName, query] = m;
|
|
2378
|
+
items.push(
|
|
2379
|
+
{name: fName, query: query, inside : query, parentFeild : this.getParentField(text, m[0]), input: m.input}
|
|
2380
|
+
);
|
|
2381
|
+
|
|
2382
|
+
text = text.replace(`${fName}(${query})`, `func`);
|
|
2383
|
+
|
|
2384
|
+
}
|
|
2385
|
+
|
|
2386
|
+
//items.push({name: "NOFUNCTIONS", query: str})
|
|
2387
|
+
|
|
2388
|
+
return {
|
|
2389
|
+
functions: items,
|
|
2390
|
+
query : text
|
|
2391
|
+
};
|
|
2392
|
+
}
|
|
2393
|
+
|
|
2394
|
+
getParentField(text, funcName){
|
|
2395
|
+
var split = text.split(".");
|
|
2396
|
+
for (let i = 0; i < split.length; i++) {
|
|
2397
|
+
|
|
2398
|
+
if(split[i].includes(funcName)){
|
|
2399
|
+
return split[i -1];
|
|
2400
|
+
}
|
|
2401
|
+
}
|
|
2402
|
+
return "";
|
|
2403
|
+
}
|
|
2404
|
+
|
|
2405
|
+
findExpression(fieldName, expression){
|
|
2406
|
+
// loop through and find expression
|
|
2407
|
+
for (const key in expression) {
|
|
2408
|
+
|
|
2409
|
+
if(expression[key].field === fieldName){
|
|
2410
|
+
return expression[key]
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
|
|
2415
|
+
describeExpressionPartsFunctions(cachedExpr, functions){
|
|
2416
|
+
cachedExpr.functions = [];
|
|
2417
|
+
if(functions.length > 0){
|
|
2418
|
+
for (let item in functions) {
|
|
2419
|
+
|
|
2420
|
+
var part = functions[item];
|
|
2421
|
+
var partQuery = part.inside;
|
|
2422
|
+
// get entity of inside function
|
|
2423
|
+
var entity = this.getEntity(partQuery);
|
|
2424
|
+
|
|
2425
|
+
part.entity = entity;
|
|
2426
|
+
|
|
2427
|
+
// is the function name white listed
|
|
2428
|
+
if(this.isFunction(part.name)){
|
|
2429
|
+
var scriptInner = {
|
|
2430
|
+
select : false,
|
|
2431
|
+
where: false,
|
|
2432
|
+
and : [],
|
|
2433
|
+
include : [],
|
|
2434
|
+
raw: false,
|
|
2435
|
+
entity : "",
|
|
2436
|
+
entityMap : [],
|
|
2437
|
+
take : 0,
|
|
2438
|
+
skip: 0,
|
|
2439
|
+
orderBy : false,
|
|
2440
|
+
orderByDesc : false
|
|
2441
|
+
};
|
|
2442
|
+
|
|
2443
|
+
if(part.name === "where"){
|
|
2444
|
+
// TODO: we need to Get this working
|
|
2445
|
+
// recall to parse inner query of function that is being called
|
|
2446
|
+
this.buildScript(part.inside, part.name, scriptInner, part.parentFeild);
|
|
2447
|
+
part.parentName = scriptInner.parentName;
|
|
2448
|
+
part.entity = scriptInner.where.entity;
|
|
2449
|
+
part.expr = scriptInner.where.expr;
|
|
2450
|
+
part.selectFields = scriptInner.where.selectFields;
|
|
2451
|
+
part[scriptInner.parentName] = scriptInner.where[scriptInner.parentName];
|
|
2452
|
+
}
|
|
2453
|
+
if(part.name === "include"){
|
|
2454
|
+
// TODO: we need to Get this working
|
|
2455
|
+
// recall to parse inner query of function that is being called
|
|
2456
|
+
this.buildScript(part.inside, part.name, scriptInner, part.parentFeild);
|
|
2457
|
+
part.parentName = scriptInner.parentName;
|
|
2458
|
+
part.entity = scriptInner.include.entity;
|
|
2459
|
+
part.expr = scriptInner.include.expr;
|
|
2460
|
+
part.selectFields = scriptInner.include.selectFields;
|
|
2461
|
+
part[scriptInner.parentName] = scriptInner.include[scriptInner.parentName];
|
|
2462
|
+
|
|
2463
|
+
}
|
|
2464
|
+
if(part.name === "select"){
|
|
2465
|
+
// TODO: we need to Get this working
|
|
2466
|
+
// recall to parse inner query of function that is being called
|
|
2467
|
+
this.buildScript(part.inside, part.name, scriptInner, part.parentFeild);
|
|
2468
|
+
part.parentName = scriptInner.parentName;
|
|
2469
|
+
part.entity = scriptInner.select.entity;
|
|
2470
|
+
part.expr = scriptInner.select.expr;
|
|
2471
|
+
part.selectFields = scriptInner.select.selectFields;
|
|
2472
|
+
part[scriptInner.parentName] = scriptInner.select[scriptInner.parentName];
|
|
2473
|
+
}
|
|
2474
|
+
|
|
2475
|
+
// will search multiple values in a field
|
|
2476
|
+
if(part.name === "any"){
|
|
2477
|
+
// var members = data.memberContext.Member.where(r => r.first_name.any($$), "Rich, james, Oliver" ).toList();
|
|
2478
|
+
var expre = this.findExpression(part.parentFeild, cachedExpr.query.expressions)
|
|
2479
|
+
expre.func = "IN";
|
|
2480
|
+
expre.arg = `(${part.query})`;
|
|
2481
|
+
expre.isFunction = true;
|
|
2482
|
+
}
|
|
2483
|
+
|
|
2484
|
+
if(part.name === "like"){
|
|
2485
|
+
// var members = data.memberContext.Member.where(r => r.space_id == $$ && r.user_id != null && r.first_name.like($$),data.params.query.id, "r" ).toList();
|
|
2486
|
+
var expre = this.findExpression(part.parentFeild, cachedExpr.query.expressions)
|
|
2487
|
+
expre.func = part.name;
|
|
2488
|
+
expre.arg = part.query;
|
|
2489
|
+
expre.isFunction = true;
|
|
2490
|
+
}
|
|
2491
|
+
|
|
2492
|
+
}
|
|
2493
|
+
else{
|
|
2494
|
+
throw "Cannot have inner functions unless its a Where, Include, Select or Like caluse"
|
|
2495
|
+
}
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2499
|
+
}
|
|
2500
|
+
|
|
2501
|
+
describeExpressionParts(parts) {
|
|
2502
|
+
if(parts.query) {
|
|
2503
|
+
let match, fields, func, arg;
|
|
2504
|
+
var part = {};
|
|
2505
|
+
part.inside = parts.query.query;
|
|
2506
|
+
part.expressions = [];
|
|
2507
|
+
var partQuery = part.inside;
|
|
2508
|
+
var entity = this.getEntity(partQuery);
|
|
2509
|
+
var exprPartRegExp = this.OPERATORS_REGEX(entity);
|
|
2510
|
+
// check if query contains an AND.
|
|
2511
|
+
var trimmedQuery = partQuery.replace(/\s/g, '');
|
|
2512
|
+
var splitByAnd = trimmedQuery.split("and");
|
|
2513
|
+
for (let splitAnds in splitByAnd) {
|
|
2514
|
+
|
|
2515
|
+
if (match = splitByAnd[splitAnds].match(exprPartRegExp)) {
|
|
2516
|
+
fields = match[1].split(".");
|
|
2517
|
+
func = (match[2] ? fields[fields.length - 1] : (match[3] || "exists"));
|
|
2518
|
+
|
|
2519
|
+
if (func == "==" || func == "===") {
|
|
2520
|
+
func = "=";
|
|
2521
|
+
}
|
|
2522
|
+
else if (func == "!==") {
|
|
2523
|
+
func = "!=";
|
|
2524
|
+
}
|
|
2525
|
+
|
|
2526
|
+
arg = match[2] || match[4];
|
|
2527
|
+
if (arg == "true" || arg == "false") {
|
|
2528
|
+
arg = arg == "true";
|
|
2529
|
+
}
|
|
2530
|
+
else if (arg && arg.charAt(0) == arg.charAt(arg.length - 1) && (arg.charAt(0) == "'" || arg.charAt(0) == '"')) {
|
|
2531
|
+
arg = arg.slice(1, -1);
|
|
2532
|
+
}
|
|
2533
|
+
|
|
2534
|
+
part.entity = entity;
|
|
2535
|
+
|
|
2536
|
+
part.expressions.push({
|
|
2537
|
+
field: fields[0],
|
|
2538
|
+
func : func.toLowerCase(),
|
|
2539
|
+
arg : arg
|
|
2540
|
+
});
|
|
2541
|
+
parts.query = part;
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
}
|
|
2545
|
+
|
|
2546
|
+
return parts;
|
|
2547
|
+
}
|
|
2548
|
+
|
|
2549
|
+
getEntity(str){
|
|
2550
|
+
var clean = str.replace(/\s/g, '');
|
|
2551
|
+
return clean.substring(0, 1);
|
|
2552
|
+
}
|
|
2553
|
+
|
|
2554
|
+
isMapped(name, maps){
|
|
2555
|
+
for (let item in maps) {
|
|
2556
|
+
var map = maps[item];
|
|
2557
|
+
if(tools.capitalizeFirstLetter(name) === map.name){
|
|
2558
|
+
return true
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
return false;
|
|
2562
|
+
}
|
|
2563
|
+
|
|
2564
|
+
//const res1 = extractInside(`User.include(a => a.Profile.where(r => r.name.startWith(i))).single()`, 'where');
|
|
2565
|
+
// const res2 = extractInside(`User.include(a => a.Profile.select(r => r.name === "rick")).single()`, 'select');
|
|
2566
|
+
extractInside(str, fname) {
|
|
2567
|
+
if(this.isFunction(fname)){
|
|
2568
|
+
const startIndex = str.indexOf(fname) + fname.length;
|
|
2569
|
+
const stack = [];
|
|
2570
|
+
for (let i = startIndex; i < str.length; i++) {
|
|
2571
|
+
if(str[i] === '(') {
|
|
2572
|
+
stack.push(str[i]);
|
|
2573
|
+
|
|
2574
|
+
} else if (str[i] === ')') {
|
|
2575
|
+
stack.pop();
|
|
2576
|
+
}
|
|
2577
|
+
if(stack.length === 0) {
|
|
2578
|
+
return str.substring(startIndex+1, i);
|
|
2579
|
+
}
|
|
2580
|
+
}
|
|
2581
|
+
return str;
|
|
2582
|
+
}
|
|
2583
|
+
else{
|
|
2584
|
+
return null;
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
|
|
2588
|
+
isFunction(func){
|
|
2589
|
+
var funcList = [ "include", "like", "any"]
|
|
2590
|
+
for(var i =0; i < funcList.length; i++){
|
|
2591
|
+
if(funcList[i] === func){
|
|
2592
|
+
return true
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
return false;
|
|
2596
|
+
}
|
|
2597
|
+
|
|
2598
|
+
}
|
|
2599
|
+
|
|
2600
|
+
module.exports = queryScript;
|
|
2601
|
+
** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/QueryLanguage/queryScrip// Version 0.0.19
|
|
2602
|
+
var tools = require('masterrecord/Tools');
|
|
2603
|
+
|
|
2604
|
+
class SQLLiteEngine {
|
|
2605
|
+
|
|
2606
|
+
unsupportedWords = ["order"]
|
|
2607
|
+
|
|
2608
|
+
update(query){
|
|
2609
|
+
var sqlQuery = ` UPDATE [${query.tableName}]
|
|
2610
|
+
SET ${query.arg}
|
|
2611
|
+
WHERE [${query.tableName}].[${query.primaryKey}] = ${query.primaryKeyValue}` // primary key for that table =
|
|
2612
|
+
return this._run(sqlQuery);
|
|
2613
|
+
}
|
|
2614
|
+
|
|
2615
|
+
delete(queryObject){
|
|
2616
|
+
var sqlObject = this._buildDeleteObject(queryObject);
|
|
2617
|
+
var sqlQuery = `DELETE FROM [${sqlObject.tableName}] WHERE [${sqlObject.tableName}].[${sqlObject.primaryKey}] = ${sqlObject.value}`;
|
|
2618
|
+
return this._execute(sqlQuery);
|
|
2619
|
+
}
|
|
2620
|
+
|
|
2621
|
+
insert(queryObject){
|
|
2622
|
+
var sqlObject = this._buildSQLInsertObject(queryObject, queryObject.__entity);
|
|
2623
|
+
var query = `INSERT INTO [${sqlObject.tableName}] (${sqlObject.columns})
|
|
2624
|
+
VALUES (${sqlObject.values})`;
|
|
2625
|
+
var queryObj = this._run(query);
|
|
2626
|
+
var open = {
|
|
2627
|
+
"id": queryObj.lastInsertRowid
|
|
2628
|
+
};
|
|
2629
|
+
return open;
|
|
2630
|
+
}
|
|
2631
|
+
|
|
2632
|
+
get(query, entity, context){
|
|
2633
|
+
var queryString = {};
|
|
2634
|
+
try {
|
|
2635
|
+
if(query.raw){
|
|
2636
|
+
queryString.query = query.raw;
|
|
2637
|
+
}
|
|
2638
|
+
else{
|
|
2639
|
+
if(typeof query === 'string'){
|
|
2640
|
+
queryString.query = query;
|
|
2641
|
+
}
|
|
2642
|
+
else{
|
|
2643
|
+
queryString = this.buildQuery(query, entity, context);
|
|
2644
|
+
}
|
|
2645
|
+
}
|
|
2646
|
+
if(queryString.query){
|
|
2647
|
+
console.log("SQL:", queryString.query);
|
|
2648
|
+
var queryReturn = this.db.prepare(queryString.query).get();
|
|
2649
|
+
return queryReturn;
|
|
2650
|
+
}
|
|
2651
|
+
return null;
|
|
2652
|
+
} catch (err) {
|
|
2653
|
+
console.error(err);
|
|
2654
|
+
return null;
|
|
2655
|
+
}
|
|
2656
|
+
}
|
|
2657
|
+
|
|
2658
|
+
getCount(queryObject, entity, context){
|
|
2659
|
+
var query = queryObject.script;
|
|
2660
|
+
var queryString = {};
|
|
2661
|
+
try {
|
|
2662
|
+
if(query.raw){
|
|
2663
|
+
queryString.query = query.raw;
|
|
2664
|
+
}
|
|
2665
|
+
else{
|
|
2666
|
+
if(query.count === undefined){
|
|
2667
|
+
query.count = "none";
|
|
2668
|
+
}
|
|
2669
|
+
queryString.entity = this.getEntity(entity.__name, query.entityMap);
|
|
2670
|
+
queryString.query = `SELECT ${this.buildCount(query, entity)} ${this.buildFrom(query, entity)} ${this.buildWhere(query, entity)}`
|
|
2671
|
+
}
|
|
2672
|
+
if(queryString.query){
|
|
2673
|
+
var queryCount = queryString.query
|
|
2674
|
+
console.log("SQL:", queryCount );
|
|
2675
|
+
var queryReturn = this.db.prepare(queryCount ).get();
|
|
2676
|
+
return queryReturn;
|
|
2677
|
+
}
|
|
2678
|
+
return null;
|
|
2679
|
+
} catch (err) {
|
|
2680
|
+
console.error(err);
|
|
2681
|
+
return null;
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
|
|
2685
|
+
all(query, entity, context){
|
|
2686
|
+
var selectQuery = {};
|
|
2687
|
+
try {
|
|
2688
|
+
if(query.raw){
|
|
2689
|
+
selectQuery.query = query.raw;
|
|
2690
|
+
}
|
|
2691
|
+
else{
|
|
2692
|
+
|
|
2693
|
+
selectQuery = this.buildQuery(query, entity, context);
|
|
2694
|
+
}
|
|
2695
|
+
if(selectQuery.query){
|
|
2696
|
+
console.log("SQL:", selectQuery.query);
|
|
2697
|
+
var queryReturn = this.db.prepare(selectQuery.query).all();
|
|
2698
|
+
return queryReturn;
|
|
2699
|
+
}
|
|
2700
|
+
return null;
|
|
2701
|
+
} catch (err) {
|
|
2702
|
+
console.error(err);
|
|
2703
|
+
return null;
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2706
|
+
|
|
2707
|
+
changeNullQuery(query){
|
|
2708
|
+
if(query.where){
|
|
2709
|
+
var whereClaus;
|
|
2710
|
+
whereClaus = query.where.expr.replace("=== null", "is null");
|
|
2711
|
+
if(whereClaus === query.where.expr){
|
|
2712
|
+
whereClaus = query.where.expr.replace("!= null", "is not null");
|
|
2713
|
+
}
|
|
2714
|
+
query.where.expr = whereClaus;
|
|
2715
|
+
}
|
|
2716
|
+
|
|
2717
|
+
}
|
|
2718
|
+
|
|
2719
|
+
buildCount(query, mainQuery){
|
|
2720
|
+
var entity = this.getEntity(query.parentName, query.entityMap);
|
|
2721
|
+
if(query.count){
|
|
2722
|
+
if(query.count !== "none"){
|
|
2723
|
+
return `COUNT(${entity}.${query.count.selectFields[0]})`
|
|
2724
|
+
}
|
|
2725
|
+
else{
|
|
2726
|
+
return `COUNT(*)`
|
|
2727
|
+
}
|
|
2728
|
+
}
|
|
2729
|
+
else{
|
|
2730
|
+
return ""
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2733
|
+
|
|
2734
|
+
buildQuery(query, entity, context, limit){
|
|
2735
|
+
|
|
2736
|
+
var queryObject = {};
|
|
2737
|
+
queryObject.entity = this.getEntity(entity.__name, query.entityMap);
|
|
2738
|
+
queryObject.select = this.buildSelect(query, entity);
|
|
2739
|
+
queryObject.count = this.buildCount(query, entity);
|
|
2740
|
+
queryObject.from = this.buildFrom(query, entity);
|
|
2741
|
+
queryObject.include = this.buildInclude(query, entity, context, queryObject);
|
|
2742
|
+
queryObject.where = this.buildWhere(query, entity);
|
|
2743
|
+
queryObject.and = this.buildAnd(query, entity);
|
|
2744
|
+
queryObject.take = this.buildTake(query);
|
|
2745
|
+
queryObject.skip = this.buildSkip(query);
|
|
2746
|
+
queryObject.orderBy = this.buildOrderBy(query);
|
|
2747
|
+
|
|
2748
|
+
|
|
2749
|
+
var queryString = `${queryObject.select} ${queryObject.count} ${queryObject.from} ${queryObject.include} ${queryObject.where} ${queryObject.and} ${queryObject.orderBy} ${queryObject.take} ${queryObject.skip}`;
|
|
2750
|
+
return {
|
|
2751
|
+
query : queryString,
|
|
2752
|
+
entity : this.getEntity(entity.__name, query.entityMap)
|
|
2753
|
+
}
|
|
2754
|
+
|
|
2755
|
+
}
|
|
2756
|
+
|
|
2757
|
+
buildOrderBy(query){
|
|
2758
|
+
// ORDER BY column1, column2, ... ASC|DESC;
|
|
2759
|
+
var $that = this;
|
|
2760
|
+
var orderByType = "ASC";
|
|
2761
|
+
var orderByEntity = query.orderBy;
|
|
2762
|
+
var strQuery = "";
|
|
2763
|
+
if(orderByEntity === false){
|
|
2764
|
+
orderByType = "DESC";
|
|
2765
|
+
orderByEntity = query.orderByDesc;
|
|
2766
|
+
}
|
|
2767
|
+
if(orderByEntity){
|
|
2768
|
+
var entity = this.getEntity(query.parentName, query.entityMap);
|
|
2769
|
+
var fieldList = "";
|
|
2770
|
+
for (const item in orderByEntity.selectFields) {
|
|
2771
|
+
fieldList += `${entity}.${orderByEntity.selectFields[item]}, `;
|
|
2772
|
+
};
|
|
2773
|
+
fieldList = fieldList.replace(/,\s*$/, "");
|
|
2774
|
+
strQuery = "ORDER BY";
|
|
2775
|
+
strQuery += ` ${fieldList} ${orderByType}`;
|
|
2776
|
+
}
|
|
2777
|
+
return strQuery;
|
|
2778
|
+
}
|
|
2779
|
+
|
|
2780
|
+
buildTake(query){
|
|
2781
|
+
if(query.take){
|
|
2782
|
+
return `LIMIT ${query.take}`
|
|
2783
|
+
}
|
|
2784
|
+
else{
|
|
2785
|
+
return "";
|
|
2786
|
+
}
|
|
2787
|
+
}
|
|
2788
|
+
|
|
2789
|
+
buildSkip(query){
|
|
2790
|
+
if(query.skip){
|
|
2791
|
+
return `OFFSET ${query.skip}`
|
|
2792
|
+
}
|
|
2793
|
+
else{
|
|
2794
|
+
return "";
|
|
2795
|
+
}
|
|
2796
|
+
}
|
|
2797
|
+
|
|
2798
|
+
buildAnd(query, mainQuery){
|
|
2799
|
+
// loop through the AND
|
|
2800
|
+
// loop update ther where .expr
|
|
2801
|
+
var andEntity = query.and;
|
|
2802
|
+
var strQuery = "";
|
|
2803
|
+
var $that = this;
|
|
2804
|
+
var str = "";
|
|
2805
|
+
|
|
2806
|
+
if(andEntity){
|
|
2807
|
+
var entity = this.getEntity(query.parentName, query.entityMap);
|
|
2808
|
+
var andList = [];
|
|
2809
|
+
for (let entityPart in andEntity) { // loop through list of and's
|
|
2810
|
+
var itemEntity = andEntity[entityPart]; // get the entityANd
|
|
2811
|
+
for (let table in itemEntity[query.parentName]) { // find the main table
|
|
2812
|
+
var item = itemEntity[query.parentName][table];
|
|
2813
|
+
for (let exp in item.expressions) {
|
|
2814
|
+
var field = tools.capitalizeFirstLetter(item.expressions[exp].field);
|
|
2815
|
+
if(mainQuery[field]){
|
|
2816
|
+
if(mainQuery[field].isNavigational){
|
|
2817
|
+
entity = $that.getEntity(field, query.entityMap);
|
|
2818
|
+
field = item.fields[1];
|
|
2819
|
+
}
|
|
2820
|
+
}
|
|
2821
|
+
if(item.expressions[exp].arg === "null"){
|
|
2822
|
+
if(item.expressions[exp].func === "="){
|
|
2823
|
+
item.expressions[exp].func = "is"
|
|
2824
|
+
}
|
|
2825
|
+
if(item.expressions[exp].func === "!="){
|
|
2826
|
+
item.expressions[exp].func = "is not"
|
|
2827
|
+
}
|
|
2828
|
+
}
|
|
2829
|
+
if(strQuery === ""){
|
|
2830
|
+
if(item.expressions[exp].arg === "null"){
|
|
2831
|
+
strQuery = `${entity}.${field} ${item.expressions[exp].func} ${item.expressions[exp].arg}`;
|
|
2832
|
+
}else{
|
|
2833
|
+
strQuery = `${entity}.${field} ${item.expressions[exp].func} '${item.expressions[exp].arg}'`;
|
|
2834
|
+
}
|
|
2835
|
+
}
|
|
2836
|
+
else{
|
|
2837
|
+
if(item.expressions[exp].arg === "null"){
|
|
2838
|
+
strQuery = `${strQuery} and ${entity}.${field} ${item.expressions[exp].func} ${item.expressions[exp].arg}`;
|
|
2839
|
+
}else{
|
|
2840
|
+
strQuery = `${strQuery} and ${entity}.${field} ${item.expressions[exp].func} '${item.expressions[exp].arg}'`;
|
|
2841
|
+
}
|
|
2842
|
+
|
|
2843
|
+
}
|
|
2844
|
+
}
|
|
2845
|
+
andList.push(strQuery);
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
}
|
|
2849
|
+
|
|
2850
|
+
if(andList.length > 0){
|
|
2851
|
+
str = `and ${andList.join(" and ")}`;
|
|
2852
|
+
}
|
|
2853
|
+
return str
|
|
2854
|
+
}
|
|
2855
|
+
|
|
2856
|
+
buildWhere(query, mainQuery){
|
|
2857
|
+
var whereEntity = query.where;
|
|
2858
|
+
|
|
2859
|
+
var strQuery = "";
|
|
2860
|
+
var $that = this;
|
|
2861
|
+
if(whereEntity){
|
|
2862
|
+
var entity = this.getEntity(query.parentName, query.entityMap);
|
|
2863
|
+
|
|
2864
|
+
var item = whereEntity[query.parentName].query;
|
|
2865
|
+
for (let exp in item.expressions) {
|
|
2866
|
+
var field = item.expressions[exp].field.toLowerCase();
|
|
2867
|
+
// var field = tools.capitalizeFirstLetter(item.expressions[exp].field); removed this because it was causing issues not sure why we added it in the firstplace
|
|
2868
|
+
if(mainQuery[field]){
|
|
2869
|
+
if(mainQuery[field].isNavigational){
|
|
2870
|
+
entity = $that.getEntity(field, query.entityMap);
|
|
2871
|
+
field = item.fields[1];
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2874
|
+
if(item.expressions[exp].arg === "null"){
|
|
2875
|
+
if(item.expressions[exp].func === "="){
|
|
2876
|
+
item.expressions[exp].func = "is"
|
|
2877
|
+
}
|
|
2878
|
+
if(item.expressions[exp].func === "!="){
|
|
2879
|
+
item.expressions[exp].func = "is not"
|
|
2880
|
+
}
|
|
2881
|
+
}
|
|
2882
|
+
if(strQuery === ""){
|
|
2883
|
+
if(item.expressions[exp].arg === "null"){
|
|
2884
|
+
strQuery = `WHERE ${entity}.${field} ${item.expressions[exp].func} ${item.expressions[exp].arg}`;
|
|
2885
|
+
}else{
|
|
2886
|
+
if(item.expressions[exp].func === "IN"){
|
|
2887
|
+
strQuery = `WHERE ${entity}.${field} ${item.expressions[exp].func} ${item.expressions[exp].arg}`;
|
|
2888
|
+
}
|
|
2889
|
+
else{
|
|
2890
|
+
strQuery = `WHERE ${entity}.${field} ${item.expressions[exp].func} '${item.expressions[exp].arg}'`;
|
|
2891
|
+
}
|
|
2892
|
+
}
|
|
2893
|
+
}
|
|
2894
|
+
else{
|
|
2895
|
+
if(item.expressions[exp].arg === "null"){
|
|
2896
|
+
strQuery = `${strQuery} and ${entity}.${field} ${item.expressions[exp].func} ${item.expressions[exp].arg}`;
|
|
2897
|
+
}else{
|
|
2898
|
+
strQuery = `${strQuery} and ${entity}.${field} ${item.expressions[exp].func} '${item.expressions[exp].arg}'`;
|
|
2899
|
+
}
|
|
2900
|
+
|
|
2901
|
+
}
|
|
2902
|
+
}
|
|
2903
|
+
|
|
2904
|
+
|
|
2905
|
+
|
|
2906
|
+
}
|
|
2907
|
+
return strQuery;
|
|
2908
|
+
}
|
|
2909
|
+
|
|
2910
|
+
buildInclude( query, entity, context){
|
|
2911
|
+
var includeQuery = "";
|
|
2912
|
+
for (let part in query.include) {
|
|
2913
|
+
var includeEntity = query.include[part];
|
|
2914
|
+
var $that = this;
|
|
2915
|
+
if(includeEntity){
|
|
2916
|
+
var parentObj = includeEntity[query.parentName];
|
|
2917
|
+
var currentContext = "";
|
|
2918
|
+
if(includeEntity.selectFields){
|
|
2919
|
+
currentContext = context[tools.capitalizeFirstLetter(includeEntity.selectFields[0])];
|
|
2920
|
+
}
|
|
2921
|
+
|
|
2922
|
+
if(parentObj){
|
|
2923
|
+
parentObj.entityMap = query.entityMap;
|
|
2924
|
+
var foreignKey = $that.getForeignKey(entity.__name, currentContext.__entity);
|
|
2925
|
+
var mainPrimaryKey = $that.getPrimarykey(entity);
|
|
2926
|
+
var mainEntity = $that.getEntity(entity.__name, query.entityMap);
|
|
2927
|
+
if(currentContext.__entity[entity.__name].type === "hasManyThrough"){
|
|
2928
|
+
var foreignTable = tools.capitalizeFirstLetter(currentContext.__entity[entity.__name].foreignTable); //to uppercase letter
|
|
2929
|
+
foreignKey = $that.getPrimarykey(currentContext.__entity);
|
|
2930
|
+
mainPrimaryKey = context[foreignTable].__entity[currentContext.__entity.__name].foreignKey;
|
|
2931
|
+
var mainEntity = $that.getEntity(foreignTable,query.entityMap);
|
|
2932
|
+
}
|
|
2933
|
+
// add foreign key to select so that it picks it up
|
|
2934
|
+
if(parentObj.select){
|
|
2935
|
+
parentObj.select.selectFields.push(foreignKey);
|
|
2936
|
+
}else{
|
|
2937
|
+
parentObj.select = {};
|
|
2938
|
+
parentObj.select.selectFields = [];
|
|
2939
|
+
parentObj.select.selectFields.push(foreignKey);
|
|
2940
|
+
}
|
|
2941
|
+
|
|
2942
|
+
var innerQuery = $that.buildQuery(parentObj, currentContext.__entity, context);
|
|
2943
|
+
|
|
2944
|
+
includeQuery += `LEFT JOIN (${innerQuery.query}) AS ${innerQuery.entity} ON ${ mainEntity}.${mainPrimaryKey} = ${innerQuery.entity}.${foreignKey} `;
|
|
2945
|
+
|
|
2946
|
+
}
|
|
2947
|
+
}
|
|
2948
|
+
}
|
|
2949
|
+
return includeQuery;
|
|
2950
|
+
}
|
|
2951
|
+
|
|
2952
|
+
buildFrom(query, entity){
|
|
2953
|
+
var entityName = this.getEntity(entity.__name, query.entityMap);
|
|
2954
|
+
if(entityName ){
|
|
2955
|
+
return `FROM ${entity.__name } AS ${entityName}`;
|
|
2956
|
+
}
|
|
2957
|
+
else{ return "" }
|
|
2958
|
+
}
|
|
2959
|
+
|
|
2960
|
+
buildSelect(query, entity){
|
|
2961
|
+
// this means that there is a select statement
|
|
2962
|
+
var select = "SELECT";
|
|
2963
|
+
var arr = "";
|
|
2964
|
+
var $that = this;
|
|
2965
|
+
if(query.select){
|
|
2966
|
+
for (const item in query.select.selectFields) {
|
|
2967
|
+
arr += `${$that.getEntity(entity.__name, query.entityMap)}.${query.select.selectFields[item]}, `;
|
|
2968
|
+
};
|
|
2969
|
+
|
|
2970
|
+
}
|
|
2971
|
+
else{
|
|
2972
|
+
var entityList = this.getEntityList(entity);
|
|
2973
|
+
for (const item in entityList) {
|
|
2974
|
+
arr += `${$that.getEntity(entity.__name, query.entityMap)}.${entityList[item]}, `;
|
|
2975
|
+
};
|
|
2976
|
+
}
|
|
2977
|
+
arr = arr.replace(/,\s*$/, "");
|
|
2978
|
+
return `${select} ${arr} `;
|
|
2979
|
+
}
|
|
2980
|
+
|
|
2981
|
+
getForeignKey(name, entity){
|
|
2982
|
+
if(entity && name){
|
|
2983
|
+
return entity[name].foreignKey;
|
|
2984
|
+
}
|
|
2985
|
+
}
|
|
2986
|
+
|
|
2987
|
+
getPrimarykey(entity){
|
|
2988
|
+
for (const item in entity) {
|
|
2989
|
+
if(entity[item].primary){
|
|
2990
|
+
if(entity[item].primary === true){
|
|
2991
|
+
return entity[item].name;
|
|
2992
|
+
}
|
|
2993
|
+
}
|
|
2994
|
+
};
|
|
2995
|
+
}
|
|
2996
|
+
|
|
2997
|
+
getForeignTable(name, entity){
|
|
2998
|
+
if(entity && name){
|
|
2999
|
+
return entity[name].foreignTable;
|
|
3000
|
+
}
|
|
3001
|
+
}
|
|
3002
|
+
|
|
3003
|
+
getInclude(name, query){
|
|
3004
|
+
var include = query.include;
|
|
3005
|
+
if(include){
|
|
3006
|
+
for (let part in include) {
|
|
3007
|
+
if(tools.capitalizeFirstLetter(include[part].selectFields[0]) === name){
|
|
3008
|
+
return include[part];
|
|
3009
|
+
}
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
3012
|
+
else{
|
|
3013
|
+
return "";
|
|
3014
|
+
}
|
|
3015
|
+
}
|
|
3016
|
+
|
|
3017
|
+
getEntity(name, maps){
|
|
3018
|
+
for (let item in maps) {
|
|
3019
|
+
var map = maps[item];
|
|
3020
|
+
if(tools.capitalizeFirstLetter(name) === tools.capitalizeFirstLetter(map.name)){
|
|
3021
|
+
return map.entity
|
|
3022
|
+
}
|
|
3023
|
+
}
|
|
3024
|
+
return "";
|
|
3025
|
+
}
|
|
3026
|
+
|
|
3027
|
+
// return a list of entity names and skip foreign keys and underscore.
|
|
3028
|
+
getEntityList(entity){
|
|
3029
|
+
var entitiesList = [];
|
|
3030
|
+
var $that = this;
|
|
3031
|
+
for (var ent in entity) {
|
|
3032
|
+
if(!ent.startsWith("_")){
|
|
3033
|
+
if(!entity[ent].foreignKey){
|
|
3034
|
+
if(entity[ent].relationshipTable){
|
|
3035
|
+
if($that.chechUnsupportedWords(entity[ent].relationshipTable)){
|
|
3036
|
+
entitiesList.push(`'${entity[ent].relationshipTable}'`);
|
|
3037
|
+
}
|
|
3038
|
+
else{
|
|
3039
|
+
entitiesList.push(entity[ent].relationshipTable);
|
|
3040
|
+
}
|
|
3041
|
+
}
|
|
3042
|
+
else{
|
|
3043
|
+
if($that.chechUnsupportedWords(ent)){
|
|
3044
|
+
entitiesList.push(`'${ent}'`);
|
|
3045
|
+
}
|
|
3046
|
+
else{
|
|
3047
|
+
entitiesList.push(ent);
|
|
3048
|
+
}
|
|
3049
|
+
}
|
|
3050
|
+
}
|
|
3051
|
+
else{
|
|
3052
|
+
|
|
3053
|
+
if(entity[ent].relationshipType === "belongsTo"){
|
|
3054
|
+
var name = entity[ent].foreignKey;
|
|
3055
|
+
if($that.chechUnsupportedWords(name)){
|
|
3056
|
+
entitiesList.push(`'${name}'`);
|
|
3057
|
+
//entitiesList.push(`'${ent}'`);
|
|
3058
|
+
}
|
|
3059
|
+
else{
|
|
3060
|
+
entitiesList.push(name);
|
|
3061
|
+
//entitiesList.push(ent);
|
|
3062
|
+
}
|
|
3063
|
+
}
|
|
3064
|
+
|
|
3065
|
+
}
|
|
3066
|
+
}
|
|
3067
|
+
}
|
|
3068
|
+
return entitiesList
|
|
3069
|
+
}
|
|
3070
|
+
chechUnsupportedWords(word){
|
|
3071
|
+
for (var item in this.unsupportedWords) {
|
|
3072
|
+
var text = this.unsupportedWords[item];
|
|
3073
|
+
if(text === word){
|
|
3074
|
+
return true
|
|
3075
|
+
}
|
|
3076
|
+
}
|
|
3077
|
+
return false;
|
|
3078
|
+
}
|
|
3079
|
+
|
|
3080
|
+
startTransaction(){
|
|
3081
|
+
this.db.prepare('BEGIN').run();
|
|
3082
|
+
}
|
|
3083
|
+
|
|
3084
|
+
endTransaction(){
|
|
3085
|
+
this.db.prepare('COMMIT').run();
|
|
3086
|
+
}
|
|
3087
|
+
|
|
3088
|
+
errorTransaction(){
|
|
3089
|
+
this.db.prepare('ROLLBACK').run();
|
|
3090
|
+
}
|
|
3091
|
+
|
|
3092
|
+
_buildSQLEqualTo(model){
|
|
3093
|
+
var $that = this;
|
|
3094
|
+
var argument = null;
|
|
3095
|
+
var dirtyFields = model.__dirtyFields;
|
|
3096
|
+
|
|
3097
|
+
for (var column in dirtyFields) {
|
|
3098
|
+
|
|
3099
|
+
var type = model.__entity[dirtyFields[column]].type;
|
|
3100
|
+
|
|
3101
|
+
if(model.__entity[dirtyFields[column]].relationshipType === "belongsTo"){
|
|
3102
|
+
type = "belongsTo";
|
|
3103
|
+
}
|
|
3104
|
+
// TODO Boolean value is a string with a letter
|
|
3105
|
+
switch(type){
|
|
3106
|
+
case "belongsTo" :
|
|
3107
|
+
const foreignKey = model.__entity[dirtyFields[column]].foreignKey;
|
|
3108
|
+
argument = `${foreignKey} = ${model[dirtyFields[column]]},`;
|
|
3109
|
+
break;
|
|
3110
|
+
case "integer" :
|
|
3111
|
+
//model.__entity[dirtyFields[column]].skipGetFunction = true;
|
|
3112
|
+
var columneValue = model[`_${dirtyFields[column]}`];
|
|
3113
|
+
argument = argument === null ? `[${dirtyFields[column]}] = ${model[dirtyFields[column]]},` : `${argument} [${dirtyFields[column]}] = ${columneValue},`;
|
|
3114
|
+
//model.__entity[dirtyFields[column]].skipGetFunction = false;
|
|
3115
|
+
break;
|
|
3116
|
+
case "string" :
|
|
3117
|
+
argument = argument === null ? `[${dirtyFields[column]}] = '${$that._santizeSingleQuotes(model[dirtyFields[column]])}',` : `${argument} [${dirtyFields[column]}] = '${$that._santizeSingleQuotes(model[dirtyFields[column]])}',`;
|
|
3118
|
+
break;
|
|
3119
|
+
case "boolean" :
|
|
3120
|
+
var bool = "";
|
|
3121
|
+
if(model.__entity[dirtyFields[column]].valueConversion){
|
|
3122
|
+
bool = tools.convertBooleanToNumber(model[dirtyFields[column]]);
|
|
3123
|
+
}
|
|
3124
|
+
else{
|
|
3125
|
+
bool = model[dirtyFields[column]];
|
|
3126
|
+
}
|
|
3127
|
+
argument = argument === null ? `[${dirtyFields[column]}] = '${bool}',` : `${argument} [${dirtyFields[column]}] = ${bool},`;
|
|
3128
|
+
break;
|
|
3129
|
+
case "time" :
|
|
3130
|
+
argument = argument === null ? `[${dirtyFields[column]}] = '${model[dirtyFields[column]]}',` : `${argument} [${dirtyFields[column]}] = ${model[dirtyFields[column]]},`;
|
|
3131
|
+
break;
|
|
3132
|
+
case "belongsTo" :
|
|
3133
|
+
var fore = `_${dirtyFields[column]}`;
|
|
3134
|
+
argument = argument === null ? `[${model.__entity[dirtyFields[column]].foreignKey}] = '${model[fore]}',` : `${argument} [${model.__entity[dirtyFields[column]].foreignKey}] = '${model[fore]}',`;
|
|
3135
|
+
break;
|
|
3136
|
+
case "hasMany" :
|
|
3137
|
+
argument = argument === null ? `[${dirtyFields[column]}] = '${model[dirtyFields[column]]}',` : `${argument} [${dirtyFields[column]}] = '${model[dirtyFields[column]]}',`;
|
|
3138
|
+
break;
|
|
3139
|
+
default:
|
|
3140
|
+
argument = argument === null ? `[${dirtyFields[column]}] = '${model[dirtyFields[column]]}',` : `${argument} [${dirtyFields[column]}] = '${model[dirtyFields[column]]}',`;
|
|
3141
|
+
}
|
|
3142
|
+
}
|
|
3143
|
+
|
|
3144
|
+
if(argument){
|
|
3145
|
+
return argument.replace(/,\s*$/, "");
|
|
3146
|
+
}
|
|
3147
|
+
else{
|
|
3148
|
+
return -1;
|
|
3149
|
+
}
|
|
3150
|
+
|
|
3151
|
+
}
|
|
3152
|
+
|
|
3153
|
+
|
|
3154
|
+
_buildDeleteObject(currentModel){
|
|
3155
|
+
var primaryKey = currentModel.__Key === undefined ? tools.getPrimaryKeyObject(currentModel.__entity) : currentModel.__Key;
|
|
3156
|
+
var value = currentModel.__value === undefined ? currentModel[primaryKey] : currentModel.__value;
|
|
3157
|
+
var tableName = currentModel.__tableName === undefined ? currentModel.__entity.__name : currentModel.__tableName;
|
|
3158
|
+
return {tableName: tableName, primaryKey : primaryKey, value : value};
|
|
3159
|
+
}
|
|
3160
|
+
|
|
3161
|
+
|
|
3162
|
+
// return columns and value strings
|
|
3163
|
+
_buildSQLInsertObject(fields, modelEntity){
|
|
3164
|
+
var $that = this;
|
|
3165
|
+
var columns = null;
|
|
3166
|
+
var values = null;
|
|
3167
|
+
for (var column in modelEntity) {
|
|
3168
|
+
// column1 = value1, column2 = value2, ...
|
|
3169
|
+
if(column.indexOf("__") === -1 ){
|
|
3170
|
+
// call the get method if avlable
|
|
3171
|
+
var fieldColumn = "";
|
|
3172
|
+
// check if get function is avaliable if so use that
|
|
3173
|
+
fieldColumn = fields[column];
|
|
3174
|
+
|
|
3175
|
+
if((fieldColumn !== undefined && fieldColumn !== null ) && typeof(fieldColumn) !== "object"){
|
|
3176
|
+
switch(modelEntity[column].type){
|
|
3177
|
+
case "string" :
|
|
3178
|
+
fieldColumn = `'${$that._santizeSingleQuotes(fields[column])}'`;
|
|
3179
|
+
break;
|
|
3180
|
+
case "time" :
|
|
3181
|
+
fieldColumn = fields[column];
|
|
3182
|
+
break;
|
|
3183
|
+
}
|
|
3184
|
+
|
|
3185
|
+
var relationship = modelEntity[column].relationshipType
|
|
3186
|
+
if(relationship === "belongsTo"){
|
|
3187
|
+
column = modelEntity[column].foreignKey
|
|
3188
|
+
}
|
|
3189
|
+
|
|
3190
|
+
columns = columns === null ? `'${column}',` : `${columns} '${column}',`;
|
|
3191
|
+
values = values === null ? `${fieldColumn},` : `${values} ${fieldColumn},`;
|
|
3192
|
+
|
|
3193
|
+
}
|
|
3194
|
+
else{
|
|
3195
|
+
switch(modelEntity[column].type){
|
|
3196
|
+
case "belongsTo" :
|
|
3197
|
+
var fieldObject = tools.findTrackedObject(fields.__context.__trackedEntities, column );
|
|
3198
|
+
if( Object.keys(fieldObject).length > 0){
|
|
3199
|
+
var primaryKey = tools.getPrimaryKeyObject(fieldObject.__entity);
|
|
3200
|
+
fieldColumn = fieldObject[primaryKey];
|
|
3201
|
+
column = modelEntity[column].foreignKey;
|
|
3202
|
+
columns = columns === null ? `'${column}',` : `${columns} '${column}',`;
|
|
3203
|
+
values = values === null ? `${fieldColumn},` : `${values} ${fieldColumn},`;
|
|
3204
|
+
}else{
|
|
3205
|
+
console.log("Cannot find belings to relationship")
|
|
3206
|
+
}
|
|
3207
|
+
|
|
3208
|
+
break;
|
|
3209
|
+
}
|
|
3210
|
+
|
|
3211
|
+
}
|
|
3212
|
+
}
|
|
3213
|
+
}
|
|
3214
|
+
return {tableName: modelEntity.__name, columns: columns.replace(/,\s*$/, ""), values: values.replace(/,\s*$/, "")};
|
|
3215
|
+
|
|
3216
|
+
}
|
|
3217
|
+
|
|
3218
|
+
// will add double single quotes to allow sting to be saved.
|
|
3219
|
+
_santizeSingleQuotes(string){
|
|
3220
|
+
if (typeof string === 'string' || string instanceof String){
|
|
3221
|
+
return string.replace(/'/g, "''");
|
|
3222
|
+
}
|
|
3223
|
+
else{
|
|
3224
|
+
console.log("warning - Field being passed is not a string");
|
|
3225
|
+
throw "error warning - Field being passed is not a string";
|
|
3226
|
+
}
|
|
3227
|
+
}
|
|
3228
|
+
|
|
3229
|
+
// converts any object into SQL parameter select string
|
|
3230
|
+
_convertEntityToSelectParameterString(obj, entityName){
|
|
3231
|
+
// todo: loop throgh object and append string with comma to
|
|
3232
|
+
var mainString = "";
|
|
3233
|
+
const entries = Object.keys(obj);
|
|
3234
|
+
|
|
3235
|
+
for (const [name] of entries) {
|
|
3236
|
+
mainString += `${mainString}, ${entityName}.${name}`;
|
|
3237
|
+
}
|
|
3238
|
+
return mainString;;
|
|
3239
|
+
}
|
|
3240
|
+
|
|
3241
|
+
_execute(query){
|
|
3242
|
+
console.log("SQL:", query);
|
|
3243
|
+
return this.db.exec(query);
|
|
3244
|
+
}
|
|
3245
|
+
|
|
3246
|
+
_run(query){
|
|
3247
|
+
console.log("SQL:", query);
|
|
3248
|
+
return this.db.prepare(query).run();
|
|
3249
|
+
}
|
|
3250
|
+
|
|
3251
|
+
setDB(db, type){
|
|
3252
|
+
this.db = db;
|
|
3253
|
+
this.dbType = type; // this will let us know which type of sqlengine to use.
|
|
3254
|
+
}
|
|
3255
|
+
}
|
|
3256
|
+
|
|
3257
|
+
module.exports = SQLLiteEngine;
|
|
3258
|
+
** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/SQLLiteEngine.js **
|
|
3259
|
+
// Version 0.0.4
|
|
3260
|
+
class Tools{
|
|
3261
|
+
|
|
3262
|
+
static checkIfArrayLike(obj) {
|
|
3263
|
+
if (Array.isArray(obj)) {
|
|
3264
|
+
return true;
|
|
3265
|
+
}
|
|
3266
|
+
|
|
3267
|
+
if (
|
|
3268
|
+
obj &&
|
|
3269
|
+
typeof obj === 'object' &&
|
|
3270
|
+
Object.keys(obj).some(k => !isNaN(k)) &&
|
|
3271
|
+
'0' in obj
|
|
3272
|
+
) {
|
|
3273
|
+
return true;
|
|
3274
|
+
}
|
|
3275
|
+
|
|
3276
|
+
return -1;
|
|
3277
|
+
}
|
|
3278
|
+
|
|
3279
|
+
static returnEntityList(list, entityList ){
|
|
3280
|
+
var newList = [];
|
|
3281
|
+
for(var max = 0; max < list.length; max++ ){
|
|
3282
|
+
var ent = entityList[list[max]];
|
|
3283
|
+
if(ent){
|
|
3284
|
+
if(ent.relationshipType === "hasMany" || ent.relationshipType === "hasOne"){
|
|
3285
|
+
newList.push(ent.name);
|
|
3286
|
+
}
|
|
3287
|
+
}
|
|
3288
|
+
}
|
|
3289
|
+
return newList;
|
|
3290
|
+
}
|
|
3291
|
+
|
|
3292
|
+
static findEntity(name, entityList){
|
|
3293
|
+
return entityList[name];
|
|
3294
|
+
}
|
|
3295
|
+
|
|
3296
|
+
// this will remove everthing from back slash amount
|
|
3297
|
+
static removeBackwardSlashSection(string, amount, type){
|
|
3298
|
+
type = type === undefined ? "\\" : type;
|
|
3299
|
+
var stringArray = string.split(type);
|
|
3300
|
+
for(var i = 0; i < amount; i++){
|
|
3301
|
+
stringArray.pop();
|
|
3302
|
+
}
|
|
3303
|
+
return stringArray.join(type);
|
|
3304
|
+
}
|
|
3305
|
+
|
|
3306
|
+
static getPrimaryKeyObject(model){
|
|
3307
|
+
for (var key in model) {
|
|
3308
|
+
if (model.hasOwnProperty(key)) {
|
|
3309
|
+
if(model[key].primary){
|
|
3310
|
+
if(model[key].primary === true){
|
|
3311
|
+
return key
|
|
3312
|
+
}
|
|
3313
|
+
}
|
|
3314
|
+
}
|
|
3315
|
+
}
|
|
3316
|
+
}
|
|
3317
|
+
|
|
3318
|
+
static findForeignTable(name, model){
|
|
3319
|
+
for (var key in model) {
|
|
3320
|
+
if (model.hasOwnProperty(key)) {
|
|
3321
|
+
if(model[key].foreignTable){
|
|
3322
|
+
if(model[key].foreignTable === name){
|
|
3323
|
+
return model[key];
|
|
3324
|
+
}
|
|
3325
|
+
}
|
|
3326
|
+
}
|
|
3327
|
+
}
|
|
3328
|
+
return null;
|
|
3329
|
+
}
|
|
3330
|
+
|
|
3331
|
+
static createNewInstance(validModel, type, classModel){
|
|
3332
|
+
return new type(validModel, classModel);
|
|
3333
|
+
}
|
|
3334
|
+
|
|
3335
|
+
static findTrackedObject(obj, name){
|
|
3336
|
+
for (const property in obj) {
|
|
3337
|
+
if(obj[property].__name === name){
|
|
3338
|
+
return obj[property];
|
|
3339
|
+
}
|
|
3340
|
+
}
|
|
3341
|
+
return {};
|
|
3342
|
+
}
|
|
3343
|
+
|
|
3344
|
+
static clearAllProto(proto){
|
|
3345
|
+
|
|
3346
|
+
var newproto = {}
|
|
3347
|
+
if(proto.__proto__ ){
|
|
3348
|
+
for (var key in proto) {
|
|
3349
|
+
if(!key.startsWith("_")){
|
|
3350
|
+
var typeObj = typeof(proto[key]);
|
|
3351
|
+
newproto[key] = proto[key];
|
|
3352
|
+
if(typeObj === "object"){
|
|
3353
|
+
proto[key] = this.clearAllProto(proto[key]);
|
|
3354
|
+
}
|
|
3355
|
+
}
|
|
3356
|
+
}
|
|
3357
|
+
}
|
|
3358
|
+
|
|
3359
|
+
newproto["__name"] = proto["__name"];
|
|
3360
|
+
newproto["__state"] = proto["__state"];
|
|
3361
|
+
newproto["__entity"] = proto["__entity"];
|
|
3362
|
+
newproto["__context"] = proto["__context"];
|
|
3363
|
+
newproto["__dirtyFields"] = proto["__dirtyFields"];
|
|
3364
|
+
|
|
3365
|
+
newproto.__proto__ = null;
|
|
3366
|
+
return newproto;
|
|
3367
|
+
|
|
3368
|
+
}
|
|
3369
|
+
|
|
3370
|
+
static removePrimarykeyandVirtual(currentModel, modelEntity){
|
|
3371
|
+
var newCurrentModel = Object.create(currentModel);
|
|
3372
|
+
|
|
3373
|
+
for(var entity in modelEntity) {
|
|
3374
|
+
var currentEntity = modelEntity[entity];
|
|
3375
|
+
if (modelEntity.hasOwnProperty(entity)) {
|
|
3376
|
+
if(currentEntity.primary === true){
|
|
3377
|
+
delete newCurrentModel[`_${entity}`];
|
|
3378
|
+
}
|
|
3379
|
+
}
|
|
3380
|
+
if(currentEntity.virtual === true){
|
|
3381
|
+
// skip it from the insert
|
|
3382
|
+
delete newCurrentModel[`_${entity}`];
|
|
3383
|
+
}
|
|
3384
|
+
|
|
3385
|
+
}
|
|
3386
|
+
return newCurrentModel;
|
|
3387
|
+
}
|
|
3388
|
+
|
|
3389
|
+
static getEntity(name, modelEntity){
|
|
3390
|
+
for(var entity in modelEntity) {
|
|
3391
|
+
var currentEntity = modelEntity[entity];
|
|
3392
|
+
if (modelEntity.hasOwnProperty(entity)) {
|
|
3393
|
+
if(currentEntity.__name === name){
|
|
3394
|
+
return currentEntity;
|
|
3395
|
+
}
|
|
3396
|
+
}
|
|
3397
|
+
}
|
|
3398
|
+
return false;
|
|
3399
|
+
}
|
|
3400
|
+
|
|
3401
|
+
static capitalize = (s) => {
|
|
3402
|
+
if (typeof s !== 'string') return ''
|
|
3403
|
+
return s.charAt(0).toUpperCase() + s.slice(1)
|
|
3404
|
+
}
|
|
3405
|
+
|
|
3406
|
+
static capitalizeFirstLetter(string) {
|
|
3407
|
+
return string.charAt(0).toUpperCase() + string.slice(1);
|
|
3408
|
+
}
|
|
3409
|
+
|
|
3410
|
+
// return randome letter that is not the skip letter
|
|
3411
|
+
static getRandomLetter(length, skip){
|
|
3412
|
+
var result = '';
|
|
3413
|
+
var characters = 'abcdefghijklmnopqrstuvwxyz';
|
|
3414
|
+
var charactersLength = characters.length;
|
|
3415
|
+
|
|
3416
|
+
for ( var i = 0; i < length; i++ ) {
|
|
3417
|
+
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
|
3418
|
+
if(skip){
|
|
3419
|
+
for ( var b = 0; b < skip.length; b++ ) {
|
|
3420
|
+
if(result === skip[i].entity){
|
|
3421
|
+
result = "";
|
|
3422
|
+
i--;
|
|
3423
|
+
}
|
|
3424
|
+
}
|
|
3425
|
+
}
|
|
3426
|
+
}
|
|
3427
|
+
|
|
3428
|
+
return result;
|
|
3429
|
+
}
|
|
3430
|
+
|
|
3431
|
+
// TODO: this should be removed once we create a SQLIte Manager;
|
|
3432
|
+
// converts any object into SQL parameter select string
|
|
3433
|
+
|
|
3434
|
+
static convertEntityToSelectParameterString(obj){
|
|
3435
|
+
// todo: loop throgh object and append string with comma to
|
|
3436
|
+
var mainString = "";
|
|
3437
|
+
const entries = Object.keys(obj);
|
|
3438
|
+
for (const key of entries) {
|
|
3439
|
+
if(obj[key].type !== 'hasManyThrough' && obj[key].type !== "hasMany" && obj[key].type !== "hasOne"){
|
|
3440
|
+
if(obj[key].name){
|
|
3441
|
+
mainString = mainString === "" ? `${obj.__name}.${obj[key].name}` : `${mainString}, ${obj.__name}.${obj[key].name}`;
|
|
3442
|
+
}
|
|
3443
|
+
}
|
|
3444
|
+
}
|
|
3445
|
+
return mainString;;
|
|
3446
|
+
}
|
|
3447
|
+
|
|
3448
|
+
static convertBooleanToNumber(num) {
|
|
3449
|
+
num = num === 'true' ? true : (num === 'false' ? false : num);
|
|
3450
|
+
return num ? 1 : 0;
|
|
3451
|
+
}
|
|
3452
|
+
}
|
|
3453
|
+
|
|
3454
|
+
module.exports = Tools;
|
|
3455
|
+
** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/Tools.js **
|
|
3456
|
+
// Version 0.0.13
|
|
3457
|
+
|
|
3458
|
+
var modelBuilder = require('./Entity/entityModelBuilder');
|
|
3459
|
+
var query = require('masterrecord/QueryLanguage/queryMethods');
|
|
3460
|
+
var tools = require('./Tools');
|
|
3461
|
+
var SQLLiteEngine = require('masterrecord/SQLLiteEngine');
|
|
3462
|
+
var MYSQLEngine = require('masterrecord/mySQLEngine');
|
|
3463
|
+
var insertManager = require('./insertManager');
|
|
3464
|
+
var deleteManager = require('./deleteManager');
|
|
3465
|
+
var globSearch = require("glob");
|
|
3466
|
+
var fs = require('fs');
|
|
3467
|
+
const appRoot = require('app-root-path');
|
|
3468
|
+
const MySQLClient = require('masterrecord/mySQLSyncConnect');
|
|
3469
|
+
|
|
3470
|
+
class context {
|
|
3471
|
+
_isModelValid = {
|
|
3472
|
+
isValid: true,
|
|
3473
|
+
errors: []
|
|
3474
|
+
};
|
|
3475
|
+
__entities = [];
|
|
3476
|
+
__builderEntities = [];
|
|
3477
|
+
__trackedEntities = [];
|
|
3478
|
+
__relationshipModels = [];
|
|
3479
|
+
__environment = "";
|
|
3480
|
+
__name = "";
|
|
3481
|
+
isSQLite = false;
|
|
3482
|
+
isMySQL = false;
|
|
3483
|
+
isPostgres = false;
|
|
3484
|
+
|
|
3485
|
+
constructor(){
|
|
3486
|
+
this. __environment = process.env.master;
|
|
3487
|
+
this.__name = this.constructor.name;
|
|
3488
|
+
this._SQLEngine = "";
|
|
3489
|
+
}
|
|
3490
|
+
|
|
3491
|
+
/*
|
|
3492
|
+
SQLite expected model
|
|
3493
|
+
{
|
|
3494
|
+
"type": "better-sqlite3",
|
|
3495
|
+
"connection" : "/db/",
|
|
3496
|
+
"password": "",
|
|
3497
|
+
"username": ""
|
|
3498
|
+
}
|
|
3499
|
+
*/
|
|
3500
|
+
__SQLiteInit(env, sqlName){
|
|
3501
|
+
try{
|
|
3502
|
+
|
|
3503
|
+
const sqlite3 = require(sqlName);
|
|
3504
|
+
let DBAddress = env.completeConnection;
|
|
3505
|
+
var db = new sqlite3(DBAddress, env);
|
|
3506
|
+
db.__name = sqlName;
|
|
3507
|
+
this._SQLEngine = new SQLLiteEngine();
|
|
3508
|
+
return db;
|
|
3509
|
+
}
|
|
3510
|
+
catch (e) {
|
|
3511
|
+
console.log("error SQL", e);
|
|
3512
|
+
throw error(e)
|
|
3513
|
+
}
|
|
3514
|
+
}
|
|
3515
|
+
|
|
3516
|
+
/*
|
|
3517
|
+
mysql expected model
|
|
3518
|
+
{
|
|
3519
|
+
"type": "mysql",
|
|
3520
|
+
host : 'localhost',
|
|
3521
|
+
user : 'me',
|
|
3522
|
+
password : 'secret',
|
|
3523
|
+
database : 'my_db'
|
|
3524
|
+
}
|
|
3525
|
+
*/
|
|
3526
|
+
__mysqlInit(env, sqlName){
|
|
3527
|
+
try{
|
|
3528
|
+
|
|
3529
|
+
//const mysql = require(sqlName);
|
|
3530
|
+
const connection = new MySQLClient(env);
|
|
3531
|
+
this._SQLEngine = new MYSQLEngine();
|
|
3532
|
+
this._SQLEngine.__name = sqlName;
|
|
3533
|
+
return connection;
|
|
3534
|
+
|
|
3535
|
+
}
|
|
3536
|
+
catch (e) {
|
|
3537
|
+
console.log("error SQL", e);
|
|
3538
|
+
}
|
|
3539
|
+
}
|
|
3540
|
+
|
|
3541
|
+
__clearErrorHandler(){
|
|
3542
|
+
this._isModelValid = {
|
|
3543
|
+
isValid: true,
|
|
3544
|
+
errors: []
|
|
3545
|
+
};
|
|
3546
|
+
};
|
|
3547
|
+
|
|
3548
|
+
__findSettings(root, rootFolderLocation, envType){
|
|
3549
|
+
|
|
3550
|
+
if(envType === undefined){
|
|
3551
|
+
envType = "development";
|
|
3552
|
+
}
|
|
3553
|
+
var rootFolder = `${root}/${rootFolderLocation}`;
|
|
3554
|
+
var search = `${rootFolder}/**/*env.${envType}.json`;
|
|
3555
|
+
var files = globSearch.sync(search, rootFolder);
|
|
3556
|
+
var file = files[0];
|
|
3557
|
+
if(file === undefined){
|
|
3558
|
+
root = tools.removeBackwardSlashSection(root, 1, "/");
|
|
3559
|
+
rootFolder = `${root}/${rootFolderLocation}`;
|
|
3560
|
+
var search = `${rootFolder}/**/*env.${envType}.json`;
|
|
3561
|
+
var files = globSearch.sync(search,rootFolder);
|
|
3562
|
+
file = files[0];
|
|
3563
|
+
if(file === undefined){
|
|
3564
|
+
root = tools.removeBackwardSlashSection(root, 1, "/");
|
|
3565
|
+
rootFolder = `${root}/${rootFolderLocation}`;
|
|
3566
|
+
var search = `${rootFolder}/**/*env.${envType}.json`;
|
|
3567
|
+
var files = globSearch.sync(search,rootFolder);
|
|
3568
|
+
file = files[0];
|
|
3569
|
+
if(file === undefined){
|
|
3570
|
+
console.log(`could not find file - ${rootFolder}/env.${envType}.json`);
|
|
3571
|
+
throw error(`could not find file - ${rootFolder}/env.${envType}.json`);
|
|
3572
|
+
}
|
|
3573
|
+
|
|
3574
|
+
}
|
|
3575
|
+
|
|
3576
|
+
}
|
|
3577
|
+
|
|
3578
|
+
return {
|
|
3579
|
+
file: file,
|
|
3580
|
+
rootFolder : root
|
|
3581
|
+
};
|
|
3582
|
+
}
|
|
3583
|
+
|
|
3584
|
+
useSqlite(rootFolderLocation){
|
|
3585
|
+
try{
|
|
3586
|
+
this.isSQLite = true;
|
|
3587
|
+
var root = process.cwd();
|
|
3588
|
+
var envType = this.__environment;
|
|
3589
|
+
var contextName = this.__name;
|
|
3590
|
+
var file = this.__findSettings(root, rootFolderLocation, envType);
|
|
3591
|
+
var settings = require(file.file);
|
|
3592
|
+
var options = settings[contextName];
|
|
3593
|
+
|
|
3594
|
+
if(options === undefined){
|
|
3595
|
+
console.log("settings missing context name settings");
|
|
3596
|
+
throw error("settings missing context name settings");
|
|
3597
|
+
}
|
|
3598
|
+
|
|
3599
|
+
this.validateSQLiteOptions(options);
|
|
3600
|
+
options.completeConnection = `${file.rootFolder}${options.connection}`;
|
|
3601
|
+
var dbDirectory = options.completeConnection.substr(0, options.completeConnection.lastIndexOf("\/"));
|
|
3602
|
+
|
|
3603
|
+
if (!fs.existsSync(dbDirectory)){
|
|
3604
|
+
fs.mkdirSync(dbDirectory);
|
|
3605
|
+
}
|
|
3606
|
+
|
|
3607
|
+
this.db = this.__SQLiteInit(options, "better-sqlite3");
|
|
3608
|
+
this._SQLEngine.setDB(this.db, "better-sqlite3");
|
|
3609
|
+
return this;
|
|
3610
|
+
}
|
|
3611
|
+
catch(err){
|
|
3612
|
+
console.log("error:",err );
|
|
3613
|
+
throw error(err);
|
|
3614
|
+
}
|
|
3615
|
+
}
|
|
3616
|
+
|
|
3617
|
+
validateSQLiteOptions(options){
|
|
3618
|
+
if(options.hasOwnProperty('connect') === undefined){
|
|
3619
|
+
console.log("connnect string settings is missing")
|
|
3620
|
+
throw error("connection string settings is missing");
|
|
3621
|
+
}
|
|
3622
|
+
|
|
3623
|
+
}
|
|
3624
|
+
|
|
3625
|
+
useMySql(rootFolderLocation){
|
|
3626
|
+
|
|
3627
|
+
this.isMySQL = true;
|
|
3628
|
+
var envType = this.__environment;
|
|
3629
|
+
var contextName = this.__name;
|
|
3630
|
+
var root = appRoot.path;
|
|
3631
|
+
var file = this.__findSettings(root, rootFolderLocation, envType);
|
|
3632
|
+
var settings = require(file.file);
|
|
3633
|
+
var options = settings[contextName];
|
|
3634
|
+
|
|
3635
|
+
if(options === undefined){
|
|
3636
|
+
console.log("settings missing context name settings");
|
|
3637
|
+
throw error("settings missing context name settings");
|
|
3638
|
+
}
|
|
3639
|
+
|
|
3640
|
+
this.db = this.__mysqlInit(options, "mysql2");
|
|
3641
|
+
this._SQLEngine.setDB(this.db, "mysql");
|
|
3642
|
+
return this;
|
|
3643
|
+
|
|
3644
|
+
}
|
|
3645
|
+
|
|
3646
|
+
|
|
3647
|
+
dbset(model, name){
|
|
3648
|
+
var validModel = modelBuilder.create(model);
|
|
3649
|
+
validModel.__name = name === undefined ? model.name : name;
|
|
3650
|
+
this.__entities.push(validModel); // model object
|
|
3651
|
+
var buildMod = tools.createNewInstance(validModel, query, this);
|
|
3652
|
+
this.__builderEntities.push(buildMod); // query builder entites
|
|
3653
|
+
this[validModel.__name] = buildMod;
|
|
3654
|
+
}
|
|
3655
|
+
|
|
3656
|
+
modelState(){
|
|
3657
|
+
return this._isModelValid;
|
|
3658
|
+
}
|
|
3659
|
+
|
|
3660
|
+
saveChanges(){
|
|
3661
|
+
try{
|
|
3662
|
+
var tracked = this.__trackedEntities;
|
|
3663
|
+
|
|
3664
|
+
if(tracked.length > 0){
|
|
3665
|
+
// start transaction
|
|
3666
|
+
if(this.isSQLite){
|
|
3667
|
+
this._SQLEngine.startTransaction();
|
|
3668
|
+
for (var model in tracked) {
|
|
3669
|
+
var currentModel = tracked[model];
|
|
3670
|
+
switch(currentModel.__state) {
|
|
3671
|
+
case "insert":
|
|
3672
|
+
var insert = new insertManager(this._SQLEngine, this._isModelValid, this.__entities);
|
|
3673
|
+
insert.init(currentModel);
|
|
3674
|
+
|
|
3675
|
+
break;
|
|
3676
|
+
case "modified":
|
|
3677
|
+
if(currentModel.__dirtyFields.length > 0){
|
|
3678
|
+
var cleanCurrentModel = tools.removePrimarykeyandVirtual(currentModel, currentModel._entity);
|
|
3679
|
+
// build columns equal to value string
|
|
3680
|
+
var argu = this._SQLEngine._buildSQLEqualTo(cleanCurrentModel);
|
|
3681
|
+
if(argu !== -1 ){
|
|
3682
|
+
var primaryKey = tools.getPrimaryKeyObject(cleanCurrentModel.__entity);
|
|
3683
|
+
var sqlUpdate = {tableName: cleanCurrentModel.__entity.__name, arg: argu, primaryKey : primaryKey, primaryKeyValue : cleanCurrentModel[primaryKey] };
|
|
3684
|
+
this._SQLEngine.update(sqlUpdate);
|
|
3685
|
+
}
|
|
3686
|
+
else{
|
|
3687
|
+
console.log("Nothing has been tracked, modified, created or added");
|
|
3688
|
+
}
|
|
3689
|
+
|
|
3690
|
+
}
|
|
3691
|
+
else{
|
|
3692
|
+
console.log("Tracked entity modified with no values being changed");
|
|
3693
|
+
}
|
|
3694
|
+
|
|
3695
|
+
// code block
|
|
3696
|
+
break;
|
|
3697
|
+
case "delete":
|
|
3698
|
+
var deleteObject = new deleteManager(this._SQLEngine, this.__entities);
|
|
3699
|
+
deleteObject.init(currentModel);
|
|
3700
|
+
|
|
3701
|
+
break;
|
|
3702
|
+
}
|
|
3703
|
+
}
|
|
3704
|
+
this.__clearErrorHandler();
|
|
3705
|
+
this._SQLEngine.endTransaction();
|
|
3706
|
+
}
|
|
3707
|
+
if(this.isMySQL){
|
|
3708
|
+
//this._SQLEngine.startTransaction();
|
|
3709
|
+
for (var model in tracked) {
|
|
3710
|
+
var currentModel = tracked[model];
|
|
3711
|
+
switch(currentModel.__state) {
|
|
3712
|
+
case "insert":
|
|
3713
|
+
var insert = new insertManager(this._SQLEngine, this._isModelValid, this.__entities);
|
|
3714
|
+
insert.init(currentModel);
|
|
3715
|
+
|
|
3716
|
+
break;
|
|
3717
|
+
case "modified":
|
|
3718
|
+
if(currentModel.__dirtyFields.length > 0){
|
|
3719
|
+
var cleanCurrentModel = tools.removePrimarykeyandVirtual(currentModel, currentModel._entity);
|
|
3720
|
+
// build columns equal to value string
|
|
3721
|
+
var argu = this._SQLEngine._buildSQLEqualTo(cleanCurrentModel);
|
|
3722
|
+
if(argu !== -1 ){
|
|
3723
|
+
var primaryKey = tools.getPrimaryKeyObject(cleanCurrentModel.__entity);
|
|
3724
|
+
var sqlUpdate = {tableName: cleanCurrentModel.__entity.__name, arg: argu, primaryKey : primaryKey, primaryKeyValue : cleanCurrentModel[primaryKey] };
|
|
3725
|
+
this._SQLEngine.update(sqlUpdate);
|
|
3726
|
+
}
|
|
3727
|
+
else{
|
|
3728
|
+
console.log("Nothing has been tracked, modified, created or added");
|
|
3729
|
+
}
|
|
3730
|
+
|
|
3731
|
+
}
|
|
3732
|
+
else{
|
|
3733
|
+
console.log("Tracked entity modified with no values being changed");
|
|
3734
|
+
}
|
|
3735
|
+
|
|
3736
|
+
// code block
|
|
3737
|
+
break;
|
|
3738
|
+
case "delete":
|
|
3739
|
+
var deleteObject = new deleteManager(this._SQLEngine, this.__entities);
|
|
3740
|
+
deleteObject.init(currentModel);
|
|
3741
|
+
|
|
3742
|
+
break;
|
|
3743
|
+
}
|
|
3744
|
+
}
|
|
3745
|
+
this.__clearErrorHandler();
|
|
3746
|
+
//this._SQLEngine.endTransaction();
|
|
3747
|
+
}
|
|
3748
|
+
}
|
|
3749
|
+
else{
|
|
3750
|
+
console.log("save changes has no tracked entities");
|
|
3751
|
+
}
|
|
3752
|
+
}
|
|
3753
|
+
|
|
3754
|
+
catch(error){
|
|
3755
|
+
this.__clearErrorHandler();
|
|
3756
|
+
|
|
3757
|
+
console.log("error", error);
|
|
3758
|
+
if(this.isSQLite){
|
|
3759
|
+
this._SQLEngine.errorTransaction();
|
|
3760
|
+
}
|
|
3761
|
+
this.__clearTracked();
|
|
3762
|
+
throw error;
|
|
3763
|
+
}
|
|
3764
|
+
|
|
3765
|
+
this.__clearTracked();
|
|
3766
|
+
return true;
|
|
3767
|
+
}
|
|
3768
|
+
|
|
3769
|
+
|
|
3770
|
+
_execute(query){
|
|
3771
|
+
this._SQLEngine._execute(query);
|
|
3772
|
+
}
|
|
3773
|
+
|
|
3774
|
+
// __track(model){
|
|
3775
|
+
// this.__trackedEntities.push(model);
|
|
3776
|
+
// return model;
|
|
3777
|
+
// }
|
|
3778
|
+
|
|
3779
|
+
__track(model){
|
|
3780
|
+
var add = true;
|
|
3781
|
+
for (var mod in this.__trackedEntities) {
|
|
3782
|
+
var id = this.__trackedEntities[mod].__ID;
|
|
3783
|
+
if(id === undefined){
|
|
3784
|
+
id = Math.floor((Math.random() * 100000) + 1);
|
|
3785
|
+
}
|
|
3786
|
+
if(id === model.__ID){
|
|
3787
|
+
add = false;
|
|
3788
|
+
}
|
|
3789
|
+
}
|
|
3790
|
+
if(this.__trackedEntities.length === 0){
|
|
3791
|
+
this.__trackedEntities.push(model);
|
|
3792
|
+
}
|
|
3793
|
+
else{
|
|
3794
|
+
if(add){
|
|
3795
|
+
this.__trackedEntities.push(model);
|
|
3796
|
+
}
|
|
3797
|
+
}
|
|
3798
|
+
|
|
3799
|
+
return model;
|
|
3800
|
+
}
|
|
3801
|
+
|
|
3802
|
+
__findTracked(id){
|
|
3803
|
+
if(id){
|
|
3804
|
+
for (var model in this.__trackedEntities) {
|
|
3805
|
+
if(this.__trackedEntities[model].__ID === id){
|
|
3806
|
+
return this.__trackedEntities[model];
|
|
3807
|
+
}
|
|
3808
|
+
}
|
|
3809
|
+
}
|
|
3810
|
+
return null;
|
|
3811
|
+
}
|
|
3812
|
+
|
|
3813
|
+
__clearTracked(){
|
|
3814
|
+
this.__trackedEntities = [];
|
|
3815
|
+
}
|
|
3816
|
+
}
|
|
3817
|
+
|
|
3818
|
+
|
|
3819
|
+
module.exports = context;
|
|
3820
|
+
** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/context.js **
|
|
3821
|
+
// version 0.0.2
|
|
3822
|
+
var tools = require('./Tools');
|
|
3823
|
+
class DeleteManager{
|
|
3824
|
+
constructor(sqlEngine, entities){
|
|
3825
|
+
this._SQLEngine = sqlEngine;
|
|
3826
|
+
this._allEntities = entities;
|
|
3827
|
+
}
|
|
3828
|
+
|
|
3829
|
+
init(currentModel){
|
|
3830
|
+
var $that = this;
|
|
3831
|
+
try{
|
|
3832
|
+
this.cascadeDelete(currentModel);
|
|
3833
|
+
}
|
|
3834
|
+
catch(error){
|
|
3835
|
+
throw error;
|
|
3836
|
+
}
|
|
3837
|
+
}
|
|
3838
|
+
|
|
3839
|
+
cascadeDelete(currentModel){
|
|
3840
|
+
var $that = this;
|
|
3841
|
+
if(!Array.isArray(currentModel)){
|
|
3842
|
+
const entityKeys = Object.keys(currentModel.__entity);
|
|
3843
|
+
// loop though all entity properties
|
|
3844
|
+
for (const property of entityKeys) {
|
|
3845
|
+
// cascade delete
|
|
3846
|
+
if(currentModel.__entity[property].type === "hasOne" || currentModel.__entity[property].type === "hasMany"){
|
|
3847
|
+
var curModel = currentModel[property];
|
|
3848
|
+
if(curModel === null){
|
|
3849
|
+
// check if state is nullable - if so and nothing comes back dont call cascadeDelete
|
|
3850
|
+
var prp = currentModel.__entity[property];
|
|
3851
|
+
if(!prp.nullable){
|
|
3852
|
+
throw "No relationship record found - please set hasOne or hasMany to nullable. "
|
|
3853
|
+
}
|
|
3854
|
+
}
|
|
3855
|
+
else{
|
|
3856
|
+
$that.cascadeDelete( currentModel[property]);
|
|
3857
|
+
}
|
|
3858
|
+
}
|
|
3859
|
+
}
|
|
3860
|
+
this._SQLEngine.delete(currentModel);
|
|
3861
|
+
}
|
|
3862
|
+
else{
|
|
3863
|
+
|
|
3864
|
+
for(let i = 0 ; i < currentModel.length; i++) {
|
|
3865
|
+
const entityKeys = Object.keys(currentModel[i].__entity);
|
|
3866
|
+
// loop though all entity properties
|
|
3867
|
+
for (const property of entityKeys) {
|
|
3868
|
+
// cascade delete
|
|
3869
|
+
if(currentModel[i].__entity[property].type === "hasOne" || currentModel[i].__entity[property].type === "hasMany"){
|
|
3870
|
+
$that.cascadeDelete( currentModel[i][property]);
|
|
3871
|
+
}
|
|
3872
|
+
}
|
|
3873
|
+
this._SQLEngine.delete(currentModel[i]);
|
|
3874
|
+
}
|
|
3875
|
+
}
|
|
3876
|
+
|
|
3877
|
+
|
|
3878
|
+
}
|
|
3879
|
+
}
|
|
3880
|
+
|
|
3881
|
+
module.exports = DeleteManager;
|
|
3882
|
+
** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/deleteManager.js **
|
|
3883
|
+
|
|
3884
|
+
// version 0.0.10
|
|
3885
|
+
var tools = require('./Tools');
|
|
3886
|
+
var queryScript = require('masterrecord/QueryLanguage/queryScript');
|
|
3887
|
+
|
|
3888
|
+
class InsertManager {
|
|
3889
|
+
|
|
3890
|
+
constructor(sqlEngine, errorModel, allEntities ){
|
|
3891
|
+
this._SQLEngine = sqlEngine;
|
|
3892
|
+
this._errorModel = errorModel;
|
|
3893
|
+
this._allEntities = allEntities;
|
|
3894
|
+
this.__queryObject = new queryScript();
|
|
3895
|
+
}
|
|
3896
|
+
|
|
3897
|
+
init(currentModel){
|
|
3898
|
+
this.runQueries(currentModel);
|
|
3899
|
+
}
|
|
3900
|
+
|
|
3901
|
+
runQueries(currentModel){
|
|
3902
|
+
var $that = this;
|
|
3903
|
+
var cleanCurrentModel = tools.clearAllProto(currentModel);
|
|
3904
|
+
this.validateEntity(cleanCurrentModel, currentModel, currentModel.__entity);
|
|
3905
|
+
if(this._errorModel.isValid){
|
|
3906
|
+
|
|
3907
|
+
var modelEntity = currentModel.__entity;
|
|
3908
|
+
// TODO: if you try to add belongs to you must have a tag added first. if you dont throw error
|
|
3909
|
+
currentModel = this.belongsToInsert(currentModel, modelEntity);
|
|
3910
|
+
var SQL = this._SQLEngine.insert(cleanCurrentModel);
|
|
3911
|
+
var primaryKey = tools.getPrimaryKeyObject(currentModel.__entity);
|
|
3912
|
+
// return all fields that have auto and dont have a value to the current model on insert
|
|
3913
|
+
if(currentModel.__entity[primaryKey].auto === true){
|
|
3914
|
+
var query = `select * from ${currentModel.__entity.__name} where ${primaryKey} = ${ SQL.id }`;
|
|
3915
|
+
var jj = this.__queryObject.raw(query);
|
|
3916
|
+
var getQueryModel = this._SQLEngine.get(jj, currentModel.__entity, currentModel.__context );
|
|
3917
|
+
var idVal;
|
|
3918
|
+
|
|
3919
|
+
if(!getQueryModel[0]){
|
|
3920
|
+
idVal = getQueryModel[primaryKey]
|
|
3921
|
+
}
|
|
3922
|
+
else{
|
|
3923
|
+
idVal = getQueryModel[0][primaryKey];
|
|
3924
|
+
}
|
|
3925
|
+
|
|
3926
|
+
currentModel[primaryKey] = idVal;
|
|
3927
|
+
}
|
|
3928
|
+
|
|
3929
|
+
const proto = Object.getPrototypeOf(currentModel);
|
|
3930
|
+
const props = Object.getOwnPropertyNames(proto);
|
|
3931
|
+
const cleanPropList = tools.returnEntityList(props, modelEntity);
|
|
3932
|
+
const modelKeys = Object.keys(currentModel);
|
|
3933
|
+
const mergedArray = [...new Set(modelKeys.concat(cleanPropList))];
|
|
3934
|
+
// loop through model properties
|
|
3935
|
+
for (const property of mergedArray) {
|
|
3936
|
+
var propertyModel = currentModel[property];
|
|
3937
|
+
var entityProperty = modelEntity[property] ? modelEntity[property] : {};
|
|
3938
|
+
if(entityProperty.type === "hasOne"){
|
|
3939
|
+
// make sure property model is an object not a primary data type like number or string
|
|
3940
|
+
|
|
3941
|
+
if(typeof(propertyModel) === "object" || typeof(propertyModel) === "function" ){
|
|
3942
|
+
// check if model has its own entity
|
|
3943
|
+
if(modelEntity){
|
|
3944
|
+
// check if property has a value because we dont want this to run on every insert if nothing was added
|
|
3945
|
+
propertyModel.__entity = tools.getEntity(property, $that._allEntities);
|
|
3946
|
+
propertyModel[currentModel.__entity.__name] = SQL.id;
|
|
3947
|
+
$that.runQueries(propertyModel);
|
|
3948
|
+
}
|
|
3949
|
+
else{
|
|
3950
|
+
throw `Relationship "${entityProperty.name}" could not be found please check if object has correct spelling or if it has been added to the context class`
|
|
3951
|
+
}
|
|
3952
|
+
}
|
|
3953
|
+
}
|
|
3954
|
+
|
|
3955
|
+
if(entityProperty.type === "hasMany"){
|
|
3956
|
+
if(tools.checkIfArrayLike(propertyModel)){
|
|
3957
|
+
const propertyKeys = Object.keys(propertyModel);
|
|
3958
|
+
for (const propertykey of propertyKeys) {
|
|
3959
|
+
if(propertyModel[propertykey]){
|
|
3960
|
+
propertyModel[propertykey].__entity = tools.getEntity(property, $that._allEntities);
|
|
3961
|
+
propertyModel[propertykey][currentModel.__entity.__name] = SQL.id;
|
|
3962
|
+
$that.runQueries(propertyModel[propertykey]);
|
|
3963
|
+
}
|
|
3964
|
+
}
|
|
3965
|
+
}
|
|
3966
|
+
else{
|
|
3967
|
+
throw `Relationship "${entityProperty.name}" must be an array`;
|
|
3968
|
+
}
|
|
3969
|
+
}
|
|
3970
|
+
|
|
3971
|
+
}
|
|
3972
|
+
}
|
|
3973
|
+
else{
|
|
3974
|
+
var messages = this._errorModel.errors;
|
|
3975
|
+
const combinedError = messages.join('; and ');
|
|
3976
|
+
throw combinedError;
|
|
3977
|
+
|
|
3978
|
+
}
|
|
3979
|
+
}
|
|
3980
|
+
|
|
3981
|
+
|
|
3982
|
+
|
|
3983
|
+
// will insert belongs to row first and return the id so that next call can be make correctly
|
|
3984
|
+
belongsToInsert(currentModel, modelEntity){
|
|
3985
|
+
var $that = this;
|
|
3986
|
+
for(var entity in modelEntity) {
|
|
3987
|
+
if(modelEntity[entity].relationshipType === "belongsTo"){
|
|
3988
|
+
var foreignKey = modelEntity[entity].foreignKey === undefined ? modelEntity[entity].name : modelEntity[entity].foreignKey;
|
|
3989
|
+
var newPropertyModel = currentModel[foreignKey];
|
|
3990
|
+
// check if model is a an object. If so insert the child first then the parent.
|
|
3991
|
+
if(typeof newPropertyModel === 'object'){
|
|
3992
|
+
newPropertyModel.__entity = tools.getEntity(entity, $that._allEntities);
|
|
3993
|
+
var propertyCleanCurrentModel = tools.clearAllProto(newPropertyModel);
|
|
3994
|
+
this.validateEntity(propertyCleanCurrentModel, newPropertyModel, newPropertyModel.__entity);
|
|
3995
|
+
var propertySQL = this._SQLEngine.insert(newPropertyModel);
|
|
3996
|
+
currentModel[foreignKey] = propertySQL.id;
|
|
3997
|
+
}
|
|
3998
|
+
}
|
|
3999
|
+
}
|
|
4000
|
+
// todo:
|
|
4001
|
+
// loop through all modelEntity and find all the belongs to
|
|
4002
|
+
// if belongs to is true then make sql call to insert
|
|
4003
|
+
// update the currentModel.
|
|
4004
|
+
return currentModel;
|
|
4005
|
+
}
|
|
4006
|
+
|
|
4007
|
+
// validate entity for nullable fields and if the entity has any values at all
|
|
4008
|
+
validateEntity(currentModel, currentRealModel, entityModel){
|
|
4009
|
+
for(var entity in entityModel) {
|
|
4010
|
+
var currentEntity = entityModel[entity];
|
|
4011
|
+
if (entityModel.hasOwnProperty(entity)) {
|
|
4012
|
+
// check if there is a default value
|
|
4013
|
+
if(currentEntity.default){
|
|
4014
|
+
if(currentRealModel[entity] === undefined || currentRealModel[entity] === null){
|
|
4015
|
+
// if its empty add the default value
|
|
4016
|
+
currentRealModel[entity] = currentEntity.default;
|
|
4017
|
+
}
|
|
4018
|
+
}
|
|
4019
|
+
|
|
4020
|
+
// SKIP belongs too ----- // call sets for correct data for DB
|
|
4021
|
+
if(currentEntity.type !== "belongsTo" && currentEntity.type !== "hasMany"){
|
|
4022
|
+
if(currentEntity.relationshipType !== "belongsTo"){
|
|
4023
|
+
// primary is always null in an insert so validation insert must be null
|
|
4024
|
+
if(currentEntity.nullable === false && !currentEntity.primary){
|
|
4025
|
+
// if it doesnt have a get method then call error
|
|
4026
|
+
if(currentEntity.set === undefined){
|
|
4027
|
+
if(currentModel[entity] === undefined || currentModel[entity] === null ){
|
|
4028
|
+
this._errorModel.isValid = false;
|
|
4029
|
+
var errorMessage = `Entity ${currentModel.__entity.__name} column ${entity} is a required Field`;
|
|
4030
|
+
this._errorModel.errors.push(errorMessage);
|
|
4031
|
+
//throw errorMessage;
|
|
4032
|
+
}
|
|
4033
|
+
}
|
|
4034
|
+
else{
|
|
4035
|
+
var realData = currentEntity.set(currentModel[entity]);
|
|
4036
|
+
currentRealModel[entity] = realData;
|
|
4037
|
+
currentModel[entity] = realData;
|
|
4038
|
+
}
|
|
4039
|
+
}
|
|
4040
|
+
}
|
|
4041
|
+
|
|
4042
|
+
}
|
|
4043
|
+
}
|
|
4044
|
+
|
|
4045
|
+
}
|
|
4046
|
+
}
|
|
4047
|
+
|
|
4048
|
+
}
|
|
4049
|
+
|
|
4050
|
+
|
|
4051
|
+
module.exports = InsertManager;
|
|
4052
|
+
|
|
4053
|
+
** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/insertManager.js **
|
|
4054
|
+
// version : 0.0.3
|
|
4055
|
+
|
|
4056
|
+
var tools = require('masterrecord/Tools');
|
|
4057
|
+
var util = require('util');
|
|
4058
|
+
|
|
4059
|
+
class SQLLiteEngine {
|
|
4060
|
+
|
|
4061
|
+
unsupportedWords = ["order"]
|
|
4062
|
+
|
|
4063
|
+
update(query){
|
|
4064
|
+
var sqlQuery = ` UPDATE ${query.tableName} SET ${query.arg} WHERE ${query.tableName}.${query.primaryKey} = ${query.primaryKeyValue}` // primary key for that table =
|
|
4065
|
+
return this._run(sqlQuery);
|
|
4066
|
+
}
|
|
4067
|
+
|
|
4068
|
+
delete(queryObject){
|
|
4069
|
+
var sqlObject = this._buildDeleteObject(queryObject);
|
|
4070
|
+
var sqlQuery = `DELETE FROM ${sqlObject.tableName} WHERE ${sqlObject.tableName}.${sqlObject.primaryKey} = ${sqlObject.value}`;
|
|
4071
|
+
return this._run(sqlQuery);
|
|
4072
|
+
}
|
|
4073
|
+
|
|
4074
|
+
insert(queryObject){
|
|
4075
|
+
var sqlObject = this._buildSQLInsertObject(queryObject, queryObject.__entity);
|
|
4076
|
+
var query = `INSERT INTO ${sqlObject.tableName} (${sqlObject.columns}) VALUES (${sqlObject.values})`;
|
|
4077
|
+
var queryObj = this._run(query);
|
|
4078
|
+
// return
|
|
4079
|
+
var open = {
|
|
4080
|
+
"id": queryObj.insertId
|
|
4081
|
+
};
|
|
4082
|
+
return open;
|
|
4083
|
+
}
|
|
4084
|
+
|
|
4085
|
+
get(query, entity, context){
|
|
4086
|
+
var queryString = {};
|
|
4087
|
+
try {
|
|
4088
|
+
if(query.raw){
|
|
4089
|
+
queryString.query = query.raw;
|
|
4090
|
+
}
|
|
4091
|
+
else{
|
|
4092
|
+
queryString = this.buildQuery(query, entity, context);
|
|
4093
|
+
}
|
|
4094
|
+
if(queryString.query){
|
|
4095
|
+
console.log("SQL:", queryString.query);
|
|
4096
|
+
this.db.connect(this.db);
|
|
4097
|
+
const result = this.db.query(queryString.query);
|
|
4098
|
+
console.log("results:", result);
|
|
4099
|
+
return result;
|
|
4100
|
+
}
|
|
4101
|
+
return null;
|
|
4102
|
+
} catch (err) {
|
|
4103
|
+
console.error(err);
|
|
4104
|
+
return null;
|
|
4105
|
+
}
|
|
4106
|
+
}
|
|
4107
|
+
|
|
4108
|
+
getCount(queryObject, entity, context){
|
|
4109
|
+
var query = queryObject.script;
|
|
4110
|
+
var queryString = {};
|
|
4111
|
+
try {
|
|
4112
|
+
if(query.raw){
|
|
4113
|
+
queryString.query = query.raw;
|
|
4114
|
+
}
|
|
4115
|
+
else{
|
|
4116
|
+
queryString = this.buildQuery(query, entity, context);
|
|
4117
|
+
}
|
|
4118
|
+
if(queryString.query){
|
|
4119
|
+
var queryCount = queryObject.count(queryString.query)
|
|
4120
|
+
console.log("SQL:", queryCount );
|
|
4121
|
+
var queryReturn = this.db.prepare(queryCount ).get();
|
|
4122
|
+
return queryReturn;
|
|
4123
|
+
}
|
|
4124
|
+
return null;
|
|
4125
|
+
} catch (err) {
|
|
4126
|
+
console.error(err);
|
|
4127
|
+
return null;
|
|
4128
|
+
}
|
|
4129
|
+
}
|
|
4130
|
+
|
|
4131
|
+
all(query, entity, context){
|
|
4132
|
+
var queryString = {};
|
|
4133
|
+
try {
|
|
4134
|
+
if(query.raw){
|
|
4135
|
+
queryString.query = query.raw;
|
|
4136
|
+
}
|
|
4137
|
+
else{
|
|
4138
|
+
queryString = this.buildQuery(query, entity, context);
|
|
4139
|
+
}
|
|
4140
|
+
if(queryString.query){
|
|
4141
|
+
console.log("SQL:", queryString.query);
|
|
4142
|
+
this.db.connect(this.db);
|
|
4143
|
+
const result = this.db.query(queryString.query);
|
|
4144
|
+
console.log("results:", result);
|
|
4145
|
+
return result;
|
|
4146
|
+
}
|
|
4147
|
+
return null;
|
|
4148
|
+
} catch (err) {
|
|
4149
|
+
console.error(err);
|
|
4150
|
+
return null;
|
|
4151
|
+
}
|
|
4152
|
+
}
|
|
4153
|
+
|
|
4154
|
+
|
|
4155
|
+
buildQuery(query, entity, context){
|
|
4156
|
+
|
|
4157
|
+
var queryObject = {};
|
|
4158
|
+
if(entity){
|
|
4159
|
+
queryObject.entity = this.getEntity(entity.__name, query.entityMap);
|
|
4160
|
+
queryObject.select = this.buildSelect(query, entity);
|
|
4161
|
+
queryObject.from = this.buildFrom(query, entity);
|
|
4162
|
+
queryObject.include = this.buildInclude(query, entity, context, queryObject);
|
|
4163
|
+
queryObject.where = this.buildWhere(query, entity);
|
|
4164
|
+
|
|
4165
|
+
var queryString = `${queryObject.select} ${queryObject.from} ${queryObject.include} ${queryObject.where}`;
|
|
4166
|
+
return {
|
|
4167
|
+
query : queryString,
|
|
4168
|
+
entity : this.getEntity(entity.__name, query.entityMap)
|
|
4169
|
+
}
|
|
4170
|
+
}
|
|
4171
|
+
else{
|
|
4172
|
+
console.log("Error: Entity object is blank");
|
|
4173
|
+
}
|
|
4174
|
+
|
|
4175
|
+
|
|
4176
|
+
}
|
|
4177
|
+
|
|
4178
|
+
buildWhere(query, mainQuery){
|
|
4179
|
+
var whereEntity = query.where;
|
|
4180
|
+
var strQuery = "";
|
|
4181
|
+
var $that = this;
|
|
4182
|
+
if(whereEntity){
|
|
4183
|
+
var entity = this.getEntity(query.parentName, query.entityMap);
|
|
4184
|
+
for (let part in whereEntity[query.parentName]) {
|
|
4185
|
+
var item = whereEntity[query.parentName][part];
|
|
4186
|
+
for (let exp in item.expressions) {
|
|
4187
|
+
var field = tools.capitalizeFirstLetter(item.expressions[exp].field);
|
|
4188
|
+
if(mainQuery[field]){
|
|
4189
|
+
if(mainQuery[field].isNavigational){
|
|
4190
|
+
entity = $that.getEntity(field, query.entityMap);
|
|
4191
|
+
field = item.fields[1];
|
|
4192
|
+
}
|
|
4193
|
+
}
|
|
4194
|
+
if(strQuery === ""){
|
|
4195
|
+
strQuery = `WHERE ${entity}.${field} ${item.expressions[exp].func} '${item.expressions[exp].arg}'`;
|
|
4196
|
+
}
|
|
4197
|
+
else{
|
|
4198
|
+
strQuery = `${strQuery} and ${entity}.${field} ${item.expressions[exp].func} '${item.expressions[exp].arg}'`;
|
|
4199
|
+
}
|
|
4200
|
+
}
|
|
4201
|
+
}
|
|
4202
|
+
}
|
|
4203
|
+
return strQuery;
|
|
4204
|
+
}
|
|
4205
|
+
|
|
4206
|
+
buildInclude( query, entity, context){
|
|
4207
|
+
var includeQuery = "";
|
|
4208
|
+
for (let part in query.include) {
|
|
4209
|
+
var includeEntity = query.include[part];
|
|
4210
|
+
var $that = this;
|
|
4211
|
+
if(includeEntity){
|
|
4212
|
+
var parentObj = includeEntity[query.parentName];
|
|
4213
|
+
var currentContext = "";
|
|
4214
|
+
if(includeEntity.selectFields){
|
|
4215
|
+
currentContext = context[tools.capitalizeFirstLetter(includeEntity.selectFields[0])];
|
|
4216
|
+
}
|
|
4217
|
+
|
|
4218
|
+
if(parentObj){
|
|
4219
|
+
parentObj.entityMap = query.entityMap;
|
|
4220
|
+
var foreignKey = $that.getForeignKey(entity.__name, currentContext.__entity);
|
|
4221
|
+
var mainPrimaryKey = $that.getPrimarykey(entity);
|
|
4222
|
+
var mainEntity = $that.getEntity(entity.__name, query.entityMap);
|
|
4223
|
+
if(currentContext.__entity[entity.__name].type === "hasManyThrough"){
|
|
4224
|
+
var foreignTable = tools.capitalizeFirstLetter(currentContext.__entity[entity.__name].foreignTable); //to uppercase letter
|
|
4225
|
+
foreignKey = $that.getPrimarykey(currentContext.__entity);
|
|
4226
|
+
mainPrimaryKey = context[foreignTable].__entity[currentContext.__entity.__name].foreignKey;
|
|
4227
|
+
var mainEntity = $that.getEntity(foreignTable,query.entityMap);
|
|
4228
|
+
}
|
|
4229
|
+
// add foreign key to select so that it picks it up
|
|
4230
|
+
if(parentObj.select){
|
|
4231
|
+
parentObj.select.selectFields.push(foreignKey);
|
|
4232
|
+
}else{
|
|
4233
|
+
parentObj.select = {};
|
|
4234
|
+
parentObj.select.selectFields = [];
|
|
4235
|
+
parentObj.select.selectFields.push(foreignKey);
|
|
4236
|
+
}
|
|
4237
|
+
|
|
4238
|
+
var innerQuery = $that.buildQuery(parentObj, currentContext.__entity, context);
|
|
4239
|
+
|
|
4240
|
+
includeQuery += `LEFT JOIN (${innerQuery.query}) AS ${innerQuery.entity} ON ${ mainEntity}.${mainPrimaryKey} = ${innerQuery.entity}.${foreignKey} `;
|
|
4241
|
+
|
|
4242
|
+
}
|
|
4243
|
+
}
|
|
4244
|
+
}
|
|
4245
|
+
return includeQuery;
|
|
4246
|
+
}
|
|
4247
|
+
|
|
4248
|
+
buildFrom(query, entity){
|
|
4249
|
+
var entityName = this.getEntity(entity.__name, query.entityMap);
|
|
4250
|
+
if(entityName ){
|
|
4251
|
+
return `FROM ${entity.__name } AS ${entityName}`;
|
|
4252
|
+
}
|
|
4253
|
+
else{ return "" }
|
|
4254
|
+
}
|
|
4255
|
+
|
|
4256
|
+
buildSelect(query, entity){
|
|
4257
|
+
// this means that there is a select statement
|
|
4258
|
+
var select = "SELECT";
|
|
4259
|
+
var arr = "";
|
|
4260
|
+
var $that = this;
|
|
4261
|
+
if(query.select){
|
|
4262
|
+
for (const item in query.select.selectFields) {
|
|
4263
|
+
arr += `${$that.getEntity(entity.__name, query.entityMap)}.${query.select.selectFields[item]}, `;
|
|
4264
|
+
};
|
|
4265
|
+
|
|
4266
|
+
}
|
|
4267
|
+
else{
|
|
4268
|
+
var entityList = this.getEntityList(entity);
|
|
4269
|
+
for (const item in entityList) {
|
|
4270
|
+
arr += `${$that.getEntity(entity.__name, query.entityMap)}.${entityList[item]}, `;
|
|
4271
|
+
};
|
|
4272
|
+
}
|
|
4273
|
+
arr = arr.replace(/,\s*$/, "");
|
|
4274
|
+
return `${select} ${arr} `;
|
|
4275
|
+
}
|
|
4276
|
+
|
|
4277
|
+
getForeignKey(name, entity){
|
|
4278
|
+
if(entity && name){
|
|
4279
|
+
return entity[name].foreignKey;
|
|
4280
|
+
}
|
|
4281
|
+
}
|
|
4282
|
+
|
|
4283
|
+
getPrimarykey(entity){
|
|
4284
|
+
for (const item in entity) {
|
|
4285
|
+
if(entity[item].primary){
|
|
4286
|
+
if(entity[item].primary === true){
|
|
4287
|
+
return entity[item].name;
|
|
4288
|
+
}
|
|
4289
|
+
}
|
|
4290
|
+
};
|
|
4291
|
+
}
|
|
4292
|
+
|
|
4293
|
+
getForeignTable(name, entity){
|
|
4294
|
+
if(entity && name){
|
|
4295
|
+
return entity[name].foreignTable;
|
|
4296
|
+
}
|
|
4297
|
+
}
|
|
4298
|
+
|
|
4299
|
+
getInclude(name, query){
|
|
4300
|
+
var include = query.include;
|
|
4301
|
+
if(include){
|
|
4302
|
+
for (let part in include) {
|
|
4303
|
+
if(tools.capitalizeFirstLetter(include[part].selectFields[0]) === name){
|
|
4304
|
+
return include[part];
|
|
4305
|
+
}
|
|
4306
|
+
}
|
|
4307
|
+
}
|
|
4308
|
+
else{
|
|
4309
|
+
return "";
|
|
4310
|
+
}
|
|
4311
|
+
}
|
|
4312
|
+
|
|
4313
|
+
getEntity(name, maps){
|
|
4314
|
+
for (let item in maps) {
|
|
4315
|
+
var map = maps[item];
|
|
4316
|
+
if(tools.capitalizeFirstLetter(name) === map.name){
|
|
4317
|
+
return map.entity
|
|
4318
|
+
}
|
|
4319
|
+
}
|
|
4320
|
+
return "";
|
|
4321
|
+
}
|
|
4322
|
+
|
|
4323
|
+
// return a list of entity names and skip foreign keys and underscore.
|
|
4324
|
+
getEntityList(entity){
|
|
4325
|
+
var entitiesList = [];
|
|
4326
|
+
var $that = this;
|
|
4327
|
+
for (var ent in entity) {
|
|
4328
|
+
if(!ent.startsWith("_")){
|
|
4329
|
+
if(!entity[ent].foreignKey){
|
|
4330
|
+
if(entity[ent].relationshipTable){
|
|
4331
|
+
if($that.chechUnsupportedWords(entity[ent].relationshipTable)){
|
|
4332
|
+
entitiesList.push(`'${entity[ent].relationshipTable}'`);
|
|
4333
|
+
}
|
|
4334
|
+
else{
|
|
4335
|
+
entitiesList.push(entity[ent].relationshipTable);
|
|
4336
|
+
}
|
|
4337
|
+
}
|
|
4338
|
+
else{
|
|
4339
|
+
if($that.chechUnsupportedWords(ent)){
|
|
4340
|
+
entitiesList.push(`'${ent}'`);
|
|
4341
|
+
}
|
|
4342
|
+
else{
|
|
4343
|
+
entitiesList.push(ent);
|
|
4344
|
+
}
|
|
4345
|
+
}
|
|
4346
|
+
}
|
|
4347
|
+
else{
|
|
4348
|
+
|
|
4349
|
+
if(entity[ent].relationshipType === "belongsTo"){
|
|
4350
|
+
var name = entity[ent].foreignKey;
|
|
4351
|
+
if($that.chechUnsupportedWords(name)){
|
|
4352
|
+
entitiesList.push(`'${name}'`);
|
|
4353
|
+
//entitiesList.push(`'${ent}'`);
|
|
4354
|
+
}
|
|
4355
|
+
else{
|
|
4356
|
+
entitiesList.push(name);
|
|
4357
|
+
//entitiesList.push(ent);
|
|
4358
|
+
}
|
|
4359
|
+
}
|
|
4360
|
+
|
|
4361
|
+
}
|
|
4362
|
+
}
|
|
4363
|
+
}
|
|
4364
|
+
return entitiesList
|
|
4365
|
+
}
|
|
4366
|
+
|
|
4367
|
+
chechUnsupportedWords(word){
|
|
4368
|
+
for (var item in this.unsupportedWords) {
|
|
4369
|
+
var text = this.unsupportedWords[item];
|
|
4370
|
+
if(text === word){
|
|
4371
|
+
return true
|
|
4372
|
+
}
|
|
4373
|
+
}
|
|
4374
|
+
return false;
|
|
4375
|
+
}
|
|
4376
|
+
|
|
4377
|
+
startTransaction(){
|
|
4378
|
+
this.db.prepare('BEGIN').run();
|
|
4379
|
+
}
|
|
4380
|
+
|
|
4381
|
+
endTransaction(){
|
|
4382
|
+
this.db.prepare('COMMIT').run();
|
|
4383
|
+
}
|
|
4384
|
+
|
|
4385
|
+
errorTransaction(){
|
|
4386
|
+
this.db.prepare('ROLLBACK').run();
|
|
4387
|
+
}
|
|
4388
|
+
|
|
4389
|
+
_buildSQLEqualTo(model){
|
|
4390
|
+
var $that = this;
|
|
4391
|
+
var argument = null;
|
|
4392
|
+
var dirtyFields = model.__dirtyFields;
|
|
4393
|
+
|
|
4394
|
+
for (var column in dirtyFields) {
|
|
4395
|
+
// TODO Boolean value is a string with a letter
|
|
4396
|
+
var type = model.__entity[dirtyFields[column]].type;
|
|
4397
|
+
|
|
4398
|
+
if(model.__entity[dirtyFields[column]].relationshipType === "belongsTo"){
|
|
4399
|
+
type = "belongsTo";
|
|
4400
|
+
}
|
|
4401
|
+
|
|
4402
|
+
switch(type){
|
|
4403
|
+
case "belongsTo" :
|
|
4404
|
+
const foreignKey = model.__entity[dirtyFields[column]].foreignKey;
|
|
4405
|
+
argument = `${foreignKey} = ${model[dirtyFields[column]]},`;
|
|
4406
|
+
break;
|
|
4407
|
+
case "integer" :
|
|
4408
|
+
const columneValue = model[`_${dirtyFields[column]}`];
|
|
4409
|
+
argument = argument === null ? `[${dirtyFields[column]}] = ${model[dirtyFields[column]]},` : `${argument} [${dirtyFields[column]}] = ${columneValue},`;
|
|
4410
|
+
break;
|
|
4411
|
+
case "string" :
|
|
4412
|
+
argument = argument === null ? `${dirtyFields[column]} = '${$that._santizeSingleQuotes(model[dirtyFields[column]])}',` : `${argument} ${dirtyFields[column]} = '${$that._santizeSingleQuotes(model[dirtyFields[column]])}',`;
|
|
4413
|
+
break;
|
|
4414
|
+
case "boolean" :
|
|
4415
|
+
argument = argument === null ? `${dirtyFields[column]} = '${this.boolType(model[dirtyFields[column]])}',` : `${argument} ${dirtyFields[column]} = '${this.boolType(model[dirtyFields[column]])}',`;
|
|
4416
|
+
break;
|
|
4417
|
+
default:
|
|
4418
|
+
argument = argument === null ? `${dirtyFields[column]} = '${model[dirtyFields[column]]}',` : `${argument} ${dirtyFields[column]} = '${model[dirtyFields[column]]}',`;
|
|
4419
|
+
}
|
|
4420
|
+
}
|
|
4421
|
+
if(argument){
|
|
4422
|
+
return argument.replace(/,\s*$/, "");
|
|
4423
|
+
}
|
|
4424
|
+
else{
|
|
4425
|
+
return -1;
|
|
4426
|
+
}
|
|
4427
|
+
}
|
|
4428
|
+
|
|
4429
|
+
boolType(type){
|
|
4430
|
+
var jj = String(type);
|
|
4431
|
+
switch(jj) {
|
|
4432
|
+
case "true":
|
|
4433
|
+
return 1
|
|
4434
|
+
break;
|
|
4435
|
+
case "false":
|
|
4436
|
+
return 0
|
|
4437
|
+
break;
|
|
4438
|
+
default:
|
|
4439
|
+
return type;
|
|
4440
|
+
}
|
|
4441
|
+
}
|
|
4442
|
+
|
|
4443
|
+
|
|
4444
|
+
_buildDeleteObject(currentModel){
|
|
4445
|
+
var primaryKey = currentModel.__Key === undefined ? tools.getPrimaryKeyObject(currentModel.__entity) : currentModel.__Key;
|
|
4446
|
+
var value = currentModel.__value === undefined ? currentModel[primaryKey] : currentModel.__value;
|
|
4447
|
+
var tableName = currentModel.__tableName === undefined ? currentModel.__entity.__name : currentModel.__tableName;
|
|
4448
|
+
return {tableName: tableName, primaryKey : primaryKey, value : value};
|
|
4449
|
+
}
|
|
4450
|
+
|
|
4451
|
+
|
|
4452
|
+
// return columns and value strings
|
|
4453
|
+
_buildSQLInsertObject(fields, modelEntity){
|
|
4454
|
+
var $that = this;
|
|
4455
|
+
var columns = null;
|
|
4456
|
+
var values = null;
|
|
4457
|
+
for (var column in modelEntity) {
|
|
4458
|
+
// column1 = value1, column2 = value2, ...
|
|
4459
|
+
if(column.indexOf("__") === -1 ){
|
|
4460
|
+
// call the get method if avlable
|
|
4461
|
+
var fieldColumn = "";
|
|
4462
|
+
// check if get function is avaliable if so use that
|
|
4463
|
+
fieldColumn = fields[column];
|
|
4464
|
+
|
|
4465
|
+
if((fieldColumn !== undefined && fieldColumn !== null) && typeof(fieldColumn) !== "object"){
|
|
4466
|
+
switch(modelEntity[column].type){
|
|
4467
|
+
case "string" :
|
|
4468
|
+
fieldColumn = `'${$that._santizeSingleQuotes(fields[column])}'`;
|
|
4469
|
+
break;
|
|
4470
|
+
case "time" :
|
|
4471
|
+
fieldColumn = fields[column];
|
|
4472
|
+
break;
|
|
4473
|
+
}
|
|
4474
|
+
|
|
4475
|
+
var relationship = modelEntity[column].relationshipType
|
|
4476
|
+
if(relationship === "belongsTo"){
|
|
4477
|
+
column = modelEntity[column].foreignKey
|
|
4478
|
+
}
|
|
4479
|
+
|
|
4480
|
+
|
|
4481
|
+
columns = columns === null ? `${column},` : `${columns} ${column},`;
|
|
4482
|
+
values = values === null ? `${fieldColumn},` : `${values} ${fieldColumn},`;
|
|
4483
|
+
|
|
4484
|
+
}
|
|
4485
|
+
}
|
|
4486
|
+
}
|
|
4487
|
+
|
|
4488
|
+
return {tableName: modelEntity.__name, columns: columns.replace(/,\s*$/, ""), values: values.replace(/,\s*$/, "")};
|
|
4489
|
+
|
|
4490
|
+
}
|
|
4491
|
+
|
|
4492
|
+
// will add double single quotes to allow sting to be saved.
|
|
4493
|
+
_santizeSingleQuotes(string){
|
|
4494
|
+
|
|
4495
|
+
if(typeof string === "string"){
|
|
4496
|
+
return string.replace(/'/g, "''");
|
|
4497
|
+
}else{
|
|
4498
|
+
return `${string}`;
|
|
4499
|
+
}
|
|
4500
|
+
}
|
|
4501
|
+
|
|
4502
|
+
// converts any object into SQL parameter select string
|
|
4503
|
+
_convertEntityToSelectParameterString(obj, entityName){
|
|
4504
|
+
// todo: loop throgh object and append string with comma to
|
|
4505
|
+
var mainString = "";
|
|
4506
|
+
const entries = Object.keys(obj);
|
|
4507
|
+
|
|
4508
|
+
for (const [name] of entries) {
|
|
4509
|
+
mainString += `${mainString}, ${entityName}.${name}`;
|
|
4510
|
+
}
|
|
4511
|
+
return mainString;;
|
|
4512
|
+
}
|
|
4513
|
+
|
|
4514
|
+
_execute(query){
|
|
4515
|
+
console.log("SQL:", query);
|
|
4516
|
+
this.db.connect(this.db);
|
|
4517
|
+
return this.db.query(query);
|
|
4518
|
+
}
|
|
4519
|
+
|
|
4520
|
+
_run(query){
|
|
4521
|
+
try{
|
|
4522
|
+
|
|
4523
|
+
console.log("SQL:", query);
|
|
4524
|
+
this.db.connect(this.db);
|
|
4525
|
+
const result = this.db.query(query);
|
|
4526
|
+
|
|
4527
|
+
return result;}
|
|
4528
|
+
catch (error) {
|
|
4529
|
+
console.error(error);
|
|
4530
|
+
// Expected output: ReferenceError: nonExistentFunction is not defined
|
|
4531
|
+
// (Note: the exact output may be browser-dependent)
|
|
4532
|
+
}
|
|
4533
|
+
}
|
|
4534
|
+
|
|
4535
|
+
setDB(db, type){
|
|
4536
|
+
this.db = db;
|
|
4537
|
+
this.dbType = type; // this will let us know which type of sqlengine to use.
|
|
4538
|
+
}
|
|
4539
|
+
}
|
|
4540
|
+
|
|
4541
|
+
module.exports = SQLLiteEngine;
|
|
4542
|
+
|
|
4543
|
+
|
|
4544
|
+
|
|
4545
|
+
|
|
4546
|
+
/***
|
|
4547
|
+
*
|
|
4548
|
+
*
|
|
4549
|
+
*
|
|
4550
|
+
* const mysql = require('mysql2/promise');
|
|
4551
|
+
|
|
4552
|
+
class MySQLClient {
|
|
4553
|
+
constructor(config) {
|
|
4554
|
+
this.config = config;
|
|
4555
|
+
this.pool = mysql.createPool(config);
|
|
4556
|
+
}
|
|
4557
|
+
|
|
4558
|
+
async query(sql, params = []) {
|
|
4559
|
+
const connection = await this.pool.getConnection();
|
|
4560
|
+
try {
|
|
4561
|
+
const [results] = await connection.execute(sql, params);
|
|
4562
|
+
return results;
|
|
4563
|
+
} finally {
|
|
4564
|
+
connection.release();
|
|
4565
|
+
}
|
|
4566
|
+
}
|
|
4567
|
+
|
|
4568
|
+
async close() {
|
|
4569
|
+
await this.pool.end();
|
|
4570
|
+
}
|
|
4571
|
+
}
|
|
4572
|
+
|
|
4573
|
+
module.exports = MySQLClient;
|
|
4574
|
+
|
|
4575
|
+
*/
|
|
4576
|
+
** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/mySQLEngine.js **
|
|
4577
|
+
var MySql = require('sync-mysql2');
|
|
4578
|
+
|
|
4579
|
+
|
|
4580
|
+
class MySQLClient {
|
|
4581
|
+
constructor(config) {
|
|
4582
|
+
this.config = config;
|
|
4583
|
+
this.connection = null;
|
|
4584
|
+
}
|
|
4585
|
+
|
|
4586
|
+
connect() {
|
|
4587
|
+
if (!this.connection) {
|
|
4588
|
+
this.connection = new MySql(this.config);
|
|
4589
|
+
}
|
|
4590
|
+
}
|
|
4591
|
+
|
|
4592
|
+
query(sql, params = []) {
|
|
4593
|
+
try {
|
|
4594
|
+
if (!this.connection) {
|
|
4595
|
+
throw new Error('Database connection not established. Call connect() first.');
|
|
4596
|
+
}
|
|
4597
|
+
var jj = this.connection.query(sql);
|
|
4598
|
+
this.connection.finishAll();
|
|
4599
|
+
return jj;
|
|
4600
|
+
} catch (err) {
|
|
4601
|
+
console.error(err);
|
|
4602
|
+
return null;
|
|
4603
|
+
}
|
|
4604
|
+
|
|
4605
|
+
}
|
|
4606
|
+
|
|
4607
|
+
close() {
|
|
4608
|
+
if (this.connection) {
|
|
4609
|
+
this.connection.end();
|
|
4610
|
+
this.connection = null;
|
|
4611
|
+
}
|
|
4612
|
+
}
|
|
4613
|
+
}
|
|
4614
|
+
|
|
4615
|
+
module.exports = MySQLClient;
|
|
4616
|
+
|
|
4617
|
+
** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/mySQLSyncConnect.js **
|
|
4618
|
+
{
|
|
4619
|
+
"name": "masterrecord",
|
|
4620
|
+
"dependencies": {
|
|
4621
|
+
"commander": "^13.1.0",
|
|
4622
|
+
"glob" : "^11.0.1",
|
|
4623
|
+
"deep-object-diff" : "^1.1.9",
|
|
4624
|
+
"pg" : "^8.14.1",
|
|
4625
|
+
"sync-mysql2" : "^1.0.5",
|
|
4626
|
+
"app-root-path": "^3.1.0"
|
|
4627
|
+
},
|
|
4628
|
+
"version": "0.1.4",
|
|
4629
|
+
"description": "An Object-relational mapping for the Master framework. Master Record connects classes to relational database tables to establish a database with almost zero-configuration ",
|
|
4630
|
+
"homepage": "https://github.com/Tailor/MasterRecord#readme",
|
|
4631
|
+
"repository": {
|
|
4632
|
+
"type": "git",
|
|
4633
|
+
"url": "git+https://github.com/Tailor/Masterrecord.git"
|
|
4634
|
+
},
|
|
4635
|
+
"main": "MasterRecord.js",
|
|
4636
|
+
"scripts": {
|
|
4637
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
4638
|
+
},
|
|
4639
|
+
"author": "Alexander Rich",
|
|
4640
|
+
"license": "ISC",
|
|
4641
|
+
"bin": {
|
|
4642
|
+
"masterrecord": "./migrations/cli.js"
|
|
4643
|
+
}
|
|
4644
|
+
}
|
|
4645
|
+
|
|
4646
|
+
** LOCATION: /Users/alexanderrich/Documents/development/MasterRecord/package.json **
|