operations.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. import { binaryScalarOperations } from './binaryScalarOperations';
  2. import { LabelParamEditor } from './components/LabelParamEditor';
  3. import {
  4. defaultAddOperationHandler,
  5. functionRendererLeft,
  6. functionRendererRight,
  7. getPromAndLokiOperationDisplayName,
  8. getRangeVectorParamDef,
  9. rangeRendererLeftWithParams,
  10. rangeRendererRightWithParams,
  11. } from './shared/operationUtils';
  12. import {
  13. QueryBuilderOperation,
  14. QueryBuilderOperationDef,
  15. QueryWithOperations,
  16. VisualQueryModeller,
  17. } from './shared/types';
  18. import { PromOperationId, PromVisualQuery, PromVisualQueryOperationCategory } from './types';
  19. export function getOperationDefinitions(): QueryBuilderOperationDef[] {
  20. const list: QueryBuilderOperationDef[] = [
  21. {
  22. id: PromOperationId.HistogramQuantile,
  23. name: 'Histogram quantile',
  24. params: [{ name: 'Quantile', type: 'number', options: [0.99, 0.95, 0.9, 0.75, 0.5, 0.25] }],
  25. defaultParams: [0.9],
  26. category: PromVisualQueryOperationCategory.Functions,
  27. renderer: functionRendererLeft,
  28. addOperationHandler: defaultAddOperationHandler,
  29. },
  30. {
  31. id: PromOperationId.LabelReplace,
  32. name: 'Label replace',
  33. params: [
  34. { name: 'Destination label', type: 'string' },
  35. { name: 'Replacement', type: 'string' },
  36. { name: 'Source label', type: 'string' },
  37. { name: 'Regex', type: 'string' },
  38. ],
  39. category: PromVisualQueryOperationCategory.Functions,
  40. defaultParams: ['', '$1', '', '(.*)'],
  41. renderer: functionRendererRight,
  42. addOperationHandler: defaultAddOperationHandler,
  43. },
  44. {
  45. id: PromOperationId.Ln,
  46. name: 'Ln',
  47. params: [],
  48. defaultParams: [],
  49. category: PromVisualQueryOperationCategory.Functions,
  50. renderer: functionRendererLeft,
  51. addOperationHandler: defaultAddOperationHandler,
  52. },
  53. createRangeFunction(PromOperationId.Changes),
  54. createRangeFunction(PromOperationId.Rate, true),
  55. createRangeFunction(PromOperationId.Irate),
  56. createRangeFunction(PromOperationId.Increase, true),
  57. createRangeFunction(PromOperationId.Idelta),
  58. createRangeFunction(PromOperationId.Delta),
  59. createFunction({
  60. id: PromOperationId.HoltWinters,
  61. params: [
  62. getRangeVectorParamDef(),
  63. { name: 'Smoothing Factor', type: 'number' },
  64. { name: 'Trend Factor', type: 'number' },
  65. ],
  66. defaultParams: ['$__interval', 0.5, 0.5],
  67. alternativesKey: 'range function',
  68. category: PromVisualQueryOperationCategory.RangeFunctions,
  69. renderer: rangeRendererRightWithParams,
  70. addOperationHandler: addOperationWithRangeVector,
  71. changeTypeHandler: operationTypeChangedHandlerForRangeFunction,
  72. }),
  73. createFunction({
  74. id: PromOperationId.PredictLinear,
  75. params: [getRangeVectorParamDef(), { name: 'Seconds from now', type: 'number' }],
  76. defaultParams: ['$__interval', 60],
  77. alternativesKey: 'range function',
  78. category: PromVisualQueryOperationCategory.RangeFunctions,
  79. renderer: rangeRendererRightWithParams,
  80. addOperationHandler: addOperationWithRangeVector,
  81. changeTypeHandler: operationTypeChangedHandlerForRangeFunction,
  82. }),
  83. createFunction({
  84. id: PromOperationId.QuantileOverTime,
  85. params: [getRangeVectorParamDef(), { name: 'Quantile', type: 'number' }],
  86. defaultParams: ['$__interval', 0.5],
  87. alternativesKey: 'overtime function',
  88. category: PromVisualQueryOperationCategory.RangeFunctions,
  89. renderer: rangeRendererLeftWithParams,
  90. addOperationHandler: addOperationWithRangeVector,
  91. changeTypeHandler: operationTypeChangedHandlerForRangeFunction,
  92. }),
  93. ...binaryScalarOperations,
  94. {
  95. id: PromOperationId.NestedQuery,
  96. name: 'Binary operation with query',
  97. params: [],
  98. defaultParams: [],
  99. category: PromVisualQueryOperationCategory.BinaryOps,
  100. renderer: (model, def, innerExpr) => innerExpr,
  101. addOperationHandler: addNestedQueryHandler,
  102. },
  103. createFunction({ id: PromOperationId.Absent }),
  104. createFunction({
  105. id: PromOperationId.Acos,
  106. category: PromVisualQueryOperationCategory.Trigonometric,
  107. }),
  108. createFunction({
  109. id: PromOperationId.Acosh,
  110. category: PromVisualQueryOperationCategory.Trigonometric,
  111. }),
  112. createFunction({
  113. id: PromOperationId.Asin,
  114. category: PromVisualQueryOperationCategory.Trigonometric,
  115. }),
  116. createFunction({
  117. id: PromOperationId.Asinh,
  118. category: PromVisualQueryOperationCategory.Trigonometric,
  119. }),
  120. createFunction({
  121. id: PromOperationId.Atan,
  122. category: PromVisualQueryOperationCategory.Trigonometric,
  123. }),
  124. createFunction({
  125. id: PromOperationId.Atanh,
  126. category: PromVisualQueryOperationCategory.Trigonometric,
  127. }),
  128. createFunction({ id: PromOperationId.Ceil }),
  129. createFunction({
  130. id: PromOperationId.Clamp,
  131. name: 'Clamp',
  132. params: [
  133. { name: 'Minimum Scalar', type: 'number' },
  134. { name: 'Maximum Scalar', type: 'number' },
  135. ],
  136. defaultParams: [1, 1],
  137. }),
  138. createFunction({
  139. id: PromOperationId.ClampMax,
  140. params: [{ name: 'Maximum Scalar', type: 'number' }],
  141. defaultParams: [1],
  142. }),
  143. createFunction({
  144. id: PromOperationId.ClampMin,
  145. params: [{ name: 'Minimum Scalar', type: 'number' }],
  146. defaultParams: [1],
  147. }),
  148. createFunction({
  149. id: PromOperationId.Cos,
  150. category: PromVisualQueryOperationCategory.Trigonometric,
  151. }),
  152. createFunction({
  153. id: PromOperationId.Cosh,
  154. category: PromVisualQueryOperationCategory.Trigonometric,
  155. }),
  156. createFunction({
  157. id: PromOperationId.DayOfMonth,
  158. category: PromVisualQueryOperationCategory.Time,
  159. }),
  160. createFunction({
  161. id: PromOperationId.DayOfWeek,
  162. category: PromVisualQueryOperationCategory.Time,
  163. }),
  164. createFunction({
  165. id: PromOperationId.DaysInMonth,
  166. category: PromVisualQueryOperationCategory.Time,
  167. }),
  168. createFunction({ id: PromOperationId.Deg }),
  169. createRangeFunction(PromOperationId.Deriv),
  170. //
  171. createFunction({ id: PromOperationId.Exp }),
  172. createFunction({ id: PromOperationId.Floor }),
  173. createFunction({ id: PromOperationId.Group }),
  174. createFunction({ id: PromOperationId.Hour }),
  175. createFunction({
  176. id: PromOperationId.LabelJoin,
  177. params: [
  178. {
  179. name: 'Destination Label',
  180. type: 'string',
  181. editor: LabelParamEditor,
  182. },
  183. {
  184. name: 'Separator',
  185. type: 'string',
  186. },
  187. {
  188. name: 'Source Label',
  189. type: 'string',
  190. restParam: true,
  191. optional: true,
  192. editor: LabelParamEditor,
  193. },
  194. ],
  195. defaultParams: ['', ',', ''],
  196. renderer: labelJoinRenderer,
  197. addOperationHandler: labelJoinAddOperationHandler,
  198. }),
  199. createFunction({ id: PromOperationId.Log10 }),
  200. createFunction({ id: PromOperationId.Log2 }),
  201. createFunction({ id: PromOperationId.Minute }),
  202. createFunction({ id: PromOperationId.Month }),
  203. createFunction({
  204. id: PromOperationId.Pi,
  205. renderer: (model) => `${model.id}()`,
  206. }),
  207. createFunction({
  208. id: PromOperationId.Quantile,
  209. params: [{ name: 'Value', type: 'number' }],
  210. defaultParams: [1],
  211. renderer: functionRendererLeft,
  212. }),
  213. createFunction({ id: PromOperationId.Rad }),
  214. createRangeFunction(PromOperationId.Resets),
  215. createFunction({
  216. id: PromOperationId.Round,
  217. category: PromVisualQueryOperationCategory.Functions,
  218. params: [{ name: 'To Nearest', type: 'number' }],
  219. defaultParams: [1],
  220. }),
  221. createFunction({ id: PromOperationId.Scalar }),
  222. createFunction({ id: PromOperationId.Sgn }),
  223. createFunction({ id: PromOperationId.Sin, category: PromVisualQueryOperationCategory.Trigonometric }),
  224. createFunction({
  225. id: PromOperationId.Sinh,
  226. category: PromVisualQueryOperationCategory.Trigonometric,
  227. }),
  228. createFunction({ id: PromOperationId.Sort }),
  229. createFunction({ id: PromOperationId.SortDesc }),
  230. createFunction({ id: PromOperationId.Sqrt }),
  231. createFunction({ id: PromOperationId.Stddev }),
  232. createFunction({
  233. id: PromOperationId.Tan,
  234. category: PromVisualQueryOperationCategory.Trigonometric,
  235. }),
  236. createFunction({
  237. id: PromOperationId.Tanh,
  238. category: PromVisualQueryOperationCategory.Trigonometric,
  239. }),
  240. createFunction({
  241. id: PromOperationId.Time,
  242. renderer: (model) => `${model.id}()`,
  243. }),
  244. createFunction({ id: PromOperationId.Timestamp }),
  245. createFunction({
  246. id: PromOperationId.Vector,
  247. params: [{ name: 'Value', type: 'number' }],
  248. defaultParams: [1],
  249. renderer: (model) => `${model.id}(${model.params[0]})`,
  250. }),
  251. createFunction({ id: PromOperationId.Year }),
  252. ];
  253. return list;
  254. }
  255. export function createFunction(definition: Partial<QueryBuilderOperationDef>): QueryBuilderOperationDef {
  256. return {
  257. ...definition,
  258. id: definition.id!,
  259. name: definition.name ?? getPromAndLokiOperationDisplayName(definition.id!),
  260. params: definition.params ?? [],
  261. defaultParams: definition.defaultParams ?? [],
  262. category: definition.category ?? PromVisualQueryOperationCategory.Functions,
  263. renderer: definition.renderer ?? (definition.params ? functionRendererRight : functionRendererLeft),
  264. addOperationHandler: definition.addOperationHandler ?? defaultAddOperationHandler,
  265. };
  266. }
  267. export function createRangeFunction(name: string, withRateInterval = false): QueryBuilderOperationDef {
  268. return {
  269. id: name,
  270. name: getPromAndLokiOperationDisplayName(name),
  271. params: [getRangeVectorParamDef(withRateInterval)],
  272. defaultParams: [withRateInterval ? '$__rate_interval' : '$__interval'],
  273. alternativesKey: 'range function',
  274. category: PromVisualQueryOperationCategory.RangeFunctions,
  275. renderer: operationWithRangeVectorRenderer,
  276. addOperationHandler: addOperationWithRangeVector,
  277. changeTypeHandler: operationTypeChangedHandlerForRangeFunction,
  278. };
  279. }
  280. function operationTypeChangedHandlerForRangeFunction(
  281. operation: QueryBuilderOperation,
  282. newDef: QueryBuilderOperationDef
  283. ) {
  284. // validate current parameter
  285. if (operation.params[0] === '$__rate_interval' && newDef.defaultParams[0] !== '$__rate_interval') {
  286. operation.params = newDef.defaultParams;
  287. } else if (operation.params[0] === '$__interval' && newDef.defaultParams[0] !== '$__interval') {
  288. operation.params = newDef.defaultParams;
  289. }
  290. return operation;
  291. }
  292. export function operationWithRangeVectorRenderer(
  293. model: QueryBuilderOperation,
  294. def: QueryBuilderOperationDef,
  295. innerExpr: string
  296. ) {
  297. let rangeVector = (model.params ?? [])[0] ?? '5m';
  298. return `${def.id}(${innerExpr}[${rangeVector}])`;
  299. }
  300. /**
  301. * Since there can only be one operation with range vector this will replace the current one (if one was added )
  302. */
  303. export function addOperationWithRangeVector(
  304. def: QueryBuilderOperationDef,
  305. query: PromVisualQuery,
  306. modeller: VisualQueryModeller
  307. ) {
  308. const newOperation: QueryBuilderOperation = {
  309. id: def.id,
  310. params: def.defaultParams,
  311. };
  312. if (query.operations.length > 0) {
  313. // If operation exists it has to be in the registry so no point to check if it was found
  314. const firstOp = modeller.getOperationDef(query.operations[0].id)!;
  315. if (firstOp.addOperationHandler === addOperationWithRangeVector) {
  316. return {
  317. ...query,
  318. operations: [newOperation, ...query.operations.slice(1)],
  319. };
  320. }
  321. }
  322. return {
  323. ...query,
  324. operations: [newOperation, ...query.operations],
  325. };
  326. }
  327. function addNestedQueryHandler(def: QueryBuilderOperationDef, query: PromVisualQuery): PromVisualQuery {
  328. return {
  329. ...query,
  330. binaryQueries: [
  331. ...(query.binaryQueries ?? []),
  332. {
  333. operator: '/',
  334. query,
  335. },
  336. ],
  337. };
  338. }
  339. function labelJoinRenderer(model: QueryBuilderOperation, def: QueryBuilderOperationDef, innerExpr: string) {
  340. if (typeof model.params[1] !== 'string') {
  341. throw 'The separator must be a string';
  342. }
  343. const separator = `"${model.params[1]}"`;
  344. return `${model.id}(${innerExpr}, "${model.params[0]}", ${separator}, "${model.params.slice(2).join(separator)}")`;
  345. }
  346. function labelJoinAddOperationHandler<T extends QueryWithOperations>(def: QueryBuilderOperationDef, query: T) {
  347. const newOperation: QueryBuilderOperation = {
  348. id: def.id,
  349. params: def.defaultParams,
  350. };
  351. return {
  352. ...query,
  353. operations: [...query.operations, newOperation],
  354. };
  355. }