Query utils: Use webmercator utils, reuse code and always substitute tokens

This commit is contained in:
Raul Marin 2019-07-15 14:16:21 +02:00
parent bdbe132311
commit 63b6af2ac7

View File

@ -1,48 +1,41 @@
'use strict'; 'use strict';
const SubstitutionTokens = require('./substitution-tokens'); const windshaftUtils = require('windshaft').utils;
function prepareQuery(sql) { module.exports.extractTableNames = function (query) {
var affectedTableRegexCache = {
bbox: /!bbox!/g,
scale_denominator: /!scale_denominator!/g,
pixel_width: /!pixel_width!/g,
pixel_height: /!pixel_height!/g
};
return sql
.replace(affectedTableRegexCache.bbox, 'ST_MakeEnvelope(0,0,0,0)')
.replace(affectedTableRegexCache.scale_denominator, '0')
.replace(affectedTableRegexCache.pixel_width, '1')
.replace(affectedTableRegexCache.pixel_height, '1');
}
module.exports.extractTableNames = function extractTableNames(query) {
return [ return [
'SELECT * FROM CDB_QueryTablesText($windshaft$', 'SELECT * FROM CDB_QueryTablesText($windshaft$',
prepareQuery(query), substituteDummyTokens(query),
'$windshaft$) as tablenames' '$windshaft$) as tablenames'
].join(''); ].join('');
}; };
module.exports.getQueryActualRowCount = function (query) { module.exports.getQueryActualRowCount = function (query) {
return `select COUNT(*) AS rows FROM (${query}) AS __cdb_query`; return `select COUNT(*) AS rows FROM (${substituteDummyTokens(query)}) AS __cdb_query`;
}; };
function getQueryRowEstimation(query) { function getQueryRowEstimation(query) {
return 'select CDB_EstimateRowCount($windshaft$' + query + '$windshaft$) as rows'; return 'select CDB_EstimateRowCount($windshaft$' + substituteDummyTokens(query) + '$windshaft$) as rows';
} }
module.exports.getQueryRowEstimation = getQueryRowEstimation; module.exports.getQueryRowEstimation = getQueryRowEstimation;
function getQueryGeometryType(query, geometryColumn) {
return `
SELECT ST_GeometryType(${geometryColumn}) AS geom_type
FROM (${substituteDummyTokens(query)}) AS __cdb_query
WHERE ${geometryColumn} IS NOT NULL
LIMIT 1
`;
}
module.exports.getQueryGeometryType = getQueryGeometryType;
module.exports.getAggregationMetadata = ctx => ` module.exports.getAggregationMetadata = ctx => `
WITH WITH
rowEstimation AS ( rowEstimation AS (
${getQueryRowEstimation(ctx.query)} ${getQueryRowEstimation(ctx.query)}
), ),
geometryType AS ( geometryType AS (
SELECT ST_GeometryType(${ctx.geometryColumn}) as geom_type ${getQueryGeometryType(ctx.query, ctx.geometryColumn)}
FROM (${ctx.query}) AS __cdb_query WHERE ${ctx.geometryColumn} IS NOT NULL LIMIT 1
) )
SELECT SELECT
rows AS count, rows AS count,
@ -85,7 +78,7 @@ module.exports.getQueryTopCategories = function (query, column, topN, includeNul
const where = includeNulls ? '' : `WHERE ${column} IS NOT NULL`; const where = includeNulls ? '' : `WHERE ${column} IS NOT NULL`;
return ` return `
SELECT ${column} AS category, COUNT(*) AS frequency SELECT ${column} AS category, COUNT(*) AS frequency
FROM (${query}) AS __cdb_query FROM (${substituteDummyTokens(query)}) AS __cdb_query
${where} ${where}
GROUP BY ${column} ORDER BY 2 DESC GROUP BY ${column} ORDER BY 2 DESC
LIMIT ${topN} LIMIT ${topN}
@ -116,7 +109,7 @@ module.exports.getQuerySample = function (query, sampleProb, limit = null, rando
SELECT setseed(${randomSeed}) SELECT setseed(${randomSeed})
) )
SELECT ${columnSelector(columns)} SELECT ${columnSelector(columns)}
FROM (${query}) AS __cdb_query FROM (${substituteDummyTokens(query)}) AS __cdb_query
WHERE random() < ${sampleProb} WHERE random() < ${sampleProb}
${limitClause} ${limitClause}
`; `;
@ -157,19 +150,11 @@ function simpleQueryTable(sql) {
return false; return false;
} }
module.exports.getQueryGeometryType = function (query, geometryColumn) {
return `
SELECT ST_GeometryType(${geometryColumn}) AS geom_type
FROM (${query}) AS __cdb_query
WHERE ${geometryColumn} IS NOT NULL
LIMIT 1
`;
};
function getQueryLimited(query, limit = 0) { function getQueryLimited(query, limit = 0) {
return ` return `
SELECT * SELECT *
FROM (${query}) AS __cdb_query FROM (${substituteDummyTokens(query)}) AS __cdb_query
LIMIT ${limit} LIMIT ${limit}
`; `;
} }
@ -181,32 +166,32 @@ function queryPromise(dbConnection, query) {
} }
function substituteDummyTokens(sql) { function substituteDummyTokens(sql) {
return sql && SubstitutionTokens.replace(sql, { return subsituteTokensForZoom(sql, 0);
bbox: 'ST_MakeEnvelope(0,0,0,0)',
scale_denominator: '0',
pixel_width: '1',
pixel_height: '1'
});
} }
function subsituteTokensForZoom(sql, zoom, singleTile=false) { function subsituteTokensForZoom(sql, zoom) {
const tileRes = 256; if (!sql) {
const wmSize = 6378137.0*2*Math.PI; return undefined;
const nTiles = Math.pow(2, zoom);
const tileSize = wmSize / nTiles;
const resolution = tileSize / tileRes;
const scaleDenominator = resolution / 0.00028;
const x0 = -wmSize/2, y0 = -wmSize/2;
let bbox = `ST_MakeEnvelope(${x0}, ${y0}, ${x0+wmSize}, ${y0+wmSize})`;
if (singleTile) {
bbox = `ST_MakeEnvelope(${x0}, ${y0}, ${x0 + tileSize}, ${y0 + tileSize})`;
} }
return SubstitutionTokens.replace(sql, { const affectedTableRegexCache = {
bbox: bbox, bbox: /!bbox!/g,
scale_denominator: scaleDenominator, scale_denominator: /!scale_denominator!/g,
pixel_width: resolution, pixel_width: /!pixel_width!/g,
pixel_height: resolution pixel_height: /!pixel_height!/g
}); };
const webmercator = new windshaftUtils.WebMercatorHelper();
const resolution = webmercator.getResolution({ z : zoom });
const scaleDenominator = resolution.dividedBy(0.00028);
// We always use the whole world as the bbox
const extent = webmercator.getExtent({ x : 0, y : 0, z : 0 });
return sql
.replace(affectedTableRegexCache.bbox,
`ST_MakeEnvelope(${extent.xmin}, ${extent.ymin}, ${extent.xmax}, ${extent.ymax}, 3857)`)
.replace(affectedTableRegexCache.scale_denominator, scaleDenominator)
.replace(affectedTableRegexCache.pixel_width, resolution)
.replace(affectedTableRegexCache.pixel_height, resolution);
} }
module.exports.queryPromise = queryPromise; module.exports.queryPromise = queryPromise;