query_part.ts 6.3 KB

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