Bläddra i källkod

HUE-2173 [search] Add nested text facet on a string field

Romain Rigaux 9 år sedan
förälder
incheckning
0adee37d96

+ 10 - 2
apps/search/src/search/static/search/js/search.ko.js

@@ -589,6 +589,9 @@ var Collection = function (vm, collection) {
     facet.properties.limit.subscribe(function () {
       vm.search();
     });
+    facet.properties.mincount.subscribe(function () {
+      vm.search();
+    });
     if (facet.properties.gap) {
       facet.properties.gap.subscribe(function () {
         vm.search();
@@ -737,6 +740,9 @@ var Collection = function (vm, collection) {
           facet.properties.limit.subscribe(function () {
             vm.search();
           });
+          facet.properties.mincount.subscribe(function () {
+            vm.search();
+          });
           if (facet.properties.gap) {
             facet.properties.gap.subscribe(function () {
               vm.search();
@@ -1348,8 +1354,8 @@ var SearchViewModel = function (collection_json, query_json, initial_json) {
     });
   }
 
-  self.draggableHit = ko.observable(bareWidgetBuilder("Hit Count", "hit-widget"));
-  self.draggableFacet = ko.observable(bareWidgetBuilder("Facet", "facet-widget"));
+  self.draggableHit = ko.observable(bareWidgetBuilder("Hit Count", "hit-widget")); // Not used
+  self.draggableFacet = ko.observable(bareWidgetBuilder("Facet", "facet-widget")); // Deprecated
   self.draggableResultset = ko.observable(bareWidgetBuilder("Grid Results", "resultset-widget"));
   self.draggableHtmlResultset = ko.observable(bareWidgetBuilder("HTML Results", "html-resultset-widget"));
   self.draggableHistogram = ko.observable(bareWidgetBuilder("Histogram", "histogram-widget")); // Deprecated
@@ -1367,6 +1373,8 @@ var SearchViewModel = function (collection_json, query_json, initial_json) {
   self.draggableTimeline = ko.observable(bareWidgetBuilder("Timeline", "timeline-widget"));
   self.draggableGradienMap = ko.observable(bareWidgetBuilder("Gradient Map", "gradient-map-widget"));
   self.draggableTree2 = ko.observable(bareWidgetBuilder("Tree", "tree2-widget"));
+  self.draggableTextFacet = ko.observable(bareWidgetBuilder("Text Facet", "text-facet-widget")); 
+
 
   self.availableDateFields = ko.computed(function() {
     return $.grep(self.collection.availableFacetFields(), function(field) { return DATE_TYPES.indexOf(field.type()) != -1; });

+ 167 - 3
apps/search/src/search/templates/common_search.mako

@@ -196,7 +196,7 @@ from django.utils.translation import ugettext as _
 
       </%def>
       <%def name="widgets()">
-    <div data-bind="css: { 'draggable-widget': true, 'disabled': !availableDraggableChart() },
+    <div data-bind="visible: ! $root.isLatest(), css: { 'draggable-widget': true, 'disabled': !availableDraggableChart() },
                     draggable: {data: draggableFacet(), isEnabled: availableDraggableChart,
                     options: {'start': function(event, ui){lastWindowScrollPosition = $(window).scrollTop();$('.card-body').slideUp('fast');},
                               'stop': function(event, ui){$('.card-body').slideDown('fast', function(){$(window).scrollTop(lastWindowScrollPosition)});}}}"
@@ -205,6 +205,15 @@ from django.utils.translation import ugettext as _
                        <i class="fa fa-sort-amount-asc"></i>
          </a>
     </div>
+    <div data-bind="visible: $root.isLatest(), css: { 'draggable-widget': true, 'disabled': !availableDraggableChart() },
+                    draggable: {data: draggableTextFacet(), isEnabled: availableDraggableChart,
+                    options: {'start': function(event, ui){lastWindowScrollPosition = $(window).scrollTop();$('.card-body').slideUp('fast');},
+                              'stop': function(event, ui){$('.card-body').slideDown('fast', function(){$(window).scrollTop(lastWindowScrollPosition)});}}}"
+         title="${_('Text Facet')}" rel="tooltip" data-placement="top">
+         <a data-bind="style: { cursor: $root.availableDraggableChart() ? 'move' : 'default' }">
+                       <i class="fa fa-sort-amount-asc"></i>
+         </a>
+    </div>
     <div data-bind="visible: ! $root.isLatest(), css: { 'draggable-widget': true, 'disabled': !availableDraggableChart() },
                     draggable: {data: draggablePie(), isEnabled: availableDraggableChart,
                     options: {'start': function(event, ui){lastWindowScrollPosition = $(window).scrollTop();$('.card-body').slideUp('fast');},
@@ -494,7 +503,57 @@ ${ dashboard.layout_skeleton() }
 
 
 <script type="text/html" id="facet-toggle2">
-  <!-- ko if: $root.isEditing -->
+
+    ## Dimension 1
+   <div class="facet-field-cnt" data-bind="visible: properties.canRange">
+      <span class="facet-field-label facet-field-label-fixed-width">${ _('Type') }</span>
+      <a href="javascript: void(0)" title="${ _('Toggle how to group the values') }" data-bind="click: $root.collection.toggleRangeFacet">
+        <i class="fa" data-bind="css: { 'fa-arrows-h': type() == 'range', 'fa-circle': type() == 'field', 'fa-level-up': type() == 'range-up' }, attr: { title: type() == 'field' ? 'Range' : type() == 'range-up' ? 'Range and up' : 'Term' }"></i>
+        <span data-bind="visible: type() == 'range'">${_('range')}</span>
+        <span data-bind="visible: type() == 'range-up'">${_('range & up')}</span>
+        <span data-bind="visible: type() == 'field'">${_('field')}</span>
+      </a>
+    </div>
+    <div class="facet-field-cnt">
+      <span class="facet-field-label facet-field-label-fixed-width">${ _('Sorting') }</span>
+      <a href="javascript: void(0)" title="${ _('Toggle sort order') }" data-bind="click: $root.collection.toggleSortFacet">
+        <i class="fa" data-bind="css: { 'fa-caret-down': properties.sort() == 'desc', 'fa-caret-up': properties.sort() == 'asc' }"></i>
+        <span data-bind="visible: properties.sort() == 'desc'">${_('descending')}</span>
+        <span data-bind="visible: properties.sort() == 'asc'">${_('ascending')}</span>
+      </a>
+    </div>
+
+    <!-- ko if: type() == 'range' || type() == 'range-up' || (type() == 'nested' && typeof properties.min != "undefined") -->
+      <!-- ko ifnot: properties.isDate() -->
+        <div class="slider-cnt" data-bind="slider: {start: properties.min, end: properties.max, gap: properties.initial_gap, min: properties.initial_start, max: properties.initial_end, properties: properties, labels: SLIDER_LABELS}"></div>
+      <!-- /ko -->
+      <!-- ko if: properties.isDate() && $root.collection.timeFilter && $root.collection.timeFilter.field && $root.collection.timeFilter.field() != field() -->
+        <div data-bind="daterangepicker: {start: properties.start, end: properties.end, gap: properties.initial_gap, relatedgap: properties.gap, min: properties.min, max: properties.max}"></div>
+        <br/>
+      <!-- /ko -->
+    <!-- /ko -->
+
+    
+      <div class="facet-field-cnt">
+        <span class="spinedit-cnt">
+          <span class="facet-field-label facet-field-label-fixed-width">
+            ${ _('Limit') }
+          </span>
+          <input type="text" class="input-medium" data-bind="spinedit: properties.limit"/>
+        </span>
+      </div>
+    
+      <div class="facet-field-cnt">
+        <span class="spinedit-cnt">
+          <span class="facet-field-label">
+            ${ _('Min Count') }
+          </span>
+          <input type="text" class="input-medium" data-bind="spinedit: properties.mincount"/>
+        </span>
+      </div>
+
+  ## Dimensions > 1
+  <!-- ko if: $root.isEditing -->  
   <span data-bind="foreach: properties.facets, visible: !$parents[1].isLoading()">
     <div class="filter-box">
       <div class="title">
@@ -504,7 +563,7 @@ ${ dashboard.layout_skeleton() }
         <div class="hit-title" data-bind="text: field, attr: {'title': field}"></div>
         <div class="clearfix"></div>
       </div>
-
+ 
       <div class="content">
         <div class="facet-field-cnt">
           <span class="spinedit-cnt">
@@ -534,6 +593,7 @@ ${ dashboard.layout_skeleton() }
     </div>
   </span>
   <!-- /ko -->
+
   <!-- ko ifnot: $root.isEditing -->
   <span data-bind="foreach: properties.facets, visible: !$parents[1].isLoading()">
     <div class="filter-box" style="opacity: 0.7">
@@ -720,6 +780,110 @@ ${ dashboard.layout_skeleton() }
   <!-- /ko -->
 </script>
 
+
+<script type="text/html" id="text-facet-widget">
+  <div class="widget-spinner" data-bind="visible: isLoading()">
+    <!--[if !IE]> --><i class="fa fa-spinner fa-spin"></i><!-- <![endif]-->
+    <!--[if IE]><img src="${ static('desktop/art/spinner.gif') }" /><![endif]-->
+  </div>
+
+  <!-- ko if: $root.getFacetFromQuery(id()).has_data() -->
+  <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">
+      <span data-bind="template: { name: 'facet-toggle2', afterRender: function(){ $root.getWidgetById($parent.id()).isLoading(false); } }">
+      </span>
+    </div>
+    <div data-bind="with: $root.collection.getFacetById($parent.id())">
+      <!-- ko if: properties.type() == 'field' -->
+        <div data-bind="foreach: $parent.counts">
+          <div class="trigger-exclude">
+              <!-- ko if: $index() < $parent.properties.limit() -->
+                <!-- ko if: ! $data.selected -->
+                  <a class="exclude pointer" data-bind="click: function(){ $root.query.toggleFacet({facet: $data, widget_id: $parent.id(), 'exclude': true}) }" title="${ _('Exclude this value') }"><i class="fa fa-minus"></i></a>
+                  <div class="hellip">
+                    <a class="pointer" dir="ltr" data-bind="html: prettifyDate($data.value), click: function(){ $root.query.toggleFacet({facet: $data, widget_id: $parent.id()}) }, attr: {'title': $data.value + ' (' + $data.count + ')'}"></a>
+                    <span class="pointer counter" dir="rtl" data-bind="text: ' (' + $data.count + ')', click: function(){ $root.query.toggleFacet({facet: $data, widget_id: $parent.id()}) }"></span>
+                  </div>
+                <!-- /ko -->
+                <!-- ko if: $data.selected -->
+                  <span class="pointer" data-bind="click: function(){ $root.query.toggleFacet({facet: $data, widget_id: $parent.id()}) }">
+                    <a class="include pointer" data-bind="visible: ! exclude"><i class="fa fa-times"></i></a>
+                    <a class="include pointer" data-bind="visible: exclude"><i class="fa fa-plus"></i></a>
+                    <div class="hellip">
+                      <strong data-bind="html: prettifyDate($data.value), attr: {'title': $data.value}"></strong>
+                    </div>
+                  </span>
+                <!-- /ko -->
+              <!-- /ko -->
+              <!-- ko if: $index() == $parent.properties.limit() -->
+                <!-- ko if: $parent.properties.prevLimit == undefined || $parent.properties.prevLimit == $parent.properties.limit() -->
+                  <a class="pointer" data-bind="click: function(){ $root.collection.upDownFacetLimit($parent.id(), 'up') }">
+                    ${ _('Show more...') }
+                  </a>
+                <!-- /ko -->
+                <!-- ko if: $parent.properties.prevLimit != undefined && $parent.properties.prevLimit != $parent.properties.limit() -->
+                  <a class="pointer" data-bind="click: function(){ $root.collection.upDownFacetLimit($parent.id(), 'up') }">
+                    ${ _('Show more') }
+                  </a>
+                  /
+                  <a class="pointer" data-bind="click: function(){ $root.collection.upDownFacetLimit($parent.id(), 'down') }">
+                    ${ _('less...') }
+                  </a>
+                <!-- /ko -->
+              <!-- /ko -->
+          </div>
+        </div>
+      <!-- /ko -->
+      <!-- ko if: properties.type() == 'range' -->
+        <div data-bind="foreach: $parent.counts">
+          <div class="trigger-exclude">
+              <!-- ko if: ! selected -->
+                <a class="exclude pointer" data-bind="click: function(){ $root.query.selectRangeFacet({count: $data.value, widget_id: $parent.id(), from: $data.from, to: $data.to, cat: $data.field, 'exclude': true}) }" title="${ _('Exclude this value') }"><i class="fa fa-minus"></i></a>
+                <div class="hellip">
+                  <a class="pointer" data-bind="html: $data.is_single_unit_gap ? prettifyDate($data.from, $parent, $data.to) : prettifyDateRange($data.from, $data.to, $parent, false), click: function(){ $root.query.selectRangeFacet({count: $data.value, widget_id: $parent.id(), from: $data.from, to: $data.to, cat: $data.field}) }, attr: {'title': ($data.is_single_unit_gap ? $data.from : $data.from + ' - ' + $data.to) + ' (' + $data.value + ')'}"></a>
+                  <span class="pointer counter" data-bind="text: ' (' + $data.value + ')', click: function(){ $root.query.selectRangeFacet({count: $data.value, widget_id: $parent.id(), from: $data.from, to: $data.to, cat: $data.field}) }"></span>
+                </div>
+              <!-- /ko -->
+              <!-- ko if: selected -->
+                <span class="pointer" data-bind="click: function(){ $root.query.selectRangeFacet({count: $data.value, widget_id: $parent.id(), from: $data.from, to: $data.to, cat: $data.field}) }">
+                  <a class="include pointer" data-bind="visible: ! exclude"><i class="fa fa-times"></i></a>
+                  <a class="include pointer" data-bind="visible: exclude"><i class="fa fa-plus"></i></a>
+                  <div class="hellip">
+                    <strong data-bind="html: $data.is_single_unit_gap ? prettifyDate($data.from, $parent, $data.to) : prettifyDateRange($data.from, $data.to, $parent, false), attr: {'title': ($data.is_single_unit_gap ? $data.from : $data.from + ' - ' + $data.to) + ' (' + $data.value + ')'}"></strong>
+                  </div>
+                </span>
+              <!-- /ko -->
+          </div>
+        </div>
+      <!-- /ko -->
+      <!-- ko if: properties.type() == 'range-up' -->
+        <div data-bind="foreach: $parent.counts">
+          <div class="trigger-exclude">
+              <!-- ko if: ! selected -->
+                <a class="exclude pointer" data-bind="click: function(){ $root.query.selectRangeUpFacet({count: $data.value, widget_id: $parent.id(), from: $data.from, to: $data.to, cat: $data.field, 'exclude': true, is_up: $data.is_up}) }" title="${ _('Exclude this value') }"><i class="fa fa-minus"></i></a>
+                <div class="hellip">
+                  <a class="pointer" data-bind="html: prettifyDate($data.from, $parent) + ($data.is_up ? ' & Up' : ' & Less'), click: function(){ $root.query.selectRangeUpFacet({count: $data.value, widget_id: $parent.id(), from: $data.from, to: $data.to, cat: $data.field, is_up: $data.is_up}) }, attr: {'title': $data.from + ($data.is_up ? ' & Up' : ' & Less') + ' (' + $data.total_counts + ')'}"></a>
+                  <span class="pointer counter" data-bind="text: ' (' + $data.total_counts + ')', click: function(){ $root.query.selectRangeUpFacet({count: $data.value, widget_id: $parent.id(), from: $data.from, to: $data.to, cat: $data.field, is_up: $data.is_up}) }"></span>
+                </div>
+              <!-- /ko -->
+              <!-- ko if: selected -->
+                <span class="pointer" data-bind="click: function(){ $root.query.selectRangeUpFacet({count: $data.value, widget_id: $parent.id(), from: $data.from, to: $data.to, cat: $data.field, is_up: $data.is_up}) }">
+                  <a class="include pointer" data-bind="visible: ! exclude"><i class="fa fa-times"></i></a>
+                  <a class="include pointer" data-bind="visible: exclude"><i class="fa fa-plus"></i></a>
+                  <div class="hellip">
+                    <strong data-bind="html: prettifyDate($data.from, $parent) + ($data.is_up ? ' & Up' : ' & Less')"></strong>
+                  </div>
+                </span>
+              <!-- /ko -->
+          </div>
+        </div>
+      <!-- /ko -->
+    </div>
+  </div>
+  <!-- /ko -->
+</script>
+
+
 <script type="text/html" id="resultset-widget">
   <!-- ko if: $root.collection.template.isGridLayout() -->
 

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

@@ -562,7 +562,9 @@ def _create_facet(collection, user, facet_id, facet_label, facet_field, widget_t
     else:
       facet_type = 'field'
 
-    if widget_type in ('bucket-widget', 'pie2-widget', 'timeline-widget', 'tree2-widget'):
+    if widget_type in ('bucket-widget', 'pie2-widget', 'timeline-widget', 'tree2-widget', 'text-facet-widget'):
+      if widget_type == 'text-facet-widget':
+        properties['type'] = facet_type
       facet_type = 'nested'
       properties['facets_form'] = {'field': '', 'mincount': 1, 'limit': 10, 'aggregate': 'count'}
       properties['facets'] = []
@@ -571,7 +573,7 @@ def _create_facet(collection, user, facet_id, facet_label, facet_field, widget_t
         properties['timelineChartType'] = 'bar'
       elif widget_type == 'tree2-widget':
         properties['scope'] = 'tree'
-        properties['limit'] = 5
+        properties['facets_form']['limit'] = 5
         properties['isOldPivot'] = True
       else:
         properties['scope'] = 'stack'

+ 1 - 0
desktop/libs/libsolr/src/libsolr/api.py

@@ -302,6 +302,7 @@ class SolrApi(object):
       timeFilter = self._get_range_borders(collection, query)
 
       for facet in collection['facets']:
+        print facet
         if facet['type'] == 'query':
           params += (('facet.query', '%s' % facet['field']),)
         elif facet['type'] == 'range' or facet['type'] == 'range-up':