Adapt QueryRewriter to new requirements

This commit is contained in:
Javier Goizueta
2016-05-12 18:30:10 +02:00
parent 5fb7f07498
commit 85788f42a6
+120 -26
View File
@@ -1,5 +1,10 @@
var _ = require('underscore');
var TableNameParser = require('./table_name_parser');
// this is meant as a hack for development (otherwise we'd be coupling
// this to Camshaft impolementation)
var queryBuilder = require('camshaft/lib/filter/query-builder');
function OverviewsQueryRewriter(options) {
this.options = options;
@@ -119,26 +124,34 @@ function replace_table_in_query(sql, old_table_name, replacement) {
return sql.replace(new RegExp(regexp, 'g'), replacement);
}
function overviews_query(query, overviews, zoom_level_expression) {
function replace_table_in_query_with_schema(query, table, schema, replacement) {
if ( replacement ) {
query = replace_table_in_query(query, table, replacement);
var parsed_table = TableNameParser.parse(table);
if (!parsed_table.schema && schema) {
// replace also the qualified table name, if the table wasn't qualified
parsed_table.schema = schema;
table = TableNameParser.table_identifier(parsed_table);
query = replace_table_in_query(query, table, replacement);
}
}
return query;
}
// Build query to use overviews for a variant zoom level (given by a expression to
// be evaluated by the database server)
function overviews_query_with_zoom_expression(query, overviews, zoom_level_expression) {
var replaced_query = query;
var sql = "WITH\n _vovw_scale AS ( SELECT " + zoom_level_expression + " AS _vovw_z )";
var replacement;
for ( var table in overviews ) {
if (overviews.hasOwnProperty(table)) {
var table_overviews = overviews[table];
var table_view = overviews_view_name(table);
var schema = table_overviews.schema;
replacement = "(\n" + overviews_view_for_table(table, table_overviews) + "\n ) AS " + table_view;
replaced_query = replace_table_in_query(replaced_query, table, replacement);
var parsed_table = TableNameParser.parse(table);
if (!parsed_table.schema && schema) {
// replace also the qualified table name, if the table wasn't qualified
parsed_table.schema = schema;
table = TableNameParser.table_identifier(parsed_table);
replaced_query = replace_table_in_query(replaced_query, table, replacement);
}
}
}
_.each(_.keys(overviews), function(table) {
var table_overviews = overviews[table];
var table_view = overviews_view_name(table);
var schema = table_overviews.schema;
replacement = "(\n" + overviews_view_for_table(table, table_overviews) + "\n ) AS " + table_view;
replaced_query = replace_table_in_query_with_schema(replaced_query, table, schema, replacement);
});
if ( replaced_query !== query ) {
sql += "\n";
sql += replaced_query;
@@ -148,6 +161,54 @@ function overviews_query(query, overviews, zoom_level_expression) {
return sql;
}
// Build query to use overviews for a specific zoom level value
function overviews_query_with_definite_zoom(query, overviews, zoom_level) {
var replaced_query = query;
var replacement;
_.each(_.keys(overviews), function(table) {
var table_overviews = overviews[table];
var schema = table_overviews.schema;
replacement = overview_table_for_zoom_level(table_overviews, zoom_level);
replaced_query = replace_table_in_query_with_schema(replaced_query, table, schema, replacement);
});
return replaced_query;
}
// Find a suitable overview table for a specific zoom_level
function overview_table_for_zoom_level(table_overviews, zoom_level) {
var overview_table;
if ( table_overviews ) {
overview_table = table_overviews[zoom_level];
if ( !overview_table ) {
_.every(_.keys(table_overviews).sort(function(x,y){ return x-y; }), function(overview_zoom) {
if ( +overview_zoom > +zoom_level ) {
overview_table = table_overviews[overview_zoom];
return false;
} else {
return true;
}
});
}
}
if ( overview_table ) {
overview_table = overview_table.table;
}
return overview_table;
}
function apply_filters_to_query(query, filters) {
// TODO: it'd be nice to have a public API in camshaft to apply filters!
if ( filters && !_.isEmpty(filters)) {
query = queryBuilder.getSql(query, filters);
}
return query;
}
function zoom_level_for_bounding_box(query, bounding_box) {
// TODO: implement
return '100'; // force use of base table
}
// Transform an SQL query so that it uses overviews.
// overviews contains metadata about the overviews to be used:
// { 'table-name': {1: { table: 'overview-table-1' }, ... }, ... }
@@ -166,13 +227,50 @@ function overviews_query(query, overviews, zoom_level_expression) {
// doesnn't prevent substitution inside literals).
// But the transformation will currently only be applied to simple queries
// of the form detected by the overviews_supported_query function.
OverviewsQueryRewriter.prototype.query = function(query, data) {
var overviews = this.overviews_metadata(data);
if ( !overviews || !this.is_supported_query(query)) {
OverviewsQueryRewriter.prototype.query = function(query, data, options) {
options = options || {};
var overviews = data && data.overviews;
var unfiltered_query = data && data.unfiltered_query;
var filters = data && data.filters;
if ( !unfiltered_query ) {
unfiltered_query = query;
}
if ( !overviews || !this.is_supported_query(unfiltered_query)) {
return query;
}
var zoom_level_expression = this.options.zoom_level || '0';
return overviews_query(query, overviews, zoom_level_expression);
var rewritten_query;
var zoom_level_expression = this.options.zoom_level;
var zoom_level;
if ( _.has(options, 'bounding_box') ) {
// The presence of this key, even if it has a null value indicates the
// query cannot handle dynamic zoom expressinos (is not for the tiler)
zoom_level = zoom_level_for_bounding_box(unfiltered_query, options.bounding_box);
} else if ( options.zoom_level ) {
zoom_level = options.zoom_level;
}
if ( !zoom_level && !zoom_level_expression ) {
zoom_level = '0';
}
if ( zoom_level || zoom_level === '0' || zoom_level === 0 ) {
rewritten_query = overviews_query_with_definite_zoom(unfiltered_query, overviews, zoom_level);
} else {
rewritten_query = overviews_query_with_zoom_expression(unfiltered_query, overviews, zoom_level_expression);
}
if ( rewritten_query === unfiltered_query ) {
// could not or didn't need to alter the query
rewritten_query = query;
} else {
rewritten_query = apply_filters_to_query(rewritten_query, filters);
}
return rewritten_query;
};
OverviewsQueryRewriter.prototype.is_supported_query = function(sql) {
@@ -188,7 +286,3 @@ OverviewsQueryRewriter.prototype.is_supported_query = function(sql) {
);
return !!(sql.match(unwrapped_query) || sql.match(wrapped_query));
};
OverviewsQueryRewriter.prototype.overviews_metadata = function(data) {
return data && data.overviews;
};