query_utils.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. import { escapeRegExp } from 'lodash';
  2. import { PIPE_PARSERS } from './syntax';
  3. import { LokiQuery, LokiQueryType } from './types';
  4. export function formatQuery(selector: string | undefined): string {
  5. return `${selector || ''}`.trim();
  6. }
  7. /**
  8. * Returns search terms from a LogQL query.
  9. * E.g., `{} |= foo |=bar != baz` returns `['foo', 'bar']`.
  10. */
  11. export function getHighlighterExpressionsFromQuery(input: string): string[] {
  12. let expression = input;
  13. const results = [];
  14. // Consume filter expression from left to right
  15. while (expression) {
  16. const filterStart = expression.search(/\|=|\|~|!=|!~/);
  17. // Nothing more to search
  18. if (filterStart === -1) {
  19. break;
  20. }
  21. // Drop terms for negative filters
  22. const filterOperator = expression.slice(filterStart, filterStart + 2);
  23. const skip = expression.slice(filterStart).search(/!=|!~/) === 0;
  24. expression = expression.slice(filterStart + 2);
  25. if (skip) {
  26. continue;
  27. }
  28. // Check if there is more chained, by just looking for the next pipe-operator
  29. const filterEnd = expression.search(/\|/);
  30. let filterTerm;
  31. if (filterEnd === -1) {
  32. filterTerm = expression.trim();
  33. } else {
  34. filterTerm = expression.slice(0, filterEnd).trim();
  35. expression = expression.slice(filterEnd);
  36. }
  37. const quotedTerm = filterTerm.match(/"(.*?)"/);
  38. const backtickedTerm = filterTerm.match(/`(.*?)`/);
  39. const term = quotedTerm || backtickedTerm;
  40. if (term) {
  41. const unwrappedFilterTerm = term[1];
  42. const regexOperator = filterOperator === '|~';
  43. let resultTerm = '';
  44. // Only filter expressions with |~ operator are treated as regular expressions
  45. if (regexOperator) {
  46. // When using backticks, Loki doesn't require to escape special characters and we can just push regular expression to highlights array
  47. // When using quotes, we have extra backslash escaping and we need to replace \\ with \
  48. resultTerm = backtickedTerm ? unwrappedFilterTerm : unwrappedFilterTerm.replace(/\\\\/g, '\\');
  49. } else {
  50. // We need to escape this string so it is not matched as regular expression
  51. resultTerm = escapeRegExp(unwrappedFilterTerm);
  52. }
  53. if (resultTerm) {
  54. results.push(resultTerm);
  55. }
  56. } else {
  57. return results;
  58. }
  59. }
  60. return results;
  61. }
  62. export function queryHasPipeParser(expr: string): boolean {
  63. const parsers = PIPE_PARSERS.map((parser) => `${parser.label}`).join('|');
  64. const regexp = new RegExp(`\\\|\\\s?(${parsers})`);
  65. return regexp.test(expr);
  66. }
  67. export function addParsedLabelToQuery(expr: string, key: string, value: string | number, operator: string) {
  68. return expr + ` | ${key}${operator}"${value.toString()}"`;
  69. }
  70. // we are migrating from `.instant` and `.range` to `.queryType`
  71. // this function returns a new query object that:
  72. // - has `.queryType`
  73. // - does not have `.instant`
  74. // - does not have `.range`
  75. export function getNormalizedLokiQuery(query: LokiQuery): LokiQuery {
  76. // if queryType field contains invalid data we behave as if the queryType is empty
  77. const { queryType } = query;
  78. const hasValidQueryType =
  79. queryType === LokiQueryType.Range || queryType === LokiQueryType.Instant || queryType === LokiQueryType.Stream;
  80. // if queryType exists, it is respected
  81. if (hasValidQueryType) {
  82. const { instant, range, ...rest } = query;
  83. return rest;
  84. }
  85. // if no queryType, and instant===true, it's instant
  86. if (query.instant === true) {
  87. const { instant, range, ...rest } = query;
  88. return { ...rest, queryType: LokiQueryType.Instant };
  89. }
  90. // otherwise it is range
  91. const { instant, range, ...rest } = query;
  92. return { ...rest, queryType: LokiQueryType.Range };
  93. }