[#111,filter/geo][m]: geo filter support - filter editor working though not sure actual query is working (!).
* Extensive refactoring of Model.Query and View.FilterEditor to do this cleanly (geo_distance and term treated similarly now)
This commit is contained in:
parent
952c85a912
commit
8fe04ddd4f
@ -90,10 +90,13 @@ this.recline.Backend.Memory = this.recline.Backend.Memory || {};
|
||||
// in place filtering
|
||||
this._applyFilters = function(results, queryObj) {
|
||||
_.each(queryObj.filters, function(filter) {
|
||||
results = _.filter(results, function(doc) {
|
||||
var fieldId = _.keys(filter.term)[0];
|
||||
return (doc[fieldId] == filter.term[fieldId]);
|
||||
});
|
||||
// if a term filter ...
|
||||
if (filter.term) {
|
||||
results = _.filter(results, function(doc) {
|
||||
var fieldId = _.keys(filter.term)[0];
|
||||
return (doc[fieldId] == filter.term[fieldId]);
|
||||
});
|
||||
}
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
49
src/model.js
49
src/model.js
@ -393,7 +393,7 @@ my.FieldList = Backbone.Collection.extend({
|
||||
// may just pass this straight through e.g. for an SQL backend this could be
|
||||
// the full SQL query
|
||||
//
|
||||
// * filters: dict of ElasticSearch filters. These will be and-ed together for
|
||||
// * filters: array of ElasticSearch filters. These will be and-ed together for
|
||||
// execution.
|
||||
//
|
||||
// **Examples**
|
||||
@ -417,6 +417,37 @@ my.Query = Backbone.Model.extend({
|
||||
filters: []
|
||||
};
|
||||
},
|
||||
_filterTemplates: {
|
||||
term: {
|
||||
'{{fieldId}}': ''
|
||||
},
|
||||
geo_distance: {
|
||||
distance: '10km',
|
||||
'{{fieldId}}': {
|
||||
lon: 0,
|
||||
lat: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
// ### addFilter
|
||||
//
|
||||
// Add a new filter (appended to the list of filters) with default
|
||||
// value. To set value for the filter use updateFilter.
|
||||
//
|
||||
// @param type type of this filter e.g. term, geo_distance etc
|
||||
// @param fieldId the field to add this filter on
|
||||
addFilter: function(type, fieldId) {
|
||||
var tmpl = JSON.stringify(this._filterTemplates[type]);
|
||||
var filters = this.get('filters');
|
||||
var filter = {};
|
||||
filter[type] = JSON.parse(Mustache.render(tmpl, {type: type, fieldId: fieldId}));
|
||||
filter[type]._type = type;
|
||||
filter[type]._field = fieldId;
|
||||
filters.push(filter);
|
||||
this.trigger('change:filters:new-blank');
|
||||
},
|
||||
updateFilter: function(index, value) {
|
||||
},
|
||||
// #### addTermFilter
|
||||
//
|
||||
// Set (update or add) a terms filter to filters
|
||||
@ -436,6 +467,22 @@ my.Query = Backbone.Model.extend({
|
||||
this.trigger('change:filters:new-blank');
|
||||
}
|
||||
},
|
||||
addGeoDistanceFilter: function(field) {
|
||||
var filters = this.get('filters');
|
||||
var filter = {
|
||||
geo_distance: {
|
||||
distance: '10km',
|
||||
}
|
||||
};
|
||||
filter.geo_distance[field] = {
|
||||
'lon': 0,
|
||||
'lat': 0
|
||||
};
|
||||
filters.push(filter);
|
||||
this.set({filters: filters});
|
||||
// adding a new blank filter and do not want to trigger a new query
|
||||
this.trigger('change:filters:new-blank');
|
||||
},
|
||||
// ### removeFilter
|
||||
//
|
||||
// Remove a filter from filters at index filterIndex
|
||||
|
||||
@ -15,7 +15,8 @@ my.FilterEditor = Backbone.View.extend({
|
||||
<fieldset> \
|
||||
<label>Filter type</label> \
|
||||
<select class="filterType"> \
|
||||
<option value="term">Term (text) filter</option> \
|
||||
<option value="term">Term (text)</option> \
|
||||
<option value="geo_distance">Geo distance</option> \
|
||||
</select> \
|
||||
<label>Field</label> \
|
||||
<select class="fields"> \
|
||||
@ -27,21 +28,37 @@ my.FilterEditor = Backbone.View.extend({
|
||||
</fieldset> \
|
||||
</form> \
|
||||
<form class="form-stacked js-edit"> \
|
||||
{{#termFilters}} \
|
||||
<div class="control-group filter-term filter" data-filter-id={{id}}> \
|
||||
<label class="control-label" for="">{{label}}</label> \
|
||||
<div class="controls"> \
|
||||
<input type="text" value="{{value}}" name="{{fieldId}}" data-filter-field="{{fieldId}}" data-filter-id="{{id}}" data-filter-type="term" /> \
|
||||
<a class="js-remove-filter" href="#">×</a> \
|
||||
</div> \
|
||||
</div> \
|
||||
{{/termFilters}} \
|
||||
{{#termFilters.length}} \
|
||||
{{#filters}} \
|
||||
{{{filterRender}}} \
|
||||
{{/filters}} \
|
||||
{{#filters.length}} \
|
||||
<button type="submit" class="btn">Update</button> \
|
||||
{{/termFilters.length}} \
|
||||
{{/filters.length}} \
|
||||
</form> \
|
||||
</div> \
|
||||
',
|
||||
filterTemplates: {
|
||||
term: ' \
|
||||
<div class="control-group filter-{{_type}} filter"> \
|
||||
<label class="control-label" for="">{{_field}}</label> \
|
||||
<div class="controls"> \
|
||||
<input type="text" value="{{_value}}" name="term" data-filter-field="{{_field}}" data-filter-id="{{id}}" data-filter-type="{{_type}}" /> \
|
||||
<a class="js-remove-filter" href="#">×</a> \
|
||||
</div> \
|
||||
</div> \
|
||||
',
|
||||
geo_distance: ' \
|
||||
<div class="control-group filter-{{_type}} filter"> \
|
||||
<label class="control-label" for="">{{_field}}</label> \
|
||||
<a class="js-remove-filter" href="#">×</a> \
|
||||
<div class="controls"> \
|
||||
<input type="text" value="{{_value.lon}}" name="lon" data-filter-field="{{_field}}" data-filter-id="{{id}}" data-filter-type="{{_type}}" /> \
|
||||
<input type="text" value="{{_value.lat}}" name="lat" data-filter-field="{{_field}}" data-filter-id="{{id}}" data-filter-type="{{_type}}" /> \
|
||||
<input type="text" value="{{distance}}" name="distance" data-filter-field="{{_field}}" data-filter-id="{{id}}" data-filter-type="{{_type}}" /> \
|
||||
</div> \
|
||||
</div> \
|
||||
'
|
||||
},
|
||||
events: {
|
||||
'click .js-remove-filter': 'onRemoveFilter',
|
||||
'click .js-add-filter': 'onAddFilterShow',
|
||||
@ -51,30 +68,28 @@ my.FilterEditor = Backbone.View.extend({
|
||||
initialize: function() {
|
||||
this.el = $(this.el);
|
||||
_.bindAll(this, 'render');
|
||||
this.model.fields.bind('all', this.render);
|
||||
this.model.queryState.bind('change', this.render);
|
||||
this.model.queryState.bind('change:filters:new-blank', this.render);
|
||||
this.render();
|
||||
},
|
||||
render: function() {
|
||||
var self = this;
|
||||
var tmplData = $.extend(true, {}, this.model.queryState.toJSON());
|
||||
// we will use idx in list as there id ...
|
||||
tmplData.filters = _.map(tmplData.filters, function(filter, idx) {
|
||||
filter.id = idx;
|
||||
return filter;
|
||||
});
|
||||
tmplData.termFilters = _.filter(tmplData.filters, function(filter) {
|
||||
return filter.term !== undefined;
|
||||
});
|
||||
tmplData.termFilters = _.map(tmplData.termFilters, function(filter) {
|
||||
var fieldId = _.keys(filter.term)[0];
|
||||
return {
|
||||
id: filter.id,
|
||||
fieldId: fieldId,
|
||||
label: fieldId,
|
||||
value: filter.term[fieldId]
|
||||
};
|
||||
});
|
||||
tmplData.fields = this.model.fields.toJSON();
|
||||
tmplData.filterRender = function() {
|
||||
var filterType = _.keys(this)[0];
|
||||
var _data = this[filterType];
|
||||
_data.id = this.id;
|
||||
_data._type = filterType;
|
||||
_data._value = _data[_data._field];
|
||||
return Mustache.render(self.filterTemplates[filterType], _data);
|
||||
};
|
||||
var out = Mustache.render(this.template, tmplData);
|
||||
this.el.html(out);
|
||||
},
|
||||
@ -90,9 +105,7 @@ my.FilterEditor = Backbone.View.extend({
|
||||
$target.hide();
|
||||
var filterType = $target.find('select.filterType').val();
|
||||
var field = $target.find('select.fields').val();
|
||||
if (filterType === 'term') {
|
||||
this.model.queryState.addTermFilter(field);
|
||||
}
|
||||
this.model.queryState.addFilter(filterType, field);
|
||||
// trigger render explicitly as queryState change will not be triggered (as blank value for filter)
|
||||
this.render();
|
||||
},
|
||||
@ -109,10 +122,20 @@ my.FilterEditor = Backbone.View.extend({
|
||||
var $form = $(e.target);
|
||||
_.each($form.find('input'), function(input) {
|
||||
var $input = $(input);
|
||||
var filterIndex = parseInt($input.attr('data-filter-id'));
|
||||
var value = $input.val();
|
||||
var filterType = $input.attr('data-filter-type');
|
||||
var fieldId = $input.attr('data-filter-field');
|
||||
filters[filterIndex].term[fieldId] = value;
|
||||
var filterIndex = parseInt($input.attr('data-filter-id'));
|
||||
var name = $input.attr('name');
|
||||
var value = $input.val();
|
||||
if (filterType === 'term') {
|
||||
filters[filterIndex].term[fieldId] = value;
|
||||
} else if (filterType === 'geo_distance') {
|
||||
if (name === 'distance') {
|
||||
filters[filterIndex].geo_distance.distance = parseInt(value);
|
||||
} else {
|
||||
filters[filterIndex].geo_distance[fieldId][name] = parseFloat(value);
|
||||
}
|
||||
}
|
||||
});
|
||||
self.model.queryState.set({filters: filters});
|
||||
self.model.queryState.trigger('change');
|
||||
|
||||
@ -150,6 +150,33 @@ test('Query', function () {
|
||||
});
|
||||
|
||||
test('Query.addFilter', function () {
|
||||
var query = new recline.Model.Query();
|
||||
query.addFilter('term', 'xyz');
|
||||
var exp = {
|
||||
term: {
|
||||
xyz: '',
|
||||
_field: 'xyz',
|
||||
_type: 'term'
|
||||
}
|
||||
};
|
||||
deepEqual(exp, query.get('filters')[0]);
|
||||
|
||||
query.addFilter('geo_distance', 'xyz');
|
||||
var exp = {
|
||||
geo_distance: {
|
||||
distance: '10km',
|
||||
xyz: {
|
||||
lon: 0,
|
||||
lat: 0
|
||||
},
|
||||
_field: 'xyz',
|
||||
_type: 'geo_distance'
|
||||
}
|
||||
};
|
||||
deepEqual(exp, query.get('filters')[1]);
|
||||
});
|
||||
|
||||
test('Query.addTermFilter', function () {
|
||||
var query = new recline.Model.Query();
|
||||
query.addTermFilter('xyz', 'this-value');
|
||||
deepEqual({term: {xyz: 'this-value'}}, query.get('filters')[0]);
|
||||
|
||||
@ -39,3 +39,28 @@ test('basics', function () {
|
||||
view.remove();
|
||||
});
|
||||
|
||||
test('geo_distance', function () {
|
||||
var dataset = Fixture.getDataset();
|
||||
var view = new recline.View.FilterEditor({
|
||||
model: dataset
|
||||
});
|
||||
$('.fixtures').append(view.el);
|
||||
|
||||
var $addForm = view.el.find('form.js-add');
|
||||
// submit the form
|
||||
$addForm.find('select.filterType').val('geo_distance');
|
||||
$addForm.find('select.fields').val('lon');
|
||||
$addForm.submit();
|
||||
|
||||
// now check we have new filter
|
||||
$editForm = view.el.find('form.js-edit');
|
||||
equal($editForm.find('.filter-geo_distance').length, 1)
|
||||
deepEqual(_.keys(dataset.queryState.attributes.filters[0].geo_distance), ['distance', 'lon', '_type', '_field']);
|
||||
|
||||
// now set filter value and apply
|
||||
$editForm.find('input[name="lat"]').val(10);
|
||||
$editForm.submit();
|
||||
equal(dataset.queryState.attributes.filters[0].geo_distance.lon.lat, 10);
|
||||
|
||||
view.remove();
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user