2018-10-23 23:45:42 +08:00
|
|
|
'use strict';
|
|
|
|
|
2016-03-19 00:22:02 +08:00
|
|
|
var _ = require('underscore');
|
|
|
|
var PSQL = require('cartodb-psql');
|
|
|
|
var BBoxFilter = require('../models/filter/bbox');
|
2016-03-22 20:10:42 +08:00
|
|
|
var DataviewFactory = require('../models/dataview/factory');
|
2016-05-14 00:46:58 +08:00
|
|
|
var DataviewFactoryWithOverviews = require('../models/dataview/overviews/factory');
|
2018-03-23 00:53:24 +08:00
|
|
|
const dbParamsFromReqParams = require('../utils/database-params');
|
2019-10-07 16:06:01 +08:00
|
|
|
var OverviewsQueryRewriter = require('../utils/overviews-query-rewriter');
|
2016-05-12 00:16:18 +08:00
|
|
|
var overviewsQueryRewriter = new OverviewsQueryRewriter({
|
2019-11-12 20:10:35 +08:00
|
|
|
zoom_level: 'cartodb.CDB_ZoomFromScale(!scale_denominator!)'
|
2016-05-12 00:16:18 +08:00
|
|
|
});
|
2016-03-19 00:22:02 +08:00
|
|
|
|
2016-06-16 23:27:00 +08:00
|
|
|
var dot = require('dot');
|
|
|
|
dot.templateSettings.strip = false;
|
|
|
|
|
2016-04-14 23:09:07 +08:00
|
|
|
function DataviewBackend(analysisBackend) {
|
|
|
|
this.analysisBackend = analysisBackend;
|
2016-03-19 00:22:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = DataviewBackend;
|
|
|
|
|
2017-10-09 22:29:35 +08:00
|
|
|
DataviewBackend.prototype.getDataview = function (mapConfigProvider, user, params, callback) {
|
2018-07-30 21:19:53 +08:00
|
|
|
const dataviewName = params.dataviewName;
|
2017-10-09 22:29:35 +08:00
|
|
|
|
2018-07-30 21:19:53 +08:00
|
|
|
mapConfigProvider.getMapConfig(function (err, mapConfig) {
|
|
|
|
if (err) {
|
|
|
|
return callback(err);
|
|
|
|
}
|
2016-03-19 00:22:02 +08:00
|
|
|
|
2018-07-30 21:19:53 +08:00
|
|
|
var dataviewDefinition = getDataviewDefinition(mapConfig.obj(), dataviewName);
|
|
|
|
if (!dataviewDefinition) {
|
2018-08-16 18:16:20 +08:00
|
|
|
const error = new Error(`Dataview '${dataviewName}' does not exist`);
|
2018-07-30 21:59:43 +08:00
|
|
|
error.type = 'dataview';
|
|
|
|
error.http_status = 400;
|
|
|
|
return callback(error);
|
2018-07-30 21:19:53 +08:00
|
|
|
}
|
2017-12-13 16:45:35 +08:00
|
|
|
|
2018-07-31 19:26:38 +08:00
|
|
|
if (!validFilterParams(params)) {
|
2018-07-30 22:00:30 +08:00
|
|
|
const error = new Error('Both own_filter and no_filters cannot be sent in the same request');
|
|
|
|
error.type = 'dataview';
|
|
|
|
error.http_status = 400;
|
2018-07-31 18:15:20 +08:00
|
|
|
return callback(error);
|
2018-07-30 21:19:53 +08:00
|
|
|
}
|
2018-03-23 00:53:24 +08:00
|
|
|
|
2018-07-31 19:33:33 +08:00
|
|
|
var pg;
|
|
|
|
var overrideParams;
|
|
|
|
var dataview;
|
2018-07-30 21:52:04 +08:00
|
|
|
|
|
|
|
try {
|
2018-07-31 19:33:33 +08:00
|
|
|
pg = new PSQL(dbParamsFromReqParams(params));
|
|
|
|
var query = getQueryWithFilters(dataviewDefinition, params);
|
|
|
|
var queryRewriteData = getQueryRewriteData(mapConfig, dataviewDefinition, params);
|
|
|
|
var dataviewFactory = DataviewFactoryWithOverviews.getFactory(overviewsQueryRewriter, queryRewriteData, {
|
|
|
|
bbox: params.bbox
|
|
|
|
});
|
|
|
|
dataview = dataviewFactory.getDataview(query, dataviewDefinition);
|
2018-07-31 19:26:38 +08:00
|
|
|
var ownFilter = +params.own_filter;
|
2018-07-30 21:52:04 +08:00
|
|
|
overrideParams = getOverrideParams(params, !!ownFilter);
|
|
|
|
} catch (error) {
|
|
|
|
return callback(error);
|
|
|
|
}
|
|
|
|
|
2018-08-29 21:06:48 +08:00
|
|
|
dataview.getResult(pg, overrideParams, function (err, dataviewResult, stats = {}) {
|
2018-07-30 21:19:53 +08:00
|
|
|
if (err) {
|
|
|
|
return callback(err);
|
|
|
|
}
|
|
|
|
|
2018-08-29 19:50:21 +08:00
|
|
|
return callback(null, dataviewResult, stats);
|
2018-07-30 21:19:53 +08:00
|
|
|
});
|
|
|
|
});
|
2016-03-19 00:22:02 +08:00
|
|
|
};
|
|
|
|
|
2018-07-31 19:26:38 +08:00
|
|
|
function validFilterParams (params) {
|
|
|
|
var ownFilter = +params.own_filter;
|
|
|
|
var noFilters = +params.no_filters;
|
|
|
|
|
|
|
|
return !(Number.isFinite(ownFilter) && Number.isFinite(noFilters));
|
|
|
|
}
|
|
|
|
|
|
|
|
function getQueryWithFilters (dataviewDefinition, params) {
|
|
|
|
var ownFilter = +params.own_filter;
|
|
|
|
var noFilters = +params.no_filters;
|
|
|
|
var query = getDataviewQuery(dataviewDefinition, ownFilter, noFilters);
|
|
|
|
|
|
|
|
if (params.bbox) {
|
|
|
|
var bboxFilter = new BBoxFilter({column: 'the_geom_webmercator', srid: 3857}, {bbox: params.bbox});
|
|
|
|
query = bboxFilter.sql(query);
|
|
|
|
}
|
|
|
|
|
|
|
|
return query;
|
|
|
|
}
|
|
|
|
|
2017-12-13 16:45:35 +08:00
|
|
|
function getDataviewQuery(dataviewDefinition, ownFilter, noFilters) {
|
2017-12-12 18:54:09 +08:00
|
|
|
if (noFilters) {
|
2017-10-20 22:19:24 +08:00
|
|
|
return dataviewDefinition.sql.no_filters;
|
|
|
|
} else if (ownFilter === 1) {
|
|
|
|
return dataviewDefinition.sql.own_filter_on;
|
2017-10-20 22:47:56 +08:00
|
|
|
} else {
|
|
|
|
return dataviewDefinition.sql.own_filter_off;
|
2017-10-20 22:19:24 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-01 00:57:38 +08:00
|
|
|
function getQueryRewriteData(mapConfig, dataviewDefinition, params) {
|
|
|
|
var sourceId = dataviewDefinition.source.id; // node.id
|
|
|
|
var layer = _.find(mapConfig.obj().layers, function(l) {
|
|
|
|
return l.options.source && (l.options.source.id === sourceId);
|
|
|
|
});
|
|
|
|
var queryRewriteData = layer && layer.options.query_rewrite_data;
|
|
|
|
if (queryRewriteData && dataviewDefinition.node.type === 'source') {
|
|
|
|
queryRewriteData = _.extend({}, queryRewriteData, {
|
|
|
|
filters: dataviewDefinition.node.filters,
|
|
|
|
unfiltered_query: dataviewDefinition.sql.own_filter_on
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-06-01 01:00:37 +08:00
|
|
|
if (params.bbox && queryRewriteData) {
|
|
|
|
var bbox_filter_definition = {
|
|
|
|
type: 'bbox',
|
|
|
|
options: {
|
|
|
|
column: 'the_geom_webmercator',
|
|
|
|
srid: 3857
|
|
|
|
},
|
|
|
|
params: {
|
|
|
|
bbox: params.bbox
|
|
|
|
}
|
|
|
|
};
|
|
|
|
queryRewriteData = _.extend(queryRewriteData, { bbox_filter: bbox_filter_definition });
|
2017-06-01 00:57:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return queryRewriteData;
|
|
|
|
}
|
|
|
|
|
2017-06-01 00:52:41 +08:00
|
|
|
function getOverrideParams(params, ownFilter) {
|
2017-11-07 23:14:47 +08:00
|
|
|
var overrideParams = _.reduce(_.pick(params, 'start', 'end', 'bins', 'offset', 'categories'),
|
2017-06-01 00:52:41 +08:00
|
|
|
function castNumbers(overrides, val, k) {
|
|
|
|
if (!Number.isFinite(+val)) {
|
|
|
|
throw new Error('Invalid number format for parameter \'' + k + '\'');
|
|
|
|
}
|
|
|
|
overrides[k] = +val;
|
|
|
|
return overrides;
|
|
|
|
},
|
|
|
|
{ownFilter: ownFilter}
|
|
|
|
);
|
2017-06-09 00:34:25 +08:00
|
|
|
|
2017-08-03 18:24:05 +08:00
|
|
|
// validation will be delegated to the proper dataview
|
|
|
|
if (params.aggregation !== undefined) {
|
2017-06-09 00:34:25 +08:00
|
|
|
overrideParams.aggregation = params.aggregation;
|
|
|
|
}
|
|
|
|
|
|
|
|
return overrideParams;
|
2017-06-01 00:52:41 +08:00
|
|
|
}
|
|
|
|
|
2017-09-29 18:32:46 +08:00
|
|
|
DataviewBackend.prototype.search = function (mapConfigProvider, user, dataviewName, params, callback) {
|
2018-07-31 17:59:39 +08:00
|
|
|
mapConfigProvider.getMapConfig(function (err, mapConfig) {
|
|
|
|
if (err) {
|
|
|
|
return callback(err);
|
|
|
|
}
|
2016-03-23 19:14:17 +08:00
|
|
|
|
2018-07-31 17:59:39 +08:00
|
|
|
var dataviewDefinition = getDataviewDefinition(mapConfig.obj(), dataviewName);
|
|
|
|
if (!dataviewDefinition) {
|
2018-08-16 18:16:20 +08:00
|
|
|
const error = new Error(`Dataview '${dataviewName}' does not exist`);
|
2018-07-31 17:59:39 +08:00
|
|
|
error.type = 'dataview';
|
|
|
|
error.http_status = 400;
|
|
|
|
return callback(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
var pg;
|
2018-07-31 18:43:54 +08:00
|
|
|
var query;
|
|
|
|
var dataview;
|
2018-07-31 18:47:03 +08:00
|
|
|
var userQuery = params.q;
|
2016-03-23 19:14:17 +08:00
|
|
|
|
2018-07-31 17:59:39 +08:00
|
|
|
try {
|
|
|
|
pg = new PSQL(dbParamsFromReqParams(params));
|
2018-07-31 19:26:38 +08:00
|
|
|
query = getQueryWithOwnFilters(dataviewDefinition, params);
|
2018-07-31 18:43:54 +08:00
|
|
|
dataview = DataviewFactory.getDataview(query, dataviewDefinition);
|
2018-07-31 17:59:39 +08:00
|
|
|
} catch (error) {
|
|
|
|
return callback(error);
|
|
|
|
}
|
2016-03-23 19:14:17 +08:00
|
|
|
|
2018-07-31 17:59:39 +08:00
|
|
|
dataview.search(pg, userQuery, function (err, result) {
|
|
|
|
if (err) {
|
|
|
|
return callback(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
return callback(null, result);
|
|
|
|
});
|
|
|
|
});
|
2016-03-23 19:14:17 +08:00
|
|
|
};
|
|
|
|
|
2018-07-31 19:26:38 +08:00
|
|
|
function getQueryWithOwnFilters (dataviewDefinition, params) {
|
2018-07-31 18:43:54 +08:00
|
|
|
var ownFilter = +params.own_filter;
|
|
|
|
ownFilter = !!ownFilter;
|
|
|
|
|
|
|
|
var query = (ownFilter) ? dataviewDefinition.sql.own_filter_on : dataviewDefinition.sql.own_filter_off;
|
|
|
|
|
|
|
|
if (params.bbox) {
|
|
|
|
var bboxFilter = new BBoxFilter({ column: 'the_geom', srid: 4326 }, { bbox: params.bbox });
|
|
|
|
query = bboxFilter.sql(query);
|
|
|
|
}
|
|
|
|
|
|
|
|
return query;
|
|
|
|
}
|
|
|
|
|
2016-03-19 00:22:02 +08:00
|
|
|
function getDataviewDefinition(mapConfig, dataviewName) {
|
|
|
|
var dataviews = mapConfig.dataviews || {};
|
|
|
|
return dataviews[dataviewName];
|
|
|
|
}
|