From 905659d86fdb900360bbd27147346e365870a5f9 Mon Sep 17 00:00:00 2001 From: Rufus Pollock Date: Sat, 31 Mar 2012 15:08:27 +0100 Subject: [PATCH] [#62,view/query][m]: start of faceting support with QueryFacetEditor. * QueryFacetEditor in DataExplorer which displays facets but nothing yet happens. * NB: very much less than half way through (things look worse rather than better atm). --- css/data-explorer.css | 4 +++ demo/js/app.js | 33 ++++++++++++++++----- src/model.js | 21 ++++++++++++++ src/view.js | 67 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 118 insertions(+), 7 deletions(-) diff --git a/css/data-explorer.css b/css/data-explorer.css index 0fab70e1..a88f53cd 100644 --- a/css/data-explorer.css +++ b/css/data-explorer.css @@ -60,6 +60,10 @@ clear: both; } +.recline-query-facet-editor { + clear: both; +} + /********************************************************** * Notifications *********************************************************/ diff --git a/demo/js/app.js b/demo/js/app.js index 9198a913..e921031c 100755 --- a/demo/js/app.js +++ b/demo/js/app.js @@ -81,19 +81,38 @@ function localDataset() { , name: '1-my-test-dataset' , id: datasetId }, - fields: [{id: 'x'}, {id: 'y'}, {id: 'z'}, {id: 'label'}], + fields: [{id: 'x'}, {id: 'y'}, {id: 'z'}, {id: 'country'}, {id: 'label'}], documents: [ - {id: 0, x: 1, y: 2, z: 3, label: 'first'} - , {id: 1, x: 2, y: 4, z: 6, label: 'second'} - , {id: 2, x: 3, y: 6, z: 9, label: 'third'} - , {id: 3, x: 4, y: 8, z: 12, label: 'fourth'} - , {id: 4, x: 5, y: 10, z: 15, label: 'fifth'} - , {id: 5, x: 6, y: 12, z: 18, label: 'sixth'} + {id: 0, x: 1, y: 2, z: 3, country: 'DE', label: 'first'} + , {id: 1, x: 2, y: 4, z: 6, country: 'UK', label: 'second'} + , {id: 2, x: 3, y: 6, z: 9, country: 'US', label: 'third'} + , {id: 3, x: 4, y: 8, z: 12, country: 'UK', label: 'fourth'} + , {id: 4, x: 5, y: 10, z: 15, country: 'UK', label: 'fifth'} + , {id: 5, x: 6, y: 12, z: 18, country: 'DE', label: 'sixth'} ] }; var backend = new recline.Backend.Memory(); backend.addDataset(inData); var dataset = new recline.Model.Dataset({id: datasetId}, backend); + // TODO: auto-compute in Memory backend ?? + dataset.facets = { + 'country': { + terms: [ + { + term: 'UK', + count: 3 + }, + { + term: 'DE', + count: 2 + }, + { + term: 'US', + count: 1 + } + ] + } + }; return dataset; } diff --git a/src/model.js b/src/model.js index 207a9872..41934e67 100644 --- a/src/model.js +++ b/src/model.js @@ -23,6 +23,7 @@ my.Dataset = Backbone.Model.extend({ } this.fields = new my.FieldList(); this.currentDocuments = new my.DocumentList(); + this.facets = {}; this.docCount = null; this.queryState = new my.Query(); this.queryState.bind('change', this.query); @@ -116,6 +117,26 @@ my.Query = Backbone.Model.extend({ defaults: { size: 100 , from: 0 + , facets: {} + }, + initialize: function() { + _.bindAll(this, 'addFacet'); + }, + + addFacet: function(fieldId) { + // TODO: utilize field info to determine facet type ?? + var facets = this.get('facets'); + if (fieldId in facets) { + return; + } + facets[fieldId] = { + terms: { + field: fieldId + } + } + this.set({facets: facets}); + // trigger change event (does not seem to be triggered o/w) + // this.change(); } }); diff --git a/src/view.js b/src/view.js index 72b1c0d3..ae9c24f4 100644 --- a/src/view.js +++ b/src/view.js @@ -168,6 +168,12 @@ my.DataExplorer = Backbone.View.extend({ model: this.model.queryState }); this.el.find('.header').append(queryEditor.el); + var queryFacetEditor = new my.QueryFacetEditor({ + model: this.model.queryState + }, + this.model + ); + this.el.find('.header').append(queryFacetEditor.el); }, setupRouting: function() { @@ -275,6 +281,67 @@ my.QueryEditor = Backbone.View.extend({ } }); +my.QueryFacetEditor = Backbone.View.extend({ + className: 'recline-query-facet-editor', + template: ' \ + Add filter on \ + \ +
\ + {{#facets}} \ + {{label}} \ + \ + {{/facets}} \ +
\ + ', + + events: { + 'change .js-faceton': 'onAddFilter', + 'click .js-facet-show-toggle': 'onFacetShowToggle' + }, + initialize: function(model, dataset) { + _.bindAll(this, 'render'); + this.el = $(this.el); + this.model.bind('change', this.render); + this.dataset = dataset; + this.dataset.fields.bind('add', this.render); + this.dataset.fields.bind('reset', this.render); + this.dataset.fields.bind('remove', this.render); + this.render(); + }, + render: function() { + var tmplData = { + query: this.model.toJSON(), + fields: this.dataset.fields.toJSON() + }; + tmplData.facets = _.map(this.dataset.facets, function(data, key) { + var out = _.extend({label: key}, data); + return out; + }) + var templated = $.mustache(this.template, tmplData); + this.el.html(templated); + }, + onAddFilter: function(e) { + var fieldId = $(e.target).find('option:selected').attr('name'); + this.model.addFacet(fieldId); + // calculate facets for that field (if not already there??) + // re-render will happen automatically as dataset will have updated + }, + onFacetShowToggle: function(e) { + e.preventDefault(); + var $a = $(e.target); + var facetId = $a.attr('data-facet'); + var $ul = this.el.find('.facet-items[data-facet="' + facetId + '"]'); + $ul.toggle(); + } +}); /* ========================================================== */ // ## Miscellaneous Utilities