@nymphjs/query-parser 1.0.0-beta.122 → 1.0.0-beta.123

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.
@@ -1,6 +1,7 @@
1
1
  import queryParser from './queryParser.js';
2
2
  const BlogPost = function () { };
3
3
  const Category = function () { };
4
+ const List = function () { };
4
5
  describe('queryParser', () => {
5
6
  it('parses a basic query', () => {
6
7
  const query = 'search';
@@ -45,13 +46,13 @@ describe('queryParser', () => {
45
46
  ]);
46
47
  });
47
48
  it('gives the correct entity and default fields to bare queries', () => {
48
- const query = 'category<{cat search}>';
49
+ const query = 'category<{Cat search}>';
49
50
  const [options, ...selectors] = queryParser({
50
51
  query,
51
52
  entityClass: BlogPost,
52
53
  defaultFields: ['text'],
53
54
  qrefMap: {
54
- cat: {
55
+ Cat: {
55
56
  class: Category,
56
57
  defaultFields: ['name'],
57
58
  },
@@ -82,13 +83,13 @@ describe('queryParser', () => {
82
83
  ]);
83
84
  });
84
85
  it('parses the example query from the readme', () => {
85
- const query = 'limit:4 foobar (| [archived] mdate<"2 weeks ago") category<{cat Tech}>';
86
+ const query = 'limit:4 foobar (| [archived] mdate<"2 weeks ago") category<{Cat Tech}>';
86
87
  const [options, ...selectors] = queryParser({
87
88
  query,
88
89
  entityClass: BlogPost,
89
90
  defaultFields: ['title', 'body'],
90
91
  qrefMap: {
91
- cat: {
92
+ Cat: {
92
93
  class: Category,
93
94
  defaultFields: ['name'],
94
95
  },
@@ -254,6 +255,99 @@ describe('queryParser', () => {
254
255
  },
255
256
  ]);
256
257
  });
258
+ it('parses a query with not search', () => {
259
+ const query = 'limit:4 foobar -prop(a search string)';
260
+ const [options, ...selectors] = queryParser({
261
+ query,
262
+ entityClass: BlogPost,
263
+ defaultFields: ['title', 'body'],
264
+ qrefMap: {
265
+ cat: {
266
+ class: Category,
267
+ defaultFields: ['name'],
268
+ },
269
+ },
270
+ });
271
+ expect(options).toEqual({
272
+ class: BlogPost,
273
+ limit: 4,
274
+ });
275
+ expect(selectors).toEqual([
276
+ {
277
+ type: '&',
278
+ '!search': [['prop', 'a search string']],
279
+ },
280
+ {
281
+ type: '|',
282
+ ilike: [
283
+ ['title', '%foobar%'],
284
+ ['body', '%foobar%'],
285
+ ],
286
+ },
287
+ ]);
288
+ });
289
+ it('parses a query with colon search', () => {
290
+ const query = 'limit:4 foobar prop:string';
291
+ const [options, ...selectors] = queryParser({
292
+ query,
293
+ entityClass: BlogPost,
294
+ defaultFields: ['title', 'body'],
295
+ qrefMap: {
296
+ cat: {
297
+ class: Category,
298
+ defaultFields: ['name'],
299
+ },
300
+ },
301
+ });
302
+ expect(options).toEqual({
303
+ class: BlogPost,
304
+ limit: 4,
305
+ });
306
+ expect(selectors).toEqual([
307
+ {
308
+ type: '&',
309
+ search: [['prop', 'string']],
310
+ },
311
+ {
312
+ type: '|',
313
+ ilike: [
314
+ ['title', '%foobar%'],
315
+ ['body', '%foobar%'],
316
+ ],
317
+ },
318
+ ]);
319
+ });
320
+ it('parses a query with not colon search', () => {
321
+ const query = 'limit:4 foobar -prop:string';
322
+ const [options, ...selectors] = queryParser({
323
+ query,
324
+ entityClass: BlogPost,
325
+ defaultFields: ['title', 'body'],
326
+ qrefMap: {
327
+ cat: {
328
+ class: Category,
329
+ defaultFields: ['name'],
330
+ },
331
+ },
332
+ });
333
+ expect(options).toEqual({
334
+ class: BlogPost,
335
+ limit: 4,
336
+ });
337
+ expect(selectors).toEqual([
338
+ {
339
+ type: '&',
340
+ '!search': [['prop', 'string']],
341
+ },
342
+ {
343
+ type: '|',
344
+ ilike: [
345
+ ['title', '%foobar%'],
346
+ ['body', '%foobar%'],
347
+ ],
348
+ },
349
+ ]);
350
+ });
257
351
  it('parses a query with search in a selector', () => {
258
352
  const query = 'limit:4 foobar (| prop(a search string) prop2("another search string"))';
259
353
  const [options, ...selectors] = queryParser({
@@ -293,6 +387,49 @@ describe('queryParser', () => {
293
387
  },
294
388
  ]);
295
389
  });
390
+ it('parses a query with colon search in a selector', () => {
391
+ const query = 'limit:4 foobar (| prop:string prop2:"another search string" -prop3:pickle -prop4:\'another -pickle string\')';
392
+ const [options, ...selectors] = queryParser({
393
+ query,
394
+ entityClass: BlogPost,
395
+ defaultFields: ['title', 'body'],
396
+ qrefMap: {
397
+ cat: {
398
+ class: Category,
399
+ defaultFields: ['name'],
400
+ },
401
+ },
402
+ });
403
+ expect(options).toEqual({
404
+ class: BlogPost,
405
+ limit: 4,
406
+ });
407
+ expect(selectors).toEqual([
408
+ {
409
+ type: '&',
410
+ selector: [
411
+ {
412
+ type: '|',
413
+ search: [
414
+ ['prop', 'string'],
415
+ ['prop2', '"another search string"'],
416
+ ],
417
+ '!search': [
418
+ ['prop3', 'pickle'],
419
+ ['prop4', "'another -pickle string'"],
420
+ ],
421
+ },
422
+ ],
423
+ },
424
+ {
425
+ type: '|',
426
+ ilike: [
427
+ ['title', '%foobar%'],
428
+ ['body', '%foobar%'],
429
+ ],
430
+ },
431
+ ]);
432
+ });
296
433
  it('parses a query with lt and tag', () => {
297
434
  const query = 'limit:4 foobar mdate<"2 weeks ago" <tag>';
298
435
  const [options, ...selectors] = queryParser({
@@ -477,7 +614,7 @@ describe('queryParser', () => {
477
614
  ]);
478
615
  });
479
616
  it('parses simple not equal clauses', () => {
480
- const query = 'prop!=string';
617
+ const query = '-prop=string';
481
618
  const [options, ...selectors] = queryParser({
482
619
  query,
483
620
  entityClass: BlogPost,
@@ -509,7 +646,7 @@ describe('queryParser', () => {
509
646
  ]);
510
647
  });
511
648
  it('parses quoted not equal clauses', () => {
512
- const query = 'prop!="a string"';
649
+ const query = '-prop="a string"';
513
650
  const [options, ...selectors] = queryParser({
514
651
  query,
515
652
  entityClass: BlogPost,
@@ -541,7 +678,7 @@ describe('queryParser', () => {
541
678
  ]);
542
679
  });
543
680
  it('parses json not equal clauses', () => {
544
- const query = 'prop!=true';
681
+ const query = '-prop=true';
545
682
  const [options, ...selectors] = queryParser({
546
683
  query,
547
684
  entityClass: BlogPost,
@@ -573,7 +710,7 @@ describe('queryParser', () => {
573
710
  ]);
574
711
  });
575
712
  it('parses not guid clauses', () => {
576
- const query = '{!111111111111111111111111}';
713
+ const query = '-{111111111111111111111111}';
577
714
  const [options, ...selectors] = queryParser({
578
715
  query,
579
716
  entityClass: BlogPost,
@@ -605,7 +742,7 @@ describe('queryParser', () => {
605
742
  ]);
606
743
  });
607
744
  it('parses not tag clauses', () => {
608
- const query = '<!tagname>';
745
+ const query = '-<tagname>';
609
746
  const [options, ...selectors] = queryParser({
610
747
  query,
611
748
  entityClass: BlogPost,
@@ -637,7 +774,7 @@ describe('queryParser', () => {
637
774
  ]);
638
775
  });
639
776
  it('parses not truthy clauses', () => {
640
- const query = '[!truthyname]';
777
+ const query = '-[truthyname]';
641
778
  const [options, ...selectors] = queryParser({
642
779
  query,
643
780
  entityClass: BlogPost,
@@ -669,7 +806,7 @@ describe('queryParser', () => {
669
806
  ]);
670
807
  });
671
808
  it('parses not ref clauses', () => {
672
- const query = 'prop!<{111111111111111111111111}>';
809
+ const query = '-prop<{111111111111111111111111}>';
673
810
  const [options, ...selectors] = queryParser({
674
811
  query,
675
812
  entityClass: BlogPost,
@@ -701,7 +838,7 @@ describe('queryParser', () => {
701
838
  ]);
702
839
  });
703
840
  it('parses simple not contain clauses', () => {
704
- const query = 'prop!<string>';
841
+ const query = '-prop<string>';
705
842
  const [options, ...selectors] = queryParser({
706
843
  query,
707
844
  entityClass: BlogPost,
@@ -733,7 +870,7 @@ describe('queryParser', () => {
733
870
  ]);
734
871
  });
735
872
  it('parses json not contain clauses', () => {
736
- const query = 'prop!<true>';
873
+ const query = '-prop<true>';
737
874
  const [options, ...selectors] = queryParser({
738
875
  query,
739
876
  entityClass: BlogPost,
@@ -765,7 +902,7 @@ describe('queryParser', () => {
765
902
  ]);
766
903
  });
767
904
  it('parses not match clauses', () => {
768
- const query = 'prop!~/regex/';
905
+ const query = '-prop~/regex/';
769
906
  const [options, ...selectors] = queryParser({
770
907
  query,
771
908
  entityClass: BlogPost,
@@ -797,7 +934,7 @@ describe('queryParser', () => {
797
934
  ]);
798
935
  });
799
936
  it('parses not imatch clauses', () => {
800
- const query = 'prop!~/regex/i';
937
+ const query = '-prop~/regex/i';
801
938
  const [options, ...selectors] = queryParser({
802
939
  query,
803
940
  entityClass: BlogPost,
@@ -829,7 +966,7 @@ describe('queryParser', () => {
829
966
  ]);
830
967
  });
831
968
  it('parses simple not like clauses', () => {
832
- const query = 'prop!~pattern';
969
+ const query = '-prop~pattern';
833
970
  const [options, ...selectors] = queryParser({
834
971
  query,
835
972
  entityClass: BlogPost,
@@ -861,7 +998,7 @@ describe('queryParser', () => {
861
998
  ]);
862
999
  });
863
1000
  it('parses quoted not like clauses', () => {
864
- const query = 'prop!~"a pattern"';
1001
+ const query = '-prop~"a pattern"';
865
1002
  const [options, ...selectors] = queryParser({
866
1003
  query,
867
1004
  entityClass: BlogPost,
@@ -893,7 +1030,7 @@ describe('queryParser', () => {
893
1030
  ]);
894
1031
  });
895
1032
  it('parses not ilike clauses', () => {
896
- const query = 'prop!~"a pattern"i';
1033
+ const query = '-prop~"a pattern"i';
897
1034
  const [options, ...selectors] = queryParser({
898
1035
  query,
899
1036
  entityClass: BlogPost,
@@ -924,6 +1061,22 @@ describe('queryParser', () => {
924
1061
  },
925
1062
  ]);
926
1063
  });
1064
+ it('parses simple not gt clauses', () => {
1065
+ const query = '-prop>30.5';
1066
+ const [options, ...selectors] = queryParser({
1067
+ query,
1068
+ entityClass: BlogPost,
1069
+ });
1070
+ expect(options).toEqual({
1071
+ class: BlogPost,
1072
+ });
1073
+ expect(selectors).toEqual([
1074
+ {
1075
+ type: '&',
1076
+ '!gt': [['prop', 30.5]],
1077
+ },
1078
+ ]);
1079
+ });
927
1080
  it('parses simple relative gt clauses', () => {
928
1081
  const query = 'prop>yesterday';
929
1082
  const [options, ...selectors] = queryParser({
@@ -940,6 +1093,22 @@ describe('queryParser', () => {
940
1093
  },
941
1094
  ]);
942
1095
  });
1096
+ it('parses simple relative not gt clauses', () => {
1097
+ const query = '-prop>yesterday';
1098
+ const [options, ...selectors] = queryParser({
1099
+ query,
1100
+ entityClass: BlogPost,
1101
+ });
1102
+ expect(options).toEqual({
1103
+ class: BlogPost,
1104
+ });
1105
+ expect(selectors).toEqual([
1106
+ {
1107
+ type: '&',
1108
+ '!gt': [['prop', null, 'yesterday']],
1109
+ },
1110
+ ]);
1111
+ });
943
1112
  it('parses quote relative gt clauses', () => {
944
1113
  const query = 'prop>"3 weeks ago"';
945
1114
  const [options, ...selectors] = queryParser({
@@ -956,6 +1125,22 @@ describe('queryParser', () => {
956
1125
  },
957
1126
  ]);
958
1127
  });
1128
+ it('parses quote relative not gt clauses', () => {
1129
+ const query = '-prop>"3 weeks ago"';
1130
+ const [options, ...selectors] = queryParser({
1131
+ query,
1132
+ entityClass: BlogPost,
1133
+ });
1134
+ expect(options).toEqual({
1135
+ class: BlogPost,
1136
+ });
1137
+ expect(selectors).toEqual([
1138
+ {
1139
+ type: '&',
1140
+ '!gt': [['prop', null, '3 weeks ago']],
1141
+ },
1142
+ ]);
1143
+ });
959
1144
  it('parses simple gte clauses', () => {
960
1145
  const query = 'prop>=30.5';
961
1146
  const [options, ...selectors] = queryParser({
@@ -972,6 +1157,22 @@ describe('queryParser', () => {
972
1157
  },
973
1158
  ]);
974
1159
  });
1160
+ it('parses simple not gte clauses', () => {
1161
+ const query = '-prop>=30.5';
1162
+ const [options, ...selectors] = queryParser({
1163
+ query,
1164
+ entityClass: BlogPost,
1165
+ });
1166
+ expect(options).toEqual({
1167
+ class: BlogPost,
1168
+ });
1169
+ expect(selectors).toEqual([
1170
+ {
1171
+ type: '&',
1172
+ '!gte': [['prop', 30.5]],
1173
+ },
1174
+ ]);
1175
+ });
975
1176
  it('parses simple relative gte clauses', () => {
976
1177
  const query = 'prop>=yesterday';
977
1178
  const [options, ...selectors] = queryParser({
@@ -988,6 +1189,22 @@ describe('queryParser', () => {
988
1189
  },
989
1190
  ]);
990
1191
  });
1192
+ it('parses simple relative not gte clauses', () => {
1193
+ const query = '-prop>=yesterday';
1194
+ const [options, ...selectors] = queryParser({
1195
+ query,
1196
+ entityClass: BlogPost,
1197
+ });
1198
+ expect(options).toEqual({
1199
+ class: BlogPost,
1200
+ });
1201
+ expect(selectors).toEqual([
1202
+ {
1203
+ type: '&',
1204
+ '!gte': [['prop', null, 'yesterday']],
1205
+ },
1206
+ ]);
1207
+ });
991
1208
  it('parses quote relative gte clauses', () => {
992
1209
  const query = 'prop>="3 weeks ago"';
993
1210
  const [options, ...selectors] = queryParser({
@@ -1004,6 +1221,22 @@ describe('queryParser', () => {
1004
1221
  },
1005
1222
  ]);
1006
1223
  });
1224
+ it('parses quote relative not gte clauses', () => {
1225
+ const query = '-prop>="3 weeks ago"';
1226
+ const [options, ...selectors] = queryParser({
1227
+ query,
1228
+ entityClass: BlogPost,
1229
+ });
1230
+ expect(options).toEqual({
1231
+ class: BlogPost,
1232
+ });
1233
+ expect(selectors).toEqual([
1234
+ {
1235
+ type: '&',
1236
+ '!gte': [['prop', null, '3 weeks ago']],
1237
+ },
1238
+ ]);
1239
+ });
1007
1240
  it('parses simple lt clauses', () => {
1008
1241
  const query = 'prop<30.5';
1009
1242
  const [options, ...selectors] = queryParser({
@@ -1020,6 +1253,22 @@ describe('queryParser', () => {
1020
1253
  },
1021
1254
  ]);
1022
1255
  });
1256
+ it('parses simple not lt clauses', () => {
1257
+ const query = '-prop<30.5';
1258
+ const [options, ...selectors] = queryParser({
1259
+ query,
1260
+ entityClass: BlogPost,
1261
+ });
1262
+ expect(options).toEqual({
1263
+ class: BlogPost,
1264
+ });
1265
+ expect(selectors).toEqual([
1266
+ {
1267
+ type: '&',
1268
+ '!lt': [['prop', 30.5]],
1269
+ },
1270
+ ]);
1271
+ });
1023
1272
  it('parses simple relative lt clauses', () => {
1024
1273
  const query = 'prop<yesterday';
1025
1274
  const [options, ...selectors] = queryParser({
@@ -1036,6 +1285,22 @@ describe('queryParser', () => {
1036
1285
  },
1037
1286
  ]);
1038
1287
  });
1288
+ it('parses simple relative not lt clauses', () => {
1289
+ const query = '-prop<yesterday';
1290
+ const [options, ...selectors] = queryParser({
1291
+ query,
1292
+ entityClass: BlogPost,
1293
+ });
1294
+ expect(options).toEqual({
1295
+ class: BlogPost,
1296
+ });
1297
+ expect(selectors).toEqual([
1298
+ {
1299
+ type: '&',
1300
+ '!lt': [['prop', null, 'yesterday']],
1301
+ },
1302
+ ]);
1303
+ });
1039
1304
  it('parses quote relative lt clauses', () => {
1040
1305
  const query = 'prop<"3 weeks ago"';
1041
1306
  const [options, ...selectors] = queryParser({
@@ -1052,6 +1317,22 @@ describe('queryParser', () => {
1052
1317
  },
1053
1318
  ]);
1054
1319
  });
1320
+ it('parses quote relative not lt clauses', () => {
1321
+ const query = '-prop<"3 weeks ago"';
1322
+ const [options, ...selectors] = queryParser({
1323
+ query,
1324
+ entityClass: BlogPost,
1325
+ });
1326
+ expect(options).toEqual({
1327
+ class: BlogPost,
1328
+ });
1329
+ expect(selectors).toEqual([
1330
+ {
1331
+ type: '&',
1332
+ '!lt': [['prop', null, '3 weeks ago']],
1333
+ },
1334
+ ]);
1335
+ });
1055
1336
  it('parses simple lte clauses', () => {
1056
1337
  const query = 'prop<=30.5';
1057
1338
  const [options, ...selectors] = queryParser({
@@ -1068,6 +1349,22 @@ describe('queryParser', () => {
1068
1349
  },
1069
1350
  ]);
1070
1351
  });
1352
+ it('parses simple not lte clauses', () => {
1353
+ const query = '-prop<=30.5';
1354
+ const [options, ...selectors] = queryParser({
1355
+ query,
1356
+ entityClass: BlogPost,
1357
+ });
1358
+ expect(options).toEqual({
1359
+ class: BlogPost,
1360
+ });
1361
+ expect(selectors).toEqual([
1362
+ {
1363
+ type: '&',
1364
+ '!lte': [['prop', 30.5]],
1365
+ },
1366
+ ]);
1367
+ });
1071
1368
  it('parses simple relative lte clauses', () => {
1072
1369
  const query = 'prop<=yesterday';
1073
1370
  const [options, ...selectors] = queryParser({
@@ -1084,6 +1381,22 @@ describe('queryParser', () => {
1084
1381
  },
1085
1382
  ]);
1086
1383
  });
1384
+ it('parses simple relative not lte clauses', () => {
1385
+ const query = '-prop<=yesterday';
1386
+ const [options, ...selectors] = queryParser({
1387
+ query,
1388
+ entityClass: BlogPost,
1389
+ });
1390
+ expect(options).toEqual({
1391
+ class: BlogPost,
1392
+ });
1393
+ expect(selectors).toEqual([
1394
+ {
1395
+ type: '&',
1396
+ '!lte': [['prop', null, 'yesterday']],
1397
+ },
1398
+ ]);
1399
+ });
1087
1400
  it('parses quote relative lte clauses', () => {
1088
1401
  const query = 'prop<="3 weeks ago"';
1089
1402
  const [options, ...selectors] = queryParser({
@@ -1100,8 +1413,24 @@ describe('queryParser', () => {
1100
1413
  },
1101
1414
  ]);
1102
1415
  });
1416
+ it('parses quote relative not lte clauses', () => {
1417
+ const query = '-prop<="3 weeks ago"';
1418
+ const [options, ...selectors] = queryParser({
1419
+ query,
1420
+ entityClass: BlogPost,
1421
+ });
1422
+ expect(options).toEqual({
1423
+ class: BlogPost,
1424
+ });
1425
+ expect(selectors).toEqual([
1426
+ {
1427
+ type: '&',
1428
+ '!lte': [['prop', null, '3 weeks ago']],
1429
+ },
1430
+ ]);
1431
+ });
1103
1432
  it('parses complex selectors', () => {
1104
- const query = '(! [published] <draft>) (| cdate>"6 months ago" mdate>"1 month ago")';
1433
+ const query = '(! [published] <draft>) (| -cdate>"6 months ago" mdate>"1 month ago")';
1105
1434
  const [options, ...selectors] = queryParser({
1106
1435
  query,
1107
1436
  entityClass: BlogPost,
@@ -1115,10 +1444,8 @@ describe('queryParser', () => {
1115
1444
  selector: [
1116
1445
  {
1117
1446
  type: '|',
1118
- gt: [
1119
- ['cdate', null, '6 months ago'],
1120
- ['mdate', null, '1 month ago'],
1121
- ],
1447
+ '!gt': [['cdate', null, '6 months ago']],
1448
+ gt: [['mdate', null, '1 month ago']],
1122
1449
  },
1123
1450
  {
1124
1451
  type: '!&',
@@ -1130,7 +1457,7 @@ describe('queryParser', () => {
1130
1457
  ]);
1131
1458
  });
1132
1459
  it('parses nested selectors', () => {
1133
- const query = '(! [published] <draft> (| cdate>"6 months ago" mdate>"1 month ago"))';
1460
+ const query = '(! [published] <draft> (| -cdate>"6 months ago" mdate>"1 month ago"))';
1134
1461
  const [options, ...selectors] = queryParser({
1135
1462
  query,
1136
1463
  entityClass: BlogPost,
@@ -1146,17 +1473,15 @@ describe('queryParser', () => {
1146
1473
  selector: [
1147
1474
  {
1148
1475
  type: '|',
1149
- gt: [
1150
- ['cdate', null, '6 months ago'],
1151
- ['mdate', null, '1 month ago'],
1152
- ],
1476
+ '!gt': [['cdate', null, '6 months ago']],
1477
+ gt: [['mdate', null, '1 month ago']],
1153
1478
  },
1154
1479
  ],
1155
1480
  },
1156
1481
  ]);
1157
1482
  });
1158
1483
  it('parses nested qref clauses', () => {
1159
- const query = 'categories<{Category parent<{Category id="sort+newsletters"}>}> ';
1484
+ const query = 'categories<{Category parent<{Category list<{List frosting name(favorite)}>}> -parent<{Category id="sort+newsletters"}>}>';
1160
1485
  const [options, ...selectors] = queryParser({
1161
1486
  query,
1162
1487
  entityClass: BlogPost,
@@ -1166,6 +1491,10 @@ describe('queryParser', () => {
1166
1491
  class: Category,
1167
1492
  defaultFields: ['name'],
1168
1493
  },
1494
+ List: {
1495
+ class: List,
1496
+ defaultFields: ['name'],
1497
+ },
1169
1498
  },
1170
1499
  });
1171
1500
  expect(options).toEqual({
@@ -1182,6 +1511,33 @@ describe('queryParser', () => {
1182
1511
  {
1183
1512
  type: '&',
1184
1513
  qref: [
1514
+ [
1515
+ 'parent',
1516
+ [
1517
+ { class: Category },
1518
+ {
1519
+ type: '&',
1520
+ qref: [
1521
+ [
1522
+ 'list',
1523
+ [
1524
+ { class: List },
1525
+ {
1526
+ type: '&',
1527
+ search: [['name', 'favorite']],
1528
+ },
1529
+ {
1530
+ type: '|',
1531
+ ilike: [['name', '%frosting%']],
1532
+ },
1533
+ ],
1534
+ ],
1535
+ ],
1536
+ },
1537
+ ],
1538
+ ],
1539
+ ],
1540
+ '!qref': [
1185
1541
  [
1186
1542
  'parent',
1187
1543
  [
@@ -1200,5 +1556,22 @@ describe('queryParser', () => {
1200
1556
  },
1201
1557
  ]);
1202
1558
  });
1559
+ it('handles badly nested qref', () => {
1560
+ const query = 'categories<{Category parent<{Category id="top"}>';
1561
+ const [options, ...selectors] = queryParser({
1562
+ query,
1563
+ entityClass: BlogPost,
1564
+ });
1565
+ expect(options).toEqual({
1566
+ class: BlogPost,
1567
+ });
1568
+ expect(selectors).toEqual([
1569
+ {
1570
+ type: '&',
1571
+ contain: [['categories', '{Category parent<{Category}']],
1572
+ equal: [['id', 'top']],
1573
+ },
1574
+ ]);
1575
+ });
1203
1576
  });
1204
1577
  //# sourceMappingURL=queryParser.test.js.map