Prechádzať zdrojové kódy

[search] Multi filter for range facets

Romain Rigaux 11 rokov pred
rodič
commit
d12fd2a

+ 10 - 1
apps/search/src/search/api.py

@@ -121,7 +121,16 @@ class SolrApi(object):
           params += (('facet.field', '{!ex=%s}%s' % (facet['field'], facet['field'])),)
 
     for fq in query['fqs']:
-      params += (('fq', urllib.unquote(utf_quoter('{!tag=%s}{!field f=%s}%s' % (fq['field'], fq['field'], fq['filter'])))),)
+      model_facet = [facet for facet in collection['facets'] if facet['id'] == fq['id']][0]
+      print '---'
+      print model_facet
+      if model_facet['type'] == 'field':        
+        params += (('fq', ' '.join([urllib.unquote(utf_quoter('{!tag=%s}{!field f=%s}%s' % (fq['field'], fq['field'], _filter))) for _filter in fq['filter']])),)
+      elif model_facet['type'] == 'range':
+        # Set end range if last range
+        if not fq['filter'].get('to'):           
+          fq['filter']['to'] = model_facet['properties']['end'] if model_facet else '*'
+        params += (('fq', urllib.unquote(utf_quoter('{!tag=%s}%s:[%s TO %s}' % (fq['field'],fq['field'], fq['filter']['from'], fq['filter']['to'])))),)
 
     if collection['template']['fieldsSelected'] and collection['template']['isGridLayout']:
       fields = collection['template']['fieldsSelected'] + [collection['idField']] if collection['idField'] else []

+ 3 - 2
apps/search/src/search/models.py

@@ -503,8 +503,9 @@ def augment_solr_response2(response, collection, query, solr_query):
           counts = response['facet_counts']['facet_ranges'][name]['counts']
           if collection_facet['properties']['sort'] == 'asc':
             counts = reversepairs(counts) 
-          if facet['widgetType'] in ('facet-widget', 'pie-widget'):
-            counts = pairwise2(name, selected_values.get((facet['id'], name, 'field'), []), counts)          
+#          if facet['widgetType'] in ('facet-widget', 'pie-widget'):
+#            print selected_values # 'from'
+#            counts = pairwise2(name, selected_values.get((facet['id'], name, 'field'), []), counts)          
           facet = {
             'id': collection_facet['id'],
             'field': name,

+ 21 - 12
apps/search/src/search/templates/search2.mako

@@ -32,7 +32,7 @@ ${ commonheader(_('Search'), "search", user, "60px") | n,unicode }
       <button type="button" title="${ _('Edit') }" rel="tooltip" data-placement="bottom" data-bind="click: toggleEditing, css: {'btn': true, 'btn-inverse': isEditing}"><i class="fa fa-pencil"></i></button>
       <button type="button" title="${ _('Settings') }" rel="tooltip" data-placement="bottom" data-bind="css: {'btn': true}"><i class="fa fa-cogs"></i></button>    
       <button type="button" title="${ _('Save') }" rel="tooltip" data-placement="bottom" data-loading-text="${ _("Saving...") }"  data-bind="click: save, css: {'btn': true}"><i class="fa fa-save"></i></button>
-      <button type="button" title="${ _('History') }" rel="tooltip" data-placement="bottom" data-bind="css: {'btn': true}"><i class="fa fa-archive"></i></button>
+      <button type="button" title="${ _('Share') }" rel="tooltip" data-placement="bottom" data-bind="css: {'btn': true}"><i class="fa fa-link"></i></button>
       &nbsp;&nbsp;&nbsp;            
       <a class="btn" href="${ url('search:new_search') }" title="${ _('New') }" rel="tooltip" data-placement="bottom" data-bind="css: {'btn': true}"><i class="fa fa-file-o"></i></a>
       <a class="btn" href="${ url('search:admin_collections') }" title="${ _('Collections') }" rel="tooltip" data-placement="bottom" data-bind="css: {'btn': true}"><i class="fa fa-tags"></i></a> 
@@ -49,9 +49,6 @@ ${ commonheader(_('Search'), "search", user, "60px") | n,unicode }
       <input data-bind="value: query.q" name="query" maxlength="256" type="text" class="search-query input-xxlarge" id="id_query" style="cursor: auto;">
       
       <button type="submit" id="search-btn" class="btn btn-inverse"><i class="fa fa-search"></i></button>
-      <span style="padding-left:15px">
-        <button type="button" id="download-btn" class="btn btn-inverse dropdown-toggle" data-toggle="dropdown"><i class="fa fa-download"></i></button>
-      </span>
     </div>
   </form>
 </div>
@@ -232,7 +229,7 @@ ${ commonheader(_('Search'), "search", user, "60px") | n,unicode }
           <span data-bind="text: value, click: function(){ $root.query.toggleFacet({facet: $data, widget_id: $parent.id}) }"></span>
           <i data-bind="click: function(){ $root.query.toggleFacet({facet: $data, widget_id: $parent.id}) }" class="fa fa-times"></i>            
         <!-- /ko -->
-        <!-- ko if: !selected -->           
+        <!-- ko if: ! selected -->           
           <span data-bind="text: value + ' (' + count + ')', click: function(){ $root.query.toggleFacet({facet: $data, widget_id: $parent.id}); }"></span>
         <!-- /ko -->
         </a>
@@ -329,6 +326,12 @@ ${ commonheader(_('Search'), "search", user, "60px") | n,unicode }
 
 <script type="text/html" id="resultset-pagination">
   <span data-bind="text: $data.response.numFound"></span> ${ _(' results') } <i class="fa fa-arrow-right"></i>
+  
+  <span class="pull-right">
+    <i class="hfo hfo-file-csv"></i>
+    <i class="hfo hfo-file-xls"></i>
+    <i class="fa fa-save"></i>
+  </span>
 </script>
 
 <script type="text/html" id="histogram-widget">
@@ -356,7 +359,7 @@ ${ commonheader(_('Search'), "search", user, "60px") | n,unicode }
     <a href="javascript:void(0)"><i class="fa fa-plus"></i></a>
     <a href="javascript:void(0)"><i class="fa fa-minus"></i></a>
 
-    <div data-bind="timelineChart: {data: counts, field: field, label: label, transformer: timelineChartDataTransformer, onClick: function(d){viewModel.query.selectFacet({count: d.y,selected: false,value: d.x,cat: field})}, onComplete: function(){viewModel.getWidgetById(id).isLoading(false)}}" />
+    <div data-bind="timelineChart: {data: counts, field: field, label: label, transformer: timelineChartDataTransformer, onSelectRange: function(from, to){viewModel.query.selectRangeFacet({from: from, to: to, cat: field})}, onComplete: function(){viewModel.getWidgetById(id).isLoading(false)}}" />
   </div>
   <!-- /ko -->
 </script>
@@ -382,7 +385,11 @@ ${ commonheader(_('Search'), "search", user, "60px") | n,unicode }
       ${ _('Gap') }: <input type="text" data-bind="value: properties.gap" />
     </div> 
 
-    <div data-bind="barChart: {data: counts, field: field, label: label, transformer: barChartDataTransformer, onClick: function(d){viewModel.query.selectFacet({count: d.y,selected: false,value: d.x,cat: field})}, onComplete: function(){viewModel.getWidgetById(id).isLoading(false)}}" />
+    <div data-bind="barChart: {data: {counts: counts, widget_id: $parent.id()}, field: field, label: label, 
+      transformer: barChartDataTransformer,
+      onClick: function(d){viewModel.query.selectRangeFacet({count: d.y, widget_id: d.widget_id, from: d.x, to: d.x_end, cat: field})}, 
+      onComplete: function(){viewModel.getWidgetById(id).isLoading(false)}}"
+    />
   </div>
   <!-- /ko -->
 </script>
@@ -400,7 +407,7 @@ ${ commonheader(_('Search'), "search", user, "60px") | n,unicode }
       ${ _('Field') }: <input type="text" data-bind="value: field" />
     </div>
 
-    <div data-bind="pieChart: {data: {counts: counts, widget_id: $parent.id}, transformer: pieChartDataTransformer, onClick: function(d){viewModel.query.toggleFacet({facet: d.data.obj, widget_id: d.data.obj.widget_id})}, onComplete: function(){viewModel.getWidgetById(id).isLoading(false)}}" />
+    <div data-bind="pieChart: {data: {counts: counts, widget_id: $parent.id()}, transformer: pieChartDataTransformer, onClick: function(d){viewModel.query.toggleFacet({facet: d.data.obj, widget_id: d.data.obj.widget_id})}, onComplete: function(){viewModel.getWidgetById(id).isLoading(false)}}" />
   </div>
   <!-- /ko -->
   <div class="widget-spinner" data-bind="visible: isLoading()">
@@ -419,7 +426,7 @@ ${ commonheader(_('Search'), "search", user, "60px") | n,unicode }
 
 <script type="text/html" id="filter-widget">
   <div data-bind="foreach: $root.query.fqs">
-    <span data-bind="text: ko.mapping.toJSON($data)"></span>
+    <span data-bind="text: ko.mapping.toJSON($data), click: viewModel.query.removeFilter"></span>
   </div>
   <div class="widget-spinner" data-bind="visible: isLoading()">
     <!--[if !IE]> --><i class="fa fa-spinner fa-spin"></i><!-- <![endif]-->
@@ -644,11 +651,13 @@ function pieChartDataTransformer(data) {
 
 function barChartDataTransformer(data) {
   var _data = [];
-  for (var i = 0; i < data.length; i = i + 2) {
+  for (var i = 0; i < data.counts.length; i = i + 2) {
     _data.push({
       series: 0,
-      x: data[i],
-      y: data[i + 1]
+      x: data.counts[i],
+      x_end: data.counts[i + 2],
+      y: data.counts[i + 1],
+      widget_id: data.widget_id
     });
   }
   return _data;

+ 45 - 28
apps/search/static/js/search.ko.js

@@ -200,23 +200,35 @@ var Query = function (vm, query) {
 //[{'wid': 1, 'filter: ['c', 'd'], 'type': 'field', 'field': 'user_location'},
 // {'wid': 1, 'filter: [{'from': 1, 'to': 10}], 'type': 'range', 'field': 'created_at'}
 // {'wid': 2, ....}
-// ]
- // {"count":27,"selected":false,"value":"dhclient","cat":"process"}
-  
-  self.toggleFacet = function (data) {//alert(ko.mapping.toJSON(data)); 
+// ]  
+  self.toggleFacet = function (data) {alert(ko.mapping.toJSON(data)); 
 	var fq = self.getFacetFilter(data.widget_id);
 	
+	// Special case when text facet is a range
+	var _facet = vm.collection.getFacetById(data.widget_id);
+	if (_facet.type() == 'range') {
+      self.selectRangeFacet({'widget_id': data.widget_id, 'cat': data.facet.cat, 'from': data.facet.value, 'to': data.facet.value});
+      return;
+	}
+	
 	if (fq == null) {
-	    self.fqs.push({
-	      'id': data.widget_id,
-	      'field': data.facet.cat,
-	      'filter': data.facet.value,
-	      'type': 'field'
-	    });
+      self.fqs.push(ko.mapping.fromJS({
+        'id': data.widget_id,
+        'field': data.facet.cat,
+        'filter': [data.facet.value],
+        'type': 'field'
+      }));
 	} else {
       $.each(self.fqs(), function (index, fq) {
-        if (fq.id == data.widget_id) {
-          self.fqs.remove(fq); 
+        if (fq.id() == data.widget_id) {
+          if (fq.filter.indexOf(data.facet.value) > -1) {
+        	fq.filter.remove(data.facet.value);
+        	if (fq.filter().length == 0) {
+              self.fqs.remove(fq);
+        	}
+          } else {
+        	fq.filter.push(data.facet.value);
+          }
         }
       });
 	}
@@ -224,31 +236,34 @@ var Query = function (vm, query) {
     vm.search();
   }  
   
-  self.selectFacet = function (data) {//alert(ko.mapping.toJSON(data)); 
-    self.fqs.push({
+  self.selectRangeFacet = function (data) {
+    self.removeFilter(ko.mapping.fromJS({'id': data.widget_id}));
+	
+    self.fqs.push(ko.mapping.fromJS({
       'id': data.widget_id,
-      'field': data.facet.cat,
-      'filter': data.facet.value,
-      'type': 'field'
-    });
-    
-    vm.search();
-  }
+      'field': data.cat,
+      'filter': {'from': data.from, 'to': data.to},
+      'type': 'range'
+    }));
 
-  self.unselectFacet = function (data) {
+    vm.search();
+  }  
+  
+  self.removeFilter = function (data) { 
     $.each(self.fqs(), function (index, fq) {
-      if (fq.id == data.widget_id) {
-        self.fqs.remove(fq); 
+      if (fq.id() == data.id()) {          
+        self.fqs.remove(fq);
+        return false;
       }
     });
-    
+
     vm.search();
-  }  
+  } 
 
   self.getFacetFilter = function (widget_id) {
     var _fq = null;
     $.each(self.fqs(), function (index, fq) {
-      if (fq.id == widget_id) {
+      if (fq.id() == widget_id) {
         _fq = fq;
         return false;
       }
@@ -377,7 +392,9 @@ var Collection = function (vm, collection) {
    vm.search();
   };
   
-  self.toggleFacet = function (facet_field, event) {	
+  self.toggleFacet = function (facet_field, event) {
+	vm.query.removeFilter(ko.mapping.fromJS({'id': facet_field.id})); // Reset filter query
+	
     if (facet_field.properties.canRange()) {
 	   if (facet_field.type() == 'field' && facet_field.properties.sort() == 'desc') {
 		 facet_field.type('range');