Windshaft-cartodb/lib/models/dataview/formula.js

120 lines
3.0 KiB
JavaScript
Raw Normal View History

'use strict';
const BaseDataview = require('./base');
const debug = require('debug')('windshaft:dataview:formula');
2017-11-29 19:33:41 +08:00
const utils = require('../../utils/query-utils');
const formulaQueryTpl = ctx =>
2019-10-22 01:07:24 +08:00
`SELECT
2017-11-29 19:33:41 +08:00
${ctx.operation}(${utils.handleFloatColumn(ctx)}) AS result,
${utils.countNULLs(ctx)} AS nulls_count
2019-10-22 01:07:24 +08:00
${ctx.isFloatColumn ? `,${utils.countInfinites(ctx)} AS infinities_count,` : ''}
${ctx.isFloatColumn ? `${utils.countNaNs(ctx)} AS nans_count` : ''}
2017-11-29 19:33:41 +08:00
FROM (${ctx.query}) __cdb_formula`;
2017-09-15 16:49:20 +08:00
const VALID_OPERATIONS = {
count: true,
avg: true,
sum: true,
min: true,
max: true
};
2017-09-15 16:49:20 +08:00
const TYPE = 'formula';
/**
{
type: 'formula',
options: {
column: 'name',
operation: 'count' // count, sum, avg
}
}
*/
2017-09-15 16:48:44 +08:00
module.exports = class Formula extends BaseDataview {
constructor (query, options = {}, queries = {}) {
super();
this._checkOptions(options);
this.query = query;
this.queries = queries;
this.column = options.column || '1';
this.operation = options.operation;
this._isFloatColumn = null;
}
_checkOptions (options) {
2017-09-15 16:48:44 +08:00
if (typeof options.operation !== 'string') {
2019-10-22 01:07:24 +08:00
throw new Error('Formula expects \'operation\' in dataview options');
2017-09-15 16:48:44 +08:00
}
if (!VALID_OPERATIONS[options.operation]) {
throw new Error(`Formula does not support '${options.operation}' operation`);
2017-09-15 16:48:44 +08:00
}
if (options.operation !== 'count' && typeof options.column !== 'string') {
2019-10-22 01:07:24 +08:00
throw new Error('Formula expects \'column\' in dataview options');
2017-09-15 16:48:44 +08:00
}
}
2017-09-15 16:48:44 +08:00
sql (psql, override, callback) {
if (!callback) {
callback = override;
override = {};
}
if (this._isFloatColumn === null) {
this._isFloatColumn = false;
this.getColumnType(psql, this.column, this.queries.no_filters, (err, type) => {
2017-09-15 16:48:44 +08:00
if (!err && !!type) {
this._isFloatColumn = type.float;
2017-09-15 16:48:44 +08:00
}
this.sql(psql, override, callback);
2017-09-15 16:48:44 +08:00
});
return null;
}
const formulaSql = formulaQueryTpl({
isFloatColumn: this._isFloatColumn,
query: this.query,
operation: this.operation,
column: this.column
2017-09-15 16:48:44 +08:00
});
2017-09-15 16:48:44 +08:00
debug(formulaSql);
2017-09-15 16:48:44 +08:00
return callback(null, formulaSql);
}
2017-09-15 20:48:54 +08:00
format (res) {
const {
result = 0,
nulls_count = 0,
nans_count,
infinities_count
2017-09-15 20:48:54 +08:00
} = res.rows[0] || {};
return {
2017-09-15 16:48:44 +08:00
operation: this.operation,
result,
nulls: nulls_count,
nans: nans_count,
infinities: infinities_count
2017-09-15 16:48:44 +08:00
};
}
2017-09-15 16:48:44 +08:00
getType () {
return TYPE;
}
2017-09-15 16:48:44 +08:00
toString () {
return JSON.stringify({
_type: TYPE,
_query: this.query,
_column: this.column,
_operation: this.operation
});
}
};