Pārlūkot izejas kodu

HUE-2560 [metastore] Added table stats to Metastore

Modified refresh algo
Removed sortable from metastore
Enrico Berti 10 gadi atpakaļ
vecāks
revīzija
fefaaf4

+ 55 - 9
apps/beeswax/src/beeswax/static/beeswax/js/stats.utils.js

@@ -18,13 +18,17 @@ function showStats(options) {
   if (options.isTable) {
     $("#columnAnalysis").hide();
     $("#tableAnalysis .stats-refresh").data("startUrl", options.statsUrl);
+    $("#tableAnalysis .stats-refresh").data("refreshUrl", options.refreshUrl);
     $("#tableAnalysis .stats-refresh").data("tableName", options.tableName);
+    $("#tableAnalysis .stats-refresh").data("isTable", true);
     $("#tableAnalysis .popover-content").css("opacity", ".5");
   }
   else {
     $("#tableAnalysis").hide();
     $("#columnAnalysis .stats-refresh").data("startUrl", options.statsUrl);
+    $("#columnAnalysis .stats-refresh").data("refreshUrl", options.refreshUrl);
     $("#columnAnalysis .stats-refresh").data("columnName", options.columnName);
+    $("#columnAnalysis .stats-refresh").data("isTable", false);
     $("#columnAnalysis .popover-content").css("opacity", ".5");
   }
   $.ajax({
@@ -68,14 +72,14 @@ function showStats(options) {
         }
       }
       else {
-        $(document).trigger("error", "${ _('There was a problem loading the table stats.') }");
+        $(document).trigger("error", options.errorLabel);
         $("#tableAnalysis").hide();
         $("#columnAnalysis").hide();
       }
     },
     error: function (e) {
       if (e.status == 500) {
-        $(document).trigger("error", "${ _('There was a problem loading the table stats.') }");
+        $(document).trigger("error", options.errorLabel);
         $("#tableAnalysis").hide();
         $("#columnAnalysis").hide();
       }
@@ -83,30 +87,72 @@ function showStats(options) {
   });
 }
 
-function showTableStats(statsUrl, tableName, callback) {
+function showTableStats(statsUrl, refreshUrl, tableName, errorLabel, callback) {
   showStats({
     isTable: true,
     statsUrl: statsUrl,
+    refreshUrl: refreshUrl,
     tableName: tableName,
+    errorLabel: errorLabel,
     callback: callback
   });
 }
 
-function showColumnStats(statsUrl, columnName, callback) {
+function showColumnStats(statsUrl, refreshUrl, columnName, errorLabel, callback) {
   showStats({
     isTable: false,
     statsUrl: statsUrl,
+    refreshUrl: refreshUrl,
     columnName: columnName,
+    errorLabel: errorLabel,
     callback: callback
   });
 }
 
-$(document).ready(function () {
-  $("#tableAnalysis .stats-refresh").on("click", function () {
-    showTableStats($("#tableAnalysis .stats-refresh").data("startUrl"), $("#tableAnalysis .stats-refresh").data("tableName"));
+function refreshLoop(el){
+  $.get($(el).data("watchUrl"), function (data){
+    if (data && data.status != -1 && !data.isSuccess && !data.isFailure){
+      $(el).data("refreshTimeout", window.setTimeout(function(){
+        refreshLoop($(el));
+      }, 2000))
+    }
+    else {
+      $(el).removeClass("fa-spin");
+      $(el).parents(".popover").find(".popover-content").css("opacity", "1");
+      if (data.isSuccess){
+        if (el.data("isTable")){
+          showTableStats($(el).data("startUrl"), $(el).data("tableName"));
+        }
+        else {
+          showColumnStats($(el).data("startUrl"), $(el).data("columnName"));
+        }
+      }
+      else {
+        $(document).trigger("error", data.message);
+      }
+    }
   });
+}
 
-  $("#columnAnalysis .stats-refresh").on("click", function () {
-    showColumnStats($("#columnAnalysis .stats-refresh").data("startUrl"), $("#columnAnalysis .stats-refresh").data("columnName"));
+$(document).ready(function () {
+  $(".stats-refresh").on("click", function () {
+    var _this = $(this);
+    _this.parents(".popover").find(".popover-content").css("opacity", ".5");
+    if (_this.hasClass("fa-spin")){
+      _this.removeClass("fa-spin");
+      _this.parents(".popover").find(".popover-content").css("opacity", "1");
+      window.clearTimeout(_this.data("refreshTimeout"));
+    }
+    else {
+      _this.addClass("fa-spin");
+      $.post(_this.data("refreshUrl"), function(data){
+        if (data && data.status == 0 && data.watch_url != ""){
+          _this.data("watchUrl", data.watch_url);
+          _this.data("refreshTimeout", window.setTimeout(function(){
+            refreshLoop(_this);
+          }, 2000))
+        }
+      });
+    }
   });
 });

+ 6 - 2
apps/beeswax/src/beeswax/templates/execute.mako

@@ -1018,6 +1018,8 @@ var HIVE_AUTOCOMPLETE_FAILS_QUIETLY_ON = [500]; // error codes from beeswax/view
 var HIVE_AUTOCOMPLETE_USER = "${ user }";
 var HIVE_AUTOCOMPLETE_APP = "${app_name}";
 
+var STATS_PROBLEMS = "${ _('There was a problem loading the stats.') }";
+
 var HIVE_AUTOCOMPLETE_GLOBAL_CALLBACK = function (data) {
   if (data != null && data.error && typeof resetNavigator != "undefined") {
     resetNavigator();
@@ -1260,9 +1262,10 @@ $(document).ready(function () {
                     _column.find("a.col-stats").on("click", function () {
                       var _link = $(this);
                       var statsUrl = "/${ app_name }/api/table/" + viewModel.database() + "/" + _table.data("table") + "/stats/" + col.name;
+                      var refreshUrl = "/${ app_name }/api/analyze/" + viewModel.database() + "/" + _table.data("table") + "/" + col.name;
                       $("#columnAnalysis .popover-content").html("<i class='fa fa-spinner fa-spin'></i>");
                       $("#columnAnalysis").show().css("top", _link.position().top - $("#columnAnalysis").outerHeight() / 2 + _link.outerHeight() / 2).css("left", _link.position().left + _link.outerWidth());
-                      showColumnStats(statsUrl, col.name, function () {
+                      showColumnStats(statsUrl, refreshUrl, col.name, STATS_PROBLEMS, function () {
                         $("#columnAnalysis").show().css("top", _link.position().top - $("#columnAnalysis").outerHeight() / 2 + _link.outerHeight() / 2).css("left", _link.position().left + _link.outerWidth());
                       });
                     });
@@ -1306,9 +1309,10 @@ $(document).ready(function () {
             _table.find("a.table-stats").on("click", function () {
               var _link = $(this);
               var statsUrl = "/${ app_name }/api/table/" + viewModel.database() + "/" + _table.data("table") + "/stats/";
+              var refreshUrl = "/${ app_name }/api/analyze/" + viewModel.database() + "/" + _table.data("table") + "/";
               $("#tableAnalysis .popover-content").html("<i class='fa fa-spinner fa-spin'></i>");
               $("#tableAnalysis").show().css("top", _link.position().top - $("#tableAnalysis").outerHeight()/2 + _link.outerHeight()/2).css("left", _link.position().left + _link.outerWidth());
-              showTableStats(statsUrl, _table.data("table"), function(){
+              showTableStats(statsUrl, refreshUrl, _table.data("table"), STATS_PROBLEMS, function(){
                 $("#tableAnalysis").show().css("top", _link.position().top - $("#tableAnalysis").outerHeight()/2 + _link.outerHeight()/2).css("left", _link.position().left + _link.outerWidth());
               });
             });

+ 56 - 34
apps/metastore/src/metastore/templates/describe_table.mako

@@ -36,18 +36,18 @@ ${ components.menubar() }
 <link rel="stylesheet" href="${ static('metastore/css/metastore.css') }" type="text/css">
 
 
-<%def name="column_table(cols, withStats=False)">
-  <table class="table table-striped table-condensed sampleTable
+<%def name="column_table(cols, id, withStats=False)">
+  <table id="${id}" class="table table-striped table-condensed sampleTable
   %if withStats:
   skip-extender
   %endif
   ">
     <thead>
       <tr>
+        <th width="1%">&nbsp;</th>
         %if withStats:
-          <th width="10">&nbsp;</th>
+          <th width="1%" class="no-sort">&nbsp;</th>
         %endif
-        <th width="10">&nbsp;</th>
         <th>${_('Name')}</th>
         <th>${_('Type')}</th>
         <th>${_('Comment')}</th>
@@ -56,10 +56,10 @@ ${ components.menubar() }
     <tbody>
       % for column in cols:
         <tr>
+          <td>${ loop.index }</td>
           %if withStats:
             <td class="row-selector-exclude"><a href="javascript:void(0)" data-column="${ column.name }"><i class="fa fa-bar-chart" title="${ _('View statistics') }"></i></a></td>
           %endif
-          <td>${ loop.index }</td>
           <td title="${ _("Scroll to the column") }">
             <a href="javascript:void(0)" data-row-selector="true" class="column-selector">${ column.name }</a>
           </td>
@@ -113,12 +113,12 @@ ${ components.menubar() }
 
             <div class="tab-content">
               <div class="tab-pane" id="columns">
-                ${ column_table(table.cols, True) }
+                ${ column_table(table.cols, "columnTable", True) }
               </div>
 
               % if table.partition_keys:
               <div class="tab-pane" id="partitionColumns">
-                ${ column_table(table.partition_keys) }
+                ${ column_table(table.partition_keys, "partitionTable") }
               </div>
               % endif
 
@@ -156,6 +156,13 @@ ${ components.menubar() }
                   % endfor
                   </tbody>
                 </table>
+                <div id="jumpToColumnAlert" class="alert hide" style="margin-top: 12px;">
+                  <button type="button" class="close" data-dismiss="alert">&times;</button>
+                  <strong>${_('Did you know?')}</strong>
+                  <ul>
+                    <li>${ _('If the sample contains a large number of columns, click a row to select a column to jump to') }</li>
+                  </ul>
+                </div>
               % endif
               </div>
               % endif
@@ -228,6 +235,8 @@ ${ components.menubar() }
 <script src="${ static('beeswax/js/stats.utils.js') }"></script>
 
 <script type="text/javascript" charset="utf-8">
+  var STATS_PROBLEMS = "${ _('There was a problem loading the stats.') }";
+
   $(document).ready(function () {
 
     $(".column-selector").on("click", function () {
@@ -248,31 +257,47 @@ ${ components.menubar() }
     % endif
 
     $('a[data-toggle="tab"]').on('shown', function () {
-      $(".sampleTable").not('.initialized').addClass('initialized').dataTable({
-        "bPaginate": false,
-        "bLengthChange": false,
-        "bInfo": false,
-        "bFilter": false,
-        "fnInitComplete": function () {
-          $(this).parent().jHueTableScroller();
-          if ($(this).hasClass("skip-extender")){
-            $(this).jHueTableExtender({
-              includeNavigator: false,
-              fixedHeader: true
-            });
-          }
-          else {
-            $(this).jHueTableExtender({
-              hintElement: "#jumpToColumnAlert",
-              fixedHeader: true
+      var sortables = [];
+      $(".sampleTable").not('.initialized').each(function () {
+        var _id = $(this).attr("id");
+        if (sortables[_id] === undefined) {
+          sortables[_id] = [];
+        }
+        $('#' + _id + ' thead th').each(function () {
+          if ($(this).hasClass('no-sort')) {
+            sortables[_id].push({
+              "bSortable": false
             });
+          } else {
+            sortables[_id].push(null);
           }
-        },
-        "oLanguage": {
-          "sEmptyTable": "${_('No data available')}",
-          "sZeroRecords": "${_('No matching records')}"
-        }
+        });
       });
+
+      for (var id in sortables) {
+        $("#" + id).addClass("initialized");
+        $("#" + id).dataTable({
+          "aoColumns": sortables[id],
+          "bPaginate": false,
+          "bLengthChange": false,
+          "bInfo": false,
+          "bFilter": false,
+          "bAutoWidth": false,
+          "fnInitComplete": function () {
+            $(this).parent().jHueTableScroller();
+            if (! $(this).hasClass("skip-extender")) {
+              $(this).jHueTableExtender({
+                hintElement: "#jumpToColumnAlert",
+                fixedHeader: true
+              });
+            }
+          },
+          "oLanguage": {
+            "sEmptyTable": "${_('No data available')}",
+            "sZeroRecords": "${_('No matching records')}"
+          }
+        });
+      }
     });
 
     $("#import-data-btn").click(function () {
@@ -292,9 +317,10 @@ ${ components.menubar() }
       var _link = $(this);
       var _col = _link.data("column");
       var statsUrl = "/beeswax/api/table/${database}/${table.name}/stats/" + _col;
+      var refreshUrl = "/beeswax/api/analyze/${database}/${table.name}/stats/" + _col;
       $("#columnAnalysis .popover-content").html("<i class='fa fa-spinner fa-spin'></i>");
       $("#columnAnalysis").show().css("top", _link.position().top - $("#columnAnalysis").outerHeight() / 2 + _link.outerHeight() / 2).css("left", _link.position().left + _link.outerWidth());
-      showColumnStats(statsUrl, _col, function () {
+      showColumnStats(statsUrl, refreshUrl, _col, STATS_PROBLEMS, function () {
         $("#columnAnalysis").show().css("top", _link.position().top - $("#columnAnalysis").outerHeight() / 2 + _link.outerHeight() / 2).css("left", _link.position().left + _link.outerWidth());
       });
     });
@@ -303,10 +329,6 @@ ${ components.menubar() }
       $("#columnAnalysis").hide();
     });
 
-    $("#columnAnalysis .stats-refresh").on("click", function () {
-      showColumnStats($("#columnAnalysis .stats-refresh").data("startUrl"), $("#columnAnalysis .stats-refresh").data("columnName"));
-    });
-
   });
 </script>
 

+ 58 - 24
apps/metastore/src/metastore/templates/tables.mako

@@ -63,36 +63,51 @@ ${ components.menubar() }
             % endif
           </%def>
         </%actionbar:render>
-
+        <div style="padding-left: 10px; padding-right: 10px">
           <table class="table table-condensed datatables" data-tablescroller-disable="true">
-          <thead>
-            <tr>
-              <th width="1%"><div class="hueCheckbox selectAll fa" data-selectables="tableCheck"></div></th>
-              <th>${_('Table Name')}</th>
-            </tr>
-          </thead>
-          <tbody>
-          % for table in tables:
-            <tr>
-              <td data-row-selector-exclude="true" width="1%">
-                <div class="hueCheckbox tableCheck fa"
-                     data-view-url="${ url('metastore:describe_table', database=database, table=table) }"
-                     data-browse-url="${ url('metastore:read_table', database=database, table=table) }"
-                     data-drop-name="${ table }"
-                     data-row-selector-exclude="true"></div>
-              </td>
-              <td>
-                <a href="${ url('metastore:describe_table', database=database, table=table) }" data-row-selector="true">${ table }</a>
-              </td>
-            </tr>
-          % endfor
-          </tbody>
-        </table>
+            <thead>
+              <tr>
+                <th width="1%"><div class="hueCheckbox selectAll fa" data-selectables="tableCheck"></div></th>
+                <th>&nbsp;</th>
+                <th>${_('Table Name')}</th>
+              </tr>
+            </thead>
+            <tbody>
+            % for table in tables:
+              <tr>
+                <td data-row-selector-exclude="true" width="1%">
+                  <div class="hueCheckbox tableCheck fa"
+                       data-view-url="${ url('metastore:describe_table', database=database, table=table) }"
+                       data-browse-url="${ url('metastore:read_table', database=database, table=table) }"
+                       data-drop-name="${ table }"
+                       data-row-selector-exclude="true"></div>
+                </td>
+                <td class="row-selector-exclude"><a href="javascript:void(0)" data-table="${ table }"><i class="fa fa-bar-chart" title="${ _('View statistics') }"></i></a></td>
+                <td>
+                  <a href="${ url('metastore:describe_table', database=database, table=table) }" data-row-selector="true">${ table }</a>
+                </td>
+              </tr>
+            % endfor
+            </tbody>
+          </table>
+        </div>
       </div>
     </div>
   </div>
 </div>
 
+<div id="tableAnalysis" class="popover mega-popover right">
+  <div class="arrow"></div>
+  <h3 class="popover-title" style="text-align: left">
+    <a class="pull-right pointer close-popover" style="margin-left: 8px"><i class="fa fa-times"></i></a>
+    <a class="pull-right pointer stats-refresh" style="margin-left: 8px"><i class="fa fa-refresh"></i></a>
+    <span class="pull-right stats-warning muted" rel="tooltip" data-placement="top" title="${ _('The column stats for this table are not accurate') }" style="margin-left: 8px"><i class="fa fa-exclamation-triangle"></i></span>
+    <strong class="table-name"></strong> ${ _(' table analysis') }
+  </h3>
+  <div class="popover-content" style="text-align: left"></div>
+</div>
+
+
 <div id="dropTable" class="modal hide fade">
   <form id="dropTableForm" action="${ url('metastore:drop_table', database=database) }" method="POST">
     ${ csrf_token(request) | n,unicode }
@@ -113,8 +128,11 @@ ${ components.menubar() }
 <link rel="stylesheet" href="${ static('metastore/css/metastore.css') }" type="text/css">
 
 <script src="${ static('desktop/ext/js/knockout-min.js') }" type="text/javascript" charset="utf-8"></script>
+<script src="${ static('beeswax/js/stats.utils.js') }"></script>
 
 <script type="text/javascript" charset="utf-8">
+  var STATS_PROBLEMS = "${ _('There was a problem loading the stats.') }";
+
   $(document).ready(function () {
     var viewModel = {
       availableTables: ko.observableArray(${ tables_json | n }),
@@ -130,6 +148,7 @@ ${ components.menubar() }
       "bInfo": false,
       "bFilter": true,
       "aoColumns": [
+        {"bSortable": false, "sWidth": "1%" },
         {"bSortable": false, "sWidth": "1%" },
         null
       ],
@@ -145,6 +164,21 @@ ${ components.menubar() }
 
     $("a[data-row-selector='true']").jHueRowSelector();
 
+    $("a[data-table]").on("click", function () {
+      var _link = $(this);
+      var statsUrl = "/beeswax/api/table/${database}/" + _link.data("table") + "/stats/";
+      var refreshUrl = "/beeswax/api/analyze/${database}/" + _link.data("table") + "/";
+      $("#tableAnalysis .popover-content").html("<i class='fa fa-spinner fa-spin'></i>");
+      $("#tableAnalysis").show().css("top", _link.position().top - $("#tableAnalysis").outerHeight()/2 + _link.outerHeight()/2).css("left", _link.position().left + _link.outerWidth());
+      showTableStats(statsUrl, refreshUrl, _link.data("table"), STATS_PROBLEMS, function(){
+        $("#tableAnalysis").show().css("top", _link.position().top - $("#tableAnalysis").outerHeight()/2 + _link.outerHeight()/2).css("left", _link.position().left + _link.outerWidth());
+      });
+    });
+
+    $(document).on("click", "#tableAnalysis .close-popover", function () {
+      $("#tableAnalysis").hide();
+    });
+
     $("#id_database").chosen({
       disable_search_threshold: 5,
       width: "100%",