@nymphjs/query-parser 1.0.0-beta.122 → 1.0.0-beta.124
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/CHANGELOG.md +12 -0
- package/README.md +48 -46
- package/dist/queryParser.js +285 -120
- package/dist/queryParser.js.map +1 -1
- package/dist/queryParser.test.js +511 -29
- package/dist/queryParser.test.js.map +1 -1
- package/package.json +3 -3
- package/src/queryParser.test.ts +578 -29
- package/src/queryParser.ts +300 -120
package/src/queryParser.test.ts
CHANGED
|
@@ -3,6 +3,13 @@ import queryParser from './queryParser.js';
|
|
|
3
3
|
|
|
4
4
|
const BlogPost = function () {} as unknown as typeof Entity;
|
|
5
5
|
const Category = function () {} as unknown as typeof Entity;
|
|
6
|
+
const List = function () {} as unknown as typeof Entity;
|
|
7
|
+
|
|
8
|
+
const Mail = function () {} as unknown as typeof Entity;
|
|
9
|
+
const Headers = function () {} as unknown as typeof Entity;
|
|
10
|
+
const Body = function () {} as unknown as typeof Entity;
|
|
11
|
+
const Label = function () {} as unknown as typeof Entity;
|
|
12
|
+
const Domain = function () {} as unknown as typeof Entity;
|
|
6
13
|
|
|
7
14
|
describe('queryParser', () => {
|
|
8
15
|
it('parses a basic query', () => {
|
|
@@ -55,13 +62,13 @@ describe('queryParser', () => {
|
|
|
55
62
|
});
|
|
56
63
|
|
|
57
64
|
it('gives the correct entity and default fields to bare queries', () => {
|
|
58
|
-
const query = 'category<{
|
|
65
|
+
const query = 'category<{Cat search}>';
|
|
59
66
|
const [options, ...selectors] = queryParser({
|
|
60
67
|
query,
|
|
61
68
|
entityClass: BlogPost,
|
|
62
69
|
defaultFields: ['text'],
|
|
63
70
|
qrefMap: {
|
|
64
|
-
|
|
71
|
+
Cat: {
|
|
65
72
|
class: Category,
|
|
66
73
|
defaultFields: ['name'],
|
|
67
74
|
},
|
|
@@ -97,13 +104,13 @@ describe('queryParser', () => {
|
|
|
97
104
|
|
|
98
105
|
it('parses the example query from the readme', () => {
|
|
99
106
|
const query =
|
|
100
|
-
'limit:4 foobar (| [archived] mdate<"2 weeks ago") category<{
|
|
107
|
+
'limit:4 foobar (| [archived] mdate<"2 weeks ago") category<{Cat Tech}>';
|
|
101
108
|
const [options, ...selectors] = queryParser({
|
|
102
109
|
query,
|
|
103
110
|
entityClass: BlogPost,
|
|
104
111
|
defaultFields: ['title', 'body'],
|
|
105
112
|
qrefMap: {
|
|
106
|
-
|
|
113
|
+
Cat: {
|
|
107
114
|
class: Category,
|
|
108
115
|
defaultFields: ['name'],
|
|
109
116
|
},
|
|
@@ -289,6 +296,108 @@ describe('queryParser', () => {
|
|
|
289
296
|
]);
|
|
290
297
|
});
|
|
291
298
|
|
|
299
|
+
it('parses a query with not search', () => {
|
|
300
|
+
const query = 'limit:4 foobar -prop(a search string)';
|
|
301
|
+
const [options, ...selectors] = queryParser({
|
|
302
|
+
query,
|
|
303
|
+
entityClass: BlogPost,
|
|
304
|
+
defaultFields: ['title', 'body'],
|
|
305
|
+
qrefMap: {
|
|
306
|
+
cat: {
|
|
307
|
+
class: Category,
|
|
308
|
+
defaultFields: ['name'],
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
expect(options).toEqual({
|
|
314
|
+
class: BlogPost,
|
|
315
|
+
limit: 4,
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
expect(selectors).toEqual([
|
|
319
|
+
{
|
|
320
|
+
type: '&',
|
|
321
|
+
'!search': [['prop', 'a search string']],
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
type: '|',
|
|
325
|
+
ilike: [
|
|
326
|
+
['title', '%foobar%'],
|
|
327
|
+
['body', '%foobar%'],
|
|
328
|
+
],
|
|
329
|
+
},
|
|
330
|
+
]);
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
it('parses a query with colon search', () => {
|
|
334
|
+
const query = 'limit:4 foobar prop:string';
|
|
335
|
+
const [options, ...selectors] = queryParser({
|
|
336
|
+
query,
|
|
337
|
+
entityClass: BlogPost,
|
|
338
|
+
defaultFields: ['title', 'body'],
|
|
339
|
+
qrefMap: {
|
|
340
|
+
cat: {
|
|
341
|
+
class: Category,
|
|
342
|
+
defaultFields: ['name'],
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
expect(options).toEqual({
|
|
348
|
+
class: BlogPost,
|
|
349
|
+
limit: 4,
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
expect(selectors).toEqual([
|
|
353
|
+
{
|
|
354
|
+
type: '&',
|
|
355
|
+
search: [['prop', 'string']],
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
type: '|',
|
|
359
|
+
ilike: [
|
|
360
|
+
['title', '%foobar%'],
|
|
361
|
+
['body', '%foobar%'],
|
|
362
|
+
],
|
|
363
|
+
},
|
|
364
|
+
]);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it('parses a query with not colon search', () => {
|
|
368
|
+
const query = 'limit:4 foobar -prop:string';
|
|
369
|
+
const [options, ...selectors] = queryParser({
|
|
370
|
+
query,
|
|
371
|
+
entityClass: BlogPost,
|
|
372
|
+
defaultFields: ['title', 'body'],
|
|
373
|
+
qrefMap: {
|
|
374
|
+
cat: {
|
|
375
|
+
class: Category,
|
|
376
|
+
defaultFields: ['name'],
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
expect(options).toEqual({
|
|
382
|
+
class: BlogPost,
|
|
383
|
+
limit: 4,
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
expect(selectors).toEqual([
|
|
387
|
+
{
|
|
388
|
+
type: '&',
|
|
389
|
+
'!search': [['prop', 'string']],
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
type: '|',
|
|
393
|
+
ilike: [
|
|
394
|
+
['title', '%foobar%'],
|
|
395
|
+
['body', '%foobar%'],
|
|
396
|
+
],
|
|
397
|
+
},
|
|
398
|
+
]);
|
|
399
|
+
});
|
|
400
|
+
|
|
292
401
|
it('parses a query with search in a selector', () => {
|
|
293
402
|
const query =
|
|
294
403
|
'limit:4 foobar (| prop(a search string) prop2("another search string"))';
|
|
@@ -332,6 +441,53 @@ describe('queryParser', () => {
|
|
|
332
441
|
]);
|
|
333
442
|
});
|
|
334
443
|
|
|
444
|
+
it('parses a query with colon search in a selector', () => {
|
|
445
|
+
const query =
|
|
446
|
+
'limit:4 foobar (| prop:string prop2:"another search string" -prop3:pickle -prop4:\'another -pickle string\')';
|
|
447
|
+
const [options, ...selectors] = queryParser({
|
|
448
|
+
query,
|
|
449
|
+
entityClass: BlogPost,
|
|
450
|
+
defaultFields: ['title', 'body'],
|
|
451
|
+
qrefMap: {
|
|
452
|
+
cat: {
|
|
453
|
+
class: Category,
|
|
454
|
+
defaultFields: ['name'],
|
|
455
|
+
},
|
|
456
|
+
},
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
expect(options).toEqual({
|
|
460
|
+
class: BlogPost,
|
|
461
|
+
limit: 4,
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
expect(selectors).toEqual([
|
|
465
|
+
{
|
|
466
|
+
type: '&',
|
|
467
|
+
selector: [
|
|
468
|
+
{
|
|
469
|
+
type: '|',
|
|
470
|
+
search: [
|
|
471
|
+
['prop', 'string'],
|
|
472
|
+
['prop2', '"another search string"'],
|
|
473
|
+
],
|
|
474
|
+
'!search': [
|
|
475
|
+
['prop3', 'pickle'],
|
|
476
|
+
['prop4', "'another -pickle string'"],
|
|
477
|
+
],
|
|
478
|
+
},
|
|
479
|
+
],
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
type: '|',
|
|
483
|
+
ilike: [
|
|
484
|
+
['title', '%foobar%'],
|
|
485
|
+
['body', '%foobar%'],
|
|
486
|
+
],
|
|
487
|
+
},
|
|
488
|
+
]);
|
|
489
|
+
});
|
|
490
|
+
|
|
335
491
|
it('parses a query with lt and tag', () => {
|
|
336
492
|
const query = 'limit:4 foobar mdate<"2 weeks ago" <tag>';
|
|
337
493
|
const [options, ...selectors] = queryParser({
|
|
@@ -537,7 +693,7 @@ describe('queryParser', () => {
|
|
|
537
693
|
});
|
|
538
694
|
|
|
539
695
|
it('parses simple not equal clauses', () => {
|
|
540
|
-
const query = 'prop
|
|
696
|
+
const query = '-prop=string';
|
|
541
697
|
const [options, ...selectors] = queryParser({
|
|
542
698
|
query,
|
|
543
699
|
entityClass: BlogPost,
|
|
@@ -575,7 +731,7 @@ describe('queryParser', () => {
|
|
|
575
731
|
});
|
|
576
732
|
|
|
577
733
|
it('parses quoted not equal clauses', () => {
|
|
578
|
-
const query = 'prop
|
|
734
|
+
const query = '-prop="a string"';
|
|
579
735
|
const [options, ...selectors] = queryParser({
|
|
580
736
|
query,
|
|
581
737
|
entityClass: BlogPost,
|
|
@@ -613,7 +769,7 @@ describe('queryParser', () => {
|
|
|
613
769
|
});
|
|
614
770
|
|
|
615
771
|
it('parses json not equal clauses', () => {
|
|
616
|
-
const query = 'prop
|
|
772
|
+
const query = '-prop=true';
|
|
617
773
|
const [options, ...selectors] = queryParser({
|
|
618
774
|
query,
|
|
619
775
|
entityClass: BlogPost,
|
|
@@ -651,7 +807,7 @@ describe('queryParser', () => {
|
|
|
651
807
|
});
|
|
652
808
|
|
|
653
809
|
it('parses not guid clauses', () => {
|
|
654
|
-
const query = '{
|
|
810
|
+
const query = '-{111111111111111111111111}';
|
|
655
811
|
const [options, ...selectors] = queryParser({
|
|
656
812
|
query,
|
|
657
813
|
entityClass: BlogPost,
|
|
@@ -689,7 +845,7 @@ describe('queryParser', () => {
|
|
|
689
845
|
});
|
|
690
846
|
|
|
691
847
|
it('parses not tag clauses', () => {
|
|
692
|
-
const query = '
|
|
848
|
+
const query = '-<tagname>';
|
|
693
849
|
const [options, ...selectors] = queryParser({
|
|
694
850
|
query,
|
|
695
851
|
entityClass: BlogPost,
|
|
@@ -727,7 +883,7 @@ describe('queryParser', () => {
|
|
|
727
883
|
});
|
|
728
884
|
|
|
729
885
|
it('parses not truthy clauses', () => {
|
|
730
|
-
const query = '[
|
|
886
|
+
const query = '-[truthyname]';
|
|
731
887
|
const [options, ...selectors] = queryParser({
|
|
732
888
|
query,
|
|
733
889
|
entityClass: BlogPost,
|
|
@@ -765,7 +921,7 @@ describe('queryParser', () => {
|
|
|
765
921
|
});
|
|
766
922
|
|
|
767
923
|
it('parses not ref clauses', () => {
|
|
768
|
-
const query = 'prop
|
|
924
|
+
const query = '-prop<{111111111111111111111111}>';
|
|
769
925
|
const [options, ...selectors] = queryParser({
|
|
770
926
|
query,
|
|
771
927
|
entityClass: BlogPost,
|
|
@@ -803,7 +959,7 @@ describe('queryParser', () => {
|
|
|
803
959
|
});
|
|
804
960
|
|
|
805
961
|
it('parses simple not contain clauses', () => {
|
|
806
|
-
const query = 'prop
|
|
962
|
+
const query = '-prop<string>';
|
|
807
963
|
const [options, ...selectors] = queryParser({
|
|
808
964
|
query,
|
|
809
965
|
entityClass: BlogPost,
|
|
@@ -841,7 +997,7 @@ describe('queryParser', () => {
|
|
|
841
997
|
});
|
|
842
998
|
|
|
843
999
|
it('parses json not contain clauses', () => {
|
|
844
|
-
const query = 'prop
|
|
1000
|
+
const query = '-prop<true>';
|
|
845
1001
|
const [options, ...selectors] = queryParser({
|
|
846
1002
|
query,
|
|
847
1003
|
entityClass: BlogPost,
|
|
@@ -879,7 +1035,7 @@ describe('queryParser', () => {
|
|
|
879
1035
|
});
|
|
880
1036
|
|
|
881
1037
|
it('parses not match clauses', () => {
|
|
882
|
-
const query = 'prop
|
|
1038
|
+
const query = '-prop~/regex/';
|
|
883
1039
|
const [options, ...selectors] = queryParser({
|
|
884
1040
|
query,
|
|
885
1041
|
entityClass: BlogPost,
|
|
@@ -917,7 +1073,7 @@ describe('queryParser', () => {
|
|
|
917
1073
|
});
|
|
918
1074
|
|
|
919
1075
|
it('parses not imatch clauses', () => {
|
|
920
|
-
const query = 'prop
|
|
1076
|
+
const query = '-prop~/regex/i';
|
|
921
1077
|
const [options, ...selectors] = queryParser({
|
|
922
1078
|
query,
|
|
923
1079
|
entityClass: BlogPost,
|
|
@@ -955,7 +1111,7 @@ describe('queryParser', () => {
|
|
|
955
1111
|
});
|
|
956
1112
|
|
|
957
1113
|
it('parses simple not like clauses', () => {
|
|
958
|
-
const query = 'prop
|
|
1114
|
+
const query = '-prop~pattern';
|
|
959
1115
|
const [options, ...selectors] = queryParser({
|
|
960
1116
|
query,
|
|
961
1117
|
entityClass: BlogPost,
|
|
@@ -993,7 +1149,7 @@ describe('queryParser', () => {
|
|
|
993
1149
|
});
|
|
994
1150
|
|
|
995
1151
|
it('parses quoted not like clauses', () => {
|
|
996
|
-
const query = 'prop
|
|
1152
|
+
const query = '-prop~"a pattern"';
|
|
997
1153
|
const [options, ...selectors] = queryParser({
|
|
998
1154
|
query,
|
|
999
1155
|
entityClass: BlogPost,
|
|
@@ -1031,7 +1187,7 @@ describe('queryParser', () => {
|
|
|
1031
1187
|
});
|
|
1032
1188
|
|
|
1033
1189
|
it('parses not ilike clauses', () => {
|
|
1034
|
-
const query = 'prop
|
|
1190
|
+
const query = '-prop~"a pattern"i';
|
|
1035
1191
|
const [options, ...selectors] = queryParser({
|
|
1036
1192
|
query,
|
|
1037
1193
|
entityClass: BlogPost,
|
|
@@ -1068,6 +1224,25 @@ describe('queryParser', () => {
|
|
|
1068
1224
|
]);
|
|
1069
1225
|
});
|
|
1070
1226
|
|
|
1227
|
+
it('parses simple not gt clauses', () => {
|
|
1228
|
+
const query = '-prop>30.5';
|
|
1229
|
+
const [options, ...selectors] = queryParser({
|
|
1230
|
+
query,
|
|
1231
|
+
entityClass: BlogPost,
|
|
1232
|
+
});
|
|
1233
|
+
|
|
1234
|
+
expect(options).toEqual({
|
|
1235
|
+
class: BlogPost,
|
|
1236
|
+
});
|
|
1237
|
+
|
|
1238
|
+
expect(selectors).toEqual([
|
|
1239
|
+
{
|
|
1240
|
+
type: '&',
|
|
1241
|
+
'!gt': [['prop', 30.5]],
|
|
1242
|
+
},
|
|
1243
|
+
]);
|
|
1244
|
+
});
|
|
1245
|
+
|
|
1071
1246
|
it('parses simple relative gt clauses', () => {
|
|
1072
1247
|
const query = 'prop>yesterday';
|
|
1073
1248
|
const [options, ...selectors] = queryParser({
|
|
@@ -1087,6 +1262,25 @@ describe('queryParser', () => {
|
|
|
1087
1262
|
]);
|
|
1088
1263
|
});
|
|
1089
1264
|
|
|
1265
|
+
it('parses simple relative not gt clauses', () => {
|
|
1266
|
+
const query = '-prop>yesterday';
|
|
1267
|
+
const [options, ...selectors] = queryParser({
|
|
1268
|
+
query,
|
|
1269
|
+
entityClass: BlogPost,
|
|
1270
|
+
});
|
|
1271
|
+
|
|
1272
|
+
expect(options).toEqual({
|
|
1273
|
+
class: BlogPost,
|
|
1274
|
+
});
|
|
1275
|
+
|
|
1276
|
+
expect(selectors).toEqual([
|
|
1277
|
+
{
|
|
1278
|
+
type: '&',
|
|
1279
|
+
'!gt': [['prop', null, 'yesterday']],
|
|
1280
|
+
},
|
|
1281
|
+
]);
|
|
1282
|
+
});
|
|
1283
|
+
|
|
1090
1284
|
it('parses quote relative gt clauses', () => {
|
|
1091
1285
|
const query = 'prop>"3 weeks ago"';
|
|
1092
1286
|
const [options, ...selectors] = queryParser({
|
|
@@ -1106,6 +1300,25 @@ describe('queryParser', () => {
|
|
|
1106
1300
|
]);
|
|
1107
1301
|
});
|
|
1108
1302
|
|
|
1303
|
+
it('parses quote relative not gt clauses', () => {
|
|
1304
|
+
const query = '-prop>"3 weeks ago"';
|
|
1305
|
+
const [options, ...selectors] = queryParser({
|
|
1306
|
+
query,
|
|
1307
|
+
entityClass: BlogPost,
|
|
1308
|
+
});
|
|
1309
|
+
|
|
1310
|
+
expect(options).toEqual({
|
|
1311
|
+
class: BlogPost,
|
|
1312
|
+
});
|
|
1313
|
+
|
|
1314
|
+
expect(selectors).toEqual([
|
|
1315
|
+
{
|
|
1316
|
+
type: '&',
|
|
1317
|
+
'!gt': [['prop', null, '3 weeks ago']],
|
|
1318
|
+
},
|
|
1319
|
+
]);
|
|
1320
|
+
});
|
|
1321
|
+
|
|
1109
1322
|
it('parses simple gte clauses', () => {
|
|
1110
1323
|
const query = 'prop>=30.5';
|
|
1111
1324
|
const [options, ...selectors] = queryParser({
|
|
@@ -1125,6 +1338,25 @@ describe('queryParser', () => {
|
|
|
1125
1338
|
]);
|
|
1126
1339
|
});
|
|
1127
1340
|
|
|
1341
|
+
it('parses simple not gte clauses', () => {
|
|
1342
|
+
const query = '-prop>=30.5';
|
|
1343
|
+
const [options, ...selectors] = queryParser({
|
|
1344
|
+
query,
|
|
1345
|
+
entityClass: BlogPost,
|
|
1346
|
+
});
|
|
1347
|
+
|
|
1348
|
+
expect(options).toEqual({
|
|
1349
|
+
class: BlogPost,
|
|
1350
|
+
});
|
|
1351
|
+
|
|
1352
|
+
expect(selectors).toEqual([
|
|
1353
|
+
{
|
|
1354
|
+
type: '&',
|
|
1355
|
+
'!gte': [['prop', 30.5]],
|
|
1356
|
+
},
|
|
1357
|
+
]);
|
|
1358
|
+
});
|
|
1359
|
+
|
|
1128
1360
|
it('parses simple relative gte clauses', () => {
|
|
1129
1361
|
const query = 'prop>=yesterday';
|
|
1130
1362
|
const [options, ...selectors] = queryParser({
|
|
@@ -1144,6 +1376,25 @@ describe('queryParser', () => {
|
|
|
1144
1376
|
]);
|
|
1145
1377
|
});
|
|
1146
1378
|
|
|
1379
|
+
it('parses simple relative not gte clauses', () => {
|
|
1380
|
+
const query = '-prop>=yesterday';
|
|
1381
|
+
const [options, ...selectors] = queryParser({
|
|
1382
|
+
query,
|
|
1383
|
+
entityClass: BlogPost,
|
|
1384
|
+
});
|
|
1385
|
+
|
|
1386
|
+
expect(options).toEqual({
|
|
1387
|
+
class: BlogPost,
|
|
1388
|
+
});
|
|
1389
|
+
|
|
1390
|
+
expect(selectors).toEqual([
|
|
1391
|
+
{
|
|
1392
|
+
type: '&',
|
|
1393
|
+
'!gte': [['prop', null, 'yesterday']],
|
|
1394
|
+
},
|
|
1395
|
+
]);
|
|
1396
|
+
});
|
|
1397
|
+
|
|
1147
1398
|
it('parses quote relative gte clauses', () => {
|
|
1148
1399
|
const query = 'prop>="3 weeks ago"';
|
|
1149
1400
|
const [options, ...selectors] = queryParser({
|
|
@@ -1163,6 +1414,25 @@ describe('queryParser', () => {
|
|
|
1163
1414
|
]);
|
|
1164
1415
|
});
|
|
1165
1416
|
|
|
1417
|
+
it('parses quote relative not gte clauses', () => {
|
|
1418
|
+
const query = '-prop>="3 weeks ago"';
|
|
1419
|
+
const [options, ...selectors] = queryParser({
|
|
1420
|
+
query,
|
|
1421
|
+
entityClass: BlogPost,
|
|
1422
|
+
});
|
|
1423
|
+
|
|
1424
|
+
expect(options).toEqual({
|
|
1425
|
+
class: BlogPost,
|
|
1426
|
+
});
|
|
1427
|
+
|
|
1428
|
+
expect(selectors).toEqual([
|
|
1429
|
+
{
|
|
1430
|
+
type: '&',
|
|
1431
|
+
'!gte': [['prop', null, '3 weeks ago']],
|
|
1432
|
+
},
|
|
1433
|
+
]);
|
|
1434
|
+
});
|
|
1435
|
+
|
|
1166
1436
|
it('parses simple lt clauses', () => {
|
|
1167
1437
|
const query = 'prop<30.5';
|
|
1168
1438
|
const [options, ...selectors] = queryParser({
|
|
@@ -1182,6 +1452,25 @@ describe('queryParser', () => {
|
|
|
1182
1452
|
]);
|
|
1183
1453
|
});
|
|
1184
1454
|
|
|
1455
|
+
it('parses simple not lt clauses', () => {
|
|
1456
|
+
const query = '-prop<30.5';
|
|
1457
|
+
const [options, ...selectors] = queryParser({
|
|
1458
|
+
query,
|
|
1459
|
+
entityClass: BlogPost,
|
|
1460
|
+
});
|
|
1461
|
+
|
|
1462
|
+
expect(options).toEqual({
|
|
1463
|
+
class: BlogPost,
|
|
1464
|
+
});
|
|
1465
|
+
|
|
1466
|
+
expect(selectors).toEqual([
|
|
1467
|
+
{
|
|
1468
|
+
type: '&',
|
|
1469
|
+
'!lt': [['prop', 30.5]],
|
|
1470
|
+
},
|
|
1471
|
+
]);
|
|
1472
|
+
});
|
|
1473
|
+
|
|
1185
1474
|
it('parses simple relative lt clauses', () => {
|
|
1186
1475
|
const query = 'prop<yesterday';
|
|
1187
1476
|
const [options, ...selectors] = queryParser({
|
|
@@ -1201,6 +1490,25 @@ describe('queryParser', () => {
|
|
|
1201
1490
|
]);
|
|
1202
1491
|
});
|
|
1203
1492
|
|
|
1493
|
+
it('parses simple relative not lt clauses', () => {
|
|
1494
|
+
const query = '-prop<yesterday';
|
|
1495
|
+
const [options, ...selectors] = queryParser({
|
|
1496
|
+
query,
|
|
1497
|
+
entityClass: BlogPost,
|
|
1498
|
+
});
|
|
1499
|
+
|
|
1500
|
+
expect(options).toEqual({
|
|
1501
|
+
class: BlogPost,
|
|
1502
|
+
});
|
|
1503
|
+
|
|
1504
|
+
expect(selectors).toEqual([
|
|
1505
|
+
{
|
|
1506
|
+
type: '&',
|
|
1507
|
+
'!lt': [['prop', null, 'yesterday']],
|
|
1508
|
+
},
|
|
1509
|
+
]);
|
|
1510
|
+
});
|
|
1511
|
+
|
|
1204
1512
|
it('parses quote relative lt clauses', () => {
|
|
1205
1513
|
const query = 'prop<"3 weeks ago"';
|
|
1206
1514
|
const [options, ...selectors] = queryParser({
|
|
@@ -1220,6 +1528,25 @@ describe('queryParser', () => {
|
|
|
1220
1528
|
]);
|
|
1221
1529
|
});
|
|
1222
1530
|
|
|
1531
|
+
it('parses quote relative not lt clauses', () => {
|
|
1532
|
+
const query = '-prop<"3 weeks ago"';
|
|
1533
|
+
const [options, ...selectors] = queryParser({
|
|
1534
|
+
query,
|
|
1535
|
+
entityClass: BlogPost,
|
|
1536
|
+
});
|
|
1537
|
+
|
|
1538
|
+
expect(options).toEqual({
|
|
1539
|
+
class: BlogPost,
|
|
1540
|
+
});
|
|
1541
|
+
|
|
1542
|
+
expect(selectors).toEqual([
|
|
1543
|
+
{
|
|
1544
|
+
type: '&',
|
|
1545
|
+
'!lt': [['prop', null, '3 weeks ago']],
|
|
1546
|
+
},
|
|
1547
|
+
]);
|
|
1548
|
+
});
|
|
1549
|
+
|
|
1223
1550
|
it('parses simple lte clauses', () => {
|
|
1224
1551
|
const query = 'prop<=30.5';
|
|
1225
1552
|
const [options, ...selectors] = queryParser({
|
|
@@ -1239,6 +1566,25 @@ describe('queryParser', () => {
|
|
|
1239
1566
|
]);
|
|
1240
1567
|
});
|
|
1241
1568
|
|
|
1569
|
+
it('parses simple not lte clauses', () => {
|
|
1570
|
+
const query = '-prop<=30.5';
|
|
1571
|
+
const [options, ...selectors] = queryParser({
|
|
1572
|
+
query,
|
|
1573
|
+
entityClass: BlogPost,
|
|
1574
|
+
});
|
|
1575
|
+
|
|
1576
|
+
expect(options).toEqual({
|
|
1577
|
+
class: BlogPost,
|
|
1578
|
+
});
|
|
1579
|
+
|
|
1580
|
+
expect(selectors).toEqual([
|
|
1581
|
+
{
|
|
1582
|
+
type: '&',
|
|
1583
|
+
'!lte': [['prop', 30.5]],
|
|
1584
|
+
},
|
|
1585
|
+
]);
|
|
1586
|
+
});
|
|
1587
|
+
|
|
1242
1588
|
it('parses simple relative lte clauses', () => {
|
|
1243
1589
|
const query = 'prop<=yesterday';
|
|
1244
1590
|
const [options, ...selectors] = queryParser({
|
|
@@ -1258,6 +1604,25 @@ describe('queryParser', () => {
|
|
|
1258
1604
|
]);
|
|
1259
1605
|
});
|
|
1260
1606
|
|
|
1607
|
+
it('parses simple relative not lte clauses', () => {
|
|
1608
|
+
const query = '-prop<=yesterday';
|
|
1609
|
+
const [options, ...selectors] = queryParser({
|
|
1610
|
+
query,
|
|
1611
|
+
entityClass: BlogPost,
|
|
1612
|
+
});
|
|
1613
|
+
|
|
1614
|
+
expect(options).toEqual({
|
|
1615
|
+
class: BlogPost,
|
|
1616
|
+
});
|
|
1617
|
+
|
|
1618
|
+
expect(selectors).toEqual([
|
|
1619
|
+
{
|
|
1620
|
+
type: '&',
|
|
1621
|
+
'!lte': [['prop', null, 'yesterday']],
|
|
1622
|
+
},
|
|
1623
|
+
]);
|
|
1624
|
+
});
|
|
1625
|
+
|
|
1261
1626
|
it('parses quote relative lte clauses', () => {
|
|
1262
1627
|
const query = 'prop<="3 weeks ago"';
|
|
1263
1628
|
const [options, ...selectors] = queryParser({
|
|
@@ -1277,9 +1642,28 @@ describe('queryParser', () => {
|
|
|
1277
1642
|
]);
|
|
1278
1643
|
});
|
|
1279
1644
|
|
|
1645
|
+
it('parses quote relative not lte clauses', () => {
|
|
1646
|
+
const query = '-prop<="3 weeks ago"';
|
|
1647
|
+
const [options, ...selectors] = queryParser({
|
|
1648
|
+
query,
|
|
1649
|
+
entityClass: BlogPost,
|
|
1650
|
+
});
|
|
1651
|
+
|
|
1652
|
+
expect(options).toEqual({
|
|
1653
|
+
class: BlogPost,
|
|
1654
|
+
});
|
|
1655
|
+
|
|
1656
|
+
expect(selectors).toEqual([
|
|
1657
|
+
{
|
|
1658
|
+
type: '&',
|
|
1659
|
+
'!lte': [['prop', null, '3 weeks ago']],
|
|
1660
|
+
},
|
|
1661
|
+
]);
|
|
1662
|
+
});
|
|
1663
|
+
|
|
1280
1664
|
it('parses complex selectors', () => {
|
|
1281
1665
|
const query =
|
|
1282
|
-
'(! [published] <draft>) (| cdate>"6 months ago" mdate>"1 month ago")';
|
|
1666
|
+
'(! [published] <draft>) (| -cdate>"6 months ago" mdate>"1 month ago")';
|
|
1283
1667
|
const [options, ...selectors] = queryParser({
|
|
1284
1668
|
query,
|
|
1285
1669
|
entityClass: BlogPost,
|
|
@@ -1295,10 +1679,8 @@ describe('queryParser', () => {
|
|
|
1295
1679
|
selector: [
|
|
1296
1680
|
{
|
|
1297
1681
|
type: '|',
|
|
1298
|
-
gt: [
|
|
1299
|
-
|
|
1300
|
-
['mdate', null, '1 month ago'],
|
|
1301
|
-
],
|
|
1682
|
+
'!gt': [['cdate', null, '6 months ago']],
|
|
1683
|
+
gt: [['mdate', null, '1 month ago']],
|
|
1302
1684
|
},
|
|
1303
1685
|
{
|
|
1304
1686
|
type: '!&',
|
|
@@ -1312,7 +1694,7 @@ describe('queryParser', () => {
|
|
|
1312
1694
|
|
|
1313
1695
|
it('parses nested selectors', () => {
|
|
1314
1696
|
const query =
|
|
1315
|
-
'(! [published] <draft> (| cdate>"6 months ago" mdate>"1 month ago"))';
|
|
1697
|
+
'(! [published] <draft> (| -cdate>"6 months ago" mdate>"1 month ago"))';
|
|
1316
1698
|
const [options, ...selectors] = queryParser({
|
|
1317
1699
|
query,
|
|
1318
1700
|
entityClass: BlogPost,
|
|
@@ -1330,10 +1712,8 @@ describe('queryParser', () => {
|
|
|
1330
1712
|
selector: [
|
|
1331
1713
|
{
|
|
1332
1714
|
type: '|',
|
|
1333
|
-
gt: [
|
|
1334
|
-
|
|
1335
|
-
['mdate', null, '1 month ago'],
|
|
1336
|
-
],
|
|
1715
|
+
'!gt': [['cdate', null, '6 months ago']],
|
|
1716
|
+
gt: [['mdate', null, '1 month ago']],
|
|
1337
1717
|
},
|
|
1338
1718
|
],
|
|
1339
1719
|
},
|
|
@@ -1342,7 +1722,7 @@ describe('queryParser', () => {
|
|
|
1342
1722
|
|
|
1343
1723
|
it('parses nested qref clauses', () => {
|
|
1344
1724
|
const query =
|
|
1345
|
-
'categories<{Category parent<{Category id="sort+newsletters"}>}>
|
|
1725
|
+
'categories<{Category parent<{Category list<{List frosting name(favorite)}>}> -parent<{Category id="sort+newsletters"}>}>';
|
|
1346
1726
|
const [options, ...selectors] = queryParser({
|
|
1347
1727
|
query,
|
|
1348
1728
|
entityClass: BlogPost,
|
|
@@ -1352,6 +1732,10 @@ describe('queryParser', () => {
|
|
|
1352
1732
|
class: Category,
|
|
1353
1733
|
defaultFields: ['name'],
|
|
1354
1734
|
},
|
|
1735
|
+
List: {
|
|
1736
|
+
class: List,
|
|
1737
|
+
defaultFields: ['name'],
|
|
1738
|
+
},
|
|
1355
1739
|
},
|
|
1356
1740
|
});
|
|
1357
1741
|
|
|
@@ -1370,6 +1754,33 @@ describe('queryParser', () => {
|
|
|
1370
1754
|
{
|
|
1371
1755
|
type: '&',
|
|
1372
1756
|
qref: [
|
|
1757
|
+
[
|
|
1758
|
+
'parent',
|
|
1759
|
+
[
|
|
1760
|
+
{ class: Category },
|
|
1761
|
+
{
|
|
1762
|
+
type: '&',
|
|
1763
|
+
qref: [
|
|
1764
|
+
[
|
|
1765
|
+
'list',
|
|
1766
|
+
[
|
|
1767
|
+
{ class: List },
|
|
1768
|
+
{
|
|
1769
|
+
type: '&',
|
|
1770
|
+
search: [['name', 'favorite']],
|
|
1771
|
+
},
|
|
1772
|
+
{
|
|
1773
|
+
type: '|',
|
|
1774
|
+
ilike: [['name', '%frosting%']],
|
|
1775
|
+
},
|
|
1776
|
+
],
|
|
1777
|
+
],
|
|
1778
|
+
],
|
|
1779
|
+
},
|
|
1780
|
+
],
|
|
1781
|
+
],
|
|
1782
|
+
],
|
|
1783
|
+
'!qref': [
|
|
1373
1784
|
[
|
|
1374
1785
|
'parent',
|
|
1375
1786
|
[
|
|
@@ -1388,4 +1799,142 @@ describe('queryParser', () => {
|
|
|
1388
1799
|
},
|
|
1389
1800
|
]);
|
|
1390
1801
|
});
|
|
1802
|
+
|
|
1803
|
+
it('handles badly nested qref', () => {
|
|
1804
|
+
const query = 'categories<{Category parent<{Category id="top"}>';
|
|
1805
|
+
const [options, ...selectors] = queryParser({
|
|
1806
|
+
query,
|
|
1807
|
+
entityClass: BlogPost,
|
|
1808
|
+
});
|
|
1809
|
+
|
|
1810
|
+
expect(options).toEqual({
|
|
1811
|
+
class: BlogPost,
|
|
1812
|
+
});
|
|
1813
|
+
|
|
1814
|
+
expect(selectors).toEqual([
|
|
1815
|
+
{
|
|
1816
|
+
type: '&',
|
|
1817
|
+
contain: [['categories', '{Category parent<{Category}']],
|
|
1818
|
+
equal: [['id', 'top']],
|
|
1819
|
+
},
|
|
1820
|
+
]);
|
|
1821
|
+
});
|
|
1822
|
+
|
|
1823
|
+
it('parses a realistic query', () => {
|
|
1824
|
+
const query =
|
|
1825
|
+
'labels<{Label [aggbox]}> -[trash] [delivered] -<spam> -<bare> -<autoreply> -<draft> -<sent> limit:10 reverse:true';
|
|
1826
|
+
const [options, ...selectors] = queryParser({
|
|
1827
|
+
query,
|
|
1828
|
+
entityClass: Mail,
|
|
1829
|
+
defaultFields: ['subject', 'from', 'sender'],
|
|
1830
|
+
qrefMap: {
|
|
1831
|
+
Mail: {
|
|
1832
|
+
class: Mail,
|
|
1833
|
+
defaultFields: ['subject', 'from', 'sender'],
|
|
1834
|
+
},
|
|
1835
|
+
Headers: {
|
|
1836
|
+
class: Headers,
|
|
1837
|
+
defaultFields: ['Message-ID'],
|
|
1838
|
+
},
|
|
1839
|
+
Body: {
|
|
1840
|
+
class: Body,
|
|
1841
|
+
defaultFields: ['text'],
|
|
1842
|
+
},
|
|
1843
|
+
Label: {
|
|
1844
|
+
class: Label,
|
|
1845
|
+
defaultFields: ['name', 'id'],
|
|
1846
|
+
},
|
|
1847
|
+
Domain: {
|
|
1848
|
+
class: Domain,
|
|
1849
|
+
defaultFields: ['fqdn'],
|
|
1850
|
+
},
|
|
1851
|
+
},
|
|
1852
|
+
bareHandler: (
|
|
1853
|
+
input,
|
|
1854
|
+
entityClass,
|
|
1855
|
+
defaultFields = ['subject', 'from', 'sender'],
|
|
1856
|
+
) => {
|
|
1857
|
+
if (entityClass === Mail) {
|
|
1858
|
+
return {
|
|
1859
|
+
type: '|',
|
|
1860
|
+
search: defaultFields.map((field) => [field, input]) as [
|
|
1861
|
+
string,
|
|
1862
|
+
string,
|
|
1863
|
+
][],
|
|
1864
|
+
qref: [
|
|
1865
|
+
[
|
|
1866
|
+
'body',
|
|
1867
|
+
[
|
|
1868
|
+
{ class: Body },
|
|
1869
|
+
{
|
|
1870
|
+
type: '&',
|
|
1871
|
+
search: ['text', input],
|
|
1872
|
+
},
|
|
1873
|
+
],
|
|
1874
|
+
],
|
|
1875
|
+
[
|
|
1876
|
+
'body',
|
|
1877
|
+
[
|
|
1878
|
+
{ class: Body },
|
|
1879
|
+
{
|
|
1880
|
+
type: '&',
|
|
1881
|
+
search: ['html', input],
|
|
1882
|
+
},
|
|
1883
|
+
],
|
|
1884
|
+
],
|
|
1885
|
+
[
|
|
1886
|
+
'body',
|
|
1887
|
+
[
|
|
1888
|
+
{ class: Body },
|
|
1889
|
+
{
|
|
1890
|
+
type: '&',
|
|
1891
|
+
search: ['attachments', input],
|
|
1892
|
+
},
|
|
1893
|
+
],
|
|
1894
|
+
],
|
|
1895
|
+
],
|
|
1896
|
+
};
|
|
1897
|
+
}
|
|
1898
|
+
if (defaultFields.length) {
|
|
1899
|
+
return {
|
|
1900
|
+
type: '|',
|
|
1901
|
+
search: defaultFields.map((field) => [field, input]) as [
|
|
1902
|
+
string,
|
|
1903
|
+
string,
|
|
1904
|
+
][],
|
|
1905
|
+
};
|
|
1906
|
+
}
|
|
1907
|
+
return {};
|
|
1908
|
+
},
|
|
1909
|
+
});
|
|
1910
|
+
|
|
1911
|
+
expect(options).toEqual({
|
|
1912
|
+
class: Mail,
|
|
1913
|
+
limit: 10,
|
|
1914
|
+
reverse: true,
|
|
1915
|
+
});
|
|
1916
|
+
|
|
1917
|
+
expect(selectors).toEqual([
|
|
1918
|
+
{
|
|
1919
|
+
type: '&',
|
|
1920
|
+
'!tag': ['spam', 'bare', 'autoreply', 'draft', 'sent'],
|
|
1921
|
+
'!truthy': ['trash'],
|
|
1922
|
+
qref: [
|
|
1923
|
+
[
|
|
1924
|
+
'labels',
|
|
1925
|
+
[
|
|
1926
|
+
{
|
|
1927
|
+
class: Label,
|
|
1928
|
+
},
|
|
1929
|
+
{
|
|
1930
|
+
truthy: ['aggbox'],
|
|
1931
|
+
type: '&',
|
|
1932
|
+
},
|
|
1933
|
+
],
|
|
1934
|
+
],
|
|
1935
|
+
],
|
|
1936
|
+
truthy: ['delivered'],
|
|
1937
|
+
},
|
|
1938
|
+
]);
|
|
1939
|
+
});
|
|
1391
1940
|
});
|