Merge branch 'master' into 691-date-histogram
This commit is contained in:
commit
b2b68ffd5c
@ -1,7 +1,7 @@
|
|||||||
1. Test (make clean all check), fix if broken before proceeding
|
1. Test (make clean all check), fix if broken before proceeding
|
||||||
2. Ensure proper version in package.json
|
2. Ensure proper version in package.json
|
||||||
3. Ensure NEWS section exists for the new version, review it, add release date
|
3. Ensure NEWS section exists for the new version, review it, add release date
|
||||||
4. Recreate yarn.lock with: `yarn upgrade`
|
4. If there are modified dependencies in package.json, update them with `yarn upgrade {{package_name}}@{{version}}`
|
||||||
5. Commit package.json, yarn.lock, NEWS
|
5. Commit package.json, yarn.lock, NEWS
|
||||||
6. git tag -a Major.Minor.Patch # use NEWS section as content
|
6. git tag -a Major.Minor.Patch # use NEWS section as content
|
||||||
7. Stub NEWS/package for next version
|
7. Stub NEWS/package for next version
|
||||||
|
38
NEWS.md
38
NEWS.md
@ -1,5 +1,43 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 3.9.9
|
||||||
|
Released 2017-mm-dd
|
||||||
|
|
||||||
|
|
||||||
|
## 3.9.8
|
||||||
|
Released 2017-07-21
|
||||||
|
|
||||||
|
- Upgrades windshaft to [3.2.2](https://github.com/CartoDB/windshaft/releases/tag/3.2.2).
|
||||||
|
|
||||||
|
|
||||||
|
## 3.9.7
|
||||||
|
Released 2017-07-20
|
||||||
|
|
||||||
|
Bug fixes:
|
||||||
|
- Respond with 204 (No content) when vector tile has no data #712
|
||||||
|
|
||||||
|
Announcements:
|
||||||
|
- Upgrades turbo-carto to [0.19.2](https://github.com/CartoDB/turbo-carto/releases/tag/0.19.2)
|
||||||
|
|
||||||
|
|
||||||
|
## 3.9.6
|
||||||
|
Released 2017-07-11
|
||||||
|
|
||||||
|
- Dataviews: support for aggregation in search results #708
|
||||||
|
|
||||||
|
|
||||||
|
## 3.9.5
|
||||||
|
Released 2017-06-27
|
||||||
|
|
||||||
|
- Dataviews: support special numeric values (±Infinity, NaN) #700
|
||||||
|
|
||||||
|
|
||||||
|
## 3.9.4
|
||||||
|
Released 2017-06-22
|
||||||
|
|
||||||
|
Announcements:
|
||||||
|
- Upgrades camshaft to [0.55.6](https://github.com/CartoDB/camshaft/releases/tag/0.55.6).
|
||||||
|
|
||||||
## 3.9.3
|
## 3.9.3
|
||||||
Released 2017-06-16
|
Released 2017-06-16
|
||||||
|
|
||||||
|
@ -324,8 +324,7 @@ var config = {
|
|||||||
// whether the affected tables for a given SQL must query directly postgresql or use the SQL API
|
// whether the affected tables for a given SQL must query directly postgresql or use the SQL API
|
||||||
cdbQueryTablesFromPostgres: true,
|
cdbQueryTablesFromPostgres: true,
|
||||||
// whether in mapconfig is available stats & metadata for each layer
|
// whether in mapconfig is available stats & metadata for each layer
|
||||||
layerMetadata: true
|
layerStats: true
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -324,7 +324,7 @@ var config = {
|
|||||||
// whether the affected tables for a given SQL must query directly postgresql or use the SQL API
|
// whether the affected tables for a given SQL must query directly postgresql or use the SQL API
|
||||||
cdbQueryTablesFromPostgres: true,
|
cdbQueryTablesFromPostgres: true,
|
||||||
// whether in mapconfig is available stats & metadata for each layer
|
// whether in mapconfig is available stats & metadata for each layer
|
||||||
layerMetadata: false
|
layerStats: false
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -324,7 +324,7 @@ var config = {
|
|||||||
// whether the affected tables for a given SQL must query directly postgresql or use the SQL API
|
// whether the affected tables for a given SQL must query directly postgresql or use the SQL API
|
||||||
cdbQueryTablesFromPostgres: true,
|
cdbQueryTablesFromPostgres: true,
|
||||||
// whether in mapconfig is available stats & metadata for each layer
|
// whether in mapconfig is available stats & metadata for each layer
|
||||||
layerMetadata: true
|
layerStats: true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -318,7 +318,7 @@ var config = {
|
|||||||
// whether the affected tables for a given SQL must query directly postgresql or use the SQL API
|
// whether the affected tables for a given SQL must query directly postgresql or use the SQL API
|
||||||
cdbQueryTablesFromPostgres: true,
|
cdbQueryTablesFromPostgres: true,
|
||||||
// whether in mapconfig is available stats & metadata for each layer
|
// whether in mapconfig is available stats & metadata for each layer
|
||||||
layerMetadata: true
|
layerStats: true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -193,6 +193,10 @@ BaseController.prototype.sendError = function(req, res, err, label) {
|
|||||||
|
|
||||||
var statusCode = findStatusCode(err);
|
var statusCode = findStatusCode(err);
|
||||||
|
|
||||||
|
if (err.message === 'Tile does not exist' && req.params.format === 'mvt') {
|
||||||
|
statusCode = 204;
|
||||||
|
}
|
||||||
|
|
||||||
debug('[%s ERROR] -- %d: %s, %s', label, statusCode, err, err.stack);
|
debug('[%s ERROR] -- %d: %s, %s', label, statusCode, err, err.stack);
|
||||||
|
|
||||||
// If a callback was requested, force status to 200
|
// If a callback was requested, force status to 200
|
||||||
|
@ -5,11 +5,32 @@ var debug = require('debug')('windshaft:widget:aggregation');
|
|||||||
var dot = require('dot');
|
var dot = require('dot');
|
||||||
dot.templateSettings.strip = false;
|
dot.templateSettings.strip = false;
|
||||||
|
|
||||||
|
var filteredQueryTpl = dot.template([
|
||||||
|
'filtered_source AS (',
|
||||||
|
' SELECT *',
|
||||||
|
' FROM ({{=it._query}}) _cdb_filtered_source',
|
||||||
|
' {{?it._aggregationColumn && it._isFloatColumn}}WHERE',
|
||||||
|
' {{=it._aggregationColumn}} != \'infinity\'::float',
|
||||||
|
' AND',
|
||||||
|
' {{=it._aggregationColumn}} != \'-infinity\'::float',
|
||||||
|
' AND',
|
||||||
|
' {{=it._aggregationColumn}} != \'NaN\'::float{{?}}',
|
||||||
|
')'
|
||||||
|
].join(' \n'));
|
||||||
|
|
||||||
var summaryQueryTpl = dot.template([
|
var summaryQueryTpl = dot.template([
|
||||||
'summary AS (',
|
'summary AS (',
|
||||||
' SELECT',
|
' SELECT',
|
||||||
' count(1) AS count,',
|
' count(1) AS count,',
|
||||||
' sum(CASE WHEN {{=it._column}} IS NULL THEN 1 ELSE 0 END) AS nulls_count',
|
' sum(CASE WHEN {{=it._column}} IS NULL THEN 1 ELSE 0 END) AS nulls_count',
|
||||||
|
' {{?it._isFloatColumn}},sum(',
|
||||||
|
' CASE',
|
||||||
|
' WHEN {{=it._aggregationColumn}} = \'infinity\'::float OR {{=it._aggregationColumn}} = \'-infinity\'::float',
|
||||||
|
' THEN 1',
|
||||||
|
' ELSE 0',
|
||||||
|
' END',
|
||||||
|
' ) AS infinities_count,',
|
||||||
|
' sum(CASE WHEN {{=it._aggregationColumn}} = \'NaN\'::float THEN 1 ELSE 0 END) AS nans_count{{?}}',
|
||||||
' FROM ({{=it._query}}) _cdb_aggregation_nulls',
|
' FROM ({{=it._query}}) _cdb_aggregation_nulls',
|
||||||
')'
|
')'
|
||||||
].join('\n'));
|
].join('\n'));
|
||||||
@ -18,7 +39,7 @@ var rankedCategoriesQueryTpl = dot.template([
|
|||||||
'categories AS(',
|
'categories AS(',
|
||||||
' SELECT {{=it._column}} AS category, {{=it._aggregation}} AS value,',
|
' SELECT {{=it._column}} AS category, {{=it._aggregation}} AS value,',
|
||||||
' row_number() OVER (ORDER BY {{=it._aggregation}} desc) as rank',
|
' row_number() OVER (ORDER BY {{=it._aggregation}} desc) as rank',
|
||||||
' FROM ({{=it._query}}) _cdb_aggregation_all',
|
' FROM filtered_source',
|
||||||
' {{?it._aggregationColumn!==null}}WHERE {{=it._aggregationColumn}} IS NOT NULL{{?}}',
|
' {{?it._aggregationColumn!==null}}WHERE {{=it._aggregationColumn}} IS NOT NULL{{?}}',
|
||||||
' GROUP BY {{=it._column}}',
|
' GROUP BY {{=it._column}}',
|
||||||
' ORDER BY 2 DESC',
|
' ORDER BY 2 DESC',
|
||||||
@ -44,22 +65,25 @@ var categoriesSummaryCountQueryTpl = dot.template([
|
|||||||
].join('\n'));
|
].join('\n'));
|
||||||
|
|
||||||
var rankedAggregationQueryTpl = dot.template([
|
var rankedAggregationQueryTpl = dot.template([
|
||||||
'SELECT CAST(category AS text), value, false as agg, nulls_count, min_val, max_val, count, categories_count',
|
'SELECT CAST(category AS text), value, false as agg, nulls_count, min_val, max_val,',
|
||||||
|
' count, categories_count{{?it._isFloatColumn}}, nans_count, infinities_count{{?}}',
|
||||||
' FROM categories, summary, categories_summary_min_max, categories_summary_count',
|
' FROM categories, summary, categories_summary_min_max, categories_summary_count',
|
||||||
' WHERE rank < {{=it._limit}}',
|
' WHERE rank < {{=it._limit}}',
|
||||||
'UNION ALL',
|
'UNION ALL',
|
||||||
'SELECT \'Other\' category, {{=it._aggregationFn}}(value) as value, true as agg, nulls_count, min_val, max_val,',
|
'SELECT \'Other\' category, {{=it._aggregationFn}}(value) as value, true as agg, nulls_count,',
|
||||||
' count, categories_count',
|
' min_val, max_val, count, categories_count{{?it._isFloatColumn}}, nans_count, infinities_count{{?}}',
|
||||||
' FROM categories, summary, categories_summary_min_max, categories_summary_count',
|
' FROM categories, summary, categories_summary_min_max, categories_summary_count',
|
||||||
' WHERE rank >= {{=it._limit}}',
|
' WHERE rank >= {{=it._limit}}',
|
||||||
'GROUP BY nulls_count, min_val, max_val, count, categories_count'
|
'GROUP BY nulls_count, min_val, max_val, count,',
|
||||||
|
' categories_count{{?it._isFloatColumn}}, nans_count, infinities_count{{?}}'
|
||||||
].join('\n'));
|
].join('\n'));
|
||||||
|
|
||||||
var aggregationQueryTpl = dot.template([
|
var aggregationQueryTpl = dot.template([
|
||||||
'SELECT CAST({{=it._column}} AS text) AS category, {{=it._aggregation}} AS value, false as agg,',
|
'SELECT CAST({{=it._column}} AS text) AS category, {{=it._aggregation}} AS value, false as agg,',
|
||||||
' nulls_count, min_val, max_val, count, categories_count',
|
' nulls_count, min_val, max_val, count, categories_count{{?it._isFloatColumn}}, nans_count, infinities_count{{?}}',
|
||||||
'FROM ({{=it._query}}) _cdb_aggregation_all, summary, categories_summary_min_max, categories_summary_count',
|
'FROM ({{=it._query}}) _cdb_aggregation_all, summary, categories_summary_min_max, categories_summary_count',
|
||||||
'GROUP BY category, nulls_count, min_val, max_val, count, categories_count',
|
'GROUP BY category, nulls_count, min_val, max_val, count,',
|
||||||
|
' categories_count{{?it._isFloatColumn}}, nans_count, infinities_count{{?}}',
|
||||||
'ORDER BY value DESC'
|
'ORDER BY value DESC'
|
||||||
].join('\n'));
|
].join('\n'));
|
||||||
|
|
||||||
@ -84,7 +108,7 @@ var TYPE = 'aggregation';
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
function Aggregation(query, options) {
|
function Aggregation(query, options, queries) {
|
||||||
if (!_.isString(options.column)) {
|
if (!_.isString(options.column)) {
|
||||||
throw new Error('Aggregation expects `column` in widget options');
|
throw new Error('Aggregation expects `column` in widget options');
|
||||||
}
|
}
|
||||||
@ -108,9 +132,11 @@ function Aggregation(query, options) {
|
|||||||
BaseWidget.apply(this);
|
BaseWidget.apply(this);
|
||||||
|
|
||||||
this.query = query;
|
this.query = query;
|
||||||
|
this.queries = queries;
|
||||||
this.column = options.column;
|
this.column = options.column;
|
||||||
this.aggregation = options.aggregation;
|
this.aggregation = options.aggregation;
|
||||||
this.aggregationColumn = options.aggregationColumn;
|
this.aggregationColumn = options.aggregationColumn;
|
||||||
|
this._isFloatColumn = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Aggregation.prototype = new BaseWidget();
|
Aggregation.prototype = new BaseWidget();
|
||||||
@ -119,19 +145,39 @@ Aggregation.prototype.constructor = Aggregation;
|
|||||||
module.exports = Aggregation;
|
module.exports = Aggregation;
|
||||||
|
|
||||||
Aggregation.prototype.sql = function(psql, override, callback) {
|
Aggregation.prototype.sql = function(psql, override, callback) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
if (!callback) {
|
if (!callback) {
|
||||||
callback = override;
|
callback = override;
|
||||||
override = {};
|
override = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.aggregationColumn && this._isFloatColumn === null) {
|
||||||
|
this._isFloatColumn = false;
|
||||||
|
this.getColumnType(psql, this.aggregationColumn, this.queries.no_filters, function (err, type) {
|
||||||
|
if (!err && !!type) {
|
||||||
|
self._isFloatColumn = type.float;
|
||||||
|
}
|
||||||
|
self.sql(psql, override, callback);
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var _query = this.query;
|
var _query = this.query;
|
||||||
|
|
||||||
var aggregationSql;
|
var aggregationSql;
|
||||||
|
|
||||||
if (!!override.ownFilter) {
|
if (!!override.ownFilter) {
|
||||||
aggregationSql = [
|
aggregationSql = [
|
||||||
this.getCategoriesCTESql(_query, this.column, this.aggregation, this.aggregationColumn),
|
this.getCategoriesCTESql(
|
||||||
|
_query,
|
||||||
|
this.column,
|
||||||
|
this.aggregation,
|
||||||
|
this.aggregationColumn,
|
||||||
|
this._isFloatColumn
|
||||||
|
),
|
||||||
aggregationQueryTpl({
|
aggregationQueryTpl({
|
||||||
|
_isFloatColumn: this._isFloatColumn,
|
||||||
_query: _query,
|
_query: _query,
|
||||||
_column: this.column,
|
_column: this.column,
|
||||||
_aggregation: this.getAggregationSql(),
|
_aggregation: this.getAggregationSql(),
|
||||||
@ -140,8 +186,15 @@ Aggregation.prototype.sql = function(psql, override, callback) {
|
|||||||
].join('\n');
|
].join('\n');
|
||||||
} else {
|
} else {
|
||||||
aggregationSql = [
|
aggregationSql = [
|
||||||
this.getCategoriesCTESql(_query, this.column, this.aggregation, this.aggregationColumn),
|
this.getCategoriesCTESql(
|
||||||
|
_query,
|
||||||
|
this.column,
|
||||||
|
this.aggregation,
|
||||||
|
this.aggregationColumn,
|
||||||
|
this._isFloatColumn
|
||||||
|
),
|
||||||
rankedAggregationQueryTpl({
|
rankedAggregationQueryTpl({
|
||||||
|
_isFloatColumn: this._isFloatColumn,
|
||||||
_query: _query,
|
_query: _query,
|
||||||
_column: this.column,
|
_column: this.column,
|
||||||
_aggregationFn: this.aggregation !== 'count' ? this.aggregation : 'sum',
|
_aggregationFn: this.aggregation !== 'count' ? this.aggregation : 'sum',
|
||||||
@ -155,30 +208,38 @@ Aggregation.prototype.sql = function(psql, override, callback) {
|
|||||||
return callback(null, aggregationSql);
|
return callback(null, aggregationSql);
|
||||||
};
|
};
|
||||||
|
|
||||||
Aggregation.prototype.getCategoriesCTESql = function(query, column, aggregation, aggregationColumn) {
|
Aggregation.prototype.getCategoriesCTESql = function(query, column, aggregation, aggregationColumn, isFloatColumn) {
|
||||||
return [
|
return [
|
||||||
"WITH",
|
"WITH",
|
||||||
[
|
[
|
||||||
summaryQueryTpl({
|
filteredQueryTpl({
|
||||||
_query: query,
|
_isFloatColumn: isFloatColumn,
|
||||||
_column: column
|
_query: this.query,
|
||||||
}),
|
_column: this.column,
|
||||||
rankedCategoriesQueryTpl({
|
_aggregationColumn: aggregation !== 'count' ? aggregationColumn : null
|
||||||
_query: query,
|
}),
|
||||||
_column: column,
|
summaryQueryTpl({
|
||||||
_aggregation: this.getAggregationSql(),
|
_isFloatColumn: isFloatColumn,
|
||||||
_aggregationColumn: aggregation !== 'count' ? aggregationColumn : null
|
_query: query,
|
||||||
}),
|
_column: column,
|
||||||
categoriesSummaryMinMaxQueryTpl({
|
_aggregationColumn: aggregation !== 'count' ? aggregationColumn : null
|
||||||
_query: query,
|
}),
|
||||||
_column: column
|
rankedCategoriesQueryTpl({
|
||||||
}),
|
_query: query,
|
||||||
categoriesSummaryCountQueryTpl({
|
_column: column,
|
||||||
_query: query,
|
_aggregation: this.getAggregationSql(),
|
||||||
_column: column
|
_aggregationColumn: aggregation !== 'count' ? aggregationColumn : null
|
||||||
})
|
}),
|
||||||
].join(',\n')
|
categoriesSummaryMinMaxQueryTpl({
|
||||||
].join('\n');
|
_query: query,
|
||||||
|
_column: column
|
||||||
|
}),
|
||||||
|
categoriesSummaryCountQueryTpl({
|
||||||
|
_query: query,
|
||||||
|
_column: column
|
||||||
|
})
|
||||||
|
].join(',\n')
|
||||||
|
].join('\n');
|
||||||
};
|
};
|
||||||
|
|
||||||
var aggregationFnQueryTpl = dot.template('{{=it._aggregationFn}}({{=it._aggregationColumn}})');
|
var aggregationFnQueryTpl = dot.template('{{=it._aggregationFn}}({{=it._aggregationColumn}})');
|
||||||
@ -193,6 +254,8 @@ Aggregation.prototype.format = function(result) {
|
|||||||
var categories = [];
|
var categories = [];
|
||||||
var count = 0;
|
var count = 0;
|
||||||
var nulls = 0;
|
var nulls = 0;
|
||||||
|
var nans = 0;
|
||||||
|
var infinities = 0;
|
||||||
var minValue = 0;
|
var minValue = 0;
|
||||||
var maxValue = 0;
|
var maxValue = 0;
|
||||||
var categoriesCount = 0;
|
var categoriesCount = 0;
|
||||||
@ -202,12 +265,15 @@ Aggregation.prototype.format = function(result) {
|
|||||||
var firstRow = result.rows[0];
|
var firstRow = result.rows[0];
|
||||||
count = firstRow.count;
|
count = firstRow.count;
|
||||||
nulls = firstRow.nulls_count;
|
nulls = firstRow.nulls_count;
|
||||||
|
nans = firstRow.nans_count;
|
||||||
|
infinities = firstRow.infinities_count;
|
||||||
minValue = firstRow.min_val;
|
minValue = firstRow.min_val;
|
||||||
maxValue = firstRow.max_val;
|
maxValue = firstRow.max_val;
|
||||||
categoriesCount = firstRow.categories_count;
|
categoriesCount = firstRow.categories_count;
|
||||||
|
|
||||||
result.rows.forEach(function(row) {
|
result.rows.forEach(function(row) {
|
||||||
categories.push(_.omit(row, 'count', 'nulls_count', 'min_val', 'max_val', 'categories_count'));
|
categories.push(_.omit(row, 'count', 'nulls_count', 'min_val',
|
||||||
|
'max_val', 'categories_count', 'nans_count', 'infinities_count'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,6 +281,8 @@ Aggregation.prototype.format = function(result) {
|
|||||||
aggregation: this.aggregation,
|
aggregation: this.aggregation,
|
||||||
count: count,
|
count: count,
|
||||||
nulls: nulls,
|
nulls: nulls,
|
||||||
|
nans: nans,
|
||||||
|
infinities: infinities,
|
||||||
min: minValue,
|
min: minValue,
|
||||||
max: maxValue,
|
max: maxValue,
|
||||||
categoriesCount: categoriesCount,
|
categoriesCount: categoriesCount,
|
||||||
@ -253,6 +321,8 @@ Aggregation.prototype.search = function(psql, userQuery, callback) {
|
|||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var _userQuery = psql.escapeLiteral('%' + userQuery + '%');
|
var _userQuery = psql.escapeLiteral('%' + userQuery + '%');
|
||||||
|
var _value = this.aggregation !== 'count' && this.aggregationColumn ?
|
||||||
|
this.aggregation + '(' + this.aggregationColumn + ')' : 'count(1)';
|
||||||
|
|
||||||
// TODO unfiltered will be wrong as filters are already applied at this point
|
// TODO unfiltered will be wrong as filters are already applied at this point
|
||||||
var query = searchQueryTpl({
|
var query = searchQueryTpl({
|
||||||
@ -265,7 +335,7 @@ Aggregation.prototype.search = function(psql, userQuery, callback) {
|
|||||||
_searchFiltered: filterCategoriesQueryTpl({
|
_searchFiltered: filterCategoriesQueryTpl({
|
||||||
_query: this.query,
|
_query: this.query,
|
||||||
_column: this.column,
|
_column: this.column,
|
||||||
_value: 'count(1)',
|
_value: _value,
|
||||||
_userQuery: _userQuery
|
_userQuery: _userQuery
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
var dot = require('dot');
|
||||||
|
dot.templateSettings.strip = false;
|
||||||
|
|
||||||
function BaseDataview() {}
|
function BaseDataview() {}
|
||||||
|
|
||||||
module.exports = BaseDataview;
|
module.exports = BaseDataview;
|
||||||
@ -24,3 +27,42 @@ BaseDataview.prototype.getResult = function(psql, override, callback) {
|
|||||||
BaseDataview.prototype.search = function(psql, userQuery, callback) {
|
BaseDataview.prototype.search = function(psql, userQuery, callback) {
|
||||||
return callback(null, this.format({ rows: [] }));
|
return callback(null, this.format({ rows: [] }));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var FLOAT_OIDS = {
|
||||||
|
700: true,
|
||||||
|
701: true,
|
||||||
|
1700: true
|
||||||
|
};
|
||||||
|
|
||||||
|
var DATE_OIDS = {
|
||||||
|
1082: true,
|
||||||
|
1114: true,
|
||||||
|
1184: true
|
||||||
|
};
|
||||||
|
|
||||||
|
var columnTypeQueryTpl = dot.template(
|
||||||
|
'SELECT pg_typeof({{=it.column}})::oid FROM ({{=it.query}}) _cdb_column_type limit 1'
|
||||||
|
);
|
||||||
|
|
||||||
|
BaseDataview.prototype.getColumnType = function (psql, column, query, callback) {
|
||||||
|
var readOnlyTransaction = true;
|
||||||
|
|
||||||
|
var columnTypeQuery = columnTypeQueryTpl({
|
||||||
|
column: column, query: query
|
||||||
|
});
|
||||||
|
|
||||||
|
psql.query(columnTypeQuery, function(err, result) {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
var pgType = result.rows[0].pg_typeof;
|
||||||
|
callback(null, getPGTypeName(pgType));
|
||||||
|
}, readOnlyTransaction);
|
||||||
|
};
|
||||||
|
|
||||||
|
function getPGTypeName (pgType) {
|
||||||
|
return {
|
||||||
|
float: FLOAT_OIDS.hasOwnProperty(pgType),
|
||||||
|
date: DATE_OIDS.hasOwnProperty(pgType)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@ -7,9 +7,19 @@ dot.templateSettings.strip = false;
|
|||||||
|
|
||||||
var formulaQueryTpl = dot.template([
|
var formulaQueryTpl = dot.template([
|
||||||
'SELECT',
|
'SELECT',
|
||||||
'{{=it._operation}}({{=it._column}}) AS result,',
|
' {{=it._operation}}({{=it._column}}) AS result,',
|
||||||
'(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nulls WHERE {{=it._column}} IS NULL) AS nulls_count',
|
' (SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nulls WHERE {{=it._column}} IS NULL) AS nulls_count',
|
||||||
'FROM ({{=it._query}}) _cdb_formula'
|
' {{?it._isFloatColumn}},(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{{?}}',
|
||||||
|
'FROM ({{=it._query}}) _cdb_formula',
|
||||||
|
'{{?it._isFloatColumn && it._operation !== \'count\'}}WHERE',
|
||||||
|
' {{=it._column}} != \'infinity\'::float',
|
||||||
|
'AND',
|
||||||
|
' {{=it._column}} != \'-infinity\'::float',
|
||||||
|
'AND',
|
||||||
|
' {{=it._column}} != \'NaN\'::float{{?}}'
|
||||||
].join('\n'));
|
].join('\n'));
|
||||||
|
|
||||||
var VALID_OPERATIONS = {
|
var VALID_OPERATIONS = {
|
||||||
@ -31,7 +41,7 @@ var TYPE = 'formula';
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
function Formula(query, options) {
|
function Formula(query, options, queries) {
|
||||||
if (!_.isString(options.operation)) {
|
if (!_.isString(options.operation)) {
|
||||||
throw new Error('Formula expects `operation` in widget options');
|
throw new Error('Formula expects `operation` in widget options');
|
||||||
}
|
}
|
||||||
@ -47,8 +57,10 @@ function Formula(query, options) {
|
|||||||
BaseWidget.apply(this);
|
BaseWidget.apply(this);
|
||||||
|
|
||||||
this.query = query;
|
this.query = query;
|
||||||
|
this.queries = queries;
|
||||||
this.column = options.column || '1';
|
this.column = options.column || '1';
|
||||||
this.operation = options.operation;
|
this.operation = options.operation;
|
||||||
|
this._isFloatColumn = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Formula.prototype = new BaseWidget();
|
Formula.prototype = new BaseWidget();
|
||||||
@ -57,14 +69,27 @@ Formula.prototype.constructor = Formula;
|
|||||||
module.exports = Formula;
|
module.exports = Formula;
|
||||||
|
|
||||||
Formula.prototype.sql = function(psql, override, callback) {
|
Formula.prototype.sql = function(psql, override, callback) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
if (!callback) {
|
if (!callback) {
|
||||||
callback = override;
|
callback = override;
|
||||||
override = {};
|
override = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
var _query = this.query;
|
if (this._isFloatColumn === null) {
|
||||||
|
this._isFloatColumn = false;
|
||||||
|
this.getColumnType(psql, this.column, this.queries.no_filters, function (err, type) {
|
||||||
|
if (!err && !!type) {
|
||||||
|
self._isFloatColumn = type.float;
|
||||||
|
}
|
||||||
|
self.sql(psql, override, callback);
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var formulaSql = formulaQueryTpl({
|
var formulaSql = formulaQueryTpl({
|
||||||
_query: _query,
|
_isFloatColumn: this._isFloatColumn,
|
||||||
|
_query: this.query,
|
||||||
_operation: this.operation,
|
_operation: this.operation,
|
||||||
_column: this.column
|
_column: this.column
|
||||||
});
|
});
|
||||||
@ -78,13 +103,17 @@ Formula.prototype.format = function(result) {
|
|||||||
var formattedResult = {
|
var formattedResult = {
|
||||||
operation: this.operation,
|
operation: this.operation,
|
||||||
result: 0,
|
result: 0,
|
||||||
nulls: 0
|
nulls: 0,
|
||||||
|
nans: 0,
|
||||||
|
infinities: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
if (result.rows.length) {
|
if (result.rows.length) {
|
||||||
formattedResult.operation = this.operation;
|
formattedResult.operation = this.operation;
|
||||||
formattedResult.result = result.rows[0].result;
|
formattedResult.result = result.rows[0].result;
|
||||||
formattedResult.nulls = result.rows[0].nulls_count;
|
formattedResult.nulls = result.rows[0].nulls_count;
|
||||||
|
formattedResult.nans = result.rows[0].nans_count;
|
||||||
|
formattedResult.infinities = result.rows[0].infinities_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
return formattedResult;
|
return formattedResult;
|
||||||
|
@ -5,11 +5,6 @@ var debug = require('debug')('windshaft:dataview:histogram');
|
|||||||
var dot = require('dot');
|
var dot = require('dot');
|
||||||
dot.templateSettings.strip = false;
|
dot.templateSettings.strip = false;
|
||||||
|
|
||||||
var columnTypeQueryTpl = dot.template(
|
|
||||||
'SELECT pg_typeof({{=it.column}})::oid FROM ({{=it.query}}) _cdb_histogram_column_type limit 1'
|
|
||||||
);
|
|
||||||
var columnCastTpl = dot.template("date_part('epoch', {{=it.column}})");
|
|
||||||
|
|
||||||
var dateIntervalQueryTpl = dot.template([
|
var dateIntervalQueryTpl = dot.template([
|
||||||
'WITH',
|
'WITH',
|
||||||
'dates AS (',
|
'dates AS (',
|
||||||
@ -54,12 +49,27 @@ var MAX_INTERVAL_VALUE = 366;
|
|||||||
var BIN_MIN_NUMBER = 6;
|
var BIN_MIN_NUMBER = 6;
|
||||||
var BIN_MAX_NUMBER = 48;
|
var BIN_MAX_NUMBER = 48;
|
||||||
|
|
||||||
|
var filteredQueryTpl = dot.template([
|
||||||
|
'filtered_source AS (',
|
||||||
|
' SELECT *',
|
||||||
|
' FROM ({{=it._query}}) _cdb_filtered_source',
|
||||||
|
' WHERE',
|
||||||
|
' {{=it._column}} IS NOT NULL',
|
||||||
|
' {{?it._isFloatColumn}}AND',
|
||||||
|
' {{=it._column}} != \'infinity\'::float',
|
||||||
|
' AND',
|
||||||
|
' {{=it._column}} != \'-infinity\'::float',
|
||||||
|
' AND',
|
||||||
|
' {{=it._column}} != \'NaN\'::float{{?}}',
|
||||||
|
')'
|
||||||
|
].join(' \n'));
|
||||||
|
|
||||||
var basicsQueryTpl = dot.template([
|
var basicsQueryTpl = dot.template([
|
||||||
'basics AS (',
|
'basics AS (',
|
||||||
' SELECT',
|
' SELECT',
|
||||||
' max({{=it._column}}) AS max_val, min({{=it._column}}) AS min_val,',
|
' max({{=it._column}}) AS max_val, min({{=it._column}}) AS min_val,',
|
||||||
' avg({{=it._column}}) AS avg_val, count(1) AS total_rows',
|
' avg({{=it._column}}) AS avg_val, count(1) AS total_rows',
|
||||||
' FROM ({{=it._query}}) _cdb_basics',
|
' FROM filtered_source',
|
||||||
')'
|
')'
|
||||||
].join(' \n'));
|
].join(' \n'));
|
||||||
|
|
||||||
@ -68,7 +78,7 @@ var overrideBasicsQueryTpl = dot.template([
|
|||||||
' SELECT',
|
' SELECT',
|
||||||
' max({{=it._end}}) AS max_val, min({{=it._start}}) AS min_val,',
|
' max({{=it._end}}) AS max_val, min({{=it._start}}) AS min_val,',
|
||||||
' avg({{=it._column}}) AS avg_val, count(1) AS total_rows',
|
' avg({{=it._column}}) AS avg_val, count(1) AS total_rows',
|
||||||
' FROM ({{=it._query}}) _cdb_basics',
|
' FROM filtered_source',
|
||||||
')'
|
')'
|
||||||
].join('\n'));
|
].join('\n'));
|
||||||
|
|
||||||
@ -79,7 +89,7 @@ var iqrQueryTpl = dot.template([
|
|||||||
' SELECT quartile, max(_cdb_iqr_column) AS quartile_max from (',
|
' SELECT quartile, max(_cdb_iqr_column) AS quartile_max from (',
|
||||||
' SELECT {{=it._column}} AS _cdb_iqr_column, ntile(4) over (order by {{=it._column}}',
|
' SELECT {{=it._column}} AS _cdb_iqr_column, ntile(4) over (order by {{=it._column}}',
|
||||||
' ) AS quartile',
|
' ) AS quartile',
|
||||||
' FROM ({{=it._query}}) _cdb_rank) _cdb_quartiles',
|
' FROM filtered_source) _cdb_quartiles',
|
||||||
' WHERE quartile = 1 or quartile = 3',
|
' WHERE quartile = 1 or quartile = 3',
|
||||||
' GROUP BY quartile',
|
' GROUP BY quartile',
|
||||||
' ) _cdb_iqr',
|
' ) _cdb_iqr',
|
||||||
@ -98,7 +108,7 @@ var binsQueryTpl = dot.template([
|
|||||||
' )',
|
' )',
|
||||||
' )',
|
' )',
|
||||||
' END AS bins_number',
|
' END AS bins_number',
|
||||||
' FROM basics, iqrange, ({{=it._query}}) _cdb_bins',
|
' FROM basics, iqrange, filtered_source',
|
||||||
' LIMIT 1',
|
' LIMIT 1',
|
||||||
')'
|
')'
|
||||||
].join('\n'));
|
].join('\n'));
|
||||||
@ -118,11 +128,34 @@ var nullsQueryTpl = dot.template([
|
|||||||
')'
|
')'
|
||||||
].join('\n'));
|
].join('\n'));
|
||||||
|
|
||||||
|
var infinitiesQueryTpl = dot.template([
|
||||||
|
'infinities AS (',
|
||||||
|
' SELECT',
|
||||||
|
' count(*) AS infinities_count',
|
||||||
|
' FROM ({{=it._query}}) _cdb_histogram_infinities',
|
||||||
|
' WHERE',
|
||||||
|
' {{=it._column}} = \'infinity\'::float',
|
||||||
|
' OR',
|
||||||
|
' {{=it._column}} = \'-infinity\'::float',
|
||||||
|
')'
|
||||||
|
].join('\n'));
|
||||||
|
|
||||||
|
var nansQueryTpl = dot.template([
|
||||||
|
'nans AS (',
|
||||||
|
' SELECT',
|
||||||
|
' count(*) AS nans_count',
|
||||||
|
' FROM ({{=it._query}}) _cdb_histogram_infinities',
|
||||||
|
' WHERE {{=it._column}} = \'NaN\'::float',
|
||||||
|
')'
|
||||||
|
].join('\n'));
|
||||||
|
|
||||||
var histogramQueryTpl = dot.template([
|
var histogramQueryTpl = dot.template([
|
||||||
'SELECT',
|
'SELECT',
|
||||||
' (max_val - min_val) / cast(bins_number as float) AS bin_width,',
|
' (max_val - min_val) / cast(bins_number as float) AS bin_width,',
|
||||||
' bins_number,',
|
' bins_number,',
|
||||||
' nulls_count,',
|
' nulls_count,',
|
||||||
|
' {{?it._isFloatColumn}}infinities_count,',
|
||||||
|
' nans_count,{{?}}',
|
||||||
' avg_val,',
|
' avg_val,',
|
||||||
' CASE WHEN min_val = max_val',
|
' CASE WHEN min_val = max_val',
|
||||||
' THEN 0',
|
' THEN 0',
|
||||||
@ -132,9 +165,9 @@ var histogramQueryTpl = dot.template([
|
|||||||
' max({{=it._column}})::numeric AS max,',
|
' max({{=it._column}})::numeric AS max,',
|
||||||
' avg({{=it._column}})::numeric AS avg,',
|
' avg({{=it._column}})::numeric AS avg,',
|
||||||
' count(*) AS freq',
|
' count(*) AS freq',
|
||||||
'FROM ({{=it._query}}) _cdb_histogram, basics, nulls, bins',
|
'FROM filtered_source, basics, nulls, bins{{?it._isFloatColumn}}, infinities, nans{{?}}',
|
||||||
'WHERE {{=it._column}} IS NOT NULL',
|
'GROUP BY bin, bins_number, bin_width, nulls_count,',
|
||||||
'GROUP BY bin, bins_number, bin_width, nulls_count, avg_val',
|
' avg_val{{?it._isFloatColumn}}, infinities_count, nans_count{{?}}',
|
||||||
'ORDER BY bin'
|
'ORDER BY bin'
|
||||||
].join('\n'));
|
].join('\n'));
|
||||||
|
|
||||||
@ -268,54 +301,47 @@ Histogram.prototype.constructor = Histogram;
|
|||||||
|
|
||||||
module.exports = Histogram;
|
module.exports = Histogram;
|
||||||
|
|
||||||
var DATE_OIDS = {
|
|
||||||
1082: true,
|
|
||||||
1114: true,
|
|
||||||
1184: true
|
|
||||||
};
|
|
||||||
|
|
||||||
Histogram.prototype.sql = function(psql, override, callback) {
|
Histogram.prototype.sql = function(psql, override, callback) {
|
||||||
// jshint maxcomplexity: 7
|
var self = this;
|
||||||
|
|
||||||
if (!callback) {
|
if (!callback) {
|
||||||
callback = override;
|
callback = override;
|
||||||
override = {};
|
override = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
var _column = this.column;
|
|
||||||
|
|
||||||
var columnTypeQuery = columnTypeQueryTpl({
|
|
||||||
column: _column, query: this.queries.no_filters
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this._columnType === null) {
|
if (this._columnType === null) {
|
||||||
psql.query(columnTypeQuery, function(err, result) {
|
this.getColumnType(psql, this.column, this.queries.no_filters, function (err, type) {
|
||||||
// assume numeric, will fail later
|
// assume numeric, will fail later
|
||||||
self._columnType = 'numeric';
|
self._columnType = 'numeric';
|
||||||
if (!err && !!result.rows[0]) {
|
if (!err && !!type) {
|
||||||
var pgType = result.rows[0].pg_typeof;
|
self._columnType = Object.keys(type).find(function (key) {
|
||||||
if (DATE_OIDS.hasOwnProperty(pgType)) {
|
return type[key];
|
||||||
self._columnType = 'date';
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self.sql(psql, override, callback);
|
self.sql(psql, override, callback);
|
||||||
}, true); // use read-only transaction
|
}, true); // use read-only transaction
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._buildQuery(psql, override, callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
Histogram.prototype._buildQuery = function (psql, override, callback) {
|
||||||
|
var filteredQuery, basicsQuery, binsQuery;
|
||||||
|
var _column = this.column;
|
||||||
|
var _query = this.query;
|
||||||
|
|
||||||
if (this._columnType === 'date') {
|
if (this._columnType === 'date') {
|
||||||
return this._buildDateHistogramQuery(psql, override, callback);
|
return this._buildDateHistogramQuery(psql, override, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._columnType === 'date') {
|
filteredQuery = filteredQueryTpl({
|
||||||
_column = columnCastTpl({ column: this.column});
|
_isFloatColumn: this._columnType === 'float',
|
||||||
}
|
_query: _query,
|
||||||
var _query = this.query;
|
_column: _column
|
||||||
|
});
|
||||||
|
|
||||||
var basicsQuery, binsQuery;
|
if (this._shouldOverride(override)) {
|
||||||
|
|
||||||
if (override && _.has(override, 'start') && _.has(override, 'end') && _.has(override, 'bins')) {
|
|
||||||
debug('overriding with %j', override);
|
debug('overriding with %j', override);
|
||||||
basicsQuery = overrideBasicsQueryTpl({
|
basicsQuery = overrideBasicsQueryTpl({
|
||||||
_query: _query,
|
_query: _query,
|
||||||
@ -335,7 +361,7 @@ Histogram.prototype.sql = function(psql, override, callback) {
|
|||||||
_column: _column
|
_column: _column
|
||||||
});
|
});
|
||||||
|
|
||||||
if (override && _.has(override, 'bins')) {
|
if (this._shouldOverrideBins(override)) {
|
||||||
binsQuery = [
|
binsQuery = [
|
||||||
overrideBinsQueryTpl({
|
overrideBinsQueryTpl({
|
||||||
_bins: override.bins
|
_bins: override.bins
|
||||||
@ -356,17 +382,34 @@ Histogram.prototype.sql = function(psql, override, callback) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var histogramSql = [
|
var cteSql = [
|
||||||
"WITH",
|
filteredQuery,
|
||||||
[
|
basicsQuery,
|
||||||
basicsQuery,
|
binsQuery,
|
||||||
binsQuery,
|
nullsQueryTpl({
|
||||||
nullsQueryTpl({
|
_query: _query,
|
||||||
|
_column: _column
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
if (this._columnType === 'float') {
|
||||||
|
cteSql.push(
|
||||||
|
infinitiesQueryTpl({
|
||||||
|
_query: _query,
|
||||||
|
_column: _column
|
||||||
|
}),
|
||||||
|
nansQueryTpl({
|
||||||
_query: _query,
|
_query: _query,
|
||||||
_column: _column
|
_column: _column
|
||||||
})
|
})
|
||||||
].join(',\n'),
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var histogramSql = [
|
||||||
|
"WITH",
|
||||||
|
cteSql.join(',\n'),
|
||||||
histogramQueryTpl({
|
histogramQueryTpl({
|
||||||
|
_isFloatColumn: this._columnType === 'float',
|
||||||
_query: _query,
|
_query: _query,
|
||||||
_column: _column
|
_column: _column
|
||||||
})
|
})
|
||||||
@ -377,6 +420,14 @@ Histogram.prototype.sql = function(psql, override, callback) {
|
|||||||
return callback(null, histogramSql);
|
return callback(null, histogramSql);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Histogram.prototype._shouldOverride = function (override) {
|
||||||
|
return override && _.has(override, 'start') && _.has(override, 'end') && _.has(override, 'bins');
|
||||||
|
};
|
||||||
|
|
||||||
|
Histogram.prototype._shouldOverrideBins = function (override) {
|
||||||
|
return override && _.has(override, 'bins');
|
||||||
|
};
|
||||||
|
|
||||||
Histogram.prototype._buildDateHistogramQuery = function (psql, override, callback) {
|
Histogram.prototype._buildDateHistogramQuery = function (psql, override, callback) {
|
||||||
var _column = this.column;
|
var _column = this.column;
|
||||||
var _query = this.query;
|
var _query = this.query;
|
||||||
@ -492,14 +543,16 @@ Histogram.prototype.format = function(result, override) {
|
|||||||
override = override || {};
|
override = override || {};
|
||||||
var buckets = [];
|
var buckets = [];
|
||||||
|
|
||||||
var aggregation = getAggregation(override, this.aggregation);
|
|
||||||
var offset = getOffset(override, this.offset);
|
|
||||||
var binsCount = getBinsCount(override);
|
var binsCount = getBinsCount(override);
|
||||||
var width = getWidth(override);
|
var width = getWidth(override);
|
||||||
var binsStart = getBinStart(override);
|
var binsStart = getBinStart(override);
|
||||||
var nulls = 0;
|
var nulls = 0;
|
||||||
|
var infinities = 0;
|
||||||
|
var nans = 0;
|
||||||
var avg;
|
var avg;
|
||||||
var timestampStart;
|
var timestampStart;
|
||||||
|
var aggregation;
|
||||||
|
var offset;
|
||||||
|
|
||||||
if (result.rows.length) {
|
if (result.rows.length) {
|
||||||
var firstRow = result.rows[0];
|
var firstRow = result.rows[0];
|
||||||
@ -507,10 +560,27 @@ Histogram.prototype.format = function(result, override) {
|
|||||||
width = firstRow.bin_width || width;
|
width = firstRow.bin_width || width;
|
||||||
avg = firstRow.avg_val;
|
avg = firstRow.avg_val;
|
||||||
nulls = firstRow.nulls_count;
|
nulls = firstRow.nulls_count;
|
||||||
binsStart = populateBinStart(override, firstRow);
|
|
||||||
timestampStart = firstRow.timestamp_start;
|
timestampStart = firstRow.timestamp_start;
|
||||||
|
infinities = firstRow.infinities_count;
|
||||||
|
nans = firstRow.nans_count;
|
||||||
|
binsStart = populateBinStart(override, firstRow);
|
||||||
|
|
||||||
|
if (timestampStart) {
|
||||||
|
aggregation = getAggregation(override, this.aggregation);
|
||||||
|
offset = getOffset(override, this.offset);
|
||||||
|
}
|
||||||
|
|
||||||
buckets = result.rows.map(function(row) {
|
buckets = result.rows.map(function(row) {
|
||||||
return _.omit(row, 'bins_number', 'bin_width', 'nulls_count', 'avg_val', 'timestamp_start');
|
return _.omit(
|
||||||
|
row,
|
||||||
|
'bins_number',
|
||||||
|
'bin_width',
|
||||||
|
'nulls_count',
|
||||||
|
'infinities_count',
|
||||||
|
'nans_count',
|
||||||
|
'avg_val',
|
||||||
|
'timestamp_start'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -522,6 +592,8 @@ Histogram.prototype.format = function(result, override) {
|
|||||||
bins_count: binsCount,
|
bins_count: binsCount,
|
||||||
bins_start: binsStart,
|
bins_start: binsStart,
|
||||||
nulls: nulls,
|
nulls: nulls,
|
||||||
|
infinities: infinities,
|
||||||
|
nans: nans,
|
||||||
avg: avg,
|
avg: avg,
|
||||||
bins: buckets
|
bins: buckets
|
||||||
};
|
};
|
||||||
|
@ -1,14 +1,36 @@
|
|||||||
var BaseOverviewsDataview = require('./base');
|
var BaseOverviewsDataview = require('./base');
|
||||||
var BaseDataview = require('../aggregation');
|
var BaseDataview = require('../aggregation');
|
||||||
|
var debug = require('debug')('windshaft:widget:aggregation:overview');
|
||||||
|
|
||||||
var dot = require('dot');
|
var dot = require('dot');
|
||||||
dot.templateSettings.strip = false;
|
dot.templateSettings.strip = false;
|
||||||
|
|
||||||
|
var filteredQueryTpl = dot.template([
|
||||||
|
'filtered_source AS (',
|
||||||
|
' SELECT *',
|
||||||
|
' FROM ({{=it._query}}) _cdb_filtered_source',
|
||||||
|
' {{?it._aggregationColumn && it._isFloatColumn}}WHERE',
|
||||||
|
' {{=it._aggregationColumn}} != \'infinity\'::float',
|
||||||
|
' AND',
|
||||||
|
' {{=it._aggregationColumn}} != \'-infinity\'::float',
|
||||||
|
' AND',
|
||||||
|
' {{=it._aggregationColumn}} != \'NaN\'::float{{?}}',
|
||||||
|
')'
|
||||||
|
].join(' \n'));
|
||||||
|
|
||||||
var summaryQueryTpl = dot.template([
|
var summaryQueryTpl = dot.template([
|
||||||
'summary AS (',
|
'summary AS (',
|
||||||
' SELECT',
|
' SELECT',
|
||||||
' sum(_feature_count) AS count,',
|
' sum(_feature_count) AS count,',
|
||||||
' sum(CASE WHEN {{=it._column}} IS NULL THEN 1 ELSE 0 END) AS nulls_count',
|
' sum(CASE WHEN {{=it._column}} IS NULL THEN 1 ELSE 0 END) AS nulls_count',
|
||||||
|
' {{?it._isFloatColumn}},sum(',
|
||||||
|
' CASE',
|
||||||
|
' WHEN {{=it._aggregationColumn}} = \'infinity\'::float OR {{=it._aggregationColumn}} = \'-infinity\'::float',
|
||||||
|
' THEN 1',
|
||||||
|
' ELSE 0',
|
||||||
|
' END',
|
||||||
|
' ) AS infinities_count,',
|
||||||
|
' sum(CASE WHEN {{=it._aggregationColumn}} = \'NaN\'::float THEN 1 ELSE 0 END) AS nans_count{{?}}',
|
||||||
' FROM ({{=it._query}}) _cdb_aggregation_nulls',
|
' FROM ({{=it._query}}) _cdb_aggregation_nulls',
|
||||||
')'
|
')'
|
||||||
].join('\n'));
|
].join('\n'));
|
||||||
@ -17,7 +39,7 @@ var rankedCategoriesQueryTpl = dot.template([
|
|||||||
'categories AS(',
|
'categories AS(',
|
||||||
' SELECT {{=it._column}} AS category, {{=it._aggregation}} AS value,',
|
' SELECT {{=it._column}} AS category, {{=it._aggregation}} AS value,',
|
||||||
' row_number() OVER (ORDER BY {{=it._aggregation}} desc) as rank',
|
' row_number() OVER (ORDER BY {{=it._aggregation}} desc) as rank',
|
||||||
' FROM ({{=it._query}}) _cdb_aggregation_all',
|
' FROM filtered_source',
|
||||||
' {{?it._aggregationColumn!==null}}WHERE {{=it._aggregationColumn}} IS NOT NULL{{?}}',
|
' {{?it._aggregationColumn!==null}}WHERE {{=it._aggregationColumn}} IS NOT NULL{{?}}',
|
||||||
' GROUP BY {{=it._column}}',
|
' GROUP BY {{=it._column}}',
|
||||||
' ORDER BY 2 DESC',
|
' ORDER BY 2 DESC',
|
||||||
@ -36,40 +58,46 @@ var categoriesSummaryCountQueryTpl = dot.template([
|
|||||||
' SELECT count(1) AS categories_count',
|
' SELECT count(1) AS categories_count',
|
||||||
' FROM (',
|
' FROM (',
|
||||||
' SELECT {{=it._column}} AS category',
|
' SELECT {{=it._column}} AS category',
|
||||||
' FROM ({{=it._query}}) _cdb_categories',
|
' FROM filtered_source',
|
||||||
' GROUP BY {{=it._column}}',
|
' GROUP BY {{=it._column}}',
|
||||||
' ) _cdb_categories_count',
|
' ) _cdb_categories_count',
|
||||||
')'
|
')'
|
||||||
].join('\n'));
|
].join('\n'));
|
||||||
|
|
||||||
var rankedAggregationQueryTpl = dot.template([
|
var rankedAggregationQueryTpl = dot.template([
|
||||||
'SELECT CAST(category AS text), value, false as agg, nulls_count, min_val, max_val, count, categories_count',
|
'SELECT CAST(category AS text), value, false as agg, nulls_count, min_val, max_val,',
|
||||||
|
' count, categories_count{{?it._isFloatColumn}}, nans_count, infinities_count{{?}}',
|
||||||
' FROM categories, summary, categories_summary_min_max, categories_summary_count',
|
' FROM categories, summary, categories_summary_min_max, categories_summary_count',
|
||||||
' WHERE rank < {{=it._limit}}',
|
' WHERE rank < {{=it._limit}}',
|
||||||
'UNION ALL',
|
'UNION ALL',
|
||||||
'SELECT \'Other\' category, sum(value), true as agg, nulls_count, min_val, max_val, count, categories_count',
|
'SELECT \'Other\' category, sum(value), true as agg, nulls_count, min_val, max_val,',
|
||||||
|
' count, categories_count{{?it._isFloatColumn}}, nans_count, infinities_count{{?}}',
|
||||||
' FROM categories, summary, categories_summary_min_max, categories_summary_count',
|
' FROM categories, summary, categories_summary_min_max, categories_summary_count',
|
||||||
' WHERE rank >= {{=it._limit}}',
|
' WHERE rank >= {{=it._limit}}',
|
||||||
'GROUP BY nulls_count, min_val, max_val, count, categories_count'
|
'GROUP BY nulls_count, min_val, max_val, count,',
|
||||||
|
' categories_count{{?it._isFloatColumn}}, nans_count, infinities_count{{?}}'
|
||||||
].join('\n'));
|
].join('\n'));
|
||||||
|
|
||||||
var aggregationQueryTpl = dot.template([
|
var aggregationQueryTpl = dot.template([
|
||||||
'SELECT CAST({{=it._column}} AS text) AS category, {{=it._aggregation}} AS value, false as agg,',
|
'SELECT CAST({{=it._column}} AS text) AS category, {{=it._aggregation}} AS value, false as agg,',
|
||||||
' nulls_count, min_val, max_val, count, categories_count',
|
' nulls_count, min_val, max_val, count, categories_count{{?it._isFloatColumn}}, nans_count, infinities_count{{?}}',
|
||||||
'FROM ({{=it._query}}) _cdb_aggregation_all, summary, categories_summary_min_max, categories_summary_count',
|
'FROM filtered_source, summary, categories_summary_min_max, categories_summary_count',
|
||||||
'GROUP BY category, nulls_count, min_val, max_val, count, categories_count',
|
'GROUP BY category, nulls_count, min_val, max_val, count,',
|
||||||
|
' categories_count{{?it._isFloatColumn}}, nans_count, infinities_count{{?}}',
|
||||||
'ORDER BY value DESC'
|
'ORDER BY value DESC'
|
||||||
].join('\n'));
|
].join('\n'));
|
||||||
|
|
||||||
var CATEGORIES_LIMIT = 6;
|
var CATEGORIES_LIMIT = 6;
|
||||||
|
|
||||||
function Aggregation(query, options, queryRewriter, queryRewriteData, params) {
|
function Aggregation(query, options, queryRewriter, queryRewriteData, params, queries) {
|
||||||
BaseOverviewsDataview.call(this, query, options, BaseDataview, queryRewriter, queryRewriteData, params);
|
BaseOverviewsDataview.call(this, query, options, BaseDataview, queryRewriter, queryRewriteData, params, queries);
|
||||||
|
|
||||||
this.query = query;
|
this.query = query;
|
||||||
|
this.queries = queries;
|
||||||
this.column = options.column;
|
this.column = options.column;
|
||||||
this.aggregation = options.aggregation;
|
this.aggregation = options.aggregation;
|
||||||
this.aggregationColumn = options.aggregationColumn;
|
this.aggregationColumn = options.aggregationColumn;
|
||||||
|
this._isFloatColumn = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Aggregation.prototype = Object.create(BaseOverviewsDataview.prototype);
|
Aggregation.prototype = Object.create(BaseOverviewsDataview.prototype);
|
||||||
@ -78,27 +106,49 @@ Aggregation.prototype.constructor = Aggregation;
|
|||||||
module.exports = Aggregation;
|
module.exports = Aggregation;
|
||||||
|
|
||||||
Aggregation.prototype.sql = function(psql, override, callback) {
|
Aggregation.prototype.sql = function(psql, override, callback) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
if (!callback) {
|
if (!callback) {
|
||||||
callback = override;
|
callback = override;
|
||||||
override = {};
|
override = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
var _query = this.rewrittenQuery(this.query);
|
var _query = this.rewrittenQuery(this.query);
|
||||||
|
var _aggregationColumn = this.aggregation !== 'count' ? this.aggregationColumn : null;
|
||||||
|
|
||||||
|
if (this.aggregationColumn && this._isFloatColumn === null) {
|
||||||
|
this._isFloatColumn = false;
|
||||||
|
this.getColumnType(psql, this.aggregationColumn, this.queries.no_filters, function (err, type) {
|
||||||
|
if (!err && !!type) {
|
||||||
|
self._isFloatColumn = type.float;
|
||||||
|
}
|
||||||
|
self.sql(psql, override, callback);
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var aggregationSql;
|
var aggregationSql;
|
||||||
if (!!override.ownFilter) {
|
if (!!override.ownFilter) {
|
||||||
aggregationSql = [
|
aggregationSql = [
|
||||||
"WITH",
|
"WITH",
|
||||||
[
|
[
|
||||||
summaryQueryTpl({
|
filteredQueryTpl({
|
||||||
|
_isFloatColumn: this._isFloatColumn,
|
||||||
_query: _query,
|
_query: _query,
|
||||||
_column: this.column
|
_column: this.column,
|
||||||
|
_aggregationColumn: _aggregationColumn
|
||||||
|
}),
|
||||||
|
summaryQueryTpl({
|
||||||
|
_isFloatColumn: this._isFloatColumn,
|
||||||
|
_query: _query,
|
||||||
|
_column: this.column,
|
||||||
|
_aggregationColumn: _aggregationColumn
|
||||||
}),
|
}),
|
||||||
rankedCategoriesQueryTpl({
|
rankedCategoriesQueryTpl({
|
||||||
_query: _query,
|
_query: _query,
|
||||||
_column: this.column,
|
_column: this.column,
|
||||||
_aggregation: this.getAggregationSql(),
|
_aggregation: this.getAggregationSql(),
|
||||||
_aggregationColumn: this.aggregation !== 'count' ? this.aggregationColumn : null
|
_aggregationColumn: _aggregationColumn
|
||||||
}),
|
}),
|
||||||
categoriesSummaryMinMaxQueryTpl({
|
categoriesSummaryMinMaxQueryTpl({
|
||||||
_query: _query,
|
_query: _query,
|
||||||
@ -110,6 +160,7 @@ Aggregation.prototype.sql = function(psql, override, callback) {
|
|||||||
})
|
})
|
||||||
].join(',\n'),
|
].join(',\n'),
|
||||||
aggregationQueryTpl({
|
aggregationQueryTpl({
|
||||||
|
_isFloatColumn: this._isFloatColumn,
|
||||||
_query: _query,
|
_query: _query,
|
||||||
_column: this.column,
|
_column: this.column,
|
||||||
_aggregation: this.getAggregationSql(),
|
_aggregation: this.getAggregationSql(),
|
||||||
@ -120,15 +171,23 @@ Aggregation.prototype.sql = function(psql, override, callback) {
|
|||||||
aggregationSql = [
|
aggregationSql = [
|
||||||
"WITH",
|
"WITH",
|
||||||
[
|
[
|
||||||
summaryQueryTpl({
|
filteredQueryTpl({
|
||||||
|
_isFloatColumn: this._isFloatColumn,
|
||||||
_query: _query,
|
_query: _query,
|
||||||
_column: this.column
|
_column: this.column,
|
||||||
|
_aggregationColumn: _aggregationColumn
|
||||||
|
}),
|
||||||
|
summaryQueryTpl({
|
||||||
|
_isFloatColumn: this._isFloatColumn,
|
||||||
|
_query: _query,
|
||||||
|
_column: this.column,
|
||||||
|
_aggregationColumn: _aggregationColumn
|
||||||
}),
|
}),
|
||||||
rankedCategoriesQueryTpl({
|
rankedCategoriesQueryTpl({
|
||||||
_query: _query,
|
_query: _query,
|
||||||
_column: this.column,
|
_column: this.column,
|
||||||
_aggregation: this.getAggregationSql(),
|
_aggregation: this.getAggregationSql(),
|
||||||
_aggregationColumn: this.aggregation !== 'count' ? this.aggregationColumn : null
|
_aggregationColumn: _aggregationColumn
|
||||||
}),
|
}),
|
||||||
categoriesSummaryMinMaxQueryTpl({
|
categoriesSummaryMinMaxQueryTpl({
|
||||||
_query: _query,
|
_query: _query,
|
||||||
@ -140,6 +199,7 @@ Aggregation.prototype.sql = function(psql, override, callback) {
|
|||||||
})
|
})
|
||||||
].join(',\n'),
|
].join(',\n'),
|
||||||
rankedAggregationQueryTpl({
|
rankedAggregationQueryTpl({
|
||||||
|
_isFloatColumn: this._isFloatColumn,
|
||||||
_query: _query,
|
_query: _query,
|
||||||
_column: this.column,
|
_column: this.column,
|
||||||
_limit: CATEGORIES_LIMIT
|
_limit: CATEGORIES_LIMIT
|
||||||
@ -147,6 +207,8 @@ Aggregation.prototype.sql = function(psql, override, callback) {
|
|||||||
].join('\n');
|
].join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug(aggregationSql);
|
||||||
|
|
||||||
return callback(null, aggregationSql);
|
return callback(null, aggregationSql);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
var _ = require('underscore');
|
var _ = require('underscore');
|
||||||
var BaseDataview = require('../base');
|
var BaseDataview = require('../base');
|
||||||
|
|
||||||
function BaseOverviewsDataview(query, queryOptions, BaseDataview, queryRewriter, queryRewriteData, options) {
|
function BaseOverviewsDataview(query, queryOptions, BaseDataview, queryRewriter, queryRewriteData, options, queries) {
|
||||||
this.BaseDataview = BaseDataview;
|
this.BaseDataview = BaseDataview;
|
||||||
this.query = query;
|
this.query = query;
|
||||||
this.queryOptions = queryOptions;
|
this.queryOptions = queryOptions;
|
||||||
this.queryRewriter = queryRewriter;
|
this.queryRewriter = queryRewriter;
|
||||||
this.queryRewriteData = queryRewriteData;
|
this.queryRewriteData = queryRewriteData;
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.baseDataview = new this.BaseDataview(this.query, this.queryOptions);
|
this.queries = queries;
|
||||||
|
this.baseDataview = new this.BaseDataview(this.query, this.queryOptions, this.queries);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = BaseOverviewsDataview;
|
module.exports = BaseOverviewsDataview;
|
||||||
|
@ -1,34 +1,61 @@
|
|||||||
var BaseOverviewsDataview = require('./base');
|
var BaseOverviewsDataview = require('./base');
|
||||||
var BaseDataview = require('../formula');
|
var BaseDataview = require('../formula');
|
||||||
|
var debug = require('debug')('windshaft:widget:formula:overview');
|
||||||
|
|
||||||
var dot = require('dot');
|
var dot = require('dot');
|
||||||
dot.templateSettings.strip = false;
|
dot.templateSettings.strip = false;
|
||||||
|
|
||||||
var formulaQueryTpls = {
|
var formulaQueryTpls = {
|
||||||
'count': dot.template([
|
'count': dot.template([
|
||||||
'SELECT',
|
'SELECT',
|
||||||
'sum(_feature_count) AS result,',
|
'sum(_feature_count) AS result,',
|
||||||
'(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nulls WHERE {{=it._column}} IS NULL) AS nulls_count',
|
'(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nulls WHERE {{=it._column}} IS NULL) AS nulls_count',
|
||||||
'FROM ({{=it._query}}) _cdb_formula'
|
'{{?it._isFloatColumn}},(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_infinities',
|
||||||
].join('\n')),
|
' WHERE {{=it._column}} = \'infinity\'::float OR {{=it._column}} = \'-infinity\'::float) AS infinities_count,',
|
||||||
'sum': dot.template([
|
'(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nans',
|
||||||
'SELECT',
|
' WHERE {{=it._column}} = \'NaN\'::float) AS nans_count{{?}}',
|
||||||
'sum({{=it._column}}*_feature_count) AS result,',
|
'FROM ({{=it._query}}) _cdb_formula'
|
||||||
'(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nulls WHERE {{=it._column}} IS NULL) AS nulls_count',
|
].join('\n')),
|
||||||
'FROM ({{=it._query}}) _cdb_formula'
|
'sum': dot.template([
|
||||||
].join('\n')),
|
'SELECT',
|
||||||
'avg': dot.template([
|
'sum({{=it._column}}*_feature_count) AS result,',
|
||||||
'SELECT',
|
'(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nulls WHERE {{=it._column}} IS NULL) AS nulls_count',
|
||||||
'sum({{=it._column}}*_feature_count)/sum(_feature_count) AS result,',
|
'{{?it._isFloatColumn}},(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_infinities',
|
||||||
'(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nulls WHERE {{=it._column}} IS NULL) AS nulls_count',
|
' WHERE {{=it._column}} = \'infinity\'::float OR {{=it._column}} = \'-infinity\'::float) AS infinities_count',
|
||||||
'FROM ({{=it._query}}) _cdb_formula'
|
',(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nans',
|
||||||
].join('\n')),
|
' WHERE {{=it._column}} = \'NaN\'::float) AS nans_count{{?}}',
|
||||||
|
'FROM ({{=it._query}}) _cdb_formula',
|
||||||
|
'{{?it._isFloatColumn}}WHERE',
|
||||||
|
' {{=it._column}} != \'infinity\'::float',
|
||||||
|
'AND',
|
||||||
|
' {{=it._column}} != \'-infinity\'::float',
|
||||||
|
'AND',
|
||||||
|
' {{=it._column}} != \'NaN\'::float{{?}}'
|
||||||
|
].join('\n')),
|
||||||
|
'avg': dot.template([
|
||||||
|
'SELECT',
|
||||||
|
'sum({{=it._column}}*_feature_count)/sum(_feature_count) AS result,',
|
||||||
|
'(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nulls WHERE {{=it._column}} IS NULL) AS nulls_count',
|
||||||
|
'{{?it._isFloatColumn}},(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_infinities',
|
||||||
|
' WHERE {{=it._column}} = \'infinity\'::float OR {{=it._column}} = \'-infinity\'::float) AS infinities_count',
|
||||||
|
',(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nans',
|
||||||
|
' WHERE {{=it._column}} = \'NaN\'::float) AS nans_count{{?}}',
|
||||||
|
'FROM ({{=it._query}}) _cdb_formula',
|
||||||
|
'{{?it._isFloatColumn}}WHERE',
|
||||||
|
' {{=it._column}} != \'infinity\'::float',
|
||||||
|
'AND',
|
||||||
|
' {{=it._column}} != \'-infinity\'::float',
|
||||||
|
'AND',
|
||||||
|
' {{=it._column}} != \'NaN\'::float{{?}}'
|
||||||
|
].join('\n')),
|
||||||
};
|
};
|
||||||
|
|
||||||
function Formula(query, options, queryRewriter, queryRewriteData, params) {
|
function Formula(query, options, queryRewriter, queryRewriteData, params, queries) {
|
||||||
BaseOverviewsDataview.call(this, query, options, BaseDataview, queryRewriter, queryRewriteData, params);
|
BaseOverviewsDataview.call(this, query, options, BaseDataview, queryRewriter, queryRewriteData, params, queries);
|
||||||
this.column = options.column || '1';
|
this.column = options.column || '1';
|
||||||
this.operation = options.operation;
|
this.operation = options.operation;
|
||||||
|
this._isFloatColumn = null;
|
||||||
|
this.queries = queries;
|
||||||
}
|
}
|
||||||
|
|
||||||
Formula.prototype = Object.create(BaseOverviewsDataview.prototype);
|
Formula.prototype = Object.create(BaseOverviewsDataview.prototype);
|
||||||
@ -36,21 +63,38 @@ Formula.prototype.constructor = Formula;
|
|||||||
|
|
||||||
module.exports = Formula;
|
module.exports = Formula;
|
||||||
|
|
||||||
Formula.prototype.sql = function(psql, override, callback) {
|
Formula.prototype.sql = function (psql, override, callback) {
|
||||||
|
var self = this;
|
||||||
var formulaQueryTpl = formulaQueryTpls[this.operation];
|
var formulaQueryTpl = formulaQueryTpls[this.operation];
|
||||||
|
|
||||||
if ( formulaQueryTpl ) {
|
if (formulaQueryTpl) {
|
||||||
// supported formula for use with overviews
|
// supported formula for use with overviews
|
||||||
|
if (this._isFloatColumn === null) {
|
||||||
|
this._isFloatColumn = false;
|
||||||
|
this.getColumnType(psql, this.column, this.queries.no_filters, function (err, type) {
|
||||||
|
if (!err && !!type) {
|
||||||
|
self._isFloatColumn = type.float;
|
||||||
|
}
|
||||||
|
self.sql(psql, override, callback);
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var formulaSql = formulaQueryTpl({
|
var formulaSql = formulaQueryTpl({
|
||||||
_query: this.rewrittenQuery(this.query),
|
_isFloatColumn: this._isFloatColumn,
|
||||||
|
_query: this.rewrittenQuery(this.query),
|
||||||
_operation: this.operation,
|
_operation: this.operation,
|
||||||
_column: this.column
|
_column: this.column
|
||||||
});
|
});
|
||||||
|
|
||||||
callback = callback || override;
|
callback = callback || override;
|
||||||
|
|
||||||
|
debug(formulaSql);
|
||||||
|
|
||||||
return callback(null, formulaSql);
|
return callback(null, formulaSql);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// default behaviour
|
// default behaviour
|
||||||
return this.defaultSql(psql, override, callback);
|
return this.defaultSql(psql, override, callback);
|
||||||
};
|
};
|
||||||
|
@ -1,23 +1,35 @@
|
|||||||
var _ = require('underscore');
|
var _ = require('underscore');
|
||||||
var BaseOverviewsDataview = require('./base');
|
var BaseOverviewsDataview = require('./base');
|
||||||
var BaseDataview = require('../histogram');
|
var BaseDataview = require('../histogram');
|
||||||
|
var debug = require('debug')('windshaft:dataview:histogram:overview');
|
||||||
|
|
||||||
var dot = require('dot');
|
var dot = require('dot');
|
||||||
dot.templateSettings.strip = false;
|
dot.templateSettings.strip = false;
|
||||||
|
|
||||||
var columnTypeQueryTpl = dot.template(
|
|
||||||
'SELECT pg_typeof({{=it.column}})::oid FROM ({{=it.query}}) _cdb_histogram_column_type limit 1'
|
|
||||||
);
|
|
||||||
|
|
||||||
var BIN_MIN_NUMBER = 6;
|
var BIN_MIN_NUMBER = 6;
|
||||||
var BIN_MAX_NUMBER = 48;
|
var BIN_MAX_NUMBER = 48;
|
||||||
|
|
||||||
|
var filteredQueryTpl = dot.template([
|
||||||
|
'filtered_source AS (',
|
||||||
|
' SELECT *',
|
||||||
|
' FROM ({{=it._query}}) _cdb_filtered_source',
|
||||||
|
' WHERE',
|
||||||
|
' {{=it._column}} IS NOT NULL',
|
||||||
|
' {{?it._isFloatColumn}}AND',
|
||||||
|
' {{=it._column}} != \'infinity\'::float',
|
||||||
|
' AND',
|
||||||
|
' {{=it._column}} != \'-infinity\'::float',
|
||||||
|
' AND',
|
||||||
|
' {{=it._column}} != \'NaN\'::float{{?}}',
|
||||||
|
')'
|
||||||
|
].join(' \n'));
|
||||||
|
|
||||||
var basicsQueryTpl = dot.template([
|
var basicsQueryTpl = dot.template([
|
||||||
'basics AS (',
|
'basics AS (',
|
||||||
' SELECT',
|
' SELECT',
|
||||||
' max({{=it._column}}) AS max_val, min({{=it._column}}) AS min_val,',
|
' max({{=it._column}}) AS max_val, min({{=it._column}}) AS min_val,',
|
||||||
' sum({{=it._column}}*_feature_count)/sum(_feature_count) AS avg_val, sum(_feature_count) AS total_rows',
|
' sum({{=it._column}}*_feature_count)/sum(_feature_count) AS avg_val, sum(_feature_count) AS total_rows',
|
||||||
' FROM ({{=it._query}}) _cdb_basics',
|
' FROM filtered_source',
|
||||||
')'
|
')'
|
||||||
].join(' \n'));
|
].join(' \n'));
|
||||||
|
|
||||||
@ -26,7 +38,7 @@ var overrideBasicsQueryTpl = dot.template([
|
|||||||
' SELECT',
|
' SELECT',
|
||||||
' max({{=it._end}}) AS max_val, min({{=it._start}}) AS min_val,',
|
' max({{=it._end}}) AS max_val, min({{=it._start}}) AS min_val,',
|
||||||
' sum({{=it._column}}*_feature_count)/sum(_feature_count) AS avg_val, sum(_feature_count) AS total_rows',
|
' sum({{=it._column}}*_feature_count)/sum(_feature_count) AS avg_val, sum(_feature_count) AS total_rows',
|
||||||
' FROM ({{=it._query}}) _cdb_basics',
|
' FROM filtered_source',
|
||||||
')'
|
')'
|
||||||
].join('\n'));
|
].join('\n'));
|
||||||
|
|
||||||
@ -37,7 +49,7 @@ var iqrQueryTpl = dot.template([
|
|||||||
' SELECT quartile, max(_cdb_iqr_column) AS quartile_max from (',
|
' SELECT quartile, max(_cdb_iqr_column) AS quartile_max from (',
|
||||||
' SELECT {{=it._column}} AS _cdb_iqr_column, ntile(4) over (order by {{=it._column}}',
|
' SELECT {{=it._column}} AS _cdb_iqr_column, ntile(4) over (order by {{=it._column}}',
|
||||||
' ) AS quartile',
|
' ) AS quartile',
|
||||||
' FROM ({{=it._query}}) _cdb_rank) _cdb_quartiles',
|
' FROM filtered_source) _cdb_quartiles',
|
||||||
' WHERE quartile = 1 or quartile = 3',
|
' WHERE quartile = 1 or quartile = 3',
|
||||||
' GROUP BY quartile',
|
' GROUP BY quartile',
|
||||||
' ) _cdb_iqr',
|
' ) _cdb_iqr',
|
||||||
@ -56,7 +68,7 @@ var binsQueryTpl = dot.template([
|
|||||||
' )',
|
' )',
|
||||||
' )',
|
' )',
|
||||||
' END AS bins_number',
|
' END AS bins_number',
|
||||||
' FROM basics, iqrange, ({{=it._query}}) _cdb_bins',
|
' FROM basics, iqrange, filtered_source',
|
||||||
' LIMIT 1',
|
' LIMIT 1',
|
||||||
')'
|
')'
|
||||||
].join('\n'));
|
].join('\n'));
|
||||||
@ -76,11 +88,34 @@ var nullsQueryTpl = dot.template([
|
|||||||
')'
|
')'
|
||||||
].join('\n'));
|
].join('\n'));
|
||||||
|
|
||||||
|
var infinitiesQueryTpl = dot.template([
|
||||||
|
'infinities AS (',
|
||||||
|
' SELECT',
|
||||||
|
' count(*) AS infinities_count',
|
||||||
|
' FROM ({{=it._query}}) _cdb_histogram_infinities',
|
||||||
|
' WHERE',
|
||||||
|
' {{=it._column}} = \'infinity\'::float',
|
||||||
|
' OR',
|
||||||
|
' {{=it._column}} = \'-infinity\'::float',
|
||||||
|
')'
|
||||||
|
].join('\n'));
|
||||||
|
|
||||||
|
var nansQueryTpl = dot.template([
|
||||||
|
'nans AS (',
|
||||||
|
' SELECT',
|
||||||
|
' count(*) AS nans_count',
|
||||||
|
' FROM ({{=it._query}}) _cdb_histogram_infinities',
|
||||||
|
' WHERE {{=it._column}} = \'NaN\'::float',
|
||||||
|
')'
|
||||||
|
].join('\n'));
|
||||||
|
|
||||||
var histogramQueryTpl = dot.template([
|
var histogramQueryTpl = dot.template([
|
||||||
'SELECT',
|
'SELECT',
|
||||||
' (max_val - min_val) / cast(bins_number as float) AS bin_width,',
|
' (max_val - min_val) / cast(bins_number as float) AS bin_width,',
|
||||||
' bins_number,',
|
' bins_number,',
|
||||||
' nulls_count,',
|
' nulls_count,',
|
||||||
|
' {{?it._isFloatColumn}}infinities_count,',
|
||||||
|
' nans_count,{{?}}',
|
||||||
' avg_val,',
|
' avg_val,',
|
||||||
' CASE WHEN min_val = max_val',
|
' CASE WHEN min_val = max_val',
|
||||||
' THEN 0',
|
' THEN 0',
|
||||||
@ -90,14 +125,14 @@ var histogramQueryTpl = dot.template([
|
|||||||
' max({{=it._column}})::numeric AS max,',
|
' max({{=it._column}})::numeric AS max,',
|
||||||
' sum({{=it._column}}*_feature_count)/sum(_feature_count)::numeric AS avg,',
|
' sum({{=it._column}}*_feature_count)/sum(_feature_count)::numeric AS avg,',
|
||||||
' sum(_feature_count) AS freq',
|
' sum(_feature_count) AS freq',
|
||||||
'FROM ({{=it._query}}) _cdb_histogram, basics, nulls, bins',
|
'FROM filtered_source, basics, nulls, bins{{?it._isFloatColumn}},infinities, nans{{?}}',
|
||||||
'WHERE {{=it._column}} IS NOT NULL',
|
|
||||||
'GROUP BY bin, bins_number, bin_width, nulls_count, avg_val',
|
'GROUP BY bin, bins_number, bin_width, nulls_count, avg_val',
|
||||||
|
' {{?it._isFloatColumn}}, infinities_count, nans_count{{?}}',
|
||||||
'ORDER BY bin'
|
'ORDER BY bin'
|
||||||
].join('\n'));
|
].join('\n'));
|
||||||
|
|
||||||
function Histogram(query, options, queryRewriter, queryRewriteData, params, queries) {
|
function Histogram(query, options, queryRewriter, queryRewriteData, params, queries) {
|
||||||
BaseOverviewsDataview.call(this, query, options, BaseDataview, queryRewriter, queryRewriteData, params);
|
BaseOverviewsDataview.call(this, query, options, BaseDataview, queryRewriter, queryRewriteData, params, queries);
|
||||||
|
|
||||||
this.query = query;
|
this.query = query;
|
||||||
this.queries = queries;
|
this.queries = queries;
|
||||||
@ -112,36 +147,23 @@ Histogram.prototype.constructor = Histogram;
|
|||||||
|
|
||||||
module.exports = Histogram;
|
module.exports = Histogram;
|
||||||
|
|
||||||
|
|
||||||
var DATE_OIDS = {
|
|
||||||
1082: true,
|
|
||||||
1114: true,
|
|
||||||
1184: true
|
|
||||||
};
|
|
||||||
|
|
||||||
Histogram.prototype.sql = function(psql, override, callback) {
|
Histogram.prototype.sql = function(psql, override, callback) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
if (!callback) {
|
if (!callback) {
|
||||||
callback = override;
|
callback = override;
|
||||||
override = {};
|
override = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
var _column = this.column;
|
|
||||||
|
|
||||||
var columnTypeQuery = columnTypeQueryTpl({
|
|
||||||
column: _column, query: this.rewrittenQuery(this.queries.no_filters)
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this._columnType === null) {
|
if (this._columnType === null) {
|
||||||
psql.query(columnTypeQuery, function(err, result) {
|
this.getColumnType(psql, this.column, this.queries.no_filters, function (err, type) {
|
||||||
// assume numeric, will fail later
|
// assume numeric, will fail later
|
||||||
self._columnType = 'numeric';
|
self._columnType = 'numeric';
|
||||||
if (!err && !!result.rows[0]) {
|
if (!err && !!type) {
|
||||||
var pgType = result.rows[0].pg_typeof;
|
self._columnType = Object.keys(type).find(function (key) {
|
||||||
if (DATE_OIDS.hasOwnProperty(pgType)) {
|
return type[key];
|
||||||
self._columnType = 'date';
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self.sql(psql, override, callback);
|
self.sql(psql, override, callback);
|
||||||
}, true); // use read-only transaction
|
}, true); // use read-only transaction
|
||||||
@ -154,11 +176,24 @@ Histogram.prototype.sql = function(psql, override, callback) {
|
|||||||
return this.defaultSql(psql, override, callback);
|
return this.defaultSql(psql, override, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var histogramSql = this._buildQuery(override);
|
||||||
|
|
||||||
|
return callback(null, histogramSql);
|
||||||
|
};
|
||||||
|
|
||||||
|
Histogram.prototype._buildQuery = function (override) {
|
||||||
|
var filteredQuery, basicsQuery, binsQuery;
|
||||||
|
var _column = this.column;
|
||||||
var _query = this.rewrittenQuery(this.query);
|
var _query = this.rewrittenQuery(this.query);
|
||||||
|
|
||||||
var basicsQuery, binsQuery;
|
filteredQuery = filteredQueryTpl({
|
||||||
|
_isFloatColumn: this._columnType === 'float',
|
||||||
|
_query: _query,
|
||||||
|
_column: _column
|
||||||
|
});
|
||||||
|
|
||||||
if (override && _.has(override, 'start') && _.has(override, 'end') && _.has(override, 'bins')) {
|
if (this._shouldOverride(override)) {
|
||||||
|
debug('overriding with %j', override);
|
||||||
basicsQuery = overrideBasicsQueryTpl({
|
basicsQuery = overrideBasicsQueryTpl({
|
||||||
_query: _query,
|
_query: _query,
|
||||||
_column: _column,
|
_column: _column,
|
||||||
@ -177,7 +212,7 @@ Histogram.prototype.sql = function(psql, override, callback) {
|
|||||||
_column: _column
|
_column: _column
|
||||||
});
|
});
|
||||||
|
|
||||||
if (override && _.has(override, 'bins')) {
|
if (this._shouldOverrideBins(override)) {
|
||||||
binsQuery = [
|
binsQuery = [
|
||||||
overrideBinsQueryTpl({
|
overrideBinsQueryTpl({
|
||||||
_bins: override.bins
|
_bins: override.bins
|
||||||
@ -198,22 +233,50 @@ Histogram.prototype.sql = function(psql, override, callback) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var cteSql = [
|
||||||
|
filteredQuery,
|
||||||
|
basicsQuery,
|
||||||
|
binsQuery,
|
||||||
|
nullsQueryTpl({
|
||||||
|
_query: _query,
|
||||||
|
_column: _column
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
var histogramSql = [
|
if (this._columnType === 'float') {
|
||||||
"WITH",
|
cteSql.push(
|
||||||
[
|
infinitiesQueryTpl({
|
||||||
basicsQuery,
|
_query: _query,
|
||||||
binsQuery,
|
_column: _column
|
||||||
nullsQueryTpl({
|
}),
|
||||||
|
nansQueryTpl({
|
||||||
_query: _query,
|
_query: _query,
|
||||||
_column: _column
|
_column: _column
|
||||||
})
|
})
|
||||||
].join(',\n'),
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var histogramSql = [
|
||||||
|
"WITH",
|
||||||
|
cteSql.join(',\n'),
|
||||||
histogramQueryTpl({
|
histogramQueryTpl({
|
||||||
|
_isFloatColumn: this._columnType === 'float',
|
||||||
_query: _query,
|
_query: _query,
|
||||||
_column: _column
|
_column: _column
|
||||||
})
|
})
|
||||||
].join('\n');
|
].join('\n');
|
||||||
|
|
||||||
return callback(null, histogramSql);
|
debug(histogramSql);
|
||||||
|
|
||||||
|
return histogramSql;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Histogram.prototype._shouldOverride = function (override) {
|
||||||
|
return override && _.has(override, 'start') && _.has(override, 'end') && _.has(override, 'bins');
|
||||||
|
};
|
||||||
|
|
||||||
|
Histogram.prototype._shouldOverrideBins = function (override) {
|
||||||
|
return override && _.has(override, 'bins');
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
var BaseOverviewsDataview = require('./base');
|
var BaseOverviewsDataview = require('./base');
|
||||||
var BaseDataview = require('../list');
|
var BaseDataview = require('../list');
|
||||||
|
|
||||||
function List(query, options, queryRewriter, queryRewriteData, params) {
|
function List(query, options, queryRewriter, queryRewriteData, params, queries) {
|
||||||
BaseOverviewsDataview.call(this, query, options, BaseDataview, queryRewriter, queryRewriteData, params);
|
BaseOverviewsDataview.call(this, query, options, BaseDataview, queryRewriter, queryRewriteData, params, queries);
|
||||||
}
|
}
|
||||||
|
|
||||||
List.prototype = Object.create(BaseOverviewsDataview.prototype);
|
List.prototype = Object.create(BaseOverviewsDataview.prototype);
|
||||||
|
11
package.json
11
package.json
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"name": "windshaft-cartodb",
|
"name": "windshaft-cartodb",
|
||||||
"version": "3.9.3",
|
"version": "3.9.9",
|
||||||
"description": "A map tile server for CartoDB",
|
"description": "A map tile server for CartoDB",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"cartodb"
|
"cartodb"
|
||||||
@ -16,11 +16,12 @@
|
|||||||
"contributors": [
|
"contributors": [
|
||||||
"Simon Tokumine <simon@vizzuality.com>",
|
"Simon Tokumine <simon@vizzuality.com>",
|
||||||
"Javi Santana <jsantana@vizzuality.com>",
|
"Javi Santana <jsantana@vizzuality.com>",
|
||||||
"Sandro Santilli <strk@vizzuality.com>"
|
"Sandro Santilli <strk@vizzuality.com>",
|
||||||
|
"Carlos Matallín <matallo@carto.com>"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"body-parser": "~1.14.0",
|
"body-parser": "~1.14.0",
|
||||||
"camshaft": "0.55.5",
|
"camshaft": "0.55.6",
|
||||||
"cartodb-psql": "0.8.0",
|
"cartodb-psql": "0.8.0",
|
||||||
"cartodb-query-tables": "0.2.0",
|
"cartodb-query-tables": "0.2.0",
|
||||||
"cartodb-redis": "0.13.2",
|
"cartodb-redis": "0.13.2",
|
||||||
@ -37,9 +38,9 @@
|
|||||||
"request": "~2.79.0",
|
"request": "~2.79.0",
|
||||||
"step": "~0.0.6",
|
"step": "~0.0.6",
|
||||||
"step-profiler": "~0.3.0",
|
"step-profiler": "~0.3.0",
|
||||||
"turbo-carto": "0.19.1",
|
"turbo-carto": "0.19.2",
|
||||||
"underscore": "~1.6.0",
|
"underscore": "~1.6.0",
|
||||||
"windshaft": "3.2.1",
|
"windshaft": "3.2.2",
|
||||||
"yargs": "~5.0.0"
|
"yargs": "~5.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -145,4 +145,182 @@ describe('aggregations happy cases', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var widgetSearchExpects = {
|
||||||
|
'count': [ { category: 'other_a', value: 3 } ],
|
||||||
|
'sum': [ { category: 'other_a', value: 6 } ],
|
||||||
|
'avg': [ { category: 'other_a', value: 2 } ],
|
||||||
|
'max': [ { category: 'other_a', value: 3 } ],
|
||||||
|
'min': [ { category: 'other_a', value: 1 } ]
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.keys(operations_and_values).forEach(function (operation) {
|
||||||
|
var description = 'should search OTHER category using "' + operation + '"';
|
||||||
|
|
||||||
|
it(description, function (done) {
|
||||||
|
this.testClient = new TestClient(aggregationOperationMapConfig(operation, query_other, 'cat', 'val'));
|
||||||
|
this.testClient.widgetSearch('cat', 'other_a', function (err, res, searchResult) {
|
||||||
|
assert.ifError(err);
|
||||||
|
|
||||||
|
assert.ok(searchResult);
|
||||||
|
assert.equal(searchResult.type, 'aggregation');
|
||||||
|
|
||||||
|
assert.equal(searchResult.categories.length, 1);
|
||||||
|
assert.deepEqual(
|
||||||
|
searchResult.categories,
|
||||||
|
widgetSearchExpects[operation]
|
||||||
|
);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('aggregation-dataview: special float values', function() {
|
||||||
|
|
||||||
|
afterEach(function(done) {
|
||||||
|
if (this.testClient) {
|
||||||
|
this.testClient.drain(done);
|
||||||
|
} else {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function createMapConfig(layers, dataviews, analysis) {
|
||||||
|
return {
|
||||||
|
version: '1.5.0',
|
||||||
|
layers: layers,
|
||||||
|
dataviews: dataviews || {},
|
||||||
|
analyses: analysis || []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var mapConfig = createMapConfig(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "cartodb",
|
||||||
|
"options": {
|
||||||
|
"source": {
|
||||||
|
"id": "a0"
|
||||||
|
},
|
||||||
|
"cartocss": "#points { marker-width: 10; marker-fill: red; }",
|
||||||
|
"cartocss_version": "2.3.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
{
|
||||||
|
val_aggregation: {
|
||||||
|
source: {
|
||||||
|
id: 'a0'
|
||||||
|
},
|
||||||
|
type: 'aggregation',
|
||||||
|
options: {
|
||||||
|
column: 'cat',
|
||||||
|
aggregation: 'avg',
|
||||||
|
aggregationColumn: 'val'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sum_aggregation_numeric: {
|
||||||
|
source: {
|
||||||
|
id: 'a1'
|
||||||
|
},
|
||||||
|
type: 'aggregation',
|
||||||
|
options: {
|
||||||
|
column: 'cat',
|
||||||
|
aggregation: 'sum',
|
||||||
|
aggregationColumn: 'val'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "a0",
|
||||||
|
"type": "source",
|
||||||
|
"params": {
|
||||||
|
"query": [
|
||||||
|
'SELECT',
|
||||||
|
' null::geometry the_geom_webmercator,',
|
||||||
|
' CASE',
|
||||||
|
' WHEN x % 4 = 0 THEN \'infinity\'::float',
|
||||||
|
' WHEN x % 4 = 1 THEN \'-infinity\'::float',
|
||||||
|
' WHEN x % 4 = 2 THEN \'NaN\'::float',
|
||||||
|
' ELSE x',
|
||||||
|
' END AS val,',
|
||||||
|
' CASE',
|
||||||
|
' WHEN x % 2 = 0 THEN \'category_1\'',
|
||||||
|
' ELSE \'category_2\'',
|
||||||
|
' END AS cat',
|
||||||
|
'FROM generate_series(1, 1000) x'
|
||||||
|
].join('\n')
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"id": "a1",
|
||||||
|
"type": "source",
|
||||||
|
"params": {
|
||||||
|
"query": [
|
||||||
|
'SELECT',
|
||||||
|
' null::geometry the_geom_webmercator,',
|
||||||
|
' CASE',
|
||||||
|
' WHEN x % 3 = 0 THEN \'NaN\'::numeric',
|
||||||
|
' WHEN x % 3 = 1 THEN x',
|
||||||
|
' ELSE x',
|
||||||
|
' END AS val,',
|
||||||
|
' CASE',
|
||||||
|
' WHEN x % 2 = 0 THEN \'category_1\'',
|
||||||
|
' ELSE \'category_2\'',
|
||||||
|
' END AS cat',
|
||||||
|
'FROM generate_series(1, 1000) x'
|
||||||
|
].join('\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Source a0
|
||||||
|
// -----------------------------------------------
|
||||||
|
// the_geom_webmercator | val | cat
|
||||||
|
// ----------------------+-----------+------------
|
||||||
|
// | -Infinity | category_2
|
||||||
|
// | NaN | category_1
|
||||||
|
// | 3 | category_2
|
||||||
|
// | Infinity | category_1
|
||||||
|
// | -Infinity | category_2
|
||||||
|
// | NaN | category_1
|
||||||
|
// | 7 | category_2
|
||||||
|
// | Infinity | category_1
|
||||||
|
// | -Infinity | category_2
|
||||||
|
// | NaN | category_1
|
||||||
|
// | 11 | category_2
|
||||||
|
// | " | "
|
||||||
|
|
||||||
|
var filters = [{ own_filter: 0 }, {}];
|
||||||
|
filters.forEach(function (filter) {
|
||||||
|
it('should handle special float values using filter: ' + JSON.stringify(filter), function(done) {
|
||||||
|
this.testClient = new TestClient(mapConfig, 1234);
|
||||||
|
this.testClient.getDataview('val_aggregation', { own_filter: 0 }, function(err, dataview) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.ok(dataview.infinities === (250 + 250));
|
||||||
|
assert.ok(dataview.nans === 250);
|
||||||
|
assert.ok(dataview.categories.length === 1);
|
||||||
|
dataview.categories.forEach(function (category) {
|
||||||
|
assert.ok(category.category === 'category_2');
|
||||||
|
assert.ok(category.value === 501);
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle special numeric values using filter: ' + JSON.stringify(filter), function(done) {
|
||||||
|
this.testClient = new TestClient(mapConfig, 1234);
|
||||||
|
this.testClient.getDataview('sum_aggregation_numeric', { own_filter: 0 }, function(err, dataview) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.ok(dataview.nans === 333);
|
||||||
|
assert.ok(dataview.categories.length === 2);
|
||||||
|
dataview.categories.forEach(function (category) {
|
||||||
|
assert.ok(category.value !== null);
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
80
test/acceptance/dataviews/formula.js
Normal file
80
test/acceptance/dataviews/formula.js
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
require('../../support/test_helper');
|
||||||
|
var assert = require('../../support/assert');
|
||||||
|
var TestClient = require('../../support/test-client');
|
||||||
|
|
||||||
|
function createMapConfig(layers, dataviews, analysis) {
|
||||||
|
return {
|
||||||
|
version: '1.5.0',
|
||||||
|
layers: layers,
|
||||||
|
dataviews: dataviews || {},
|
||||||
|
analyses: analysis || []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('formula-dataview: special float values', function() {
|
||||||
|
|
||||||
|
afterEach(function(done) {
|
||||||
|
if (this.testClient) {
|
||||||
|
this.testClient.drain(done);
|
||||||
|
} else {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var mapConfig = createMapConfig(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "cartodb",
|
||||||
|
"options": {
|
||||||
|
"source": {
|
||||||
|
"id": "a0"
|
||||||
|
},
|
||||||
|
"cartocss": "#points { marker-width: 10; marker-fill: red; }",
|
||||||
|
"cartocss_version": "2.3.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
{
|
||||||
|
val_formula: {
|
||||||
|
source: {
|
||||||
|
id: 'a0'
|
||||||
|
},
|
||||||
|
type: 'formula',
|
||||||
|
options: {
|
||||||
|
column: 'val',
|
||||||
|
operation: 'avg'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "a0",
|
||||||
|
"type": "source",
|
||||||
|
"params": {
|
||||||
|
"query": [
|
||||||
|
'SELECT',
|
||||||
|
' null::geometry the_geom_webmercator,',
|
||||||
|
' CASE',
|
||||||
|
' WHEN x % 4 = 0 THEN \'infinity\'::float',
|
||||||
|
' WHEN x % 4 = 1 THEN \'-infinity\'::float',
|
||||||
|
' WHEN x % 4 = 2 THEN \'NaN\'::float',
|
||||||
|
' ELSE x',
|
||||||
|
' END AS val',
|
||||||
|
'FROM generate_series(1, 1000) x'
|
||||||
|
].join('\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
it('should filter infinities out and count them in the summary', function(done) {
|
||||||
|
this.testClient = new TestClient(mapConfig, 1234);
|
||||||
|
this.testClient.getDataview('val_formula', {}, function(err, dataview) {
|
||||||
|
assert.ok(!err, err);
|
||||||
|
assert.equal(dataview.result, 501);
|
||||||
|
assert.ok(dataview.infinities === (250 + 250));
|
||||||
|
assert.ok(dataview.nans === 250);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -13,6 +13,15 @@ function createMapConfig(layers, dataviews, analysis) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createMapConfig(layers, dataviews, analysis) {
|
||||||
|
return {
|
||||||
|
version: '1.5.0',
|
||||||
|
layers: layers,
|
||||||
|
dataviews: dataviews || {},
|
||||||
|
analyses: analysis || []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
describe('histogram-dataview', function() {
|
describe('histogram-dataview', function() {
|
||||||
|
|
||||||
afterEach(function(done) {
|
afterEach(function(done) {
|
||||||
@ -90,7 +99,6 @@ describe('histogram-dataview', function() {
|
|||||||
this.testClient = new TestClient(mapConfig, 1234);
|
this.testClient = new TestClient(mapConfig, 1234);
|
||||||
this.testClient.getDataview('pop_max_histogram', params, function(err, res) {
|
this.testClient.getDataview('pop_max_histogram', params, function(err, res) {
|
||||||
assert.ok(!err, err);
|
assert.ok(!err, err);
|
||||||
|
|
||||||
assert.ok(res.errors);
|
assert.ok(res.errors);
|
||||||
assert.equal(res.errors.length, 1);
|
assert.equal(res.errors.length, 1);
|
||||||
assert.ok(res.errors[0].match(/Invalid number format for parameter 'bins'/));
|
assert.ok(res.errors[0].match(/Invalid number format for parameter 'bins'/));
|
||||||
@ -689,3 +697,70 @@ describe('histogram-dataview for date column type', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('histogram-dataview: special float valuer', function() {
|
||||||
|
|
||||||
|
afterEach(function(done) {
|
||||||
|
if (this.testClient) {
|
||||||
|
this.testClient.drain(done);
|
||||||
|
} else {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var mapConfig = createMapConfig(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "cartodb",
|
||||||
|
"options": {
|
||||||
|
"source": {
|
||||||
|
"id": "a0"
|
||||||
|
},
|
||||||
|
"cartocss": "#points { marker-width: 10; marker-fill: red; }",
|
||||||
|
"cartocss_version": "2.3.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
{
|
||||||
|
val_histogram: {
|
||||||
|
source: {
|
||||||
|
id: 'a0'
|
||||||
|
},
|
||||||
|
type: 'histogram',
|
||||||
|
options: {
|
||||||
|
column: 'val'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "a0",
|
||||||
|
"type": "source",
|
||||||
|
"params": {
|
||||||
|
"query": [
|
||||||
|
'SELECT',
|
||||||
|
' null::geometry the_geom_webmercator,',
|
||||||
|
' CASE',
|
||||||
|
' WHEN x % 4 = 0 THEN \'infinity\'::float',
|
||||||
|
' WHEN x % 4 = 1 THEN \'-infinity\'::float',
|
||||||
|
' WHEN x % 4 = 2 THEN \'NaN\'::float',
|
||||||
|
' ELSE x',
|
||||||
|
' END AS val',
|
||||||
|
'FROM generate_series(1, 1000) x'
|
||||||
|
].join('\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
it('should filter infinities out and count them in the summary', function(done) {
|
||||||
|
this.testClient = new TestClient(mapConfig, 1234);
|
||||||
|
this.testClient.getDataview('val_histogram', {}, function(err, dataview) {
|
||||||
|
assert.ok(!err, err);
|
||||||
|
assert.ok(dataview.infinities === (250 + 250));
|
||||||
|
assert.ok(dataview.nans === 250);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@ -124,6 +124,13 @@ describe('dataviews using tables with overviews', function() {
|
|||||||
params: {
|
params: {
|
||||||
query: 'select * from test_table_overviews'
|
query: 'select * from test_table_overviews'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'data-source-special-float-values',
|
||||||
|
type: 'source',
|
||||||
|
params: {
|
||||||
|
query: 'select * from test_special_float_values_table_overviews'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
dataviews: {
|
dataviews: {
|
||||||
@ -144,6 +151,17 @@ describe('dataviews using tables with overviews', function() {
|
|||||||
aggregationColumn: 'name',
|
aggregationColumn: 'name',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
test_categories_special_values: {
|
||||||
|
type: 'aggregation',
|
||||||
|
source: {
|
||||||
|
id: 'data-source-special-float-values'
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
column: 'name',
|
||||||
|
aggregation: 'sum',
|
||||||
|
aggregationColumn: 'value',
|
||||||
|
}
|
||||||
|
},
|
||||||
test_histogram: {
|
test_histogram: {
|
||||||
type: 'histogram',
|
type: 'histogram',
|
||||||
source: {id: 'data-source'},
|
source: {id: 'data-source'},
|
||||||
@ -160,6 +178,16 @@ describe('dataviews using tables with overviews', function() {
|
|||||||
bins: 2
|
bins: 2
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
test_histogram_special_values: {
|
||||||
|
type: 'histogram',
|
||||||
|
source: {
|
||||||
|
id: 'data-source-special-float-values'
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
column: 'value',
|
||||||
|
bins: 2
|
||||||
|
}
|
||||||
|
},
|
||||||
test_avg: {
|
test_avg: {
|
||||||
type: 'formula',
|
type: 'formula',
|
||||||
source: {id: 'data-source'},
|
source: {id: 'data-source'},
|
||||||
@ -168,6 +196,16 @@ describe('dataviews using tables with overviews', function() {
|
|||||||
operation: 'avg'
|
operation: 'avg'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
test_formula_sum_special_values: {
|
||||||
|
type: 'formula',
|
||||||
|
source: {
|
||||||
|
id: 'data-source-special-float-values'
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
column: 'value',
|
||||||
|
operation: 'sum'
|
||||||
|
}
|
||||||
|
},
|
||||||
test_count: {
|
test_count: {
|
||||||
type: 'formula',
|
type: 'formula',
|
||||||
source: {id: 'data-source'},
|
source: {id: 'data-source'},
|
||||||
@ -202,6 +240,17 @@ describe('dataviews using tables with overviews', function() {
|
|||||||
cartocss_version: '2.3.0',
|
cartocss_version: '2.3.0',
|
||||||
source: { id: 'data-source' }
|
source: { id: 'data-source' }
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'mapnik',
|
||||||
|
options: {
|
||||||
|
sql: 'select * from test_special_float_values_table_overviews',
|
||||||
|
cartocss: '#layer { marker-fill: red; marker-width: 32; marker-allow-overlap: true; }',
|
||||||
|
cartocss_version: '2.3.0',
|
||||||
|
source: {
|
||||||
|
id: 'data-source-special-float-values'
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
@ -212,7 +261,14 @@ describe('dataviews using tables with overviews', function() {
|
|||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
}
|
}
|
||||||
assert.deepEqual(formula_result, {"operation":"sum","result":15,"nulls":0,"type":"formula"});
|
assert.deepEqual(formula_result, {
|
||||||
|
"operation":"sum",
|
||||||
|
"result":15,
|
||||||
|
"infinities": 0,
|
||||||
|
"nans": 0,
|
||||||
|
"nulls":0,
|
||||||
|
"type":"formula"
|
||||||
|
});
|
||||||
|
|
||||||
testClient.drain(done);
|
testClient.drain(done);
|
||||||
});
|
});
|
||||||
@ -224,7 +280,14 @@ describe('dataviews using tables with overviews', function() {
|
|||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
}
|
}
|
||||||
assert.deepEqual(formula_result, {"operation":"avg","result":3,"nulls":0,"type":"formula"});
|
assert.deepEqual(formula_result, {
|
||||||
|
"operation":"avg",
|
||||||
|
"result":3,
|
||||||
|
"nulls":0,
|
||||||
|
"type":"formula",
|
||||||
|
"infinities": 0,
|
||||||
|
"nans": 0
|
||||||
|
});
|
||||||
|
|
||||||
testClient.drain(done);
|
testClient.drain(done);
|
||||||
});
|
});
|
||||||
@ -236,7 +299,14 @@ describe('dataviews using tables with overviews', function() {
|
|||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
}
|
}
|
||||||
assert.deepEqual(formula_result, {"operation":"count","result":5,"nulls":0,"type":"formula"});
|
assert.deepEqual(formula_result, {
|
||||||
|
"operation":"count",
|
||||||
|
"result":5,
|
||||||
|
"nulls":0,
|
||||||
|
"type":"formula",
|
||||||
|
"infinities": 0,
|
||||||
|
"nans": 0
|
||||||
|
});
|
||||||
|
|
||||||
testClient.drain(done);
|
testClient.drain(done);
|
||||||
});
|
});
|
||||||
@ -248,7 +318,14 @@ describe('dataviews using tables with overviews', function() {
|
|||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
}
|
}
|
||||||
assert.deepEqual(formula_result, {"operation":"max","result":5,"nulls":0,"type":"formula"});
|
assert.deepEqual(formula_result, {
|
||||||
|
"operation": "max",
|
||||||
|
"result": 5,
|
||||||
|
"nulls": 0,
|
||||||
|
"infinities": 0,
|
||||||
|
"nans": 0,
|
||||||
|
"type": "formula"
|
||||||
|
});
|
||||||
|
|
||||||
testClient.drain(done);
|
testClient.drain(done);
|
||||||
});
|
});
|
||||||
@ -260,7 +337,14 @@ describe('dataviews using tables with overviews', function() {
|
|||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
}
|
}
|
||||||
assert.deepEqual(formula_result, {"operation":"min","result":1,"nulls":0,"type":"formula"});
|
assert.deepEqual(formula_result, {
|
||||||
|
"operation": "min",
|
||||||
|
"result": 1,
|
||||||
|
"nulls": 0,
|
||||||
|
"infinities": 0,
|
||||||
|
"nans": 0,
|
||||||
|
"type": "formula"
|
||||||
|
});
|
||||||
|
|
||||||
testClient.drain(done);
|
testClient.drain(done);
|
||||||
});
|
});
|
||||||
@ -275,7 +359,14 @@ describe('dataviews using tables with overviews', function() {
|
|||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
}
|
}
|
||||||
assert.deepEqual(formula_result, {"operation":"sum","result":15,"nulls":0,"type":"formula"});
|
assert.deepEqual(formula_result, {
|
||||||
|
"operation":"sum",
|
||||||
|
"result":15,
|
||||||
|
"nulls":0,
|
||||||
|
"infinities": 0,
|
||||||
|
"nans": 0,
|
||||||
|
"type":"formula"
|
||||||
|
});
|
||||||
|
|
||||||
testClient.drain(done);
|
testClient.drain(done);
|
||||||
});
|
});
|
||||||
@ -372,7 +463,14 @@ describe('dataviews using tables with overviews', function() {
|
|||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
}
|
}
|
||||||
assert.deepEqual(formula_result, {"operation":"sum","result":1,"nulls":0,"type":"formula"});
|
assert.deepEqual(formula_result, {
|
||||||
|
"operation":"sum",
|
||||||
|
"result":1,
|
||||||
|
"nulls":0,
|
||||||
|
"infinities": 0,
|
||||||
|
"nans": 0,
|
||||||
|
"type":"formula"
|
||||||
|
});
|
||||||
testClient.drain(done);
|
testClient.drain(done);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -383,7 +481,14 @@ describe('dataviews using tables with overviews', function() {
|
|||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
}
|
}
|
||||||
assert.deepEqual(formula_result, {"operation":"avg","result":1,"nulls":0,"type":"formula"});
|
assert.deepEqual(formula_result, {
|
||||||
|
"operation":"avg",
|
||||||
|
"result":1,
|
||||||
|
"nulls":0,
|
||||||
|
"infinities": 0,
|
||||||
|
"nans": 0,
|
||||||
|
"type":"formula"
|
||||||
|
});
|
||||||
|
|
||||||
testClient.drain(done);
|
testClient.drain(done);
|
||||||
});
|
});
|
||||||
@ -395,7 +500,14 @@ describe('dataviews using tables with overviews', function() {
|
|||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
}
|
}
|
||||||
assert.deepEqual(formula_result, {"operation":"count","result":1,"nulls":0,"type":"formula"});
|
assert.deepEqual(formula_result, {
|
||||||
|
"operation":"count",
|
||||||
|
"result":1,
|
||||||
|
"infinities": 0,
|
||||||
|
"nans": 0,
|
||||||
|
"nulls":0,
|
||||||
|
"type":"formula"
|
||||||
|
});
|
||||||
|
|
||||||
testClient.drain(done);
|
testClient.drain(done);
|
||||||
});
|
});
|
||||||
@ -407,7 +519,14 @@ describe('dataviews using tables with overviews', function() {
|
|||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
}
|
}
|
||||||
assert.deepEqual(formula_result, {"operation":"max","result":1,"nulls":0,"type":"formula"});
|
assert.deepEqual(formula_result, {
|
||||||
|
"operation": "max",
|
||||||
|
"result": 1,
|
||||||
|
"nulls": 0,
|
||||||
|
"infinities": 0,
|
||||||
|
"nans": 0,
|
||||||
|
"type": "formula"
|
||||||
|
});
|
||||||
|
|
||||||
testClient.drain(done);
|
testClient.drain(done);
|
||||||
});
|
});
|
||||||
@ -419,7 +538,14 @@ describe('dataviews using tables with overviews', function() {
|
|||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
}
|
}
|
||||||
assert.deepEqual(formula_result, {"operation":"min","result":1,"nulls":0,"type":"formula"});
|
assert.deepEqual(formula_result, {
|
||||||
|
"operation": "min",
|
||||||
|
"result": 1,
|
||||||
|
"nulls": 0,
|
||||||
|
"infinities": 0,
|
||||||
|
"nans": 0,
|
||||||
|
"type": "formula"
|
||||||
|
});
|
||||||
|
|
||||||
testClient.drain(done);
|
testClient.drain(done);
|
||||||
});
|
});
|
||||||
@ -437,7 +563,14 @@ describe('dataviews using tables with overviews', function() {
|
|||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
}
|
}
|
||||||
assert.deepEqual(formula_result, {"operation":"sum","result":1,"nulls":0,"type":"formula"});
|
assert.deepEqual(formula_result, {
|
||||||
|
"operation":"sum",
|
||||||
|
"result":1,
|
||||||
|
"nulls":0,
|
||||||
|
"infinities": 0,
|
||||||
|
"nans": 0,
|
||||||
|
"type":"formula"
|
||||||
|
});
|
||||||
testClient.drain(done);
|
testClient.drain(done);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -445,5 +578,69 @@ describe('dataviews using tables with overviews', function() {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('aggregation special float values', function () {
|
||||||
|
var params = {};
|
||||||
|
|
||||||
|
it("should expose an aggregation dataview filtering special float values out", function (done) {
|
||||||
|
var testClient = new TestClient(overviewsMapConfig);
|
||||||
|
testClient.getDataview('test_categories_special_values', params, function (err, dataview) {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
assert.deepEqual(dataview, {
|
||||||
|
aggregation: 'sum',
|
||||||
|
count: 5,
|
||||||
|
nulls: 0,
|
||||||
|
nans: 1,
|
||||||
|
infinities: 1,
|
||||||
|
min: 6,
|
||||||
|
max: 6,
|
||||||
|
categoriesCount: 1,
|
||||||
|
categories: [ { category: 'Hawai', value: 6, agg: false } ],
|
||||||
|
type: 'aggregation'
|
||||||
|
});
|
||||||
|
testClient.drain(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should expose a histogram dataview filtering special float values out', function (done) {
|
||||||
|
var testClient = new TestClient(overviewsMapConfig);
|
||||||
|
testClient.getDataview('test_histogram_special_values', params, function (err, dataview) {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
assert.deepEqual(dataview, {
|
||||||
|
bin_width: 0,
|
||||||
|
bins_count: 1,
|
||||||
|
bins_start: 3,
|
||||||
|
nulls: 0,
|
||||||
|
infinities: 1,
|
||||||
|
nans: 1,
|
||||||
|
avg: 3,
|
||||||
|
bins: [ { bin: 0, min: 3, max: 3, avg: 3, freq: 2 } ],
|
||||||
|
type: 'histogram'
|
||||||
|
});
|
||||||
|
testClient.drain(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should expose a formula (sum) dataview filtering special float values out', function (done) {
|
||||||
|
var testClient = new TestClient(overviewsMapConfig);
|
||||||
|
testClient.getDataview('test_formula_sum_special_values', params, function (err, dataview) {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
assert.deepEqual(dataview, {
|
||||||
|
operation: 'sum',
|
||||||
|
result: 6,
|
||||||
|
nulls: 0,
|
||||||
|
nans: 1,
|
||||||
|
infinities: 1,
|
||||||
|
type: 'formula'
|
||||||
|
});
|
||||||
|
testClient.drain(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
53
test/acceptance/mvt.js
Normal file
53
test/acceptance/mvt.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
require('../support/test_helper');
|
||||||
|
|
||||||
|
const assert = require('../support/assert');
|
||||||
|
const TestClient = require('../support/test-client');
|
||||||
|
|
||||||
|
function createMapConfig (sql = TestClient.SQL.ONE_POINT) {
|
||||||
|
return {
|
||||||
|
version: '1.6.0',
|
||||||
|
layers: [{
|
||||||
|
type: "cartodb",
|
||||||
|
options: {
|
||||||
|
sql: sql,
|
||||||
|
cartocss: TestClient.CARTOCSS.POINTS,
|
||||||
|
cartocss_version: '2.3.0',
|
||||||
|
interactivity: 'cartodb_id'
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('mvt', function () {
|
||||||
|
const testCases = [
|
||||||
|
{
|
||||||
|
desc: 'should get empty mvt with code 204 (no content)',
|
||||||
|
coords: { z: 0, x: 0, y: 0 },
|
||||||
|
format: 'mvt',
|
||||||
|
status: 204,
|
||||||
|
mapConfig: createMapConfig(TestClient.SQL.EMPTY)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: 'should get mvt tile with code 200 (ok)',
|
||||||
|
coords: { z: 0, x: 0, y: 0 },
|
||||||
|
format: 'mvt',
|
||||||
|
status: 200,
|
||||||
|
mapConfig: createMapConfig()
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
testCases.forEach(function (test) {
|
||||||
|
it(test.desc, done => {
|
||||||
|
const testClient = new TestClient(test.mapConfig, 1234);
|
||||||
|
const { z, x, y } = test.coords;
|
||||||
|
const { format, status } = test;
|
||||||
|
|
||||||
|
testClient.getTile(z, x, y, { format, status }, (err, res) => {
|
||||||
|
assert.ifError(err);
|
||||||
|
|
||||||
|
assert.equal(res.statusCode, test.status);
|
||||||
|
testClient.drain(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -322,6 +322,25 @@ describe('widgets', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
[adm0name].forEach(function(userQuery) {
|
||||||
|
it('should search with sum aggregation: ' + userQuery, function(done) {
|
||||||
|
this.testClient = new TestClient(aggregationSumMapConfig);
|
||||||
|
this.testClient.widgetSearch('adm0name', userQuery, function (err, res, searchResult) {
|
||||||
|
assert.ok(!err, err);
|
||||||
|
assert.ok(searchResult);
|
||||||
|
assert.equal(searchResult.type, 'aggregation');
|
||||||
|
|
||||||
|
assert.equal(searchResult.categories.length, 1);
|
||||||
|
assert.deepEqual(
|
||||||
|
searchResult.categories,
|
||||||
|
[{ category:"Argentina", value:28015640 }]
|
||||||
|
);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -339,6 +339,78 @@ INSERT INTO _vovw_2_test_table_overviews VALUES
|
|||||||
INSERT INTO _vovw_1_test_table_overviews VALUES
|
INSERT INTO _vovw_1_test_table_overviews VALUES
|
||||||
('2011-09-21 14:02:21.358706', '2011-09-21 14:02:21.314252', 1, 'Hawai', 'Calle de Pérez Galdós 9, Madrid, Spain', 3.0, '0101000020E610000000000000000020C00000000000004440', '0101000020110F000076491621312319C122D4663F1DCC5241', 5);
|
('2011-09-21 14:02:21.358706', '2011-09-21 14:02:21.314252', 1, 'Hawai', 'Calle de Pérez Galdós 9, Madrid, Spain', 3.0, '0101000020E610000000000000000020C00000000000004440', '0101000020110F000076491621312319C122D4663F1DCC5241', 5);
|
||||||
|
|
||||||
|
-- table with overviews whit special float values
|
||||||
|
|
||||||
|
CREATE TABLE test_special_float_values_table_overviews (
|
||||||
|
cartodb_id integer NOT NULL,
|
||||||
|
name character varying,
|
||||||
|
address character varying,
|
||||||
|
value float8,
|
||||||
|
the_geom geometry,
|
||||||
|
the_geom_webmercator geometry,
|
||||||
|
_feature_count integer,
|
||||||
|
CONSTRAINT enforce_dims_the_geom CHECK ((st_ndims(the_geom) = 2)),
|
||||||
|
CONSTRAINT enforce_dims_the_geom_webmercator CHECK ((st_ndims(the_geom_webmercator) = 2)),
|
||||||
|
CONSTRAINT enforce_geotype_the_geom CHECK (((geometrytype(the_geom) = 'POINT'::text) OR (the_geom IS NULL))),
|
||||||
|
CONSTRAINT enforce_geotype_the_geom_webmercator CHECK (((geometrytype(the_geom_webmercator) = 'POINT'::text) OR (the_geom_webmercator IS NULL))),
|
||||||
|
CONSTRAINT enforce_srid_the_geom CHECK ((st_srid(the_geom) = 4326)),
|
||||||
|
CONSTRAINT enforce_srid_the_geom_webmercator CHECK ((st_srid(the_geom_webmercator) = 3857))
|
||||||
|
);
|
||||||
|
|
||||||
|
GRANT ALL ON TABLE test_special_float_values_table_overviews TO :TESTUSER;
|
||||||
|
GRANT SELECT ON TABLE test_special_float_values_table_overviews TO :PUBLICUSER;
|
||||||
|
|
||||||
|
CREATE SEQUENCE test_special_float_values_table_overviews_cartodb_id_seq
|
||||||
|
START WITH 1
|
||||||
|
INCREMENT BY 1
|
||||||
|
NO MINVALUE
|
||||||
|
NO MAXVALUE
|
||||||
|
CACHE 1;
|
||||||
|
|
||||||
|
ALTER SEQUENCE test_special_float_values_table_overviews_cartodb_id_seq OWNED BY test_special_float_values_table_overviews.cartodb_id;
|
||||||
|
|
||||||
|
SELECT pg_catalog.setval('test_special_float_values_table_overviews_cartodb_id_seq', 60, true);
|
||||||
|
|
||||||
|
ALTER TABLE test_special_float_values_table_overviews ALTER COLUMN cartodb_id SET DEFAULT nextval('test_special_float_values_table_overviews_cartodb_id_seq'::regclass);
|
||||||
|
|
||||||
|
INSERT INTO test_special_float_values_table_overviews VALUES
|
||||||
|
(1, 'Hawai', 'Calle de Pérez Galdós 9, Madrid, Spain', 1.0, '0101000020E6100000A6B73F170D990DC064E8D84125364440', '0101000020110F000076491621312319C122D4663F1DCC5241', 1),
|
||||||
|
(2, 'El Estocolmo', 'Calle de la Palma 72, Madrid, Spain', 2.0, '0101000020E6100000C90567F0F7AB0DC0AB07CC43A6364440', '0101000020110F0000C4356B29423319C15DD1092DADCC5241', 1),
|
||||||
|
(3, 'El Rey del Tallarín', 'Plaza Conde de Toreno 2, Madrid, Spain', 'NaN'::float, '0101000020E610000021C8410933AD0DC0CB0EF10F5B364440', '0101000020110F000053E71AC64D3419C10F664E4659CC5241', 1),
|
||||||
|
(4, 'El Lacón', 'Manuel Fernández y González 8, Madrid, Spain', 4.0, '0101000020E6100000BC5983F755990DC07D923B6C22354440', '0101000020110F00005DACDB056F2319C1EC41A980FCCA5241', 1),
|
||||||
|
(5, 'El Pico', 'Calle Divino Pastor 12, Madrid, Spain', 'infinity'::float, '0101000020E61000003B6D8D08C6A10DC0371B2B31CF364440', '0101000020110F00005F716E91992A19C17DAAA4D6DACC5241', 1);
|
||||||
|
|
||||||
|
ALTER TABLE ONLY test_special_float_values_table_overviews ADD CONSTRAINT test_special_float_values_table_overviews_pkey PRIMARY KEY (cartodb_id);
|
||||||
|
|
||||||
|
CREATE INDEX test_special_float_values_table_overviews_the_geom_idx ON test_special_float_values_table_overviews USING gist (the_geom);
|
||||||
|
CREATE INDEX test_special_float_values_table_overviews_the_geom_webmercator_idx ON test_special_float_values_table_overviews USING gist (the_geom_webmercator);
|
||||||
|
|
||||||
|
GRANT ALL ON TABLE test_special_float_values_table_overviews TO :TESTUSER;
|
||||||
|
GRANT SELECT ON TABLE test_special_float_values_table_overviews TO :PUBLICUSER;
|
||||||
|
|
||||||
|
CREATE TABLE _vovw_1_test_special_float_values_table_overviews (
|
||||||
|
cartodb_id integer NOT NULL,
|
||||||
|
name character varying,
|
||||||
|
address character varying,
|
||||||
|
value float8,
|
||||||
|
the_geom geometry,
|
||||||
|
the_geom_webmercator geometry,
|
||||||
|
_feature_count integer,
|
||||||
|
CONSTRAINT enforce_dims_the_geom CHECK ((st_ndims(the_geom) = 2)),
|
||||||
|
CONSTRAINT enforce_dims_the_geom_webmercator CHECK ((st_ndims(the_geom_webmercator) = 2)),
|
||||||
|
CONSTRAINT enforce_geotype_the_geom CHECK (((geometrytype(the_geom) = 'POINT'::text) OR (the_geom IS NULL))),
|
||||||
|
CONSTRAINT enforce_geotype_the_geom_webmercator CHECK (((geometrytype(the_geom_webmercator) = 'POINT'::text) OR (the_geom_webmercator IS NULL))),
|
||||||
|
CONSTRAINT enforce_srid_the_geom CHECK ((st_srid(the_geom) = 4326)),
|
||||||
|
CONSTRAINT enforce_srid_the_geom_webmercator CHECK ((st_srid(the_geom_webmercator) = 3857))
|
||||||
|
);
|
||||||
|
|
||||||
|
GRANT ALL ON TABLE _vovw_1_test_special_float_values_table_overviews TO :TESTUSER;
|
||||||
|
GRANT SELECT ON TABLE _vovw_1_test_special_float_values_table_overviews TO :PUBLICUSER;
|
||||||
|
|
||||||
|
INSERT INTO _vovw_1_test_special_float_values_table_overviews VALUES
|
||||||
|
(1, 'Hawai', 'Calle de Pérez Galdós 9, Madrid, Spain', 3, '0101000020E610000000000000000020C00000000000004440', '0101000020110F000076491621312319C122D4663F1DCC5241', 2),
|
||||||
|
(3, 'El Rey del Tallarín', 'Plaza Conde de Toreno 2, Madrid, Spain', 'NaN'::float, '0101000020E610000021C8410933AD0DC0CB0EF10F5B364440', '0101000020110F000053E71AC64D3419C10F664E4659CC5241', 1),
|
||||||
|
(4, 'El Lacón', 'Manuel Fernández y González 8, Madrid, Spain', 'infinity'::float, '0101000020E6100000BC5983F755990DC07D923B6C22354440', '0101000020110F00005DACDB056F2319C1EC41A980FCCA5241', 2);
|
||||||
|
|
||||||
-- analysis tables -----------------------------------------------
|
-- analysis tables -----------------------------------------------
|
||||||
|
|
||||||
|
@ -75,6 +75,11 @@ module.exports.CARTOCSS = {
|
|||||||
].join('\n')
|
].join('\n')
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports.SQL = {
|
||||||
|
EMPTY: 'select 1 as cartodb_id, null::geometry as the_geom_webmercator',
|
||||||
|
ONE_POINT: 'select 1 as cartodb_id, \'SRID=3857;POINT(0 0)\'::geometry the_geom_webmercator'
|
||||||
|
}
|
||||||
|
|
||||||
TestClient.prototype.getWidget = function(widgetName, params, callback) {
|
TestClient.prototype.getWidget = function(widgetName, params, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
@ -525,7 +530,7 @@ TestClient.prototype.getTile = function(z, x, y, params, callback) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var expectedResponse = {
|
var expectedResponse = {
|
||||||
status: 200,
|
status: params.status || 200,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json; charset=utf-8'
|
'Content-Type': 'application/json; charset=utf-8'
|
||||||
}
|
}
|
||||||
@ -542,7 +547,12 @@ TestClient.prototype.getTile = function(z, x, y, params, callback) {
|
|||||||
|
|
||||||
if (isMvt) {
|
if (isMvt) {
|
||||||
request.encoding = 'binary';
|
request.encoding = 'binary';
|
||||||
expectedResponse.headers['Content-Type'] = 'application/x-protobuf';
|
|
||||||
|
if (expectedResponse.status === 200) {
|
||||||
|
expectedResponse.headers['Content-Type'] = 'application/x-protobuf';
|
||||||
|
} else if (expectedResponse.status === 204) {
|
||||||
|
expectedResponse.headers['Content-Type'] = undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var isGeojson = format.match(/geojson$/);
|
var isGeojson = format.match(/geojson$/);
|
||||||
@ -561,15 +571,16 @@ TestClient.prototype.getTile = function(z, x, y, params, callback) {
|
|||||||
|
|
||||||
assert.response(server, request, expectedResponse, function(res, err) {
|
assert.response(server, request, expectedResponse, function(res, err) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
|
|
||||||
var obj;
|
var obj;
|
||||||
|
|
||||||
if (isPng) {
|
if (isPng) {
|
||||||
obj = mapnik.Image.fromBytes(new Buffer(res.body, 'binary'));
|
obj = mapnik.Image.fromBytes(new Buffer(res.body, 'binary'));
|
||||||
}
|
}
|
||||||
else if (isMvt) {
|
else if (isMvt) {
|
||||||
obj = new mapnik.VectorTile(z, x, y);
|
if (res.body) {
|
||||||
obj.setDataSync(new Buffer(res.body, 'binary'));
|
obj = new mapnik.VectorTile(z, x, y);
|
||||||
|
obj.setDataSync(new Buffer(res.body, 'binary'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
obj = JSON.parse(res.body);
|
obj = JSON.parse(res.body);
|
||||||
|
80
yarn.lock
80
yarn.lock
@ -2,7 +2,7 @@
|
|||||||
# yarn lockfile v1
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
abaculus@cartodb/abaculus#2.0.3-cdb1:
|
"abaculus@github:cartodb/abaculus#2.0.3-cdb1":
|
||||||
version "2.0.3-cdb1"
|
version "2.0.3-cdb1"
|
||||||
resolved "https://codeload.github.com/cartodb/abaculus/tar.gz/f5f34e1c80cdd8d49edd1d6fe3b2220ab2e23aaf"
|
resolved "https://codeload.github.com/cartodb/abaculus/tar.gz/f5f34e1c80cdd8d49edd1d6fe3b2220ab2e23aaf"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -53,8 +53,8 @@ ap@~0.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/ap/-/ap-0.2.0.tgz#ae0942600b29912f0d2b14ec60c45e8f330b6110"
|
resolved "https://registry.yarnpkg.com/ap/-/ap-0.2.0.tgz#ae0942600b29912f0d2b14ec60c45e8f330b6110"
|
||||||
|
|
||||||
aproba@^1.0.3:
|
aproba@^1.0.3:
|
||||||
version "1.1.1"
|
version "1.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.1.1.tgz#95d3600f07710aa0e9298c726ad5ecf2eacbabab"
|
resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.1.2.tgz#45c6629094de4e96f693ef7eab74ae079c240fc1"
|
||||||
|
|
||||||
are-we-there-yet@~1.1.2:
|
are-we-there-yet@~1.1.2:
|
||||||
version "1.1.4"
|
version "1.1.4"
|
||||||
@ -161,10 +161,6 @@ browser-stdout@1.3.0:
|
|||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f"
|
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f"
|
||||||
|
|
||||||
buffer-shims@~1.0.0:
|
|
||||||
version "1.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51"
|
|
||||||
|
|
||||||
buffer-writer@1.0.1:
|
buffer-writer@1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-1.0.1.tgz#22a936901e3029afcd7547eb4487ceb697a3bf08"
|
resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-1.0.1.tgz#22a936901e3029afcd7547eb4487ceb697a3bf08"
|
||||||
@ -198,9 +194,9 @@ camelcase@^3.0.0:
|
|||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
|
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
|
||||||
|
|
||||||
camshaft@0.55.5:
|
camshaft@0.55.6:
|
||||||
version "0.55.5"
|
version "0.55.6"
|
||||||
resolved "https://registry.yarnpkg.com/camshaft/-/camshaft-0.55.5.tgz#6831f74022b06e12ddab8e00953c7cc859598ac4"
|
resolved "https://registry.yarnpkg.com/camshaft/-/camshaft-0.55.6.tgz#11af28051c3b911fb023ae1cafb165bbd040f174"
|
||||||
dependencies:
|
dependencies:
|
||||||
async "^1.5.2"
|
async "^1.5.2"
|
||||||
bunyan "1.8.1"
|
bunyan "1.8.1"
|
||||||
@ -209,7 +205,7 @@ camshaft@0.55.5:
|
|||||||
dot "^1.0.3"
|
dot "^1.0.3"
|
||||||
request "^2.69.0"
|
request "^2.69.0"
|
||||||
|
|
||||||
canvas@cartodb/node-canvas#1.6.2-cdb2:
|
"canvas@github:cartodb/node-canvas#1.6.2-cdb2":
|
||||||
version "1.6.2-cdb2"
|
version "1.6.2-cdb2"
|
||||||
resolved "https://codeload.github.com/cartodb/node-canvas/tar.gz/8acf04557005c633f9e68524488a2657c04f3766"
|
resolved "https://codeload.github.com/cartodb/node-canvas/tar.gz/8acf04557005c633f9e68524488a2657c04f3766"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -235,7 +231,7 @@ carto@CartoDB/carto#0.15.1-cdb1:
|
|||||||
optimist "~0.6.0"
|
optimist "~0.6.0"
|
||||||
underscore "~1.6.0"
|
underscore "~1.6.0"
|
||||||
|
|
||||||
carto@cartodb/carto#0.15.1-cdb3:
|
"carto@github:cartodb/carto#0.15.1-cdb3":
|
||||||
version "0.15.1-cdb3"
|
version "0.15.1-cdb3"
|
||||||
resolved "https://codeload.github.com/cartodb/carto/tar.gz/945f5efb74fd1af1f5e1f69f409f9567f94fb5a7"
|
resolved "https://codeload.github.com/cartodb/carto/tar.gz/945f5efb74fd1af1f5e1f69f409f9567f94fb5a7"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -899,7 +895,7 @@ inflight@^1.0.4:
|
|||||||
once "^1.3.0"
|
once "^1.3.0"
|
||||||
wrappy "1"
|
wrappy "1"
|
||||||
|
|
||||||
inherits@2, inherits@~2.0.0, inherits@~2.0.1:
|
inherits@2, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3:
|
||||||
version "2.0.3"
|
version "2.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
||||||
|
|
||||||
@ -1348,8 +1344,8 @@ nock@~2.11.0:
|
|||||||
propagate "0.3.x"
|
propagate "0.3.x"
|
||||||
|
|
||||||
node-pre-gyp@~0.6.27, node-pre-gyp@~0.6.30, node-pre-gyp@~0.6.31:
|
node-pre-gyp@~0.6.27, node-pre-gyp@~0.6.30, node-pre-gyp@~0.6.31:
|
||||||
version "0.6.34"
|
version "0.6.36"
|
||||||
resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.34.tgz#94ad1c798a11d7fc67381b50d47f8cc18d9799f7"
|
resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.36.tgz#db604112cb74e0d477554e9b505b17abddfab786"
|
||||||
dependencies:
|
dependencies:
|
||||||
mkdirp "^0.5.1"
|
mkdirp "^0.5.1"
|
||||||
nopt "^4.0.1"
|
nopt "^4.0.1"
|
||||||
@ -1388,8 +1384,8 @@ normalize-package-data@^2.3.2:
|
|||||||
validate-npm-package-license "^3.0.1"
|
validate-npm-package-license "^3.0.1"
|
||||||
|
|
||||||
npmlog@^4.0.2:
|
npmlog@^4.0.2:
|
||||||
version "4.1.0"
|
version "4.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.0.tgz#dc59bee85f64f00ed424efb2af0783df25d1c0b5"
|
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
|
||||||
dependencies:
|
dependencies:
|
||||||
are-we-there-yet "~1.1.2"
|
are-we-there-yet "~1.1.2"
|
||||||
console-control-strings "~1.1.0"
|
console-control-strings "~1.1.0"
|
||||||
@ -1708,15 +1704,15 @@ readable-stream@1.1, readable-stream@~1.1.9:
|
|||||||
string_decoder "~0.10.x"
|
string_decoder "~0.10.x"
|
||||||
|
|
||||||
readable-stream@^2.0.6, readable-stream@^2.1.4:
|
readable-stream@^2.0.6, readable-stream@^2.1.4:
|
||||||
version "2.2.9"
|
version "2.3.3"
|
||||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.9.tgz#cf78ec6f4a6d1eb43d26488cac97f042e74b7fc8"
|
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c"
|
||||||
dependencies:
|
dependencies:
|
||||||
buffer-shims "~1.0.0"
|
|
||||||
core-util-is "~1.0.0"
|
core-util-is "~1.0.0"
|
||||||
inherits "~2.0.1"
|
inherits "~2.0.3"
|
||||||
isarray "~1.0.0"
|
isarray "~1.0.0"
|
||||||
process-nextick-args "~1.0.6"
|
process-nextick-args "~1.0.6"
|
||||||
string_decoder "~1.0.0"
|
safe-buffer "~5.1.1"
|
||||||
|
string_decoder "~1.0.3"
|
||||||
util-deprecate "~1.0.1"
|
util-deprecate "~1.0.1"
|
||||||
|
|
||||||
readable-stream@~1.0.2:
|
readable-stream@~1.0.2:
|
||||||
@ -1745,7 +1741,7 @@ repeat-string@^1.5.2:
|
|||||||
version "1.6.1"
|
version "1.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
|
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
|
||||||
|
|
||||||
request@2.x, request@^2.55.0, request@~2.79.0:
|
request@2.x, request@^2.55.0, request@^2.69.0, request@~2.79.0:
|
||||||
version "2.79.0"
|
version "2.79.0"
|
||||||
resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de"
|
resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -1770,7 +1766,7 @@ request@2.x, request@^2.55.0, request@~2.79.0:
|
|||||||
tunnel-agent "~0.4.1"
|
tunnel-agent "~0.4.1"
|
||||||
uuid "^3.0.0"
|
uuid "^3.0.0"
|
||||||
|
|
||||||
request@^2.69.0, request@^2.81.0:
|
request@^2.81.0:
|
||||||
version "2.81.0"
|
version "2.81.0"
|
||||||
resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
|
resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -1827,9 +1823,9 @@ rimraf@~2.4.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
glob "^6.0.1"
|
glob "^6.0.1"
|
||||||
|
|
||||||
safe-buffer@^5.0.1:
|
safe-buffer@^5.0.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||||
version "5.0.1"
|
version "5.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7"
|
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
|
||||||
|
|
||||||
safe-json-stringify@~1:
|
safe-json-stringify@~1:
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
@ -2029,11 +2025,11 @@ string_decoder@~0.10.x:
|
|||||||
version "0.10.31"
|
version "0.10.31"
|
||||||
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
|
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
|
||||||
|
|
||||||
string_decoder@~1.0.0:
|
string_decoder@~1.0.3:
|
||||||
version "1.0.0"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.0.tgz#f06f41157b664d86069f84bdbdc9b0d8ab281667"
|
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab"
|
||||||
dependencies:
|
dependencies:
|
||||||
buffer-shims "~1.0.0"
|
safe-buffer "~5.1.0"
|
||||||
|
|
||||||
stringstream@~0.0.4:
|
stringstream@~0.0.4:
|
||||||
version "0.0.5"
|
version "0.0.5"
|
||||||
@ -2107,15 +2103,15 @@ through@2:
|
|||||||
version "2.3.8"
|
version "2.3.8"
|
||||||
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
|
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
|
||||||
|
|
||||||
tilelive-bridge@cartodb/tilelive-bridge#2.3.1-cdb2:
|
"tilelive-bridge@github:cartodb/tilelive-bridge#2.3.1-cdb3":
|
||||||
version "2.3.1-cdb2"
|
version "2.3.1-cdb3"
|
||||||
resolved "https://codeload.github.com/cartodb/tilelive-bridge/tar.gz/0346c634875ac87dbf8316cb81ac46d2c30fe313"
|
resolved "https://codeload.github.com/cartodb/tilelive-bridge/tar.gz/bde83c8dcf4ada40c7c0eb1b477f212e75399d23"
|
||||||
dependencies:
|
dependencies:
|
||||||
mapnik "~3.5.0"
|
mapnik "~3.5.0"
|
||||||
mapnik-pool "~0.1.3"
|
mapnik-pool "~0.1.3"
|
||||||
sphericalmercator "1.0.x"
|
sphericalmercator "1.0.x"
|
||||||
|
|
||||||
tilelive-mapnik@cartodb/tilelive-mapnik#0.6.18-cdb2:
|
"tilelive-mapnik@github:cartodb/tilelive-mapnik#0.6.18-cdb2":
|
||||||
version "0.6.18-cdb2"
|
version "0.6.18-cdb2"
|
||||||
resolved "https://codeload.github.com/cartodb/tilelive-mapnik/tar.gz/46f1adefee90f3f46c0ede5e0833f8522634a858"
|
resolved "https://codeload.github.com/cartodb/tilelive-mapnik/tar.gz/46f1adefee90f3f46c0ede5e0833f8522634a858"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -2156,9 +2152,9 @@ tunnel-agent@~0.4.1:
|
|||||||
version "0.4.3"
|
version "0.4.3"
|
||||||
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb"
|
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb"
|
||||||
|
|
||||||
turbo-carto@0.19.1:
|
turbo-carto@0.19.2:
|
||||||
version "0.19.1"
|
version "0.19.2"
|
||||||
resolved "https://registry.yarnpkg.com/turbo-carto/-/turbo-carto-0.19.1.tgz#c32af073936a4e8f197dfea918e7441c949d7865"
|
resolved "https://registry.yarnpkg.com/turbo-carto/-/turbo-carto-0.19.2.tgz#062d68e59f89377f0cfa69a2717c047fe95e32fd"
|
||||||
dependencies:
|
dependencies:
|
||||||
cartocolor "4.0.0"
|
cartocolor "4.0.0"
|
||||||
colorbrewer "1.0.0"
|
colorbrewer "1.0.0"
|
||||||
@ -2278,9 +2274,9 @@ window-size@^0.2.0:
|
|||||||
version "0.2.0"
|
version "0.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075"
|
resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075"
|
||||||
|
|
||||||
windshaft@3.2.1:
|
windshaft@3.2.2:
|
||||||
version "3.2.1"
|
version "3.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/windshaft/-/windshaft-3.2.1.tgz#50a3afa6562315dd9e65e411660970e118f36c19"
|
resolved "https://registry.yarnpkg.com/windshaft/-/windshaft-3.2.2.tgz#7afb9d8fd8bba1bf02d39c06e8bbe5a451aad953"
|
||||||
dependencies:
|
dependencies:
|
||||||
abaculus cartodb/abaculus#2.0.3-cdb1
|
abaculus cartodb/abaculus#2.0.3-cdb1
|
||||||
canvas cartodb/node-canvas#1.6.2-cdb2
|
canvas cartodb/node-canvas#1.6.2-cdb2
|
||||||
@ -2297,7 +2293,7 @@ windshaft@3.2.1:
|
|||||||
sphericalmercator "1.0.4"
|
sphericalmercator "1.0.4"
|
||||||
step "~0.0.6"
|
step "~0.0.6"
|
||||||
tilelive "5.12.2"
|
tilelive "5.12.2"
|
||||||
tilelive-bridge cartodb/tilelive-bridge#2.3.1-cdb2
|
tilelive-bridge cartodb/tilelive-bridge#2.3.1-cdb3
|
||||||
tilelive-mapnik cartodb/tilelive-mapnik#0.6.18-cdb2
|
tilelive-mapnik cartodb/tilelive-mapnik#0.6.18-cdb2
|
||||||
torque.js "~2.11.0"
|
torque.js "~2.11.0"
|
||||||
underscore "~1.6.0"
|
underscore "~1.6.0"
|
||||||
|
Loading…
Reference in New Issue
Block a user