You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by tb...@apache.org on 2018/11/06 15:21:15 UTC

[3/9] brooklyn-ui git commit: sort search results by relevance

sort search results by relevance

and display sort fields dropdown in order used


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/commit/6ae7338f
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/tree/6ae7338f
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/diff/6ae7338f

Branch: refs/heads/master
Commit: 6ae7338f72a25cc7862ad0db37860a108dabce3d
Parents: a0cddab
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Wed Oct 31 12:56:20 2018 +0000
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Thu Nov 1 12:15:22 2018 +0000

----------------------------------------------------------------------
 .../catalog-selector.directive.js               | 65 +++++++++++++++-----
 .../catalog-selector.template.html              |  5 +-
 2 files changed, 52 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/6ae7338f/ui-modules/blueprint-composer/app/components/catalog-selector/catalog-selector.directive.js
----------------------------------------------------------------------
diff --git a/ui-modules/blueprint-composer/app/components/catalog-selector/catalog-selector.directive.js b/ui-modules/blueprint-composer/app/components/catalog-selector/catalog-selector.directive.js
index 60f7beb..9f0bd14 100644
--- a/ui-modules/blueprint-composer/app/components/catalog-selector/catalog-selector.directive.js
+++ b/ui-modules/blueprint-composer/app/components/catalog-selector/catalog-selector.directive.js
@@ -25,10 +25,11 @@ import { distanceInWordsToNow } from 'date-fns';
 const MIN_ROWS_PER_PAGE = 4;
 
 const PALETTE_VIEW_ORDERS = {
-        name: { label: "Name", field: "displayName" },
-        lastUsed: { label: "Recent", field: "-lastUsed" }, 
-        bundle: { label: "Bundle", field: "containingBundle" }, 
-        id: { label: "ID", field: "symbolicName" }, 
+        relevance: { id: "relevance", label: "Relevance", field: "relevance" },
+        name: { id: "name", label: "Name", field: "displayName" },
+        lastUsed: { id: "lastUsed", label: "Recent", field: "-lastUsed" }, 
+        bundle: { id: "bundle", label: "Bundle", field: "containingBundle" }, 
+        id: { id: "id", label: "ID", field: "symbolicName" }, 
     };
 
 const PALETTE_VIEW_MODES = {
@@ -39,7 +40,7 @@ const PALETTE_VIEW_MODES = {
     };
 
 // fields in either bundle or type record:
-const FIELDS_TO_SEARCH = ['name', 'displayName', 'symbolicName', 'version', 'type', 'supertypes', 'containingBundle', 'description', 'displayTags', 'tags'];
+const FIELDS_TO_SEARCH = ['displayName', 'name', 'symbolicName', 'version', 'type', 'supertypes', 'containingBundle', 'description', 'displayTags', 'tags'];
 
 export function catalogSelectorDirective() {
     return {
@@ -88,23 +89,42 @@ export function catalogSelectorSearchFilter() {
     return function (items, search) {
         if (search) {
             return items.filter(function (item) {
-                return search.toLowerCase().split(' ').reduce( (found, part) => 
-                    found &&
-                    FIELDS_TO_SEARCH
-                        .filter(field => item.hasOwnProperty(field) && item[field])
-                        .reduce((match, field) => {
+                item.relevance = 0;
+                let wordNum = 0;
+                return search.toLowerCase().split(' ').reduce( (found, part) => {
+                    wordNum++;
+                    let fieldNum = 0;
+                    return found &&
+                        FIELDS_TO_SEARCH.reduce((match, field) => {
                             if (match) return true;
+                            fieldNum++;
+                            if (!item.hasOwnProperty(field) || !item[field]) return false;
                             let text = item[field];
                             if (!text.toLowerCase) {
                                 text = JSON.stringify(text).toLowerCase();
                             } else {
                                 text = text.toLowerCase();
                             }
-                            return match || text.indexOf(part) > -1;
+                            let index = text.indexOf(part);
+                            if (index == -1) return false;
+                            // found, set relevance -- uses an ad hoc heuristic preferring first fields and short text length,
+                            // earlier occurrences and earlier words weighted more highly (smaller number is better)
+                            let score = fieldNum * (2 / (1 + wordNum)) * Math.log(1 + text.length * index);
+                            /* to debug the scoring function:
+                            if (item.symbolicName.indexOf("EIP") >= 0 || item.symbolicName.indexOf("OpsWorks") >= 0) { 
+                                console.log(item.symbolicName, ": match", part, "in", field,
+                                    "#", fieldNum, wordNum, 
+                                    "pos", index, "/", text.length, 
+                                    ":", item.relevance, "+=", score);
+                            }
+                            */
+                            item.relevance += score;
+                            return true;
                         }, false)
-                , true);
+                }, true);
             });
         } else {
+            items.forEach( item => item.relevance = 0 );
             return items;
         }
     }
@@ -150,7 +170,6 @@ function controller($scope, $element, $timeout, $q, $uibModal, $log, $templateCa
     
     if (!$scope.state) $scope.state = {};
     if (!$scope.state.viewMode) $scope.state.viewMode = PALETTE_VIEW_MODES.normal;
-    if (!$scope.state.currentOrder) $scope.state.currentOrder = [ PALETTE_VIEW_ORDERS.name.field, '-version' ];
     
     $scope.pagination = {
         page: 1,
@@ -247,11 +266,23 @@ function controller($scope, $element, $timeout, $q, $uibModal, $log, $templateCa
         paletteDragAndDropService.dragEnd();
         tryMarkUsed(item);
     };
+    
     $scope.sortBy = function (order) {
-        let newOrder = [].concat($scope.state.currentOrder);
-        newOrder = newOrder.filter( (o) => o !== order.field );
-        $scope.state.currentOrder = [order.field].concat(newOrder);
+        let newFirst = {};
+        if (order) {
+            newFirst[order.id] = order;
+        }
+        $scope.state.currentOrder = Object.assign(newFirst, $scope.state.currentOrder, newFirst);
+        $scope.state.currentOrderFields = [];
+        $scope.state.currentOrderValues = [];
+        Object.values($scope.state.currentOrder).forEach( it => {
+            $scope.state.currentOrderValues.push(it);
+            $scope.state.currentOrderFields.push(it.field);
+        });
     };
+    if (!$scope.state.currentOrder) $scope.state.currentOrder = Object.assign({}, PALETTE_VIEW_ORDERS);
+    $scope.sortBy();
+    
     $scope.allowFreeForm = function () {
         return [
             EntityFamily.LOCATION
@@ -295,6 +326,8 @@ function controller($scope, $element, $timeout, $q, $uibModal, $log, $templateCa
         if (l < 100000) return 'Preselected for inclusion in "Recent" filter.';
         return 'Last used: ' + distanceInWordsToNow(l, { includeSeconds: true, addSuffix: true });
     }; 
+    $scope.roundTwoDecimals = (num) => Math.round(num*100)/100.0;
+    
     $scope.showPaletteControls = false;
     $scope.onFiltersShown = () => {
       $timeout( () => {

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/6ae7338f/ui-modules/blueprint-composer/app/components/catalog-selector/catalog-selector.template.html
----------------------------------------------------------------------
diff --git a/ui-modules/blueprint-composer/app/components/catalog-selector/catalog-selector.template.html b/ui-modules/blueprint-composer/app/components/catalog-selector/catalog-selector.template.html
index 870600c..bd996e9 100644
--- a/ui-modules/blueprint-composer/app/components/catalog-selector/catalog-selector.template.html
+++ b/ui-modules/blueprint-composer/app/components/catalog-selector/catalog-selector.template.html
@@ -74,7 +74,7 @@
                     <i class="fa fa-sort"></i></div>
                 </a>
                 <ul class="dropdown-menu right-align-icon" role="menu" uib-dropdown-menu aria-labelledby="palette-sort">
-                        <li role="menuitem" ng-repeat="order in viewOrders track by $index" ng-class="{'active': state.currentOrder[0] === order.field}" class="layer">
+                        <li role="menuitem" ng-repeat="order in state.currentOrderValues track by $index" class="layer">
                             <a ng-click="sortBy(order)"><i class="fa fa-fw fa-circle"></i> {{ order.label }}</a>
                         </li>
                 </ul>
@@ -100,7 +100,7 @@
             <!-- here and below, col-xs-3 or -4 or -2 all work giving different densities;
                  this could be configurable ("compressed"=xs-2 w no labels, "normal"=xs-3, "big"=xs-4) -->
             <div class="catalog-palette-item" ng-class="state.viewMode.classes"
-                    ng-repeat="item in searchedItems = (items | catalogSelectorSearch:search | catalogSelectorFilters:this) | orderBy:state.currentOrder | limitTo:pagination.itemsPerPage:(pagination.page-1)*pagination.itemsPerPage track by (item.containingBundle + ':' + item.symbolicName + ':' + item.version)"
+                    ng-repeat="item in searchedItems = (items | catalogSelectorSearch:search | catalogSelectorFilters:this) | orderBy:state.currentOrderFields | limitTo:pagination.itemsPerPage:(pagination.page-1)*pagination.itemsPerPage track by (item.containingBundle + ':' + item.symbolicName + ':' + item.version)"
                     ng-click="onSelectItem(item)">
                 <div class="item" draggable="true" ng-dragstart="onDragItem(item, $event)" ng-dragend="onDragEnd(item, $event)">
                     <div class="item-logo">
@@ -157,6 +157,7 @@
                 <span ng-repeat-start="tag in item.displayTags" class="label label-primary palette-item-tag">{{ tag }}</span>
                 <span ng-repeat-end> </span> </p>
             <p><i class="mini-icon fa fa-fw fa-file-zip-o"></i> {{item.containingBundle}}</p>
+            <p ng-if="item.relevance"><i class="mini-icon fa fa-sort-numeric-asc"></i> Relevance score: {{ roundTwoDecimals(item.relevance) }}</p>
         </div>
     </div>
 </script>