query_part.ts 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. ///<reference path="../../../headers/common.d.ts" />
  2. import _ = require('lodash');
  3. var index = [];
  4. var categories = {
  5. Aggregations: [],
  6. Transformations: [],
  7. Math: [],
  8. Aliasing: [],
  9. Fields: [],
  10. };
  11. var groupByTimeFunctions = [];
  12. class QueryPartDef {
  13. type: string;
  14. params: any[];
  15. defaultParams: any[];
  16. renderer: any;
  17. category: any;
  18. addStrategy: any;
  19. constructor(options: any) {
  20. this.type = options.type;
  21. this.params = options.params;
  22. this.defaultParams = options.defaultParams;
  23. this.renderer = options.renderer;
  24. this.category = options.category;
  25. this.addStrategy = options.addStrategy;
  26. }
  27. static register(options: any) {
  28. index[options.type] = new QueryPartDef(options);
  29. options.category.push(index[options.type]);
  30. }
  31. }
  32. function functionRenderer(part, innerExpr) {
  33. var str = part.def.type + '(';
  34. var parameters = _.map(part.params, (value, index) => {
  35. var paramType = part.def.params[index];
  36. if (paramType.type === 'time') {
  37. if (value === 'auto') {
  38. value = '$interval';
  39. }
  40. }
  41. if (paramType.quote === 'single') {
  42. return "'" + value + "'";
  43. } else if (paramType.quote === 'double') {
  44. return '"' + value + '"';
  45. }
  46. return value;
  47. });
  48. if (innerExpr) {
  49. parameters.unshift(innerExpr);
  50. }
  51. return str + parameters.join(', ') + ')';
  52. }
  53. function aliasRenderer(part, innerExpr) {
  54. return innerExpr + ' AS ' + '"' + part.params[0] + '"';
  55. }
  56. function suffixRenderer(part, innerExpr) {
  57. return innerExpr + ' ' + part.params[0];
  58. }
  59. function identityRenderer(part, innerExpr) {
  60. return part.params[0];
  61. }
  62. function quotedIdentityRenderer(part, innerExpr) {
  63. return '"' + part.params[0] + '"';
  64. }
  65. function fieldRenderer(part, innerExpr) {
  66. if (part.params[0] === '*') {
  67. return '*';
  68. }
  69. return '"' + part.params[0] + '"';
  70. }
  71. function replaceAggregationAddStrategy(selectParts, partModel) {
  72. // look for existing aggregation
  73. for (var i = 0; i < selectParts.length; i++) {
  74. var part = selectParts[i];
  75. if (part.def.category === categories.Aggregations) {
  76. selectParts[i] = partModel;
  77. return;
  78. }
  79. }
  80. selectParts.splice(1, 0, partModel);
  81. }
  82. function addTransformationStrategy(selectParts, partModel) {
  83. var i;
  84. // look for index to add transformation
  85. for (i = 0; i < selectParts.length; i++) {
  86. var part = selectParts[i];
  87. if (part.def.category === categories.Math || part.def.category === categories.Aliasing) {
  88. break;
  89. }
  90. }
  91. selectParts.splice(i, 0, partModel);
  92. }
  93. function addMathStrategy(selectParts, partModel) {
  94. var partCount = selectParts.length;
  95. if (partCount > 0) {
  96. // if last is math, replace it
  97. if (selectParts[partCount-1].def.type === 'math') {
  98. selectParts[partCount-1] = partModel;
  99. return;
  100. }
  101. // if next to last is math, replace it
  102. if (selectParts[partCount-2].def.type === 'math') {
  103. selectParts[partCount-2] = partModel;
  104. return;
  105. }
  106. // if last is alias add it before
  107. else if (selectParts[partCount-1].def.type === 'alias') {
  108. selectParts.splice(partCount-1, 0, partModel);
  109. return;
  110. }
  111. }
  112. selectParts.push(partModel);
  113. }
  114. function addAliasStrategy(selectParts, partModel) {
  115. var partCount = selectParts.length;
  116. if (partCount > 0) {
  117. // if last is alias, replace it
  118. if (selectParts[partCount-1].def.type === 'alias') {
  119. selectParts[partCount-1] = partModel;
  120. return;
  121. }
  122. }
  123. selectParts.push(partModel);
  124. }
  125. function addFieldStrategy(selectParts, partModel, query) {
  126. // copy all parts
  127. var parts = _.map(selectParts, function(part: any) {
  128. return new QueryPart({type: part.def.type, params: _.clone(part.params)});
  129. });
  130. query.selectModels.push(parts);
  131. }
  132. QueryPartDef.register({
  133. type: 'field',
  134. addStrategy: addFieldStrategy,
  135. category: categories.Fields,
  136. params: [{type: 'field', dynamicLookup: true}],
  137. defaultParams: ['value'],
  138. renderer: fieldRenderer,
  139. });
  140. QueryPartDef.register({
  141. type: 'mean',
  142. addStrategy: replaceAggregationAddStrategy,
  143. category: categories.Aggregations,
  144. params: [],
  145. defaultParams: [],
  146. renderer: functionRenderer,
  147. });
  148. QueryPartDef.register({
  149. type: 'sum',
  150. addStrategy: replaceAggregationAddStrategy,
  151. category: categories.Aggregations,
  152. params: [],
  153. defaultParams: [],
  154. renderer: functionRenderer,
  155. });
  156. QueryPartDef.register({
  157. type: 'derivative',
  158. addStrategy: addTransformationStrategy,
  159. category: categories.Transformations,
  160. params: [{ name: "duration", type: "interval", options: ['1s', '10s', '1m', '5min', '10m', '15m', '1h']}],
  161. defaultParams: ['10s'],
  162. renderer: functionRenderer,
  163. });
  164. QueryPartDef.register({
  165. type: 'time',
  166. category: groupByTimeFunctions,
  167. params: [{ name: "interval", type: "time", options: ['auto', '1s', '10s', '1m', '5m', '10m', '15m', '1h'] }],
  168. defaultParams: ['auto'],
  169. renderer: functionRenderer,
  170. });
  171. QueryPartDef.register({
  172. type: 'fill',
  173. category: groupByTimeFunctions,
  174. params: [{ name: "fill", type: "string", options: ['none', 'null', '0', 'previous'] }],
  175. defaultParams: ['null'],
  176. renderer: functionRenderer,
  177. });
  178. QueryPartDef.register({
  179. type: 'tag',
  180. category: groupByTimeFunctions,
  181. params: [{name: 'tag', type: 'string', dynamicLookup: true}],
  182. defaultParams: ['tag'],
  183. renderer: fieldRenderer,
  184. });
  185. QueryPartDef.register({
  186. type: 'math',
  187. addStrategy: addMathStrategy,
  188. category: categories.Math,
  189. params: [{ name: "expr", type: "string"}],
  190. defaultParams: [' / 100'],
  191. renderer: suffixRenderer,
  192. });
  193. QueryPartDef.register({
  194. type: 'alias',
  195. addStrategy: addAliasStrategy,
  196. category: categories.Aliasing,
  197. params: [{ name: "name", type: "string", quote: 'double'}],
  198. defaultParams: ['alias'],
  199. renderMode: 'suffix',
  200. renderer: aliasRenderer,
  201. });
  202. class QueryPart {
  203. part: any;
  204. def: QueryPartDef;
  205. params: any[];
  206. text: string;
  207. constructor(part: any) {
  208. this.part = part;
  209. this.def = index[part.type];
  210. if (!this.def) {
  211. throw {message: 'Could not find query part ' + part.type};
  212. }
  213. part.params = part.params || _.clone(this.def.defaultParams);
  214. this.params = part.params;
  215. this.updateText();
  216. }
  217. render(innerExpr: string) {
  218. return this.def.renderer(this, innerExpr);
  219. }
  220. hasMultipleParamsInString (strValue, index) {
  221. if (strValue.indexOf(',') === -1) {
  222. return false;
  223. }
  224. return this.def.params[index + 1] && this.def.params[index + 1].optional;
  225. }
  226. updateParam (strValue, index) {
  227. // handle optional parameters
  228. // if string contains ',' and next param is optional, split and update both
  229. if (this.hasMultipleParamsInString(strValue, index)) {
  230. _.each(strValue.split(','), function(partVal: string, idx) {
  231. this.updateParam(partVal.trim(), idx);
  232. }, this);
  233. return;
  234. }
  235. if (strValue === '' && this.def.params[index].optional) {
  236. this.params.splice(index, 1);
  237. }
  238. else {
  239. this.params[index] = strValue;
  240. }
  241. this.part.params = this.params;
  242. this.updateText();
  243. }
  244. updateText() {
  245. if (this.params.length === 0) {
  246. this.text = this.def.type + '()';
  247. return;
  248. }
  249. var text = this.def.type + '(';
  250. text += this.params.join(', ');
  251. text += ')';
  252. this.text = text;
  253. }
  254. }
  255. export = {
  256. create: function(part): any {
  257. return new QueryPart(part);
  258. },
  259. getCategories: function() {
  260. return categories;
  261. }
  262. };