@tellaro/tql 3.3.0 → 4.0.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.
- 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/tokenizer.d.ts
CHANGED
|
@@ -161,6 +161,24 @@ export declare const TYPE_HINTS: TqlTypeHint[];
|
|
|
161
161
|
* enforcement in a place a reader of this file would not think to look.
|
|
162
162
|
*/
|
|
163
163
|
export declare const VISUALIZATION_TYPES: TqlVisualizationType[];
|
|
164
|
+
/**
|
|
165
|
+
* Function-style group-by bucket levels: `| stats count() by date_histogram(@timestamp, 1h)`.
|
|
166
|
+
*
|
|
167
|
+
* A name here is a `bucketFunction` token only when the next non-space character is `(`, so a
|
|
168
|
+
* FIELD with the same name (`by date_histogram`, `range eq 5`) still lexes as a field — both
|
|
169
|
+
* engines' grammars make the same distinction. `tests/statsBucketKinds.spec.ts` reads the Rust
|
|
170
|
+
* and Python grammars and fails when this list and theirs disagree.
|
|
171
|
+
*/
|
|
172
|
+
export declare const GROUP_BY_BUCKET_FUNCTIONS: readonly ["date_histogram", "geotile", "geohash", "range", "filters"];
|
|
173
|
+
/** `[min, max, default]` precision of each geo grid (`stats_buckets.GEO_PRECISION` in both engines). */
|
|
174
|
+
export declare const GEO_GRID_PRECISION: {
|
|
175
|
+
readonly geotile: readonly [0, 29, 7];
|
|
176
|
+
readonly geohash: readonly [1, 12, 5];
|
|
177
|
+
};
|
|
178
|
+
/** `date_histogram` calendar intervals (`stats_buckets.CALENDAR_WORDS` in both engines). */
|
|
179
|
+
export declare const DATE_HISTOGRAM_CALENDAR_INTERVALS: readonly ["minute", "hour", "day", "week", "month", "quarter", "year"];
|
|
180
|
+
/** `date_histogram` fixed-interval units: a positive whole number then one of these (`30s`, `1h`). */
|
|
181
|
+
export declare const DATE_HISTOGRAM_FIXED_UNITS: readonly ["ms", "s", "m", "h", "d"];
|
|
164
182
|
/**
|
|
165
183
|
* Keywords used in TQL.
|
|
166
184
|
*/
|
|
@@ -200,6 +218,8 @@ export declare class TqlTokenizer {
|
|
|
200
218
|
* Scan an identifier (field name, operator keyword, etc.)
|
|
201
219
|
*/
|
|
202
220
|
private scanIdentifier;
|
|
221
|
+
/** True when `value` names a group-by bucket function and the next non-space character is `(`. */
|
|
222
|
+
private isBucketFunctionCall;
|
|
203
223
|
/**
|
|
204
224
|
* Classify an identifier into the appropriate token type
|
|
205
225
|
*/
|
package/dist/src/tokenizer.js
CHANGED
|
@@ -125,7 +125,7 @@ const l = [
|
|
|
125
125
|
"is_loopback",
|
|
126
126
|
"is_multicast",
|
|
127
127
|
"is_link_local"
|
|
128
|
-
],
|
|
128
|
+
], E = new Set(
|
|
129
129
|
_
|
|
130
130
|
), f = [
|
|
131
131
|
// Counting
|
|
@@ -156,7 +156,7 @@ const l = [
|
|
|
156
156
|
"values",
|
|
157
157
|
"unique",
|
|
158
158
|
"distinct"
|
|
159
|
-
],
|
|
159
|
+
], T = [
|
|
160
160
|
"string",
|
|
161
161
|
"str",
|
|
162
162
|
"number",
|
|
@@ -173,7 +173,7 @@ const l = [
|
|
|
173
173
|
"object",
|
|
174
174
|
"geo",
|
|
175
175
|
"ip"
|
|
176
|
-
],
|
|
176
|
+
], m = [
|
|
177
177
|
"bar",
|
|
178
178
|
"barchart",
|
|
179
179
|
"horizontal_bar",
|
|
@@ -196,23 +196,31 @@ const l = [
|
|
|
196
196
|
"nested_donut",
|
|
197
197
|
"chord",
|
|
198
198
|
"auto"
|
|
199
|
-
], g = ["
|
|
200
|
-
|
|
199
|
+
], g = ["date_histogram", "geotile", "geohash", "range", "filters"], v = { geotile: [0, 29, 7], geohash: [1, 12, 5] }, I = [
|
|
200
|
+
"minute",
|
|
201
|
+
"hour",
|
|
202
|
+
"day",
|
|
203
|
+
"week",
|
|
204
|
+
"month",
|
|
205
|
+
"quarter",
|
|
206
|
+
"year"
|
|
207
|
+
], S = ["ms", "s", "m", "h", "d"], w = ["by", "as", "null", "true", "false", "stats", "top", "bottom"];
|
|
208
|
+
class O {
|
|
201
209
|
input;
|
|
202
210
|
position;
|
|
203
211
|
line;
|
|
204
212
|
column;
|
|
205
213
|
tokens;
|
|
206
|
-
constructor(
|
|
207
|
-
this.input =
|
|
214
|
+
constructor(t) {
|
|
215
|
+
this.input = t ?? "", this.position = 0, this.line = 1, this.column = 1, this.tokens = [];
|
|
208
216
|
}
|
|
209
217
|
/**
|
|
210
218
|
* Tokenize the entire input string
|
|
211
219
|
*/
|
|
212
220
|
tokenize() {
|
|
213
221
|
for (this.tokens = [], this.position = 0, this.line = 1, this.column = 1; !this.isAtEnd(); ) {
|
|
214
|
-
const
|
|
215
|
-
|
|
222
|
+
const t = this.scanToken();
|
|
223
|
+
t && this.tokens.push(t);
|
|
216
224
|
}
|
|
217
225
|
return this.tokens;
|
|
218
226
|
}
|
|
@@ -222,42 +230,42 @@ class w {
|
|
|
222
230
|
scanToken() {
|
|
223
231
|
if (this.isWhitespace(this.peek()))
|
|
224
232
|
return this.scanWhitespace();
|
|
225
|
-
const
|
|
226
|
-
if ((
|
|
227
|
-
return this.advance(), this.makeToken("logicalOp",
|
|
228
|
-
switch (
|
|
233
|
+
const t = this.position, n = this.line, i = this.column, e = this.advance();
|
|
234
|
+
if ((e === "&" || e === "|") && this.peek() === e)
|
|
235
|
+
return this.advance(), this.makeToken("logicalOp", e + e, t, n, i);
|
|
236
|
+
switch (e) {
|
|
229
237
|
case "(":
|
|
230
238
|
case ")":
|
|
231
|
-
return this.makeToken("paren",
|
|
239
|
+
return this.makeToken("paren", e, t, n, i);
|
|
232
240
|
case "[":
|
|
233
241
|
case "]":
|
|
234
|
-
return this.makeToken("bracket",
|
|
242
|
+
return this.makeToken("bracket", e, t, n, i);
|
|
235
243
|
case ",":
|
|
236
|
-
return this.makeToken("comma",
|
|
244
|
+
return this.makeToken("comma", e, t, n, i);
|
|
237
245
|
case "|":
|
|
238
|
-
return this.makeToken("pipe",
|
|
246
|
+
return this.makeToken("pipe", e, t, n, i);
|
|
239
247
|
}
|
|
240
|
-
return
|
|
248
|
+
return e === "=" && this.peek() === ">" ? (this.advance(), this.makeToken("arrow", "=>", t, n, i)) : e === ":" && this.peek() === ":" ? (this.advance(), this.makeToken("doubleColon", "::", t, n, i)) : e === '"' || e === "'" ? this.scanString(e, t, n, i) : this.isDigit(e) || e === "-" && this.isDigit(this.peek()) ? this.scanNumber(t, n, i) : this.isOperatorChar(e) ? this.scanSymbolOperator(t, n, i) : this.isIdentifierStart(e) ? this.scanIdentifier(t, n, i) : this.makeToken("unknown", e, t, n, i);
|
|
241
249
|
}
|
|
242
250
|
/**
|
|
243
251
|
* Scan whitespace characters
|
|
244
252
|
*/
|
|
245
253
|
scanWhitespace() {
|
|
246
|
-
const
|
|
247
|
-
let
|
|
254
|
+
const t = this.position, n = this.line, i = this.column;
|
|
255
|
+
let e = "";
|
|
248
256
|
for (; !this.isAtEnd() && this.isWhitespace(this.peek()); ) {
|
|
249
257
|
const s = this.advance();
|
|
250
|
-
|
|
258
|
+
e += s;
|
|
251
259
|
}
|
|
252
|
-
return this.makeToken("whitespace",
|
|
260
|
+
return this.makeToken("whitespace", e, t, n, i);
|
|
253
261
|
}
|
|
254
262
|
/**
|
|
255
263
|
* Scan a string literal (single or double quoted)
|
|
256
264
|
*/
|
|
257
|
-
scanString(
|
|
265
|
+
scanString(t, n, i, e) {
|
|
258
266
|
let s = "";
|
|
259
|
-
const r =
|
|
260
|
-
for (; !this.isAtEnd() && this.peek() !==
|
|
267
|
+
const r = t;
|
|
268
|
+
for (; !this.isAtEnd() && this.peek() !== t; )
|
|
261
269
|
if (this.peek() === "\\")
|
|
262
270
|
if (this.advance(), this.isAtEnd())
|
|
263
271
|
s += "\\";
|
|
@@ -295,7 +303,7 @@ class w {
|
|
|
295
303
|
start: n,
|
|
296
304
|
end: this.position,
|
|
297
305
|
line: i,
|
|
298
|
-
column:
|
|
306
|
+
column: e,
|
|
299
307
|
raw: r + s
|
|
300
308
|
} : (this.advance(), {
|
|
301
309
|
type: "string",
|
|
@@ -303,88 +311,95 @@ class w {
|
|
|
303
311
|
start: n,
|
|
304
312
|
end: this.position,
|
|
305
313
|
line: i,
|
|
306
|
-
column:
|
|
307
|
-
raw:
|
|
314
|
+
column: e,
|
|
315
|
+
raw: t + s + t
|
|
308
316
|
});
|
|
309
317
|
}
|
|
310
318
|
/**
|
|
311
319
|
* Scan a numeric literal
|
|
312
320
|
*/
|
|
313
|
-
scanNumber(
|
|
314
|
-
this.position =
|
|
315
|
-
let
|
|
316
|
-
for (this.peek() === "-" && (
|
|
317
|
-
|
|
321
|
+
scanNumber(t, n, i) {
|
|
322
|
+
this.position = t, this.column = i;
|
|
323
|
+
let e = "";
|
|
324
|
+
for (this.peek() === "-" && (e += this.advance()); !this.isAtEnd() && this.isDigit(this.peek()); )
|
|
325
|
+
e += this.advance();
|
|
318
326
|
if (this.peek() === "." && this.isDigit(this.peekNext()))
|
|
319
|
-
for (
|
|
320
|
-
|
|
327
|
+
for (e += this.advance(); !this.isAtEnd() && this.isDigit(this.peek()); )
|
|
328
|
+
e += this.advance();
|
|
321
329
|
if (this.peek() === "e" || this.peek() === "E")
|
|
322
|
-
for (
|
|
323
|
-
|
|
324
|
-
return this.makeToken("number",
|
|
330
|
+
for (e += this.advance(), (this.peek() === "+" || this.peek() === "-") && (e += this.advance()); !this.isAtEnd() && this.isDigit(this.peek()); )
|
|
331
|
+
e += this.advance();
|
|
332
|
+
return this.makeToken("number", e, t, n, i);
|
|
325
333
|
}
|
|
326
334
|
/**
|
|
327
335
|
* Scan symbol-based operators (==, !=, >, >=, <, <=, !)
|
|
328
336
|
*/
|
|
329
|
-
scanSymbolOperator(
|
|
330
|
-
this.position =
|
|
331
|
-
let
|
|
337
|
+
scanSymbolOperator(t, n, i) {
|
|
338
|
+
this.position = t, this.column = i;
|
|
339
|
+
let e = this.advance();
|
|
332
340
|
if (!this.isAtEnd()) {
|
|
333
|
-
const s =
|
|
341
|
+
const s = e + this.peek();
|
|
334
342
|
if (s === "=>")
|
|
335
|
-
return this.advance(), this.makeToken("arrow", "=>",
|
|
336
|
-
(s in h || s === "==" || s === "!=") && (
|
|
343
|
+
return this.advance(), this.makeToken("arrow", "=>", t, n, i);
|
|
344
|
+
(s in h || s === "==" || s === "!=") && (e = s, this.advance());
|
|
337
345
|
}
|
|
338
|
-
if (
|
|
346
|
+
if (e === "!") {
|
|
339
347
|
const s = this.position;
|
|
340
348
|
let r = "";
|
|
341
349
|
for (; !this.isAtEnd() && this.isIdentifierChar(this.peek()); )
|
|
342
350
|
r += this.advance();
|
|
343
351
|
if (r) {
|
|
344
352
|
if ("!" + r in u)
|
|
345
|
-
return this.makeToken("operator", "!" + r,
|
|
353
|
+
return this.makeToken("operator", "!" + r, t, n, i);
|
|
346
354
|
this.position = s, this.column = i + 1;
|
|
347
355
|
}
|
|
348
356
|
}
|
|
349
|
-
return
|
|
357
|
+
return e in h || e === "==" || e === "=" || e === "!=" || e === ">" || e === ">=" || e === "<" || e === "<=" ? this.makeToken("operator", e, t, n, i) : e === "!" ? this.makeToken("logicalOp", e, t, n, i) : this.makeToken("unknown", e, t, n, i);
|
|
350
358
|
}
|
|
351
359
|
/**
|
|
352
360
|
* Scan an identifier (field name, operator keyword, etc.)
|
|
353
361
|
*/
|
|
354
|
-
scanIdentifier(
|
|
355
|
-
this.position =
|
|
356
|
-
let
|
|
362
|
+
scanIdentifier(t, n, i) {
|
|
363
|
+
this.position = t, this.column = i;
|
|
364
|
+
let e = "";
|
|
357
365
|
const s = this.advance();
|
|
358
|
-
if (
|
|
359
|
-
return this.makeToken("unknown",
|
|
366
|
+
if (e += s, s === "@" && this.peek() === "@")
|
|
367
|
+
return this.makeToken("unknown", e, t, n, i);
|
|
360
368
|
let r = s;
|
|
361
369
|
for (; !this.isAtEnd() && this.isIdentifierChar(this.peek()); ) {
|
|
362
370
|
const c = this.advance();
|
|
363
371
|
if (c === "." && r === ".")
|
|
364
|
-
return this.position--, this.column--,
|
|
365
|
-
|
|
372
|
+
return this.position--, this.column--, e = e.slice(0, -1), this.makeToken("unknown", e, t, n, i);
|
|
373
|
+
e += c, r = c;
|
|
366
374
|
}
|
|
367
|
-
|
|
368
|
-
const a = this.classifyIdentifier(
|
|
369
|
-
return this.makeToken(a,
|
|
375
|
+
e.endsWith(".") && (this.position--, this.column--, e = e.slice(0, -1));
|
|
376
|
+
const a = this.isBucketFunctionCall(e) ? "bucketFunction" : this.classifyIdentifier(e);
|
|
377
|
+
return this.makeToken(a, e, t, n, i);
|
|
378
|
+
}
|
|
379
|
+
/** True when `value` names a group-by bucket function and the next non-space character is `(`. */
|
|
380
|
+
isBucketFunctionCall(t) {
|
|
381
|
+
if (!g.includes(t.toLowerCase())) return !1;
|
|
382
|
+
let n = this.position;
|
|
383
|
+
for (; n < this.input.length && /\s/.test(this.input[n]); ) n++;
|
|
384
|
+
return this.input[n] === "(";
|
|
370
385
|
}
|
|
371
386
|
/**
|
|
372
387
|
* Classify an identifier into the appropriate token type
|
|
373
388
|
*/
|
|
374
|
-
classifyIdentifier(
|
|
375
|
-
const n =
|
|
376
|
-
return p.includes(n) ? "logicalOp" : d.includes(n) ? "collectionOp" : i === "true" || i === "false" ? "boolean" : i === "null" ? "null" : i === "stats" ? "keyword" : l.includes(i) ? "operator" : f.includes(i) ? "aggregation" : k.includes(i) ? "mutator" :
|
|
389
|
+
classifyIdentifier(t) {
|
|
390
|
+
const n = t.toUpperCase(), i = t.toLowerCase();
|
|
391
|
+
return p.includes(n) ? "logicalOp" : d.includes(n) ? "collectionOp" : i === "true" || i === "false" ? "boolean" : i === "null" ? "null" : i === "stats" ? "keyword" : l.includes(i) ? "operator" : f.includes(i) ? "aggregation" : k.includes(i) ? "mutator" : T.includes(i) ? "typeHint" : m.includes(i) ? "visualization" : w.includes(i) ? "keyword" : "field";
|
|
377
392
|
}
|
|
378
393
|
// ============================================================================
|
|
379
394
|
// Helper Methods
|
|
380
395
|
// ============================================================================
|
|
381
|
-
makeToken(
|
|
396
|
+
makeToken(t, n, i, e, s) {
|
|
382
397
|
return {
|
|
383
|
-
type:
|
|
398
|
+
type: t,
|
|
384
399
|
value: n,
|
|
385
400
|
start: i,
|
|
386
401
|
end: this.position,
|
|
387
|
-
line:
|
|
402
|
+
line: e,
|
|
388
403
|
column: s
|
|
389
404
|
};
|
|
390
405
|
}
|
|
@@ -398,84 +413,88 @@ class w {
|
|
|
398
413
|
return this.position + 1 >= this.input.length ? "\0" : this.input[this.position + 1];
|
|
399
414
|
}
|
|
400
415
|
advance() {
|
|
401
|
-
const
|
|
402
|
-
return this.position++,
|
|
403
|
-
` ? (this.line++, this.column = 1) : this.column++,
|
|
416
|
+
const t = this.input[this.position];
|
|
417
|
+
return this.position++, t === `
|
|
418
|
+
` ? (this.line++, this.column = 1) : this.column++, t;
|
|
404
419
|
}
|
|
405
|
-
isWhitespace(
|
|
406
|
-
return
|
|
407
|
-
` ||
|
|
420
|
+
isWhitespace(t) {
|
|
421
|
+
return t === " " || t === " " || t === `
|
|
422
|
+
` || t === "\r";
|
|
408
423
|
}
|
|
409
|
-
isDigit(
|
|
410
|
-
return
|
|
424
|
+
isDigit(t) {
|
|
425
|
+
return t >= "0" && t <= "9";
|
|
411
426
|
}
|
|
412
|
-
isIdentifierStart(
|
|
413
|
-
return
|
|
427
|
+
isIdentifierStart(t) {
|
|
428
|
+
return t >= "a" && t <= "z" || t >= "A" && t <= "Z" || t === "_" || t === "@";
|
|
414
429
|
}
|
|
415
430
|
/**
|
|
416
431
|
* Check if char can continue an identifier (NOT including @ which is only valid at start)
|
|
417
432
|
*/
|
|
418
|
-
isIdentifierChar(
|
|
419
|
-
return
|
|
420
|
-
|
|
433
|
+
isIdentifierChar(t) {
|
|
434
|
+
return t >= "a" && t <= "z" || t >= "A" && t <= "Z" || t === "_" || this.isDigit(t) || t === "." || // For nested field access
|
|
435
|
+
t === "-";
|
|
421
436
|
}
|
|
422
|
-
isOperatorChar(
|
|
423
|
-
return
|
|
437
|
+
isOperatorChar(t) {
|
|
438
|
+
return t === "=" || t === "!" || t === ">" || t === "<";
|
|
424
439
|
}
|
|
425
440
|
}
|
|
426
|
-
function
|
|
427
|
-
return new
|
|
441
|
+
function A(o) {
|
|
442
|
+
return new O(o).tokenize();
|
|
428
443
|
}
|
|
429
|
-
function
|
|
430
|
-
return
|
|
444
|
+
function R(o) {
|
|
445
|
+
return A(o).filter((t) => t.type !== "whitespace");
|
|
431
446
|
}
|
|
432
|
-
function
|
|
447
|
+
function N(o, t) {
|
|
433
448
|
for (const n of o)
|
|
434
|
-
if (
|
|
449
|
+
if (t >= n.start && t < n.end)
|
|
435
450
|
return n;
|
|
436
451
|
return null;
|
|
437
452
|
}
|
|
438
|
-
function
|
|
439
|
-
return o.filter((i) => i.start < n && i.end >
|
|
453
|
+
function y(o, t, n) {
|
|
454
|
+
return o.filter((i) => i.start < n && i.end > t);
|
|
440
455
|
}
|
|
441
|
-
function
|
|
456
|
+
function x(o, t) {
|
|
442
457
|
let n = null;
|
|
443
458
|
for (const i of o)
|
|
444
|
-
if (i.end <=
|
|
459
|
+
if (i.end <= t)
|
|
445
460
|
n = i;
|
|
446
461
|
else
|
|
447
462
|
break;
|
|
448
463
|
return n;
|
|
449
464
|
}
|
|
450
|
-
function
|
|
465
|
+
function D(o, t, n = !0) {
|
|
451
466
|
const i = n ? o.filter((s) => s.type !== "whitespace") : o;
|
|
452
|
-
let
|
|
467
|
+
let e = null;
|
|
453
468
|
for (const s of i)
|
|
454
|
-
if (s.end <=
|
|
455
|
-
|
|
469
|
+
if (s.end <= t)
|
|
470
|
+
e = s;
|
|
456
471
|
else
|
|
457
472
|
break;
|
|
458
|
-
return
|
|
473
|
+
return e;
|
|
459
474
|
}
|
|
460
475
|
export {
|
|
461
476
|
f as AGGREGATION_FUNCTIONS,
|
|
462
477
|
_ as BOOLEAN_PREDICATE_MUTATORS,
|
|
463
|
-
|
|
478
|
+
E as BOOLEAN_PREDICATE_MUTATOR_SET,
|
|
464
479
|
d as COLLECTION_OPERATORS,
|
|
465
480
|
l as COMPARISON_OPERATORS,
|
|
466
|
-
|
|
481
|
+
I as DATE_HISTOGRAM_CALENDAR_INTERVALS,
|
|
482
|
+
S as DATE_HISTOGRAM_FIXED_UNITS,
|
|
483
|
+
v as GEO_GRID_PRECISION,
|
|
484
|
+
g as GROUP_BY_BUCKET_FUNCTIONS,
|
|
485
|
+
w as KEYWORDS,
|
|
467
486
|
p as LOGICAL_OPERATORS,
|
|
468
487
|
k as MUTATORS,
|
|
469
488
|
u as NEGATION_SYMBOL_OPERATORS,
|
|
470
489
|
b as SYMBOL_LOGICAL_OPERATORS,
|
|
471
490
|
h as SYMBOL_OPERATORS,
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
491
|
+
T as TYPE_HINTS,
|
|
492
|
+
O as TqlTokenizer,
|
|
493
|
+
m as VISUALIZATION_TYPES,
|
|
494
|
+
D as findPreviousToken,
|
|
495
|
+
N as findTokenAtPosition,
|
|
496
|
+
x as findTokenBefore,
|
|
497
|
+
y as findTokensInRange,
|
|
498
|
+
A as tokenizeTql,
|
|
499
|
+
R as tokenizeTqlNoWhitespace
|
|
481
500
|
};
|
package/dist/src/types.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Comprehensive type system for TQL parsing, validation, and autocomplete.
|
|
5
5
|
* Synchronized with tellaro-query-language Python/Rust implementations.
|
|
6
6
|
*/
|
|
7
|
-
export type TqlTokenType = "field" | "operator" | "logicalOp" | "collectionOp" | "string" | "number" | "boolean" | "null" | "paren" | "bracket" | "comma" | "pipe" | "arrow" | "doubleColon" | "mutator" | "aggregation" | "keyword" | "typeHint" | "visualization" | "whitespace" | "unknown" | "error";
|
|
7
|
+
export type TqlTokenType = "field" | "operator" | "logicalOp" | "collectionOp" | "string" | "number" | "boolean" | "null" | "paren" | "bracket" | "comma" | "pipe" | "arrow" | "doubleColon" | "mutator" | "aggregation" | "bucketFunction" | "keyword" | "typeHint" | "visualization" | "whitespace" | "unknown" | "error";
|
|
8
8
|
export interface TqlToken {
|
|
9
9
|
type: TqlTokenType;
|
|
10
10
|
value: string;
|
|
@@ -79,10 +79,114 @@ export interface TqlAggregationExpression {
|
|
|
79
79
|
start: number;
|
|
80
80
|
end: number;
|
|
81
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* A function-style group-by bucket level, as both engines' ASTs carry it in `bucket`.
|
|
84
|
+
*
|
|
85
|
+
* `| stats count() by date_histogram(@timestamp, 1h)` → `{ type: "date_histogram",
|
|
86
|
+
* interval: "1h", interval_kind: "fixed" }`. The same object is echoed in a grouped
|
|
87
|
+
* result's `group_by[i].bucket`.
|
|
88
|
+
*/
|
|
89
|
+
export type TqlGroupByBucket = {
|
|
90
|
+
type: "date_histogram";
|
|
91
|
+
/** `"1h"` (fixed), `"day"` (calendar) or `"auto"` */
|
|
92
|
+
interval: string;
|
|
93
|
+
interval_kind: "fixed" | "calendar" | "auto";
|
|
94
|
+
/** The auto bucket target (`auto(N)`, default 50); only for `interval_kind: "auto"` */
|
|
95
|
+
buckets?: number;
|
|
96
|
+
} | {
|
|
97
|
+
/** Web-Mercator tiles; row keys are `"z/x/y"` */
|
|
98
|
+
type: "geotile";
|
|
99
|
+
precision: number;
|
|
100
|
+
} | {
|
|
101
|
+
/** Geohash cells; row keys are the geohash */
|
|
102
|
+
type: "geohash";
|
|
103
|
+
precision: number;
|
|
104
|
+
} | {
|
|
105
|
+
/** `range(f, 0, 1024)` → `*-0`, `0-1024`, `1024-*`; each bucket is `from <= v < to` */
|
|
106
|
+
type: "range";
|
|
107
|
+
ranges: Array<{
|
|
108
|
+
key: string;
|
|
109
|
+
from?: number;
|
|
110
|
+
to?: number;
|
|
111
|
+
}>;
|
|
112
|
+
} | {
|
|
113
|
+
/**
|
|
114
|
+
* `filters(<filter> as 'label', ...)`. Result metadata carries the labels (in written
|
|
115
|
+
* order); the engines' ASTs carry `filters: [{ label, filter }]` instead.
|
|
116
|
+
*/
|
|
117
|
+
type: "filters";
|
|
118
|
+
labels?: string[];
|
|
119
|
+
filters?: Array<{
|
|
120
|
+
label: string;
|
|
121
|
+
filter: unknown;
|
|
122
|
+
}>;
|
|
123
|
+
};
|
|
124
|
+
/** A latitude/longitude pair in degrees. */
|
|
125
|
+
export interface TqlLatLon {
|
|
126
|
+
lat: number;
|
|
127
|
+
lon: number;
|
|
128
|
+
}
|
|
129
|
+
/** One group-by level of a stats expression. */
|
|
130
|
+
export interface TqlGroupByLevel {
|
|
131
|
+
/** The bucketed field */
|
|
132
|
+
field: TqlFieldReference;
|
|
133
|
+
/** Present for a bucket function, absent for a plain terms level */
|
|
134
|
+
bucket?: TqlGroupByBucket;
|
|
135
|
+
/** `by f top N` */
|
|
136
|
+
bucketSize?: number;
|
|
137
|
+
/** `by f top N with other` */
|
|
138
|
+
withOther?: boolean;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* One entry of a grouped stats result's `group_by` list (Python and Rust, in memory and
|
|
142
|
+
* from OpenSearch). `key` names the property each row's `key` object stores this level's
|
|
143
|
+
* value under — usually the field, `date_histogram(@timestamp)` when two levels share a field.
|
|
144
|
+
*/
|
|
145
|
+
export type TqlGroupByMetadata = {
|
|
146
|
+
field: string;
|
|
147
|
+
key: string;
|
|
148
|
+
bucket_size: number | null;
|
|
149
|
+
with_other: boolean;
|
|
150
|
+
bucket?: undefined;
|
|
151
|
+
} | {
|
|
152
|
+
/** null for `filters(...)`, which buckets no single field (its key is `filters`) */
|
|
153
|
+
field: string | null;
|
|
154
|
+
key: string;
|
|
155
|
+
bucket: TqlGroupByBucket;
|
|
156
|
+
};
|
|
157
|
+
/** Per-level bucket metadata a row carries in `bucket_meta[<level key>]`. */
|
|
158
|
+
export interface TqlRowBucketMeta {
|
|
159
|
+
/** date_histogram: bucket start, epoch milliseconds (UTC) */
|
|
160
|
+
epoch_ms?: number;
|
|
161
|
+
/** date_histogram with an automatic interval: the interval chosen, e.g. `"1h"` */
|
|
162
|
+
interval?: string;
|
|
163
|
+
/** geotile/geohash: cell centre latitude */
|
|
164
|
+
lat?: number;
|
|
165
|
+
/** geotile/geohash: cell centre longitude */
|
|
166
|
+
lon?: number;
|
|
167
|
+
/** geotile/geohash: north-west corner */
|
|
168
|
+
top_left?: TqlLatLon;
|
|
169
|
+
/** geotile/geohash: south-east corner */
|
|
170
|
+
bottom_right?: TqlLatLon;
|
|
171
|
+
/** range: inclusive lower bound (absent for the first bucket) */
|
|
172
|
+
from?: number;
|
|
173
|
+
/** range: exclusive upper bound (absent for the last bucket) */
|
|
174
|
+
to?: number;
|
|
175
|
+
}
|
|
176
|
+
/** One row of a grouped stats result. Time keys are ISO 8601 (`2026-10-14T10:00:00.000Z`). */
|
|
177
|
+
export interface TqlStatsRow {
|
|
178
|
+
key: Record<string, unknown>;
|
|
179
|
+
doc_count: number;
|
|
180
|
+
bucket_meta?: Record<string, TqlRowBucketMeta>;
|
|
181
|
+
aggregations?: Record<string, unknown>;
|
|
182
|
+
/** `with other`: this row holds what the last level's limit dropped; its key there is "Other" */
|
|
183
|
+
other?: boolean;
|
|
184
|
+
[metric: string]: unknown;
|
|
185
|
+
}
|
|
82
186
|
export interface TqlStatsExpression {
|
|
83
187
|
type: "stats";
|
|
84
188
|
aggregations: TqlAggregationExpression[];
|
|
85
|
-
groupBy?:
|
|
189
|
+
groupBy?: TqlGroupByLevel[];
|
|
86
190
|
topN?: number;
|
|
87
191
|
bottomN?: number;
|
|
88
192
|
visualization?: TqlVisualizationType;
|
|
@@ -224,7 +328,14 @@ export type TqlErrorCode = "UNEXPECTED_TOKEN" | "UNEXPECTED_END" | "UNBALANCED_P
|
|
|
224
328
|
* form on `count(*)` / `count()` (`count(*, top 2)`). A spelling only one engine refuses is
|
|
225
329
|
* an ENGINE_DIVERGENCE warning instead — never this.
|
|
226
330
|
*/
|
|
227
|
-
| "INVALID_STATS_MODIFIER"
|
|
331
|
+
| "INVALID_STATS_MODIFIER"
|
|
332
|
+
/**
|
|
333
|
+
* A group-by bucket function with arguments both engines refuse at parse time: a bad
|
|
334
|
+
* `date_histogram` interval or `auto(N)` target, a geo precision out of range, unsorted or
|
|
335
|
+
* malformed `range` boundaries, a duplicate `filters` label. Mirrors
|
|
336
|
+
* `cross_language_tests/fixtures/test_cases/validation/stats_bucket_errors.json`.
|
|
337
|
+
*/
|
|
338
|
+
| "INVALID_STATS_BUCKET";
|
|
228
339
|
export type TqlWarningCode = "UNKNOWN_FIELD" | "TYPE_MISMATCH" | "DEPRECATED_OPERATOR" | "REDUNDANT_PARENS"
|
|
229
340
|
/**
|
|
230
341
|
* The query parses, but only ONE of the two engines accepts it.
|