diff --git a/lib/cartodb/backends/layer-stats/mapnik-layer-stats.js b/lib/cartodb/backends/layer-stats/mapnik-layer-stats.js index 67d00d90..ee945a51 100644 --- a/lib/cartodb/backends/layer-stats/mapnik-layer-stats.js +++ b/lib/cartodb/backends/layer-stats/mapnik-layer-stats.js @@ -152,80 +152,83 @@ function _columnStats(ctx, columns, dimensions) { if (!columns) { return Promise.resolve(); } - if (_columnsMetadataRequired(ctx.metaOptions)) { - let queries = []; - let aggr = []; - if (ctx.metaOptions.columnStats) { - queries.push(new Promise(resolve => resolve({ columns }))); // add columns as first result - Object.keys(columns).forEach(name => { - aggr = aggr.concat( - columnAggregations(columns[name]) - .map(fn => `${fn}("${name}") AS "${name}_${fn}"`) - ); - if (columns[name].type === 'string') { - const topN = ctx.metaOptions.columnStats.topCategories || 1024; - const includeNulls = ctx.metaOptions.columnStats.hasOwnProperty('includeNulls') ? - ctx.metaOptions.columnStats.includeNulls : - true; - // TODO: ctx.metaOptions.columnStats.maxCategories - // => use PG stats to dismiss columns with more distinct values - queries.push( - queryUtils.queryPromise( - ctx.dbConnection, - _getSQL(ctx, sql => queryUtils.getQueryTopCategories(sql, name, topN, includeNulls)) - ).then(res => ({ columns: { [name]: { categories: res.rows } } })) - ); - } - }); - } - const dimensionsStats = {}; - let dimensionsInfo = {}; - if (ctx.metaOptions.dimensions && dimensions) { - dimensionsInfo = aggregationQuery.infoForOptions({ dimensions }); - Object.keys(dimensionsInfo).forEach(dimName => { - const info = dimensionsInfo[dimName]; - if (info.type === 'timeDimension') { - dimensionsStats[dimName] = { - params: info.params - }; - aggr = aggr.concat( - columnAggregations(info).map(fn => `${fn}(${info.sql}) AS "${dimName}_${fn}"`) - ); - } - }); - } - queries.push( - queryUtils.queryPromise( - ctx.dbConnection, - _getSQL(ctx, sql => `SELECT ${aggr.join(',')} FROM (${sql}) AS __cdb_query`) - ).then(res => { - let stats = { columns: {}, dimensions: {} }; - Object.keys(columns).forEach(name => { - stats.columns[name] = {}; - columnAggregations(columns[name]).forEach(fn => { - stats.columns[name][fn] = res.rows[0][`${name}_${fn}`]; - }); - }); - Object.keys(dimensionsInfo).forEach(name => { - stats.dimensions[name] = stats.dimensions[name] || Object.assign({}, dimensionsStats[name]); - let type = null; - columnAggregations(dimensionsInfo[name]).forEach(fn => { - type = type || - fieldTypeSafe(ctx.dbConnection, res.fields.find(f => f.name === `${name}_${fn}`)); - stats.dimensions[name][fn] = res.rows[0][`${name}_${fn}`]; - }); - stats.dimensions[name].type = type; - }); - return stats; - }) - ); - return Promise.all(queries).then(results => ({ - columns: mergeColumns(results.map(r => r.columns)), - dimensions: mergeColumns(results.map( r => r.dimensions)) - })); + if (!_columnsMetadataRequired(ctx.metaOptions)) { + return Promise.resolve({ columns }); } - return Promise.resolve({ columns }); + + let queries = []; + let aggr = []; + if (ctx.metaOptions.columnStats) { + queries.push(new Promise(resolve => resolve({ columns }))); // add columns as first result + Object.keys(columns).forEach(name => { + aggr = aggr.concat( + columnAggregations(columns[name]) + .map(fn => `${fn}("${name}") AS "${name}_${fn}"`) + ); + if (columns[name].type === 'string') { + const topN = ctx.metaOptions.columnStats.topCategories || 1024; + const includeNulls = ctx.metaOptions.columnStats.hasOwnProperty('includeNulls') ? + ctx.metaOptions.columnStats.includeNulls : + true; + + // TODO: ctx.metaOptions.columnStats.maxCategories + // => use PG stats to dismiss columns with more distinct values + queries.push( + queryUtils.queryPromise( + ctx.dbConnection, + _getSQL(ctx, sql => queryUtils.getQueryTopCategories(sql, name, topN, includeNulls)) + ).then(res => ({ columns: { [name]: { categories: res.rows } } })) + ); + } + }); + } + const dimensionsStats = {}; + let dimensionsInfo = {}; + if (ctx.metaOptions.dimensions && dimensions) { + dimensionsInfo = aggregationQuery.infoForOptions({ dimensions }); + Object.keys(dimensionsInfo).forEach(dimName => { + const info = dimensionsInfo[dimName]; + if (info.type === 'timeDimension') { + dimensionsStats[dimName] = { + params: info.params + }; + aggr = aggr.concat( + columnAggregations(info).map(fn => `${fn}(${info.sql}) AS "${dimName}_${fn}"`) + ); + } + }); + } + queries.push( + queryUtils.queryPromise( + ctx.dbConnection, + _getSQL(ctx, sql => `SELECT ${aggr.join(',')} FROM (${sql}) AS __cdb_query`) + ).then(res => { + let stats = { columns: {}, dimensions: {} }; + Object.keys(columns).forEach(name => { + stats.columns[name] = {}; + columnAggregations(columns[name]).forEach(fn => { + stats.columns[name][fn] = res.rows[0][`${name}_${fn}`]; + }); + }); + Object.keys(dimensionsInfo).forEach(name => { + stats.dimensions[name] = stats.dimensions[name] || Object.assign({}, dimensionsStats[name]); + let type = null; + columnAggregations(dimensionsInfo[name]).forEach(fn => { + type = type || + fieldTypeSafe(ctx.dbConnection, res.fields.find(f => f.name === `${name}_${fn}`)); + stats.dimensions[name][fn] = res.rows[0][`${name}_${fn}`]; + }); + stats.dimensions[name].type = type; + }); + return stats; + }) + ); + return Promise.all(queries).then(results => ({ + columns: mergeColumns(results.map(r => r.columns)), + dimensions: mergeColumns(results.map( r => r.dimensions)) + })); + } // This is adapted from SQL API: