Windshaft-cartodb/test/acceptance/stats/mapnik_stats_layergroup.js

668 lines
24 KiB
JavaScript
Raw Normal View History

'use strict';
require('../../support/test_helper');
var assert = require('../../support/assert');
var TestClient = require('../../support/test-client');
2018-10-23 23:47:48 +08:00
const serverOptions = require('../../../lib/cartodb/server_options');
const suites = [
{
desc: 'mvt (mapnik)',
usePostGIS: false
},
{
2018-10-23 23:47:48 +08:00
desc: 'mvt (postgis)',
usePostGIS: true
}
];
2018-10-23 23:47:48 +08:00
suites.forEach(({desc, usePostGIS}) => {
describe(`[${desc}] Create mapnik layergroup`, function() {
const originalUsePostGIS = serverOptions.renderer.mvt.usePostGIS;
before(function() {
2018-10-23 23:47:48 +08:00
serverOptions.renderer.mvt.usePostGIS = usePostGIS;
this.layerStatsConfig = global.environment.enabledFeatures.layerStats;
global.environment.enabledFeatures.layerStats = true;
});
after(function() {
2018-10-23 23:47:48 +08:00
serverOptions.renderer.mvt.usePostGIS = originalUsePostGIS;
global.environment.enabledFeatures.layerStats = this.layerStatsConfig;
});
var cartocssVersion = '2.3.0';
var cartocss = '#layer { line-width:16; }';
var mapnikLayer1 = {
type: 'mapnik',
options: {
sql: 'select * from test_table limit 1',
cartocss_version: cartocssVersion,
cartocss: cartocss
}
};
var mapnikLayer2 = {
type: 'mapnik',
options: {
sql: 'select * from test_table_2 limit 2',
cartocss_version: cartocssVersion,
cartocss: cartocss
}
};
var mapnikLayer3 = {
type: 'mapnik',
options: {
sql: 'select * from test_table_3 limit 3',
cartocss_version: cartocssVersion,
cartocss: cartocss
}
};
var mapnikLayer4 = {
type: 'mapnik',
options: {
sql: [
'select t1.cartodb_id, t1.the_geom, t1.the_geom_webmercator, t2.address',
' from test_table t1, test_table_2 t2',
2018-05-09 02:06:14 +08:00
' where t1.cartodb_id = t2.cartodb_id'
].join(''),
cartocss_version: cartocssVersion,
cartocss: cartocss
}
};
var mapnikLayer100 = {
type: 'mapnik',
options: {
sql: [
'SELECT * FROM test_table_100'
].join(''),
cartocss_version: cartocssVersion,
cartocss: cartocss
}
};
var httpLayer = {
type: 'http',
options: {
urlTemplate: 'http://{s}.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}.png',
subdomains: ['a','b','c']
}
};
var mapnikLayerGeomColumn = {
type: 'mapnik',
options: {
sql: 'select *, the_geom as my_geom from test_table_3 limit 2',
geom_column: 'my_geom',
cartocss_version: cartocssVersion,
cartocss: cartocss
}
};
var mapnikLayerNullCats = {
type: 'mapnik',
options: {
sql: `
WITH geom AS (
SELECT
'SRID=4326;POINT(0 0)'::geometry AS the_geom,
'SRID=3857;POINT(0 0)'::geometry AS the_geom_webmercator
)
SELECT 1 AS cartodb_id, 'A' As cat, geom.* FROM geom
UNION
SELECT 2 AS cartodb_id, 'B' As cat, geom.* FROM geom
UNION
SELECT 2 AS cartodb_id, NULL::text As cat, geom.* FROM geom
`,
cartocss_version: cartocssVersion,
cartocss: cartocss
}
};
function mapnikBasicLayerId(index) {
return 'layer' + index;
}
function typeLayerId(type, index) {
return type + '-' + mapnikBasicLayerId(index);
}
it('with one mapnik layer should response with meta-stats for that layer', function(done) {
var testClient = new TestClient({
version: '1.4.0',
layers: [
mapnikLayer1
]
});
testClient.getLayergroup(function(err, layergroup) {
2018-05-21 23:03:16 +08:00
assert.ifError(err);
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
assert.equal(layergroup.metadata.layers[0].meta.stats.estimatedFeatureCount, 1);
testClient.drain(done);
});
});
it('with two mapnik layer should response with meta-stats for every layer', function(done) {
var testClient = new TestClient({
version: '1.4.0',
layers: [
mapnikLayer1,
mapnikLayer2
]
});
testClient.getLayergroup(function(err, layergroup) {
2018-05-21 23:03:16 +08:00
assert.ifError(err);
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
assert.equal(layergroup.metadata.layers[0].meta.stats.estimatedFeatureCount, 1);
assert.equal(layergroup.metadata.layers[1].id, mapnikBasicLayerId(1));
assert.equal(layergroup.metadata.layers[1].meta.stats.estimatedFeatureCount, 2);
testClient.drain(done);
});
});
it('with three mapnik layer should response with meta-stats for every layer', function(done) {
var testClient = new TestClient({
version: '1.4.0',
layers: [
mapnikLayer1,
mapnikLayer2,
mapnikLayer3
]
});
testClient.getLayergroup(function(err, layergroup) {
2018-05-21 23:03:16 +08:00
assert.ifError(err);
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
assert.equal(layergroup.metadata.layers[0].meta.stats.estimatedFeatureCount, 1);
assert.equal(layergroup.metadata.layers[1].id, mapnikBasicLayerId(1));
assert.equal(layergroup.metadata.layers[1].meta.stats.estimatedFeatureCount, 2);
assert.equal(layergroup.metadata.layers[2].id, mapnikBasicLayerId(2));
assert.equal(layergroup.metadata.layers[2].meta.stats.estimatedFeatureCount, 3);
testClient.drain(done);
});
});
it('with one mapnik layer (sql with join) should response with meta-stats for that layer', function(done) {
var testClient = new TestClient({
version: '1.4.0',
layers: [
mapnikLayer4
]
});
testClient.getLayergroup(function(err, layergroup) {
2018-05-21 23:03:16 +08:00
assert.ifError(err);
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
assert.equal(layergroup.metadata.layers[0].meta.stats.estimatedFeatureCount, 5);
testClient.drain(done);
});
});
it('with two mapnik layer (sql with join) should response with meta-stats for every layer', function(done) {
var testClient = new TestClient({
version: '1.4.0',
layers: [
mapnikLayer4,
mapnikLayer4
]
});
testClient.getLayergroup(function(err, layergroup) {
2018-05-21 23:03:16 +08:00
assert.ifError(err);
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
assert.equal(layergroup.metadata.layers[0].meta.stats.estimatedFeatureCount, 5);
assert.equal(layergroup.metadata.layers[1].id, mapnikBasicLayerId(1));
assert.equal(layergroup.metadata.layers[1].meta.stats.estimatedFeatureCount, 5);
testClient.drain(done);
});
});
it('with two mapnik layer (with & without join) should response with meta-stats for every layer', function(done) {
var testClient = new TestClient({
version: '1.4.0',
layers: [
mapnikLayer3,
mapnikLayer4
]
});
testClient.getLayergroup(function(err, layergroup) {
2018-05-21 23:03:16 +08:00
assert.ifError(err);
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
assert.equal(layergroup.metadata.layers[0].meta.stats.estimatedFeatureCount, 3);
assert.ok(!layergroup.metadata.layers[0].meta.stats[1]);
assert.equal(layergroup.metadata.layers[1].id, mapnikBasicLayerId(1));
assert.equal(layergroup.metadata.layers[1].meta.stats.estimatedFeatureCount, 5);
assert.ok(!layergroup.metadata.layers[2]);
testClient.drain(done);
});
});
it('with mapnik and layer and httplayer should response with layer metadata with same order', function(done) {
var testClient = new TestClient({
version: '1.4.0',
layers: [
mapnikLayer1,
httpLayer
]
});
testClient.getLayergroup(function(err, layergroup) {
2018-05-21 23:03:16 +08:00
assert.ifError(err);
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
assert.equal(layergroup.metadata.layers[0].type, 'mapnik');
assert.equal(layergroup.metadata.layers[0].meta.stats.estimatedFeatureCount, 1);
assert.equal(layergroup.metadata.layers[1].id, typeLayerId('http', 0));
assert.equal(layergroup.metadata.layers[1].type, 'http');
testClient.drain(done);
});
});
it('with httpLayer and mapnik layer should response with layer metadata with same order', function(done) {
var testClient = new TestClient({
version: '1.4.0',
layers: [
httpLayer,
mapnikLayer1
]
});
testClient.getLayergroup(function (err, layergroup) {
2018-05-21 23:03:16 +08:00
assert.ifError(err);
assert.equal(layergroup.metadata.layers[0].id, typeLayerId('http', 0));
assert.equal(layergroup.metadata.layers[0].type, 'http');
assert.ok(!layergroup.metadata.layers[0].meta.cartocss);
assert.equal(layergroup.metadata.layers[1].meta.stats.estimatedFeatureCount, 1);
assert.equal(layergroup.metadata.layers[1].id, mapnikBasicLayerId(0));
assert.equal(layergroup.metadata.layers[1].type, 'mapnik');
assert.equal(layergroup.metadata.layers[1].meta.cartocss, cartocss);
testClient.drain(done);
});
});
it('should work with different geom_column', function(done) {
var testClient = new TestClient({
version: '1.4.0',
layers: [
mapnikLayerGeomColumn
]
});
testClient.getLayergroup(function(err, layergroup) {
2018-05-21 23:03:16 +08:00
assert.ifError(err);
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
// we don't care about stats here as is an aliased column
assert.ok(layergroup.metadata.layers[0].meta.stats.hasOwnProperty('estimatedFeatureCount'));
testClient.drain(done);
});
});
it('should not include the stats part if the FF is disabled', function(done) {
global.environment.enabledFeatures.layerStats = false;
var testClient = new TestClient({
version: '1.4.0',
layers: [
httpLayer,
mapnikLayer1,
httpLayer
]
});
testClient.getLayergroup(function(err, layergroup) {
2018-05-21 23:03:16 +08:00
assert.ifError(err);
assert.equal(layergroup.metadata.layers[0].id, typeLayerId('http', 0));
assert.equal(layergroup.metadata.layers[0].type, 'http');
assert.equal(layergroup.metadata.layers[1].id, mapnikBasicLayerId(0));
assert.equal(layergroup.metadata.layers[1].type, 'mapnik');
assert.ok(!layergroup.metadata.layers[1].meta.hasOwnProperty('stats'));
assert.equal(layergroup.metadata.layers[2].id, typeLayerId('http', 1));
assert.equal(layergroup.metadata.layers[2].type, 'http');
2018-05-09 18:42:42 +08:00
global.environment.enabledFeatures.layerStats = true;
testClient.drain(done);
});
});
2018-05-09 02:06:14 +08:00
function layerWithMetadata(layer, metadata) {
return Object.assign(layer, {
options: Object.assign(layer.options, { metadata })
});
}
it('should provide columns as optional metadata', function(done) {
var testClient = new TestClient({
version: '1.4.0',
layers: [
layerWithMetadata(mapnikLayer4, {
columns: true
})
]
});
testClient.getLayergroup(function(err, layergroup) {
2018-05-21 23:03:16 +08:00
assert.ifError(err);
2018-05-09 02:06:14 +08:00
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
2018-05-09 17:59:24 +08:00
assert.equal(layergroup.metadata.layers[0].meta.stats.estimatedFeatureCount, 5);
2018-05-09 02:06:14 +08:00
const expectedColumns = {
cartodb_id: { type: 'number' },
the_geom: { type: 'geometry' },
the_geom_webmercator: { type: 'geometry' },
address: { type: 'string' }
};
assert.deepEqual(layergroup.metadata.layers[0].meta.stats.columns, expectedColumns);
testClient.drain(done);
});
});
// metadata categories are ordered only partially by descending frequency;
// this orders them completely to avoid ambiguities when comparing
function withSortedCategories(columns) {
function catOrder(a, b) {
if (a.frequency !== b.frequency) {
return b.frequency - a.frequency;
}
if (a.category < b.category) {
return -1;
}
if (a.category > b.category) {
return +1;
}
return 0;
}
let sorted = {};
Object.keys(columns).forEach(name => {
let data = columns[name];
if (data.hasOwnProperty('categories')) {
data = Object.assign(data, { categories: data.categories.sort(catOrder)});
}
sorted[name] = data;
});
return sorted;
}
2018-05-09 02:06:14 +08:00
it('should provide column stats as optional metadata', function(done) {
var testClient = new TestClient({
version: '1.4.0',
layers: [
layerWithMetadata(mapnikLayer4, {
columnStats: true
})
]
});
testClient.getLayergroup(function(err, layergroup) {
2018-05-21 22:59:36 +08:00
assert.ifError(err);
2018-05-09 02:06:14 +08:00
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
2018-05-09 17:59:24 +08:00
assert.equal(layergroup.metadata.layers[0].meta.stats.estimatedFeatureCount, 5);
2018-05-09 02:06:14 +08:00
const expectedColumns = {
cartodb_id: {
type: 'number',
avg: 3,
max: 5,
min: 1,
sum: 15
},
the_geom: { type: 'geometry' },
the_geom_webmercator: { type: 'geometry' },
address: {
type: 'string',
categories: [
{
category: "Calle de la Palma 72, Madrid, Spain",
frequency: 1
},
{
category: "Calle de Pérez Galdós 9, Madrid, Spain",
frequency: 1
},
{
category: "Calle Divino Pastor 12, Madrid, Spain",
frequency: 1
},
{
category: "Manuel Fernández y González 8, Madrid, Spain",
frequency: 1
},
{
category: "Plaza Conde de Toreno 2, Madrid, Spain",
frequency: 1
}
]
}
};
assert.deepEqual(
withSortedCategories(layergroup.metadata.layers[0].meta.stats.columns),
withSortedCategories(expectedColumns)
);
2018-05-09 02:06:14 +08:00
testClient.drain(done);
});
});
it('should limit the number of categories as requested', function(done) {
var testClient = new TestClient({
version: '1.4.0',
layers: [
layerWithMetadata(mapnikLayer4, {
columnStats: { topCategories: 2 }
})
]
});
testClient.getLayergroup(function(err, layergroup) {
assert.ifError(err);
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
const columnsMetadata = layergroup.metadata.layers[0].meta.stats.columns;
assert.equal(columnsMetadata.address.categories.length, 2);
testClient.drain(done);
});
});
it('should include null categories if requested', function(done) {
var testClient = new TestClient({
version: '1.4.0',
layers: [
layerWithMetadata(mapnikLayerNullCats, {
columnStats: { includeNulls: true }
})
]
});
testClient.getLayergroup(function(err, layergroup) {
assert.ifError(err);
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
const columnsMetadata = layergroup.metadata.layers[0].meta.stats.columns;
assert.equal(columnsMetadata.cat.categories.length, 3);
testClient.drain(done);
});
});
it('should not include null categories if not requested', function(done) {
var testClient = new TestClient({
version: '1.4.0',
layers: [
layerWithMetadata(mapnikLayerNullCats, {
columnStats: { includeNulls: false }
})
]
});
testClient.getLayergroup(function(err, layergroup) {
assert.ifError(err);
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
const columnsMetadata = layergroup.metadata.layers[0].meta.stats.columns;
assert.equal(columnsMetadata.cat.categories.length, 2);
testClient.drain(done);
});
});
2018-05-09 02:06:14 +08:00
it('should provide row count as optional metadata', function(done) {
var testClient = new TestClient({
version: '1.4.0',
layers: [
layerWithMetadata(mapnikLayer4, {
featureCount: true
})
]
});
testClient.getLayergroup(function(err, layergroup) {
2018-05-21 23:03:16 +08:00
assert.ifError(err);
2018-05-09 02:06:14 +08:00
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
2018-05-09 17:59:24 +08:00
assert.equal(layergroup.metadata.layers[0].meta.stats.estimatedFeatureCount, 5);
2018-05-09 02:06:14 +08:00
assert.equal(layergroup.metadata.layers[0].meta.stats.featureCount, 5);
testClient.drain(done);
});
});
it('should provide geometry type as optional metadata', function(done) {
var testClient = new TestClient({
version: '1.4.0',
layers: [
layerWithMetadata(mapnikLayer4, {
geometryType: true
})
]
});
testClient.getLayergroup(function(err, layergroup) {
2018-05-21 23:03:16 +08:00
assert.ifError(err);
2018-05-09 02:06:14 +08:00
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
assert.equal(layergroup.metadata.layers[0].meta.stats.estimatedFeatureCount, 5);
assert.equal(layergroup.metadata.layers[0].meta.stats.geometryType, 'ST_Point');
testClient.drain(done);
});
});
it(`should not fail "TypeError: ... 'geom_type' of undefined" for empty results`, function(done) {
var testClient = new TestClient({
version: '1.8.0',
layers: [
{
type: 'mapnik',
options: {
sql: 'select * from test_table where false',
metadata: {
geometryType: true
}
}
}
]
});
testClient.getLayergroup(function(err, layergroup) {
assert.ifError(err);
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
assert.equal(layergroup.metadata.layers[0].meta.stats.geometryType, undefined);
testClient.drain(done);
});
});
2018-05-09 02:06:14 +08:00
it('should provide a sample as optional metadata', function(done) {
var testClient = new TestClient({
version: '1.4.0',
layers: [
layerWithMetadata(mapnikLayer100, {
sample: { num_rows: 30 }
2018-05-09 02:06:14 +08:00
})
]
});
testClient.getLayergroup(function(err, layergroup) {
assert.ifError(err);
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
assert.equal(layergroup.metadata.layers[0].meta.stats.estimatedFeatureCount, 100);
assert(layergroup.metadata.layers[0].meta.stats.sample.length > 0);
const expectedCols = [ 'cartodb_id', 'value', 'the_geom', 'the_geom_webmercator' ].sort();
assert.deepEqual(Object.keys(layergroup.metadata.layers[0].meta.stats.sample[0]).sort(), expectedCols);
testClient.drain(done);
});
});
it('should not provide a sample when the source table is empty', function (done) {
var testClient = new TestClient({
"version": "1.4.0",
"layers": [
{
"type": "mapnik",
"options": {
"sql": "SELECT * FROM test_table_100 limit 0",
"cartocss_version": "2.3.0",
"cartocss": "#layer { line-width:16; }",
"metadata": {
"sample": {
"num_rows": 30
},
"sql": "select * from test_table_100 limit 0"
}
}
}
]
});
2018-05-09 02:06:14 +08:00
testClient.getLayergroup(function(err, layergroup) {
2018-05-21 23:03:16 +08:00
assert.ifError(err);
2018-05-09 02:06:14 +08:00
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
assert.equal(layergroup.metadata.layers[0].meta.stats.estimatedFeatureCount, 100);
2018-05-09 02:06:14 +08:00
assert(layergroup.metadata.layers[0].meta.stats.sample.length > 0);
const expectedCols = [ 'cartodb_id', 'value', 'the_geom', 'the_geom_webmercator' ].sort();
2018-05-09 02:06:14 +08:00
assert.deepEqual(Object.keys(layergroup.metadata.layers[0].meta.stats.sample[0]).sort(), expectedCols);
testClient.drain(done);
});
});
2018-05-28 22:50:53 +08:00
it('can specify sample columns', function(done) {
var testClient = new TestClient({
version: '1.4.0',
layers: [
layerWithMetadata(mapnikLayer100, {
2018-05-28 22:50:53 +08:00
sample: {
num_rows: 30,
include_columns: [ 'cartodb_id', 'the_geom' ]
2018-05-28 22:50:53 +08:00
}
})
]
});
testClient.getLayergroup(function(err, layergroup) {
assert.ifError(err);
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
assert.equal(layergroup.metadata.layers[0].meta.stats.estimatedFeatureCount, 100);
2018-05-28 22:50:53 +08:00
assert(layergroup.metadata.layers[0].meta.stats.sample.length > 0);
const expectedCols = [ 'cartodb_id', 'the_geom' ].sort();
2018-05-28 22:50:53 +08:00
assert.deepEqual(Object.keys(layergroup.metadata.layers[0].meta.stats.sample[0]).sort(), expectedCols);
testClient.drain(done);
});
});
2018-05-09 02:06:14 +08:00
it('should only provide requested optional metadata', function(done) {
var testClient = new TestClient({
version: '1.4.0',
layers: [
layerWithMetadata(mapnikLayer4, {
geometryType: true,
featureCount: true
})
]
});
testClient.getLayergroup(function(err, layergroup) {
2018-05-21 23:03:16 +08:00
assert.ifError(err);
2018-05-09 02:06:14 +08:00
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
assert.equal(layergroup.metadata.layers[0].meta.stats.estimatedFeatureCount, 5);
assert.equal(layergroup.metadata.layers[0].meta.stats.geometryType, 'ST_Point');
assert.equal(layergroup.metadata.layers[0].meta.stats.featureCount, 5);
assert.equal(layergroup.metadata.layers[0].meta.stats.sample, undefined);
assert.equal(layergroup.metadata.layers[0].meta.stats.columns, undefined);
testClient.drain(done);
});
});
});
2018-10-23 23:47:48 +08:00
});