فهرست منبع

[search] Multi fq support for text facet

Romain Rigaux 11 سال پیش
والد
کامیت
d3b3c9d

+ 7 - 6
apps/search/src/search/api.py

@@ -92,9 +92,10 @@ class SolrApi(object):
       raise PopupException(e, title=_('Error while accessing Solr'))
 
   #@demo_handler
-  def query2(self, solr_query, collection):
+  def query2(self, solr_query, collection, query):
+    
     params = self._get_params() + (
-        ('q', solr_query['q'] or EMPTY_QUERY.get()),
+        ('q', query['q'] or EMPTY_QUERY.get()),
         ('wt', 'json'),
         ('rows', solr_query['rows']),
         ('start', solr_query['start']),
@@ -119,12 +120,12 @@ class SolrApi(object):
         elif facet['type'] == 'field':
           params += (('facet.field', '{!ex=%s}%s' % (facet['field'], facet['field'])),)
 
-    fqs = solr_query['fq']
-    for fq, val in fqs.iteritems():
-      params += (('fq', urllib.unquote(utf_quoter('{!tag=%s}{!field f=%s}%s' % (fq, fq, val)))),)
+    for fq in query['fqs']:
+      params += (('fq', urllib.unquote(utf_quoter('{!tag=%s}{!field f=%s}%s' % (fq['field'], fq['field'], fq['filter'])))),)
 
     if collection['template']['fieldsSelected'] and collection['template']['isGridLayout']:
-      params += (('fl', urllib.unquote(utf_quoter(','.join(collection['template']['fieldsSelected'])))),)
+      fields = collection['template']['fieldsSelected'] + [collection['idField']] if collection['idField'] else []
+      params += (('fl', urllib.unquote(utf_quoter(','.join(fields)))),)
     else:
       params += (('fl', '*'),)
 

+ 7 - 8
apps/search/src/search/models.py

@@ -453,17 +453,17 @@ def is_chart_field(field, charts):
   return found
 
 
-def augment_solr_response2(response, collection, solr_query):
+def augment_solr_response2(response, collection, query, solr_query):
   augmented = response
   augmented['normalized_facets'] = []
 
   normalized_facets = []
 
-  def pairwise2(cat, selected_field, iterable):
+  def pairwise2(cat, selected_values, iterable):
       pairs = []
       a, b = itertools.tee(iterable)
       for element in a:
-        pairs.append({'cat': cat, 'value': element, 'count': next(a), 'selected': element == selected_field})
+        pairs.append({'cat': cat, 'value': element, 'count': next(a), 'selected': element in selected_values})
       return pairs
 
   def reversepairs(iterable):
@@ -474,7 +474,7 @@ def augment_solr_response2(response, collection, solr_query):
         pairs.insert(0, element)
       return pairs
 
-  fq = solr_query['fq']
+  selected_values = dict([((fq['id'], fq['field'], fq['type']), fq['filter']) for fq in query['fqs']])
 
   if response and response.get('facet_counts'):
     # [{u'field': u'sun', u'type': u'query', u'id': u'67b43a63-ed22-747b-47e8-b31aad1431ea', u'label': u'sun'}
@@ -483,9 +483,9 @@ def augment_solr_response2(response, collection, solr_query):
       
       if category == 'field' and response['facet_counts']['facet_fields']:
         for name in response['facet_counts']['facet_fields']: # todo get from the list
-          selected_field = fq.get(name, '') # todo with multi filter
+          #selected_field = fq.get(name, '') # todo with multi filter
           collection_facet = get_facet_field(category, name, collection['facets'])
-          counts = pairwise2(name, selected_field, response['facet_counts']['facet_fields'][name])
+          counts = pairwise2(name, selected_values.get((facet['id'], name, category), []), response['facet_counts']['facet_fields'][name])
           if collection_facet['properties']['sort'] == 'asc':
             counts.reverse()          
           facet = {
@@ -504,8 +504,7 @@ def augment_solr_response2(response, collection, solr_query):
           if collection_facet['properties']['sort'] == 'asc':
             counts = reversepairs(counts) 
           if facet['widgetType'] in ('facet-widget', 'pie-widget'):
-            selected_field = fq.get(name, '') # todo with multi filter
-            counts = pairwise2(name, selected_field, counts)          
+            counts = pairwise2(name, selected_values.get((facet['id'], name, 'field'), []), counts)          
           facet = {
             'id': collection_facet['id'],
             'field': name,

+ 9 - 5
apps/search/src/search/templates/search2.mako

@@ -229,11 +229,11 @@ ${ commonheader(_('Search'), "search", user, "60px") | n,unicode }
       <div>
         <a href="script:void(0)">
         <!-- ko if: selected -->
-          <span data-bind="text: value, click: $root.query.unselectFacet"></span>
-          <i data-bind="click: $root.query.unselectFacet" class="fa fa-times"></i>            
+          <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 -->           
-          <span data-bind="text: value, click: $root.query.selectFacet"></span><span data-bind="click: $root.query.selectFacet"> (</span><span data-bind="text: count, click: $root.query.selectFacet"></span><span data-bind="click: $root.query.selectFacet">)</span>
+          <span data-bind="text: value + ' (' + count + ')', click: function(){ $root.query.toggleFacet({facet: $data, widget_id: $parent.id}); }"></span>
         <!-- /ko -->
         </a>
       </div>
@@ -400,7 +400,7 @@ ${ commonheader(_('Search'), "search", user, "60px") | n,unicode }
       ${ _('Field') }: <input type="text" data-bind="value: field" />
     </div>
 
-    <div data-bind="pieChart: {data: counts, transformer: pieChartDataTransformer, onClick: function(d){viewModel.query.selectFacet(d.data.obj)}, 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()">
@@ -418,6 +418,9 @@ ${ commonheader(_('Search'), "search", user, "60px") | n,unicode }
 </script>
 
 <script type="text/html" id="filter-widget">
+  <div data-bind="foreach: $root.query.fqs">
+    <span data-bind="text: ko.mapping.toJSON($data)"></span>
+  </div>
   <div class="widget-spinner" data-bind="visible: isLoading()">
     <!--[if !IE]> --><i class="fa fa-spinner fa-spin"></i><!-- <![endif]-->
     <!--[if IE]><img src="/static/art/spinner.gif" /><![endif]-->
@@ -628,7 +631,8 @@ var viewModel;
 
 function pieChartDataTransformer(data) {
   var _data = [];
-  $(data).each(function (cnt, item) {
+  $(data.counts).each(function (cnt, item) {
+    item.widget_id = data.widget_id;
     _data.push({
       label: item.value,
       value: item.count,

+ 4 - 7
apps/search/src/search/views.py

@@ -67,7 +67,7 @@ def index(request):
       return no_collections(request)
 
   collection = Collection.objects.get(id=collection_id) # TODO perms HUE-1987
-  query = {'q': '', 'fq': {}}
+  query = {'q': '', 'fqs': []}
 
   return render('search2.mako', request, {
     'collection': collection,
@@ -77,7 +77,7 @@ def index(request):
 
 def new_search(request):
   collection = Collection(name='twitter_demo', label='New Twitter Template')
-  query = {'q': '', 'fq': {}}
+  query = {'q': '', 'fqs': []}
 
   return render('search2.mako', request, {
     'collection': collection,
@@ -96,7 +96,6 @@ def search(request):
     # collection['name']=
   
   print request.POST
-  print collection
     
   if collection:
     solr_query = {}    
@@ -106,11 +105,9 @@ def search(request):
       solr_query['collection'] = collection['name']
       solr_query['rows'] = 10
       solr_query['start'] = 0
-      solr_query['fq'] = query['fq']
-      solr_query['q'] = query['q']
       
-      response = SolrApi(SOLR_URL.get(), request.user).query2(solr_query, collection)
-      response = augment_solr_response2(response, collection, solr_query)
+      response = SolrApi(SOLR_URL.get(), request.user).query2(solr_query, collection, query)
+      response = augment_solr_response2(response, collection, query, solr_query)
       print response
     except RestException, e:
       try:

+ 65 - 16
apps/search/static/js/search.ko.js

@@ -196,17 +196,65 @@ var Query = function (vm, query) {
   var self = this;
 
   self.q = ko.observable(query.q);
-  self.fq = query.fq
-
-  self.selectFacet = function (facet_json) {
-    self.fq[facet_json.cat] = facet_json.value; // need to add facet id too
+  self.fqs = ko.mapping.fromJS(query.fqs);
+//[{'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)); 
+	var fq = self.getFacetFilter(data.widget_id);
+	
+	if (fq == null) {
+	    self.fqs.push({
+	      '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); 
+        }
+      });
+	}
+    
+    vm.search();
+  }  
+  
+  self.selectFacet = function (data) {//alert(ko.mapping.toJSON(data)); 
+    self.fqs.push({
+      'id': data.widget_id,
+      'field': data.facet.cat,
+      'filter': data.facet.value,
+      'type': 'field'
+    });
+    
     vm.search();
   }
 
-  self.unselectFacet = function (facet_json) {
-    delete self.fq[facet_json.cat];
+  self.unselectFacet = function (data) {
+    $.each(self.fqs(), function (index, fq) {
+      if (fq.id == data.widget_id) {
+        self.fqs.remove(fq); 
+      }
+    });
+    
     vm.search();
-  }
+  }  
+
+  self.getFacetFilter = function (widget_id) {
+    var _fq = null;
+    $.each(self.fqs(), function (index, fq) {
+      if (fq.id == widget_id) {
+        _fq = fq;
+        return false;
+      }
+    });
+    return _fq;
+  }   
 };
 
 
@@ -246,12 +294,12 @@ var Collection = function (vm, collection) {
         "widget_type": facet_json.widgetType
       }, function (data) {
         if (data.status == 0) {
-        var facet = ko.mapping.fromJS(data.facet);
-        facet.field.subscribe(function () {
+          var facet = ko.mapping.fromJS(data.facet);
+          facet.field.subscribe(function () {
+            vm.search();
+          });      
+          self.facets.push(facet);
           vm.search();
-        });      
-        self.facets.push(facet);
-        vm.search();
         } else {
           $(document).trigger("error", data.message);
         }
@@ -269,7 +317,7 @@ var Collection = function (vm, collection) {
   
   self.getFacetById = function (facet_id) {
     var _facet = null;
-    $.each(self.facets(), function (index, facet) {//alert(ko.mapping.toJSON(category));
+    $.each(self.facets(), function (index, facet) {
       if (facet.id() == facet_id) {
         _facet = facet;
         return false;
@@ -409,7 +457,7 @@ var SearchViewModel = function (collection_json, query_json) {
       query: ko.mapping.toJSON(self.query),
       layout: ko.mapping.toJSON(self.columns)
     }, function (data) {
-      self.response(data); // Content not observable for performance
+      self.response(data);
       self.results.removeAll();
       if (data.error) {
         $(document).trigger("error", data.error);
@@ -420,7 +468,7 @@ var SearchViewModel = function (collection_json, query_json) {
           $.each(data.response.docs, function (index, item) {
             var row = [];
             var fields = self.collection.template.fieldsSelected();
-            // Field selection or whole record
+            // Display selected fields or whole json document
             if (fields.length != 0) {
               $.each(self.collection.template.fieldsSelected(), function (index, field) {  
                 row.push(item[field]);
@@ -449,7 +497,8 @@ var SearchViewModel = function (collection_json, query_json) {
   };
   
   self.removeWidget = function (widget_json) {
-    self.collection.removeFacet(widget_json.id); 
+    self.collection.removeFacet(widget_json.id);
+    // todo remove fqs
     self.search();
   }