| 
									
										
										
										
											2022-04-02 00:33:09 +08:00
										 |  |  | import { SyntaxNode } from '@lezer/common'; | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  | import { BinModifiers, OnOrIgnoring } from '@prometheus-io/lezer-promql'; | 
					
						
							| 
									
										
										
										
											2022-04-22 21:33:13 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  | import { | 
					
						
							|  |  |  |   And, | 
					
						
							|  |  |  |   BinOpExpr, | 
					
						
							|  |  |  |   Bool, | 
					
						
							|  |  |  |   By, | 
					
						
							|  |  |  |   ConvOp, | 
					
						
							| 
									
										
										
										
											2023-05-24 21:23:54 +08:00
										 |  |  |   Decolorize, | 
					
						
							| 
									
										
										
										
											2023-05-25 20:22:16 +08:00
										 |  |  |   DistinctFilter, | 
					
						
							|  |  |  |   DistinctLabel, | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |   Filter, | 
					
						
							|  |  |  |   FilterOp, | 
					
						
							|  |  |  |   Grouping, | 
					
						
							|  |  |  |   GroupingLabelList, | 
					
						
							|  |  |  |   GroupingLabels, | 
					
						
							|  |  |  |   Identifier, | 
					
						
							|  |  |  |   Ip, | 
					
						
							|  |  |  |   IpLabelFilter, | 
					
						
							|  |  |  |   Json, | 
					
						
							|  |  |  |   JsonExpression, | 
					
						
							|  |  |  |   JsonExpressionParser, | 
					
						
							|  |  |  |   LabelFilter, | 
					
						
							|  |  |  |   LabelFormatMatcher, | 
					
						
							|  |  |  |   LabelParser, | 
					
						
							|  |  |  |   LineFilter, | 
					
						
							|  |  |  |   LineFormatExpr, | 
					
						
							|  |  |  |   LogRangeExpr, | 
					
						
							|  |  |  |   Matcher, | 
					
						
							|  |  |  |   MetricExpr, | 
					
						
							|  |  |  |   Number as NumberLezer, | 
					
						
							|  |  |  |   On, | 
					
						
							|  |  |  |   Or, | 
					
						
							|  |  |  |   parser, | 
					
						
							|  |  |  |   Range, | 
					
						
							|  |  |  |   RangeAggregationExpr, | 
					
						
							|  |  |  |   RangeOp, | 
					
						
							|  |  |  |   String, | 
					
						
							|  |  |  |   UnitFilter, | 
					
						
							|  |  |  |   Unwrap, | 
					
						
							|  |  |  |   UnwrapExpr, | 
					
						
							|  |  |  |   VectorAggregationExpr, | 
					
						
							|  |  |  |   VectorOp, | 
					
						
							|  |  |  |   Without, | 
					
						
							|  |  |  | } from '@grafana/lezer-logql'; | 
					
						
							| 
									
										
										
										
											2022-04-22 21:33:13 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-02 00:33:09 +08:00
										 |  |  | import { | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |   ErrorId, | 
					
						
							| 
									
										
										
										
											2022-04-05 00:11:15 +08:00
										 |  |  |   getAllByType, | 
					
						
							| 
									
										
										
										
											2022-04-02 00:33:09 +08:00
										 |  |  |   getLeftMostChild, | 
					
						
							|  |  |  |   getString, | 
					
						
							|  |  |  |   makeBinOp, | 
					
						
							|  |  |  |   makeError, | 
					
						
							|  |  |  |   replaceVariables, | 
					
						
							|  |  |  | } from '../../prometheus/querybuilder/shared/parsingUtils'; | 
					
						
							| 
									
										
										
										
											2022-12-22 22:31:41 +08:00
										 |  |  | import { | 
					
						
							|  |  |  |   QueryBuilderLabelFilter, | 
					
						
							|  |  |  |   QueryBuilderOperation, | 
					
						
							|  |  |  |   QueryBuilderOperationParamValue, | 
					
						
							|  |  |  | } from '../../prometheus/querybuilder/shared/types'; | 
					
						
							| 
									
										
										
										
											2022-04-22 21:33:13 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  | import { binaryScalarDefs } from './binaryScalarOperations'; | 
					
						
							| 
									
										
										
										
											2022-12-22 22:31:41 +08:00
										 |  |  | import { checkParamsAreValid, getDefinitionById } from './operations'; | 
					
						
							| 
									
										
										
										
											2022-07-25 21:16:04 +08:00
										 |  |  | import { LokiOperationId, LokiVisualQuery, LokiVisualQueryBinary } from './types'; | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | interface Context { | 
					
						
							|  |  |  |   query: LokiVisualQuery; | 
					
						
							|  |  |  |   errors: ParsingError[]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | interface ParsingError { | 
					
						
							|  |  |  |   text: string; | 
					
						
							| 
									
										
										
										
											2022-04-06 17:17:49 +08:00
										 |  |  |   from?: number; | 
					
						
							|  |  |  |   to?: number; | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |   parentType?: string; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function buildVisualQueryFromString(expr: string): Context { | 
					
						
							|  |  |  |   const replacedExpr = replaceVariables(expr); | 
					
						
							|  |  |  |   const tree = parser.parse(replacedExpr); | 
					
						
							|  |  |  |   const node = tree.topNode; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // This will be modified in the handleExpression
 | 
					
						
							|  |  |  |   const visQuery: LokiVisualQuery = { | 
					
						
							|  |  |  |     labels: [], | 
					
						
							|  |  |  |     operations: [], | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-06 17:17:49 +08:00
										 |  |  |   const context: Context = { | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |     query: visQuery, | 
					
						
							|  |  |  |     errors: [], | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-06 17:17:49 +08:00
										 |  |  |   try { | 
					
						
							|  |  |  |     handleExpression(replacedExpr, node, context); | 
					
						
							|  |  |  |   } catch (err) { | 
					
						
							|  |  |  |     // Not ideal to log it here, but otherwise we would lose the stack trace.
 | 
					
						
							|  |  |  |     console.error(err); | 
					
						
							| 
									
										
										
										
											2022-06-15 15:59:29 +08:00
										 |  |  |     if (err instanceof Error) { | 
					
						
							|  |  |  |       context.errors.push({ | 
					
						
							|  |  |  |         text: err.message, | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-04-06 17:17:49 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // If we have empty query, we want to reset errors
 | 
					
						
							|  |  |  |   if (isEmptyQuery(context.query)) { | 
					
						
							|  |  |  |     context.errors = []; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |   return context; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function handleExpression(expr: string, node: SyntaxNode, context: Context) { | 
					
						
							|  |  |  |   const visQuery = context.query; | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |   switch (node.type.id) { | 
					
						
							|  |  |  |     case Matcher: { | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |       visQuery.labels.push(getLabel(expr, node)); | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |       const err = node.getChild(ErrorId); | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |       if (err) { | 
					
						
							|  |  |  |         context.errors.push(makeError(expr, err)); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |     case LineFilter: { | 
					
						
							| 
									
										
										
										
											2022-04-05 23:07:13 +08:00
										 |  |  |       const { operation, error } = getLineFilter(expr, node); | 
					
						
							|  |  |  |       if (operation) { | 
					
						
							|  |  |  |         visQuery.operations.push(operation); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       // Show error for query patterns not supported in visual query builder
 | 
					
						
							|  |  |  |       if (error) { | 
					
						
							|  |  |  |         context.errors.push(createNotSupportedError(expr, node, error)); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |     case LabelParser: { | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |       visQuery.operations.push(getLabelParser(expr, node)); | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |     case LabelFilter: { | 
					
						
							| 
									
										
										
										
											2022-04-05 23:07:13 +08:00
										 |  |  |       const { operation, error } = getLabelFilter(expr, node); | 
					
						
							|  |  |  |       if (operation) { | 
					
						
							|  |  |  |         visQuery.operations.push(operation); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       // Show error for query patterns not supported in visual query builder
 | 
					
						
							|  |  |  |       if (error) { | 
					
						
							|  |  |  |         context.errors.push(createNotSupportedError(expr, node, error)); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |     case JsonExpressionParser: { | 
					
						
							| 
									
										
										
										
											2022-07-12 21:42:01 +08:00
										 |  |  |       visQuery.operations.push(getJsonExpressionParser(expr, node)); | 
					
						
							|  |  |  |       break; | 
					
						
							| 
									
										
										
										
											2022-04-05 23:07:13 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |     case LineFormatExpr: { | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |       visQuery.operations.push(getLineFormat(expr, node)); | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |     case LabelFormatMatcher: { | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |       visQuery.operations.push(getLabelFormat(expr, node)); | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |     case UnwrapExpr: { | 
					
						
							| 
									
										
										
										
											2022-05-27 20:00:51 +08:00
										 |  |  |       const { operation, error } = handleUnwrapExpr(expr, node, context); | 
					
						
							| 
									
										
										
										
											2022-04-05 23:07:13 +08:00
										 |  |  |       if (operation) { | 
					
						
							|  |  |  |         visQuery.operations.push(operation); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       // Show error for query patterns not supported in visual query builder
 | 
					
						
							|  |  |  |       if (error) { | 
					
						
							|  |  |  |         context.errors.push(createNotSupportedError(expr, node, error)); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-05 00:44:23 +08:00
										 |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-24 21:23:54 +08:00
										 |  |  |     case Decolorize: { | 
					
						
							|  |  |  |       visQuery.operations.push(getDecolorize()); | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |     case RangeAggregationExpr: { | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |       visQuery.operations.push(handleRangeAggregation(expr, node, context)); | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |     case VectorAggregationExpr: { | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |       visQuery.operations.push(handleVectorAggregation(expr, node, context)); | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |     case BinOpExpr: { | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |       handleBinary(expr, node, context); | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |     case ErrorId: { | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |       if (isIntervalVariableError(node)) { | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       context.errors.push(makeError(expr, node)); | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-25 20:22:16 +08:00
										 |  |  |     case DistinctFilter: { | 
					
						
							|  |  |  |       visQuery.operations.push(handleDistinctFilter(expr, node, context)); | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |     default: { | 
					
						
							| 
									
										
										
										
											2022-08-09 16:19:30 +08:00
										 |  |  |       // Any other nodes we just ignore and go to its children. This should be fine as there are lots of wrapper
 | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |       // nodes that can be skipped.
 | 
					
						
							|  |  |  |       // TODO: there are probably cases where we will just skip nodes we don't support and we should be able to
 | 
					
						
							|  |  |  |       //  detect those and report back.
 | 
					
						
							|  |  |  |       let child = node.firstChild; | 
					
						
							|  |  |  |       while (child) { | 
					
						
							|  |  |  |         handleExpression(expr, child, context); | 
					
						
							|  |  |  |         child = child.nextSibling; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function getLabel(expr: string, node: SyntaxNode): QueryBuilderLabelFilter { | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |   const labelNode = node.getChild(Identifier); | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |   const label = getString(expr, labelNode); | 
					
						
							|  |  |  |   const op = getString(expr, labelNode!.nextSibling); | 
					
						
							| 
									
										
										
										
											2022-12-06 19:30:03 +08:00
										 |  |  |   let value = getString(expr, node.getChild(String)); | 
					
						
							|  |  |  |   // `value` is wrapped in double quotes, so we need to remove them. As a value can contain double quotes, we can't use RegEx here.
 | 
					
						
							|  |  |  |   value = value.substring(1, value.length - 1); | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     label, | 
					
						
							|  |  |  |     op, | 
					
						
							|  |  |  |     value, | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-05 23:07:13 +08:00
										 |  |  | function getLineFilter(expr: string, node: SyntaxNode): { operation?: QueryBuilderOperation; error?: string } { | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |   const filter = getString(expr, node.getChild(Filter)); | 
					
						
							|  |  |  |   const filterExpr = handleQuotes(getString(expr, node.getChild(String))); | 
					
						
							|  |  |  |   const ipLineFilter = node.getChild(FilterOp)?.getChild(Ip); | 
					
						
							| 
									
										
										
										
											2022-07-25 21:16:04 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (ipLineFilter) { | 
					
						
							| 
									
										
										
										
											2022-04-05 23:07:13 +08:00
										 |  |  |     return { | 
					
						
							| 
									
										
										
										
											2022-07-25 21:16:04 +08:00
										 |  |  |       operation: { | 
					
						
							|  |  |  |         id: LokiOperationId.LineFilterIpMatches, | 
					
						
							|  |  |  |         params: [filter, filterExpr], | 
					
						
							|  |  |  |       }, | 
					
						
							| 
									
										
										
										
											2022-04-05 23:07:13 +08:00
										 |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-02-09 17:03:13 +08:00
										 |  |  |   const mapFilter: Record<string, LokiOperationId> = { | 
					
						
							| 
									
										
										
										
											2022-07-25 21:16:04 +08:00
										 |  |  |     '|=': LokiOperationId.LineContains, | 
					
						
							|  |  |  |     '!=': LokiOperationId.LineContainsNot, | 
					
						
							|  |  |  |     '|~': LokiOperationId.LineMatchesRegex, | 
					
						
							|  |  |  |     '!~': LokiOperationId.LineMatchesRegexNot, | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return { | 
					
						
							| 
									
										
										
										
											2022-04-05 23:07:13 +08:00
										 |  |  |     operation: { | 
					
						
							|  |  |  |       id: mapFilter[filter], | 
					
						
							|  |  |  |       params: [filterExpr], | 
					
						
							|  |  |  |     }, | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function getLabelParser(expr: string, node: SyntaxNode): QueryBuilderOperation { | 
					
						
							|  |  |  |   const parserNode = node.firstChild; | 
					
						
							|  |  |  |   const parser = getString(expr, parserNode); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |   const string = handleQuotes(getString(expr, node.getChild(String))); | 
					
						
							| 
									
										
										
										
											2022-12-22 22:31:41 +08:00
										 |  |  |   let params: QueryBuilderOperationParamValue[] = !!string ? [string] : []; | 
					
						
							|  |  |  |   const opDef = getDefinitionById(parser); | 
					
						
							|  |  |  |   if (opDef && !checkParamsAreValid(opDef, params)) { | 
					
						
							|  |  |  |     params = opDef?.defaultParams || []; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |   return { | 
					
						
							| 
									
										
										
										
											2022-07-12 21:42:01 +08:00
										 |  |  |     id: parser, | 
					
						
							|  |  |  |     params, | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function getJsonExpressionParser(expr: string, node: SyntaxNode): QueryBuilderOperation { | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |   const parserNode = node.getChild(Json); | 
					
						
							| 
									
										
										
										
											2022-07-12 21:42:01 +08:00
										 |  |  |   const parser = getString(expr, parserNode); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |   const params = [...getAllByType(expr, node, JsonExpression)]; | 
					
						
							| 
									
										
										
										
											2022-07-12 21:42:01 +08:00
										 |  |  |   return { | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |     id: parser, | 
					
						
							|  |  |  |     params, | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-05 23:07:13 +08:00
										 |  |  | function getLabelFilter(expr: string, node: SyntaxNode): { operation?: QueryBuilderOperation; error?: string } { | 
					
						
							|  |  |  |   // Check for nodes not supported in visual builder and return error
 | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |   if (node.getChild(Or) || node.getChild(And) || node.getChild('Comma')) { | 
					
						
							| 
									
										
										
										
											2022-04-05 23:07:13 +08:00
										 |  |  |     return { | 
					
						
							|  |  |  |       error: 'Label filter with comma, "and", "or" not supported in query builder', | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |   if (node.firstChild!.type.id === IpLabelFilter) { | 
					
						
							| 
									
										
										
										
											2022-07-25 21:16:04 +08:00
										 |  |  |     const ipLabelFilter = node.firstChild; | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |     const label = ipLabelFilter?.getChild(Identifier); | 
					
						
							| 
									
										
										
										
											2022-07-25 21:16:04 +08:00
										 |  |  |     const op = label?.nextSibling; | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |     const value = ipLabelFilter?.getChild(String); | 
					
						
							| 
									
										
										
										
											2022-07-25 21:16:04 +08:00
										 |  |  |     const valueString = handleQuotes(getString(expr, value)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-05 23:07:13 +08:00
										 |  |  |     return { | 
					
						
							| 
									
										
										
										
											2022-07-25 21:16:04 +08:00
										 |  |  |       operation: { | 
					
						
							|  |  |  |         id: LokiOperationId.LabelFilterIpMatches, | 
					
						
							|  |  |  |         params: [getString(expr, label), getString(expr, op), valueString], | 
					
						
							|  |  |  |       }, | 
					
						
							| 
									
										
										
										
											2022-04-05 23:07:13 +08:00
										 |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-03 18:50:43 +08:00
										 |  |  |   const id = LokiOperationId.LabelFilter; | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |   if (node.firstChild!.type.id === UnitFilter) { | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |     const filter = node.firstChild!.firstChild; | 
					
						
							|  |  |  |     const label = filter!.firstChild; | 
					
						
							|  |  |  |     const op = label!.nextSibling; | 
					
						
							|  |  |  |     const value = op!.nextSibling; | 
					
						
							|  |  |  |     const valueString = handleQuotes(getString(expr, value)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return { | 
					
						
							| 
									
										
										
										
											2022-04-05 23:07:13 +08:00
										 |  |  |       operation: { | 
					
						
							|  |  |  |         id, | 
					
						
							|  |  |  |         params: [getString(expr, label), getString(expr, op), valueString], | 
					
						
							|  |  |  |       }, | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-04-05 23:07:13 +08:00
										 |  |  |   // In this case it is Matcher or NumberFilter
 | 
					
						
							|  |  |  |   const filter = node.firstChild; | 
					
						
							|  |  |  |   const label = filter!.firstChild; | 
					
						
							|  |  |  |   const op = label!.nextSibling; | 
					
						
							|  |  |  |   const value = op!.nextSibling; | 
					
						
							|  |  |  |   const params = [getString(expr, label), getString(expr, op), handleQuotes(getString(expr, value))]; | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-05 23:07:13 +08:00
										 |  |  |   // Special case of pipe filtering - no errors
 | 
					
						
							|  |  |  |   if (params.join('') === `__error__=`) { | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |     return { | 
					
						
							| 
									
										
										
										
											2022-04-05 23:07:13 +08:00
										 |  |  |       operation: { | 
					
						
							| 
									
										
										
										
											2022-08-03 18:50:43 +08:00
										 |  |  |         id: LokiOperationId.LabelFilterNoErrors, | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |         params: [], | 
					
						
							| 
									
										
										
										
											2022-04-05 23:07:13 +08:00
										 |  |  |       }, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-05 23:07:13 +08:00
										 |  |  |   return { | 
					
						
							|  |  |  |     operation: { | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |       id, | 
					
						
							|  |  |  |       params, | 
					
						
							| 
									
										
										
										
											2022-04-05 23:07:13 +08:00
										 |  |  |     }, | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function getLineFormat(expr: string, node: SyntaxNode): QueryBuilderOperation { | 
					
						
							| 
									
										
										
										
											2022-08-03 18:50:43 +08:00
										 |  |  |   const id = LokiOperationId.LineFormat; | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |   const string = handleQuotes(getString(expr, node.getChild(String))); | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     id, | 
					
						
							|  |  |  |     params: [string], | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function getLabelFormat(expr: string, node: SyntaxNode): QueryBuilderOperation { | 
					
						
							| 
									
										
										
										
											2022-08-03 18:50:43 +08:00
										 |  |  |   const id = LokiOperationId.LabelFormat; | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |   const renameTo = node.getChild(Identifier); | 
					
						
							| 
									
										
										
										
											2022-07-22 22:59:25 +08:00
										 |  |  |   const op = renameTo!.nextSibling; | 
					
						
							|  |  |  |   const originalLabel = op!.nextSibling; | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     id, | 
					
						
							| 
									
										
										
										
											2022-07-22 22:59:25 +08:00
										 |  |  |     params: [getString(expr, originalLabel), handleQuotes(getString(expr, renameTo))], | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-24 21:23:54 +08:00
										 |  |  | function getDecolorize(): QueryBuilderOperation { | 
					
						
							|  |  |  |   const id = LokiOperationId.Decolorize; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     id, | 
					
						
							|  |  |  |     params: [], | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-27 20:00:51 +08:00
										 |  |  | function handleUnwrapExpr( | 
					
						
							|  |  |  |   expr: string, | 
					
						
							|  |  |  |   node: SyntaxNode, | 
					
						
							|  |  |  |   context: Context | 
					
						
							|  |  |  | ): { operation?: QueryBuilderOperation; error?: string } { | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |   const unwrapExprChild = node.getChild(UnwrapExpr); | 
					
						
							|  |  |  |   const labelFilterChild = node.getChild(LabelFilter); | 
					
						
							|  |  |  |   const unwrapChild = node.getChild(Unwrap); | 
					
						
							| 
									
										
										
										
											2022-05-27 20:00:51 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (unwrapExprChild) { | 
					
						
							|  |  |  |     handleExpression(expr, unwrapExprChild, context); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (labelFilterChild) { | 
					
						
							|  |  |  |     handleExpression(expr, labelFilterChild, context); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (unwrapChild) { | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |     if (unwrapChild.nextSibling?.type.id === ConvOp) { | 
					
						
							| 
									
										
										
										
											2022-07-25 18:51:28 +08:00
										 |  |  |       const convOp = unwrapChild.nextSibling; | 
					
						
							|  |  |  |       const identifier = convOp.nextSibling; | 
					
						
							| 
									
										
										
										
											2022-05-27 20:00:51 +08:00
										 |  |  |       return { | 
					
						
							| 
									
										
										
										
											2022-07-25 18:51:28 +08:00
										 |  |  |         operation: { | 
					
						
							| 
									
										
										
										
											2022-08-03 18:50:43 +08:00
										 |  |  |           id: LokiOperationId.Unwrap, | 
					
						
							| 
									
										
										
										
											2022-07-25 18:51:28 +08:00
										 |  |  |           params: [getString(expr, identifier), getString(expr, convOp)], | 
					
						
							|  |  |  |         }, | 
					
						
							| 
									
										
										
										
											2022-05-27 20:00:51 +08:00
										 |  |  |       }; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-05 23:07:13 +08:00
										 |  |  |     return { | 
					
						
							| 
									
										
										
										
											2022-05-27 20:00:51 +08:00
										 |  |  |       operation: { | 
					
						
							| 
									
										
										
										
											2022-08-03 18:50:43 +08:00
										 |  |  |         id: LokiOperationId.Unwrap, | 
					
						
							| 
									
										
										
										
											2022-07-25 18:51:28 +08:00
										 |  |  |         params: [getString(expr, unwrapChild?.nextSibling), ''], | 
					
						
							| 
									
										
										
										
											2022-05-27 20:00:51 +08:00
										 |  |  |       }, | 
					
						
							| 
									
										
										
										
											2022-04-05 23:07:13 +08:00
										 |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-27 20:00:51 +08:00
										 |  |  |   return {}; | 
					
						
							| 
									
										
										
										
											2022-04-05 00:44:23 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2023-05-25 20:22:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  | function handleRangeAggregation(expr: string, node: SyntaxNode, context: Context) { | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |   const nameNode = node.getChild(RangeOp); | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |   const funcName = getString(expr, nameNode); | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |   const number = node.getChild(NumberLezer); | 
					
						
							|  |  |  |   const logExpr = node.getChild(LogRangeExpr); | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |   const params = number !== null && number !== undefined ? [getString(expr, number)] : []; | 
					
						
							| 
									
										
										
										
											2023-01-18 22:54:18 +08:00
										 |  |  |   const range = logExpr?.getChild(Range); | 
					
						
							|  |  |  |   const rangeValue = range ? getString(expr, range) : null; | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-18 22:54:18 +08:00
										 |  |  |   if (rangeValue) { | 
					
						
							|  |  |  |     params.unshift(rangeValue.substring(1, rangeValue.length - 1)); | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const op = { | 
					
						
							|  |  |  |     id: funcName, | 
					
						
							|  |  |  |     params, | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (logExpr) { | 
					
						
							|  |  |  |     handleExpression(expr, logExpr, context); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return op; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function handleVectorAggregation(expr: string, node: SyntaxNode, context: Context) { | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |   const nameNode = node.getChild(VectorOp); | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |   let funcName = getString(expr, nameNode); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |   const grouping = node.getChild(Grouping); | 
					
						
							| 
									
										
										
										
											2022-06-12 03:36:44 +08:00
										 |  |  |   const params = []; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |   const numberNode = node.getChild(NumberLezer); | 
					
						
							| 
									
										
										
										
											2022-06-12 03:36:44 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (numberNode) { | 
					
						
							|  |  |  |     params.push(Number(getString(expr, numberNode))); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-04-05 00:11:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (grouping) { | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |     const byModifier = grouping.getChild(By); | 
					
						
							| 
									
										
										
										
											2022-04-05 00:11:15 +08:00
										 |  |  |     if (byModifier && funcName) { | 
					
						
							|  |  |  |       funcName = `__${funcName}_by`; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |     const withoutModifier = grouping.getChild(Without); | 
					
						
							| 
									
										
										
										
											2022-04-05 00:11:15 +08:00
										 |  |  |     if (withoutModifier) { | 
					
						
							|  |  |  |       funcName = `__${funcName}_without`; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |     params.push(...getAllByType(expr, grouping, Identifier)); | 
					
						
							| 
									
										
										
										
											2022-04-05 00:11:15 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |   const metricExpr = node.getChild(MetricExpr); | 
					
						
							| 
									
										
										
										
											2022-06-12 03:36:44 +08:00
										 |  |  |   const op: QueryBuilderOperation = { id: funcName, params }; | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (metricExpr) { | 
					
						
							|  |  |  |     handleExpression(expr, metricExpr, context); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return op; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-09 17:03:13 +08:00
										 |  |  | const operatorToOpName = binaryScalarDefs.reduce<Record<string, { id: string; comparison?: boolean }>>((acc, def) => { | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |   acc[def.sign] = { | 
					
						
							|  |  |  |     id: def.id, | 
					
						
							|  |  |  |     comparison: def.comparison, | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  |   return acc; | 
					
						
							| 
									
										
										
										
											2023-02-09 17:03:13 +08:00
										 |  |  | }, {}); | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Right now binary expressions can be represented in 2 way in visual query. As additional operation in case it is | 
					
						
							|  |  |  |  * just operation with scalar or it creates a binaryQuery when it's 2 queries. | 
					
						
							|  |  |  |  * @param expr | 
					
						
							|  |  |  |  * @param node | 
					
						
							|  |  |  |  * @param context | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | function handleBinary(expr: string, node: SyntaxNode, context: Context) { | 
					
						
							|  |  |  |   const visQuery = context.query; | 
					
						
							|  |  |  |   const left = node.firstChild!; | 
					
						
							|  |  |  |   const op = getString(expr, left.nextSibling); | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |   const binModifier = getBinaryModifier(expr, node.getChild(BinModifiers)); | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   const right = node.lastChild!; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const opDef = operatorToOpName[op]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-02 00:33:09 +08:00
										 |  |  |   const leftNumber = getLastChildWithSelector(left, 'MetricExpr.LiteralExpr.Number'); | 
					
						
							|  |  |  |   const rightNumber = getLastChildWithSelector(right, 'MetricExpr.LiteralExpr.Number'); | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |   const rightBinary = right.getChild(BinOpExpr); | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (leftNumber) { | 
					
						
							|  |  |  |     // TODO: this should be already handled in case parent is binary expression as it has to be added to parent
 | 
					
						
							|  |  |  |     //  if query starts with a number that isn't handled now.
 | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     // If this is binary we don't really know if there is a query or just chained scalars. So
 | 
					
						
							|  |  |  |     // we have to traverse a bit deeper to know
 | 
					
						
							|  |  |  |     handleExpression(expr, left, context); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (rightNumber) { | 
					
						
							|  |  |  |     visQuery.operations.push(makeBinOp(opDef, expr, right, !!binModifier?.isBool)); | 
					
						
							|  |  |  |   } else if (rightBinary) { | 
					
						
							|  |  |  |     // Due to the way binary ops are parsed we can get a binary operation on the right that starts with a number which
 | 
					
						
							|  |  |  |     // is a factor for a current binary operation. So we have to add it as an operation now.
 | 
					
						
							|  |  |  |     const leftMostChild = getLeftMostChild(right); | 
					
						
							| 
									
										
										
										
											2022-04-02 00:33:09 +08:00
										 |  |  |     if (leftMostChild?.name === 'Number') { | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |       visQuery.operations.push(makeBinOp(opDef, expr, leftMostChild, !!binModifier?.isBool)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // If we added the first number literal as operation here we still can continue and handle the rest as the first
 | 
					
						
							|  |  |  |     // number will be just skipped.
 | 
					
						
							|  |  |  |     handleExpression(expr, right, context); | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     visQuery.binaryQueries = visQuery.binaryQueries || []; | 
					
						
							|  |  |  |     const binQuery: LokiVisualQueryBinary = { | 
					
						
							|  |  |  |       operator: op, | 
					
						
							|  |  |  |       query: { | 
					
						
							|  |  |  |         labels: [], | 
					
						
							|  |  |  |         operations: [], | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     if (binModifier?.isMatcher) { | 
					
						
							|  |  |  |       binQuery.vectorMatchesType = binModifier.matchType; | 
					
						
							|  |  |  |       binQuery.vectorMatches = binModifier.matches; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     visQuery.binaryQueries.push(binQuery); | 
					
						
							|  |  |  |     handleExpression(expr, right, { | 
					
						
							|  |  |  |       query: binQuery.query, | 
					
						
							|  |  |  |       errors: context.errors, | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function getBinaryModifier( | 
					
						
							|  |  |  |   expr: string, | 
					
						
							|  |  |  |   node: SyntaxNode | null | 
					
						
							|  |  |  | ): | 
					
						
							|  |  |  |   | { isBool: true; isMatcher: false } | 
					
						
							|  |  |  |   | { isBool: false; isMatcher: true; matches: string; matchType: 'ignoring' | 'on' } | 
					
						
							|  |  |  |   | undefined { | 
					
						
							|  |  |  |   if (!node) { | 
					
						
							|  |  |  |     return undefined; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |   if (node.getChild(Bool)) { | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |     return { isBool: true, isMatcher: false }; | 
					
						
							|  |  |  |   } else { | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |     const matcher = node.getChild(OnOrIgnoring); | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |     if (!matcher) { | 
					
						
							|  |  |  |       // Not sure what this could be, maybe should be an error.
 | 
					
						
							|  |  |  |       return undefined; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |     const labels = getString(expr, matcher.getChild(GroupingLabels)?.getChild(GroupingLabelList)); | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |     return { | 
					
						
							|  |  |  |       isMatcher: true, | 
					
						
							|  |  |  |       isBool: false, | 
					
						
							|  |  |  |       matches: labels, | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |       matchType: matcher.getChild(On) ? 'on' : 'ignoring', | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function isIntervalVariableError(node: SyntaxNode) { | 
					
						
							| 
									
										
										
										
											2022-09-16 21:35:20 +08:00
										 |  |  |   return node?.parent?.type.id === Range; | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-26 22:45:33 +08:00
										 |  |  | export function handleQuotes(string: string) { | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |   if (string[0] === `"` && string[string.length - 1] === `"`) { | 
					
						
							| 
									
										
										
										
											2023-06-06 17:07:21 +08:00
										 |  |  |     return string | 
					
						
							|  |  |  |       .substring(1, string.length - 1) | 
					
						
							|  |  |  |       .replace(/\\"/g, '"') | 
					
						
							|  |  |  |       .replace(/\\\\/g, '\\'); | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |   } | 
					
						
							|  |  |  |   return string.replace(/`/g, ''); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							| 
									
										
										
										
											2022-04-02 00:33:09 +08:00
										 |  |  |  * Simple helper to traverse the syntax tree. Instead of node.getChild('foo')?.getChild('bar')?.getChild('baz') you | 
					
						
							|  |  |  |  * can write getChildWithSelector(node, 'foo.bar.baz') | 
					
						
							|  |  |  |  * @param node | 
					
						
							|  |  |  |  * @param selector | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2022-04-02 00:33:09 +08:00
										 |  |  | function getLastChildWithSelector(node: SyntaxNode, selector: string) { | 
					
						
							|  |  |  |   let child: SyntaxNode | null = node; | 
					
						
							|  |  |  |   const children = selector.split('.'); | 
					
						
							|  |  |  |   for (const s of children) { | 
					
						
							|  |  |  |     child = child.getChild(s); | 
					
						
							|  |  |  |     if (!child) { | 
					
						
							|  |  |  |       return null; | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-04-02 00:33:09 +08:00
										 |  |  |   } | 
					
						
							|  |  |  |   return child; | 
					
						
							| 
									
										
										
										
											2022-03-31 16:51:49 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2022-04-05 23:07:13 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Helper function to enrich error text with information that visual query builder doesn't support that logQL | 
					
						
							|  |  |  |  * @param expr | 
					
						
							|  |  |  |  * @param node | 
					
						
							|  |  |  |  * @param error | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | function createNotSupportedError(expr: string, node: SyntaxNode, error: string) { | 
					
						
							|  |  |  |   const err = makeError(expr, node); | 
					
						
							|  |  |  |   err.text = `${error}: ${err.text}`; | 
					
						
							|  |  |  |   return err; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-04-06 17:17:49 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | function isEmptyQuery(query: LokiVisualQuery) { | 
					
						
							|  |  |  |   if (query.labels.length === 0 && query.operations.length === 0) { | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return false; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-05-25 20:22:16 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | function handleDistinctFilter(expr: string, node: SyntaxNode, context: Context): QueryBuilderOperation { | 
					
						
							|  |  |  |   const labels: string[] = []; | 
					
						
							|  |  |  |   let exploringNode = node.getChild(DistinctLabel); | 
					
						
							|  |  |  |   while (exploringNode) { | 
					
						
							|  |  |  |     const label = getString(expr, exploringNode.getChild(Identifier)); | 
					
						
							|  |  |  |     if (label) { | 
					
						
							|  |  |  |       labels.push(label); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     exploringNode = exploringNode?.getChild(DistinctLabel); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   labels.reverse(); | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     id: LokiOperationId.Distinct, | 
					
						
							|  |  |  |     params: labels, | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } |