You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@atlas.apache.org by sa...@apache.org on 2019/06/17 20:28:46 UTC

[atlas] branch master updated: ATLAS-3282 : UI : use search suggestions API for quick-search

This is an automated email from the ASF dual-hosted git repository.

sarath pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/atlas.git


The following commit(s) were added to refs/heads/master by this push:
     new 9062e2c  ATLAS-3282 : UI : use search suggestions API for quick-search
9062e2c is described below

commit 9062e2c861628d227f0c2616c5c84f2f90af9214
Author: kevalbhatt <kb...@apache.org>
AuthorDate: Mon Jun 17 18:27:22 2019 +0530

    ATLAS-3282 : UI : use search suggestions API for quick-search
    
    Signed-off-by: Sarath Subramanian <sa...@apache.org>
---
 dashboardv2/public/css/bootstrap-sidebar.css      |   2 +-
 dashboardv2/public/css/scss/common.scss           |   2 +-
 dashboardv2/public/css/scss/theme.scss            |  81 +++++----
 dashboardv2/public/js/templates/site/Header.html  |  61 ++++---
 dashboardv2/public/js/utils/CommonViewFunction.js |   2 +-
 dashboardv2/public/js/utils/Helper.js             |  26 ++-
 dashboardv2/public/js/views/graph/LineageUtils.js |   2 +-
 dashboardv2/public/js/views/site/Header.js        | 200 +++++++++++++++-------
 8 files changed, 242 insertions(+), 134 deletions(-)

diff --git a/dashboardv2/public/css/bootstrap-sidebar.css b/dashboardv2/public/css/bootstrap-sidebar.css
index f0c1feb..23db223 100644
--- a/dashboardv2/public/css/bootstrap-sidebar.css
+++ b/dashboardv2/public/css/bootstrap-sidebar.css
@@ -59,7 +59,7 @@
 
 .sidebar-nav>li {
     line-height: 50px;
-    padding: 0 20px;
+    padding: 5px 20px;
     border-bottom: 1px #1c1e2a solid;
 }
 
diff --git a/dashboardv2/public/css/scss/common.scss b/dashboardv2/public/css/scss/common.scss
index 484879b..75da4a4 100644
--- a/dashboardv2/public/css/scss/common.scss
+++ b/dashboardv2/public/css/scss/common.scss
@@ -86,7 +86,7 @@
 
 .detail-page {
     .details-backbutton {
-        display: block !important;
+        display: table-cell !important;
     }
 }
 
diff --git a/dashboardv2/public/css/scss/theme.scss b/dashboardv2/public/css/scss/theme.scss
index 579d7fd..7d75136 100644
--- a/dashboardv2/public/css/scss/theme.scss
+++ b/dashboardv2/public/css/scss/theme.scss
@@ -25,50 +25,36 @@ body {
     overflow-x: hidden;
 }
 
-.detail-page {
-    header.atlas-header {
-        .nav.navbar-nav {
-            width: 16%;
-
-            @media screen and (max-width: 1024px) {
-                width: 24%;
-            }
-        }
-
-        .global-search-container {
-            width: 65%;
-
-            @media screen and (max-width: 1024px) {
-                width: 45%;
-            }
-        }
-    }
-}
-
 header.atlas-header {
     background-color: $white;
     border-bottom: 1px $color_mystic_approx solid;
 
-    .nav.navbar-nav {
-        width: 5%;
-    }
+    .navbar-nav {
+        display: table;
 
-    .global-search-container {
-        top: 8px;
-        width: 76%;
+        li {
+            float: none;
+            display: table-cell;
+            padding: 0px 5px;
 
-        @media screen and (max-width: 1024px) {
-            width: 65%;
         }
     }
 
-    .header-menu {
-        width: 18%;
+    &>table {
+        td {
+            padding: 3px 2px;
+            position: relative;
+            transition: width 0.3s !important;
 
-        @media screen and (max-width: 1024px) {
-            width: 30%;
+            .global-search-container {
+                width: input {
+                    width: 100%;
+                }
+            }
         }
+    }
 
+    .header-menu {
         .dropdown-menu>li>a {
             color: $color_ironside_gray_approx;
 
@@ -81,7 +67,7 @@ header.atlas-header {
             }
         }
 
-        >a {
+        td>a {
             display: inline-block;
             color: $color_ironside_gray_approx;
             padding: 15px 14px;
@@ -149,7 +135,7 @@ header.atlas-header {
 
 .page-title {
     background-color: $color_white_lilac_approx;
-    padding: 10px 15px 0px 15px;
+    padding: 15px 15px 0px 15px;
 
     .title {
         padding-top: 0;
@@ -480,6 +466,25 @@ hr[size="10"] {
 .ui-menu.ui-widget-content.ui-autocomplete {
     box-shadow: 0px 11px 30px -8px grey;
     max-width: 60% !important;
+    max-height: 70vh;
+    overflow-y: auto;
+    /* prevent horizontal scrollbar */
+    overflow-x: hidden;
+
+    .ui-autocomplete-category {
+        padding: 10px;
+        color: #acacac;
+        text-transform: capitalize;
+    }
+
+    li.empty {
+        padding: 5px 2px;
+        line-height: 45px;
+
+        span.empty-message {
+            padding: 10px;
+        }
+    }
 
     .ui-menu-item {
         padding: 5px 2px;
@@ -496,17 +501,9 @@ hr[size="10"] {
 
         .ui-state-active {
             margin: 0px;
-
-            &.empty {
-                background: transparent;
-                color: inherit !important;
-                cursor: default;
-            }
-
             border: none;
             background: #cee0fa;
             color: $black !important;
-            //color: #686868 !important;
         }
 
         a,
diff --git a/dashboardv2/public/js/templates/site/Header.html b/dashboardv2/public/js/templates/site/Header.html
index 4584205..d7b731d 100644
--- a/dashboardv2/public/js/templates/site/Header.html
+++ b/dashboardv2/public/js/templates/site/Header.html
@@ -15,27 +15,42 @@
  * limitations under the License.
 -->
 <header class="clearfix atlas-header">
-    <ul class="nav navbar-nav">
-        <li>
-            <a href="javascript:void(0);" data-id="menuHamburger"><i class="fa fa-bars"></i></a>
-        </li>
-        <li class="details-backbutton"><a href="javascript:void(0);" data-id="backButton"><i class="fa fa-chevron-left"></i> Back To Results</a></li>
-    </ul>
-    <div class="form-group has-feedback align-left-right-icon search-box pull-left global-search-container">
-        <span class="fa fa-search form-control-feedback"></span>
-        <input type="text" class="form-control global-search" name="global search" placeholder="Search entities" data-id="globalSearch" />
-        <span class="fa fa-times form-control-feedback clearable" data-id="clearGlobalSearch"></span>
-    </div>
-    <div class="btn-group pull-right header-menu">
-        <a class="show-stat" href="javascript:void(0);" title="Statistics"><i class="fa fa-bar-chart"></i></a>
-        <a target="_blank" href="http://atlas.apache.org/"><i class="fa  fa-question-circle"></i></a>
-        <a href="javascript:void(0);" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="user-dropdown"><i class="fa fa-user user-circle "></i><span class="userName"></span></a>
-        <ul class="dropdown-menu">
-            <li class="aboutAtlas"><a href="javascript:void(0)">About</a></li>
-            <li role="separator" class="divider"></li>
-            <li>
-                <a href="logout.html"><i class="fa fa-sign-out"></i>Logout</a>
-            </li>
-        </ul>
-    </div>
+    <table>
+        <tr>
+            <td>
+                <ul class="nav navbar-nav">
+                    <li>
+                        <a href="javascript:void(0);" data-id="menuHamburger"><i class="fa fa-bars"></i></a>
+                    </li>
+                    <li class="details-backbutton"><a href="javascript:void(0);" data-id="backButton"><i class="fa fa-chevron-left"></i> Back To Results</a></li>
+                </ul>
+            </td>
+            <td class="global-search-container">
+                <div class="has-feedback align-left-right-icon search-box">
+                    <span class="fa fa-search form-control-feedback"></span>
+                    <input type="text" class="form-control global-search" name="global search" placeholder="Search entities" data-id="globalSearch" />
+                    <span class="fa fa-times form-control-feedback clearable" data-id="clearGlobalSearch"></span>
+                </div>
+            </td>
+            <td>
+                <!-- <div class="btn-group pull-right header-menu"> -->
+                <table class="header-menu">
+                    <tr>
+                        <td><a class="show-stat" href="javascript:void(0);" title="Statistics"><i class="fa fa-bar-chart"></i></a></td>
+                        <td><a target="_blank" href="http://atlas.apache.org/"><i class="fa  fa-question-circle"></i></a></td>
+                        <td>
+                            <a href="javascript:void(0);" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="user-dropdown"><i class="fa fa-user user-circle "></i><span class="userName"></span></a>
+                            <ul class="dropdown-menu">
+                                <li class="aboutAtlas"><a href="javascript:void(0)">About</a></li>
+                                <li role="separator" class="divider"></li>
+                                <li>
+                                    <a href="logout.html"><i class="fa fa-sign-out"></i>Logout</a>
+                                </li>
+                            </ul>
+                        </td>
+                    </tr>
+                </table>
+            </td>
+        </tr>
+    </table>
 </header>
\ No newline at end of file
diff --git a/dashboardv2/public/js/utils/CommonViewFunction.js b/dashboardv2/public/js/utils/CommonViewFunction.js
index 1fc2402..11b05d8 100644
--- a/dashboardv2/public/js/utils/CommonViewFunction.js
+++ b/dashboardv2/public/js/utils/CommonViewFunction.js
@@ -688,7 +688,7 @@ define(['require', 'utils/Utils', 'modules/Modal', 'utils/Messages', 'utils/Enum
                 collection = options.collection,
                 isGlossaryView = options.isGlossaryView,
                 data = ref.ui[(isGlossaryView ? "glossaryForm" : "categoryTermForm")].serializeArray().reduce(function(obj, item) {
-                    obj[item.name] = item.value;
+                    obj[item.name] = item.value.trim();
                     return obj;
                 }, {}),
                 newModel = new options.collection.model(),
diff --git a/dashboardv2/public/js/utils/Helper.js b/dashboardv2/public/js/utils/Helper.js
index 9886b9b..2b0f1e8 100644
--- a/dashboardv2/public/js/utils/Helper.js
+++ b/dashboardv2/public/js/utils/Helper.js
@@ -18,7 +18,8 @@
 define(['require',
     'utils/Utils',
     'd3',
-    'marionette'
+    'marionette',
+    'jquery-ui'
 ], function(require, Utils, d3) {
     'use strict';
     _.mixin({
@@ -201,6 +202,29 @@ define(['require',
             return adapter;
         });
 
+    $.widget("custom.atlasAutoComplete", $.ui.autocomplete, {
+        _create: function() {
+            this._super();
+            this.widget().menu("option", "items", "> :not(.ui-autocomplete-category,.empty)");
+        },
+        _renderMenu: function(ul, items) {
+            var that = this,
+                currentCategory = "";
+            items = _.sortBy(items, 'order');
+            $.each(items, function(index, item) {
+                var li;
+                if (item.category != currentCategory) {
+                    ul.append("<li class='ui-autocomplete-category'>" + item.category + "</li>");
+                    currentCategory = item.category;
+                }
+                that._renderItemData(ul, item);
+            });
+        },
+        _renderItemData: function(ul, item) {
+            return this._renderItem(ul, item);
+        }
+    });
+
     // For placeholder support 
     if (!('placeholder' in HTMLInputElement.prototype)) {
         var originalRender = Backbone.Marionette.LayoutView.prototype.render;
diff --git a/dashboardv2/public/js/views/graph/LineageUtils.js b/dashboardv2/public/js/views/graph/LineageUtils.js
index 2f50fbd..d2da39a 100644
--- a/dashboardv2/public/js/views/graph/LineageUtils.js
+++ b/dashboardv2/public/js/views/graph/LineageUtils.js
@@ -16,7 +16,7 @@
  * limitations under the License.
  */
 
-define(['require', ''], function(require) {
+define(['require'], function(require) {
     'use strict';
     var LinegaeUtils = {};
     LinegaeUtils.DragNode = function(options) {
diff --git a/dashboardv2/public/js/views/site/Header.js b/dashboardv2/public/js/views/site/Header.js
index 30c0420..f4543b1 100644
--- a/dashboardv2/public/js/views/site/Header.js
+++ b/dashboardv2/public/js/views/site/Header.js
@@ -61,13 +61,35 @@ define(['require',
                 this.ui.clearGlobalSearch.removeClass("in");
             };
             events['click ' + this.ui.menuHamburger] = function() {
+                this.setSearchBoxWidth({
+                    updateWidth: function(atlasHeaderWidth) {
+                        return $('body').hasClass('full-screen') ? atlasHeaderWidth - 350 : atlasHeaderWidth + 350
+                    }
+                });
                 $('body').toggleClass("full-screen");
             };
             return events;
 
         },
-        initialize: function(options) {},
-
+        initialize: function(options) {
+            this.bindEvent();
+        },
+        setSearchBoxWidth: function(options) {
+            var atlasHeaderWidth = this.$el.find(".atlas-header").width(),
+                minusWidth = Utils.getUrlState.isDetailPage() ? 400 : 250;
+            if (options && options.updateWidth) {
+                atlasHeaderWidth = options.updateWidth(atlasHeaderWidth);
+            }
+            if (atlasHeaderWidth > minusWidth) {
+                this.$el.find(".global-search-container").width(atlasHeaderWidth - minusWidth);
+            }
+        },
+        bindEvent: function() {
+            var that = this;
+            $(window).resize(function() {
+                that.setSearchBoxWidth()
+            });
+        },
         onRender: function() {
             var that = this;
             if (Globals.userLogedIn.status) {
@@ -75,23 +97,53 @@ define(['require',
             }
             this.initializeGlobalSearch();
         },
-        getSearchUrlQueryParam: function(request) {
-            var term = request.term;
-            return {
-                "excludeDeletedEntities": true,
-                "includeSubClassifications": true,
-                "includeSubTypes": true,
-                "includeClassificationAttributes": true,
-                "entityFilters": null,
-                "tagFilters": null,
-                "attributes": null,
-                "query": this.getSearchString(term),
-                "limit": 5,
-                "offset": 0,
-                "typeName": null,
-                "classification": null,
-                "termName": null
-            }
+        onShow: function() {
+            this.setSearchBoxWidth();
+        },
+        onBeforeDestroy: function() {
+            this.ui.globalSearch.atlasAutoComplete("destroy");
+        },
+        fetchSearchData: function(options) {
+            var that = this,
+                request = options.request,
+                response = options.response,
+                term = request.term,
+                sendResponse = function() {
+                    var query = that.cache[term].query,
+                        suggestions = that.cache[term].suggestions;
+                    if (query !== undefined && suggestions !== undefined) {
+                        response(that.cache[term]);
+                    }
+                };
+            $.ajax({
+                url: UrlLinks.searchApiUrl('quick'),
+                contentType: 'application/json',
+                data: {
+                    "query": this.getSearchString(term),
+                    "limit": 5,
+                    "offset": 0
+                },
+                cache: true,
+                success: function(data) {
+                    var data = data.searchResults.entities || [];
+                    that.cache[term] = _.extend({}, that.cache[term], { query: { category: "entities", data: data, order: 1 } });
+                    sendResponse();
+                }
+            });
+
+            $.ajax({
+                url: UrlLinks.searchApiUrl('suggestions'),
+                contentType: 'application/json',
+                data: {
+                    "prefixString": term
+                },
+                cache: true,
+                success: function(data) {
+                    var data = data.suggestions || [];
+                    that.cache[term] = _.extend({}, that.cache[term], { suggestions: { category: "suggestions", data: data, order: 2 } });
+                    sendResponse(data);
+                }
+            });
         },
         getSearchString: function(str) {
             if (str && str.length) {
@@ -103,86 +155,106 @@ define(['require',
         initializeGlobalSearch: function() {
             var that = this;
             this.cache = {};
-            this.ui.globalSearch.autocomplete({
+            this.ui.globalSearch.atlasAutoComplete({
                 minLength: 1,
                 autoFocus: false,
                 search: function() {
                     $(this).siblings('span.fa-search').removeClass("fa-search").addClass("fa-refresh fa-spin-custom");
                 },
                 focus: function(event, ui) {
-                    //$(this).val(ui.item.itemText);
                     return false;
                 },
                 open: function() {
                     $(this).siblings('span.fa-refresh').removeClass("fa-refresh fa-spin-custom").addClass("fa-search");
                 },
                 select: function(event, ui) {
-                    if (ui && ui.item && ui.item.value == "Empty") {
-                        return false
-                    } else {
+                    var item = ui && ui.item;
+                    event.preventDefault();
+                    event.stopPropagation();
+                    if (_.isString(item)) {
+                        var $el = $(this);
+                        $el.val(item);
+                        $el.data("valSelected", true);
+                        setTimeout(function() {
+                            $el.atlasAutoComplete("search");
+                        }, 10);
+                    } else if (_.isObject(item) && item.guid) {
                         Utils.setUrl({
-                            url: '#!/detailPage/' + ui.item.guid,
+                            url: '#!/detailPage/' + item.guid,
                             mergeBrowserUrl: false,
                             trigger: true
                         });
-                        return true
                     }
+                    return true;
                 },
                 source: function(request, response) {
                     var term = request.term;
-                    if (term in that.cache) {
+                    if (that.cache && that.cache[term]) {
                         response(that.cache[term]);
                         return;
                     }
-
-                    $.ajax({
-                        type: 'POST',
-                        url: UrlLinks.searchApiUrl('basic'),
-                        contentType: 'application/json',
-                        data: JSON.stringify(that.getSearchUrlQueryParam(request)),
-                        cache: true,
-                        success: function(data) {
-                            var data = data.entities;
-                            if (data === undefined) {
-                                data = ["Empty"];
-                            }
-                            that.cache[term] = data;
-                            response(data);
-                        }
+                    that.fetchSearchData({
+                        request: request,
+                        response: response
                     });
                 }
             }).focus(function() {
-                $(this).autocomplete("search");
+                $(this).atlasAutoComplete("search");
             }).keyup(function(event) {
                 if ($(this).val().trim() === "") {
                     that.ui.clearGlobalSearch.removeClass("in");
                 } else {
                     that.ui.clearGlobalSearch.addClass("in");
                     if (event.keyCode == 13) {
-                        Utils.setUrl({
-                            url: '#!/search/searchResult?query=' + encodeURIComponent(that.getSearchString($(this).val())) + '&searchType=basic',
-                            mergeBrowserUrl: false,
-                            trigger: true
-                        });
+                        if ($(this).data("valSelected") !== true) {
+                            Utils.setUrl({
+                                url: '#!/search/searchResult?query=' + encodeURIComponent(that.getSearchString($(this).val())) + '&searchType=basic',
+                                mergeBrowserUrl: false,
+                                trigger: true
+                            });
+                        } else {
+                            $(this).data("valSelected", false);
+                        }
                     }
                 }
-            }).autocomplete("instance")._renderItem = function(ul, item) {
-                if (item && item.value == "Empty") {
-                    return $("<li>")
-                        .append("<span class='empty'>No record found</span>")
-                        .appendTo(ul);
+            }).atlasAutoComplete("instance")._renderItem = function(ul, searchItem) {
+                if (searchItem) {
+                    var data = searchItem.data;
+                    if (data) {
+                        if (data.length == 0) {
+                            return $("<li class='empty'></li>")
+                                .append("<span class='empty-message'>No " + searchItem.category + " found</span>")
+                                .appendTo(ul);
+                        } else {
+                            var items = [];
+                            _.each(data, function(item) {
+                                var li = null;
+                                if (_.isObject(item)) {
+                                    item.itemText = Utils.getName(item) + " (" + item.typeName + ")";
+                                    var options = {},
+                                        table = '';
+                                    options.entityData = item;
+                                    var img = $('<img src="' + Utils.getEntityIconPath(options) + '">').on('error', function(error, s) {
+                                        this.src = Utils.getEntityIconPath(_.extend(options, { errorUrl: this.src }));
+                                    });
+                                    var span = $("<span>" + item.itemText + "</span>")
+                                        .prepend(img);
+                                    li = $("<li class='with-icon'>")
+                                        .append(span);
+                                    li.data("ui-autocomplete-item", item);
+                                } else {
+                                    li = $("<li>")
+                                        .append("<span>" + item + "</span>")
+                                    li.data("ui-autocomplete-item", item);
+                                }
+                                if (searchItem.category) {
+                                    items.push(li.attr("aria-label", searchItem.category + " : " + (_.isObject(item) ? item.itemText : item)));
+                                }
+                            });
+                            return ul.append(items);
+                        }
+                    }
                 }
-                item.itemText = Utils.getName(item) + " (" + item.typeName + ")";
-                var options = {},
-                    table = '';
-                options.entityData = item;
-                var img = $('<img src="' + Utils.getEntityIconPath(options) + '">').on('error', function(error, s) {
-                    this.src = Utils.getEntityIconPath(_.extend(options, { errorUrl: this.src }));
-                });
-                var link = $("<a class='search-entity-anchor ellipsis' href='#!/detailPage/" + item.guid + "'>" + item.itemText + "</a>").prepend(img);
-                return $("<li class='with-icon'>")
-                    .append(link)
-                    .appendTo(ul);
             };
         }
     });