masterrecord 0.0.24 → 0.0.25
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/DeleteManager.js +51 -0
- package/Entity/EntityModel.js +94 -30
- package/Entity/EntityModelBuilder.js +6 -28
- package/Entity/EntityTrackerModel.js +200 -20
- package/InsertManager.js +138 -0
- package/MYSQLEngine.js +409 -0
- package/Masterrecord.js +178 -119
- package/Migrations/cli.js +3 -2
- package/Migrations/migrations.js +45 -26
- package/QueryLanguage/queryManager.js +66 -0
- package/QueryLanguage/queryMethods.js +171 -0
- package/QueryLanguage/queryScript.js +331 -0
- package/SQLLiteEngine.js +409 -0
- package/Tools.js +97 -34
- package/package.json +7 -11
- package/QueryLanguage/_Expression.js +0 -322
- package/QueryLanguage/_LogicalQuery.js +0 -23
- package/QueryLanguage/_OperatorList.js +0 -88
- package/QueryLanguage/_QueryModel.js +0 -442
- package/QueryLanguage/_Tokenization.js +0 -173
- package/QueryLanguage/__Query.js +0 -386
- package/QueryLanguage/_simpleQuery.js +0 -184
- package/QueryLanguage/queryBuilder.js +0 -52
- package/SQLiteEngine.js +0 -56
|
@@ -1,322 +0,0 @@
|
|
|
1
|
-
// TODO: EXPRESSION WILL HAVE METHODS THAT WILL CONVERT EXPRESIONS TO SYNTAX TREE
|
|
2
|
-
// TODO: THEN WE WILL TURN THAT EXPRESSION SYNTEX TREE INTO SQLENGINE OBJECTS
|
|
3
|
-
// TODO: THE SQL ENGINE WILL THEN CONVERT THAT TO RAW SQL
|
|
4
|
-
// TODO: THEN THE RAW SQL WILL BE PUSHED TO SQL DATABASE
|
|
5
|
-
// TODO: ON RETURN THE OBJECT WILL PARSED USING THE ENTITY PARSER
|
|
6
|
-
// TODO: THE ENTITY WILL THEN BE TRACKED FOR ANY CHANGES
|
|
7
|
-
|
|
8
|
-
// EXAMPLE: https://www.npmjs.com/package/@rduk/expression
|
|
9
|
-
// EXAMPLE : https://goranhrovat.github.io/linqify/index.html
|
|
10
|
-
// LIST OF C# METHODS : https://medium.com/@aikeru/a-c-linq-to-javascript-translation-guide-6e1558fc1905
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
var MATCH_EXPR_REGEX = /^([\w\d$_]+?)\s*=>((?:\{\sreturn\s)?[\s\S]*(?:\})?)/;
|
|
16
|
-
const LOG_OPERATORS_REGEX = /(\|\|)|(&&)/;
|
|
17
|
-
const SPLIT_GROUP_REGEX = /(^|\||&| )\(/;
|
|
18
|
-
const REGEX_CACHE = {};
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
// ON INIT we convert the expression into an object literal with strings
|
|
22
|
-
// then we loop through each object in the object literal.
|
|
23
|
-
// we grab the value/key of the row
|
|
24
|
-
// we remove the entity so that only the query is left
|
|
25
|
-
// we while loop through each string broken by a DOT
|
|
26
|
-
// we run string through a rule checker
|
|
27
|
-
// is it a field in the current entity
|
|
28
|
-
// if so add it to selectField
|
|
29
|
-
// is it an argument
|
|
30
|
-
// run it through the nestedQuery Instance
|
|
31
|
-
// is it a nested field
|
|
32
|
-
// if so get type collection or single
|
|
33
|
-
// create new instance of query language
|
|
34
|
-
// run it through some rule checker
|
|
35
|
-
// if its a collection the next string item should be an argument
|
|
36
|
-
// then call the nested query language instance argument.
|
|
37
|
-
// if its a single then the next must be a field of that model including another nested field
|
|
38
|
-
// if its a reguler field then add it the select nested field
|
|
39
|
-
|
|
40
|
-
function describeExpressionParts(parts, exprPartRegExp) {
|
|
41
|
-
let result = [], match, desc, fields, func, arg;
|
|
42
|
-
for (let part of parts) {
|
|
43
|
-
if (part.constructor == Array) {
|
|
44
|
-
result.push(describeExpressionParts(part, exprPartRegExp));
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
if (part == "and" || part == "or") {
|
|
48
|
-
result.push(part);
|
|
49
|
-
}
|
|
50
|
-
else if (match = part.match(exprPartRegExp)) {
|
|
51
|
-
fields = match[1].split(".");
|
|
52
|
-
func = (match[2] ? fields[fields.length - 1] : (match[3] || "exists"));
|
|
53
|
-
if (func == "==" || func == "===") {
|
|
54
|
-
func = "=";
|
|
55
|
-
}
|
|
56
|
-
else if (func == "!==") {
|
|
57
|
-
func = "!=";
|
|
58
|
-
}
|
|
59
|
-
arg = match[2] || match[4];
|
|
60
|
-
if (arg == "true" || arg == "false") {
|
|
61
|
-
arg = arg == "true";
|
|
62
|
-
}
|
|
63
|
-
else if (arg && arg.charAt(0) == arg.charAt(arg.length - 1) && (arg.charAt(0) == "'" || arg.charAt(0) == '"')) {
|
|
64
|
-
arg = arg.slice(1, -1);
|
|
65
|
-
}
|
|
66
|
-
desc = {
|
|
67
|
-
field: (match[2] ? fields.slice(0, -1) : fields).join("."),
|
|
68
|
-
func: func.toLowerCase(),
|
|
69
|
-
arg: arg
|
|
70
|
-
};
|
|
71
|
-
result.push(desc);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
return result;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function splitByLogicalOperators(str, entityRegExp) {
|
|
79
|
-
let operatorIndex, parts = [], part;
|
|
80
|
-
while ((operatorIndex = str.search(LOG_OPERATORS_REGEX)) != -1) {
|
|
81
|
-
part = str.slice(0, operatorIndex).trim();
|
|
82
|
-
// Check if this part is relevant for query -> contains entity variable
|
|
83
|
-
// -> if it do nothing with entity, it shouldn't be in query eg. 1 == 1
|
|
84
|
-
if (entityRegExp.test(part)) {
|
|
85
|
-
parts.push(part);
|
|
86
|
-
}
|
|
87
|
-
parts.push(str.charAt(operatorIndex) == "|" ? "or" : "and");
|
|
88
|
-
str = str.slice(operatorIndex + 2);
|
|
89
|
-
}
|
|
90
|
-
if (str.length > 0 && entityRegExp.test(str)) {
|
|
91
|
-
parts.push(str);
|
|
92
|
-
}
|
|
93
|
-
return parts;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function splitGroupsByLogicalOperators(groups, entityRegExp, nested = false) {
|
|
97
|
-
let parts = [], tmp;
|
|
98
|
-
for (let part of groups) {
|
|
99
|
-
if (part.constructor == Array) {
|
|
100
|
-
tmp = splitGroupsByLogicalOperators(part, entityRegExp, true);
|
|
101
|
-
if (tmp.length) {
|
|
102
|
-
parts.push(tmp);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
else {
|
|
106
|
-
tmp = splitByLogicalOperators(part, entityRegExp);
|
|
107
|
-
if (tmp) {
|
|
108
|
-
parts = parts.concat(tmp);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
// Check if there are some doubled logical operators after removing irelevant parts
|
|
113
|
-
for (let i = 0; i < parts.length; i++) {
|
|
114
|
-
if ((parts[i] == "and" || parts[i] == "or") && (parts[i + 1] == "and" || parts[i + 1] == "or")) {
|
|
115
|
-
parts.splice(i, 1);
|
|
116
|
-
i--;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
const last = parts[parts.length - 1];
|
|
120
|
-
// Remove operators at end of group
|
|
121
|
-
if (last == "or" || last == "and") {
|
|
122
|
-
parts = parts.slice(0, -1);
|
|
123
|
-
}
|
|
124
|
-
// If it's only one part, return that part; but returned value must be always array
|
|
125
|
-
if (parts.length == 1 && (nested || parts[0].constructor == Array)) {
|
|
126
|
-
return parts[0];
|
|
127
|
-
}
|
|
128
|
-
return parts;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function splitByGroups(expr) {
|
|
132
|
-
const parts = [];
|
|
133
|
-
let bracketIndex, end, offset = 0, opening, closing, char;
|
|
134
|
-
let part = expr;
|
|
135
|
-
while ((bracketIndex = part.search(SPLIT_GROUP_REGEX)) != -1) {
|
|
136
|
-
// Search FIX - match symbol before bracket
|
|
137
|
-
if (bracketIndex != 0 || part.charAt(0) != "(") {
|
|
138
|
-
bracketIndex++;
|
|
139
|
-
}
|
|
140
|
-
// Count brackets -> find ending bracket
|
|
141
|
-
opening = 1;
|
|
142
|
-
closing = 0;
|
|
143
|
-
offset = bracketIndex + 1;
|
|
144
|
-
while (opening != closing && offset < part.length) {
|
|
145
|
-
char = part.charAt(offset);
|
|
146
|
-
if (char == "(") {
|
|
147
|
-
opening++;
|
|
148
|
-
}
|
|
149
|
-
else if (char == ")")
|
|
150
|
-
closing++;
|
|
151
|
-
offset++;
|
|
152
|
-
}
|
|
153
|
-
if (opening != closing) {
|
|
154
|
-
throw new Error("Expression has unclosed bracket");
|
|
155
|
-
}
|
|
156
|
-
parts.push(part.slice(0, bracketIndex).trim());
|
|
157
|
-
end = offset - 1;
|
|
158
|
-
// Find nested groups
|
|
159
|
-
parts.push(splitByGroups(part.slice(bracketIndex + 1, end).trim()));
|
|
160
|
-
part = part.slice(end + 1).trim();
|
|
161
|
-
}
|
|
162
|
-
if (parts.length == 0) {
|
|
163
|
-
parts.push(expr);
|
|
164
|
-
}
|
|
165
|
-
return parts;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
function getEntityRegExps(entityName) {
|
|
169
|
-
let REGEXPS = REGEX_CACHE[entityName];
|
|
170
|
-
if (!REGEXPS) {
|
|
171
|
-
REGEX_CACHE[entityName] = REGEXPS = {
|
|
172
|
-
MATCH_ENTITY_REGEXP: new RegExp("(^|[^\\w\\d])" + entityName + "[ \\.\\)]"),
|
|
173
|
-
OPERATORS_REGEX: new RegExp("(?:^|[^\\w\\d])" + entityName
|
|
174
|
-
+ "\\.((?:\\.?[\\w\\d_\\$]+)+)(?:\\((.*?)\\))?(?:\\s*(>|<|(?:===)|(?:!==)|(?:==)|(?:!=)|(?:<=)|(?:>=)|(?:in))\\s*(.*))?")
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
return REGEXPS;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
function convertWhereExpr(expr) {
|
|
181
|
-
const exprs = expr.expr;
|
|
182
|
-
const { MATCH_ENTITY_REGEXP, OPERATORS_REGEX } = getEntityRegExps(expr.entity);
|
|
183
|
-
const groups = splitByGroups(exprs);
|
|
184
|
-
const parts = splitGroupsByLogicalOperators(groups, MATCH_ENTITY_REGEXP);
|
|
185
|
-
expr.desc = describeExpressionParts(parts, OPERATORS_REGEX);
|
|
186
|
-
return expr;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
class Expression{
|
|
190
|
-
|
|
191
|
-
constructor(){
|
|
192
|
-
/**
|
|
193
|
-
* List of conditions
|
|
194
|
-
* @private
|
|
195
|
-
*/
|
|
196
|
-
this.conditions = [];
|
|
197
|
-
/**
|
|
198
|
-
* List of filter arguments
|
|
199
|
-
* @private
|
|
200
|
-
*/
|
|
201
|
-
this.whereArgs = [];
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
addExpression(expression, ...args) {
|
|
205
|
-
|
|
206
|
-
const conexpr = this.matchExpression(expression);
|
|
207
|
-
const expr = convertWhereExpr(conexpr);
|
|
208
|
-
// If some coditions already exists, add this WHERE as AND
|
|
209
|
-
if (this.conditions.length != 0) {
|
|
210
|
-
expr.desc.unshift("and");
|
|
211
|
-
}
|
|
212
|
-
this.whereArgs = this.whereArgs.concat(args);
|
|
213
|
-
this.conditions = this.conditions.concat(expr.desc);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// adds the context to virtual obj
|
|
217
|
-
getContext(virtualObj, contexts){
|
|
218
|
-
|
|
219
|
-
// virtual model: type, field and arg
|
|
220
|
-
var build = this.buildExpObject(virtualObj.func);
|
|
221
|
-
var foundContext = false;
|
|
222
|
-
for (var context of contexts) {
|
|
223
|
-
if(context.name === virtualObj.name){
|
|
224
|
-
build.context = new context();
|
|
225
|
-
foundContext = true;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
if(foundContext === false){
|
|
230
|
-
throw new Error(virtualObj.name + " is not a model name in dbset");
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
return build;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// builds an object from an expression
|
|
237
|
-
buildExpObject(stringExpression){
|
|
238
|
-
return {
|
|
239
|
-
name :stringExpression.match(new RegExp("^([^.]+)", "g"))[0],
|
|
240
|
-
func : stringExpression.replace(new RegExp("^([^.]+)", "g"), "").replace(/^\./, ""),
|
|
241
|
-
arg: "grab the inside of the name"
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
initExpression(expr, model, modelName) {
|
|
246
|
-
|
|
247
|
-
const str = expr.toString();
|
|
248
|
-
var that = this;
|
|
249
|
-
if (str[str.length - 1] == "}") {
|
|
250
|
-
throw new Error("Parameter expr must be simple arrow function.")
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
if (str[0] === "(") {
|
|
254
|
-
throw new Error("Use arrow function without brackets around parameter.");
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
const match = str.match(MATCH_EXPR_REGEX);
|
|
258
|
-
|
|
259
|
-
if (!match) {
|
|
260
|
-
throw new Error("Invalid expression");
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
const entity = match[1];
|
|
264
|
-
let exprStr = match[2];
|
|
265
|
-
|
|
266
|
-
const fields = [];
|
|
267
|
-
const virtualFields = [];
|
|
268
|
-
const nestedFields = [];
|
|
269
|
-
// var regex = new RegExp(entity + "\\.([\\w_]+)", "g");
|
|
270
|
-
var regexString = "(?<=:\\" + entity + ")([^,}]*)";
|
|
271
|
-
var regex = new RegExp(regexString, "g");
|
|
272
|
-
exprStr.replace(regex, function (_, field) {
|
|
273
|
-
|
|
274
|
-
// remove the entity from string
|
|
275
|
-
var fieldExp = field.replace(/(\/\*[^*]*\*\/)|(\/\/[^*]*)/g, '').replace(new RegExp("^(" + entity + "\.)", "g"), "").trim();
|
|
276
|
-
var fieldObj = that.buildExpObject(fieldExp);
|
|
277
|
-
const keys = Object.keys(model);
|
|
278
|
-
var keyNotFound = false;
|
|
279
|
-
for (const key of keys) {
|
|
280
|
-
if(fieldObj.name === model[key].name){
|
|
281
|
-
keyNotFound = true;
|
|
282
|
-
if(model[key].virtual === true){
|
|
283
|
-
// check is vitual is a single or collection one to one or one to many or many to many
|
|
284
|
-
var modelKeyType;
|
|
285
|
-
if(model[key].hasMany !== undefined){
|
|
286
|
-
modelKeyType = "collection";
|
|
287
|
-
}else{
|
|
288
|
-
modelKeyType = "single";
|
|
289
|
-
}
|
|
290
|
-
fieldObj.type = modelKeyType;
|
|
291
|
-
virtualFields.push(fieldObj); // we will do a left outer join
|
|
292
|
-
|
|
293
|
-
}
|
|
294
|
-
else{
|
|
295
|
-
// if arg has no text then that means there is no nested values
|
|
296
|
-
if(fieldObj.func === ""){
|
|
297
|
-
if (!fields.includes(fieldObj.name)) fields.push(fieldObj.name);
|
|
298
|
-
}else{
|
|
299
|
-
nestedFields.push(fieldObj);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
if(keyNotFound === false){
|
|
306
|
-
throw new Error(fieldObj.name + " is not apart of " + modelName);
|
|
307
|
-
}
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
let cachedExpr = {
|
|
311
|
-
entity: entity,
|
|
312
|
-
expr: exprStr.trim(),
|
|
313
|
-
selectFields: fields,
|
|
314
|
-
virtualFields : virtualFields,
|
|
315
|
-
nestedFields : nestedFields
|
|
316
|
-
};
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
return cachedExpr;
|
|
320
|
-
};
|
|
321
|
-
}
|
|
322
|
-
module.exports = Expression;
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
var Query = require('masterrecord/QueryLanguage/_simpleQuery');
|
|
4
|
-
|
|
5
|
-
class LogicalQuery {
|
|
6
|
-
constructor(context, modelName){
|
|
7
|
-
this.__model = context.__allContext[0].__name === modelName;
|
|
8
|
-
this.__query = new Query(this.__model, context);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
build(tokenQuery){
|
|
12
|
-
|
|
13
|
-
// we will run code to create new instances of the of the QueryModel and procss code
|
|
14
|
-
// where(r => r.name == "richard").single().flys.distinct().toList();
|
|
15
|
-
|
|
16
|
-
// loop thourgh and call all functions to build out the query;
|
|
17
|
-
return this.__query;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
module.exports = LogicalQuery;
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
// List all operators and base them on collections and non collections
|
|
2
|
-
// we can use this to validate which operators can and can't be used
|
|
3
|
-
var OperatorList = {
|
|
4
|
-
where : {
|
|
5
|
-
type: "collection",
|
|
6
|
-
section: "condition",
|
|
7
|
-
returnType : "collection"
|
|
8
|
-
},
|
|
9
|
-
contains : {
|
|
10
|
-
type: "single",
|
|
11
|
-
returnType : "none"
|
|
12
|
-
},
|
|
13
|
-
skip : {
|
|
14
|
-
type: "collection",
|
|
15
|
-
returnType : "collection",
|
|
16
|
-
section: "limit"
|
|
17
|
-
},
|
|
18
|
-
take: {
|
|
19
|
-
type: "collection",
|
|
20
|
-
returnType : "collection"
|
|
21
|
-
},
|
|
22
|
-
last: {
|
|
23
|
-
type: "collection",
|
|
24
|
-
returnType : "single"
|
|
25
|
-
},
|
|
26
|
-
orderByDescending : {
|
|
27
|
-
|
|
28
|
-
},
|
|
29
|
-
orderBy : {
|
|
30
|
-
|
|
31
|
-
},
|
|
32
|
-
groupBy : {
|
|
33
|
-
|
|
34
|
-
},
|
|
35
|
-
union: {
|
|
36
|
-
|
|
37
|
-
},
|
|
38
|
-
find : {
|
|
39
|
-
|
|
40
|
-
},
|
|
41
|
-
except : {
|
|
42
|
-
|
|
43
|
-
},
|
|
44
|
-
any : {
|
|
45
|
-
|
|
46
|
-
},
|
|
47
|
-
select : {
|
|
48
|
-
|
|
49
|
-
},
|
|
50
|
-
distinct :{
|
|
51
|
-
|
|
52
|
-
},
|
|
53
|
-
count :{
|
|
54
|
-
|
|
55
|
-
},
|
|
56
|
-
average :{
|
|
57
|
-
|
|
58
|
-
},
|
|
59
|
-
sum : {
|
|
60
|
-
|
|
61
|
-
},
|
|
62
|
-
max : {
|
|
63
|
-
|
|
64
|
-
},
|
|
65
|
-
min : {
|
|
66
|
-
|
|
67
|
-
},
|
|
68
|
-
join : {
|
|
69
|
-
type: "collection",
|
|
70
|
-
returnType: "collection"
|
|
71
|
-
},
|
|
72
|
-
groupJoin :{
|
|
73
|
-
|
|
74
|
-
},
|
|
75
|
-
toArray : {
|
|
76
|
-
|
|
77
|
-
},
|
|
78
|
-
toDictionary : {
|
|
79
|
-
|
|
80
|
-
},
|
|
81
|
-
toList : {
|
|
82
|
-
|
|
83
|
-
},
|
|
84
|
-
single : {
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
}
|