nodester 0.2.2 → 0.2.4
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/lib/middlewares/ql/sequelize/index.js +1 -1
- package/lib/middlewares/ql/sequelize/interpreter/QueryLexer.js +69 -38
- package/lib/query/traverse.js +60 -38
- package/lib/stacks/MiddlewaresStack.js +14 -4
- package/lib/tools/nql.tool.js +3 -3
- package/package.json +1 -1
- package/tests/nql.test.js +298 -229
- /package/tests/{ast.test.js → ast.js} +0 -0
|
@@ -11,6 +11,15 @@ const { ModelsTree, ModelsTreeNode } = require('./ModelsTree');
|
|
|
11
11
|
const util = require('util');
|
|
12
12
|
const debug = require('debug')('nodester:interpreter:QueryLexer');
|
|
13
13
|
|
|
14
|
+
const PARAM_TOKENS = new Enum({
|
|
15
|
+
FIELDS: Symbol('fields'),
|
|
16
|
+
INCLUDES: Symbol('includes'),
|
|
17
|
+
LIMIT: Symbol('limit'),
|
|
18
|
+
ORDER: Symbol('order'),
|
|
19
|
+
ORDER_BY: Symbol('order_by'),
|
|
20
|
+
SKIP: Symbol('skip'),
|
|
21
|
+
});
|
|
22
|
+
|
|
14
23
|
const OP_TOKENS = new Enum({
|
|
15
24
|
AND: 'and',
|
|
16
25
|
BETWEEN: 'between',
|
|
@@ -59,13 +68,12 @@ module.exports = class QueryLexer {
|
|
|
59
68
|
|
|
60
69
|
parseIsolatedQuery(queryString='', startAt=0, tree) {
|
|
61
70
|
const isSubQuery = tree.node.model !== 'root';
|
|
62
|
-
debug({ isSubQuery, startAt });
|
|
63
71
|
|
|
64
|
-
// Token is String, accumulated char-by-char.
|
|
72
|
+
// Token is a String, accumulated char-by-char.
|
|
65
73
|
let token = '';
|
|
66
74
|
// Value of param ('id=10' OR 'fields=id,text').
|
|
67
75
|
let value = [];
|
|
68
|
-
// Model, that was active before cursor went up in the tree.
|
|
76
|
+
// Model, that was active before a cursor went up in the tree.
|
|
69
77
|
let previousActive = null;
|
|
70
78
|
|
|
71
79
|
for (let i=startAt; i < queryString.length; i++) {
|
|
@@ -125,8 +133,12 @@ module.exports = class QueryLexer {
|
|
|
125
133
|
// Structure of a value depends on OP:
|
|
126
134
|
let fullOp = {};
|
|
127
135
|
switch (tree.node.op) {
|
|
128
|
-
case
|
|
129
|
-
case
|
|
136
|
+
case OP_TOKENS.NOT:
|
|
137
|
+
case OP_TOKENS.LIKE:
|
|
138
|
+
case OP_TOKENS.GREATER:
|
|
139
|
+
case OP_TOKENS.GREATER_OR_EQUAL:
|
|
140
|
+
case OP_TOKENS.LOWER:
|
|
141
|
+
case OP_TOKENS.LOWER_OR_EQUAL:
|
|
130
142
|
fullOp = { [tree.node.activeParam]: { [tree.node.op]: [token] } };
|
|
131
143
|
break;
|
|
132
144
|
default:
|
|
@@ -139,7 +151,7 @@ module.exports = class QueryLexer {
|
|
|
139
151
|
|
|
140
152
|
// Reset:
|
|
141
153
|
tree.node.resetOP();
|
|
142
|
-
tree.node.activeParam =
|
|
154
|
+
tree.node.activeParam = PARAM_TOKENS.INCLUDES;
|
|
143
155
|
token = '';
|
|
144
156
|
value = [];
|
|
145
157
|
continue;
|
|
@@ -173,19 +185,23 @@ module.exports = class QueryLexer {
|
|
|
173
185
|
|
|
174
186
|
// Reset:
|
|
175
187
|
tree.node.resetFN();
|
|
176
|
-
tree.node.activeParam =
|
|
188
|
+
tree.node.activeParam = PARAM_TOKENS.INCLUDES;
|
|
177
189
|
token = '';
|
|
178
190
|
value = [];
|
|
179
191
|
continue;
|
|
180
192
|
}
|
|
181
193
|
|
|
182
194
|
// If end of subquery:
|
|
183
|
-
if (!!tree.node.activeParam && tree.node.activeParam !==
|
|
195
|
+
if (!!tree.node.activeParam && tree.node.activeParam !== PARAM_TOKENS.INCLUDES) {
|
|
184
196
|
// Set value.
|
|
185
197
|
this.setNodeParam(tree.node, token, value);
|
|
198
|
+
|
|
186
199
|
// Reset:
|
|
187
200
|
tree.node.resetActiveParam();
|
|
188
201
|
tree.node.resetOP();
|
|
202
|
+
|
|
203
|
+
// Lift from subquery.
|
|
204
|
+
tree.up();
|
|
189
205
|
}
|
|
190
206
|
const numberOfProcessedChars = i - startAt;
|
|
191
207
|
return [ numberOfProcessedChars ];
|
|
@@ -206,7 +222,7 @@ module.exports = class QueryLexer {
|
|
|
206
222
|
}
|
|
207
223
|
|
|
208
224
|
// If param value:
|
|
209
|
-
if (tree.node.activeParam !==
|
|
225
|
+
if (tree.node.activeParam !== PARAM_TOKENS.INCLUDES) {
|
|
210
226
|
value.push(token);
|
|
211
227
|
token = '';
|
|
212
228
|
continue;
|
|
@@ -218,7 +234,7 @@ module.exports = class QueryLexer {
|
|
|
218
234
|
}
|
|
219
235
|
|
|
220
236
|
// Horizontal include:
|
|
221
|
-
if (tree.node.activeParam ===
|
|
237
|
+
if (tree.node.activeParam === PARAM_TOKENS.INCLUDES) {
|
|
222
238
|
const model = token;
|
|
223
239
|
tree.use(model) ?? tree.include(model);
|
|
224
240
|
|
|
@@ -227,7 +243,7 @@ module.exports = class QueryLexer {
|
|
|
227
243
|
tree.node.resetActiveParam();
|
|
228
244
|
tree.upToRoot();
|
|
229
245
|
|
|
230
|
-
tree.node.activeParam =
|
|
246
|
+
tree.node.activeParam = PARAM_TOKENS.INCLUDES;
|
|
231
247
|
|
|
232
248
|
token = '';
|
|
233
249
|
continue;
|
|
@@ -245,7 +261,7 @@ module.exports = class QueryLexer {
|
|
|
245
261
|
// Vertical include:
|
|
246
262
|
if (!!previousActive) {
|
|
247
263
|
tree.use(previousActive);
|
|
248
|
-
tree.node.activeParam =
|
|
264
|
+
tree.node.activeParam = PARAM_TOKENS.INCLUDES;
|
|
249
265
|
token = '';
|
|
250
266
|
continue;
|
|
251
267
|
}
|
|
@@ -256,7 +272,7 @@ module.exports = class QueryLexer {
|
|
|
256
272
|
tree.use(model) ?? tree.include(model).use(model);
|
|
257
273
|
|
|
258
274
|
// Prepare for more includes:
|
|
259
|
-
tree.node.activeParam =
|
|
275
|
+
tree.node.activeParam = PARAM_TOKENS.INCLUDES;
|
|
260
276
|
|
|
261
277
|
token = '';
|
|
262
278
|
continue;
|
|
@@ -278,7 +294,7 @@ module.exports = class QueryLexer {
|
|
|
278
294
|
tree.up();
|
|
279
295
|
|
|
280
296
|
// Prepare for more includes:
|
|
281
|
-
tree.node.activeParam =
|
|
297
|
+
tree.node.activeParam = PARAM_TOKENS.INCLUDES;
|
|
282
298
|
|
|
283
299
|
token = '';
|
|
284
300
|
continue;
|
|
@@ -290,12 +306,12 @@ module.exports = class QueryLexer {
|
|
|
290
306
|
}
|
|
291
307
|
|
|
292
308
|
tree.up();
|
|
293
|
-
tree.node.activeParam =
|
|
309
|
+
tree.node.activeParam = PARAM_TOKENS.INCLUDES;
|
|
294
310
|
|
|
295
311
|
continue;
|
|
296
312
|
}
|
|
297
313
|
|
|
298
|
-
// & can mean the end of key=value pair,
|
|
314
|
+
// & can mean the end of key=value pair in root and sub query,
|
|
299
315
|
// or the end of subincludes:
|
|
300
316
|
if (char === '&') {
|
|
301
317
|
debug('char', char, { token, node: tree.node });
|
|
@@ -307,7 +323,7 @@ module.exports = class QueryLexer {
|
|
|
307
323
|
}
|
|
308
324
|
|
|
309
325
|
// If end of key=value pair:
|
|
310
|
-
if (!!tree.node.activeParam && tree.node.activeParam !==
|
|
326
|
+
if (!!tree.node.activeParam && tree.node.activeParam !== PARAM_TOKENS.INCLUDES) {
|
|
311
327
|
// Set value.
|
|
312
328
|
this.setNodeParam(tree.node, token, value);
|
|
313
329
|
// Reset:
|
|
@@ -316,13 +332,19 @@ module.exports = class QueryLexer {
|
|
|
316
332
|
value = [];
|
|
317
333
|
continue;
|
|
318
334
|
}
|
|
319
|
-
else if (tree.node.activeParam ===
|
|
320
|
-
// If
|
|
335
|
+
else if (tree.node.activeParam === PARAM_TOKENS.INCLUDES) {
|
|
336
|
+
// If token has some chars,
|
|
337
|
+
// then it's include of a new model:
|
|
321
338
|
if (token.length > 0) {
|
|
322
339
|
const model = token;
|
|
323
340
|
// Just include, no use.
|
|
324
341
|
tree.include(model);
|
|
325
342
|
}
|
|
343
|
+
// If token is empty,
|
|
344
|
+
// it's most possibly a subquery
|
|
345
|
+
else {
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
326
348
|
|
|
327
349
|
// Then jump to root.
|
|
328
350
|
tree.upToRoot();
|
|
@@ -382,7 +404,7 @@ module.exports = class QueryLexer {
|
|
|
382
404
|
if (char === '=') {
|
|
383
405
|
const param = this.parseParamFromToken(token);
|
|
384
406
|
|
|
385
|
-
if (isSubQuery === true && param ===
|
|
407
|
+
if (isSubQuery === true && param === PARAM_TOKENS.INCLUDES) {
|
|
386
408
|
const err = new TypeError(`'include' is forbidden inside subquery (position ${ i }). Use: 'model.submodel' or 'model.submodel1+submodel2'.`);
|
|
387
409
|
throw err;
|
|
388
410
|
}
|
|
@@ -428,23 +450,29 @@ module.exports = class QueryLexer {
|
|
|
428
450
|
switch(token) {
|
|
429
451
|
case 'limit':
|
|
430
452
|
case 'l':
|
|
431
|
-
return
|
|
453
|
+
return PARAM_TOKENS.LIMIT;
|
|
454
|
+
|
|
432
455
|
case 'skip':
|
|
433
456
|
case 's':
|
|
434
457
|
case 'offset':
|
|
435
|
-
return
|
|
458
|
+
return PARAM_TOKENS.SKIP;
|
|
459
|
+
|
|
436
460
|
case 'order':
|
|
437
461
|
case 'o':
|
|
438
|
-
return
|
|
462
|
+
return PARAM_TOKENS.ORDER;
|
|
463
|
+
|
|
439
464
|
case 'order_by':
|
|
440
465
|
case 'o_by':
|
|
441
|
-
return
|
|
466
|
+
return PARAM_TOKENS.ORDER_BY;
|
|
467
|
+
|
|
442
468
|
case 'fields':
|
|
443
469
|
case 'f':
|
|
444
|
-
return
|
|
470
|
+
return PARAM_TOKENS.FIELDS;
|
|
471
|
+
|
|
445
472
|
case 'includes':
|
|
446
473
|
case 'in':
|
|
447
|
-
return
|
|
474
|
+
return PARAM_TOKENS.INCLUDES;
|
|
475
|
+
|
|
448
476
|
default:
|
|
449
477
|
return token;
|
|
450
478
|
}
|
|
@@ -453,34 +481,37 @@ module.exports = class QueryLexer {
|
|
|
453
481
|
setNodeParam(treeNode, token, value) {
|
|
454
482
|
const param = treeNode.activeParam;
|
|
455
483
|
|
|
456
|
-
debug(`set param
|
|
484
|
+
debug(`set param`, { param, token, value });
|
|
457
485
|
|
|
458
486
|
switch(param) {
|
|
459
|
-
case
|
|
487
|
+
case PARAM_TOKENS.LIMIT:
|
|
460
488
|
treeNode.limit = parseInt(token);
|
|
461
489
|
break;
|
|
462
|
-
|
|
463
|
-
case
|
|
490
|
+
|
|
491
|
+
case PARAM_TOKENS.SKIP:
|
|
464
492
|
treeNode.skip = parseInt(token);
|
|
465
493
|
break;
|
|
466
|
-
|
|
494
|
+
|
|
495
|
+
case PARAM_TOKENS.ORDER:
|
|
467
496
|
treeNode.order = token;
|
|
468
497
|
break;
|
|
469
|
-
|
|
498
|
+
|
|
499
|
+
case PARAM_TOKENS.ORDER_BY:
|
|
470
500
|
treeNode.order_by = token;
|
|
471
501
|
break;
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
502
|
+
|
|
503
|
+
case PARAM_TOKENS.FIELDS:
|
|
504
|
+
if (token) value.push(token);
|
|
475
505
|
treeNode.fields = value;
|
|
476
506
|
break;
|
|
477
|
-
|
|
507
|
+
|
|
508
|
+
case PARAM_TOKENS.INCLUDES:
|
|
478
509
|
const node = new ModelsTreeNode(token);
|
|
479
510
|
treeNode.include(node);
|
|
480
511
|
break;
|
|
512
|
+
|
|
481
513
|
default:
|
|
482
|
-
if (token)
|
|
483
|
-
value.push(token);
|
|
514
|
+
if (token) value.push(token);
|
|
484
515
|
treeNode.addWhere({ [param]: value });
|
|
485
516
|
break;
|
|
486
517
|
}
|
package/lib/query/traverse.js
CHANGED
|
@@ -102,49 +102,57 @@ function traverse(queryNode, filter=null, model=null) {
|
|
|
102
102
|
// Functions:
|
|
103
103
|
for (const fnParams of functions) {
|
|
104
104
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
105
|
+
switch(fnParams.fn) {
|
|
106
|
+
// SQL COUNT():
|
|
107
|
+
case 'count': {
|
|
108
|
+
const countParams = fnParams.args;
|
|
109
|
+
|
|
110
|
+
const [ countTarget ] = countParams;
|
|
111
|
+
// Count can be requested for this model,
|
|
112
|
+
// or for any of the available uncludes.
|
|
113
|
+
const isForRootModel = countTarget === rootModelName.plural.toLowerCase();
|
|
114
|
+
|
|
115
|
+
// Compile request:
|
|
116
|
+
// Example of desired SQL:
|
|
117
|
+
// `(SELECT COUNT(*) FROM comments WHERE comments.post_id=Post.id)`
|
|
118
|
+
//
|
|
119
|
+
let rawSQL = '(SELECT COUNT(*) FROM ';
|
|
120
|
+
let countAttribute = 'count';
|
|
121
|
+
|
|
122
|
+
// If request to count one of the includes:
|
|
123
|
+
if (!isForRootModel) {
|
|
124
|
+
// Check if it's available:
|
|
125
|
+
if (
|
|
126
126
|
!filter
|
|
127
127
|
||
|
|
128
128
|
!filter?.includes[countTarget]
|
|
129
129
|
||
|
|
130
130
|
rootModelAssociations[countTarget] === undefined
|
|
131
131
|
) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
132
|
+
const err = new NodesterQueryError(`Count for '${ countTarget }' is not available.`);
|
|
133
|
+
Error.captureStackTrace(err, traverse);
|
|
134
|
+
throw err;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const {
|
|
138
|
+
as,
|
|
139
|
+
target,
|
|
140
|
+
foreignKey,
|
|
141
|
+
sourceKey
|
|
142
|
+
} = rootModelAssociations[countTarget];
|
|
143
|
+
const { tableName } = target;
|
|
144
|
+
|
|
145
|
+
rawSQL += `${ tableName } where ${ tableName }.${ foreignKey }=${ rootModelName.singular }.${ sourceKey })`;
|
|
146
|
+
countAttribute = `${ as }_count`;
|
|
135
147
|
}
|
|
136
148
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
} = rootModelAssociations[countTarget];
|
|
141
|
-
rawSQL += `${ countTarget } where ${ countTarget }.${ foreignKey }=${ rootModelName.singular }.${ sourceKey })`;
|
|
142
|
-
countAttribute = `${ countTarget }_count`;
|
|
149
|
+
newQuery.attributes.push(
|
|
150
|
+
[sequelize.literal(rawSQL), countAttribute]
|
|
151
|
+
);
|
|
143
152
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
);
|
|
153
|
+
// Unknow function:
|
|
154
|
+
default:
|
|
155
|
+
break;
|
|
148
156
|
}
|
|
149
157
|
}
|
|
150
158
|
// Functions\
|
|
@@ -194,7 +202,7 @@ function traverse(queryNode, filter=null, model=null) {
|
|
|
194
202
|
}
|
|
195
203
|
}
|
|
196
204
|
|
|
197
|
-
// "statics"
|
|
205
|
+
// Override clauses with "statics":
|
|
198
206
|
if (filter !== null) {
|
|
199
207
|
const staticClausesEntries = Object.entries(filter.statics.clauses);
|
|
200
208
|
|
|
@@ -228,7 +236,11 @@ function traverse(queryNode, filter=null, model=null) {
|
|
|
228
236
|
|
|
229
237
|
|
|
230
238
|
// Order:
|
|
231
|
-
if (
|
|
239
|
+
if (
|
|
240
|
+
order.order === 'rand'
|
|
241
|
+
||
|
|
242
|
+
order.order === 'random'
|
|
243
|
+
) {
|
|
232
244
|
newQuery.order = sequelize.random();
|
|
233
245
|
}
|
|
234
246
|
else {
|
|
@@ -292,11 +304,21 @@ function traverse(queryNode, filter=null, model=null) {
|
|
|
292
304
|
}
|
|
293
305
|
|
|
294
306
|
|
|
295
|
-
|
|
307
|
+
/**
|
|
308
|
+
* Traverses each include in the array.
|
|
309
|
+
*
|
|
310
|
+
* @param {Array} includes
|
|
311
|
+
* @param {Model} rootModel
|
|
312
|
+
* @param {NodesterFilter} filter
|
|
313
|
+
* @param {Object} resultQuery
|
|
314
|
+
*
|
|
315
|
+
* @api private
|
|
316
|
+
*/
|
|
317
|
+
function _traverseIncludes(includes, rootModel, filter, resultQuery) {
|
|
296
318
|
const filterIncludesEntries = Object.entries(filter.includes);
|
|
297
319
|
for (let [ includeName, includeFilter ] of filterIncludesEntries) {
|
|
298
320
|
|
|
299
|
-
const association =
|
|
321
|
+
const association = rootModel.associations[includeName];
|
|
300
322
|
|
|
301
323
|
// If no such association:
|
|
302
324
|
if (!association) {
|
|
@@ -31,7 +31,9 @@ module.exports = class MiddlewaresStack {
|
|
|
31
31
|
|
|
32
32
|
// Indicates whether we can add more middlewares or no.
|
|
33
33
|
this._isLocked = false;
|
|
34
|
-
|
|
34
|
+
|
|
35
|
+
// TODO: disable/enable it in the process().
|
|
36
|
+
this._finalhandlerEnabled = !!opts.finalhandlerEnabled;
|
|
35
37
|
|
|
36
38
|
|
|
37
39
|
const env = process.env.NODE_ENV || 'development';
|
|
@@ -139,7 +141,7 @@ module.exports = class MiddlewaresStack {
|
|
|
139
141
|
* @api public
|
|
140
142
|
*/
|
|
141
143
|
lock() {
|
|
142
|
-
if (this.
|
|
144
|
+
if (this._finalhandlerEnabled) {
|
|
143
145
|
// Add final handler to the stack.
|
|
144
146
|
this.add((req, res)=>this.finalhandler(req, res)());
|
|
145
147
|
}
|
|
@@ -159,7 +161,7 @@ module.exports = class MiddlewaresStack {
|
|
|
159
161
|
unlock() {
|
|
160
162
|
this._isLocked = false;
|
|
161
163
|
|
|
162
|
-
if (this.
|
|
164
|
+
if (this._finalhandlerEnabled) {
|
|
163
165
|
this._middlewares.pop();
|
|
164
166
|
}
|
|
165
167
|
|
|
@@ -185,7 +187,15 @@ module.exports = class MiddlewaresStack {
|
|
|
185
187
|
return next.call(null, req, res, next, ...args);
|
|
186
188
|
}
|
|
187
189
|
else if (!!fn) {
|
|
188
|
-
|
|
190
|
+
// Check for a middleware (fn) type (async or sync):
|
|
191
|
+
// 👇 Why it's important
|
|
192
|
+
// https://stackoverflow.com/questions/60330963/why-an-async-function-takes-more-time-to-execute-than-a-sync-one
|
|
193
|
+
if (fn.constructor.name === 'AsyncFunction') {
|
|
194
|
+
return await fn.call(null, req, res, _next, ...args);
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
return fn.call(null, req, res, _next, ...args);
|
|
198
|
+
}
|
|
189
199
|
}
|
|
190
200
|
}
|
|
191
201
|
catch(error) {
|
package/lib/tools/nql.tool.js
CHANGED
|
@@ -22,10 +22,10 @@ function _toAST_ModelsTreeNode(node, spacing=0) {
|
|
|
22
22
|
|
|
23
23
|
ast += `${ spaces }model: ${ node.model }\n\n`;
|
|
24
24
|
|
|
25
|
-
ast += `${ spaces }fields: [\n${ node.fields.map(f => ` • ${ f },\n`) }`;
|
|
25
|
+
ast += `${ spaces }fields (${ node.fields.length }): [\n${ node.fields.map(f => ` • ${ f },\n`) }`;
|
|
26
26
|
ast += `${ spaces }]\n\n`;
|
|
27
27
|
|
|
28
|
-
ast += `${ spaces }functions: [\n${ node.functions.map(f => ` • ${ f },\n`) }`;
|
|
28
|
+
ast += `${ spaces }functions (${ node.functions.length }): [\n${ node.functions.map(f => ` • ${ f },\n`) }`;
|
|
29
29
|
ast += `${ spaces }]\n\n`;
|
|
30
30
|
|
|
31
31
|
ast += `${ spaces }where: ${ JSON.stringify(node.where) }\n\n`;
|
|
@@ -34,7 +34,7 @@ function _toAST_ModelsTreeNode(node, spacing=0) {
|
|
|
34
34
|
c => ast += `${ spaces }${ c }: ${ node[c] }\n\n`
|
|
35
35
|
);
|
|
36
36
|
|
|
37
|
-
ast += `${ spaces }includes: [\n`
|
|
37
|
+
ast += `${ spaces }includes (${ node.includes.length }): [\n`
|
|
38
38
|
node.includes.map(n => ast += _toAST_ModelsTreeNode(n, spacing + 2));
|
|
39
39
|
ast += `${ spaces }]\n`;
|
|
40
40
|
|
package/package.json
CHANGED
package/tests/nql.test.js
CHANGED
|
@@ -10,285 +10,354 @@ const {
|
|
|
10
10
|
const { ModelsTree } = require('../lib/middlewares/ql/sequelize/interpreter/ModelsTree');
|
|
11
11
|
const QueryLexer = require('../lib/middlewares/ql/sequelize/interpreter/QueryLexer');
|
|
12
12
|
|
|
13
|
-
describe('nodester Query Language', () => {
|
|
14
|
-
const queryStrings = [
|
|
15
|
-
// Simple where.
|
|
16
|
-
'id=10',
|
|
17
|
-
// All possible params.
|
|
18
|
-
'id=10&position=4&limit=3&skip=10&order=desc&order_by=index&fields=id,content,position,created_at',
|
|
19
|
-
// Simple includes.
|
|
20
|
-
'includes=comments&id=7',
|
|
21
|
-
// Include with All possible params.
|
|
22
|
-
'includes=comments(id=10&position=4&limit=3&skip=10&order=desc&order_by=index&fields=id,content,position)',
|
|
23
|
-
|
|
24
|
-
// Subinclude horizontal.
|
|
25
|
-
'includes=comments,users&id=1000',
|
|
26
|
-
// Subinclude horizontal (more entries).
|
|
27
|
-
'includes=comments(order=desc),users,likes(order=rand),reposts&id=1000',
|
|
28
|
-
// Subinclude horizontal (+ syntaxis).
|
|
29
|
-
'includes=comments(order=desc).users+likes(order=rand&order_by=position)&id=1000',
|
|
30
|
-
|
|
31
|
-
// Subinclude vertical.
|
|
32
|
-
'includes=comments.users&id=1000',
|
|
33
|
-
// Subinclude vertical (more entries).
|
|
34
|
-
'in=comments.users.avatars.sizes&position=200',
|
|
35
|
-
|
|
36
|
-
// Complex includes.
|
|
37
|
-
'includes=comments.users.avatars(fields=id,content&order=rand)&id=7&limit=3',
|
|
38
|
-
|
|
39
|
-
// Broken includes.
|
|
40
|
-
'includes=comments(order=rand)&id=7&limit=3&includes=users(fields=id,content)',
|
|
41
|
-
|
|
42
|
-
// OR simple.
|
|
43
|
-
'or(index=2,position=5)',
|
|
44
|
-
// OR shortened.
|
|
45
|
-
'|(index=2,position=5)',
|
|
46
|
-
|
|
47
|
-
// NOT inside include.
|
|
48
|
-
'includes=comments(id=not(7))',
|
|
49
|
-
|
|
50
|
-
// Like simple.
|
|
51
|
-
'title=like(some_text)',
|
|
52
|
-
|
|
53
|
-
// Subinclude and isolated Horizontal.
|
|
54
|
-
'in=comments.user,likes',
|
|
55
|
-
];
|
|
56
|
-
|
|
57
|
-
it('query "Simple where"', () => {
|
|
58
|
-
const lexer = new QueryLexer( queryStrings[0] );
|
|
59
|
-
const result = lexer.query;
|
|
60
|
-
|
|
61
|
-
const tree = new ModelsTree();
|
|
62
|
-
tree.node.addWhere({ id: ['10'] });
|
|
63
|
-
const expected = tree.root.toObject();
|
|
64
|
-
|
|
65
|
-
expect(result).toMatchObject(expected);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test('query "All possible params"', () => {
|
|
69
|
-
const lexer = new QueryLexer( queryStrings[1] );
|
|
70
|
-
const result = lexer.query;
|
|
71
13
|
|
|
72
14
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
15
|
+
describe('nodester Query Language', () => {
|
|
16
|
+
describe('flat', () => {
|
|
17
|
+
const queryStrings = [
|
|
18
|
+
// Simple where.
|
|
19
|
+
'id=10',
|
|
20
|
+
// All possible params.
|
|
21
|
+
'id=10&position=4&limit=3&skip=10&order=desc&order_by=index&fields=id,content,position,created_at',
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
it('Simple where', () => {
|
|
25
|
+
const lexer = new QueryLexer( queryStrings[0] );
|
|
26
|
+
const result = lexer.query;
|
|
27
|
+
|
|
28
|
+
const tree = new ModelsTree();
|
|
29
|
+
tree.node.addWhere({ id: ['10'] });
|
|
30
|
+
const expected = tree.root.toObject();
|
|
31
|
+
|
|
32
|
+
expect(result).toMatchObject(expected);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('All possible params', () => {
|
|
36
|
+
const lexer = new QueryLexer( queryStrings[1] );
|
|
37
|
+
const result = lexer.query;
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
const tree = new ModelsTree();
|
|
41
|
+
tree.node.addWhere({ id: ['10'], position: ['4'] });
|
|
42
|
+
tree.node.fields = [ 'id', 'content', 'position', 'created_at' ];
|
|
43
|
+
tree.node.limit = 3;
|
|
44
|
+
tree.node.skip = 10;
|
|
45
|
+
tree.node.order = 'desc';
|
|
46
|
+
tree.node.order_by = 'index';
|
|
47
|
+
const expected = tree.root.toObject();
|
|
48
|
+
|
|
49
|
+
expect(result).toMatchObject(expected);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
81
52
|
|
|
82
|
-
|
|
53
|
+
describe('includes', () => {
|
|
54
|
+
const queryStrings = [
|
|
55
|
+
// Simple includes.
|
|
56
|
+
'includes=comments&id=7',
|
|
57
|
+
// Include with All possible params.
|
|
58
|
+
'includes=comments(id=10&position=4&limit=3&skip=10&order=desc&order_by=index&fields=id,content,position)',
|
|
59
|
+
|
|
60
|
+
// 2 horizontals
|
|
61
|
+
'includes=comments,users&id=1000',
|
|
62
|
+
|
|
63
|
+
// Horizontals queried.
|
|
64
|
+
'includes=comments(order=desc),users,likes(order=rand),reposts&id=1000',
|
|
65
|
+
// Horizontals queried №2.
|
|
66
|
+
'in=reactions,comments(user_id=gte(4)&skip=10&limit=2).users,likes,reposts',
|
|
67
|
+
|
|
68
|
+
// Separated includes.
|
|
69
|
+
'includes=comments(order=rand)&id=7&limit=3&includes=users(fields=id,content)',
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
test('Simple includes', () => {
|
|
73
|
+
const lexer = new QueryLexer( queryStrings[0] );
|
|
74
|
+
const result = lexer.query;
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
const tree = new ModelsTree();
|
|
78
|
+
tree.node.addWhere({ id: ['7'] });
|
|
79
|
+
tree.include('comments');
|
|
80
|
+
const expected = tree.root.toObject();
|
|
81
|
+
|
|
82
|
+
expect(result).toMatchObject(expected);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('Include with all possible params', () => {
|
|
86
|
+
const lexer = new QueryLexer( queryStrings[1] );
|
|
87
|
+
const result = lexer.query;
|
|
88
|
+
|
|
89
|
+
const tree = new ModelsTree();
|
|
90
|
+
tree.include('comments').use('comments');
|
|
91
|
+
tree.node.addWhere({ id: ['10'], position: ['4'] });
|
|
92
|
+
tree.node.fields = [ 'id', 'content', 'position' ];
|
|
93
|
+
tree.node.limit = 3;
|
|
94
|
+
tree.node.skip = 10;
|
|
95
|
+
tree.node.order = 'desc';
|
|
96
|
+
tree.node.order_by = 'index';
|
|
97
|
+
const expected = tree.root.toObject();
|
|
98
|
+
|
|
99
|
+
expect(result).toMatchObject(expected);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('2 horizontals', () => {
|
|
103
|
+
const lexer = new QueryLexer( queryStrings[2] );
|
|
104
|
+
const result = lexer.query;
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
const tree = new ModelsTree();
|
|
108
|
+
tree.node.addWhere({ id: ['1000'] });
|
|
109
|
+
tree.include('comments');
|
|
110
|
+
tree.include('users');
|
|
111
|
+
const expected = tree.root.toObject();
|
|
112
|
+
|
|
113
|
+
expect(result).toMatchObject(expected);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test('Horizontals queried', () => {
|
|
117
|
+
const lexer = new QueryLexer( queryStrings[3] );
|
|
118
|
+
const result = lexer.query;
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
const tree = new ModelsTree();
|
|
122
|
+
tree.node.addWhere({ id: ['1000'] });
|
|
123
|
+
tree.include('comments').use('comments');
|
|
124
|
+
tree.node.order = 'desc';
|
|
125
|
+
tree.up();
|
|
126
|
+
tree.include('users');
|
|
127
|
+
tree.include('likes') && tree.use('likes');
|
|
128
|
+
tree.node.order = 'rand';
|
|
129
|
+
tree.up();
|
|
130
|
+
tree.include('reposts');
|
|
131
|
+
const expected = tree.root.toObject();
|
|
132
|
+
|
|
133
|
+
expect(result).toMatchObject(expected);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test('Horizontals queried №2', () => {
|
|
137
|
+
const lexer = new QueryLexer( queryStrings[4] );
|
|
138
|
+
const result = lexer.query;
|
|
139
|
+
|
|
140
|
+
const tree = new ModelsTree();
|
|
141
|
+
tree.include('reactions');
|
|
142
|
+
|
|
143
|
+
tree.include('comments').use('comments');
|
|
144
|
+
tree.node.addWhere({
|
|
145
|
+
user_id: {
|
|
146
|
+
gte: ['4']
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
tree.node.skip = 10;
|
|
150
|
+
tree.node.limit = 2;
|
|
151
|
+
|
|
152
|
+
tree.include('users');
|
|
153
|
+
tree.up();
|
|
154
|
+
|
|
155
|
+
tree.include('likes');
|
|
156
|
+
tree.include('reposts');
|
|
157
|
+
|
|
158
|
+
const expected = tree.root.toObject();
|
|
159
|
+
|
|
160
|
+
expect(result).toMatchObject(expected);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test('Separated includes"', () => {
|
|
164
|
+
const lexer = new QueryLexer( queryStrings[5] );
|
|
165
|
+
const result = lexer.query;
|
|
166
|
+
|
|
167
|
+
const tree = new ModelsTree();
|
|
168
|
+
tree.node.addWhere({ id: ['7'] });
|
|
169
|
+
tree.node.limit = 3;
|
|
170
|
+
tree.include('comments').use('comments');
|
|
171
|
+
tree.node.order = 'rand';
|
|
172
|
+
tree.up();
|
|
173
|
+
tree.include('users').use('users');
|
|
174
|
+
tree.node.fields = [ 'id', 'content' ];
|
|
175
|
+
const expected = tree.root.toObject();
|
|
176
|
+
|
|
177
|
+
expect(result).toMatchObject(expected);
|
|
178
|
+
});
|
|
83
179
|
});
|
|
84
180
|
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
|
|
181
|
+
describe('subincludes', () => {
|
|
182
|
+
const queryStrings = [
|
|
183
|
+
// Simple subinclude.
|
|
184
|
+
'includes=comments.users',
|
|
88
185
|
|
|
186
|
+
// Deep subincludes.
|
|
187
|
+
'in=posts.comments.users.avatars.sizes&position=200',
|
|
89
188
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
tree.include('comments');
|
|
93
|
-
const expected = tree.root.toObject();
|
|
189
|
+
// Simple horizontal subinclude, "+" syntaxis.
|
|
190
|
+
'includes=comments.users+likes',
|
|
94
191
|
|
|
95
|
-
|
|
96
|
-
|
|
192
|
+
// Subinclude query.
|
|
193
|
+
'includes=comments.users(order=rand&order_by=position)',
|
|
97
194
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const tree = new ModelsTree();
|
|
103
|
-
tree.include('comments').use('comments');
|
|
104
|
-
tree.node.addWhere({ id: ['10'], position: ['4'] });
|
|
105
|
-
tree.node.fields = [ 'id', 'content', 'position' ];
|
|
106
|
-
tree.node.limit = 3;
|
|
107
|
-
tree.node.skip = 10;
|
|
108
|
-
tree.node.order = 'desc';
|
|
109
|
-
tree.node.order_by = 'index';
|
|
110
|
-
const expected = tree.root.toObject();
|
|
111
|
-
|
|
112
|
-
expect(result).toMatchObject(expected);
|
|
113
|
-
});
|
|
195
|
+
// Complex subincludes query, "+" syntaxis.
|
|
196
|
+
'includes=comments(order=desc).users+likes(order=rand&order_by=position)&id=1000',
|
|
197
|
+
];
|
|
114
198
|
|
|
199
|
+
test('Simple subinclude', () => {
|
|
200
|
+
const lexer = new QueryLexer( queryStrings[0] );
|
|
201
|
+
const result = lexer.query;
|
|
115
202
|
|
|
116
|
-
test('query "Subinclude horizontal"', () => {
|
|
117
|
-
const lexer = new QueryLexer( queryStrings[4] );
|
|
118
|
-
const result = lexer.query;
|
|
119
203
|
|
|
204
|
+
const tree = new ModelsTree();
|
|
205
|
+
tree.include('comments').use('comments');
|
|
206
|
+
tree.include('users');
|
|
207
|
+
const expected = tree.root.toObject();
|
|
120
208
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
tree.include('comments');
|
|
124
|
-
tree.include('users');
|
|
125
|
-
const expected = tree.root.toObject();
|
|
209
|
+
expect(result).toMatchObject(expected);
|
|
210
|
+
});
|
|
126
211
|
|
|
127
|
-
|
|
128
|
-
|
|
212
|
+
test('Deep subincludes', () => {
|
|
213
|
+
const lexer = new QueryLexer( queryStrings[1] );
|
|
214
|
+
const result = lexer.query;
|
|
129
215
|
|
|
130
|
-
test('query "Subinclude horizontal (complex)"', () => {
|
|
131
|
-
const lexer = new QueryLexer( queryStrings[5] );
|
|
132
|
-
const result = lexer.query;
|
|
133
216
|
|
|
217
|
+
const tree = new ModelsTree();
|
|
218
|
+
tree.include('posts').use('posts');
|
|
219
|
+
tree.include('comments').use('comments');
|
|
220
|
+
tree.include('users').use('users');
|
|
221
|
+
tree.include('avatars').use('avatars');
|
|
222
|
+
tree.include('sizes').use('sizes');
|
|
223
|
+
const expected = tree.root.toObject();
|
|
134
224
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
tree.include('comments').use('comments');
|
|
138
|
-
tree.node.order = 'desc';
|
|
139
|
-
tree.up();
|
|
140
|
-
tree.include('users');
|
|
141
|
-
tree.include('likes') && tree.use('likes');
|
|
142
|
-
tree.node.order = 'rand';
|
|
143
|
-
tree.up();
|
|
144
|
-
tree.include('reposts');
|
|
145
|
-
const expected = tree.root.toObject();
|
|
225
|
+
expect(result).toMatchObject(expected);
|
|
226
|
+
});
|
|
146
227
|
|
|
147
|
-
|
|
148
|
-
|
|
228
|
+
test('Simple horizontal subinclude, "+" syntaxis"', () => {
|
|
229
|
+
const lexer = new QueryLexer( queryStrings[2] );
|
|
230
|
+
const result = lexer.query;
|
|
149
231
|
|
|
150
232
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
233
|
+
const tree = new ModelsTree();
|
|
234
|
+
tree.include('comments').use('comments');
|
|
235
|
+
tree.include('users');
|
|
236
|
+
tree.include('likes');
|
|
237
|
+
const expected = tree.root.toObject();
|
|
154
238
|
|
|
239
|
+
expect(result).toMatchObject(expected);
|
|
240
|
+
});
|
|
155
241
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
tree.node.order = 'desc';
|
|
160
|
-
tree.include('users');
|
|
161
|
-
tree.include('likes') && tree.use('likes');
|
|
162
|
-
tree.node.order = 'rand';
|
|
163
|
-
tree.node.order_by = 'position';
|
|
164
|
-
tree.up();
|
|
165
|
-
const expected = tree.root.toObject();
|
|
242
|
+
test('Subinclude query', () => {
|
|
243
|
+
const lexer = new QueryLexer( queryStrings[3] );
|
|
244
|
+
const result = lexer.query;
|
|
166
245
|
|
|
167
|
-
expect(result).toMatchObject(expected);
|
|
168
|
-
});
|
|
169
246
|
|
|
247
|
+
const tree = new ModelsTree();
|
|
248
|
+
tree.include('comments').use('comments');
|
|
249
|
+
tree.include('users').use('users');
|
|
250
|
+
tree.node.order = 'rand';
|
|
251
|
+
tree.node.order_by = 'position';
|
|
252
|
+
const expected = tree.root.toObject();
|
|
253
|
+
|
|
254
|
+
expect(result).toMatchObject(expected);
|
|
255
|
+
});
|
|
170
256
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
257
|
+
test('Complex subincludes query, "+" syntaxis', () => {
|
|
258
|
+
const lexer = new QueryLexer( queryStrings[4] );
|
|
259
|
+
const result = lexer.query;
|
|
174
260
|
|
|
175
261
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
262
|
+
const tree = new ModelsTree();
|
|
263
|
+
tree.node.addWhere({ id: ['1000'] });
|
|
264
|
+
tree.include('comments').use('comments');
|
|
265
|
+
tree.node.order = 'desc';
|
|
266
|
+
tree.include('users');
|
|
267
|
+
tree.include('likes') && tree.use('likes');
|
|
268
|
+
tree.node.order = 'rand';
|
|
269
|
+
tree.node.order_by = 'position';
|
|
270
|
+
tree.up();
|
|
271
|
+
const expected = tree.root.toObject();
|
|
181
272
|
|
|
182
|
-
|
|
273
|
+
expect(result).toMatchObject(expected);
|
|
274
|
+
});
|
|
183
275
|
});
|
|
184
276
|
|
|
185
|
-
|
|
186
|
-
const
|
|
187
|
-
|
|
277
|
+
describe('operations', () => {
|
|
278
|
+
const queryStrings = [
|
|
279
|
+
// OR simple.
|
|
280
|
+
'or(index=2,position=5)',
|
|
281
|
+
// OR short.
|
|
282
|
+
'|(index=2,position=5)',
|
|
188
283
|
|
|
284
|
+
// Not simple.
|
|
285
|
+
'key=not(main)',
|
|
286
|
+
// Not short.
|
|
287
|
+
'key=!(main)',
|
|
288
|
+
// NOT inside include.
|
|
289
|
+
'includes=comments(id=not(7))',
|
|
189
290
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
tree.include('users').use('users');
|
|
194
|
-
tree.include('avatars').use('avatars');
|
|
195
|
-
tree.include('sizes').use('sizes');
|
|
196
|
-
const expected = tree.root.toObject();
|
|
291
|
+
// Like simple.
|
|
292
|
+
'title=like(some_text)',
|
|
293
|
+
];
|
|
197
294
|
|
|
198
|
-
|
|
199
|
-
|
|
295
|
+
test('"OR" simple', () => {
|
|
296
|
+
const lexer = new QueryLexer( queryStrings[0] );
|
|
297
|
+
const result = lexer.query;
|
|
200
298
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
const tree = new ModelsTree();
|
|
206
|
-
tree.node.addWhere({ id: ['7'] });
|
|
207
|
-
tree.node.limit = 3;
|
|
208
|
-
tree.include('comments').use('comments');
|
|
209
|
-
tree.include('users').use('users');
|
|
210
|
-
tree.include('avatars').use('avatars');
|
|
211
|
-
tree.node.fields = [ 'id', 'content' ];
|
|
212
|
-
tree.node.order = 'rand';
|
|
213
|
-
const expected = tree.root.toObject();
|
|
214
|
-
|
|
215
|
-
expect(result).toMatchObject(expected);
|
|
216
|
-
});
|
|
299
|
+
const tree = new ModelsTree();
|
|
300
|
+
tree.node.addWhere({ or: [ { index: ['2'] }, { position: ['5'] } ] });
|
|
301
|
+
const expected = tree.root.toObject();
|
|
217
302
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
const result = lexer.query;
|
|
221
|
-
|
|
222
|
-
const tree = new ModelsTree();
|
|
223
|
-
tree.node.addWhere({ id: ['7'] });
|
|
224
|
-
tree.node.limit = 3;
|
|
225
|
-
tree.include('comments').use('comments');
|
|
226
|
-
tree.node.order = 'rand';
|
|
227
|
-
tree.up();
|
|
228
|
-
tree.include('users').use('users');
|
|
229
|
-
tree.node.fields = [ 'id', 'content' ];
|
|
230
|
-
const expected = tree.root.toObject();
|
|
231
|
-
|
|
232
|
-
expect(result).toMatchObject(expected);
|
|
233
|
-
});
|
|
303
|
+
expect(result).toMatchObject(expected);
|
|
304
|
+
});
|
|
234
305
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
306
|
+
test('"OR" short', () => {
|
|
307
|
+
const lexer = new QueryLexer( queryStrings[1] );
|
|
308
|
+
const result = lexer.query;
|
|
238
309
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
310
|
+
const tree = new ModelsTree();
|
|
311
|
+
tree.node.addWhere({ or: [ { index: ['2'] }, { position: ['5'] } ] });
|
|
312
|
+
const expected = tree.root.toObject();
|
|
242
313
|
|
|
243
|
-
|
|
244
|
-
|
|
314
|
+
expect(result).toMatchObject(expected);
|
|
315
|
+
});
|
|
245
316
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
317
|
+
test('"NOT" simple', () => {
|
|
318
|
+
const lexer = new QueryLexer( queryStrings[2] );
|
|
319
|
+
const result = lexer.query;
|
|
249
320
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
321
|
+
const tree = new ModelsTree();
|
|
322
|
+
tree.node.addWhere({ key: { not: ['main'] } });
|
|
323
|
+
const expected = tree.root.toObject();
|
|
253
324
|
|
|
254
|
-
|
|
255
|
-
|
|
325
|
+
expect(result).toMatchObject(expected);
|
|
326
|
+
});
|
|
256
327
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
328
|
+
test('"NOT" short', () => {
|
|
329
|
+
const lexer = new QueryLexer( queryStrings[3] );
|
|
330
|
+
const result = lexer.query;
|
|
260
331
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
const expected = tree.root.toObject();
|
|
332
|
+
const tree = new ModelsTree();
|
|
333
|
+
tree.node.addWhere({ key: { not: ['main'] } });
|
|
334
|
+
const expected = tree.root.toObject();
|
|
265
335
|
|
|
266
|
-
|
|
267
|
-
|
|
336
|
+
expect(result).toMatchObject(expected);
|
|
337
|
+
});
|
|
268
338
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
339
|
+
test('"NOT" inside includes', () => {
|
|
340
|
+
const lexer = new QueryLexer( queryStrings[4] );
|
|
341
|
+
const result = lexer.query;
|
|
272
342
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
343
|
+
const tree = new ModelsTree();
|
|
344
|
+
tree.include('comments').use('comments');
|
|
345
|
+
tree.node.addWhere({ id: { not: ['7'] }});
|
|
346
|
+
const expected = tree.root.toObject();
|
|
276
347
|
|
|
277
|
-
|
|
278
|
-
|
|
348
|
+
expect(result).toMatchObject(expected);
|
|
349
|
+
});
|
|
279
350
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
351
|
+
test('"Like" simple', () => {
|
|
352
|
+
const lexer = new QueryLexer( queryStrings[5] );
|
|
353
|
+
const result = lexer.query;
|
|
283
354
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
tree.up();
|
|
288
|
-
tree.include('likes');
|
|
289
|
-
const expected = tree.root.toObject();
|
|
355
|
+
const tree = new ModelsTree();
|
|
356
|
+
tree.node.addWhere({ title: { like: ['some_text'] }});
|
|
357
|
+
const expected = tree.root.toObject();
|
|
290
358
|
|
|
291
|
-
|
|
359
|
+
expect(result).toMatchObject(expected);
|
|
360
|
+
});
|
|
292
361
|
});
|
|
293
362
|
|
|
294
363
|
});
|
|
File without changes
|