@tellaro/tql 3.2.0 → 4.0.0
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/dist/index.js +86 -79
- package/dist/src/chart/index.d.ts +1 -1
- package/dist/src/chart/index.js +39 -26
- package/dist/src/chart/mappers.d.ts +861 -69
- package/dist/src/chart/mappers.js +502 -201
- package/dist/src/completionProvider.d.ts +11 -0
- package/dist/src/completionProvider.js +99 -59
- package/dist/src/index.d.ts +3 -3
- package/dist/src/parser.d.ts +36 -0
- package/dist/src/parser.js +263 -151
- package/dist/src/tokenizer.d.ts +20 -0
- package/dist/src/tokenizer.js +123 -104
- package/dist/src/types.d.ts +114 -3
- package/dist/tql.css +1 -1
- package/dist/vue/TqlChart.vue.js +1 -1
- package/dist/vue/TqlChart.vue2.js +148 -130
- package/package.json +1 -1
package/dist/src/parser.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { tokenizeTql as
|
|
2
|
-
const
|
|
1
|
+
import { tokenizeTql as b, TYPE_HINTS as g, BOOLEAN_PREDICATE_MUTATOR_SET as L, COMPARISON_OPERATORS as E, NEGATION_SYMBOL_OPERATORS as m, MUTATORS as S, SYMBOL_LOGICAL_OPERATORS as y, DATE_HISTOGRAM_CALENDAR_INTERVALS as A, GEO_GRID_PRECISION as O } from "./tokenizer.js";
|
|
2
|
+
const C = 50, w = /* @__PURE__ */ new Set(["logicalOp", "paren"]), v = /* @__PURE__ */ new Map([
|
|
3
3
|
["any", { op: "ANY", negated: !1 }],
|
|
4
4
|
["all", { op: "ALL", negated: !1 }],
|
|
5
5
|
["none", { op: "NONE", negated: !1 }],
|
|
@@ -10,13 +10,13 @@ const L = 50, O = /* @__PURE__ */ new Set(["logicalOp", "paren"]), v = /* @__PUR
|
|
|
10
10
|
["!all", { op: "ALL", negated: !0 }],
|
|
11
11
|
["!none", { op: "NONE", negated: !0 }]
|
|
12
12
|
]);
|
|
13
|
-
function
|
|
13
|
+
function G(a, e) {
|
|
14
14
|
for (const [t, s] of v)
|
|
15
|
-
if (s.op ===
|
|
15
|
+
if (s.op === a && s.negated === e && /^[a-z_]+$/.test(t))
|
|
16
16
|
return t;
|
|
17
|
-
throw new Error(`no word spelling for collection operator ${
|
|
17
|
+
throw new Error(`no word spelling for collection operator ${a} (negated=${e})`);
|
|
18
18
|
}
|
|
19
|
-
const
|
|
19
|
+
const T = "Collection syntax accepted by the Rust engine only; the Python query API rejects it", $ = "Aggregation modifier on '*' accepted by the Rust engine only; the Python query API rejects it", N = /* @__PURE__ */ new Set([
|
|
20
20
|
"field",
|
|
21
21
|
"mutator",
|
|
22
22
|
"typeHint",
|
|
@@ -24,8 +24,60 @@ const y = "Collection syntax accepted by the Rust engine only; the Python query
|
|
|
24
24
|
"keyword",
|
|
25
25
|
"visualization",
|
|
26
26
|
"collectionOp"
|
|
27
|
-
]);
|
|
28
|
-
|
|
27
|
+
]), x = 1e9, _ = 1e4;
|
|
28
|
+
function M(a) {
|
|
29
|
+
if (a === null || /^auto$/i.test(a)) return null;
|
|
30
|
+
const e = /^auto\s*\(/i.test(a) && a.endsWith(")") ? a.slice(a.indexOf("(") + 1, -1) : null;
|
|
31
|
+
if (e !== null) {
|
|
32
|
+
const s = e.trim(), r = Number(s);
|
|
33
|
+
return /^[0-9]+$/.test(s) && String(r) === s && r >= 1 && r <= _ ? null : {
|
|
34
|
+
message: `Invalid date_histogram auto bucket target '${s}': it must be a whole number from 1 to ${_}`,
|
|
35
|
+
suggestion: "Write auto(50), or omit the interval for the default of 50 buckets"
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
if (A.includes(a.toLowerCase())) return null;
|
|
39
|
+
const t = /^([0-9]+)(ms|s|m|h|d)$/.exec(a);
|
|
40
|
+
if (t) {
|
|
41
|
+
const s = Number(t[1]);
|
|
42
|
+
if (s >= 1 && s <= x) return null;
|
|
43
|
+
}
|
|
44
|
+
return /^-?[A-Za-z0-9_.]+$/.test(a) ? {
|
|
45
|
+
message: `Invalid date_histogram interval '${a}'`,
|
|
46
|
+
suggestion: `A fixed interval is a positive whole number and a unit: ms, s, m, h or d (e.g. 30s, 5m, 1h, 1d); a calendar interval is one of: ${A.join(", ")}; or auto / auto(N)`
|
|
47
|
+
} : null;
|
|
48
|
+
}
|
|
49
|
+
function R(a, e) {
|
|
50
|
+
if (e === null) return null;
|
|
51
|
+
const [t, s, r] = O[a];
|
|
52
|
+
return /^[0-9]+$/.test(e) && Number(e) >= t && Number(e) <= s || !/^-?[0-9.]+$/.test(e) ? null : {
|
|
53
|
+
message: `Invalid ${a} precision '${e}': it must be a whole number from ${t} to ${s}`,
|
|
54
|
+
suggestion: `Omit the precision for OpenSearch's default of ${r}`
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
const P = /^[-+]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][-+]?[0-9]+)?$/;
|
|
58
|
+
function D(a) {
|
|
59
|
+
let e = null;
|
|
60
|
+
for (let t = 0; t < a.length; t++) {
|
|
61
|
+
const s = a[t];
|
|
62
|
+
if (!/^[-+]?[0-9.][0-9.eE+-]*$/.test(s)) return null;
|
|
63
|
+
if (!P.test(s) || !Number.isFinite(Number(s)))
|
|
64
|
+
return {
|
|
65
|
+
message: `Invalid range() boundary '${s}': it must be a number`,
|
|
66
|
+
suggestion: "Write range(field, 0, 1024, 65536): the field, then increasing numeric boundaries",
|
|
67
|
+
index: t
|
|
68
|
+
};
|
|
69
|
+
const r = s.replace(/^\+/, ""), i = Number(s);
|
|
70
|
+
if (e && !(i > e.value))
|
|
71
|
+
return {
|
|
72
|
+
message: `range() boundaries must be strictly increasing: '${r}' follows '${e.text}'`,
|
|
73
|
+
suggestion: "List each boundary once, smallest first",
|
|
74
|
+
index: t
|
|
75
|
+
};
|
|
76
|
+
e = { value: i, text: r };
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
class V {
|
|
29
81
|
tokens;
|
|
30
82
|
allTokens;
|
|
31
83
|
// Including whitespace for highlighting
|
|
@@ -33,8 +85,9 @@ class I {
|
|
|
33
85
|
errors;
|
|
34
86
|
warnings;
|
|
35
87
|
depth;
|
|
88
|
+
input;
|
|
36
89
|
constructor(e) {
|
|
37
|
-
this.allTokens =
|
|
90
|
+
this.input = e, this.allTokens = b(e), this.tokens = this.allTokens.filter((t) => t.type !== "whitespace"), this.current = 0, this.errors = [], this.warnings = [], this.depth = 0;
|
|
38
91
|
}
|
|
39
92
|
/**
|
|
40
93
|
* Parse the input and return the result
|
|
@@ -49,7 +102,7 @@ class I {
|
|
|
49
102
|
success: !0
|
|
50
103
|
};
|
|
51
104
|
if (this.isStatsQuery())
|
|
52
|
-
return this.checkStatsModifiers(), {
|
|
105
|
+
return this.checkStatsModifiers(), this.checkStatsBucketFunctions(), {
|
|
53
106
|
ast: null,
|
|
54
107
|
// Stats AST is produced by the Python/Rust parser
|
|
55
108
|
tokens: this.allTokens,
|
|
@@ -92,7 +145,7 @@ class I {
|
|
|
92
145
|
* Parse a full expression (lowest precedence)
|
|
93
146
|
*/
|
|
94
147
|
parseExpression() {
|
|
95
|
-
if (this.depth++, this.depth >
|
|
148
|
+
if (this.depth++, this.depth > C)
|
|
96
149
|
throw new Error("Maximum query depth exceeded");
|
|
97
150
|
try {
|
|
98
151
|
return this.parseOr();
|
|
@@ -227,35 +280,35 @@ class I {
|
|
|
227
280
|
let t = this.createFieldReference(e);
|
|
228
281
|
const s = this.readTypeHintSlot(t);
|
|
229
282
|
t = s.field;
|
|
230
|
-
const
|
|
283
|
+
const r = [], i = [];
|
|
231
284
|
for (; this.check("pipe"); )
|
|
232
285
|
if (this.advance(), this.check("mutator") || this.isCatalogMutatorToken()) {
|
|
233
|
-
const
|
|
234
|
-
|
|
286
|
+
const n = this.advance();
|
|
287
|
+
r.push(n.value.toLowerCase()), i.push(this.parseMutatorCall(n));
|
|
235
288
|
} else if (this.check("field")) {
|
|
236
|
-
const
|
|
237
|
-
|
|
289
|
+
const n = this.advance();
|
|
290
|
+
r.push(n.value.toLowerCase()), this.addWarning(`Unknown mutator '${n.value}'`, n, "Check the mutator name spelling"), i.push(this.parseMutatorCall(n));
|
|
238
291
|
} else {
|
|
239
|
-
const
|
|
292
|
+
const n = this.isAtEnd() ? this.previous() : this.peek();
|
|
240
293
|
this.addError(
|
|
241
294
|
"Expected mutator name after '|'",
|
|
242
|
-
|
|
295
|
+
n,
|
|
243
296
|
"MISSING_MUTATOR",
|
|
244
297
|
"Add a mutator like 'lowercase', 'uppercase', 'trim', etc."
|
|
245
298
|
);
|
|
246
299
|
break;
|
|
247
300
|
}
|
|
248
|
-
if (
|
|
301
|
+
if (r.length > 0 && (t = {
|
|
249
302
|
...t,
|
|
250
|
-
mutators:
|
|
303
|
+
mutators: r,
|
|
251
304
|
mutatorCalls: i,
|
|
252
305
|
end: this.previous().end
|
|
253
306
|
}), this.check("doubleColon"))
|
|
254
307
|
if (s.filled) {
|
|
255
|
-
const
|
|
308
|
+
const n = this.advance();
|
|
256
309
|
this.addError(
|
|
257
310
|
"A field may carry only one type hint",
|
|
258
|
-
|
|
311
|
+
n,
|
|
259
312
|
"DUPLICATE_TYPE_HINT",
|
|
260
313
|
`Write the hint once — either '${e.value}::${t.typeHint ?? "type"} | mutator' or '${e.value} | mutator::type'`
|
|
261
314
|
), (this.check("field") || this.check("typeHint") || this.check("mutator")) && this.advance();
|
|
@@ -281,7 +334,7 @@ class I {
|
|
|
281
334
|
if (!this.check("doubleColon")) return { field: e, filled: !1 };
|
|
282
335
|
this.advance();
|
|
283
336
|
const t = this.isAtEnd() ? void 0 : this.peek();
|
|
284
|
-
if (t !== void 0 &&
|
|
337
|
+
if (t !== void 0 && g.includes(t.value.toLowerCase())) {
|
|
285
338
|
const s = this.advance();
|
|
286
339
|
return {
|
|
287
340
|
field: { ...e, typeHint: s.value.toLowerCase(), end: s.end },
|
|
@@ -300,13 +353,13 @@ class I {
|
|
|
300
353
|
this.previous(),
|
|
301
354
|
// Derived from the catalog rather than a hand-written excerpt, which had drifted
|
|
302
355
|
// to six of the fifteen names and omitted the very one being rejected.
|
|
303
|
-
`Valid types: ${
|
|
356
|
+
`Valid types: ${g.join(", ")}`
|
|
304
357
|
), { field: e, filled: !1 }) : (this.addError(
|
|
305
358
|
"Expected type hint after '::'",
|
|
306
359
|
this.peek(),
|
|
307
360
|
"MISSING_TYPE_HINT",
|
|
308
361
|
// Catalog-derived for the same reason as the warning arm, so the two cannot drift.
|
|
309
|
-
`Name a type (${
|
|
362
|
+
`Name a type (${g.join(", ")}), or remove the '::'`
|
|
310
363
|
), { field: e, filled: !1 });
|
|
311
364
|
}
|
|
312
365
|
/**
|
|
@@ -324,15 +377,15 @@ class I {
|
|
|
324
377
|
if (t === void 0)
|
|
325
378
|
return this.createPlaceholderExpression(e);
|
|
326
379
|
if (this.addWarning(
|
|
327
|
-
|
|
380
|
+
T,
|
|
328
381
|
e,
|
|
329
382
|
`Write it field-first instead: field ${e.value.toLowerCase()} value`,
|
|
330
383
|
"ENGINE_DIVERGENCE"
|
|
331
384
|
), !(this.check("field") || this.check("aggregation") || this.check("mutator") || this.check("typeHint"))) {
|
|
332
|
-
const
|
|
385
|
+
const r = this.isAtEnd() ? this.previous() : this.peek();
|
|
333
386
|
return this.addError(
|
|
334
387
|
`Expected a field name after '${e.value}'`,
|
|
335
|
-
|
|
388
|
+
r,
|
|
336
389
|
"MISSING_FIELD",
|
|
337
390
|
`Use: ${e.value} field eq "value"`
|
|
338
391
|
), this.createPlaceholderExpression(e);
|
|
@@ -350,14 +403,14 @@ class I {
|
|
|
350
403
|
* warning rather than being silently accepted or silently refused.
|
|
351
404
|
*/
|
|
352
405
|
tryParseCollectionTail(e) {
|
|
353
|
-
const t = this.check("logicalOp") && this.logicalWord(this.peek()) === "NOT", s = t ? 1 : 0,
|
|
354
|
-
if (
|
|
355
|
-
const i = v.get(
|
|
406
|
+
const t = this.check("logicalOp") && this.logicalWord(this.peek()) === "NOT", s = t ? 1 : 0, r = this.peekAt(s);
|
|
407
|
+
if (r === void 0 || r.type !== "collectionOp" && r.type !== "operator") return null;
|
|
408
|
+
const i = v.get(r.value.toLowerCase());
|
|
356
409
|
if (i === void 0 || t && i.negated) return null;
|
|
357
410
|
t && this.advance();
|
|
358
|
-
const
|
|
411
|
+
const n = this.advance();
|
|
359
412
|
return this.finishCollection(
|
|
360
|
-
|
|
413
|
+
n,
|
|
361
414
|
i.op,
|
|
362
415
|
i.negated || t,
|
|
363
416
|
e,
|
|
@@ -368,33 +421,33 @@ class I {
|
|
|
368
421
|
* The shared tail of both collection forms: an OPTIONAL inner comparison operator
|
|
369
422
|
* (defaulting to `eq`, as both grammars do) followed by a value or a value list.
|
|
370
423
|
*/
|
|
371
|
-
finishCollection(e, t, s,
|
|
372
|
-
let
|
|
424
|
+
finishCollection(e, t, s, r, i) {
|
|
425
|
+
let n = "eq";
|
|
373
426
|
if (this.check("operator")) {
|
|
374
|
-
const
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
`Drop the '${
|
|
427
|
+
const l = this.advance();
|
|
428
|
+
n = this.normalizeOperator(l.value), this.addWarning(
|
|
429
|
+
T,
|
|
430
|
+
l,
|
|
431
|
+
`Drop the '${l.value}': the Python parser accepts only field ${e.value.toLowerCase()} value`,
|
|
379
432
|
"ENGINE_DIVERGENCE"
|
|
380
433
|
);
|
|
381
434
|
}
|
|
382
|
-
const
|
|
435
|
+
const h = this.check("bracket") && this.peek().value === "[" ? this.parseValueList() : this.parseValue(), o = this.previous().end;
|
|
383
436
|
return {
|
|
384
437
|
type: "collection",
|
|
385
438
|
operator: t,
|
|
386
439
|
negated: s,
|
|
387
|
-
field:
|
|
440
|
+
field: r,
|
|
388
441
|
condition: {
|
|
389
442
|
type: "comparison",
|
|
390
|
-
field:
|
|
391
|
-
operator:
|
|
392
|
-
value:
|
|
393
|
-
start:
|
|
394
|
-
end:
|
|
443
|
+
field: r,
|
|
444
|
+
operator: n,
|
|
445
|
+
value: h,
|
|
446
|
+
start: r.start,
|
|
447
|
+
end: o
|
|
395
448
|
},
|
|
396
449
|
start: i,
|
|
397
|
-
end:
|
|
450
|
+
end: o
|
|
398
451
|
};
|
|
399
452
|
}
|
|
400
453
|
/**
|
|
@@ -404,10 +457,10 @@ class I {
|
|
|
404
457
|
* user typed rather than the normalised reference.
|
|
405
458
|
*/
|
|
406
459
|
parseComparisonTail(e, t) {
|
|
407
|
-
const s = t.mutators ?? [],
|
|
460
|
+
const s = t.mutators ?? [], r = this.consumeNegationPrefix();
|
|
408
461
|
if (!this.check("operator")) {
|
|
409
|
-
const
|
|
410
|
-
if (
|
|
462
|
+
const o = s.length > 0 ? s[s.length - 1] : null;
|
|
463
|
+
if (r === null && o !== null && L.has(o))
|
|
411
464
|
return {
|
|
412
465
|
type: "comparison",
|
|
413
466
|
field: t,
|
|
@@ -422,10 +475,10 @@ class I {
|
|
|
422
475
|
start: t.start,
|
|
423
476
|
end: t.end
|
|
424
477
|
};
|
|
425
|
-
const
|
|
478
|
+
const l = this.isAtEnd() ? this.previous() : this.peek(), u = s.length > 0 ? `'${e.value}' with mutator` : `'${e.value}'`;
|
|
426
479
|
return this.addError(
|
|
427
|
-
`Expected operator after field ${
|
|
428
|
-
|
|
480
|
+
`Expected operator after field ${u}`,
|
|
481
|
+
l,
|
|
429
482
|
"MISSING_OPERATOR",
|
|
430
483
|
"Add an operator like 'eq', 'contains', '=', etc."
|
|
431
484
|
), {
|
|
@@ -450,21 +503,21 @@ class I {
|
|
|
450
503
|
"INVALID_OPERATOR",
|
|
451
504
|
"Use '=' or 'eq' for equality"
|
|
452
505
|
);
|
|
453
|
-
let
|
|
454
|
-
if (
|
|
455
|
-
const
|
|
456
|
-
|
|
457
|
-
`'${
|
|
458
|
-
|
|
506
|
+
let n = this.normalizeOperator(i.value);
|
|
507
|
+
if (r !== null) {
|
|
508
|
+
const o = this.negateOperator(n);
|
|
509
|
+
o === null ? this.addError(
|
|
510
|
+
`'${r.value} ${i.value}' is not a TQL operator`,
|
|
511
|
+
r,
|
|
459
512
|
"INVALID_OPERATOR",
|
|
460
|
-
`'${
|
|
461
|
-
) :
|
|
513
|
+
`'${n}' has no negated form; use 'ne' or negate the whole expression: not (field ${n} value)`
|
|
514
|
+
) : n = o;
|
|
462
515
|
}
|
|
463
|
-
if (
|
|
516
|
+
if (n === "is" && this.check("logicalOp") && this.logicalWord(this.peek()) === "NOT" && (this.advance(), n = "is_not"), n === "exists" || n === "not_exists")
|
|
464
517
|
return {
|
|
465
518
|
type: "comparison",
|
|
466
519
|
field: t,
|
|
467
|
-
operator:
|
|
520
|
+
operator: n,
|
|
468
521
|
value: {
|
|
469
522
|
type: "literal",
|
|
470
523
|
valueType: "boolean",
|
|
@@ -475,78 +528,78 @@ class I {
|
|
|
475
528
|
start: t.start,
|
|
476
529
|
end: i.end
|
|
477
530
|
};
|
|
478
|
-
if ((
|
|
479
|
-
const
|
|
531
|
+
if ((n === "is" || n === "is_not") && this.check("null")) {
|
|
532
|
+
const o = this.advance();
|
|
480
533
|
return {
|
|
481
534
|
type: "comparison",
|
|
482
535
|
field: t,
|
|
483
|
-
operator:
|
|
536
|
+
operator: n,
|
|
484
537
|
value: {
|
|
485
538
|
type: "literal",
|
|
486
539
|
valueType: "null",
|
|
487
540
|
value: null,
|
|
488
|
-
start:
|
|
489
|
-
end:
|
|
541
|
+
start: o.start,
|
|
542
|
+
end: o.end
|
|
490
543
|
},
|
|
491
544
|
start: t.start,
|
|
492
|
-
end:
|
|
545
|
+
end: o.end
|
|
493
546
|
};
|
|
494
547
|
}
|
|
495
|
-
if (/^(not_)?in(_cs)?$/.test(
|
|
496
|
-
const
|
|
548
|
+
if (/^(not_)?in(_cs)?$/.test(n)) {
|
|
549
|
+
const o = this.parseValueList();
|
|
497
550
|
return {
|
|
498
551
|
type: "comparison",
|
|
499
552
|
field: t,
|
|
500
|
-
operator:
|
|
501
|
-
value:
|
|
553
|
+
operator: n,
|
|
554
|
+
value: o,
|
|
502
555
|
start: t.start,
|
|
503
556
|
end: this.previous().end
|
|
504
557
|
};
|
|
505
558
|
}
|
|
506
|
-
if (
|
|
559
|
+
if (n === "between" || n === "not_between") {
|
|
507
560
|
if (this.check("bracket") && this.peek().value === "[") {
|
|
508
|
-
const
|
|
561
|
+
const u = this.peek(), c = this.parseValueList();
|
|
509
562
|
return c.length !== 2 && this.addError(
|
|
510
|
-
`'${
|
|
511
|
-
|
|
563
|
+
`'${n}' needs exactly 2 values, got ${c.length}`,
|
|
564
|
+
u,
|
|
512
565
|
"INVALID_VALUE",
|
|
513
|
-
`Use: field ${
|
|
566
|
+
`Use: field ${n} [low, high] or field ${n} low AND high`
|
|
514
567
|
), {
|
|
515
568
|
type: "comparison",
|
|
516
569
|
field: t,
|
|
517
|
-
operator:
|
|
570
|
+
operator: n,
|
|
518
571
|
value: c,
|
|
519
572
|
start: t.start,
|
|
520
573
|
end: this.previous().end
|
|
521
574
|
};
|
|
522
575
|
}
|
|
523
|
-
const
|
|
576
|
+
const o = this.parseValue();
|
|
524
577
|
this.matchLogicalWord("AND") || this.addError(
|
|
525
578
|
"Expected 'AND' between values in 'between' operator",
|
|
526
579
|
this.isAtEnd() ? this.previous() : this.peek(),
|
|
527
580
|
"INVALID_SYNTAX",
|
|
528
581
|
"Use format: field between value1 AND value2"
|
|
529
582
|
);
|
|
530
|
-
const
|
|
583
|
+
const l = this.parseValue();
|
|
531
584
|
return {
|
|
532
585
|
type: "comparison",
|
|
533
586
|
field: t,
|
|
534
|
-
operator:
|
|
535
|
-
value: [
|
|
587
|
+
operator: n,
|
|
588
|
+
value: [o, l],
|
|
536
589
|
start: t.start,
|
|
537
|
-
end:
|
|
590
|
+
end: l.end
|
|
538
591
|
};
|
|
539
592
|
}
|
|
540
593
|
if (this.isAtEnd())
|
|
541
594
|
return this.addError(
|
|
542
|
-
`Expected a value after operator '${
|
|
595
|
+
`Expected a value after operator '${n}'`,
|
|
543
596
|
i,
|
|
544
597
|
"MISSING_VALUE",
|
|
545
598
|
'Add a value like "text", 123, or true'
|
|
546
599
|
), {
|
|
547
600
|
type: "comparison",
|
|
548
601
|
field: t,
|
|
549
|
-
operator:
|
|
602
|
+
operator: n,
|
|
550
603
|
value: {
|
|
551
604
|
type: "literal",
|
|
552
605
|
valueType: "string",
|
|
@@ -557,14 +610,14 @@ class I {
|
|
|
557
610
|
start: t.start,
|
|
558
611
|
end: i.end
|
|
559
612
|
};
|
|
560
|
-
const
|
|
613
|
+
const h = this.parseValue();
|
|
561
614
|
return {
|
|
562
615
|
type: "comparison",
|
|
563
616
|
field: t,
|
|
564
|
-
operator:
|
|
565
|
-
value:
|
|
617
|
+
operator: n,
|
|
618
|
+
value: h,
|
|
566
619
|
start: t.start,
|
|
567
|
-
end:
|
|
620
|
+
end: h.end
|
|
568
621
|
};
|
|
569
622
|
}
|
|
570
623
|
/**
|
|
@@ -680,9 +733,9 @@ class I {
|
|
|
680
733
|
return { name: t, args: s, parenthesized: !1, start: e.start, end: e.end };
|
|
681
734
|
if (this.advance(), !(this.check("paren") && this.peek().value === ")"))
|
|
682
735
|
for (; ; ) {
|
|
683
|
-
const
|
|
684
|
-
if (
|
|
685
|
-
if (s.push(
|
|
736
|
+
const r = this.parseMutatorArgument(t);
|
|
737
|
+
if (r === null) break;
|
|
738
|
+
if (s.push(r), this.check("comma")) {
|
|
686
739
|
this.advance();
|
|
687
740
|
continue;
|
|
688
741
|
}
|
|
@@ -703,11 +756,11 @@ class I {
|
|
|
703
756
|
*/
|
|
704
757
|
parseMutatorArgument(e) {
|
|
705
758
|
const t = this.isAtEnd() ? this.previous() : this.peek();
|
|
706
|
-
if (
|
|
707
|
-
const
|
|
759
|
+
if (N.has(t.type) && this.peekAt(1)?.type === "operator" && this.peekAt(1)?.value === "=") {
|
|
760
|
+
const r = this.advance();
|
|
708
761
|
this.advance();
|
|
709
762
|
const i = this.parseMutatorArgumentValue(e);
|
|
710
|
-
return i === null ? null : { name:
|
|
763
|
+
return i === null ? null : { name: r.value, ...i, start: r.start, end: i.end };
|
|
711
764
|
}
|
|
712
765
|
const s = this.parseMutatorArgumentValue(e);
|
|
713
766
|
return s === null ? null : s;
|
|
@@ -736,7 +789,7 @@ class I {
|
|
|
736
789
|
const s = this.advance();
|
|
737
790
|
return { valueType: "null", value: null, start: s.start, end: s.end };
|
|
738
791
|
}
|
|
739
|
-
if (
|
|
792
|
+
if (N.has(t.type) && !this.isAtEnd()) {
|
|
740
793
|
const s = this.advance();
|
|
741
794
|
return { valueType: "identifier", value: s.value, start: s.start, end: s.end };
|
|
742
795
|
}
|
|
@@ -806,7 +859,7 @@ class I {
|
|
|
806
859
|
*/
|
|
807
860
|
negateOperator(e) {
|
|
808
861
|
const t = `not_${e}`;
|
|
809
|
-
return
|
|
862
|
+
return E.includes(t) ? t : null;
|
|
810
863
|
}
|
|
811
864
|
normalizeOperator(e) {
|
|
812
865
|
const t = e.toLowerCase(), s = {
|
|
@@ -819,14 +872,14 @@ class I {
|
|
|
819
872
|
};
|
|
820
873
|
if (e in s)
|
|
821
874
|
return s[e];
|
|
822
|
-
if (t in
|
|
823
|
-
return
|
|
824
|
-
const
|
|
875
|
+
if (t in m)
|
|
876
|
+
return m[t];
|
|
877
|
+
const r = {
|
|
825
878
|
regex: "regexp",
|
|
826
879
|
starts_with: "startswith",
|
|
827
880
|
ends_with: "endswith"
|
|
828
881
|
};
|
|
829
|
-
return t in
|
|
882
|
+
return t in r ? r[t] : (E.includes(t), t);
|
|
830
883
|
}
|
|
831
884
|
isAtEnd() {
|
|
832
885
|
return this.current >= this.tokens.length;
|
|
@@ -858,7 +911,7 @@ class I {
|
|
|
858
911
|
isCatalogMutatorToken() {
|
|
859
912
|
if (this.isAtEnd()) return !1;
|
|
860
913
|
const e = this.peek();
|
|
861
|
-
return e.type !== "collectionOp" && e.type !== "aggregation" ? !1 :
|
|
914
|
+
return e.type !== "collectionOp" && e.type !== "aggregation" ? !1 : S.includes(e.value.toLowerCase());
|
|
862
915
|
}
|
|
863
916
|
match(...e) {
|
|
864
917
|
for (const t of e)
|
|
@@ -878,7 +931,7 @@ class I {
|
|
|
878
931
|
* source text, which the highlighting and completion surfaces read.
|
|
879
932
|
*/
|
|
880
933
|
logicalWord(e) {
|
|
881
|
-
return
|
|
934
|
+
return y[e.value] ?? e.value.toUpperCase();
|
|
882
935
|
}
|
|
883
936
|
/**
|
|
884
937
|
* Match the WORD spelling of a logical operator only, never the symbol.
|
|
@@ -889,9 +942,9 @@ class I {
|
|
|
889
942
|
* would make this package the only one of the three that does.
|
|
890
943
|
*/
|
|
891
944
|
matchLogicalWord(e) {
|
|
892
|
-
return this.check("logicalOp") && this.peek().value.toUpperCase() === e && !(this.peek().value in
|
|
945
|
+
return this.check("logicalOp") && this.peek().value.toUpperCase() === e && !(this.peek().value in y) ? (this.advance(), !0) : !1;
|
|
893
946
|
}
|
|
894
|
-
addError(e, t, s,
|
|
947
|
+
addError(e, t, s, r) {
|
|
895
948
|
this.errors.push({
|
|
896
949
|
message: e,
|
|
897
950
|
start: t.start,
|
|
@@ -900,10 +953,10 @@ class I {
|
|
|
900
953
|
column: t.column,
|
|
901
954
|
severity: "error",
|
|
902
955
|
code: s,
|
|
903
|
-
suggestion:
|
|
956
|
+
suggestion: r
|
|
904
957
|
});
|
|
905
958
|
}
|
|
906
|
-
addWarning(e, t, s,
|
|
959
|
+
addWarning(e, t, s, r = "TYPE_MISMATCH") {
|
|
907
960
|
this.warnings.push({
|
|
908
961
|
message: e,
|
|
909
962
|
start: t.start,
|
|
@@ -911,7 +964,7 @@ class I {
|
|
|
911
964
|
line: t.line,
|
|
912
965
|
column: t.column,
|
|
913
966
|
severity: "warning",
|
|
914
|
-
code:
|
|
967
|
+
code: r,
|
|
915
968
|
suggestion: s
|
|
916
969
|
});
|
|
917
970
|
}
|
|
@@ -921,7 +974,7 @@ class I {
|
|
|
921
974
|
*/
|
|
922
975
|
synchronize() {
|
|
923
976
|
for (this.advance(); !this.isAtEnd(); ) {
|
|
924
|
-
if (
|
|
977
|
+
if (w.has(this.peek().type) || this.peek().type === "paren" && this.peek().value === ")")
|
|
925
978
|
return;
|
|
926
979
|
this.advance();
|
|
927
980
|
}
|
|
@@ -982,58 +1035,114 @@ class I {
|
|
|
982
1035
|
const e = (s) => s !== void 0 && s.type === "keyword" && (s.value.toLowerCase() === "top" || s.value.toLowerCase() === "bottom");
|
|
983
1036
|
let t = this.tokens.length;
|
|
984
1037
|
for (let s = 0; s < this.tokens.length; s++) {
|
|
985
|
-
const
|
|
986
|
-
if (
|
|
1038
|
+
const r = this.tokens[s];
|
|
1039
|
+
if (r.type === "keyword" && r.value.toLowerCase() === "by" || r.value === "=>") {
|
|
987
1040
|
t = s;
|
|
988
1041
|
break;
|
|
989
1042
|
}
|
|
990
1043
|
}
|
|
991
1044
|
for (let s = 0; s < t; s++) {
|
|
992
1045
|
if (this.tokens[s].type !== "aggregation") continue;
|
|
993
|
-
const
|
|
994
|
-
if (
|
|
995
|
-
let i = 0,
|
|
996
|
-
for (let
|
|
997
|
-
const
|
|
998
|
-
if (
|
|
999
|
-
else if (
|
|
1046
|
+
const r = s + 1;
|
|
1047
|
+
if (r >= t || this.tokens[r].value !== "(") continue;
|
|
1048
|
+
let i = 0, n = -1, h = -1, o;
|
|
1049
|
+
for (let d = r; d < t; d++) {
|
|
1050
|
+
const p = this.tokens[d];
|
|
1051
|
+
if (p.value === "(") i++;
|
|
1052
|
+
else if (p.value === ")") {
|
|
1000
1053
|
if (i--, i === 0) {
|
|
1001
|
-
|
|
1054
|
+
n = d;
|
|
1002
1055
|
break;
|
|
1003
1056
|
}
|
|
1004
|
-
} else i === 1 &&
|
|
1057
|
+
} else i === 1 && p.type === "comma" && (h === -1 && (h = d), e(this.tokens[d + 1]) && (o = this.tokens[d + 1]));
|
|
1005
1058
|
}
|
|
1006
|
-
if (
|
|
1007
|
-
const
|
|
1008
|
-
|
|
1059
|
+
if (n === -1) continue;
|
|
1060
|
+
const l = this.tokens.slice(r + 1, h === -1 ? n : h), u = l.length === 0 || l.length === 1 && l[0].value === "*", c = e(this.tokens[n + 1]) ? this.tokens[n + 1] : void 0;
|
|
1061
|
+
o !== void 0 && c !== void 0 && this.addError(
|
|
1009
1062
|
"The 'top'/'bottom' modifier is given twice on one aggregation",
|
|
1010
1063
|
c,
|
|
1011
1064
|
"INVALID_STATS_MODIFIER",
|
|
1012
1065
|
"Write it once — either 'sum(x, top 10)' or 'sum(x) top 10'"
|
|
1013
1066
|
);
|
|
1014
|
-
for (const
|
|
1015
|
-
if (
|
|
1016
|
-
const
|
|
1017
|
-
this.tokens[
|
|
1018
|
-
`Expected a count after '${
|
|
1019
|
-
|
|
1067
|
+
for (const d of [o, c]) {
|
|
1068
|
+
if (d === void 0) continue;
|
|
1069
|
+
const p = this.tokens.indexOf(d);
|
|
1070
|
+
this.tokens[p + 1]?.type !== "number" && this.addError(
|
|
1071
|
+
`Expected a count after '${d.value}'`,
|
|
1072
|
+
d,
|
|
1020
1073
|
"INVALID_STATS_MODIFIER",
|
|
1021
|
-
`Give the modifier a number, like '${
|
|
1074
|
+
`Give the modifier a number, like '${d.value.toLowerCase()} 10'`
|
|
1022
1075
|
);
|
|
1023
1076
|
}
|
|
1024
|
-
|
|
1077
|
+
o !== void 0 && u && this.addError(
|
|
1025
1078
|
"'*' cannot take an in-parens 'top'/'bottom' modifier",
|
|
1026
|
-
|
|
1079
|
+
o,
|
|
1027
1080
|
"INVALID_STATS_MODIFIER",
|
|
1028
1081
|
"Name a field, or move the modifier outside the parentheses"
|
|
1029
|
-
), c !== void 0 &&
|
|
1030
|
-
|
|
1082
|
+
), c !== void 0 && u && o === void 0 && this.addWarning(
|
|
1083
|
+
$,
|
|
1031
1084
|
c,
|
|
1032
1085
|
"Name a field instead of '*', which both engines accept",
|
|
1033
1086
|
"ENGINE_DIVERGENCE"
|
|
1034
1087
|
);
|
|
1035
1088
|
}
|
|
1036
1089
|
}
|
|
1090
|
+
/**
|
|
1091
|
+
* Flag group-by bucket functions whose ARGUMENTS both engines refuse at parse time.
|
|
1092
|
+
*
|
|
1093
|
+
* Like `checkStatsModifiers`, a targeted scan rather than a stats parser: it reads each
|
|
1094
|
+
* `bucketFunction` call's comma-separated arguments and applies the same argument rules as
|
|
1095
|
+
* `stats_buckets.parse_date_histogram_interval` (Python) / `parse_date_histogram_interval`
|
|
1096
|
+
* (Rust), with the same message text, so the editor reports what the engine will. Shapes the
|
|
1097
|
+
* grammars themselves refuse (a missing field, `top N` on a bucket level) are left to the
|
|
1098
|
+
* engines' ordinary syntax errors. `tests/statsBucketKinds.spec.ts` replays
|
|
1099
|
+
* `validation/stats_bucket_errors.json` against this.
|
|
1100
|
+
*/
|
|
1101
|
+
checkStatsBucketFunctions() {
|
|
1102
|
+
for (let e = 0; e < this.tokens.length; e++) {
|
|
1103
|
+
const t = this.tokens[e];
|
|
1104
|
+
if (t.type !== "bucketFunction" || this.tokens[e + 1]?.value !== "(") continue;
|
|
1105
|
+
const s = [[]];
|
|
1106
|
+
let r = 0, i = !1;
|
|
1107
|
+
for (let l = e + 1; l < this.tokens.length; l++) {
|
|
1108
|
+
const u = this.tokens[l];
|
|
1109
|
+
if (u.value === "(") {
|
|
1110
|
+
if (r++, r === 1) continue;
|
|
1111
|
+
} else if (u.value === ")") {
|
|
1112
|
+
if (r--, r === 0) {
|
|
1113
|
+
i = !0;
|
|
1114
|
+
break;
|
|
1115
|
+
}
|
|
1116
|
+
} else if (r === 1 && u.type === "comma") {
|
|
1117
|
+
s.push([]);
|
|
1118
|
+
continue;
|
|
1119
|
+
}
|
|
1120
|
+
s[s.length - 1].push(u);
|
|
1121
|
+
}
|
|
1122
|
+
if (!i) continue;
|
|
1123
|
+
const n = (l) => l && l.length > 0 ? this.input.slice(l[0].start, l[l.length - 1].end) : null, h = t.value.toLowerCase();
|
|
1124
|
+
let o = null;
|
|
1125
|
+
if (h === "date_histogram" ? o = M(n(s[1])) : (h === "geotile" || h === "geohash") && (o = R(h, n(s[1]))), o) {
|
|
1126
|
+
this.addError(o.message, s[1][0], "INVALID_STATS_BUCKET", o.suggestion);
|
|
1127
|
+
continue;
|
|
1128
|
+
}
|
|
1129
|
+
if (h === "range") {
|
|
1130
|
+
const l = s.slice(1).map((c) => ({ raw: n(c), at: c[0] })), u = D(l.map((c) => c.raw ?? ""));
|
|
1131
|
+
u && this.addError(u.message, l[u.index]?.at ?? t, "INVALID_STATS_BUCKET", u.suggestion);
|
|
1132
|
+
} else if (h === "filters") {
|
|
1133
|
+
const l = /* @__PURE__ */ new Set();
|
|
1134
|
+
s.forEach((u, c) => {
|
|
1135
|
+
const d = u.findIndex((k) => k.type === "keyword" && k.value.toLowerCase() === "as"), p = d >= 0 ? u[d + 1] : void 0, f = p ? String(p.value) : `filter_${c + 1}`;
|
|
1136
|
+
l.has(f) && this.addError(
|
|
1137
|
+
`Duplicate filters() label '${f}'`,
|
|
1138
|
+
p ?? u[0] ?? t,
|
|
1139
|
+
"INVALID_STATS_BUCKET",
|
|
1140
|
+
"Give every filter a distinct label with `as '<label>'`"
|
|
1141
|
+
), l.add(f);
|
|
1142
|
+
});
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1037
1146
|
/**
|
|
1038
1147
|
* Check if the token stream represents a stats query.
|
|
1039
1148
|
* Handles: "| stats ...", "stats ...", or starts with a filter before "| stats".
|
|
@@ -1058,24 +1167,27 @@ class I {
|
|
|
1058
1167
|
return t >= this.tokens.length ? !1 : this.tokens[t].type === "keyword" && this.tokens[t].value.toLowerCase() === "stats";
|
|
1059
1168
|
}
|
|
1060
1169
|
}
|
|
1061
|
-
function
|
|
1062
|
-
return
|
|
1170
|
+
function I(a) {
|
|
1171
|
+
return a ? new V(a).parse() : { ast: null, errors: [], warnings: [], tokens: [], success: !0 };
|
|
1063
1172
|
}
|
|
1064
|
-
function
|
|
1065
|
-
return
|
|
1173
|
+
function W(a) {
|
|
1174
|
+
return I(a).success;
|
|
1066
1175
|
}
|
|
1067
|
-
function
|
|
1068
|
-
return
|
|
1176
|
+
function F(a) {
|
|
1177
|
+
return I(a).errors;
|
|
1069
1178
|
}
|
|
1070
|
-
function
|
|
1071
|
-
let e = `Error at line ${
|
|
1072
|
-
return
|
|
1179
|
+
function q(a) {
|
|
1180
|
+
let e = `Error at line ${a.line}, column ${a.column}: ${a.message}`;
|
|
1181
|
+
return a.suggestion && (e += ` (${a.suggestion})`), e;
|
|
1073
1182
|
}
|
|
1074
1183
|
export {
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
M as
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1184
|
+
V as TqlParser,
|
|
1185
|
+
G as collectionOperatorSpelling,
|
|
1186
|
+
M as dateHistogramIntervalProblem,
|
|
1187
|
+
q as formatParseError,
|
|
1188
|
+
R as geoPrecisionProblem,
|
|
1189
|
+
F as getTqlErrors,
|
|
1190
|
+
W as isValidTql,
|
|
1191
|
+
I as parseTql,
|
|
1192
|
+
D as rangeBoundariesProblem
|
|
1081
1193
|
};
|