2016-03-22 20:10:42 +08:00
|
|
|
var _ = require('underscore');
|
|
|
|
var BaseWidget = require('./base');
|
|
|
|
var debug = require('debug')('windshaft:widget:formula');
|
|
|
|
|
|
|
|
var dot = require('dot');
|
|
|
|
dot.templateSettings.strip = false;
|
|
|
|
|
|
|
|
var formulaQueryTpl = dot.template([
|
|
|
|
'SELECT',
|
2017-06-09 21:18:37 +08:00
|
|
|
' {{=it._operation}}({{=it._column}}) AS result,',
|
|
|
|
' (SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nulls WHERE {{=it._column}} IS NULL) AS nulls_count',
|
|
|
|
' {{?it._operation!==\'count\'}}',
|
2017-06-09 21:28:18 +08:00
|
|
|
' ,(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nulls',
|
|
|
|
' WHERE {{=it._column}} = \'infinity\'::float OR {{=it._column}} = \'-infinity\'::float) AS infinities_count',
|
|
|
|
' ,(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nulls',
|
|
|
|
' WHERE {{=it._column}} = \'NaN\'::float) AS nans_count',
|
2017-06-09 21:18:37 +08:00
|
|
|
' {{?}}',
|
|
|
|
'FROM ({{=it._query}}) _cdb_formula',
|
|
|
|
'{{?it._operation!==\'count\'}}',
|
|
|
|
'WHERE',
|
|
|
|
' {{=it._column}} != \'infinity\'::float',
|
|
|
|
'AND',
|
|
|
|
' {{=it._column}} != \'-infinity\'::float',
|
|
|
|
'AND',
|
|
|
|
' {{=it._column}} != \'NaN\'::float',
|
|
|
|
'{{?}}'
|
2016-03-22 20:10:42 +08:00
|
|
|
].join('\n'));
|
|
|
|
|
|
|
|
var VALID_OPERATIONS = {
|
|
|
|
count: true,
|
|
|
|
avg: true,
|
|
|
|
sum: true,
|
|
|
|
min: true,
|
|
|
|
max: true
|
|
|
|
};
|
|
|
|
|
|
|
|
var TYPE = 'formula';
|
|
|
|
|
|
|
|
/**
|
|
|
|
{
|
|
|
|
type: 'formula',
|
|
|
|
options: {
|
|
|
|
column: 'name',
|
|
|
|
operation: 'count' // count, sum, avg
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
function Formula(query, options) {
|
|
|
|
if (!_.isString(options.operation)) {
|
|
|
|
throw new Error('Formula expects `operation` in widget options');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!VALID_OPERATIONS[options.operation]) {
|
|
|
|
throw new Error("Formula does not support '" + options.operation + "' operation");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (options.operation !== 'count' && !_.isString(options.column)) {
|
|
|
|
throw new Error('Formula expects `column` in widget options');
|
|
|
|
}
|
|
|
|
|
|
|
|
BaseWidget.apply(this);
|
|
|
|
|
|
|
|
this.query = query;
|
|
|
|
this.column = options.column || '1';
|
|
|
|
this.operation = options.operation;
|
|
|
|
}
|
|
|
|
|
|
|
|
Formula.prototype = new BaseWidget();
|
|
|
|
Formula.prototype.constructor = Formula;
|
|
|
|
|
|
|
|
module.exports = Formula;
|
|
|
|
|
2016-06-01 17:42:24 +08:00
|
|
|
Formula.prototype.sql = function(psql, override, callback) {
|
2016-03-22 20:10:42 +08:00
|
|
|
if (!callback) {
|
|
|
|
callback = override;
|
|
|
|
override = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
var _query = this.query;
|
|
|
|
var formulaSql = formulaQueryTpl({
|
|
|
|
_query: _query,
|
|
|
|
_operation: this.operation,
|
|
|
|
_column: this.column
|
|
|
|
});
|
|
|
|
|
|
|
|
debug(formulaSql);
|
|
|
|
|
|
|
|
return callback(null, formulaSql);
|
|
|
|
};
|
|
|
|
|
|
|
|
Formula.prototype.format = function(result) {
|
|
|
|
var formattedResult = {
|
|
|
|
operation: this.operation,
|
|
|
|
result: 0,
|
2017-06-09 21:18:37 +08:00
|
|
|
nulls: 0,
|
|
|
|
nans: 0,
|
|
|
|
infinities: 0
|
2016-03-22 20:10:42 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
if (result.rows.length) {
|
|
|
|
formattedResult.operation = this.operation;
|
|
|
|
formattedResult.result = result.rows[0].result;
|
|
|
|
formattedResult.nulls = result.rows[0].nulls_count;
|
2017-06-09 21:18:37 +08:00
|
|
|
formattedResult.nans = result.rows[0].nans_count;
|
|
|
|
formattedResult.infinities = result.rows[0].infinities_count;
|
2016-03-22 20:10:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return formattedResult;
|
|
|
|
};
|
|
|
|
|
|
|
|
Formula.prototype.getType = function() {
|
|
|
|
return TYPE;
|
|
|
|
};
|
|
|
|
|
|
|
|
Formula.prototype.toString = function() {
|
|
|
|
return JSON.stringify({
|
|
|
|
_type: TYPE,
|
|
|
|
_query: this.query,
|
|
|
|
_column: this.column,
|
|
|
|
_operation: this.operation
|
|
|
|
});
|
|
|
|
};
|