query_part.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. import { map, clone } from 'lodash';
  2. import { QueryPartDef, QueryPart, functionRenderer, suffixRenderer } from 'app/features/alerting/state/query_part';
  3. const index: any[] = [];
  4. const categories: any = {
  5. Aggregations: [],
  6. Selectors: [],
  7. Transformations: [],
  8. Predictors: [],
  9. Math: [],
  10. Aliasing: [],
  11. Fields: [],
  12. };
  13. function createPart(part: any): any {
  14. const def = index[part.type];
  15. if (!def) {
  16. throw { message: 'Could not find query part ' + part.type };
  17. }
  18. return new QueryPart(part, def);
  19. }
  20. function register(options: any) {
  21. index[options.type] = new QueryPartDef(options);
  22. options.category.push(index[options.type]);
  23. }
  24. const groupByTimeFunctions: any[] = [];
  25. function aliasRenderer(part: { params: string[] }, innerExpr: string) {
  26. return innerExpr + ' AS ' + '"' + part.params[0] + '"';
  27. }
  28. function fieldRenderer(part: { params: string[] }, innerExpr: any) {
  29. if (part.params[0] === '*') {
  30. return '*';
  31. }
  32. return '"' + part.params[0] + '"';
  33. }
  34. function replaceAggregationAddStrategy(selectParts: any[], partModel: { def: { type: string } }) {
  35. // look for existing aggregation
  36. for (let i = 0; i < selectParts.length; i++) {
  37. const part = selectParts[i];
  38. if (part.def.category === categories.Aggregations) {
  39. if (part.def.type === partModel.def.type) {
  40. return;
  41. }
  42. // count distinct is allowed
  43. if (part.def.type === 'count' && partModel.def.type === 'distinct') {
  44. break;
  45. }
  46. // remove next aggregation if distinct was replaced
  47. if (part.def.type === 'distinct') {
  48. const morePartsAvailable = selectParts.length >= i + 2;
  49. if (partModel.def.type !== 'count' && morePartsAvailable) {
  50. const nextPart = selectParts[i + 1];
  51. if (nextPart.def.category === categories.Aggregations) {
  52. selectParts.splice(i + 1, 1);
  53. }
  54. } else if (partModel.def.type === 'count') {
  55. if (!morePartsAvailable || selectParts[i + 1].def.type !== 'count') {
  56. selectParts.splice(i + 1, 0, partModel);
  57. }
  58. return;
  59. }
  60. }
  61. selectParts[i] = partModel;
  62. return;
  63. }
  64. if (part.def.category === categories.Selectors) {
  65. selectParts[i] = partModel;
  66. return;
  67. }
  68. }
  69. selectParts.splice(1, 0, partModel);
  70. }
  71. function addTransformationStrategy(selectParts: any[], partModel: any) {
  72. let i;
  73. // look for index to add transformation
  74. for (i = 0; i < selectParts.length; i++) {
  75. const part = selectParts[i];
  76. if (part.def.category === categories.Math || part.def.category === categories.Aliasing) {
  77. break;
  78. }
  79. }
  80. selectParts.splice(i, 0, partModel);
  81. }
  82. function addMathStrategy(selectParts: any[], partModel: any) {
  83. const partCount = selectParts.length;
  84. if (partCount > 0) {
  85. // if last is math, replace it
  86. if (selectParts[partCount - 1].def.type === 'math') {
  87. selectParts[partCount - 1] = partModel;
  88. return;
  89. }
  90. // if next to last is math, replace it
  91. if (partCount > 1 && selectParts[partCount - 2].def.type === 'math') {
  92. selectParts[partCount - 2] = partModel;
  93. return;
  94. } else if (selectParts[partCount - 1].def.type === 'alias') {
  95. // if last is alias add it before
  96. selectParts.splice(partCount - 1, 0, partModel);
  97. return;
  98. }
  99. }
  100. selectParts.push(partModel);
  101. }
  102. function addAliasStrategy(selectParts: any[], partModel: any) {
  103. const partCount = selectParts.length;
  104. if (partCount > 0) {
  105. // if last is alias, replace it
  106. if (selectParts[partCount - 1].def.type === 'alias') {
  107. selectParts[partCount - 1] = partModel;
  108. return;
  109. }
  110. }
  111. selectParts.push(partModel);
  112. }
  113. function addFieldStrategy(selectParts: any, partModel: any, query: { selectModels: any[][] }) {
  114. // copy all parts
  115. const parts = map(selectParts, (part: any) => {
  116. return createPart({ type: part.def.type, params: clone(part.params) });
  117. });
  118. query.selectModels.push(parts);
  119. }
  120. register({
  121. type: 'field',
  122. addStrategy: addFieldStrategy,
  123. category: categories.Fields,
  124. params: [{ type: 'field', dynamicLookup: true }],
  125. defaultParams: ['value'],
  126. renderer: fieldRenderer,
  127. });
  128. // Aggregations
  129. register({
  130. type: 'count',
  131. addStrategy: replaceAggregationAddStrategy,
  132. category: categories.Aggregations,
  133. params: [],
  134. defaultParams: [],
  135. renderer: functionRenderer,
  136. });
  137. register({
  138. type: 'distinct',
  139. addStrategy: replaceAggregationAddStrategy,
  140. category: categories.Aggregations,
  141. params: [],
  142. defaultParams: [],
  143. renderer: functionRenderer,
  144. });
  145. register({
  146. type: 'integral',
  147. addStrategy: replaceAggregationAddStrategy,
  148. category: categories.Aggregations,
  149. params: [],
  150. defaultParams: [],
  151. renderer: functionRenderer,
  152. });
  153. register({
  154. type: 'mean',
  155. addStrategy: replaceAggregationAddStrategy,
  156. category: categories.Aggregations,
  157. params: [],
  158. defaultParams: [],
  159. renderer: functionRenderer,
  160. });
  161. register({
  162. type: 'median',
  163. addStrategy: replaceAggregationAddStrategy,
  164. category: categories.Aggregations,
  165. params: [],
  166. defaultParams: [],
  167. renderer: functionRenderer,
  168. });
  169. register({
  170. type: 'mode',
  171. addStrategy: replaceAggregationAddStrategy,
  172. category: categories.Aggregations,
  173. params: [],
  174. defaultParams: [],
  175. renderer: functionRenderer,
  176. });
  177. register({
  178. type: 'sum',
  179. addStrategy: replaceAggregationAddStrategy,
  180. category: categories.Aggregations,
  181. params: [],
  182. defaultParams: [],
  183. renderer: functionRenderer,
  184. });
  185. // transformations
  186. register({
  187. type: 'derivative',
  188. addStrategy: addTransformationStrategy,
  189. category: categories.Transformations,
  190. params: [
  191. {
  192. name: 'duration',
  193. type: 'interval',
  194. options: ['1s', '10s', '1m', '5m', '10m', '15m', '1h'],
  195. },
  196. ],
  197. defaultParams: ['10s'],
  198. renderer: functionRenderer,
  199. });
  200. register({
  201. type: 'spread',
  202. addStrategy: addTransformationStrategy,
  203. category: categories.Transformations,
  204. params: [],
  205. defaultParams: [],
  206. renderer: functionRenderer,
  207. });
  208. register({
  209. type: 'non_negative_derivative',
  210. addStrategy: addTransformationStrategy,
  211. category: categories.Transformations,
  212. params: [
  213. {
  214. name: 'duration',
  215. type: 'interval',
  216. options: ['1s', '10s', '1m', '5m', '10m', '15m', '1h'],
  217. },
  218. ],
  219. defaultParams: ['10s'],
  220. renderer: functionRenderer,
  221. });
  222. register({
  223. type: 'difference',
  224. addStrategy: addTransformationStrategy,
  225. category: categories.Transformations,
  226. params: [],
  227. defaultParams: [],
  228. renderer: functionRenderer,
  229. });
  230. register({
  231. type: 'non_negative_difference',
  232. addStrategy: addTransformationStrategy,
  233. category: categories.Transformations,
  234. params: [],
  235. defaultParams: [],
  236. renderer: functionRenderer,
  237. });
  238. register({
  239. type: 'moving_average',
  240. addStrategy: addTransformationStrategy,
  241. category: categories.Transformations,
  242. params: [{ name: 'window', type: 'int', options: [5, 10, 20, 30, 40] }],
  243. defaultParams: [10],
  244. renderer: functionRenderer,
  245. });
  246. register({
  247. type: 'cumulative_sum',
  248. addStrategy: addTransformationStrategy,
  249. category: categories.Transformations,
  250. params: [],
  251. defaultParams: [],
  252. renderer: functionRenderer,
  253. });
  254. register({
  255. type: 'stddev',
  256. addStrategy: addTransformationStrategy,
  257. category: categories.Transformations,
  258. params: [],
  259. defaultParams: [],
  260. renderer: functionRenderer,
  261. });
  262. register({
  263. type: 'time',
  264. category: groupByTimeFunctions,
  265. params: [
  266. {
  267. name: 'interval',
  268. type: 'time',
  269. options: ['$__interval', '1s', '10s', '1m', '5m', '10m', '15m', '1h'],
  270. },
  271. ],
  272. defaultParams: ['$__interval'],
  273. renderer: functionRenderer,
  274. });
  275. register({
  276. type: 'fill',
  277. category: groupByTimeFunctions,
  278. params: [
  279. {
  280. name: 'fill',
  281. type: 'string',
  282. options: ['none', 'null', '0', 'previous', 'linear'],
  283. },
  284. ],
  285. defaultParams: ['null'],
  286. renderer: functionRenderer,
  287. });
  288. register({
  289. type: 'elapsed',
  290. addStrategy: addTransformationStrategy,
  291. category: categories.Transformations,
  292. params: [
  293. {
  294. name: 'duration',
  295. type: 'interval',
  296. options: ['1s', '10s', '1m', '5m', '10m', '15m', '1h'],
  297. },
  298. ],
  299. defaultParams: ['10s'],
  300. renderer: functionRenderer,
  301. });
  302. // predictions
  303. register({
  304. type: 'holt_winters',
  305. addStrategy: addTransformationStrategy,
  306. category: categories.Predictors,
  307. params: [
  308. { name: 'number', type: 'int', options: [5, 10, 20, 30, 40] },
  309. { name: 'season', type: 'int', options: [0, 1, 2, 5, 10] },
  310. ],
  311. defaultParams: [10, 2],
  312. renderer: functionRenderer,
  313. });
  314. register({
  315. type: 'holt_winters_with_fit',
  316. addStrategy: addTransformationStrategy,
  317. category: categories.Predictors,
  318. params: [
  319. { name: 'number', type: 'int', options: [5, 10, 20, 30, 40] },
  320. { name: 'season', type: 'int', options: [0, 1, 2, 5, 10] },
  321. ],
  322. defaultParams: [10, 2],
  323. renderer: functionRenderer,
  324. });
  325. // Selectors
  326. register({
  327. type: 'bottom',
  328. addStrategy: replaceAggregationAddStrategy,
  329. category: categories.Selectors,
  330. params: [{ name: 'count', type: 'int' }],
  331. defaultParams: [3],
  332. renderer: functionRenderer,
  333. });
  334. register({
  335. type: 'first',
  336. addStrategy: replaceAggregationAddStrategy,
  337. category: categories.Selectors,
  338. params: [],
  339. defaultParams: [],
  340. renderer: functionRenderer,
  341. });
  342. register({
  343. type: 'last',
  344. addStrategy: replaceAggregationAddStrategy,
  345. category: categories.Selectors,
  346. params: [],
  347. defaultParams: [],
  348. renderer: functionRenderer,
  349. });
  350. register({
  351. type: 'max',
  352. addStrategy: replaceAggregationAddStrategy,
  353. category: categories.Selectors,
  354. params: [],
  355. defaultParams: [],
  356. renderer: functionRenderer,
  357. });
  358. register({
  359. type: 'min',
  360. addStrategy: replaceAggregationAddStrategy,
  361. category: categories.Selectors,
  362. params: [],
  363. defaultParams: [],
  364. renderer: functionRenderer,
  365. });
  366. register({
  367. type: 'percentile',
  368. addStrategy: replaceAggregationAddStrategy,
  369. category: categories.Selectors,
  370. params: [{ name: 'nth', type: 'int' }],
  371. defaultParams: [95],
  372. renderer: functionRenderer,
  373. });
  374. register({
  375. type: 'top',
  376. addStrategy: replaceAggregationAddStrategy,
  377. category: categories.Selectors,
  378. params: [{ name: 'count', type: 'int' }],
  379. defaultParams: [3],
  380. renderer: functionRenderer,
  381. });
  382. register({
  383. type: 'tag',
  384. category: groupByTimeFunctions,
  385. params: [{ name: 'tag', type: 'string', dynamicLookup: true }],
  386. defaultParams: ['tag'],
  387. renderer: fieldRenderer,
  388. });
  389. register({
  390. type: 'math',
  391. addStrategy: addMathStrategy,
  392. category: categories.Math,
  393. params: [{ name: 'expr', type: 'string' }],
  394. defaultParams: [' / 100'],
  395. renderer: suffixRenderer,
  396. });
  397. register({
  398. type: 'alias',
  399. addStrategy: addAliasStrategy,
  400. category: categories.Aliasing,
  401. params: [{ name: 'name', type: 'string', quote: 'double' }],
  402. defaultParams: ['alias'],
  403. renderMode: 'suffix',
  404. renderer: aliasRenderer,
  405. });
  406. export default {
  407. create: createPart,
  408. getCategories: () => {
  409. return categories;
  410. },
  411. replaceAggregationAdd: replaceAggregationAddStrategy,
  412. };