You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@asterixdb.apache.org by mh...@apache.org on 2015/09/17 23:10:18 UTC

incubator-asterixdb git commit: ASTERIXDB-1083: Fixed non-query statements' plans display on WebUI

Repository: incubator-asterixdb
Updated Branches:
  refs/heads/master 928bbd152 -> a7627425c


ASTERIXDB-1083: Fixed non-query statements' plans display on WebUI

Change-Id: I52dd69062b2aaf89798ebb8e0e58a1941ac4119e
Reviewed-on: https://asterix-gerrit.ics.uci.edu/390
Reviewed-by: Chris Hillery <ce...@lambda.nu>
Tested-by: Jenkins <je...@fulliautomatix.ics.uci.edu>


Project: http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/commit/a7627425
Tree: http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/tree/a7627425
Diff: http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/diff/a7627425

Branch: refs/heads/master
Commit: a7627425c2cc26aab567c06260cce79df0ab1f0a
Parents: 928bbd1
Author: Murtadha Hubail <mh...@uci.edu>
Authored: Thu Sep 17 01:34:10 2015 -0700
Committer: Murtadha Hubail <hu...@gmail.com>
Committed: Thu Sep 17 14:07:40 2015 -0700

----------------------------------------------------------------------
 .../apache/asterix/api/common/APIFramework.java |   4 +-
 .../asterix/api/http/servlet/APIServlet.java    |  14 +-
 .../asterix/aql/translator/AqlTranslator.java   |   3 +
 .../src/main/resources/webui/querytemplate.html | 136 ++++++++++++-------
 4 files changed, 101 insertions(+), 56 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/blob/a7627425/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
----------------------------------------------------------------------
diff --git a/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java b/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
index 0031e0d..91d8358 100644
--- a/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
+++ b/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
@@ -23,8 +23,6 @@ import java.rmi.RemoteException;
 import java.util.ArrayList;
 import java.util.List;
 
-import org.json.JSONException;
-
 import org.apache.asterix.api.common.Job.SubmissionMode;
 import org.apache.asterix.aql.base.Statement.Kind;
 import org.apache.asterix.aql.expression.FunctionDecl;
@@ -78,12 +76,14 @@ import org.apache.hyracks.algebricks.core.rewriter.base.PhysicalOptimizationConf
 import org.apache.hyracks.api.client.IHyracksClientConnection;
 import org.apache.hyracks.api.job.JobId;
 import org.apache.hyracks.api.job.JobSpecification;
+import org.json.JSONException;
 
 /**
  * Provides helper methods for compilation of a query into a JobSpec and submission
  * to Hyracks through the Hyracks client interface.
  */
 public class APIFramework {
+    public static final String HTML_STATEMENT_SEPARATOR = "<!-- BEGIN -->";
 
     private static List<Pair<AbstractRuleController, List<IAlgebraicRewriteRule>>> buildDefaultLogicalRewrites() {
         List<Pair<AbstractRuleController, List<IAlgebraicRewriteRule>>> defaultLogicalRewrites = new ArrayList<Pair<AbstractRuleController, List<IAlgebraicRewriteRule>>>();

http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/blob/a7627425/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/APIServlet.java
----------------------------------------------------------------------
diff --git a/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/APIServlet.java b/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/APIServlet.java
index 39a5644..35d4c37 100644
--- a/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/APIServlet.java
+++ b/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/APIServlet.java
@@ -34,6 +34,7 @@ import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.asterix.api.common.APIFramework;
 import org.apache.asterix.api.common.SessionConfig;
 import org.apache.asterix.api.common.SessionConfig.OutputFormat;
 import org.apache.asterix.aql.base.Statement;
@@ -63,15 +64,12 @@ public class APIServlet extends HttpServlet {
         String output = request.getParameter("output-format");
         if (output.equals("ADM")) {
             format = OutputFormat.ADM;
-        }
-        else if (output.equals("CSV")) {
+        } else if (output.equals("CSV")) {
             format = OutputFormat.CSV;
-        }
-        else if (output.equals("CSV-Header")) {
+        } else if (output.equals("CSV-Header")) {
             format = OutputFormat.CSV;
             csv_and_header = true;
-        }
-        else {
+        } else {
             // Default output format
             format = OutputFormat.JSON;
         }
@@ -106,8 +104,7 @@ public class APIServlet extends HttpServlet {
             sessionConfig.set(SessionConfig.FORMAT_HTML, true);
             sessionConfig.set(SessionConfig.FORMAT_CSV_HEADER, csv_and_header);
             sessionConfig.setOOBData(isSet(printExprParam), isSet(printRewrittenExprParam),
-                                     isSet(printLogicalPlanParam), isSet(printOptimizedLogicalPlanParam),
-                                     isSet(printJob));
+                    isSet(printLogicalPlanParam), isSet(printOptimizedLogicalPlanParam), isSet(printJob));
             MetadataManager.INSTANCE.init();
             AqlTranslator aqlTranslator = new AqlTranslator(aqlStatements, sessionConfig);
             double duration = 0;
@@ -115,6 +112,7 @@ public class APIServlet extends HttpServlet {
             aqlTranslator.compileAndExecute(hcc, hds, AqlTranslator.ResultDelivery.SYNC);
             long endTime = System.currentTimeMillis();
             duration = (endTime - startTime) / 1000.00;
+            out.println(APIFramework.HTML_STATEMENT_SEPARATOR);
             out.println("<PRE>Duration of all jobs: " + duration + " sec</PRE>");
         } catch (ParseException | TokenMgrError | org.apache.asterix.aqlplus.parser.TokenMgrError pe) {
             GlobalConfig.ASTERIX_LOGGER.log(Level.INFO, pe.toString(), pe);

http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/blob/a7627425/asterix-app/src/main/java/org/apache/asterix/aql/translator/AqlTranslator.java
----------------------------------------------------------------------
diff --git a/asterix-app/src/main/java/org/apache/asterix/aql/translator/AqlTranslator.java b/asterix-app/src/main/java/org/apache/asterix/aql/translator/AqlTranslator.java
index a684cac..dcfbc98 100644
--- a/asterix-app/src/main/java/org/apache/asterix/aql/translator/AqlTranslator.java
+++ b/asterix-app/src/main/java/org/apache/asterix/aql/translator/AqlTranslator.java
@@ -259,6 +259,9 @@ public class AqlTranslator extends AbstractAqlTranslator {
         Map<String, String> config = new HashMap<String, String>();
 
         for (Statement stmt : aqlStatements) {
+            if (sessionConfig.is(SessionConfig.FORMAT_HTML)) {
+                sessionConfig.out().println(APIFramework.HTML_STATEMENT_SEPARATOR);
+            }
             validateOperation(activeDefaultDataverse, stmt);
             AqlMetadataProvider metadataProvider = new AqlMetadataProvider(activeDefaultDataverse,
                     CentralFeedManager.getInstance());

http://git-wip-us.apache.org/repos/asf/incubator-asterixdb/blob/a7627425/asterix-app/src/main/resources/webui/querytemplate.html
----------------------------------------------------------------------
diff --git a/asterix-app/src/main/resources/webui/querytemplate.html b/asterix-app/src/main/resources/webui/querytemplate.html
index ee49122..9088251 100644
--- a/asterix-app/src/main/resources/webui/querytemplate.html
+++ b/asterix-app/src/main/resources/webui/querytemplate.html
@@ -16,13 +16,41 @@
  ! specific language governing permissions and limitations
  ! under the License.
  !-->
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta name="description" content="ASTERIX WEB PAGE" />
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<link
+	href='http://fonts.googleapis.com/css?family=Bitter|PT+Sans+Caption|Open+Sans'
+	rel='stylesheet' type='text/css'>
+<script src="/webui/static/js/jquery.min.js"></script>
+
+<link href="/webui/static/css/bootstrap.min.css" rel="stylesheet"
+	type="text/css" />
+<link href="/webui/static/css/bootstrap-responsive.min.css"
+	rel="stylesheet" type="text/css" />
+
+<script src="/webui/static/js/bootstrap.min.js"></script>
+
+<link href="/webui/static/css/style.css" rel="stylesheet"
+	type="text/css" />
+
+<script type="text/javascript">
+$(document).ready(function() {
+
+    var optionButtonSize = $('#checkboxes-on').width();
+    $('#clear-query-button, #run-btn').width(optionButtonSize);
+
+    $('#checkboxes-on').click(function() {
+        /* Displays a checkmark to indicate selection/clearing */
         if ($('#opts').is(":visible")) {
             $('#opts').hide();
             $('#queryform :input').prop('checked', false);
         } else {
-            $('#opts').show();    
+            $('#opts').show();
             $('#queryform :input').prop('checked', true);
-        }    
+        }
         return false;
     });
 
@@ -31,7 +59,7 @@
         return false;
     });
 
-    $('form#queryform :input').click( function () {
+    $('form#queryform :input').click(function() {
         /* Hides selection check on uncheck, shows when all 5 selected */
         if ($(this).val()) {
             if ($(this).is(':checked') && $('input[type=checkbox]').filter(':checked').length == 5) {
@@ -45,74 +73,90 @@
     $("form#queryform").submit(function() {
         $('#output-message').html("");
         $.post("/", $("form#queryform").serialize(), function(data) {
-
-            var resSet = 0;
-            var resPattern = /<h4>Results:<\/h4>/g;
-            var durPattern = /<PRE>Duration/g;
+            var durPattern = '<PRE>Duration';
             var errorPattern = /<div class="accordion" id="errorblock">/g;
-            var resultCount = data.match(resPattern);
-
-            if (!resPattern.test(data)) {
-                if(errorPattern.test(data)) {
-                  $('#output-heading').html('Error');
-                  $('#output-heading').addClass('error');
-                } else {
-                  $('#output-heading').html('Output');
-                  $('#output-heading').removeClass('error');
-                }
-                $('#output-message').html(data);
+            var sectionsSeparator = '<h4>';
+            var resultPat = 'Results:</h4>';
+            
+            if (errorPattern.test(data)) {
+                $('#output-heading').html('Error');
+                $('#output-heading').addClass('error');
             } else {
                 $('#output-heading').html('Output');
                 $('#output-heading').removeClass('error');
-                if (resultCount.length <= 1) {
-                    $('#output-message').html(data);
-                } else {
-                    var splitData = data.split('<PRE>Duration');
-                    var results = splitData[0].split('<h4>');
-                    var components = results.slice(1, results.length);
-                    var sections = components.length / resultCount.length;
+            }
+            var executedStatements = data.split('<!-- BEGIN -->');
+            var executedStatementsWithResultsCount = 0;
+            for (var i = 0; i < executedStatements.length; i++) {
+                if (executedStatements[i].toString().trim().length > 0) {
+                    /* check how many statements have returned data*/
+                    executedStatementsWithResultsCount++;
+                }
+            }
 
-                    for (resSet = 0; resSet < resultCount.length; resSet++) {
+            /* only a single statement returned results and/or duration message*/
+            if (executedStatementsWithResultsCount <= 2) {
+                /* print statement results and duration*/
+                $('#output-message').html(data);
+            } else {
+                var resultsCount = 1;
+                /* need to create collapse button and div per statement*/
+                for (var i = 0; i < executedStatements.length; i++) {
+                    /* last statement is always the duration message*/
+                    if (i == (executedStatements.length - 1)) {
+                        /* print duration message*/
+                        $('#output-message').append(executedStatements[i]);
+                        break;
+                    }
 
-                        $('#output-message').append('<h4>' + components[(resSet+1)*sections - 1]);
+                    if (executedStatements[i].toString().trim().length > 0) {
+                        var sections = executedStatements[i].toString().split(sectionsSeparator);
+                        /* remove the first section since it is always empty due to splitng on sectionsSeparator */
+                        sections.splice(0, 1);
 
-                        if (sections > 1) {
-                            var resNum = resSet + 1;
+                        /* if there is a results section, we need to put it before the collapsible section*/
+                        for (var j = 0; j < sections.length; j++) {
+                            /* print results section and remove it*/
+                            if (sections[j].indexOf(resultPat) >= 0) {
+                                var resultsSection = sections.splice(j, 1);
+                                $('#output-message').append(sectionsSeparator + resultsSection.toString());
+                            }
+                        }
+
+                        if (sections.length > 0) {
+                            /* generate the collapsible section for this statement*/
                             $('<button/>')
                                 .attr("class", "btn")
                                 .attr("data-toggle", "collapse")
-                                .attr("data-target", "#collapse" + resSet)
+                                .attr("data-target", "#collapse" + i)
                                 .css("margin-bottom", "1em")
-                                .html('Result Plan #' + resNum + '<i id="ibtn' + resSet + '" class="icon-plus extarget"></i>')
+                                .html('Result Plan #' + resultsCount + '<i id="ibtn' + resultsCount + '" class="icon-plus extarget"></i>')
                                 .appendTo('#output-message');
 
                             $('<div/>')
-                                .attr("id", "collapse" + resSet)
+                                .attr("id", "collapse" + i)
                                 .attr("class", "collapse in")
                                 .appendTo('#output-message');
 
-                            for (var c = 0; c < sections - 1; c++) {
-                                var pos = resSet*sections + c;
-                                $('#collapse' + resSet).append('<h4>' + components[pos]);
+                            /* put the rest of the sections in the collapsible section*/
+                            for (var k = 0; k < sections.length; k++) {
+                                $('#collapse' + i).append(sectionsSeparator + sections[k].toString());
                             }
-
-                            /* Placeholder for future on show/hide result plan behavior
-                            $('#collapse' + resSet).on('show', function() {
-                            }).on('hide', function() {
-                            });
-                            */
-
-                            $('#output-message').append("<hr/>");
                         }
+                        $('#output-message').append("<hr/>");
+                        resultsCount++;
 
+                        /* Placeholder for future on show/hide result plan behavior
+                        $('#collapse' + resSet).on('show', function() {
+                        }).on('hide', function() {
+                        });
+                        */
                     }
-                    $('#output-message').append('<PRE>Duration' + splitData[1]);
                 }
-
             }
 
             var contentString = data.toString();
-            if (contentString.indexOf("<PRE>Duration") !== -1) {
+            if (contentString.indexOf(durPattern) != -1) {
                 $('<div/>')
                     .addClass("alert alert-success")
                     .html("Success: Query Complete")