diff --git a/src/backend/memory.js b/src/backend/memory.js
index daf78dbf..39c11e9c 100644
--- a/src/backend/memory.js
+++ b/src/backend/memory.js
@@ -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;
};
diff --git a/src/model.js b/src/model.js
index 2628903e..0d9b3975 100644
--- a/src/model.js
+++ b/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
diff --git a/src/widget.filtereditor.js b/src/widget.filtereditor.js
index a53a8b69..56962891 100644
--- a/src/widget.filtereditor.js
+++ b/src/widget.filtereditor.js
@@ -15,7 +15,8 @@ my.FilterEditor = Backbone.View.extend({
\
\
\
\
',
+ filterTemplates: {
+ term: ' \
+ \
+ ',
+ geo_distance: ' \
+ \
+ '
+ },
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');
diff --git a/test/model.test.js b/test/model.test.js
index 0433be74..05508888 100644
--- a/test/model.test.js
+++ b/test/model.test.js
@@ -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]);
diff --git a/test/widget.filtereditor.test.js b/test/widget.filtereditor.test.js
index 627db158..f991918a 100644
--- a/test/widget.filtereditor.test.js
+++ b/test/widget.filtereditor.test.js
@@ -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();
+});