Home Manual Reference Source Test

src/results.js

  1. import { isoOrTimeToDate } from "./grammar";
  2. /**
  3. * A ResultError is thrown when a query generates errorful results from Influx.
  4. */
  5. export class ResultError extends Error {
  6. constructor(message) {
  7. super();
  8. this.message = `Error from InfluxDB: ${message}`;
  9. }
  10. }
  11. function groupMethod(matcher) {
  12. // We do a tiny bit of 'custom' deep equality checking here, taking
  13. // advantage of the fact that the tag keys are consistent across all
  14. // series results. This lets us match groupings much more efficiently,
  15. // ~6000x faster than the fastest vanilla equality checker (lodash)
  16. // when operating on large (~100,000 grouping) sets.
  17. const srcKeys = this.groupsTagsKeys;
  18. const dstKeys = Object.keys(matcher);
  19. if (srcKeys.length === 0 || srcKeys.length !== dstKeys.length) {
  20. return [];
  21. }
  22. L: for (let row of this.groupRows) {
  23. // eslint-disable-line no-labels
  24. for (let src of srcKeys) {
  25. if (row.tags[src] !== matcher[src]) {
  26. continue L; // eslint-disable-line no-labels
  27. }
  28. }
  29. return row.rows;
  30. }
  31. return [];
  32. }
  33. function groupsMethod() {
  34. return this.groupRows;
  35. }
  36. /**
  37. * Inner parsing function which unpacks the series into a table and attaches
  38. * methods to the array. This is quite optimized and a bit of a mess to read,
  39. * but it's all fairly easy procedural logic.
  40. *
  41. * We do this instead of subclassing Array since subclassing has some
  42. * undesirable side-effects. For example, calling .slice() on the array
  43. * makes it impossible to preserve groups as would be necessary if it's
  44. * subclassed.
  45. */
  46. function parseInner(series = [], precision) {
  47. const results = [];
  48. results.groupsTagsKeys =
  49. series.length && series[0].tags ? Object.keys(series[0].tags) : [];
  50. const tags = results.groupsTagsKeys;
  51. let nextGroup = [];
  52. results.groupRows = new Array(series.length); // Tslint:disable-line
  53. for (let i = 0; i < series.length; i += 1, results.length) {
  54. const { columns = [], values = [] } = series[i];
  55. for (let value of values) {
  56. const obj = {};
  57. for (let j = 0; j < columns.length; j += 1) {
  58. if (columns[j] === "time") {
  59. obj.time = isoOrTimeToDate(value[j], precision);
  60. }
  61. else {
  62. obj[columns[j]] = value[j];
  63. }
  64. }
  65. for (let tag of tags) {
  66. obj[tag] = series[i].tags[tag];
  67. }
  68. results.push(obj);
  69. nextGroup.push(obj);
  70. }
  71. results.groupRows[i] = {
  72. name: series[i].name,
  73. rows: nextGroup,
  74. tags: series[i].tags || {},
  75. };
  76. nextGroup = [];
  77. }
  78. results.group = groupMethod;
  79. results.groups = groupsMethod;
  80. return results;
  81. }
  82. /**
  83. * Checks if there are any errors in the IResponse and, if so, it throws them.
  84. * @private
  85. * @throws {ResultError}
  86. */
  87. export function assertNoErrors(res) {
  88. for (let result of res.results) {
  89. const { error } = result;
  90. if (error) {
  91. throw new ResultError(error);
  92. }
  93. }
  94. return res;
  95. }
  96. /**
  97. * From parses out a response to a result or list of responses.
  98. * There are three situations we cover here:
  99. * 1. A single query without groups, like `select * from myseries`
  100. * 2. A single query with groups, generated with a `group by` statement
  101. * which groups by series *tags*, grouping by times is case (1)
  102. * 3. Multiple queries of types 1 and 2
  103. * @private
  104. */
  105. export function parse(res, precision) {
  106. assertNoErrors(res);
  107. if (res.results.length === 1) {
  108. // Normalize case 3
  109. return parseInner(res.results[0].series, precision);
  110. }
  111. return res.results.map((result) => parseInner(result.series, precision));
  112. }
  113. /**
  114. * ParseSingle asserts that the response contains a single result,
  115. * and returns that result.
  116. * @throws {Error} if the number of results is not exactly one
  117. * @private
  118. */
  119. export function parseSingle(res, precision) {
  120. assertNoErrors(res);
  121. if (res.results.length !== 1) {
  122. throw new Error("node-influx expected the results length to equal 1, but " +
  123. `it was ${0}. Please report this here: https://git.io/influx-err`);
  124. }
  125. return parseInner(res.results[0].series, precision);
  126. }