Bläddra i källkod

[search] Multi queries for timeline

Romain Rigaux 11 år sedan
förälder
incheckning
880db24897

+ 11 - 5
apps/search/src/search/api.py

@@ -177,8 +177,13 @@ class SolrApi(object):
       raise PopupException(e, title=_('Error while accessing Solr'))
       raise PopupException(e, title=_('Error while accessing Solr'))
 
 
   #@demo_handler
   #@demo_handler
-  def query2(self, solr_query, collection, query):
+  def query2(self, collection, query):
+    solr_query = {}      
     
     
+    solr_query['collection'] = collection['name']
+    solr_query['rows'] = 10
+    solr_query['start'] = 0
+          
     params = self._get_params() + (
     params = self._get_params() + (
         ('q', query['q'] or EMPTY_QUERY.get()),
         ('q', query['q'] or EMPTY_QUERY.get()),
         ('wt', 'json'),
         ('wt', 'json'),
@@ -206,12 +211,13 @@ class SolrApi(object):
           params += (('facet.field', '{!ex=%s}%s' % (facet['field'], facet['field'])),)
           params += (('facet.field', '{!ex=%s}%s' % (facet['field'], facet['field'])),)
 
 
     for fq in query['fqs']:
     for fq in query['fqs']:
-      model_facet = [facet for facet in collection['facets'] if facet['id'] == fq['id']][0]
-      if model_facet['type'] == 'field':        
+      #model_facet = [facet for facet in collection['facets'] if facet['id'] == fq['id']][0]
+      if fq['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']])),)
         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':
+      elif fq['type'] == 'range':
         # Set end range if last range
         # Set end range if last range
-        if fq['filter'].get('to'):           
+        if fq['filter'].get('to'):
+          model_facet = [facet for facet in collection['facets'] if facet['id'] == fq['id']][0]           
           fq['filter']['to'] = model_facet['properties']['end'] if model_facet else '*'
           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'])))),)
         params += (('fq', urllib.unquote(utf_quoter('{!tag=%s}%s:[%s TO %s}' % (fq['field'],fq['field'], fq['filter']['from'], fq['filter']['to'])))),)
 
 

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

@@ -457,7 +457,7 @@ def is_chart_field(field, charts):
   return found
   return found
 
 
 
 
-def augment_solr_response2(response, collection, query, solr_query):
+def augment_solr_response2(response, collection, query):
   augmented = response
   augmented = response
   augmented['normalized_facets'] = []
   augmented['normalized_facets'] = []
 
 
@@ -516,6 +516,7 @@ def augment_solr_response2(response, collection, query, solr_query):
             'type': category,
             'type': category,
             'label': collection_facet['label'],
             'label': collection_facet['label'],
             'counts': counts,
             'counts': counts,
+            'extraSeries': []
           }
           }
           normalized_facets.append(facet)
           normalized_facets.append(facet)
       elif category == 'query' and response['facet_counts']['facet_queries']:
       elif category == 'query' and response['facet_counts']['facet_queries']:
@@ -552,7 +553,7 @@ def augment_solr_response2(response, collection, query, solr_query):
       response['warning'] = _("The Solr schema requires an id field for performing the result highlighting")
       response['warning'] = _("The Solr schema requires an id field for performing the result highlighting")
 
 
 
 
-  response['total_pages'] = int(math.ceil((float(response['response']['numFound']) / float(solr_query['rows']))))
+  response['total_pages'] = 111 #int(math.ceil((float(response['response']['numFound']) / float(solr_query['rows']))))
   response['search_time'] = response['responseHeader']['QTime']
   response['search_time'] = response['responseHeader']['QTime']
 
 
   if normalized_facets:
   if normalized_facets:
@@ -560,7 +561,7 @@ def augment_solr_response2(response, collection, query, solr_query):
 
 
   return augmented
   return augmented
 
 
-def augment_solr_exception(response, collection, solr_query):
+def augment_solr_exception(response, collection):
   response.update(
   response.update(
   {
   {
     "facet_counts": {   
     "facet_counts": {   

+ 46 - 34
apps/search/src/search/templates/search2.mako

@@ -199,12 +199,12 @@ ${ commonheader(_('Search'), "search", user, "60px") | n,unicode }
 
 
 
 
 <script type="text/html" id="hit-widget">
 <script type="text/html" id="hit-widget">
-  <!-- ko ifnot: $root.getFacetFromQuery(id) -->
+  <!-- ko ifnot: $root.getFacetFromQuery(id()) -->
     <a data-bind="click: showAddFacetDemiModal" class="btn" href="javascript:void(0)"><i class="fa fa-plus"></i></a>
     <a data-bind="click: showAddFacetDemiModal" class="btn" href="javascript:void(0)"><i class="fa fa-plus"></i></a>
   <!-- /ko -->
   <!-- /ko -->
 
 
-  <!-- ko if: $root.getFacetFromQuery(id) -->
-  <div class="row-fluid" data-bind="with: $root.getFacetFromQuery(id)">
+  <!-- ko if: $root.getFacetFromQuery(id()) -->
+  <div class="row-fluid" data-bind="with: $root.getFacetFromQuery(id())">
     <div data-bind="visible: $root.isEditing, with: $root.collection.getFacetById($parent.id())" style="margin-bottom: 20px">      
     <div data-bind="visible: $root.isEditing, with: $root.collection.getFacetById($parent.id())" style="margin-bottom: 20px">      
       ${ _('Label') }: <input type="text" data-bind="value: label" />
       ${ _('Label') }: <input type="text" data-bind="value: label" />
     </div>  
     </div>  
@@ -215,7 +215,7 @@ ${ commonheader(_('Search'), "search", user, "60px") | n,unicode }
 </script>
 </script>
 
 
 <script type="text/html" id="facet-widget">
 <script type="text/html" id="facet-widget">
-  <!-- ko ifnot: $root.getFacetFromQuery(id) -->
+  <!-- ko ifnot: $root.getFacetFromQuery(id()) -->
     <a data-bind="click: showAddFacetDemiModal" class="btn" href="javascript:void(0)"><i class="fa fa-plus"></i></a>
     <a data-bind="click: showAddFacetDemiModal" class="btn" href="javascript:void(0)"><i class="fa fa-plus"></i></a>
   <!-- /ko -->
   <!-- /ko -->
 
 
@@ -224,8 +224,8 @@ ${ commonheader(_('Search'), "search", user, "60px") | n,unicode }
     <!--[if IE]><img src="/static/art/spinner.gif" /><![endif]-->
     <!--[if IE]><img src="/static/art/spinner.gif" /><![endif]-->
   </div>
   </div>
 
 
-  <!-- ko if: $root.getFacetFromQuery(id) -->
-  <div class="row-fluid" data-bind="with: $root.getFacetFromQuery(id)">
+  <!-- ko if: $root.getFacetFromQuery(id()) -->
+  <div class="row-fluid" data-bind="with: $root.getFacetFromQuery(id())">
     <div data-bind="visible: $root.isEditing, with: $root.collection.getFacetById($parent.id())" style="margin-bottom: 20px">      
     <div data-bind="visible: $root.isEditing, with: $root.collection.getFacetById($parent.id())" style="margin-bottom: 20px">      
       ${ _('Label') }: <input type="text" data-bind="value: label" />
       ${ _('Label') }: <input type="text" data-bind="value: label" />
       <!-- ko if: type() == 'range' -->
       <!-- ko if: type() == 'range' -->
@@ -376,7 +376,7 @@ ${ commonheader(_('Search'), "search", user, "60px") | n,unicode }
 </script>
 </script>
 
 
 <script type="text/html" id="histogram-widget">
 <script type="text/html" id="histogram-widget">
-  <!-- ko ifnot: $root.getFacetFromQuery(id) -->
+  <!-- ko ifnot: $root.getFacetFromQuery(id()) -->
     <a data-bind="click: showAddFacetDemiModal" class="btn" href="javascript:void(0)"><i class="fa fa-plus"></i></a>
     <a data-bind="click: showAddFacetDemiModal" class="btn" href="javascript:void(0)"><i class="fa fa-plus"></i></a>
   <!-- /ko -->
   <!-- /ko -->
 
 
@@ -385,8 +385,8 @@ ${ commonheader(_('Search'), "search", user, "60px") | n,unicode }
     <!--[if IE]><img src="/static/art/spinner.gif" /><![endif]-->
     <!--[if IE]><img src="/static/art/spinner.gif" /><![endif]-->
   </div>
   </div>
 
 
-  <!-- ko if: $root.getFacetFromQuery(id) -->
-  <div class="row-fluid" data-bind="with: $root.getFacetFromQuery(id)">
+  <!-- ko if: $root.getFacetFromQuery(id()) -->
+  <div class="row-fluid" data-bind="with: $root.getFacetFromQuery(id())">
     <div data-bind="visible: $root.isEditing, with: $root.collection.getFacetById($parent.id())" style="margin-bottom: 20px">      
     <div data-bind="visible: $root.isEditing, with: $root.collection.getFacetById($parent.id())" style="margin-bottom: 20px">      
       ${ _('Label') }: <input type="text" data-bind="value: label" />
       ${ _('Label') }: <input type="text" data-bind="value: label" />
       <br/>      
       <br/>      
@@ -397,8 +397,9 @@ ${ 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-plus"></i></a>
     <a href="javascript:void(0)" data-bind="click: $root.collection.timeLineZoom"><i class="fa fa-minus"></i></a>
     <a href="javascript:void(0)" data-bind="click: $root.collection.timeLineZoom"><i class="fa fa-minus"></i></a>
+    ## <span data-bind="text: ko.mapping.toJSON(extraSeries)"></span>
 
 
-    <div data-bind="timelineChart: {datum: {counts: counts, widget_id: $parent.id(), label: label}, field: field, label: label, transformer: timelineChartDataTransformer,
+    <div data-bind="timelineChart: {datum: {counts: counts, extraSeries: extraSeries, widget_id: $parent.id(), label: label}, field: field, label: label, transformer: timelineChartDataTransformer,
       onSelectRange: function(from, to){viewModel.collection.selectTimelineFacet({from: from, to: to, cat: field, widget_id: $parent.id()})},
       onSelectRange: function(from, to){viewModel.collection.selectTimelineFacet({from: from, to: to, cat: field, widget_id: $parent.id()})},
       onComplete: function(){viewModel.getWidgetById(id).isLoading(false)}}" />
       onComplete: function(){viewModel.getWidgetById(id).isLoading(false)}}" />
   </div>
   </div>
@@ -406,7 +407,7 @@ ${ commonheader(_('Search'), "search", user, "60px") | n,unicode }
 </script>
 </script>
 
 
 <script type="text/html" id="bar-widget">
 <script type="text/html" id="bar-widget">
-  <!-- ko ifnot: $root.getFacetFromQuery(id) -->
+  <!-- ko ifnot: $root.getFacetFromQuery(id()) -->
     <a data-bind="click: showAddFacetDemiModal" class="btn" href="javascript:void(0)"><i class="fa fa-plus"></i></a>
     <a data-bind="click: showAddFacetDemiModal" class="btn" href="javascript:void(0)"><i class="fa fa-plus"></i></a>
   <!-- /ko -->
   <!-- /ko -->
 
 
@@ -415,8 +416,8 @@ ${ commonheader(_('Search'), "search", user, "60px") | n,unicode }
     <!--[if IE]><img src="/static/art/spinner.gif" /><![endif]-->
     <!--[if IE]><img src="/static/art/spinner.gif" /><![endif]-->
   </div>
   </div>
 
 
-  <!-- ko if: $root.getFacetFromQuery(id) -->
-  <div class="row-fluid" data-bind="with: $root.getFacetFromQuery(id)">
+  <!-- ko if: $root.getFacetFromQuery(id()) -->
+  <div class="row-fluid" data-bind="with: $root.getFacetFromQuery(id())">
     <div data-bind="visible: $root.isEditing, with: $root.collection.getFacetById($parent.id())" style="margin-bottom: 20px">      
     <div data-bind="visible: $root.isEditing, with: $root.collection.getFacetById($parent.id())" style="margin-bottom: 20px">      
       ${ _('Label') }: <input type="text" data-bind="value: label" />
       ${ _('Label') }: <input type="text" data-bind="value: label" />
       <br/>
       <br/>
@@ -435,7 +436,7 @@ ${ commonheader(_('Search'), "search", user, "60px") | n,unicode }
 </script>
 </script>
 
 
 <script type="text/html" id="line-widget">
 <script type="text/html" id="line-widget">
-  <!-- ko ifnot: $root.getFacetFromQuery(id) -->
+  <!-- ko ifnot: $root.getFacetFromQuery(id()) -->
     <a data-bind="click: showAddFacetDemiModal" class="btn" href="javascript:void(0)"><i class="fa fa-plus"></i></a>
     <a data-bind="click: showAddFacetDemiModal" class="btn" href="javascript:void(0)"><i class="fa fa-plus"></i></a>
   <!-- /ko -->
   <!-- /ko -->
 
 
@@ -444,8 +445,8 @@ ${ commonheader(_('Search'), "search", user, "60px") | n,unicode }
     <!--[if IE]><img src="/static/art/spinner.gif" /><![endif]-->
     <!--[if IE]><img src="/static/art/spinner.gif" /><![endif]-->
   </div>
   </div>
 
 
-  <!-- ko if: $root.getFacetFromQuery(id) -->
-  <div class="row-fluid" data-bind="with: $root.getFacetFromQuery(id)">
+  <!-- ko if: $root.getFacetFromQuery(id()) -->
+  <div class="row-fluid" data-bind="with: $root.getFacetFromQuery(id())">
     <div data-bind="visible: $root.isEditing, with: $root.collection.getFacetById($parent.id())" style="margin-bottom: 20px">
     <div data-bind="visible: $root.isEditing, with: $root.collection.getFacetById($parent.id())" style="margin-bottom: 20px">
       ${ _('Label') }: <input type="text" data-bind="value: label" />
       ${ _('Label') }: <input type="text" data-bind="value: label" />
       <br/>
       <br/>
@@ -465,12 +466,12 @@ ${ commonheader(_('Search'), "search", user, "60px") | n,unicode }
 
 
 
 
 <script type="text/html" id="pie-widget">
 <script type="text/html" id="pie-widget">
-  <!-- ko ifnot: $root.getFacetFromQuery(id) -->
+  <!-- ko ifnot: $root.getFacetFromQuery(id()) -->
     <a data-bind="click: showAddFacetDemiModal" class="btn" href="javascript:void(0)"><i class="fa fa-plus"></i></a>
     <a data-bind="click: showAddFacetDemiModal" class="btn" href="javascript:void(0)"><i class="fa fa-plus"></i></a>
   <!-- /ko -->
   <!-- /ko -->
 
 
-  <!-- ko if: $root.getFacetFromQuery(id) -->
-  <div class="row-fluid" data-bind="with: $root.getFacetFromQuery(id)">
+  <!-- ko if: $root.getFacetFromQuery(id()) -->
+  <div class="row-fluid" data-bind="with: $root.getFacetFromQuery(id())">
     <div data-bind="visible: $root.isEditing, with: $root.collection.getFacetById($parent.id())" style="margin-bottom: 20px">
     <div data-bind="visible: $root.isEditing, with: $root.collection.getFacetById($parent.id())" style="margin-bottom: 20px">
       ${ _('Label') }: <input type="text" data-bind="value: label" />
       ${ _('Label') }: <input type="text" data-bind="value: label" />
     </div>
     </div>
@@ -515,12 +516,12 @@ ${ commonheader(_('Search'), "search", user, "60px") | n,unicode }
 </script>
 </script>
 
 
 <script type="text/html" id="map-widget">
 <script type="text/html" id="map-widget">
-  <!-- ko ifnot: $root.getFacetFromQuery(id) -->
+  <!-- ko ifnot: $root.getFacetFromQuery(id()) -->
     <a data-bind="click: showAddFacetDemiModal" class="btn" href="javascript:void(0)"><i class="fa fa-plus"></i></a>
     <a data-bind="click: showAddFacetDemiModal" class="btn" href="javascript:void(0)"><i class="fa fa-plus"></i></a>
   <!-- /ko -->
   <!-- /ko -->
 
 
-  <!-- ko if: $root.getFacetFromQuery(id) -->
-  <div class="row-fluid" data-bind="with: $root.getFacetFromQuery(id)">
+  <!-- ko if: $root.getFacetFromQuery(id()) -->
+  <div class="row-fluid" data-bind="with: $root.getFacetFromQuery(id())">
     <div data-bind="visible: $root.isEditing, with: $root.collection.getFacetById($parent.id())" style="margin-bottom: 20px">
     <div data-bind="visible: $root.isEditing, with: $root.collection.getFacetById($parent.id())" style="margin-bottom: 20px">
       ${ _('Label') }: <input type="text" data-bind="value: label" />
       ${ _('Label') }: <input type="text" data-bind="value: label" />
     </div>
     </div>
@@ -896,6 +897,7 @@ function lineChartDataTransformer(rawDatum) {
 function timelineChartDataTransformer(rawDatum) {
 function timelineChartDataTransformer(rawDatum) {
   var _datum = [];
   var _datum = [];
   var _data = [];
   var _data = [];
+
   $(rawDatum.counts).each(function (cnt, item) {
   $(rawDatum.counts).each(function (cnt, item) {
     _data.push({
     _data.push({
       series: 0,
       series: 0,
@@ -904,23 +906,33 @@ function timelineChartDataTransformer(rawDatum) {
       obj: item
       obj: item
     });
     });
   });
   });
-    _datum.push({
+  
+  _datum.push({
     key: rawDatum.label,
     key: rawDatum.label,
     values: _data
     values: _data
   });
   });
-  var _data = [];
-  $(rawDatum.counts).each(function (cnt, item) {
-    _data.push({
-      series: 1,
-      x: new Date(moment(item.from).valueOf()),
-      y: item.value,
-      obj: item
-    });
-  });  
+  
+  if (rawDatum.extraSeries.length > 0) {
+  var _datum = [];
+  }
+    
+  $(rawDatum.extraSeries).each(function (cnt, item) {
+    var _data = [];
+    $(item.counts).each(function (cnt, item) {
+      _data.push({
+        series: cnt + 1,
+        x: new Date(moment(item.from).valueOf()),
+        y: item.value,
+        obj: item
+      });
+    });      
+
     _datum.push({
     _datum.push({
-    key: rawDatum.label + " #2",
-    values: _data
+      key: item.label,
+      values: _data
+    });
   });
   });
+  
   return _datum;
   return _datum;
 }
 }
 
 

+ 1 - 0
apps/search/src/search/urls.py

@@ -43,6 +43,7 @@ urlpatterns = patterns('search.views',
   url(r'^template/new_facet$', 'new_facet', name='new_facet'),
   url(r'^template/new_facet$', 'new_facet', name='new_facet'),
   url(r'^get_document$', 'get_document', name='get_document'),
   url(r'^get_document$', 'get_document', name='get_document'),
   url(r'^get_range_facet$', 'get_range_facet', name='get_range_facet'),
   url(r'^get_range_facet$', 'get_range_facet', name='get_range_facet'),
+  url(r'^get_timeline$', 'get_timeline', name='get_timeline'),
   
   
   # old
   # old
   url(r'^admin/collection/(?P<collection_id>\w+)/schema$', 'admin_collection_schema', name='admin_collection_schema'),
   url(r'^admin/collection/(?P<collection_id>\w+)/schema$', 'admin_collection_schema', name='admin_collection_schema'),

+ 42 - 12
apps/search/src/search/views.py

@@ -89,21 +89,13 @@ def search(request):
 
 
   if collection['id']:
   if collection['id']:
     hue_collection = Collection.objects.get(id=collection['id']) # TODO perms
     hue_collection = Collection.objects.get(id=collection['id']) # TODO perms
-    # collection['name']=
-  
+
   print request.POST
   print request.POST
     
     
   if collection:
   if collection:
-    solr_query = {}    
     try:      
     try:      
-      solr_query = {}      
-      
-      solr_query['collection'] = collection['name']
-      solr_query['rows'] = 10
-      solr_query['start'] = 0
-      
-      response = SolrApi(SOLR_URL.get(), request.user).query2(solr_query, collection, query)
-      response = augment_solr_response2(response, collection, query, solr_query)
+      response = SolrApi(SOLR_URL.get(), request.user).query2(collection, query)
+      response = augment_solr_response2(response, collection, query)
       print response
       print response
     except RestException, e:
     except RestException, e:
       try:
       try:
@@ -118,7 +110,7 @@ def search(request):
     response['error'] = _('There is no collection to search.')
     response['error'] = _('There is no collection to search.')
 
 
   if 'error' in response:
   if 'error' in response:
-    augment_solr_exception(response, collection, solr_query)
+    augment_solr_exception(response, collection)
 
 
   return HttpResponse(json.dumps(response), mimetype="application/json")
   return HttpResponse(json.dumps(response), mimetype="application/json")
 
 
@@ -488,6 +480,44 @@ def get_document(request):
   return HttpResponse(json.dumps(result), mimetype="application/json")
   return HttpResponse(json.dumps(result), mimetype="application/json")
 
 
 
 
+# TODO security
+def get_timeline(request):  
+  result = {'status': -1, 'message': 'Error'}
+
+#  try:
+  collection = json.loads(request.POST.get('collection', '{}'))
+  query = json.loads(request.POST.get('query', '{}'))
+
+  facet = json.loads(request.POST.get('facet', '{}'))
+  facet_filter = request.POST.get('d', '{}')
+      
+  facet_id = facet['id']
+
+  # Only care about our current field:value filter
+  for fq in query['fqs']:
+    if fq['id'] == facet_id:
+      fq['filter'] = [facet_filter] 
+  
+  # Remove other facets from collection
+  collection['facets'] = filter(lambda f: f['widgetType'] == 'histogram-widget', collection['facets'])
+  
+  response = SolrApi(SOLR_URL.get(), request.user).query2(collection, query)
+  response = augment_solr_response2(response, collection, query)
+  
+  print 'get_timeline'
+  print response['normalized_facets']
+
+  result['series'] = {'label': facet_filter, 'counts': response['normalized_facets'][0]['counts']}
+  result['status'] = 0
+  result['message'] = ''
+    
+#  except Exception, e:
+#    print e
+#    result['message'] = unicode(str(e), "utf8")
+
+  return HttpResponse(json.dumps(result), mimetype="application/json")
+
+
 # TODO security
 # TODO security
 def new_facet(request):  
 def new_facet(request):  
   result = {'status': -1, 'message': 'Error'}
   result = {'status': -1, 'message': 'Error'}

+ 74 - 38
apps/search/static/js/search.ko.js

@@ -197,6 +197,7 @@ var Query = function (vm, query) {
 
 
   self.q = ko.observable(query.q);
   self.q = ko.observable(query.q);
   self.fqs = ko.mapping.fromJS(query.fqs);
   self.fqs = ko.mapping.fromJS(query.fqs);
+  // multiq?
 
 
   self.toggleFacet = function (data) {
   self.toggleFacet = function (data) {
 	var fq = self.getFacetFilter(data.widget_id);
 	var fq = self.getFacetFilter(data.widget_id);
@@ -255,7 +256,7 @@ var Query = function (vm, query) {
 
 
   self.getFacetFilter = function (widget_id) {
   self.getFacetFilter = function (widget_id) {
     var _fq = null;
     var _fq = null;
-    $.each(self.fqs(), function (index, fq) {
+    $.each(self.fqs(), function (index, fq) { 
       if (fq.id() == widget_id) {
       if (fq.id() == widget_id) {
         _fq = fq;
         _fq = fq;
         return false;
         return false;
@@ -344,6 +345,7 @@ var Collection = function (vm, collection) {
     });
     });
     return _fields;
     return _fields;
   });
   });
+
   self.getTemplateField = function (name) {
   self.getTemplateField = function (name) {
     var _field = null;
     var _field = null;
     $.each(self.template.fields(), function (index, field) {
     $.each(self.template.fields(), function (index, field) {
@@ -474,6 +476,7 @@ var Collection = function (vm, collection) {
   
   
   self.translateSelectedField = function (index, direction) {
   self.translateSelectedField = function (index, direction) {
 	var array = self.template.fieldsSelected();
 	var array = self.template.fieldsSelected();
+
     if (direction == 'left') {
     if (direction == 'left') {
 	  self.template.fieldsSelected.splice(index - 1, 2, array[index], array[index - 1]);
 	  self.template.fieldsSelected.splice(index - 1, 2, array[index], array[index - 1]);
     } else {
     } else {
@@ -502,7 +505,7 @@ var SearchViewModel = function (collection_json, query_json) {
     var _facet = null;
     var _facet = null;
     if (self.norm_facets() !== undefined) {
     if (self.norm_facets() !== undefined) {
       $.each(self.norm_facets(), function (index, norm_facet) {  
       $.each(self.norm_facets(), function (index, norm_facet) {  
-        if (norm_facet.id == facet_id()) {
+        if (norm_facet.id == facet_id) {
           _facet = norm_facet;
           _facet = norm_facet;
         }      
         }      
       });
       });
@@ -539,47 +542,80 @@ var SearchViewModel = function (collection_json, query_json) {
   self.search = function () {
   self.search = function () {
     self.isRetrievingResults(true);
     self.isRetrievingResults(true);
     $(".jHueNotify").hide();
     $(".jHueNotify").hide();
-    $.post("/search/search", {
-      collection: ko.mapping.toJSON(self.collection),
-      query: ko.mapping.toJSON(self.query),
-      layout: ko.mapping.toJSON(self.columns)
-    }, function (data) {
-      self.response(data);
-      self.results.removeAll();
-      if (data.error) {
-        $(document).trigger("error", data.error);
-      }
-      else {
-        if (self.collection.template.isGridLayout()) {
-          // Table view
-          $.each(data.response.docs, function (index, item) {
-            var row = [];
-            var fields = self.collection.template.fieldsSelected();
-            // Display selected fields or whole json document
-            if (fields.length != 0) {
-              $.each(self.collection.template.fieldsSelected(), function (index, field) {  
-                row.push(item[field]);
-              });
-            } else {
-              row.push(ko.mapping.toJSON(item)); 
-            }
-            var doc = {'id': item[self.collection.idField], 'row': row};
-            self.results.push(doc);
-          });
+    
+    // Multi queries
+    var facet = self.query.getFacetFilter('4933579c-38b0-4599-afa1-021e2ca7ae1c');
+    var multiQs = [];
+
+    // todo APPLUY FQ filter!!
+    
+    // self.query.q().slice(1) too ?
+    if (facet && facet.filter().length > 0) { // todo + histogram there if set to multi q
+      multiQs = $.map(facet.filter(), function(d) {
+    	return $.post("/search/get_timeline", {
+            collection: ko.mapping.toJSON(self.collection),
+            query: ko.mapping.toJSON(self.query),
+            facet: ko.mapping.toJSON(facet),
+            d: d
+           }, function (data) {return data});
+      });
+    }
+
+    $.when.apply($, [
+      $.post("/search/search", {
+        collection: ko.mapping.toJSON(self.collection),
+        query: ko.mapping.toJSON(self.query),
+        layout: ko.mapping.toJSON(self.columns)
+      }, function (data) {
+        self.response(data);
+        self.results.removeAll();
+        if (data.error) {
+          $(document).trigger("error", data.error);
         }
         }
         else {
         else {
-          // Template view
-          var _mustacheTmpl = fixTemplateDotsAndFunctionNames(self.collection.template.template());
-          $.each(data.response.docs, function (index, item) {
-            addTemplateFunctions(item);
-            self.results.push(Mustache.render(_mustacheTmpl, item));
-          });
+          if (self.collection.template.isGridLayout()) {
+            // Table view
+            $.each(data.response.docs, function (index, item) {
+              var row = [];
+              var fields = self.collection.template.fieldsSelected();
+              // Display selected fields or whole json document
+              if (fields.length != 0) {
+                $.each(self.collection.template.fieldsSelected(), function (index, field) {  
+                  row.push(item[field]);
+                });
+              } else {
+                row.push(ko.mapping.toJSON(item)); 
+              }
+              var doc = {'id': item[self.collection.idField], 'row': row};
+              self.results.push(doc);
+            });
+          }
+          else {
+            // Template view
+            var _mustacheTmpl = fixTemplateDotsAndFunctionNames(self.collection.template.template());
+            $.each(data.response.docs, function (index, item) {
+              addTemplateFunctions(item);
+              self.results.push(Mustache.render(_mustacheTmpl, item));
+            });
+          }
+          self.isRetrievingResults(false);
         }
         }
-        self.isRetrievingResults(false);
+      })
+      ].concat(multiQs)
+    )
+    .done(function() {
+      if (arguments[0] instanceof Array) { // If multi queries
+    	var histoFacet = self.getFacetFromQuery('e90cc50b-3d55-fb7c-28f3-a0710ca28ae6');    	
+        for (var i = 1; i < arguments.length; i++) {
+          histoFacet.extraSeries.push(arguments[i][0]['series']);
+        }
+        self.response.valueHasMutated();
       }
       }
-    }).fail(function (xhr, textStatus, errorThrown) {
+    })
+    .fail(function (xhr, textStatus, errorThrown) {
       $(document).trigger("error", xhr.responseText);
       $(document).trigger("error", xhr.responseText);
-    }).always(function () {
+    })
+    .always(function () {
       $('.btn-loading').button('reset');
       $('.btn-loading').button('reset');
     });
     });
   };
   };