Implement category and range filters
This commit is contained in:
parent
24f7bc6596
commit
3d8f6576aa
@ -1,13 +1,38 @@
|
||||
// this is meant as a hack for development
|
||||
// TODO: reproduce here the filter application of Camshaft
|
||||
var queryBuilder = require('camshaft/lib/filter/query-builder');
|
||||
|
||||
var filters = {
|
||||
category: require('./camshaft/category'),
|
||||
range: require('./camshaft/range')
|
||||
};
|
||||
|
||||
function createFilter(filterDefinition) {
|
||||
var filterType = filterDefinition.type.toLowerCase();
|
||||
if (!filters.hasOwnProperty(filterType)) {
|
||||
throw new Error('Unknown filter type: ' + filterType);
|
||||
}
|
||||
return new filters[filterType](filterDefinition.column, filterDefinition.params);
|
||||
}
|
||||
|
||||
function CamshaftFilters(filters) {
|
||||
this.filters = filters;
|
||||
}
|
||||
|
||||
CamshaftFilters.prototype.sql = function(rawSql) {
|
||||
return queryBuilder.getSql(rawSql, this.filters);
|
||||
var filters = this.filters || {};
|
||||
var applyFilters = {};
|
||||
|
||||
return Object.keys(filters)
|
||||
.filter(function(filterName) {
|
||||
return applyFilters.hasOwnProperty(filterName) ? applyFilters[filterName] : true;
|
||||
})
|
||||
.map(function(filterName) {
|
||||
var filterDefinition = filters[filterName];
|
||||
return createFilter(filterDefinition);
|
||||
})
|
||||
.reduce(function(sql, filter) {
|
||||
return filter.sql(sql);
|
||||
}, rawSql);
|
||||
};
|
||||
|
||||
module.exports = CamshaftFilters;
|
||||
|
79
lib/cartodb/models/filter/camshaft/category.js
Normal file
79
lib/cartodb/models/filter/camshaft/category.js
Normal file
@ -0,0 +1,79 @@
|
||||
'use strict';
|
||||
|
||||
var debug = require('debug')('windshaft:filter:category');
|
||||
var dot = require('dot');
|
||||
dot.templateSettings.strip = false;
|
||||
|
||||
var filterQueryTpl = dot.template([
|
||||
'SELECT *',
|
||||
'FROM ({{=it._sql}}) _camshaft_category_filter',
|
||||
'WHERE {{=it._filters}}'
|
||||
].join('\n'));
|
||||
var escapeStringTpl = dot.template('$escape_{{=it._i}}${{=it._value}}$escape_{{=it._i}}$');
|
||||
var inConditionTpl = dot.template('{{=it._column}} IN ({{=it._values}})');
|
||||
var notInConditionTpl = dot.template('{{=it._column}} NOT IN ({{=it._values}})');
|
||||
|
||||
function Category(column, filterParams) {
|
||||
this.column = column;
|
||||
|
||||
if (!Array.isArray(filterParams.accept) && !Array.isArray(filterParams.reject)) {
|
||||
throw new Error('Category filter expects at least one array in accept or reject params');
|
||||
}
|
||||
|
||||
if (Array.isArray(filterParams.accept) && Array.isArray(filterParams.reject)) {
|
||||
if (filterParams.accept.length === 0 && filterParams.reject.length === 0) {
|
||||
throw new Error(
|
||||
'Category filter expects one value either in accept or reject params when both are provided'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
this.accept = filterParams.accept;
|
||||
this.reject = filterParams.reject;
|
||||
}
|
||||
|
||||
module.exports = Category;
|
||||
|
||||
/*
|
||||
- accept: [] => reject all
|
||||
- reject: [] => accept all
|
||||
*/
|
||||
Category.prototype.sql = function(rawSql) {
|
||||
var valueFilters = [];
|
||||
|
||||
if (Array.isArray(this.accept)) {
|
||||
if (this.accept.length > 0) {
|
||||
valueFilters.push(inConditionTpl({
|
||||
_column: this.column,
|
||||
_values: this.accept.map(function(value, i) {
|
||||
return Number.isFinite(value) ? value : escapeStringTpl({_i: i, _value: value});
|
||||
}).join(',')
|
||||
}));
|
||||
} else {
|
||||
valueFilters.push('0 = 1');
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(this.reject)) {
|
||||
if (this.reject.length > 0) {
|
||||
valueFilters.push(notInConditionTpl({
|
||||
_column: this.column,
|
||||
_values: this.reject.map(function (value, i) {
|
||||
return Number.isFinite(value) ? value : escapeStringTpl({_i: i, _value: value});
|
||||
}).join(',')
|
||||
}));
|
||||
} else {
|
||||
valueFilters.push('1 = 1');
|
||||
}
|
||||
}
|
||||
|
||||
debug(filterQueryTpl({
|
||||
_sql: rawSql,
|
||||
_filters: valueFilters.join(' AND ')
|
||||
}));
|
||||
|
||||
return filterQueryTpl({
|
||||
_sql: rawSql,
|
||||
_filters: valueFilters.join(' AND ')
|
||||
});
|
||||
};
|
43
lib/cartodb/models/filter/camshaft/range.js
Normal file
43
lib/cartodb/models/filter/camshaft/range.js
Normal file
@ -0,0 +1,43 @@
|
||||
'use strict';
|
||||
|
||||
var dot = require('dot');
|
||||
dot.templateSettings.strip = false;
|
||||
|
||||
var betweenFilterTpl = dot.template('{{=it._column}} BETWEEN {{=it._min}} AND {{=it._max}}');
|
||||
var minFilterTpl = dot.template('{{=it._column}} >= {{=it._min}}');
|
||||
var maxFilterTpl = dot.template('{{=it._column}} <= {{=it._max}}');
|
||||
var filterQueryTpl = dot.template('SELECT * FROM ({{=it._sql}}) _camshaft_range_filter WHERE {{=it._filter}}');
|
||||
|
||||
function Range(column, filterParams) {
|
||||
this.column = column;
|
||||
|
||||
if (!Number.isFinite(filterParams.min) && !Number.isFinite(filterParams.max)) {
|
||||
throw new Error('Range filter expect to have at least one value in min or max numeric params');
|
||||
}
|
||||
|
||||
this.min = filterParams.min;
|
||||
this.max = filterParams.max;
|
||||
this.columnType = filterParams.columnType;
|
||||
}
|
||||
|
||||
module.exports = Range;
|
||||
|
||||
Range.prototype.sql = function(rawSql) {
|
||||
var minMaxFilter;
|
||||
if (Number.isFinite(this.min) && Number.isFinite(this.max)) {
|
||||
minMaxFilter = betweenFilterTpl({
|
||||
_column: this.column,
|
||||
_min: this.min,
|
||||
_max: this.max
|
||||
});
|
||||
} else if (Number.isFinite(this.min)) {
|
||||
minMaxFilter = minFilterTpl({ _column: this.column, _min: this.min });
|
||||
} else {
|
||||
minMaxFilter = maxFilterTpl({ _column: this.column, _max: this.max });
|
||||
}
|
||||
|
||||
return filterQueryTpl({
|
||||
_sql: rawSql,
|
||||
_filter: minMaxFilter
|
||||
});
|
||||
};
|
Loading…
Reference in New Issue
Block a user