nodester 0.6.76 → 0.7.1
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.
|
@@ -14,7 +14,7 @@ const debug = require('debug')('nodester:interpreter:ModelsTree');
|
|
|
14
14
|
* @access public
|
|
15
15
|
*/
|
|
16
16
|
class ModelsTreeNode {
|
|
17
|
-
constructor(model, parent=null, opts={}) {
|
|
17
|
+
constructor(model, parent = null, opts = {}) {
|
|
18
18
|
this.model = model;
|
|
19
19
|
this.parent = parent;
|
|
20
20
|
|
|
@@ -30,11 +30,13 @@ class ModelsTreeNode {
|
|
|
30
30
|
this.group_by = opts.group_by ?? undefined;
|
|
31
31
|
this.skip = 0;
|
|
32
32
|
this.limit = -1; // No limit
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
this.order = opts.order ?? undefined;
|
|
35
35
|
this.order_by = opts.order_by ?? undefined;
|
|
36
36
|
// Clauses\
|
|
37
37
|
|
|
38
|
+
this.required = false; // For root filtering via ^
|
|
39
|
+
|
|
38
40
|
this._includes = opts.includes ?? [];
|
|
39
41
|
}
|
|
40
42
|
|
|
@@ -85,14 +87,14 @@ class ModelsTreeNode {
|
|
|
85
87
|
this.fn = null;
|
|
86
88
|
}
|
|
87
89
|
|
|
88
|
-
addWhere(condition={}) {
|
|
90
|
+
addWhere(condition = {}) {
|
|
89
91
|
this._where = {
|
|
90
92
|
...this.where,
|
|
91
93
|
...condition
|
|
92
94
|
}
|
|
93
95
|
}
|
|
94
96
|
|
|
95
|
-
addFunction(fnParams={}) {
|
|
97
|
+
addFunction(fnParams = {}) {
|
|
96
98
|
this._functions.push(fnParams);
|
|
97
99
|
}
|
|
98
100
|
|
|
@@ -102,6 +104,14 @@ class ModelsTreeNode {
|
|
|
102
104
|
return modelTreeNode;
|
|
103
105
|
}
|
|
104
106
|
|
|
107
|
+
markAsRequired() {
|
|
108
|
+
this.required = true;
|
|
109
|
+
// Propagate up the tree (except for root)
|
|
110
|
+
if (this.parent && this.parent.model !== 'root') {
|
|
111
|
+
this.parent.markAsRequired();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
105
115
|
toObject() {
|
|
106
116
|
return {
|
|
107
117
|
model: this.model,
|
|
@@ -116,7 +126,8 @@ class ModelsTreeNode {
|
|
|
116
126
|
limit: this.limit,
|
|
117
127
|
order: this.order,
|
|
118
128
|
order_by: this.order_by,
|
|
119
|
-
|
|
129
|
+
|
|
130
|
+
required: this.required,
|
|
120
131
|
|
|
121
132
|
includes: this.includes.map(i => i.toObject())
|
|
122
133
|
}
|
|
@@ -134,7 +145,7 @@ class ModelsTree {
|
|
|
134
145
|
this.node = this.root;
|
|
135
146
|
}
|
|
136
147
|
|
|
137
|
-
include(model, opts={}) {
|
|
148
|
+
include(model, opts = {}) {
|
|
138
149
|
debug('include', model);
|
|
139
150
|
|
|
140
151
|
const node = new ModelsTreeNode(model, this.node, opts);
|
|
@@ -153,7 +164,7 @@ class ModelsTree {
|
|
|
153
164
|
}
|
|
154
165
|
|
|
155
166
|
debug('use', model, !!foundOne ? '' : '-> failed.');
|
|
156
|
-
|
|
167
|
+
|
|
157
168
|
return foundOne;
|
|
158
169
|
}
|
|
159
170
|
|
|
@@ -242,9 +242,6 @@ module.exports = class QueryLexer {
|
|
|
242
242
|
// Reset:
|
|
243
243
|
tree.node.resetActiveParam();
|
|
244
244
|
tree.node.resetOP();
|
|
245
|
-
|
|
246
|
-
// Lift from subquery.
|
|
247
|
-
tree.up();
|
|
248
245
|
}
|
|
249
246
|
const numberOfProcessedChars = i - startAt;
|
|
250
247
|
return [numberOfProcessedChars];
|
|
@@ -259,7 +256,7 @@ module.exports = class QueryLexer {
|
|
|
259
256
|
|
|
260
257
|
// If OP token:
|
|
261
258
|
if (!!tree.node.op) {
|
|
262
|
-
switch(tree.node.op) {
|
|
259
|
+
switch (tree.node.op) {
|
|
263
260
|
case OP_TOKENS.NOT_IN:
|
|
264
261
|
case OP_TOKENS.IN:
|
|
265
262
|
value.push(token);
|
|
@@ -510,6 +507,19 @@ module.exports = class QueryLexer {
|
|
|
510
507
|
continue;
|
|
511
508
|
}
|
|
512
509
|
|
|
510
|
+
|
|
511
|
+
// ^ can mean:
|
|
512
|
+
// • root filtering (INNER JOIN) - attribute in subquery that filters root
|
|
513
|
+
if (char === '^') {
|
|
514
|
+
debug('char', char, { token, node: tree.node });
|
|
515
|
+
|
|
516
|
+
// Mark current node as required (will propagate up)
|
|
517
|
+
tree.node.markAsRequired();
|
|
518
|
+
|
|
519
|
+
// Continue to next char (don't add ^ to token)
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
|
|
513
523
|
// = can only mean the end of a param name:
|
|
514
524
|
if (char === '=') {
|
|
515
525
|
const param = this.parseParamFromToken(token);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* nodester
|
|
3
3
|
* MIT Licensed
|
|
4
4
|
*/
|
|
5
|
-
|
|
5
|
+
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
8
|
const BOUNDS = require('../../constants/Bounds');
|
|
@@ -44,7 +44,7 @@ module.exports = traverse;
|
|
|
44
44
|
*
|
|
45
45
|
* @access public
|
|
46
46
|
*/
|
|
47
|
-
function traverse(queryNode, filter=null, model=null, association=null) {
|
|
47
|
+
function traverse(queryNode, filter = null, model = null, association = null) {
|
|
48
48
|
const _model = model ?? filter.model;
|
|
49
49
|
|
|
50
50
|
try {
|
|
@@ -55,7 +55,7 @@ function traverse(queryNode, filter=null, model=null, association=null) {
|
|
|
55
55
|
throw err;
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
|
-
catch(error) {
|
|
58
|
+
catch (error) {
|
|
59
59
|
Error.captureStackTrace(error, traverse);
|
|
60
60
|
throw error;
|
|
61
61
|
}
|
|
@@ -78,8 +78,15 @@ function traverse(queryNode, filter=null, model=null, association=null) {
|
|
|
78
78
|
where,
|
|
79
79
|
|
|
80
80
|
includes,
|
|
81
|
+
required,
|
|
81
82
|
} = disassembleQueryNode(queryNode);
|
|
82
83
|
|
|
84
|
+
// If this include is marked as required (root filtering via ^),
|
|
85
|
+
// set it on the query:
|
|
86
|
+
if (required) {
|
|
87
|
+
newQuery.required = true;
|
|
88
|
+
}
|
|
89
|
+
|
|
83
90
|
|
|
84
91
|
// Attribute:
|
|
85
92
|
//
|
|
@@ -102,7 +109,7 @@ function traverse(queryNode, filter=null, model=null, association=null) {
|
|
|
102
109
|
// put them through Filter:
|
|
103
110
|
for (let attribute of filter.attributes) {
|
|
104
111
|
if (attributesAvailable.indexOf(attribute) === -1) {
|
|
105
|
-
const err = new NodesterQueryError(`Field '${
|
|
112
|
+
const err = new NodesterQueryError(`Field '${attribute}' is not present in model.`);
|
|
106
113
|
Error.captureStackTrace(err, traverse);
|
|
107
114
|
throw err;
|
|
108
115
|
}
|
|
@@ -119,7 +126,7 @@ function traverse(queryNode, filter=null, model=null, association=null) {
|
|
|
119
126
|
}
|
|
120
127
|
}
|
|
121
128
|
}
|
|
122
|
-
|
|
129
|
+
|
|
123
130
|
// At least 1 attribute is mandatory
|
|
124
131
|
// or "functions" must be set:
|
|
125
132
|
if (
|
|
@@ -138,12 +145,12 @@ function traverse(queryNode, filter=null, model=null, association=null) {
|
|
|
138
145
|
const fnName = fnParams.fn;
|
|
139
146
|
|
|
140
147
|
if (typeof filter.functions[fnName] === 'undefined') {
|
|
141
|
-
const err = new NodesterQueryError(`Function '${
|
|
148
|
+
const err = new NodesterQueryError(`Function '${fnName}' is not allowed.`);
|
|
142
149
|
Error.captureStackTrace(err, traverse);
|
|
143
150
|
throw err;
|
|
144
151
|
}
|
|
145
152
|
|
|
146
|
-
switch(fnName) {
|
|
153
|
+
switch (fnName) {
|
|
147
154
|
// SQL COUNT():
|
|
148
155
|
case 'count': {
|
|
149
156
|
mapCOUNT(
|
|
@@ -157,7 +164,7 @@ function traverse(queryNode, filter=null, model=null, association=null) {
|
|
|
157
164
|
}
|
|
158
165
|
// Any other function:
|
|
159
166
|
default:
|
|
160
|
-
consl.warn(`function ${
|
|
167
|
+
consl.warn(`function ${fnName}() is not supported`);
|
|
161
168
|
break;
|
|
162
169
|
}
|
|
163
170
|
}
|
|
@@ -167,7 +174,7 @@ function traverse(queryNode, filter=null, model=null, association=null) {
|
|
|
167
174
|
const order = {};
|
|
168
175
|
|
|
169
176
|
const clausesEntries = Object.entries(clauses);
|
|
170
|
-
for (const [
|
|
177
|
+
for (const [clauseName, value] of clausesEntries) {
|
|
171
178
|
|
|
172
179
|
// If clause is not available:
|
|
173
180
|
if (filter != null) {
|
|
@@ -176,7 +183,7 @@ function traverse(queryNode, filter=null, model=null, association=null) {
|
|
|
176
183
|
}
|
|
177
184
|
}
|
|
178
185
|
|
|
179
|
-
switch(clauseName) {
|
|
186
|
+
switch (clauseName) {
|
|
180
187
|
case 'group_by': {
|
|
181
188
|
// Check if this value is a valid attribute:
|
|
182
189
|
if (typeof value === 'undefined') {
|
|
@@ -184,7 +191,7 @@ function traverse(queryNode, filter=null, model=null, association=null) {
|
|
|
184
191
|
}
|
|
185
192
|
|
|
186
193
|
if (typeof _model.tableAttributes[value] === 'undefined') {
|
|
187
|
-
const err = new NodesterQueryError(`group_by '${
|
|
194
|
+
const err = new NodesterQueryError(`group_by '${value}' is not allowed.`);
|
|
188
195
|
Error.captureStackTrace(err, traverse);
|
|
189
196
|
throw err;
|
|
190
197
|
}
|
|
@@ -197,7 +204,7 @@ function traverse(queryNode, filter=null, model=null, association=null) {
|
|
|
197
204
|
const _value = _setValueWithBounds(value, 'number', filter.bounds.clauses.limit);
|
|
198
205
|
|
|
199
206
|
// Do not set if negative:
|
|
200
|
-
if (_value < 0)
|
|
207
|
+
if (_value < 0)
|
|
201
208
|
continue;
|
|
202
209
|
|
|
203
210
|
newQuery.limit = _value;
|
|
@@ -237,9 +244,9 @@ function traverse(queryNode, filter=null, model=null, association=null) {
|
|
|
237
244
|
const staticClausesEntries = Object.entries(filter.statics.clauses);
|
|
238
245
|
|
|
239
246
|
for (let entry of staticClausesEntries) {
|
|
240
|
-
const [
|
|
247
|
+
const [clauseName, staticClauseValue] = entry;
|
|
241
248
|
|
|
242
|
-
switch(clauseName) {
|
|
249
|
+
switch (clauseName) {
|
|
243
250
|
case 'group_by':
|
|
244
251
|
newQuery.group = staticClauseValue;
|
|
245
252
|
continue;
|
|
@@ -282,7 +289,7 @@ function traverse(queryNode, filter=null, model=null, association=null) {
|
|
|
282
289
|
newQuery.order = sequelize.random();
|
|
283
290
|
}
|
|
284
291
|
else {
|
|
285
|
-
const column = sequelize.col(
|
|
292
|
+
const column = sequelize.col(order.by);
|
|
286
293
|
switch (order.order) {
|
|
287
294
|
// MAX/MIN:
|
|
288
295
|
case 'max-asc':
|
|
@@ -293,7 +300,7 @@ function traverse(queryNode, filter=null, model=null, association=null) {
|
|
|
293
300
|
case 'min':
|
|
294
301
|
case 'min-asc':
|
|
295
302
|
case 'max-desc':
|
|
296
|
-
newQuery.order = [
|
|
303
|
+
newQuery.order = [sequelize.fn('max', column), 'DESC'];
|
|
297
304
|
break;
|
|
298
305
|
// MAX/MIN\
|
|
299
306
|
|
|
@@ -303,7 +310,7 @@ function traverse(queryNode, filter=null, model=null, association=null) {
|
|
|
303
310
|
break;
|
|
304
311
|
|
|
305
312
|
default:
|
|
306
|
-
newQuery.order = [
|
|
313
|
+
newQuery.order = [[order.by, order.order]];
|
|
307
314
|
break;
|
|
308
315
|
}
|
|
309
316
|
}
|
|
@@ -316,7 +323,7 @@ function traverse(queryNode, filter=null, model=null, association=null) {
|
|
|
316
323
|
const includeName = include.model;
|
|
317
324
|
|
|
318
325
|
if (rootModelAssociations[includeName] === undefined) {
|
|
319
|
-
const err = new NodesterQueryError(`No include named '${
|
|
326
|
+
const err = new NodesterQueryError(`No include named '${includeName}'`);
|
|
320
327
|
Error.captureStackTrace(err, traverse);
|
|
321
328
|
throw err;
|
|
322
329
|
}
|
|
@@ -330,13 +337,13 @@ function traverse(queryNode, filter=null, model=null, association=null) {
|
|
|
330
337
|
|
|
331
338
|
// Set aatributes from Query:
|
|
332
339
|
const whereEntries = Object.entries(where);
|
|
333
|
-
for (const [
|
|
340
|
+
for (const [attribute, value] of whereEntries) {
|
|
334
341
|
parseWhereEntry(attribute, value, newQuery.where, _model);
|
|
335
342
|
}
|
|
336
343
|
|
|
337
344
|
// Static attributes override previously set attributes:
|
|
338
345
|
const staticAttributesEntries = Object.entries(filter.statics.attributes);
|
|
339
|
-
for (const [
|
|
346
|
+
for (const [attribute, staticValue] of staticAttributesEntries) {
|
|
340
347
|
newQuery.where[attribute] = parseValue(staticValue, attribute, _model);
|
|
341
348
|
}
|
|
342
349
|
|
|
@@ -369,13 +376,13 @@ function traverse(queryNode, filter=null, model=null, association=null) {
|
|
|
369
376
|
*/
|
|
370
377
|
function _traverseIncludes(includes, rootModel, filter, resultQuery) {
|
|
371
378
|
const filterIncludesEntries = Object.entries(filter.includes);
|
|
372
|
-
for (let [
|
|
379
|
+
for (let [includeName, includeFilter] of filterIncludesEntries) {
|
|
373
380
|
|
|
374
381
|
const association = rootModel.associations[includeName];
|
|
375
382
|
|
|
376
383
|
// If no such association:
|
|
377
384
|
if (!association) {
|
|
378
|
-
const err = new NodesterQueryError(`No include named '${
|
|
385
|
+
const err = new NodesterQueryError(`No include named '${includeName}'`);
|
|
379
386
|
Error.captureStackTrace(err, _traverseIncludes);
|
|
380
387
|
throw err;
|
|
381
388
|
}
|
|
@@ -394,7 +401,7 @@ function _traverseIncludes(includes, rootModel, filter, resultQuery) {
|
|
|
394
401
|
}
|
|
395
402
|
|
|
396
403
|
function _traverseIncludedOrders(resultQuery, rootModel) {
|
|
397
|
-
for (let i=0; i < resultQuery.include.length; i++) {
|
|
404
|
+
for (let i = 0; i < resultQuery.include.length; i++) {
|
|
398
405
|
const include = resultQuery.include[i];
|
|
399
406
|
|
|
400
407
|
if (!include?.order) {
|
|
@@ -411,7 +418,7 @@ function _traverseIncludedOrders(resultQuery, rootModel) {
|
|
|
411
418
|
associationType
|
|
412
419
|
} = getModelAssociationProps(rootModel.associations[association]);
|
|
413
420
|
|
|
414
|
-
switch(associationType) {
|
|
421
|
+
switch (associationType) {
|
|
415
422
|
case 'HasMany': {
|
|
416
423
|
include.separate = true;
|
|
417
424
|
// resultQuery.order.push([
|
|
@@ -434,7 +441,7 @@ function _traverseIncludedOrders(resultQuery, rootModel) {
|
|
|
434
441
|
function _setValueWithBounds(value, type, bounds) {
|
|
435
442
|
if (typeof bounds === 'object') {
|
|
436
443
|
|
|
437
|
-
switch(type) {
|
|
444
|
+
switch (type) {
|
|
438
445
|
case 'number': {
|
|
439
446
|
let _value = value;
|
|
440
447
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* nodester
|
|
3
3
|
* MIT Licensed
|
|
4
4
|
*/
|
|
5
|
-
|
|
5
|
+
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
8
|
|
|
@@ -18,6 +18,7 @@ function _disassembleQueryNode(queryNode) {
|
|
|
18
18
|
functions,
|
|
19
19
|
where,
|
|
20
20
|
includes,
|
|
21
|
+
required,
|
|
21
22
|
...clauses
|
|
22
23
|
} = queryNode;
|
|
23
24
|
|
|
@@ -27,6 +28,7 @@ function _disassembleQueryNode(queryNode) {
|
|
|
27
28
|
functions: functions ?? [],
|
|
28
29
|
where: where ?? {},
|
|
29
30
|
includes: includes ?? [],
|
|
31
|
+
required: required ?? false,
|
|
30
32
|
};
|
|
31
33
|
}
|
|
32
34
|
|
package/package.json
CHANGED
package/tests/nql.test.js
CHANGED
|
@@ -24,7 +24,7 @@ describe('nodester Query Language', () => {
|
|
|
24
24
|
];
|
|
25
25
|
|
|
26
26
|
it('Simple where', async () => {
|
|
27
|
-
const lexer = new QueryLexer(
|
|
27
|
+
const lexer = new QueryLexer(queryStrings[0]);
|
|
28
28
|
const result = await lexer.parse();
|
|
29
29
|
|
|
30
30
|
const tree = new ModelsTree();
|
|
@@ -35,24 +35,24 @@ describe('nodester Query Language', () => {
|
|
|
35
35
|
});
|
|
36
36
|
|
|
37
37
|
it('Only certain attributes', async () => {
|
|
38
|
-
const lexer = new QueryLexer(
|
|
38
|
+
const lexer = new QueryLexer(queryStrings[1]);
|
|
39
39
|
const result = await lexer.parse();
|
|
40
40
|
|
|
41
41
|
const tree = new ModelsTree();
|
|
42
|
-
tree.node.attributes = [
|
|
42
|
+
tree.node.attributes = ['id', 'text'];
|
|
43
43
|
const expected = tree.root.toObject();
|
|
44
44
|
|
|
45
45
|
expect(result).toMatchObject(expected);
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
test('All possible params', async () => {
|
|
49
|
-
const lexer = new QueryLexer(
|
|
49
|
+
const lexer = new QueryLexer(queryStrings[2]);
|
|
50
50
|
const result = await lexer.parse();
|
|
51
51
|
|
|
52
52
|
|
|
53
53
|
const tree = new ModelsTree();
|
|
54
54
|
tree.node.addWhere({ id: ['10'], position: ['4'] });
|
|
55
|
-
tree.node.attributes = [
|
|
55
|
+
tree.node.attributes = ['id', 'content', 'position', 'created_at'];
|
|
56
56
|
tree.node.limit = 3;
|
|
57
57
|
tree.node.skip = 10;
|
|
58
58
|
tree.node.order = 'desc';
|
|
@@ -74,20 +74,20 @@ describe('nodester Query Language', () => {
|
|
|
74
74
|
'2-horizontals': 'includes=comments,users&id=1000',
|
|
75
75
|
// 4 horizontals with subquery.
|
|
76
76
|
'4-horizontals': 'in=categories,replies.users,comments(order_by=position&order=desc),users.avatars',
|
|
77
|
-
|
|
77
|
+
|
|
78
78
|
// Horizontals queried.
|
|
79
79
|
'horizontals-queried': 'includes=comments(order=desc),users,likes(order=rand),reposts&id=1000',
|
|
80
80
|
// Horizontals queried №2.
|
|
81
81
|
'horizontals-queried-2': 'in=comments(order_by=index&order=asc).users.karma',
|
|
82
82
|
// Horizontals queried №3.
|
|
83
83
|
'horizontals-queried-3': 'in=reactions,comments(user_id=gte(4)&skip=10&limit=2).users,likes,reposts',
|
|
84
|
-
|
|
84
|
+
|
|
85
85
|
// Separated includes.
|
|
86
86
|
'separated-includes': 'includes=comments(order=rand)&id=7&limit=3&includes=users(a=id,content)',
|
|
87
87
|
};
|
|
88
88
|
|
|
89
89
|
test('Simple includes', async () => {
|
|
90
|
-
const lexer = new QueryLexer(
|
|
90
|
+
const lexer = new QueryLexer(queryStrings['simple-includes']);
|
|
91
91
|
const result = await lexer.parse();
|
|
92
92
|
|
|
93
93
|
|
|
@@ -100,13 +100,13 @@ describe('nodester Query Language', () => {
|
|
|
100
100
|
});
|
|
101
101
|
|
|
102
102
|
test('Include with all possible params', async () => {
|
|
103
|
-
const lexer = new QueryLexer(
|
|
103
|
+
const lexer = new QueryLexer(queryStrings['include-with-params']);
|
|
104
104
|
const result = await lexer.parse();
|
|
105
105
|
|
|
106
106
|
const tree = new ModelsTree();
|
|
107
107
|
tree.include('comments').use('comments');
|
|
108
108
|
tree.node.addWhere({ id: ['10'], position: ['4'] });
|
|
109
|
-
tree.node.attributes = [
|
|
109
|
+
tree.node.attributes = ['id', 'content', 'position'];
|
|
110
110
|
tree.node.limit = 3;
|
|
111
111
|
tree.node.skip = 10;
|
|
112
112
|
tree.node.order = 'desc';
|
|
@@ -117,7 +117,7 @@ describe('nodester Query Language', () => {
|
|
|
117
117
|
});
|
|
118
118
|
|
|
119
119
|
test('2 horizontals', async () => {
|
|
120
|
-
const lexer = new QueryLexer(
|
|
120
|
+
const lexer = new QueryLexer(queryStrings['2-horizontals']);
|
|
121
121
|
const result = await lexer.parse();
|
|
122
122
|
|
|
123
123
|
|
|
@@ -133,7 +133,7 @@ describe('nodester Query Language', () => {
|
|
|
133
133
|
test('4 horizontals', async () => {
|
|
134
134
|
// in=categories,replies.users,comments(order_by=position&order=desc),users.avatars
|
|
135
135
|
|
|
136
|
-
const lexer = new QueryLexer(
|
|
136
|
+
const lexer = new QueryLexer(queryStrings['4-horizontals']);
|
|
137
137
|
const result = await lexer.parse();
|
|
138
138
|
|
|
139
139
|
|
|
@@ -162,7 +162,7 @@ describe('nodester Query Language', () => {
|
|
|
162
162
|
});
|
|
163
163
|
|
|
164
164
|
test('Horizontals queried', async () => {
|
|
165
|
-
const lexer = new QueryLexer(
|
|
165
|
+
const lexer = new QueryLexer(queryStrings['horizontals-queried']);
|
|
166
166
|
const result = await lexer.parse();
|
|
167
167
|
|
|
168
168
|
|
|
@@ -182,7 +182,7 @@ describe('nodester Query Language', () => {
|
|
|
182
182
|
});
|
|
183
183
|
|
|
184
184
|
test('Horizontals queried №2', async () => {
|
|
185
|
-
const lexer = new QueryLexer(
|
|
185
|
+
const lexer = new QueryLexer(queryStrings['horizontals-queried-2']);
|
|
186
186
|
const result = await lexer.parse();
|
|
187
187
|
|
|
188
188
|
const tree = new ModelsTree();
|
|
@@ -200,7 +200,7 @@ describe('nodester Query Language', () => {
|
|
|
200
200
|
});
|
|
201
201
|
|
|
202
202
|
test('Horizontals queried №3', async () => {
|
|
203
|
-
const lexer = new QueryLexer(
|
|
203
|
+
const lexer = new QueryLexer(queryStrings['horizontals-queried-3']);
|
|
204
204
|
const result = await lexer.parse();
|
|
205
205
|
|
|
206
206
|
const tree = new ModelsTree();
|
|
@@ -208,9 +208,9 @@ describe('nodester Query Language', () => {
|
|
|
208
208
|
|
|
209
209
|
tree.include('comments').use('comments');
|
|
210
210
|
tree.node.addWhere({
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
211
|
+
user_id: {
|
|
212
|
+
gte: ['4']
|
|
213
|
+
}
|
|
214
214
|
});
|
|
215
215
|
tree.node.skip = 10;
|
|
216
216
|
tree.node.limit = 2;
|
|
@@ -227,7 +227,7 @@ describe('nodester Query Language', () => {
|
|
|
227
227
|
});
|
|
228
228
|
|
|
229
229
|
test('Separated includes"', async () => {
|
|
230
|
-
const lexer = new QueryLexer(
|
|
230
|
+
const lexer = new QueryLexer(queryStrings['separated-includes']);
|
|
231
231
|
const result = await lexer.parse();
|
|
232
232
|
|
|
233
233
|
const tree = new ModelsTree();
|
|
@@ -237,7 +237,7 @@ describe('nodester Query Language', () => {
|
|
|
237
237
|
tree.node.order = 'rand';
|
|
238
238
|
tree.up();
|
|
239
239
|
tree.include('users').use('users');
|
|
240
|
-
tree.node.attributes = [
|
|
240
|
+
tree.node.attributes = ['id', 'content'];
|
|
241
241
|
const expected = tree.root.toObject();
|
|
242
242
|
|
|
243
243
|
expect(result).toMatchObject(expected);
|
|
@@ -260,10 +260,16 @@ describe('nodester Query Language', () => {
|
|
|
260
260
|
|
|
261
261
|
// Complex subincludes query, "+" syntaxis.
|
|
262
262
|
'includes=comments(order=desc).users+likes(order=rand&order_by=position)&id=1000',
|
|
263
|
+
|
|
264
|
+
// Complex subincludes query, "+" syntaxis with include.
|
|
265
|
+
'includes=comments(order=desc).users+likes(order=rand&order_by=position).reviews&id=1000',
|
|
266
|
+
|
|
267
|
+
// Complex subincludes query, "+" syntaxis with double include.
|
|
268
|
+
'includes=comments(order=desc).users+likes(order=rand&order_by=position).reviews.admin&id=1000',
|
|
263
269
|
];
|
|
264
270
|
|
|
265
271
|
test('Simple subinclude', async () => {
|
|
266
|
-
const lexer = new QueryLexer(
|
|
272
|
+
const lexer = new QueryLexer(queryStrings[0]);
|
|
267
273
|
const result = await lexer.parse();
|
|
268
274
|
|
|
269
275
|
|
|
@@ -276,7 +282,7 @@ describe('nodester Query Language', () => {
|
|
|
276
282
|
});
|
|
277
283
|
|
|
278
284
|
test('Deep subincludes', async () => {
|
|
279
|
-
const lexer = new QueryLexer(
|
|
285
|
+
const lexer = new QueryLexer(queryStrings[1]);
|
|
280
286
|
const result = await lexer.parse();
|
|
281
287
|
|
|
282
288
|
|
|
@@ -292,7 +298,7 @@ describe('nodester Query Language', () => {
|
|
|
292
298
|
});
|
|
293
299
|
|
|
294
300
|
test('Simple horizontal subinclude, "+" syntaxis"', async () => {
|
|
295
|
-
const lexer = new QueryLexer(
|
|
301
|
+
const lexer = new QueryLexer(queryStrings[2]);
|
|
296
302
|
const result = await lexer.parse();
|
|
297
303
|
|
|
298
304
|
|
|
@@ -306,7 +312,7 @@ describe('nodester Query Language', () => {
|
|
|
306
312
|
});
|
|
307
313
|
|
|
308
314
|
test('Subinclude query', async () => {
|
|
309
|
-
const lexer = new QueryLexer(
|
|
315
|
+
const lexer = new QueryLexer(queryStrings[3]);
|
|
310
316
|
const result = await lexer.parse();
|
|
311
317
|
|
|
312
318
|
|
|
@@ -321,7 +327,45 @@ describe('nodester Query Language', () => {
|
|
|
321
327
|
});
|
|
322
328
|
|
|
323
329
|
test('Complex subincludes query, "+" syntaxis', async () => {
|
|
324
|
-
const lexer = new QueryLexer(
|
|
330
|
+
const lexer = new QueryLexer(queryStrings[4]);
|
|
331
|
+
const result = await lexer.parse();
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
const tree = new ModelsTree();
|
|
335
|
+
tree.include('comments').use('comments');
|
|
336
|
+
tree.node.addWhere({ id: ['1000'] });
|
|
337
|
+
tree.node.order = 'desc';
|
|
338
|
+
tree.include('users');
|
|
339
|
+
tree.include('likes') && tree.use('likes');
|
|
340
|
+
tree.node.order = 'rand';
|
|
341
|
+
tree.node.order_by = 'position';
|
|
342
|
+
const expected = tree.root.toObject();
|
|
343
|
+
|
|
344
|
+
expect(result).toMatchObject(expected);
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
test('Complex subincludes query, "+" syntaxis with include', async () => {
|
|
348
|
+
const lexer = new QueryLexer(queryStrings[5]);
|
|
349
|
+
const result = await lexer.parse();
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
const tree = new ModelsTree();
|
|
353
|
+
tree.node.addWhere({ id: ['1000'] });
|
|
354
|
+
tree.include('comments').use('comments');
|
|
355
|
+
tree.node.order = 'desc';
|
|
356
|
+
tree.include('users');
|
|
357
|
+
tree.include('likes') && tree.use('likes');
|
|
358
|
+
tree.node.order = 'rand';
|
|
359
|
+
tree.node.order_by = 'position';
|
|
360
|
+
tree.include('reviews');
|
|
361
|
+
tree.up();
|
|
362
|
+
const expected = tree.root.toObject();
|
|
363
|
+
|
|
364
|
+
expect(result).toMatchObject(expected);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
test('Complex subincludes query, "+" syntaxis with double include', async () => {
|
|
368
|
+
const lexer = new QueryLexer(queryStrings[6]);
|
|
325
369
|
const result = await lexer.parse();
|
|
326
370
|
|
|
327
371
|
|
|
@@ -333,6 +377,9 @@ describe('nodester Query Language', () => {
|
|
|
333
377
|
tree.include('likes') && tree.use('likes');
|
|
334
378
|
tree.node.order = 'rand';
|
|
335
379
|
tree.node.order_by = 'position';
|
|
380
|
+
tree.include('reviews') && tree.use('reviews');
|
|
381
|
+
tree.include('admin')
|
|
382
|
+
tree.up();
|
|
336
383
|
tree.up();
|
|
337
384
|
const expected = tree.root.toObject();
|
|
338
385
|
|
|
@@ -349,22 +396,22 @@ describe('nodester Query Language', () => {
|
|
|
349
396
|
];
|
|
350
397
|
|
|
351
398
|
test('"OR" simple', async () => {
|
|
352
|
-
const lexer = new QueryLexer(
|
|
399
|
+
const lexer = new QueryLexer(queryStrings[0]);
|
|
353
400
|
const result = await lexer.parse();
|
|
354
401
|
|
|
355
402
|
const tree = new ModelsTree();
|
|
356
|
-
tree.node.addWhere({ or: [
|
|
403
|
+
tree.node.addWhere({ or: [{ index: ['2'] }, { position: ['5'] }] });
|
|
357
404
|
const expected = tree.root.toObject();
|
|
358
405
|
|
|
359
406
|
expect(result).toMatchObject(expected);
|
|
360
407
|
});
|
|
361
408
|
|
|
362
409
|
test('"OR" short', async () => {
|
|
363
|
-
const lexer = new QueryLexer(
|
|
410
|
+
const lexer = new QueryLexer(queryStrings[1]);
|
|
364
411
|
const result = await lexer.parse();
|
|
365
412
|
|
|
366
413
|
const tree = new ModelsTree();
|
|
367
|
-
tree.node.addWhere({ or: [
|
|
414
|
+
tree.node.addWhere({ or: [{ index: ['2'] }, { position: ['5'] }] });
|
|
368
415
|
const expected = tree.root.toObject();
|
|
369
416
|
|
|
370
417
|
expect(result).toMatchObject(expected);
|
|
@@ -382,7 +429,7 @@ describe('nodester Query Language', () => {
|
|
|
382
429
|
];
|
|
383
430
|
|
|
384
431
|
test('"NOT" simple', async () => {
|
|
385
|
-
const lexer = new QueryLexer(
|
|
432
|
+
const lexer = new QueryLexer(queryStrings[0]);
|
|
386
433
|
const result = await lexer.parse();
|
|
387
434
|
|
|
388
435
|
const tree = new ModelsTree();
|
|
@@ -393,7 +440,7 @@ describe('nodester Query Language', () => {
|
|
|
393
440
|
});
|
|
394
441
|
|
|
395
442
|
test('"NOT" short', async () => {
|
|
396
|
-
const lexer = new QueryLexer(
|
|
443
|
+
const lexer = new QueryLexer(queryStrings[1]);
|
|
397
444
|
const result = await lexer.parse();
|
|
398
445
|
|
|
399
446
|
const tree = new ModelsTree();
|
|
@@ -404,12 +451,12 @@ describe('nodester Query Language', () => {
|
|
|
404
451
|
});
|
|
405
452
|
|
|
406
453
|
test('"NOT" inside includes', async () => {
|
|
407
|
-
const lexer = new QueryLexer(
|
|
454
|
+
const lexer = new QueryLexer(queryStrings[2]);
|
|
408
455
|
const result = await lexer.parse();
|
|
409
456
|
|
|
410
457
|
const tree = new ModelsTree();
|
|
411
458
|
tree.include('comments').use('comments');
|
|
412
|
-
tree.node.addWhere({ id: { not: ['7'] }});
|
|
459
|
+
tree.node.addWhere({ id: { not: ['7'] } });
|
|
413
460
|
const expected = tree.root.toObject();
|
|
414
461
|
|
|
415
462
|
expect(result).toMatchObject(expected);
|
|
@@ -429,33 +476,33 @@ describe('nodester Query Language', () => {
|
|
|
429
476
|
];
|
|
430
477
|
|
|
431
478
|
test('"Like" simple', async () => {
|
|
432
|
-
const lexer = new QueryLexer(
|
|
479
|
+
const lexer = new QueryLexer(queryStrings[0]);
|
|
433
480
|
const result = await lexer.parse();
|
|
434
481
|
|
|
435
482
|
const tree = new ModelsTree();
|
|
436
|
-
tree.node.addWhere({ title: { like: ['some_text'] }});
|
|
483
|
+
tree.node.addWhere({ title: { like: ['some_text'] } });
|
|
437
484
|
const expected = tree.root.toObject();
|
|
438
485
|
|
|
439
486
|
expect(result).toMatchObject(expected);
|
|
440
487
|
});
|
|
441
488
|
|
|
442
489
|
test('"NotLike" simple', async () => {
|
|
443
|
-
const lexer = new QueryLexer(
|
|
490
|
+
const lexer = new QueryLexer(queryStrings[1]);
|
|
444
491
|
const result = await lexer.parse();
|
|
445
492
|
|
|
446
493
|
const tree = new ModelsTree();
|
|
447
|
-
tree.node.addWhere({ title: { notLike: ['some_text'] }});
|
|
494
|
+
tree.node.addWhere({ title: { notLike: ['some_text'] } });
|
|
448
495
|
const expected = tree.root.toObject();
|
|
449
496
|
|
|
450
497
|
expect(result).toMatchObject(expected);
|
|
451
498
|
});
|
|
452
499
|
|
|
453
500
|
test('"NotLike" short', async () => {
|
|
454
|
-
const lexer = new QueryLexer(
|
|
501
|
+
const lexer = new QueryLexer(queryStrings[2]);
|
|
455
502
|
const result = await lexer.parse();
|
|
456
503
|
|
|
457
504
|
const tree = new ModelsTree();
|
|
458
|
-
tree.node.addWhere({ title: { notLike: ['some_text'] }});
|
|
505
|
+
tree.node.addWhere({ title: { notLike: ['some_text'] } });
|
|
459
506
|
const expected = tree.root.toObject();
|
|
460
507
|
|
|
461
508
|
expect(result).toMatchObject(expected);
|
|
@@ -472,23 +519,23 @@ describe('nodester Query Language', () => {
|
|
|
472
519
|
];
|
|
473
520
|
|
|
474
521
|
test('"IN" simple', async () => {
|
|
475
|
-
const lexer = new QueryLexer(
|
|
522
|
+
const lexer = new QueryLexer(queryStrings[0]);
|
|
476
523
|
const result = await lexer.parse();
|
|
477
524
|
|
|
478
525
|
const tree = new ModelsTree();
|
|
479
|
-
tree.node.addWhere({ status: { in: ['REVIEWED', 'ANSWERED'] }});
|
|
526
|
+
tree.node.addWhere({ status: { in: ['REVIEWED', 'ANSWERED'] } });
|
|
480
527
|
const expected = tree.root.toObject();
|
|
481
528
|
|
|
482
529
|
expect(result).toMatchObject(expected);
|
|
483
530
|
});
|
|
484
531
|
|
|
485
532
|
test('"IN" and "limit" clause', async () => {
|
|
486
|
-
const lexer = new QueryLexer(
|
|
533
|
+
const lexer = new QueryLexer(queryStrings[1]);
|
|
487
534
|
const result = await lexer.parse();
|
|
488
535
|
|
|
489
536
|
const tree = new ModelsTree();
|
|
490
537
|
tree.node.limit = 3;
|
|
491
|
-
tree.node.addWhere({ status: { in: ['REVIEWED', 'ANSWERED'] }});
|
|
538
|
+
tree.node.addWhere({ status: { in: ['REVIEWED', 'ANSWERED'] } });
|
|
492
539
|
const expected = tree.root.toObject();
|
|
493
540
|
|
|
494
541
|
expect(result).toMatchObject(expected);
|
|
@@ -514,62 +561,62 @@ describe('nodester Query Language', () => {
|
|
|
514
561
|
];
|
|
515
562
|
|
|
516
563
|
test('Greater than', async () => {
|
|
517
|
-
const lexer = new QueryLexer(
|
|
564
|
+
const lexer = new QueryLexer(queryStrings[0]);
|
|
518
565
|
const result = await lexer.parse();
|
|
519
566
|
|
|
520
567
|
|
|
521
568
|
const tree = new ModelsTree();
|
|
522
|
-
tree.node.addWhere({ created_at: { gt: ['2022'] }});
|
|
569
|
+
tree.node.addWhere({ created_at: { gt: ['2022'] } });
|
|
523
570
|
const expected = tree.root.toObject();
|
|
524
571
|
|
|
525
572
|
expect(result).toMatchObject(expected);
|
|
526
573
|
});
|
|
527
574
|
|
|
528
575
|
test('Greater than or equal to', async () => {
|
|
529
|
-
const lexer = new QueryLexer(
|
|
576
|
+
const lexer = new QueryLexer(queryStrings[1]);
|
|
530
577
|
const result = await lexer.parse();
|
|
531
578
|
|
|
532
579
|
|
|
533
580
|
const tree = new ModelsTree();
|
|
534
|
-
tree.node.addWhere({ created_at: { gte: ['2023-12-08'] }});
|
|
581
|
+
tree.node.addWhere({ created_at: { gte: ['2023-12-08'] } });
|
|
535
582
|
const expected = tree.root.toObject();
|
|
536
583
|
|
|
537
584
|
expect(result).toMatchObject(expected);
|
|
538
585
|
});
|
|
539
586
|
|
|
540
587
|
test('Lower than', async () => {
|
|
541
|
-
const lexer = new QueryLexer(
|
|
588
|
+
const lexer = new QueryLexer(queryStrings[2]);
|
|
542
589
|
const result = await lexer.parse();
|
|
543
590
|
|
|
544
591
|
|
|
545
592
|
const tree = new ModelsTree();
|
|
546
|
-
tree.node.addWhere({ index: { lt: ['10'] }});
|
|
593
|
+
tree.node.addWhere({ index: { lt: ['10'] } });
|
|
547
594
|
const expected = tree.root.toObject();
|
|
548
595
|
|
|
549
596
|
expect(result).toMatchObject(expected);
|
|
550
597
|
});
|
|
551
598
|
|
|
552
599
|
test('Lower than or equal to', async () => {
|
|
553
|
-
const lexer = new QueryLexer(
|
|
600
|
+
const lexer = new QueryLexer(queryStrings[3]);
|
|
554
601
|
const result = await lexer.parse();
|
|
555
602
|
|
|
556
603
|
|
|
557
604
|
const tree = new ModelsTree();
|
|
558
|
-
tree.node.addWhere({ index: { lte: ['9'] }});
|
|
605
|
+
tree.node.addWhere({ index: { lte: ['9'] } });
|
|
559
606
|
const expected = tree.root.toObject();
|
|
560
607
|
|
|
561
608
|
expect(result).toMatchObject(expected);
|
|
562
609
|
});
|
|
563
610
|
|
|
564
611
|
test('Greater than in subinclude', async () => {
|
|
565
|
-
const lexer = new QueryLexer(
|
|
612
|
+
const lexer = new QueryLexer(queryStrings[4]);
|
|
566
613
|
const result = await lexer.parse();
|
|
567
614
|
|
|
568
615
|
|
|
569
616
|
const tree = new ModelsTree();
|
|
570
617
|
tree.include('comments').use('comments');
|
|
571
618
|
tree.include('likes').use('likes');
|
|
572
|
-
tree.node.addWhere({ index: { gt: ['60'] }});
|
|
619
|
+
tree.node.addWhere({ index: { gt: ['60'] } });
|
|
573
620
|
const expected = tree.root.toObject();
|
|
574
621
|
|
|
575
622
|
expect(result).toMatchObject(expected);
|
|
@@ -587,18 +634,18 @@ describe('nodester Query Language', () => {
|
|
|
587
634
|
}
|
|
588
635
|
|
|
589
636
|
test('AND (simple)', async () => {
|
|
590
|
-
const lexer = new QueryLexer(
|
|
637
|
+
const lexer = new QueryLexer(queryStrings.and_simple);
|
|
591
638
|
const result = await lexer.parse();
|
|
592
639
|
|
|
593
640
|
const tree = new ModelsTree();
|
|
594
|
-
tree.node.addWhere({ id: { gte: ['2'], lt: ['5'] }});
|
|
641
|
+
tree.node.addWhere({ id: { gte: ['2'], lt: ['5'] } });
|
|
595
642
|
const expected = tree.root.toObject();
|
|
596
643
|
|
|
597
644
|
expect(result).toMatchObject(expected);
|
|
598
645
|
});
|
|
599
646
|
|
|
600
647
|
test('AND (more OP)', async () => {
|
|
601
|
-
const lexer = new QueryLexer(
|
|
648
|
+
const lexer = new QueryLexer(queryStrings.and_more_op);
|
|
602
649
|
const result = await lexer.parse();
|
|
603
650
|
|
|
604
651
|
const tree = new ModelsTree();
|
|
@@ -611,7 +658,7 @@ describe('nodester Query Language', () => {
|
|
|
611
658
|
});
|
|
612
659
|
|
|
613
660
|
test('AND (in subincludes #0)', async () => {
|
|
614
|
-
const lexer = new QueryLexer(
|
|
661
|
+
const lexer = new QueryLexer(queryStrings.and_in_subincludes_0);
|
|
615
662
|
const result = await lexer.parse();
|
|
616
663
|
|
|
617
664
|
const tree = new ModelsTree();
|
|
@@ -625,7 +672,7 @@ describe('nodester Query Language', () => {
|
|
|
625
672
|
});
|
|
626
673
|
|
|
627
674
|
test('AND (in subincludes #1)', async () => {
|
|
628
|
-
const lexer = new QueryLexer(
|
|
675
|
+
const lexer = new QueryLexer(queryStrings.and_in_subincludes_1);
|
|
629
676
|
const result = await lexer.parse();
|
|
630
677
|
|
|
631
678
|
const tree = new ModelsTree();
|
|
@@ -642,7 +689,7 @@ describe('nodester Query Language', () => {
|
|
|
642
689
|
});
|
|
643
690
|
|
|
644
691
|
test('AND (in subincludes #2)', async () => {
|
|
645
|
-
const lexer = new QueryLexer(
|
|
692
|
+
const lexer = new QueryLexer(queryStrings.and_in_subincludes_2);
|
|
646
693
|
const result = await lexer.parse();
|
|
647
694
|
|
|
648
695
|
const tree = new ModelsTree();
|
|
@@ -669,7 +716,7 @@ describe('nodester Query Language', () => {
|
|
|
669
716
|
}
|
|
670
717
|
|
|
671
718
|
test('Count (full key name)', async () => {
|
|
672
|
-
const lexer = new QueryLexer(
|
|
719
|
+
const lexer = new QueryLexer(queryStrings.count_long);
|
|
673
720
|
const result = await lexer.parse();
|
|
674
721
|
|
|
675
722
|
|
|
@@ -684,7 +731,7 @@ describe('nodester Query Language', () => {
|
|
|
684
731
|
});
|
|
685
732
|
|
|
686
733
|
test('Count (short key name)', async () => {
|
|
687
|
-
const lexer = new QueryLexer(
|
|
734
|
+
const lexer = new QueryLexer(queryStrings.count_long);
|
|
688
735
|
const result = await lexer.parse();
|
|
689
736
|
|
|
690
737
|
|
|
@@ -699,7 +746,7 @@ describe('nodester Query Language', () => {
|
|
|
699
746
|
});
|
|
700
747
|
|
|
701
748
|
test('Count and includes', async () => {
|
|
702
|
-
const lexer = new QueryLexer(
|
|
749
|
+
const lexer = new QueryLexer(queryStrings.count_and_includes);
|
|
703
750
|
const result = await lexer.parse();
|
|
704
751
|
|
|
705
752
|
|
|
@@ -714,4 +761,46 @@ describe('nodester Query Language', () => {
|
|
|
714
761
|
expect(result).toMatchObject(expected);
|
|
715
762
|
});
|
|
716
763
|
});
|
|
764
|
+
|
|
765
|
+
describe('root-filtering', () => {
|
|
766
|
+
const queryStrings = {
|
|
767
|
+
simple: 'includes=order(^is_filled=1)',
|
|
768
|
+
complex: 'includes=product.order(^is_filled=1)+photos(limit=5)',
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
test('Simple root filtering with ^', async () => {
|
|
772
|
+
const lexer = new QueryLexer(queryStrings.simple);
|
|
773
|
+
const result = await lexer.parse();
|
|
774
|
+
|
|
775
|
+
const tree = new ModelsTree();
|
|
776
|
+
tree.include('order').use('order');
|
|
777
|
+
tree.node.addWhere({ is_filled: ['1'] });
|
|
778
|
+
tree.node.markAsRequired();
|
|
779
|
+
const expected = tree.root.toObject();
|
|
780
|
+
|
|
781
|
+
expect(result).toMatchObject(expected);
|
|
782
|
+
expect(result.includes[0].required).toBe(true);
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
test('Complex nested root filtering with ^', async () => {
|
|
786
|
+
const lexer = new QueryLexer(queryStrings.complex);
|
|
787
|
+
const result = await lexer.parse();
|
|
788
|
+
|
|
789
|
+
const tree = new ModelsTree();
|
|
790
|
+
tree.include('product').use('product');
|
|
791
|
+
tree.include('order').use('order');
|
|
792
|
+
tree.node.addWhere({ is_filled: ['1'] });
|
|
793
|
+
tree.node.markAsRequired();
|
|
794
|
+
tree.up();
|
|
795
|
+
tree.up();
|
|
796
|
+
tree.include('photos').use('photos');
|
|
797
|
+
tree.node.limit = 5;
|
|
798
|
+
const expected = tree.root.toObject();
|
|
799
|
+
|
|
800
|
+
expect(result).toMatchObject(expected);
|
|
801
|
+
expect(result.includes[0].required).toBe(true);
|
|
802
|
+
expect(result.includes[0].includes[0].required).toBe(true);
|
|
803
|
+
expect(result.includes[1].required).toBe(false);
|
|
804
|
+
});
|
|
805
|
+
});
|
|
717
806
|
});
|