134 lines
4.5 KiB
JavaScript
134 lines
4.5 KiB
JavaScript
|
"use strict";
|
||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||
|
exports.parseSingle = exports.parse = exports.assertNoErrors = exports.ResultError = void 0;
|
||
|
const grammar_1 = require("./grammar");
|
||
|
/**
|
||
|
* A ResultError is thrown when a query generates errorful results from Influx.
|
||
|
*/
|
||
|
class ResultError extends Error {
|
||
|
constructor(message) {
|
||
|
super();
|
||
|
this.message = `Error from InfluxDB: ${message}`;
|
||
|
}
|
||
|
}
|
||
|
exports.ResultError = ResultError;
|
||
|
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 = grammar_1.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}
|
||
|
*/
|
||
|
function assertNoErrors(res) {
|
||
|
for (let result of res.results) {
|
||
|
const { error } = result;
|
||
|
if (error) {
|
||
|
throw new ResultError(error);
|
||
|
}
|
||
|
}
|
||
|
return res;
|
||
|
}
|
||
|
exports.assertNoErrors = assertNoErrors;
|
||
|
/**
|
||
|
* 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
|
||
|
*/
|
||
|
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));
|
||
|
}
|
||
|
exports.parse = parse;
|
||
|
/**
|
||
|
* 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
|
||
|
*/
|
||
|
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);
|
||
|
}
|
||
|
exports.parseSingle = parseSingle;
|