Ian Maxon has submitted this change and it was merged.

Change subject: [UI] Allow logical plan to be viewed as JSON / formatted JSON
......................................................................


[UI] Allow logical plan to be viewed as JSON / formatted JSON

- user model changes: no
- storage format changes: no
- interface changes: enhancements to the web interface

details:
Added drop-down menu for printing logical plan and optimized
logical plan in string,json, and clean-json.

Change-Id: I4dd62e355048a5b8a02e074049fe41e73e74e357
Reviewed-on: https://asterix-gerrit.ics.uci.edu/1885
Integration-Tests: Jenkins <[email protected]>
Tested-by: Jenkins <[email protected]>
Contrib: Jenkins <[email protected]>
Reviewed-by: Till Westmann <[email protected]>
Reviewed-by: Ian Maxon <[email protected]>
---
M 
asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SessionConfig.java
M asterixdb/asterix-app/pom.xml
M 
asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
M 
asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ApiServlet.java
M 
asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java
M 
asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/RestApiServlet.java
M 
asterixdb/asterix-app/src/main/java/org/apache/asterix/api/java/AsterixJavaClient.java
M 
asterixdb/asterix-app/src/main/java/org/apache/asterix/drivers/AsterixClientDriver.java
M asterixdb/asterix-app/src/main/resources/webui/querytemplate.html
M asterixdb/asterix-app/src/main/resources/webui/static/css/style.css
A 
asterixdb/asterix-app/src/test/java/org/apache/asterix/test/jsonplan/JsonLogicalPlanTest.java
A 
asterixdb/asterix-app/src/test/java/org/apache/asterix/test/jsonplan/JsonOptimizedLogicalPlanTest.java
M 
hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/AbstractLogicalOperator.java
A 
hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/AbstractLogicalOperatorPrettyPrintVisitor.java
M 
hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitor.java
A 
hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitorJson.java
M 
hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/PlanPrettyPrinter.java
M 
hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/visitors/ILogicalOperatorVisitor.java
18 files changed, 1,259 insertions(+), 217 deletions(-)

Approvals:
  Anon. E. Moose #1000171: 
  Till Westmann: Looks good to me, approved
  Jenkins: Verified; ; Verified
  Ian Maxon: Looks good to me, approved

Objections:
  Jenkins: Violations found



diff --git 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SessionConfig.java
 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SessionConfig.java
index cfd4e87..6e67612 100644
--- 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SessionConfig.java
+++ 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SessionConfig.java
@@ -21,6 +21,9 @@
 import java.io.Serializable;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
 
 /**
  * SessionConfig captures several different parameters for controlling
@@ -36,7 +39,6 @@
  * <li>It allows you to specify output format-specific parameters.
  */
 public class SessionConfig implements Serializable {
-
     private static final long serialVersionUID = 1L;
 
     /**
@@ -47,6 +49,29 @@
         CSV,
         CLEAN_JSON,
         LOSSLESS_JSON
+    };
+
+    /**
+     * Used to specify the format for logical plan and optimized logical plan.
+     */
+
+    public enum PlanFormat {
+        JSON,
+        STRING;
+        public static PlanFormat get(String fmtString, String label, 
PlanFormat defaultFmt, Logger logger) {
+            try {
+                if (fmtString != null) {
+                    String format =
+                            ("JSON".equalsIgnoreCase(fmtString) || 
"CLEAN_JSON".equalsIgnoreCase(fmtString))
+                                    ? "JSON"
+                                    : fmtString;
+                    return PlanFormat.valueOf(format);
+                }
+            } catch (IllegalArgumentException e) {
+                logger.log(Level.INFO, fmtString + ": unsupported " + label + 
", using " + defaultFmt + "instead", e);
+            }
+            return defaultFmt;
+        }
     };
 
     /**
@@ -106,6 +131,7 @@
 
     // Output format.
     private final OutputFormat fmt;
+    private final PlanFormat lpfmt;
 
     // Standard execution flags.
     private final boolean executeQuery;
@@ -116,13 +142,18 @@
     private final Map<String, Boolean> flags;
 
     public SessionConfig(OutputFormat fmt) {
-        this(fmt, true, true, true);
+        this(fmt, PlanFormat.STRING);
+    }
+
+    public SessionConfig(OutputFormat fmt, PlanFormat lpfmt) {
+        this(fmt, true, true, true, lpfmt);
     }
 
     /**
      * Create a SessionConfig object with all optional values set to defaults:
      * - All format flags set to "false".
      * - All out-of-band outputs set to "false".
+     *
      * @param fmt
      *            Output format for execution output.
      * @param optimize
@@ -131,13 +162,20 @@
      *            Whether to execute the query or not.
      * @param generateJobSpec
      *            Whether to generate the Hyracks job specification (if
+     * @param lpfmt
+     *            Plan format for logical plan.
      */
     public SessionConfig(OutputFormat fmt, boolean optimize, boolean 
executeQuery, boolean generateJobSpec) {
+        this(fmt, optimize, executeQuery, generateJobSpec, PlanFormat.STRING);
+    }
+    public SessionConfig(OutputFormat fmt, boolean optimize, boolean 
executeQuery, boolean generateJobSpec,
+            PlanFormat lpfmt) {
         this.fmt = fmt;
         this.optimize = optimize;
         this.executeQuery = executeQuery;
         this.generateJobSpec = generateJobSpec;
         this.flags = new HashMap<>();
+        this.lpfmt = lpfmt;
     }
 
     /**
@@ -148,6 +186,13 @@
     }
 
     /**
+     * Retrieve the PlanFormat for this execution.
+     */
+    public PlanFormat getLpfmt() {
+        return this.lpfmt;
+    }
+
+    /**
      * Retrieve the value of the "execute query" flag.
      */
     public boolean isExecuteQuery() {
diff --git a/asterixdb/asterix-app/pom.xml b/asterixdb/asterix-app/pom.xml
index ee733d9..b039071 100644
--- a/asterixdb/asterix-app/pom.xml
+++ b/asterixdb/asterix-app/pom.xml
@@ -279,6 +279,10 @@
   </profiles>
   <dependencies>
     <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-databind</artifactId>
+    </dependency>
+    <dependency>
       <groupId>org.apache.hyracks</groupId>
       <artifactId>hyracks-control-cc</artifactId>
     </dependency>
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
index 567d587..8290446 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
@@ -84,8 +84,10 @@
 import 
org.apache.hyracks.algebricks.core.algebra.expressions.IExpressionTypeComputer;
 import 
org.apache.hyracks.algebricks.core.algebra.expressions.IMergeAggregationExpressionFactory;
 import 
org.apache.hyracks.algebricks.core.algebra.expressions.IMissableTypeComputer;
+import 
org.apache.hyracks.algebricks.core.algebra.prettyprint.AbstractLogicalOperatorPrettyPrintVisitor;
 import 
org.apache.hyracks.algebricks.core.algebra.prettyprint.AlgebricksAppendable;
 import 
org.apache.hyracks.algebricks.core.algebra.prettyprint.LogicalOperatorPrettyPrintVisitor;
+import 
org.apache.hyracks.algebricks.core.algebra.prettyprint.LogicalOperatorPrettyPrintVisitorJson;
 import 
org.apache.hyracks.algebricks.core.algebra.prettyprint.PlanPrettyPrinter;
 import 
org.apache.hyracks.algebricks.core.rewriter.base.AlgebricksOptimizationContext;
 import 
org.apache.hyracks.algebricks.core.rewriter.base.IOptimizationContextFactory;
@@ -111,6 +113,8 @@
     private static final int MIN_FRAME_LIMIT_FOR_SORT = 3;
     private static final int MIN_FRAME_LIMIT_FOR_GROUP_BY = 4;
     private static final int MIN_FRAME_LIMIT_FOR_JOIN = 5;
+    private static final String LPLAN = "Logical plan";
+    private static final String OPLAN = "Optimized logical plan";
 
     // A white list of supported configurable parameters.
     private static final Set<String> CONFIGURABLE_PARAMETER_NAMES =
@@ -156,7 +160,13 @@
     private void printPlanPrefix(SessionOutput output, String planName) {
         if (output.config().is(SessionConfig.FORMAT_HTML)) {
             output.out().println("<h4>" + planName + ":</h4>");
-            output.out().println("<pre>");
+            if (LPLAN.equalsIgnoreCase(planName)) {
+                output.out().println("<pre class = query-plan>");
+            } else if (OPLAN.equalsIgnoreCase(planName)) {
+                output.out().println("<pre class = query-optimized-plan>");
+            } else {
+                output.out().println("<pre>");
+            }
         } else {
             output.out().println("----------" + planName + ":");
         }
@@ -219,7 +229,13 @@
 
             printPlanPrefix(output, "Logical plan");
             if (rwQ != null || (statement != null && statement.getKind() == 
Statement.Kind.LOAD)) {
-                LogicalOperatorPrettyPrintVisitor pvisitor = new 
LogicalOperatorPrettyPrintVisitor(output.out());
+                AbstractLogicalOperatorPrettyPrintVisitor pvisitor;
+                if 
(output.config().getLpfmt().equals(SessionConfig.PlanFormat.JSON)) {
+                    pvisitor = new 
LogicalOperatorPrettyPrintVisitorJson(output.out());
+                } else {
+                    pvisitor = new 
LogicalOperatorPrettyPrintVisitor(output.out());
+
+                }
                 PlanPrettyPrinter.printPlan(plan, pvisitor, 0);
             }
             printPlanPostfix(output);
@@ -273,8 +289,13 @@
                 } else {
                     printPlanPrefix(output, "Optimized logical plan");
                     if (rwQ != null || (statement != null && 
statement.getKind() == Statement.Kind.LOAD)) {
-                        LogicalOperatorPrettyPrintVisitor pvisitor =
-                                new 
LogicalOperatorPrettyPrintVisitor(output.out());
+                        AbstractLogicalOperatorPrettyPrintVisitor pvisitor;
+                        if 
(output.config().getLpfmt().equals(SessionConfig.PlanFormat.JSON)) {
+                            pvisitor = new 
LogicalOperatorPrettyPrintVisitorJson(output.out());
+
+                        } else {
+                            pvisitor = new 
LogicalOperatorPrettyPrintVisitor(output.out());
+                        }
                         PlanPrettyPrinter.printPlan(plan, pvisitor, 0);
                     }
                     printPlanPostfix(output);
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ApiServlet.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ApiServlet.java
index fbe5852..a29d869 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ApiServlet.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ApiServlet.java
@@ -52,6 +52,7 @@
 import org.apache.asterix.translator.IStatementExecutorFactory;
 import org.apache.asterix.translator.SessionConfig;
 import org.apache.asterix.translator.SessionConfig.OutputFormat;
+import org.apache.asterix.translator.SessionConfig.PlanFormat;
 import org.apache.asterix.translator.SessionOutput;
 import org.apache.hyracks.api.client.IHyracksClientConnection;
 import org.apache.hyracks.api.dataset.IHyracksDataset;
@@ -67,7 +68,6 @@
 import io.netty.handler.codec.http.HttpResponseStatus;
 
 public class ApiServlet extends AbstractServlet {
-
     private static final Logger LOGGER = 
Logger.getLogger(ApiServlet.class.getName());
     public static final String HTML_STATEMENT_SEPARATOR = "<!-- BEGIN -->";
 
@@ -88,18 +88,20 @@
         this.componentProvider = componentProvider;
     }
 
-    @Override
-    protected void post(IServletRequest request, IServletResponse response) {
+    @Override protected void post(IServletRequest request, IServletResponse 
response) {
         // Query language
-        ILangCompilationProvider compilationProvider = 
"AQL".equals(request.getParameter("query-language"))
-                ? aqlCompilationProvider : sqlppCompilationProvider;
+        ILangCompilationProvider compilationProvider = 
"AQL".equals(request.getParameter("query-language")) ?
+                aqlCompilationProvider :
+                sqlppCompilationProvider;
         IParserFactory parserFactory = compilationProvider.getParserFactory();
 
         // Output format.
         PrintWriter out = response.writer();
         OutputFormat format;
+
         boolean csvAndHeader = false;
         String output = request.getParameter("output-format");
+
         if ("CSV-Header".equals(output)) {
             output = "CSV";
             csvAndHeader = true;
@@ -112,6 +114,8 @@
             // Default output format
             format = OutputFormat.CLEAN_JSON;
         }
+        PlanFormat planFormat =
+                PlanFormat.get(request.getParameter("plan-format"), "plan 
format", PlanFormat.STRING, LOGGER);
 
         String query = request.getParameter("query");
         String wrapperArray = request.getParameter("wrapper-array");
@@ -144,12 +148,14 @@
             }
             IParser parser = parserFactory.createParser(query);
             List<Statement> aqlStatements = parser.parse();
-            SessionConfig sessionConfig = new SessionConfig(format, true, 
isSet(executeQuery), true);
+            SessionConfig sessionConfig =
+                    new SessionConfig(format, true, isSet(executeQuery), true, 
planFormat);
             sessionConfig.set(SessionConfig.FORMAT_HTML, true);
             sessionConfig.set(SessionConfig.FORMAT_CSV_HEADER, csvAndHeader);
             sessionConfig.set(SessionConfig.FORMAT_WRAPPER_ARRAY, 
isSet(wrapperArray));
-            sessionConfig.setOOBData(isSet(printExprParam), 
isSet(printRewrittenExprParam),
-                    isSet(printLogicalPlanParam), 
isSet(printOptimizedLogicalPlanParam), isSet(printJob));
+            sessionConfig
+                    .setOOBData(isSet(printExprParam), 
isSet(printRewrittenExprParam), isSet(printLogicalPlanParam),
+                            isSet(printOptimizedLogicalPlanParam), 
isSet(printJob));
             SessionOutput sessionOutput = new SessionOutput(sessionConfig, 
out);
             MetadataManager.INSTANCE.init();
             IStatementExecutor translator = 
statementExectorFactory.create(appCtx, aqlStatements, sessionOutput,
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java
index 6a92a26..f8f5c18 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java
@@ -114,7 +114,8 @@
         CLIENT_ID("client_context_id"),
         PRETTY("pretty"),
         MODE("mode"),
-        TIMEOUT("timeout");
+        TIMEOUT("timeout"),
+        PLAN_FORMAT("plan-format");
 
         private final String str;
 
@@ -236,6 +237,7 @@
         SessionOutput.ResultAppender appendStatus = 
ResultUtil.createResultStatusAppender();
 
         SessionConfig.OutputFormat format = getFormat(param.format);
+        //TODO:get the parameters from UI.Currently set to clean_json.
         SessionConfig sessionConfig = new SessionConfig(format);
         sessionConfig.set(SessionConfig.FORMAT_WRAPPER_ARRAY, true);
         sessionConfig.set(SessionConfig.FORMAT_INDENT_JSON, param.pretty);
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/RestApiServlet.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/RestApiServlet.java
index 3d00d88..117d7fb 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/RestApiServlet.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/RestApiServlet.java
@@ -46,6 +46,7 @@
 import org.apache.asterix.translator.IStatementExecutorFactory;
 import org.apache.asterix.translator.SessionConfig;
 import org.apache.asterix.translator.SessionConfig.OutputFormat;
+import org.apache.asterix.translator.SessionConfig.PlanFormat;
 import org.apache.asterix.translator.SessionOutput;
 import org.apache.hyracks.api.client.IHyracksClientConnection;
 import org.apache.hyracks.api.dataset.IHyracksDataset;
@@ -107,6 +108,8 @@
                 format = OutputFormat.CSV;
             }
         }
+        PlanFormat planFormat =
+                PlanFormat.get(request.getParameter("plan-format"), "plan 
format", PlanFormat.STRING, LOGGER);
 
         // If it's JSON, check for the "lossless" flag
 
@@ -117,7 +120,7 @@
 
         SessionOutput.ResultAppender appendHandle = (app, handle) -> 
app.append("{ \"").append("handle")
                 .append("\":" + " \"").append(handle).append("\" }");
-        SessionConfig sessionConfig = new SessionConfig(format);
+        SessionConfig sessionConfig = new SessionConfig(format, planFormat);
 
         // If it's JSON or ADM, check for the "wrapper-array" flag. Default is
         // "true" for JSON and "false" for ADM. (Not applicable for CSV.)
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/java/AsterixJavaClient.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/java/AsterixJavaClient.java
index 675bb9b..58a7f09 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/java/AsterixJavaClient.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/java/AsterixJavaClient.java
@@ -37,6 +37,7 @@
 import org.apache.asterix.translator.IStatementExecutorFactory;
 import org.apache.asterix.translator.SessionConfig;
 import org.apache.asterix.translator.SessionConfig.OutputFormat;
+import org.apache.asterix.translator.SessionConfig.PlanFormat;
 import org.apache.asterix.translator.SessionOutput;
 import org.apache.hyracks.api.client.IHyracksClientConnection;
 import org.apache.hyracks.api.job.JobSpecification;
@@ -80,11 +81,19 @@
     }
 
     public void compile() throws Exception {
-        compile(true, false, false, false, false, false, false);
+        compile(true, false, true, false, false, false, false);
     }
 
     public void compile(boolean optimize, boolean printRewrittenExpressions, 
boolean printLogicalPlan,
             boolean printOptimizedPlan, boolean printPhysicalOpsOnly, boolean 
generateBinaryRuntime, boolean printJob)
+            throws Exception {
+        compile(optimize, printRewrittenExpressions, printLogicalPlan, 
printOptimizedPlan, printPhysicalOpsOnly,
+                generateBinaryRuntime, printJob, PlanFormat.STRING);
+    }
+
+    public void compile(boolean optimize, boolean printRewrittenExpressions, 
boolean printLogicalPlan,
+            boolean printOptimizedPlan, boolean printPhysicalOpsOnly, boolean 
generateBinaryRuntime, boolean printJob,
+            PlanFormat pformat)
             throws Exception {
         queryJobSpec = null;
         dmlJobs = null;
@@ -101,7 +110,7 @@
         List<Statement> statements = parser.parse();
         MetadataManager.INSTANCE.init();
 
-        SessionConfig conf = new SessionConfig(OutputFormat.ADM, optimize, 
true, generateBinaryRuntime);
+        SessionConfig conf = new SessionConfig(OutputFormat.ADM, optimize, 
true, generateBinaryRuntime, pformat);
         conf.setOOBData(false, printRewrittenExpressions, printLogicalPlan, 
printOptimizedPlan, printJob);
         if (printPhysicalOpsOnly) {
             conf.set(SessionConfig.FORMAT_ONLY_PHYSICAL_OPS, true);
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/drivers/AsterixClientDriver.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/drivers/AsterixClientDriver.java
index ba44833..14a5fe0 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/drivers/AsterixClientDriver.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/drivers/AsterixClientDriver.java
@@ -53,8 +53,8 @@
         }
         boolean exec = new Boolean(acc.execute);
         IHyracksClientConnection hcc = exec ? new 
HyracksConnection("localhost", acc.hyracksPort) : null;
-        AsterixJavaClient q = compileQuery(hcc, acc.getArguments().get(0), new 
Boolean(acc.optimize),
-                new Boolean(acc.onlyPhysical), exec || new 
Boolean(acc.hyracksJob));
+        AsterixJavaClient q = compileQuery(hcc, acc.getArguments().get(0), new 
Boolean(acc.optimize), false,
+                exec || new Boolean(acc.hyracksJob));
         if (exec) {
             q.execute();
         }
@@ -64,8 +64,9 @@
             boolean onlyPhysical, boolean createBinaryRuntime) throws 
Exception {
         ILangCompilationProvider compilationProvider = new 
AqlCompilationProvider();
         FileReader reader = new FileReader(filename);
-        AsterixJavaClient q = new AsterixJavaClient(null, hcc, reader, 
compilationProvider,
-                new DefaultStatementExecutorFactory(), new 
StorageComponentProvider());
+        AsterixJavaClient q =
+                new AsterixJavaClient(null, hcc, reader, compilationProvider, 
new DefaultStatementExecutorFactory(),
+                        new StorageComponentProvider());
         q.compile(optimize, true, true, true, onlyPhysical, 
createBinaryRuntime, createBinaryRuntime);
         return q;
     }
diff --git a/asterixdb/asterix-app/src/main/resources/webui/querytemplate.html 
b/asterixdb/asterix-app/src/main/resources/webui/querytemplate.html
index 383754e..1157c27 100644
--- a/asterixdb/asterix-app/src/main/resources/webui/querytemplate.html
+++ b/asterixdb/asterix-app/src/main/resources/webui/querytemplate.html
@@ -19,24 +19,24 @@
 <!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>
+  <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" />
+  <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>
+  <script src="/webui/static/js/bootstrap.min.js"></script>
 
-<link href="/webui/static/css/style.css" rel="stylesheet"
-       type="text/css" />
-<script src="/webui/static/js/jquery.json-viewer.js"></script>
-<link href="/webui/static/css/jquery.json-viewer.css" type="text/css" 
rel="stylesheet" />
+  <link href="/webui/static/css/style.css" rel="stylesheet"
+        type="text/css" />
+  <script src="/webui/static/js/jquery.json-viewer.js"></script>
+  <link href="/webui/static/css/jquery.json-viewer.css" type="text/css" 
rel="stylesheet" />
 
   <script type="text/javascript">
 $(document).ready(function() {
@@ -169,13 +169,13 @@
                 }
             }
 
-            /* Handling Pretty JSON */
+            /* Handling Pretty JSON-query result */
             var resultFormat = $('#output-format option:checked').text();
             if ( resultFormat == 'JSON (formatted)') {
               $('.result-content').each(
                   function(idx) {
                     var results = $(this).text().split('\n');
-                    $(this).css('padding-left', '20px');
+                     $(this).css('padding-left', '20px');
                     $(this).text('');
                     for (var iter1 = 0; iter1 < results.length - 1; iter1++) {
                       if (results[iter1].length < 1) {
@@ -183,10 +183,36 @@
                       }
                       var resultJSON = $.parseJSON(results[iter1]);
                       $(this).append($('<div/>').attr("id", 
"json-record"+idx+"-"+iter1));
-                      $('#json-record'+idx+"-"+iter1).jsonViewer(resultJSON, 
{collapsed: true, level: 1});
+                      $('#json-record'+idx+"-"+iter1).jsonViewer(resultJSON, 
{collapsed: true, level: 10});
                     }
                   }
                 );
+            }
+
+
+            /* Handling Pretty JSON-logical plan */
+            var planFormat = $('#plan-format option:checked').text();
+            $('.query-plan').addClass("outer");
+            $('.query-optimized-plan').addClass("outer");
+            if ( planFormat == 'JSON (formatted)') {
+              $('.query-plan').each(
+                  function() {
+                   var planSt = $(this).text();
+                    $(this).text('');
+                    var planJSON = $.parseJSON(planSt);
+                    $(this).append($('<div/>').attr("id", "json-queryPlan"));
+                    $('#json-queryPlan').jsonViewer(planJSON, {collapsed: 
false, level: 10});
+                  }
+              );
+              $('.query-optimized-plan').each(
+                  function() {
+                   var opPlanSt = $(this).text();
+                    $(this).text('');
+                    var opPlanJSON = $.parseJSON(opPlanSt);
+                    $(this).append($('<div/>').attr("id", 
"json-queryOptimizedPlan").attr("class","inner"));
+                    $('#json-queryOptimizedPlan').jsonViewer(opPlanJSON, 
{collapsed: false, level: 10});
+                  }
+              );
             }
 
 
@@ -203,106 +229,129 @@
 });
 </script>
 
-<meta charset=utf-8 />
-<title>AsterixDB Web Interface</title>
+  <meta charset=utf-8 />
+  <title>AsterixDB Web Interface</title>
 </head>
 
 <body>
-  <div class="navbar navbar-fixed-top">
-    <div class="navbar-inner">
-      <div class="container">
-        <a class="btn btn-navbar" data-toggle="collapse" 
data-target=".nav-collapse">
-          <span class="icon-bar"></span>
-          <span class="icon-bar"></span>
-          <span class="icon-bar"></span>
-        </a>
+<div class="navbar navbar-fixed-top">
+  <div class="navbar-inner">
+    <div class="container">
+      <a class="btn btn-navbar" data-toggle="collapse" 
data-target=".nav-collapse">
+        <span class="icon-bar"></span>
+        <span class="icon-bar"></span>
+        <span class="icon-bar"></span>
+      </a>
 
-        <!-- Temporary logo placeholder -->
-        <a class="brand" href="#"><img 
src="/webui/static/img/finalasterixlogo.png"></a>
+      <!-- Temporary logo placeholder -->
+      <a class="brand" href="#"><img 
src="/webui/static/img/finalasterixlogo.png"></a>
 
-        <div class="nav-collapse collapse">
-          <ul class="nav">
-            <li><a href="https://asterixdb.apache.org/"; target="_blank">
-                    Open source<img class="extarget" 
src="/webui/static/img/targetlink.png"/></a></li>
-            <li><a href="https://issues.apache.org/jira/browse/ASTERIXDB"; 
target="_blank">
-                    File issues<img class="extarget" 
src="/webui/static/img/targetlink.png"/></a></li>
-            <li><a href="https://ci.apache.org/projects/asterixdb/index.html"; 
target="_blank">
-                    Documentation<img class="extarget" 
src="/webui/static/img/targetlink.png"/></a></li>
-            <li><a href="https://asterixdb.apache.org/community.html"; 
target="_blank">
-                    Contact<img class="extarget" 
src="/webui/static/img/targetlink.png"/></a></li>
-          </ul>
-        </div><!--/.nav-collapse -->
-      </div>
+      <div class="nav-collapse collapse">
+        <ul class="nav">
+          <li><a href="https://asterixdb.apache.org/"; target="_blank">
+            Open source<img class="extarget" 
src="/webui/static/img/targetlink.png"/></a></li>
+          <li><a href="https://issues.apache.org/jira/browse/ASTERIXDB"; 
target="_blank">
+            File issues<img class="extarget" 
src="/webui/static/img/targetlink.png"/></a></li>
+          <li><a href="https://ci.apache.org/projects/asterixdb/index.html"; 
target="_blank">
+            Documentation<img class="extarget" 
src="/webui/static/img/targetlink.png"/></a></li>
+          <li><a href="https://asterixdb.apache.org/community.html"; 
target="_blank">
+            Contact<img class="extarget" 
src="/webui/static/img/targetlink.png"/></a></li>
+        </ul>
+      </div><!--/.nav-collapse -->
     </div>
   </div>
+</div>
 
-  <div class="content">
+<div class="content">
     <div class="container">
-      <div class="row-fluid">
+        <div class="row-fluid">
 
-        <div class="span6">
+            <div class="span6">
 
-          <form id="queryform" class="form-horizontal" method="post">
-            <div style="margin-bottom: 1em;">
-              <label class="query">Query</label>
-              <textarea rows="10" id="qry" name="query" class="query" 
value="%s" placeholder="Type your query ..."></textarea>
+                <form id="queryform" class="form-horizontal" method="post">
+                    <div style="margin-bottom: 1em;">
+                        <label class="query">Query</label>
+                        <textarea rows="10" id="qry" name="query" 
class="query" value="%s"
+                                  placeholder="Type your query ..."></textarea>
+                    </div>
+
+                    <div class="btn-group">
+                        <button id="checkboxes-on" class="btn">
+                            <i id="opts" class="icon-ok" 
style="display:none;"></i>Select Options
+                        </button>
+                        <button id="clear-query-button" class="btn">Clear 
Query</button>
+                        <!-- <button id="checkboxes-off" class="btn">Clear All 
Options</button> -->
+                        <button type="submit" id="run-btn" class="btn 
btn-custom-darken">Run</button>
+                    </div>
+
+                    <div>
+                        <label id="query-language" class="optlabel"> Query 
Language:<br/>
+                            <select name="query-language" class="btn 
btn-width">
+                                <option selected value="SQLPP">SQL++</option>
+                                <option value="AQL">AQL</option>
+                            </select>
+                        </label>
+                        <label id="output-format" class="optlabel"> Output 
Format:<br/>
+                            <select name="output-format" class="btn btn-width">
+                                <option selected 
value="CLEAN_JSON">JSON</option>
+                                <option value="CLEAN_JSON">JSON 
(formatted)</option>
+                                <option value="ADM">ADM</option>
+                                <option value="CSV">CSV (no header)</option>
+                                <option value="CSV-Header">CSV (with 
header)</option>
+                                <option value="LOSSLESS_JSON">JSON 
(lossless)</option>
+                            </select>
+                        </label>
+                        <label id="plan-format" class="optlabel"> Plan 
Format:<br/>
+                            <select name="plan-format"  class="btn btn-width">
+                                <option selected value="JSON">JSON</option>
+                                <option value="CLEAN_JSON">JSON 
(formatted)</option>
+                                <option value="STRING">String</option>
+                            </select>
+                        </label>
+                        <label class="optlabel"><input type="checkbox" 
name="wrapper-array" value="true"/> Wrap results
+                            in outer array</label>
+                        <label class="checkbox optlabel"><input 
type="checkbox" name="print-expr-tree" value="true"/>
+                            Print parsed expressions</label>
+                        <label class="checkbox optlabel"><input 
type="checkbox" name="print-rewritten-expr-tree"
+                                                                value="true"/> 
Print rewritten expressions</label>
+                        <div><label class="checkbox optlabel"><input 
type="checkbox"
+                                                                               
          name="print-logical-plan"
+                                                                               
          value="true"/> Print logical
+                            plan</label></div>
+
+                     <div><label class="checkbox optlabel"><input 
type="checkbox"
+                                                                               
          name="print-optimized-logical-plan"
+                                                                               
          value="true"/> Print optimized
+                            logical plan</label></div>
+
+                        <label class="checkbox optlabel"><input 
type="checkbox" name="print-job" value="true"/> Print
+                            Hyracks job</label>
+                        <label class="optlabel"><input type="checkbox" 
name="execute-query" value="true" checked/>
+                            Execute query</label>
+                    </div>
+                </form>
             </div>
 
-            <div class="btn-group">
-              <button id="checkboxes-on" class="btn">
-                  <i id="opts" class="icon-ok" 
style="display:none;"></i>Select Options</button>
-              <button id="clear-query-button" class="btn">Clear Query</button>
-              <!-- <button id="checkboxes-off" class="btn">Clear All 
Options</button> -->
-              <button type="submit" id="run-btn" class="btn 
btn-custom-darken">Run</button>
+            <div class="span6">
+                <div class="output">
+                    <label id="output-heading" class="heading">Output</label>
+                    <div id="output-message" class="message">
+                    </div>
+                </div>
             </div>
-
-            <div>
-              <label id="query-language" class="optlabel"> Query Language:<br/>
-                <select name="query-language" class="btn">
-                  <option selected value="SQLPP">SQL++</option>
-                  <option value="AQL">AQL</option>
-                </select>
-              </label>
-              <label id="output-format" class="optlabel"> Output Format:<br/>
-                <select name="output-format" class="btn">
-                  <option selected value="CLEAN_JSON">JSON</option>
-                  <option value="CLEAN_JSON">JSON (formatted)</option>
-                  <option value="ADM">ADM</option>
-                  <option value="CSV">CSV (no header)</option>
-                  <option value="CSV-Header">CSV (with header)</option>
-                  <option value="LOSSLESS_JSON">JSON (lossless)</option>
-                </select>
-              </label>
-              <label class="optlabel"><input type="checkbox" 
name="wrapper-array" value="true" /> Wrap results in outer array</label>
-              <label class="checkbox optlabel"><input type="checkbox" 
name="print-expr-tree" value="true" /> Print parsed expressions</label>
-              <label class="checkbox optlabel"><input type="checkbox" 
name="print-rewritten-expr-tree" value="true" /> Print rewritten 
expressions</label>
-              <label class="checkbox optlabel"><input type="checkbox" 
name="print-logical-plan" value="true" /> Print logical plan</label>
-              <label class="checkbox optlabel"><input type="checkbox" 
name="print-optimized-logical-plan" value="true" /> Print optimized logical 
plan</label>
-              <label class="checkbox optlabel"><input type="checkbox" 
name="print-job" value="true" /> Print Hyracks job</label>
-              <label class="optlabel"><input type="checkbox" 
name="execute-query" value="true" checked/> Execute query</label>
-            </div>
-          </form>
-       </div>
-
-       <div class="span6">
-         <div class="output">
-           <label id="output-heading" class="heading">Output</label>
-           <div id="output-message" class="message">
-           </div>
-         </div>
-       </div>
-
-      </div>
+        </div>
     </div>
 </div>
-  <div class="footer">
-    <section class="line"><hr></section>
-    <section class="content">
-      <section class="left">
-      </section>
-      <section class="right">
-      </section>
+<div class="footer">
+    <section class="line">
+        <hr>
     </section>
-  </div>
+    <section class="content">
+        <section class="left">
+        </section>
+        <section class="right">
+        </section>
+    </section>
+</div>
 </body>
 </html>
diff --git 
a/asterixdb/asterix-app/src/main/resources/webui/static/css/style.css 
b/asterixdb/asterix-app/src/main/resources/webui/static/css/style.css
index b9b733c..1982e3a 100644
--- a/asterixdb/asterix-app/src/main/resources/webui/static/css/style.css
+++ b/asterixdb/asterix-app/src/main/resources/webui/static/css/style.css
@@ -230,3 +230,33 @@
 .span6 {
     padding: 24px;
 }
+
+.inline-half {
+    display: inline-block;
+    width: 50%;
+    float:left;
+}
+.inline-btn-width{
+    display: inline-block;
+    width: 58%;
+}
+
+.btn-width{
+    width: 30%;
+}
+.inner{
+    float : none;
+}
+
+pre.outer {
+    padding-left :20px;
+    overflow-x : scroll !important;
+    overflow-y : scroll !important;
+    word-wrap : normal !important;
+    word-break : normal !important;
+}
+
+.wrapper {
+    overflow: hidden;
+}
+
diff --git 
a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/jsonplan/JsonLogicalPlanTest.java
 
b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/jsonplan/JsonLogicalPlanTest.java
new file mode 100644
index 0000000..77d0130
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/jsonplan/JsonLogicalPlanTest.java
@@ -0,0 +1 @@
+/* * Licensed to the Apache Software Foundation (ASF) under one * or more 
contributor license agreements.  See the NOTICE file * distributed with this 
work for additional information * regarding copyright ownership.  The ASF 
licenses this file * to you under the Apache License, Version 2.0 (the * 
"License"); you may not use this file except in compliance * with the License.  
You may obtain a copy of the License at * *   
http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable 
law or agreed to in writing, * software distributed under the License is 
distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * 
KIND, either express or implied.  See the License for the * specific language 
governing permissions and limitations * under the License. */package 
org.apache.asterix.test.jsonplan;import java.io.BufferedReader;import 
java.io.File;import java.io.FileInputStream;import 
java.io.InputStreamReader;import java.io.PrintWriter;
 import java.io.Reader;import java.util.ArrayList;import 
java.util.Collection;import java.util.logging.Logger;import 
org.apache.asterix.api.common.AsterixHyracksIntegrationUtil;import 
org.apache.asterix.api.java.AsterixJavaClient;import 
org.apache.asterix.app.translator.DefaultStatementExecutorFactory;import 
org.apache.asterix.common.config.GlobalConfig;import 
org.apache.asterix.common.context.IStorageComponentProvider;import 
org.apache.asterix.common.dataflow.ICcApplicationContext;import 
org.apache.asterix.common.exceptions.AsterixException;import 
org.apache.asterix.compiler.provider.AqlCompilationProvider;import 
org.apache.asterix.compiler.provider.ILangCompilationProvider;import 
org.apache.asterix.compiler.provider.SqlppCompilationProvider;import 
org.apache.asterix.external.util.ExternalDataConstants;import 
org.apache.asterix.external.util.IdentitiyResolverFactory;import 
org.apache.asterix.file.StorageComponentProvider;import 
org.apache.asterix.test.base.Asterix
 TestHelper;import org.apache.asterix.test.common.TestHelper;import 
org.apache.asterix.test.runtime.HDFSCluster;import 
org.apache.asterix.translator.IStatementExecutorFactory;import 
org.apache.asterix.translator.SessionConfig.PlanFormat;import 
org.apache.hyracks.api.client.IHyracksClientConnection;import 
org.junit.AfterClass;import org.junit.Assume;import 
org.junit.BeforeClass;import org.junit.Test;import 
org.junit.internal.AssumptionViolatedException;import 
org.junit.runner.RunWith;import org.junit.runners.Parameterized;import 
org.junit.runners.Parameterized.Parameters;import 
com.fasterxml.jackson.core.JsonParser;import 
com.fasterxml.jackson.databind.ObjectMapper;@RunWith(Parameterized.class)public 
class JsonLogicalPlanTest {    private static final Logger LOGGER =            
Logger.getLogger(org.apache.asterix.test.jsonplan.JsonLogicalPlanTest.class.getName());
    protected static final String SEPARATOR = File.separator;    private static 
final String EXTEN
 SION_AQL = "aql";    private static final String EXTENSION_SQLPP = "sqlpp";    
private static final String EXTENSION_RESULT = "plan";    private static final 
String FILENAME_IGNORE = "ignore.txt";    private static final String 
FILENAME_ONLY = "only.txt";    private static final String PATH_BASE =          
  "src" + SEPARATOR + "test" + SEPARATOR + "resources" + SEPARATOR + 
"optimizerts" + SEPARATOR;    private static final String PATH_QUERIES = 
PATH_BASE + "queries" + SEPARATOR;    protected static String PATH_ACTUAL = 
"target" + File.separator + "jplantest" + SEPARATOR;    protected static 
boolean optimized = false;    private static final ArrayList<String> ignore = 
AsterixTestHelper.readTestListFile(FILENAME_IGNORE, PATH_BASE);    private 
static final ArrayList<String> only = 
AsterixTestHelper.readTestListFile(FILENAME_ONLY, PATH_BASE);    protected 
static String TEST_CONFIG_FILE_NAME = "asterix-build-configuration.xml";    
private static final ILangCompilationProvi
 der aqlCompilationProvider = new AqlCompilationProvider();    private static 
final ILangCompilationProvider sqlppCompilationProvider = new 
SqlppCompilationProvider();    protected static ILangCompilationProvider 
extensionLangCompilationProvider = null;    protected static 
IStatementExecutorFactory statementExecutorFactory = new 
DefaultStatementExecutorFactory();    protected static 
IStorageComponentProvider storageComponentProvider = new 
StorageComponentProvider();    protected static AsterixHyracksIntegrationUtil 
integrationUtil = new AsterixHyracksIntegrationUtil();    @BeforeClass    
public static void setUp() throws Exception {        
System.setProperty(GlobalConfig.CONFIG_FILE_PROPERTY, TEST_CONFIG_FILE_NAME);   
     final File outdir = new File(PATH_ACTUAL);        outdir.mkdirs();        
HDFSCluster.getInstance().setup();        integrationUtil.init(true);        // 
Set the node resolver to be the identity resolver that expects node names       
 // to be nod
 e controller ids; a valid assumption in test environment.        
System.setProperty(ExternalDataConstants.NODE_RESOLVER_FACTORY_PROPERTY,        
        IdentitiyResolverFactory.class.getName());    }    @AfterClass    
public static void tearDown() throws Exception {        File outdir = new 
File(PATH_ACTUAL);        File[] files = outdir.listFiles();        if (files 
== null || files.length == 0) {            outdir.delete();        }        
HDFSCluster.getInstance().cleanup();        integrationUtil.deinit(true);    }  
  private static void suiteBuildPerFile(File file, Collection<Object[]> 
testArgs, String path) {        if (file.isDirectory() && 
!file.getName().startsWith(".")) {            for (File innerfile : 
file.listFiles()) {                String subdir = innerfile.isDirectory() ? 
path + innerfile.getName() + SEPARATOR : path;                
suiteBuildPerFile(innerfile, testArgs, subdir);            }        }        if 
(file.isFile() && (file.getN
 ame().endsWith(EXTENSION_AQL) || file.getName().endsWith(EXTENSION_SQLPP))) {  
          String resultFileName = AsterixTestHelper.extToResExt(file.getName(), 
EXTENSION_RESULT);            File actualFile = new File(PATH_ACTUAL + 
SEPARATOR + path + resultFileName);            testArgs.add(new Object[] { 
file, actualFile });        }    }    @Parameters(name = "JsonLogicalPlanTest 
{index}: {0}")    public static Collection<Object[]> tests() {        
Collection<Object[]> testArgs = new ArrayList<>();        if (only.isEmpty()) { 
           suiteBuildPerFile(new File(PATH_QUERIES), testArgs, "");        } 
else {            for (String path : only) {                
suiteBuildPerFile(new File(PATH_QUERIES + path), testArgs,                      
  path.lastIndexOf(SEPARATOR) < 0 ? "" : path.substring(0, 
path.lastIndexOf(SEPARATOR) + 1));            }        }        return 
testArgs;    }    private final File actualFile;    private final File 
queryFile;    public J
 sonLogicalPlanTest(final File queryFile, final File actualFile) {        
this.queryFile = queryFile;        this.actualFile = actualFile;    }    @Test  
  public void test() throws Exception {        try {            String 
queryFileShort =                    
queryFile.getPath().substring(PATH_QUERIES.length()).replace(SEPARATOR.charAt(0),
 '/');            if (!only.isEmpty()) {                boolean toRun = 
TestHelper.isInPrefixList(only, queryFileShort);                if (!toRun) {   
                 LOGGER.info("SKIP TEST: \"" + queryFile.getPath()              
              + "\" \"only.txt\" not empty and not in \"only.txt\".");          
      }                Assume.assumeTrue(toRun);            }            
boolean skipped = TestHelper.isInPrefixList(ignore, queryFileShort);            
if (skipped) {                LOGGER.info("SKIP TEST: \"" + queryFile.getPath() 
+ "\" in \"ignore.txt\".");            }            
Assume.assumeTrue(!skipped);       
      LOGGER.info("RUN TEST: \"" + queryFile.getPath() + "\"");            
Reader query = new BufferedReader(new InputStreamReader(new 
FileInputStream(queryFile), "UTF-8"));            // Forces the creation of 
actualFile.            actualFile.getParentFile().mkdirs();            
PrintWriter plan = new PrintWriter(actualFile);            
ILangCompilationProvider provider =                    
queryFile.getName().endsWith("aql") ? aqlCompilationProvider : 
sqlppCompilationProvider;            if (extensionLangCompilationProvider != 
null) {                provider = extensionLangCompilationProvider;            
}            IHyracksClientConnection hcc = 
integrationUtil.getHyracksClientConnection();            AsterixJavaClient 
asterix =                    new AsterixJavaClient((ICcApplicationContext) 
integrationUtil.cc.getApplicationContext(), hcc,                            
query, plan, provider, statementExecutorFactory, storageComponentProvider);     
       try {    
             asterix.compile(true, false, !optimized, optimized, false, false, 
false, PlanFormat.JSON);            } catch (AsterixException e) {              
  plan.close();                query.close();                throw new 
Exception("Compile ERROR for " + queryFile + ": " + e.getMessage(), e);         
   }            plan.close();            query.close();            
BufferedReader readerActual =                    new BufferedReader(new 
InputStreamReader(new FileInputStream(actualFile), "UTF-8"));            String 
lineActual, objectActual = "";            boolean firstPlan = false;            
while ((lineActual = readerActual.readLine()) != null) {                if 
(lineActual.contains("--")) {                    if (firstPlan) {               
         break;                    }                    firstPlan = true;       
         } else {                    objectActual = objectActual + lineActual;  
              }            }            try {  
               final JsonParser parser = new 
ObjectMapper().getJsonFactory().createJsonParser(objectActual);                
while (parser.nextToken() != null) {                }            } finally {    
            readerActual.close();            }        } catch (Exception e) {   
         if (!(e instanceof AssumptionViolatedException)) {                
LOGGER.severe("Test \"" + queryFile.getPath() + "\" FAILED!");                
throw new Exception("Test \"" + queryFile.getPath() + "\" FAILED!", e);         
   } else {                throw e;            }        }    }}
\ No newline at end of file
diff --git 
a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/jsonplan/JsonOptimizedLogicalPlanTest.java
 
b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/jsonplan/JsonOptimizedLogicalPlanTest.java
new file mode 100644
index 0000000..b8e4595
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/jsonplan/JsonOptimizedLogicalPlanTest.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.test.jsonplan;
+
+import java.io.File;
+import java.util.logging.Logger;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class JsonOptimizedLogicalPlanTest extends JsonLogicalPlanTest {
+
+    private static final Logger LOGGER =
+            
Logger.getLogger(org.apache.asterix.test.jsonplan.JsonOptimizedLogicalPlanTest.class.getName());
+
+    public JsonOptimizedLogicalPlanTest(File queryFile, File actualFile) {
+        super(queryFile, actualFile);
+        optimized = true;
+        PATH_ACTUAL = "target" + File.separator + "joptplantest" + SEPARATOR;
+    }
+}
\ No newline at end of file
diff --git 
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/AbstractLogicalOperator.java
 
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/AbstractLogicalOperator.java
index 4686f32..8b38a2b 100644
--- 
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/AbstractLogicalOperator.java
+++ 
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/AbstractLogicalOperator.java
@@ -19,7 +19,7 @@
 package org.apache.hyracks.algebricks.core.algebra.operators.logical;
 
 import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -63,7 +63,7 @@
 
     private AbstractLogicalOperator.ExecutionMode mode = 
AbstractLogicalOperator.ExecutionMode.UNPARTITIONED;
     protected IPhysicalOperator physicalOperator;
-    private final Map<String, Object> annotations = new HashMap<>();
+    private final Map<String, Object> annotations = new 
IdentityHashMap<String, Object>();
     private boolean bJobGenEnabled = true;
 
     protected final List<Mutable<ILogicalOperator>> inputs;
diff --git 
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/AbstractLogicalOperatorPrettyPrintVisitor.java
 
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/AbstractLogicalOperatorPrettyPrintVisitor.java
new file mode 100644
index 0000000..140ba80
--- /dev/null
+++ 
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/AbstractLogicalOperatorPrettyPrintVisitor.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.hyracks.algebricks.core.algebra.prettyprint;
+
+import org.apache.commons.lang3.mutable.Mutable;
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalOperator;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
+import org.apache.hyracks.algebricks.core.algebra.base.IPhysicalOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractLogicalOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractOperatorWithNestedPlans;
+import 
org.apache.hyracks.algebricks.core.algebra.visitors.ILogicalExpressionVisitor;
+import 
org.apache.hyracks.algebricks.core.algebra.visitors.ILogicalOperatorVisitor;
+
+public abstract class AbstractLogicalOperatorPrettyPrintVisitor implements 
ILogicalOperatorVisitor<Void, Integer> {
+    ILogicalExpressionVisitor<String, Integer> exprVisitor;
+    AlgebricksAppendable buffer;
+
+    public AbstractLogicalOperatorPrettyPrintVisitor() {
+        this(new AlgebricksAppendable());
+    }
+
+    public AbstractLogicalOperatorPrettyPrintVisitor(Appendable app) {
+        this(new AlgebricksAppendable(app), new 
LogicalExpressionPrettyPrintVisitor());
+    }
+
+    public AbstractLogicalOperatorPrettyPrintVisitor(AlgebricksAppendable 
buffer) {
+        this(buffer, new LogicalExpressionPrettyPrintVisitor());
+    }
+
+    public AbstractLogicalOperatorPrettyPrintVisitor(AlgebricksAppendable 
buffer,
+            ILogicalExpressionVisitor<String, Integer> exprVisitor) {
+        reset(buffer);
+        this.exprVisitor = exprVisitor;
+    }
+
+    public AlgebricksAppendable reset(AlgebricksAppendable buffer) {
+        AlgebricksAppendable old = this.buffer;
+        this.buffer = buffer;
+        return old;
+    }
+
+    public AlgebricksAppendable get() {
+        return buffer;
+    }
+
+    @Override
+    public String toString() {
+        return buffer.toString();
+    }
+
+    CharSequence str(Object o) {
+        return String.valueOf(o);
+    }
+
+    protected static void appendln(AlgebricksAppendable buf, String s) throws 
AlgebricksException {
+        buf.append(s);
+        buf.append("\n");
+    }
+
+    protected static void append(AlgebricksAppendable buf, String s) throws 
AlgebricksException {
+        buf.append(s);
+    }
+
+    protected static void pad(AlgebricksAppendable buf, int indent) throws 
AlgebricksException {
+        for (int i = 0; i < indent; ++i) {
+            buf.append(' ');
+        }
+    }
+
+    public static void printPhysicalOperator(AbstractLogicalOperator op, int 
indent, AlgebricksAppendable out)
+            throws AlgebricksException {
+        IPhysicalOperator pOp = op.getPhysicalOperator();
+        pad(out, indent);
+        appendln(out, "-- " + pOp.toString() + "  |" + op.getExecutionMode() + 
"|");
+        if (op.hasNestedPlans()) {
+            AbstractOperatorWithNestedPlans opNest = 
(AbstractOperatorWithNestedPlans) op;
+            for (ILogicalPlan p : opNest.getNestedPlans()) {
+                pad(out, indent + 8);
+                appendln(out, "{");
+                printPhysicalOps(p, out, indent + 10);
+                pad(out, indent + 8);
+                appendln(out, "}");
+            }
+        }
+        for (Mutable<ILogicalOperator> i : op.getInputs()) {
+            printPhysicalOperator((AbstractLogicalOperator) i.getValue(), 
indent + 2, out);
+        }
+    }
+
+    public static void printPhysicalOps(ILogicalPlan plan, 
AlgebricksAppendable out, int indent)
+            throws AlgebricksException {
+        for (Mutable<ILogicalOperator> root : plan.getRoots()) {
+            printPhysicalOperator((AbstractLogicalOperator) root.getValue(), 
indent, out);
+        }
+    }
+}
diff --git 
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitor.java
 
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitor.java
index 2139627..fe63b89 100644
--- 
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitor.java
+++ 
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitor.java
@@ -25,18 +25,21 @@
 import org.apache.hyracks.algebricks.common.utils.Pair;
 import org.apache.hyracks.algebricks.common.utils.Triple;
 import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalOperator;
 import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
+import org.apache.hyracks.algebricks.core.algebra.base.IPhysicalOperator;
 import org.apache.hyracks.algebricks.core.algebra.base.LogicalVariable;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractLogicalOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractOperatorWithNestedPlans;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractUnnestMapOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.AggregateOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.AssignOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.DataSourceScanOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.DelegateOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.DistinctOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.DistributeResultOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.EmptyTupleSourceOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.ExchangeOperator;
-import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.DelegateOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.GroupByOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.IndexInsertDeleteUpsertOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.InnerJoinOperator;
@@ -65,48 +68,50 @@
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.WriteOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.WriteResultOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.visitors.ILogicalExpressionVisitor;
-import 
org.apache.hyracks.algebricks.core.algebra.visitors.ILogicalOperatorVisitor;
 
-public class LogicalOperatorPrettyPrintVisitor implements 
ILogicalOperatorVisitor<Void, Integer> {
-
-    ILogicalExpressionVisitor<String, Integer> exprVisitor;
-    AlgebricksAppendable buffer;
+public class LogicalOperatorPrettyPrintVisitor extends 
AbstractLogicalOperatorPrettyPrintVisitor {
 
     public LogicalOperatorPrettyPrintVisitor() {
-        this(new AlgebricksAppendable());
-    }
-
-    public LogicalOperatorPrettyPrintVisitor(Appendable app) {
-        this(new AlgebricksAppendable(app), new 
LogicalExpressionPrettyPrintVisitor());
-    }
-
-    public LogicalOperatorPrettyPrintVisitor(AlgebricksAppendable buffer) {
-        this(buffer, new LogicalExpressionPrettyPrintVisitor());
+        super();
     }
 
     public LogicalOperatorPrettyPrintVisitor(AlgebricksAppendable buffer,
             ILogicalExpressionVisitor<String, Integer> exprVisitor) {
-        reset(buffer);
-        this.exprVisitor = exprVisitor;
+        super(buffer, exprVisitor);
     }
 
-    public AlgebricksAppendable reset(AlgebricksAppendable buffer) {
-        AlgebricksAppendable old = this.buffer;
-        this.buffer = buffer;
-        return old;
+    public LogicalOperatorPrettyPrintVisitor(AlgebricksAppendable buffer) {
+        super(buffer);
     }
 
-    public AlgebricksAppendable get() {
-        return buffer;
+    public LogicalOperatorPrettyPrintVisitor(Appendable app) {
+        super(app);
     }
 
-    @Override
-    public String toString() {
-        return buffer.toString();
+    public static void printPlan(ILogicalPlan plan, 
LogicalOperatorPrettyPrintVisitor pvisitor, int indent)
+            throws AlgebricksException {
+        for (Mutable<ILogicalOperator> root : plan.getRoots()) {
+            printOperator((AbstractLogicalOperator) root.getValue(), pvisitor, 
indent);
+        }
     }
 
-    CharSequence str(Object o) {
-        return String.valueOf(o);
+    public static void printOperator(AbstractLogicalOperator op, 
LogicalOperatorPrettyPrintVisitor pvisitor, int indent)
+            throws AlgebricksException {
+        final AlgebricksAppendable out = pvisitor.get();
+        op.accept(pvisitor, indent);
+        IPhysicalOperator pOp = op.getPhysicalOperator();
+
+        if (pOp != null) {
+            out.append("\n");
+            pad(out, indent);
+            appendln(out, "-- " + pOp.toString() + "  |" + 
op.getExecutionMode() + "|");
+        } else {
+            appendln(out, " -- |" + op.getExecutionMode() + "|");
+        }
+
+        for (Mutable<ILogicalOperator> i : op.getInputs()) {
+            printOperator((AbstractLogicalOperator) i.getValue(), pvisitor, 
indent + 2);
+        }
     }
 
     @Override
@@ -480,7 +485,7 @@
                 } else {
                     addIndent(indent).append("       {\n");
                 }
-                PlanPrettyPrinter.printPlan(p, this, indent + 10);
+                printPlan(p, this, indent + 10);
                 addIndent(indent).append("       }");
             }
         }
diff --git 
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitorJson.java
 
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitorJson.java
new file mode 100644
index 0000000..fefb1e9
--- /dev/null
+++ 
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitorJson.java
@@ -0,0 +1,748 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.hyracks.algebricks.core.algebra.prettyprint;
+
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.mutable.Mutable;
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+import org.apache.hyracks.algebricks.common.utils.Pair;
+import org.apache.hyracks.algebricks.common.utils.Triple;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalOperator;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
+import org.apache.hyracks.algebricks.core.algebra.base.IPhysicalOperator;
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalVariable;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractLogicalOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractOperatorWithNestedPlans;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractUnnestMapOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.AggregateOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.AssignOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.DataSourceScanOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.DelegateOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.DistinctOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.DistributeResultOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.EmptyTupleSourceOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.ExchangeOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.GroupByOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.IndexInsertDeleteUpsertOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.InnerJoinOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.InsertDeleteUpsertOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.InsertDeleteUpsertOperator.Kind;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.IntersectOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.LeftOuterJoinOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.LeftOuterUnnestMapOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.LeftOuterUnnestOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.LimitOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.MaterializeOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.NestedTupleSourceOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.OrderOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.ProjectOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.ReplicateOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.RunningAggregateOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.ScriptOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.SelectOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.SinkOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.SplitOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.SubplanOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.TokenizeOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.UnionAllOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.UnnestMapOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.UnnestOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.WriteOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.WriteResultOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.visitors.ILogicalExpressionVisitor;
+
+public class LogicalOperatorPrettyPrintVisitorJson extends 
AbstractLogicalOperatorPrettyPrintVisitor {
+    Map<AbstractLogicalOperator, String> operatorIdentity = new HashMap<>();
+
+    public LogicalOperatorPrettyPrintVisitorJson(Appendable app) {
+        super(app);
+    }
+
+    IdCounter idCounter = new IdCounter();
+
+    public class IdCounter {
+        private int id;
+        private Deque<Integer> prefix;
+
+        public IdCounter() {
+            prefix = new LinkedList<Integer>();
+            prefix.add(1);
+            this.id = 0;
+        }
+
+        public void previousPrefix() {
+            this.id = prefix.removeLast();
+        }
+
+        public void nextPrefix() {
+            prefix.add(this.id);
+            this.id = 0;
+        }
+
+        public String printOperatorId(AbstractLogicalOperator op) {
+            String stringPrefix = "";
+            Object[] values = this.prefix.toArray();
+            for (Object val : values) {
+                stringPrefix = stringPrefix.isEmpty() ? val.toString() : 
stringPrefix + "." + val.toString();
+            }
+            if (!operatorIdentity.containsKey(op)) {
+                String opId = stringPrefix.isEmpty() ? "" + 
Integer.toString(++id)
+                        : stringPrefix + "." + Integer.toString(++id);
+                operatorIdentity.put(op, opId);
+            }
+            return operatorIdentity.get(op);
+        }
+    }
+
+    public static void printPlanJson(ILogicalPlan plan, 
LogicalOperatorPrettyPrintVisitorJson pvisitor, int indent)
+            throws AlgebricksException {
+        for (Mutable<ILogicalOperator> root : plan.getRoots()) {
+            printOperatorJson((AbstractLogicalOperator) root.getValue(), 
pvisitor, indent);
+        }
+    }
+
+    public static void printOperatorJson(AbstractLogicalOperator op, 
LogicalOperatorPrettyPrintVisitorJson pvisitor,
+            int indent) throws AlgebricksException {
+        int currentIndent = indent;
+        final AlgebricksAppendable out = pvisitor.get();
+        pad(out, currentIndent);
+        appendln(out, "{");
+        currentIndent++;
+        op.accept(pvisitor, currentIndent);
+        appendln(out, ",");
+        pad(out, currentIndent);
+        append(out, "\"operatorId\" : \"" + 
pvisitor.idCounter.printOperatorId(op) + "\"");
+        IPhysicalOperator pOp = op.getPhysicalOperator();
+        if (pOp != null) {
+            appendln(out, ",");
+            pad(out, currentIndent);
+            String pOperator = "\"physical-operator\":\"" + pOp.toString() + 
"\"";
+            append(out, pOperator);
+        }
+        appendln(out, ",");
+        pad(out, currentIndent);
+        append(out, "\"execution-mode\":\"" + op.getExecutionMode() + '"');
+        if (!op.getInputs().isEmpty()) {
+            appendln(out, ",");
+            pad(out, currentIndent);
+            appendln(out, "\"inputs\":[");
+            boolean moreInputes = false;
+            for (Mutable<ILogicalOperator> k : op.getInputs()) {
+                if (moreInputes) {
+                    append(out, ",");
+                }
+                printOperatorJson((AbstractLogicalOperator) k.getValue(), 
pvisitor, currentIndent + 4);
+                moreInputes = true;
+            }
+            pad(out, currentIndent + 2);
+            appendln(out, "]");
+        }
+        out.append("\n");
+        pad(out, currentIndent - 1);
+        appendln(out, "}");
+    }
+
+    public void variablePrintHelper(List<LogicalVariable> variables, Integer 
indent) throws AlgebricksException {
+        if (!variables.isEmpty()) {
+            addIndent(0).append(",\n");
+            addIndent(indent).append("\"variables\" :[");
+            boolean first = true;
+            for (LogicalVariable v : variables) {
+                if (!first) {
+                    buffer.append(",");
+                }
+                buffer.append("\"" + str(v) + "\"");
+                first = false;
+            }
+            buffer.append("]");
+        }
+    }
+
+    @Override
+    public Void visitAggregateOperator(AggregateOperator op, Integer indent) 
throws AlgebricksException {
+        addIndent(indent).append("\"operator\":\"aggregate\"");
+        variablePrintHelper(op.getVariables(), indent);
+
+        return null;
+    }
+
+    @Override
+    public Void visitRunningAggregateOperator(RunningAggregateOperator op, 
Integer indent) throws AlgebricksException {
+        addIndent(indent).append("\"operator\":\"running-aggregate\"");
+        variablePrintHelper(op.getVariables(), indent);
+        if (!op.getExpressions().isEmpty()) {
+            addIndent(0).append(",\n");
+            pprintExprList(op.getExpressions(), indent);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visitEmptyTupleSourceOperator(EmptyTupleSourceOperator op, 
Integer indent) throws AlgebricksException {
+        addIndent(indent).append("\"operator\":\"empty-tuple-source\"");
+        return null;
+    }
+
+    @Override
+    public Void visitGroupByOperator(GroupByOperator op, Integer indent) 
throws AlgebricksException {
+        addIndent(indent).append("\"operator\":\"group-by\"");
+
+        if (op.isGroupAll()) {
+            buffer.append(",\n");
+            addIndent(indent).append("\"option\":\"all\"");
+        }
+        if (!op.getGroupByList().isEmpty()) {
+            buffer.append(",\n");
+            addIndent(indent).append("\"group-by-list\":");
+            pprintVeList(op.getGroupByList(), indent);
+        }
+        if (!op.getDecorList().isEmpty()) {
+            buffer.append(",\n");
+            addIndent(indent).append("\"decor-list\":");
+            pprintVeList(op.getDecorList(), indent);
+        }
+        if (!op.getNestedPlans().isEmpty()) {
+            buffer.append(",\n");
+            addIndent(indent).append("\"subplan\":");
+            printNestedPlans(op, indent);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visitDistinctOperator(DistinctOperator op, Integer indent) 
throws AlgebricksException {
+        addIndent(indent).append("\"operator\":\"distinct\"");
+        if (!op.getExpressions().isEmpty()) {
+            addIndent(0).append(",\n");
+            pprintExprList(op.getExpressions(), indent);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visitInnerJoinOperator(InnerJoinOperator op, Integer indent) 
throws AlgebricksException {
+        addIndent(indent).append("\"operator\":\"join\",\n");
+        addIndent(indent)
+                .append("\"condition\":" + "\"" + 
op.getCondition().getValue().accept(exprVisitor, indent) + "\"");
+        return null;
+    }
+
+    @Override
+    public Void visitLeftOuterJoinOperator(LeftOuterJoinOperator op, Integer 
indent) throws AlgebricksException {
+        addIndent(indent).append("\"operator\":\"left-outer-join\",\n");
+        addIndent(indent)
+                .append("\"condition\":" + "\"" + 
op.getCondition().getValue().accept(exprVisitor, indent) + "\"");
+        return null;
+    }
+
+    @Override
+    public Void visitNestedTupleSourceOperator(NestedTupleSourceOperator op, 
Integer indent)
+            throws AlgebricksException {
+        addIndent(indent).append("\"operator\":\"nested-tuple-source\"");
+        return null;
+    }
+
+    @Override
+    public Void visitOrderOperator(OrderOperator op, Integer indent) throws 
AlgebricksException {
+        addIndent(indent).append("\"operator\":\"order\"");
+        for (Pair<OrderOperator.IOrder, Mutable<ILogicalExpression>> p : 
op.getOrderExpressions()) {
+            buffer.append(",\n");
+            if (op.getTopK() != -1) {
+                addIndent(indent).append("\"topK\":\"" + op.getTopK() + 
"\",\n");
+            }
+            String fst = getOrderString(p.first);
+            addIndent(indent).append("\"first\":" + fst + ",\n");
+            addIndent(indent)
+                    .append("\"second\":\"" + 
p.second.getValue().accept(exprVisitor, indent).replace('"', ' ') + "\"");
+        }
+        return null;
+    }
+
+    private String getOrderString(OrderOperator.IOrder first) {
+        switch (first.getKind()) {
+            case ASC:
+                return "\"ASC\"";
+            case DESC:
+                return "\"DESC\"";
+            default:
+                return first.getExpressionRef().toString();
+        }
+    }
+
+    @Override
+    public Void visitAssignOperator(AssignOperator op, Integer indent) throws 
AlgebricksException {
+        addIndent(indent).append("\"operator\":\"assign\"");
+        variablePrintHelper(op.getVariables(), indent);
+        if (!op.getExpressions().isEmpty()) {
+            addIndent(0).append(",\n");
+            pprintExprList(op.getExpressions(), indent);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visitWriteOperator(WriteOperator op, Integer indent) throws 
AlgebricksException {
+        addIndent(indent).append("\"operator\":\"write\"");
+        if (!op.getExpressions().isEmpty()) {
+            addIndent(0).append(",\n");
+            pprintExprList(op.getExpressions(), indent);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visitDistributeResultOperator(DistributeResultOperator op, 
Integer indent) throws AlgebricksException {
+        addIndent(indent).append("\"operator\":\"distribute-result\"");
+        if (!op.getExpressions().isEmpty()) {
+            addIndent(0).append(",\n");
+            pprintExprList(op.getExpressions(), indent);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visitWriteResultOperator(WriteResultOperator op, Integer 
indent) throws AlgebricksException {
+        addIndent(indent).append("\"operator\":\"load\",\n");
+        addIndent(indent).append(str(op.getDataSource())).append("\"from\":")
+                
.append(op.getPayloadExpression().getValue().accept(exprVisitor, indent) + 
",\n");
+        addIndent(indent).append("\"partitioned-by\":{");
+        pprintExprList(op.getKeyExpressions(), indent);
+        addIndent(indent).append("}");
+        return null;
+    }
+
+    @Override
+    public Void visitSelectOperator(SelectOperator op, Integer indent) throws 
AlgebricksException {
+        addIndent(indent).append("\"operator\":\"select\",\n");
+        addIndent(indent).append("\"expressions\":\""
+                + op.getCondition().getValue().accept(exprVisitor, 
indent).replace('"', ' ') + "\"");
+        return null;
+    }
+
+    @Override
+    public Void visitProjectOperator(ProjectOperator op, Integer indent) 
throws AlgebricksException {
+        addIndent(indent).append("\"operator\":\"project\"");
+        variablePrintHelper(op.getVariables(), indent);
+        return null;
+    }
+
+    @Override
+    public Void visitSubplanOperator(SubplanOperator op, Integer indent) 
throws AlgebricksException {
+        if (!op.getNestedPlans().isEmpty()) {
+            addIndent(indent).append("\"subplan\":");
+            printNestedPlans(op, indent);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visitUnionOperator(UnionAllOperator op, Integer indent) throws 
AlgebricksException {
+        addIndent(indent).append("\"operator\":\"union\"");
+        for (Triple<LogicalVariable, LogicalVariable, LogicalVariable> v : 
op.getVariableMappings()) {
+            buffer.append(",\n");
+            addIndent(indent)
+                    .append("\"values\":[" + "\"" + v.first + "\"," + "\"" + 
v.second + "\"," + "\"" + v.third + "\"]");
+        }
+        return null;
+    }
+
+    @Override
+    public Void visitIntersectOperator(IntersectOperator op, Integer indent) 
throws AlgebricksException {
+        addIndent(indent).append("\"operator\":\"intersect\",\n");
+
+        addIndent(indent).append("\"output-variables\":[");
+        for (int i = 0; i < op.getOutputVars().size(); i++) {
+            if (i > 0) {
+                buffer.append(", ");
+            }
+            buffer.append("\"" + str(op.getOutputVars().get(i)) + "\"");
+        }
+        buffer.append("],");
+        addIndent(indent).append("\"input_variables\":[");
+        for (int i = 0; i < op.getNumInput(); i++) {
+            if (i > 0) {
+                buffer.append(",\n");
+            }
+            buffer.append("[");
+            for (int j = 0; j < op.getInputVariables(i).size(); j++) {
+                if (j > 0) {
+                    buffer.append(", ");
+                }
+                buffer.append("\"" + str(op.getInputVariables(i).get(j)) + 
"\"");
+            }
+            buffer.append(']');
+        }
+        buffer.append("]");
+        return null;
+    }
+
+    @Override
+    public Void visitUnnestOperator(UnnestOperator op, Integer indent) throws 
AlgebricksException {
+        addIndent(indent).append("\"operator\":\"unnest\"");
+        variablePrintHelper(op.getVariables(), indent);
+        if (op.getPositionalVariable() != null) {
+            buffer.append(",\n");
+            addIndent(indent).append("\"position\":\"" + 
op.getPositionalVariable() + "\"");
+        }
+        buffer.append(",\n");
+        addIndent(indent).append("\"expressions\":\""
+                + op.getExpressionRef().getValue().accept(exprVisitor, 
indent).replace('"', ' ') + "\"");
+        return null;
+    }
+
+    @Override
+    public Void visitLeftOuterUnnestOperator(LeftOuterUnnestOperator op, 
Integer indent) throws AlgebricksException {
+        addIndent(indent).append("\"operator\":\"outer-unnest\",\n");
+        addIndent(indent).append("\"variables\":[\"" + op.getVariable() + 
"\"]");
+        if (op.getPositionalVariable() != null) {
+            buffer.append(",\n");
+            addIndent(indent).append("\"position\":" + 
op.getPositionalVariable());
+        }
+        buffer.append(",\n");
+        addIndent(indent).append("\"expressions\":\""
+                + op.getExpressionRef().getValue().accept(exprVisitor, 
indent).replace('"', ' ') + "\"");
+        return null;
+    }
+
+    @Override
+    public Void visitUnnestMapOperator(UnnestMapOperator op, Integer indent) 
throws AlgebricksException {
+        return printAbstractUnnestMapOperator(op, indent, "unnest-map");
+    }
+
+    @Override
+    public Void visitLeftOuterUnnestMapOperator(LeftOuterUnnestMapOperator op, 
Integer indent)
+            throws AlgebricksException {
+        return printAbstractUnnestMapOperator(op, indent, 
"left-outer-unnest-map");
+    }
+
+    private Void printAbstractUnnestMapOperator(AbstractUnnestMapOperator op, 
Integer indent, String opSignature)
+            throws AlgebricksException {
+        AlgebricksAppendable plan = addIndent(indent).append("\"operator\":\"" 
+ opSignature + "\"");
+        variablePrintHelper(op.getVariables(), indent);
+        buffer.append(",\n");
+        addIndent(indent).append("\"expressions\":\""
+                + op.getExpressionRef().getValue().accept(exprVisitor, 
indent).replace('"', ' ') + "\"");
+        appendFilterInformation(plan, op.getMinFilterVars(), 
op.getMaxFilterVars(), indent);
+        return null;
+    }
+
+    @Override
+    public Void visitDataScanOperator(DataSourceScanOperator op, Integer 
indent) throws AlgebricksException {
+        AlgebricksAppendable plan = 
addIndent(indent).append("\"operator\":\"data-scan\"");
+        if (!op.getProjectVariables().isEmpty()) {
+            addIndent(0).append(",\n");
+            addIndent(indent).append("\"project-variables\":[");
+            boolean first = true;
+            for (LogicalVariable v : op.getProjectVariables()) {
+                if (!first) {
+                    buffer.append(",");
+                }
+                buffer.append("\"" + str(v) + "\"");
+                first = false;
+            }
+            buffer.append("]");
+        }
+        variablePrintHelper(op.getVariables(), indent);
+        if (op.getDataSource() != null) {
+            addIndent(0).append(",\n");
+            addIndent(indent).append("\"data-source\":\"" + op.getDataSource() 
+ "\"");
+        }
+        appendFilterInformation(plan, op.getMinFilterVars(), 
op.getMaxFilterVars(), indent);
+        return null;
+    }
+
+    private Void appendFilterInformation(AlgebricksAppendable plan, 
List<LogicalVariable> minFilterVars,
+            List<LogicalVariable> maxFilterVars, Integer indent) throws 
AlgebricksException {
+        if (minFilterVars != null || maxFilterVars != null) {
+            plan.append(",\n");
+            addIndent(indent);
+            plan.append("\"with-filter-on\":{");
+        }
+        if (minFilterVars != null) {
+            buffer.append("\n");
+            addIndent(indent).append("\"min\":[");
+            boolean first = true;
+            for (LogicalVariable v : minFilterVars) {
+                if (!first) {
+                    buffer.append(",");
+                }
+                buffer.append("\"" + str(v) + "\"");
+                first = false;
+            }
+            buffer.append("]");
+        }
+        if (minFilterVars != null && maxFilterVars != null) {
+            buffer.append(",");
+        }
+        if (maxFilterVars != null) {
+            buffer.append("\n");
+            addIndent(indent).append("\"max\":[");
+            boolean first = true;
+            for (LogicalVariable v : maxFilterVars) {
+                if (!first) {
+                    buffer.append(",");
+                }
+                buffer.append("\"" + str(v) + "\"");
+                first = false;
+            }
+            buffer.append("]");
+        }
+        if (minFilterVars != null || maxFilterVars != null) {
+            plan.append("\n");
+            addIndent(indent).append("}");
+        }
+        return null;
+    }
+
+    @Override
+    public Void visitLimitOperator(LimitOperator op, Integer indent) throws 
AlgebricksException {
+        addIndent(indent).append("\"operator\":\"limit\",\n");
+        addIndent(indent).append("\"value\":\"" + 
op.getMaxObjects().getValue().accept(exprVisitor, indent) + "\"");
+        ILogicalExpression offset = op.getOffset().getValue();
+        if (offset != null) {
+            buffer.append(",\n");
+            addIndent(indent).append("\"offset\":\"" + 
offset.accept(exprVisitor, indent) + "\"");
+        }
+        return null;
+    }
+
+    @Override
+    public Void visitExchangeOperator(ExchangeOperator op, Integer indent) 
throws AlgebricksException {
+        addIndent(indent).append("\"operator\":\"exchange\"");
+        return null;
+    }
+
+    @Override
+    public Void visitScriptOperator(ScriptOperator op, Integer indent) throws 
AlgebricksException {
+        addIndent(indent).append("\"operator\":\"script\"");
+        if (!op.getInputVariables().isEmpty()) {
+            addIndent(0).append(",\n");
+            addIndent(indent).append("\"in\":[");
+            boolean first = true;
+            for (LogicalVariable v : op.getInputVariables()) {
+                if (!first) {
+                    buffer.append(",");
+                }
+                buffer.append("\"" + str(v) + "\"");
+                first = false;
+            }
+            buffer.append("]");
+        }
+        if (!op.getOutputVariables().isEmpty()) {
+            addIndent(0).append(",\n");
+            addIndent(indent).append("\"out\":[");
+            boolean first = true;
+            for (LogicalVariable v : op.getOutputVariables()) {
+                if (!first) {
+                    buffer.append(",");
+                }
+                buffer.append("\"" + str(v) + "\"");
+                first = false;
+            }
+            buffer.append("]");
+        }
+        return null;
+    }
+
+    @Override
+    public Void visitReplicateOperator(ReplicateOperator op, Integer indent) 
throws AlgebricksException {
+        addIndent(indent).append("\"operator\":\"replicate\"");
+        return null;
+    }
+
+    @Override
+    public Void visitSplitOperator(SplitOperator op, Integer indent) throws 
AlgebricksException {
+        Mutable<ILogicalExpression> branchingExpression = 
op.getBranchingExpression();
+        addIndent(indent).append("\"operator\":\"split\",\n");
+        addIndent(indent).append("\"" + 
branchingExpression.getValue().accept(exprVisitor, indent) + "\"");
+        return null;
+    }
+
+    @Override
+    public Void visitMaterializeOperator(MaterializeOperator op, Integer 
indent) throws AlgebricksException {
+        addIndent(indent).append("\"operator\":\"materialize\"");
+        return null;
+    }
+
+    @Override
+    public Void visitInsertDeleteUpsertOperator(InsertDeleteUpsertOperator op, 
Integer indent)
+            throws AlgebricksException {
+        String header = "\"operator\":\"" + 
getIndexOpString(op.getOperation()) + "\",\n";
+        addIndent(indent).append(header);
+        addIndent(indent).append(str("\"data-source\":\"" + op.getDataSource() 
+ "\",\n"));
+        addIndent(indent).append("\"from-record\":\"")
+                
.append(op.getPayloadExpression().getValue().accept(exprVisitor, indent) + 
"\"");
+        if (op.getAdditionalNonFilteringExpressions() != null) {
+            buffer.append(",\n\"meta\":\"");
+            pprintExprList(op.getAdditionalNonFilteringExpressions(), 0);
+            buffer.append("\"");
+        }
+        buffer.append(",\n");
+        addIndent(indent).append("\"partitioned-by\":{");
+        pprintExprList(op.getPrimaryKeyExpressions(), 0);
+        buffer.append("}");
+        if (op.getOperation() == Kind.UPSERT) {
+            addIndent(indent).append(",\n\"out\":{\n");
+            addIndent(indent).append("\"record-before-upsert\":\"" + 
op.getBeforeOpRecordVar() + "\"");
+            if (op.getBeforeOpAdditionalNonFilteringVars() != null) {
+                buffer.append(",\n");
+                addIndent(indent)
+                        .append("\"additional-before-upsert\":\"" + 
op.getBeforeOpAdditionalNonFilteringVars() + "\"");
+            }
+            addIndent(indent).append("}");
+        }
+        if (op.isBulkload()) {
+            buffer.append(",\n");
+            addIndent(indent).append("\"bulkload\":\"true\"");
+        }
+        return null;
+    }
+
+    @Override
+    public Void 
visitIndexInsertDeleteUpsertOperator(IndexInsertDeleteUpsertOperator op, 
Integer indent)
+            throws AlgebricksException {
+        String header = getIndexOpString(op.getOperation());
+        addIndent(indent).append("\"operator\":\"" + header + "\",\n");
+        addIndent(indent).append("\"index\":\"" + op.getIndexName() + "\",\n");
+        
addIndent(indent).append("\"on\":\"").append(str(op.getDataSourceIndex().getDataSource())
 + "\",\n");
+        addIndent(indent).append("\"from\":{");
+
+        if (op.getOperation() == Kind.UPSERT) {
+
+            addIndent(indent).append("[\"replace\":\"");
+            pprintExprList(op.getPrevSecondaryKeyExprs(), 0);
+            buffer.append("\",\n");
+            addIndent(indent).append("\"with\":\"");
+            pprintExprList(op.getSecondaryKeyExpressions(), 0);
+            buffer.append("\"}");
+        } else {
+            pprintExprList(op.getSecondaryKeyExpressions(), 0);
+        }
+        buffer.append("\n");
+        addIndent(indent).append("}");
+        if (op.isBulkload()) {
+            buffer.append(",\n");
+            buffer.append("\"bulkload\":\"true\"");
+        }
+        return null;
+    }
+
+    public String getIndexOpString(Kind opKind) {
+        switch (opKind) {
+            case DELETE:
+                return "delete-from";
+            case INSERT:
+                return "insert-into";
+            case UPSERT:
+                return "upsert-into";
+        }
+        return null;
+    }
+
+    @Override
+    public Void visitTokenizeOperator(TokenizeOperator op, Integer indent) 
throws AlgebricksException {
+        addIndent(indent).append("\"operator\":\"tokenize\"");
+        variablePrintHelper(op.getTokenizeVars(), indent);
+        if (!op.getSecondaryKeyExpressions().isEmpty()) {
+            addIndent(0).append(",\n");
+            pprintExprList(op.getSecondaryKeyExpressions(), indent);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visitSinkOperator(SinkOperator op, Integer indent) throws 
AlgebricksException {
+        addIndent(indent).append("\"operator\":\"sink\"");
+        return null;
+    }
+
+    @Override
+    public Void visitDelegateOperator(DelegateOperator op, Integer indent) 
throws AlgebricksException {
+        addIndent(indent).append("\"operator\":\"" + op.toString() + "\"");
+        return null;
+    }
+
+    protected AlgebricksAppendable addIndent(int level) throws 
AlgebricksException {
+        for (int i = 0; i < level; ++i) {
+            buffer.append(' ');
+        }
+        return buffer;
+    }
+
+    protected void printNestedPlans(AbstractOperatorWithNestedPlans op, 
Integer indent) throws AlgebricksException {
+        idCounter.nextPrefix();
+        buffer.append("[\n");
+        boolean first = true;
+        for (ILogicalPlan p : op.getNestedPlans()) {
+            if (!first) {
+                buffer.append(",");
+            }
+            printPlanJson(p, this, indent + 4);
+            first = false;
+
+        }
+        addIndent(indent).append("]");
+        idCounter.previousPrefix();
+    }
+
+    //Done--Look for exprRef
+    protected void pprintExprList(List<Mutable<ILogicalExpression>> 
expressions, Integer indent)
+            throws AlgebricksException {
+        addIndent(indent);
+        buffer.append("\"expressions\":\"");
+        boolean first = true;
+        for (Mutable<ILogicalExpression> exprRef : expressions) {
+            if (first) {
+                first = false;
+            } else {
+                buffer.append(", ");
+            }
+            buffer.append(exprRef.getValue().accept(exprVisitor, 
indent).replace('"', ' '));
+        }
+        buffer.append("\"");
+    }
+
+    protected void pprintVeList(List<Pair<LogicalVariable, 
Mutable<ILogicalExpression>>> vePairList, Integer indent)
+            throws AlgebricksException {
+        buffer.append("[");
+        boolean fst = true;
+        for (Pair<LogicalVariable, Mutable<ILogicalExpression>> ve : 
vePairList) {
+            if (fst) {
+                fst = false;
+            } else {
+                buffer.append(",");
+            }
+            if (ve.first != null) {
+                buffer.append("{\"variable\":\"" + 
ve.first.toString().replace('"', ' ') + "\"," + "\"expression\":\""
+                        + ve.second.toString().replace('"', ' ') + "\"}");
+            } else {
+                buffer.append("{\"expression\":\"" + 
ve.second.getValue().accept(exprVisitor, indent).replace('"', ' ')
+                        + "\"}");
+            }
+        }
+        buffer.append("]");
+    }
+
+}
diff --git 
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/PlanPrettyPrinter.java
 
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/PlanPrettyPrinter.java
index 9a55f11..cf99d3b 100644
--- 
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/PlanPrettyPrinter.java
+++ 
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/PlanPrettyPrinter.java
@@ -18,76 +18,42 @@
  */
 package org.apache.hyracks.algebricks.core.algebra.prettyprint;
 
-import org.apache.commons.lang3.mutable.Mutable;
 import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
-import org.apache.hyracks.algebricks.core.algebra.base.ILogicalOperator;
 import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
-import org.apache.hyracks.algebricks.core.algebra.base.IPhysicalOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractLogicalOperator;
-import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractOperatorWithNestedPlans;
 
 public class PlanPrettyPrinter {
-    public static void printPlan(ILogicalPlan plan, 
LogicalOperatorPrettyPrintVisitor pvisitor, int indent)
-            throws AlgebricksException {
-        for (Mutable<ILogicalOperator> root : plan.getRoots()) {
-            printOperator((AbstractLogicalOperator) root.getValue(), pvisitor, 
indent);
+    @FunctionalInterface
+    public interface print<T1, T2, T3> {
+        void apply(T1 arg1, T2 arg2, T3 arg3) throws AlgebricksException;
+    }
+
+    public static void printOperator(AbstractLogicalOperator op, 
LogicalOperatorPrettyPrintVisitor pvisitor,
+            int indent) throws AlgebricksException {
+        print<AbstractLogicalOperator, LogicalOperatorPrettyPrintVisitor, 
Integer> printOperator =
+                LogicalOperatorPrettyPrintVisitor::printOperator;
+        printOperator.apply(op, pvisitor, indent);
+    }
+
+    public static <T extends AbstractLogicalOperatorPrettyPrintVisitor> void 
printPlan(ILogicalPlan plan,
+            T pvisitor, int indent) throws AlgebricksException {
+        if 
(pvisitor.getClass().equals(LogicalOperatorPrettyPrintVisitor.class)) {
+            print<ILogicalPlan, LogicalOperatorPrettyPrintVisitor, Integer> 
printPlan =
+                    LogicalOperatorPrettyPrintVisitor::printPlan;
+            printPlan.apply(plan,(LogicalOperatorPrettyPrintVisitor) pvisitor, 
indent);
         }
+        else if 
(pvisitor.getClass().equals(LogicalOperatorPrettyPrintVisitorJson.class)) {
+            print<ILogicalPlan, LogicalOperatorPrettyPrintVisitorJson, 
Integer> printPlan =
+                    LogicalOperatorPrettyPrintVisitorJson::printPlanJson;
+            printPlan.apply(plan, 
(LogicalOperatorPrettyPrintVisitorJson)pvisitor, indent);
+        }
+
     }
 
     public static void printPhysicalOps(ILogicalPlan plan, 
AlgebricksAppendable out, int indent)
             throws AlgebricksException {
-        for (Mutable<ILogicalOperator> root : plan.getRoots()) {
-            printPhysicalOperator((AbstractLogicalOperator) root.getValue(), 
indent, out);
-        }
-    }
-
-    public static void printOperator(AbstractLogicalOperator op, 
LogicalOperatorPrettyPrintVisitor pvisitor, int indent)
-            throws AlgebricksException {
-        final AlgebricksAppendable out = pvisitor.get();
-        op.accept(pvisitor, indent);
-        IPhysicalOperator pOp = op.getPhysicalOperator();
-
-        if (pOp != null) {
-            out.append("\n");
-            pad(out, indent);
-            appendln(out, "-- " + pOp.toString() + "  |" + 
op.getExecutionMode() + "|");
-        } else {
-            appendln(out, " -- |" + op.getExecutionMode() + "|");
-        }
-
-        for (Mutable<ILogicalOperator> i : op.getInputs()) {
-            printOperator((AbstractLogicalOperator) i.getValue(), pvisitor, 
indent + 2);
-        }
-    }
-
-    private static void printPhysicalOperator(AbstractLogicalOperator op, int 
indent, AlgebricksAppendable out)
-            throws AlgebricksException {
-        IPhysicalOperator pOp = op.getPhysicalOperator();
-        pad(out, indent);
-        appendln(out, "-- " + pOp.toString() + "  |" + op.getExecutionMode() + 
"|");
-        if (op.hasNestedPlans()) {
-            AbstractOperatorWithNestedPlans opNest = 
(AbstractOperatorWithNestedPlans) op;
-            for (ILogicalPlan p : opNest.getNestedPlans()) {
-                pad(out, indent + 8);
-                appendln(out, "{");
-                printPhysicalOps(p, out, indent + 10);
-                pad(out, indent + 8);
-                appendln(out, "}");
-            }
-        }
-        for (Mutable<ILogicalOperator> i : op.getInputs()) {
-            printPhysicalOperator((AbstractLogicalOperator) i.getValue(), 
indent + 2, out);
-        }
-    }
-
-    private static void appendln(AlgebricksAppendable buf, String s) throws 
AlgebricksException {
-        buf.append(s);
-        buf.append("\n");
-    }
-
-    private static void pad(AlgebricksAppendable buf, int indent) throws 
AlgebricksException {
-        for (int i = 0; i < indent; ++i) {
-            buf.append(' ');
-        }
+        print<ILogicalPlan, AlgebricksAppendable, Integer> printOperator =
+                AbstractLogicalOperatorPrettyPrintVisitor::printPhysicalOps;
+        printOperator.apply(plan, out, indent);
     }
 }
diff --git 
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/visitors/ILogicalOperatorVisitor.java
 
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/visitors/ILogicalOperatorVisitor.java
index 8da41e2..deb98b0 100644
--- 
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/visitors/ILogicalOperatorVisitor.java
+++ 
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/visitors/ILogicalOperatorVisitor.java
@@ -22,11 +22,11 @@
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.AggregateOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.AssignOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.DataSourceScanOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.DelegateOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.DistinctOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.DistributeResultOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.EmptyTupleSourceOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.ExchangeOperator;
-import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.DelegateOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.GroupByOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.IndexInsertDeleteUpsertOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.InnerJoinOperator;

-- 
To view, visit https://asterix-gerrit.ics.uci.edu/1885
To unsubscribe, visit https://asterix-gerrit.ics.uci.edu/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I4dd62e355048a5b8a02e074049fe41e73e74e357
Gerrit-PatchSet: 30
Gerrit-Project: asterixdb
Gerrit-Branch: master
Gerrit-Owner: [email protected]
Gerrit-Reviewer: Anon. E. Moose #1000171
Gerrit-Reviewer: Ian Maxon <[email protected]>
Gerrit-Reviewer: Jenkins <[email protected]>
Gerrit-Reviewer: Shiva Jahangiri <[email protected]>
Gerrit-Reviewer: Till Westmann <[email protected]>

Reply via email to