src/results.js
- import { isoOrTimeToDate } from "./grammar";
- /**
- * A ResultError is thrown when a query generates errorful results from Influx.
- */
- export class ResultError extends Error {
- constructor(message) {
- super();
- this.message = `Error from InfluxDB: ${message}`;
- }
- }
- function groupMethod(matcher) {
- // We do a tiny bit of 'custom' deep equality checking here, taking
- // advantage of the fact that the tag keys are consistent across all
- // series results. This lets us match groupings much more efficiently,
- // ~6000x faster than the fastest vanilla equality checker (lodash)
- // when operating on large (~100,000 grouping) sets.
- const srcKeys = this.groupsTagsKeys;
- const dstKeys = Object.keys(matcher);
- if (srcKeys.length === 0 || srcKeys.length !== dstKeys.length) {
- return [];
- }
- L: for (let row of this.groupRows) {
- // eslint-disable-line no-labels
- for (let src of srcKeys) {
- if (row.tags[src] !== matcher[src]) {
- continue L; // eslint-disable-line no-labels
- }
- }
- return row.rows;
- }
- return [];
- }
- function groupsMethod() {
- return this.groupRows;
- }
- /**
- * Inner parsing function which unpacks the series into a table and attaches
- * methods to the array. This is quite optimized and a bit of a mess to read,
- * but it's all fairly easy procedural logic.
- *
- * We do this instead of subclassing Array since subclassing has some
- * undesirable side-effects. For example, calling .slice() on the array
- * makes it impossible to preserve groups as would be necessary if it's
- * subclassed.
- */
- function parseInner(series = [], precision) {
- const results = [];
- results.groupsTagsKeys =
- series.length && series[0].tags ? Object.keys(series[0].tags) : [];
- const tags = results.groupsTagsKeys;
- let nextGroup = [];
- results.groupRows = new Array(series.length); // Tslint:disable-line
- for (let i = 0; i < series.length; i += 1, results.length) {
- const { columns = [], values = [] } = series[i];
- for (let value of values) {
- const obj = {};
- for (let j = 0; j < columns.length; j += 1) {
- if (columns[j] === "time") {
- obj.time = isoOrTimeToDate(value[j], precision);
- }
- else {
- obj[columns[j]] = value[j];
- }
- }
- for (let tag of tags) {
- obj[tag] = series[i].tags[tag];
- }
- results.push(obj);
- nextGroup.push(obj);
- }
- results.groupRows[i] = {
- name: series[i].name,
- rows: nextGroup,
- tags: series[i].tags || {},
- };
- nextGroup = [];
- }
- results.group = groupMethod;
- results.groups = groupsMethod;
- return results;
- }
- /**
- * Checks if there are any errors in the IResponse and, if so, it throws them.
- * @private
- * @throws {ResultError}
- */
- export function assertNoErrors(res) {
- for (let result of res.results) {
- const { error } = result;
- if (error) {
- throw new ResultError(error);
- }
- }
- return res;
- }
- /**
- * From parses out a response to a result or list of responses.
- * There are three situations we cover here:
- * 1. A single query without groups, like `select * from myseries`
- * 2. A single query with groups, generated with a `group by` statement
- * which groups by series *tags*, grouping by times is case (1)
- * 3. Multiple queries of types 1 and 2
- * @private
- */
- export function parse(res, precision) {
- assertNoErrors(res);
- if (res.results.length === 1) {
- // Normalize case 3
- return parseInner(res.results[0].series, precision);
- }
- return res.results.map((result) => parseInner(result.series, precision));
- }
- /**
- * ParseSingle asserts that the response contains a single result,
- * and returns that result.
- * @throws {Error} if the number of results is not exactly one
- * @private
- */
- export function parseSingle(res, precision) {
- assertNoErrors(res);
- if (res.results.length !== 1) {
- throw new Error("node-influx expected the results length to equal 1, but " +
- `it was ${0}. Please report this here: https://git.io/influx-err`);
- }
- return parseInner(res.results[0].series, precision);
- }