You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by kr...@apache.org on 2016/10/27 21:59:02 UTC

[2/2] lucene-solr:jira/solr-8593: Update Calcite rules

Update Calcite rules


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/073fecfa
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/073fecfa
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/073fecfa

Branch: refs/heads/jira/solr-8593
Commit: 073fecfa53943a0fd6368d19a3e0b764d6bfb4a7
Parents: e1ed290
Author: Kevin Risden <kr...@apache.org>
Authored: Thu Oct 27 16:58:56 2016 -0500
Committer: Kevin Risden <kr...@apache.org>
Committed: Thu Oct 27 16:58:56 2016 -0500

----------------------------------------------------------------------
 .../apache/solr/handler/sql/SolrAggregate.java  |  24 ++-
 .../apache/solr/handler/sql/SolrEnumerator.java |  51 +++++-
 .../apache/solr/handler/sql/SolrProject.java    |   6 +-
 .../org/apache/solr/handler/sql/SolrRel.java    |  38 ++---
 .../org/apache/solr/handler/sql/SolrRules.java  |  12 +-
 .../org/apache/solr/handler/sql/SolrSchema.java |   2 +-
 .../org/apache/solr/handler/sql/SolrSort.java   |   6 +-
 .../org/apache/solr/handler/sql/SolrTable.java  | 160 ++++++++++---------
 .../handler/sql/SolrToEnumerableConverter.java  |  36 ++++-
 9 files changed, 200 insertions(+), 135 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/073fecfa/solr/core/src/java/org/apache/solr/handler/sql/SolrAggregate.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/sql/SolrAggregate.java b/solr/core/src/java/org/apache/solr/handler/sql/SolrAggregate.java
index 02b50c5..f913585 100644
--- a/solr/core/src/java/org/apache/solr/handler/sql/SolrAggregate.java
+++ b/solr/core/src/java/org/apache/solr/handler/sql/SolrAggregate.java
@@ -65,27 +65,21 @@ class SolrAggregate extends Aggregate implements SolrRel {
     implementor.visitChild(0, getInput());
 
     final List<String> inNames = SolrRules.solrFieldNames(getInput().getRowType());
-    final List<String> outNames = SolrRules.solrFieldNames(getRowType());
 
-    Map<String, String> fieldMappings = new HashMap<>();
-    for(AggregateCall aggCall : aggCalls) {
+    for(Pair<AggregateCall, String> namedAggCall : getNamedAggCalls()) {
+      AggregateCall aggCall = namedAggCall.getKey();
       Pair<String, String> metric = toSolrMetric(implementor, aggCall, inNames);
-      implementor.addMetric(metric);
-      fieldMappings.put(aggCall.getName(), metric.getKey().toLowerCase(Locale.ROOT) + "(" + metric.getValue() + ")");
+      implementor.addMetricPair(namedAggCall.getValue(), metric.getKey(), metric.getValue());
+      if(aggCall.getName() == null) {
+        implementor.addFieldMapping(namedAggCall.getValue(),
+            aggCall.getAggregation().getName() + "(" + inNames.get(aggCall.getArgList().get(0)) + ")");
+      }
     }
 
-    List<String> buckets = new ArrayList<>();
-    for(int group : groupSet) {
+    for(int group : getGroupSet()) {
       String inName = inNames.get(group);
-      String name = implementor.fieldMappings.getOrDefault(inName, inName);
-      buckets.add(name);
-      if(!fieldMappings.containsKey(name)) {
-        fieldMappings.put(name, name);
-      }
+      implementor.addBucket(inName);
     }
-
-    implementor.addBuckets(buckets);
-    implementor.addFieldMappings(fieldMappings);
   }
 
   private Pair<String, String> toSolrMetric(Implementor implementor, AggregateCall aggCall, List<String> inNames) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/073fecfa/solr/core/src/java/org/apache/solr/handler/sql/SolrEnumerator.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/sql/SolrEnumerator.java b/solr/core/src/java/org/apache/solr/handler/sql/SolrEnumerator.java
index b9d0cec..1714e67 100644
--- a/solr/core/src/java/org/apache/solr/handler/sql/SolrEnumerator.java
+++ b/solr/core/src/java/org/apache/solr/handler/sql/SolrEnumerator.java
@@ -19,14 +19,19 @@ package org.apache.solr.handler.sql;
 import org.apache.calcite.linq4j.Enumerator;
 import org.apache.solr.client.solrj.io.Tuple;
 import org.apache.solr.client.solrj.io.stream.TupleStream;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
 import java.util.List;
+import java.util.Map;
 
 /** Enumerator that reads from a Solr collection. */
 class SolrEnumerator implements Enumerator<Object> {
+  private static final Logger logger = LoggerFactory.getLogger(SolrEnumerator.class);
+
   private final TupleStream tupleStream;
-  private final List<String> fields;
+  private final List<Map.Entry<String, Class>> fields;
   private Tuple current;
 
   /** Creates a SolrEnumerator.
@@ -34,7 +39,7 @@ class SolrEnumerator implements Enumerator<Object> {
    * @param tupleStream Solr TupleStream
    * @param fields Fields to get from each Tuple
    */
-  SolrEnumerator(TupleStream tupleStream, List<String> fields) {
+  SolrEnumerator(TupleStream tupleStream, List<Map.Entry<String, Class>> fields) {
     this.tupleStream = tupleStream;
     try {
       this.tupleStream.open();
@@ -51,18 +56,54 @@ class SolrEnumerator implements Enumerator<Object> {
    */
   public Object current() {
     if (fields.size() == 1) {
-      return current.get(fields.get(0));
+      return this.getter(current, fields.get(0));
     } else {
       // Build an array with all fields in this row
       Object[] row = new Object[fields.size()];
       for (int i = 0; i < fields.size(); i++) {
-        row[i] = current.get(fields.get(i));
+        row[i] = this.getter(current, fields.get(i));
       }
 
       return row;
     }
   }
 
+  private Object getter(Tuple tuple, Map.Entry<String, Class> field) {
+    Object val = tuple.get(field.getKey());
+    Class clazz = field.getValue();
+
+    if(clazz.equals(Double.class)) {
+      return val == null ? 0D : val;
+    }
+
+    if(clazz.equals(Long.class)) {
+      if(val == null) {
+        return 0L;
+      }
+      if(val instanceof Double) {
+        return this.getRealVal(val);
+      }
+      return val;
+    }
+
+    return val;
+  }
+
+  private Object getRealVal(Object val) {
+    // Check if Double is really a Long
+    if(val instanceof Double) {
+      Double doubleVal = (double) val;
+      //make sure that double has no decimals and fits within Long
+      if(doubleVal % 1 == 0 && doubleVal >= Long.MIN_VALUE && doubleVal <= Long.MAX_VALUE) {
+        return doubleVal.longValue();
+      }
+      return doubleVal;
+    }
+
+    // Wasn't a double so just return original Object
+    return val;
+  }
+
   public boolean moveNext() {
     try {
       Tuple tuple = this.tupleStream.read();
@@ -73,7 +114,7 @@ class SolrEnumerator implements Enumerator<Object> {
         return true;
       }
     } catch (IOException e) {
-      e.printStackTrace();
+      logger.error("IOException", e);
       return false;
     }
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/073fecfa/solr/core/src/java/org/apache/solr/handler/sql/SolrProject.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/sql/SolrProject.java b/solr/core/src/java/org/apache/solr/handler/sql/SolrProject.java
index fb7e5e8..c4217f2 100644
--- a/solr/core/src/java/org/apache/solr/handler/sql/SolrProject.java
+++ b/solr/core/src/java/org/apache/solr/handler/sql/SolrProject.java
@@ -28,9 +28,7 @@ import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.util.Pair;
 
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 /**
  * Implementation of {@link org.apache.calcite.rel.core.Project} relational expression in Solr.
@@ -57,12 +55,10 @@ class SolrProject extends Project implements SolrRel {
     implementor.visitChild(0, getInput());
     final SolrRules.RexToSolrTranslator translator = new SolrRules.RexToSolrTranslator(
         (JavaTypeFactory) getCluster().getTypeFactory(), SolrRules.solrFieldNames(getInput().getRowType()));
-    final Map<String, String> fieldMappings = new HashMap<>();
     for (Pair<RexNode, String> pair : getNamedProjects()) {
       final String name = pair.right;
       final String expr = pair.left.accept(translator);
-      fieldMappings.put(name, expr);
+      implementor.addFieldMapping(name, expr);
     }
-    implementor.addFieldMappings(fieldMappings);
   }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/073fecfa/solr/core/src/java/org/apache/solr/handler/sql/SolrRel.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/sql/SolrRel.java b/solr/core/src/java/org/apache/solr/handler/sql/SolrRel.java
index 5a2ab6f..ce67a30 100644
--- a/solr/core/src/java/org/apache/solr/handler/sql/SolrRel.java
+++ b/solr/core/src/java/org/apache/solr/handler/sql/SolrRel.java
@@ -21,10 +21,7 @@ import org.apache.calcite.plan.RelOptTable;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.util.Pair;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 /**
  * Relational expression that uses Solr calling convention.
@@ -40,36 +37,41 @@ interface SolrRel extends RelNode {
     final Map<String, String> fieldMappings = new HashMap<>();
     String query = null;
     String limitValue = null;
-    final List<String> order = new ArrayList<>();
+    final List<Pair<String, String>> orders = new ArrayList<>();
     final List<String> buckets = new ArrayList<>();
     final List<Pair<String, String>> metricPairs = new ArrayList<>();
 
     RelOptTable table;
     SolrTable solrTable;
 
-    void addFieldMappings(Map<String, String> fieldMappings) {
-      this.fieldMappings.putAll(fieldMappings);
+    void addFieldMapping(String key, String val) {
+      if(key != null && !fieldMappings.containsKey(key)) {
+        this.fieldMappings.put(key, val);
+      }
     }
 
     void addQuery(String query) {
       this.query = query;
     }
 
-    void addOrder(List<String> order) {
-      for(String orderItem : order) {
-        String[] orderParts = orderItem.split(" ", 2);
-        String fieldName = orderParts[0];
-        String direction = orderParts[1];
-       this.order.add(this.fieldMappings.getOrDefault(fieldName, fieldName) + " " + direction);
-      }
+    void addOrder(String column, String direction) {
+      column = this.fieldMappings.getOrDefault(column, column);
+      this.orders.add(new Pair<>(column, direction));
     }
 
-    void addBuckets(List<String> buckets) {
-      this.buckets.addAll(buckets);
+    void addBucket(String bucket) {
+      bucket = this.fieldMappings.getOrDefault(bucket, bucket);
+      this.buckets.add(bucket);
     }
 
-    void addMetric(Pair<String, String> metricPair) {
-      this.metricPairs.add(metricPair);
+    void addMetricPair(String outName, String metric, String column) {
+      column = this.fieldMappings.getOrDefault(column, column);
+      this.metricPairs.add(new Pair<>(metric, column));
+
+      String metricIdentifier = metric + "(" + column + ")";
+      if(outName != null) {
+        this.addFieldMapping(outName, metricIdentifier.toLowerCase(Locale.ROOT));
+      }
     }
 
     void setLimit(String limit) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/073fecfa/solr/core/src/java/org/apache/solr/handler/sql/SolrRules.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/sql/SolrRules.java b/solr/core/src/java/org/apache/solr/handler/sql/SolrRules.java
index 7bc142b..7d1aa59 100644
--- a/solr/core/src/java/org/apache/solr/handler/sql/SolrRules.java
+++ b/solr/core/src/java/org/apache/solr/handler/sql/SolrRules.java
@@ -65,7 +65,7 @@ class SolrRules {
           public int size() {
             return rowType.getFieldCount();
           }
-        });
+        }, true);
   }
 
   /** Translator from {@link RexNode} to strings in Solr's expression language. */
@@ -127,9 +127,11 @@ class SolrRules {
 
     abstract public RelNode convert(RelNode rel);
 
+    /**
+     * @see ConverterRule
+     */
     @Override
     public void onMatch(RelOptRuleCall call) {
-      /** @see ConverterRule */
       RelNode rel = call.rel(0);
       if (rel.getTraitSet().contains(Convention.NONE)) {
         final RelNode converted = convert(rel);
@@ -218,10 +220,14 @@ class SolrRules {
    * Rule to convert an {@link LogicalAggregate} to an {@link SolrAggregate}.
    */
   private static class SolrAggregateRule extends SolrConverterRule {
+    private static final Predicate<RelNode> AGGREGATE_PREDICTE = relNode ->
+        Aggregate.IS_SIMPLE.apply(((LogicalAggregate)relNode));// &&
+//        !((LogicalAggregate)relNode).containsDistinctCall();
+
     private static final RelOptRule AGGREGATE_RULE = new SolrAggregateRule();
 
     private SolrAggregateRule() {
-      super(LogicalAggregate.class, relNode -> Aggregate.IS_SIMPLE.apply(((LogicalAggregate)relNode)), "SolrAggregateRule");
+      super(LogicalAggregate.class, AGGREGATE_PREDICTE, "SolrAggregateRule");
     }
 
      public RelNode convert(RelNode rel) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/073fecfa/solr/core/src/java/org/apache/solr/handler/sql/SolrSchema.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/sql/SolrSchema.java b/solr/core/src/java/org/apache/solr/handler/sql/SolrSchema.java
index 1b94e4c..8c3eaa9 100644
--- a/solr/core/src/java/org/apache/solr/handler/sql/SolrSchema.java
+++ b/solr/core/src/java/org/apache/solr/handler/sql/SolrSchema.java
@@ -46,7 +46,7 @@ class SolrSchema extends AbstractSchema {
     String zk = this.properties.getProperty("zk");
     try(CloudSolrClient cloudSolrClient = new CloudSolrClient.Builder().withZkHost(zk).build()) {
       cloudSolrClient.connect();
-      Set<String> collections = cloudSolrClient.getZkStateReader().getClusterState().getCollections();
+      Set<String> collections = cloudSolrClient.getZkStateReader().getClusterState().getCollectionsMap().keySet();
 
       final ImmutableMap.Builder<String, Table> builder = ImmutableMap.builder();
       for (String collection : collections) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/073fecfa/solr/core/src/java/org/apache/solr/handler/sql/SolrSort.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/sql/SolrSort.java b/solr/core/src/java/org/apache/solr/handler/sql/SolrSort.java
index 4f6cd4a..7deabeb 100644
--- a/solr/core/src/java/org/apache/solr/handler/sql/SolrSort.java
+++ b/solr/core/src/java/org/apache/solr/handler/sql/SolrSort.java
@@ -29,7 +29,6 @@ import org.apache.calcite.rel.type.RelDataTypeField;
 import org.apache.calcite.rex.RexLiteral;
 import org.apache.calcite.rex.RexNode;
 
-import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -58,7 +57,6 @@ class SolrSort extends Sort implements SolrRel {
     implementor.visitChild(0, getInput());
 
     List<RelFieldCollation> sortCollations = collation.getFieldCollations();
-    List<String> fieldOrder = new ArrayList<>();
     if (!sortCollations.isEmpty()) {
       // Construct a series of order clauses from the desired collation
       final List<RelDataTypeField> fields = getRowType().getFieldList();
@@ -68,10 +66,8 @@ class SolrSort extends Sort implements SolrRel {
         if (fieldCollation.getDirection().equals(RelFieldCollation.Direction.DESCENDING)) {
           direction = "desc";
         }
-        fieldOrder.add(name + " " + direction);
+        implementor.addOrder(name, direction);
       }
-
-      implementor.addOrder(fieldOrder);
     }
 
     if(fetch != null) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/073fecfa/solr/core/src/java/org/apache/solr/handler/sql/SolrTable.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/sql/SolrTable.java b/solr/core/src/java/org/apache/solr/handler/sql/SolrTable.java
index 6884658..6e4631c 100644
--- a/solr/core/src/java/org/apache/solr/handler/sql/SolrTable.java
+++ b/solr/core/src/java/org/apache/solr/handler/sql/SolrTable.java
@@ -36,18 +36,18 @@ import org.apache.solr.client.solrj.io.stream.*;
 import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;
 import org.apache.solr.client.solrj.io.stream.metrics.*;
 import org.apache.solr.common.params.CommonParams;
-import org.apache.solr.update.VersionInfo;
+import org.apache.solr.common.params.ModifiableSolrParams;
 
 import java.io.IOException;
 import java.util.*;
+import java.util.stream.Collectors;
 
 /**
  * Table based on a Solr collection
  */
 class SolrTable extends AbstractQueryableTable implements TranslatableTable {
   private static final String DEFAULT_QUERY = "*:*";
-  private static final String DEFAULT_VERSION_FIELD = VersionInfo.VERSION_FIELD;
-  private static final String DEFAULT_SCORE_FIELD = "score";
+  private static final String DEFAULT_VERSION_FIELD = "_version_";
 
   private final String collection;
   private final SolrSchema schema;
@@ -82,64 +82,81 @@ class SolrTable extends AbstractQueryableTable implements TranslatableTable {
    * @param query A string for the query
    * @return Enumerator of results
    */
-  private Enumerable<Object> query(final Properties properties, final List<String> fields,
-                                   final String query, final List<String> order, final List<String> buckets,
+  private Enumerable<Object> query(final Properties properties, final List<Map.Entry<String, Class>> fields,
+                                   final String query, final List<Pair<String, String>> orders, final List<String> buckets,
                                    final List<Pair<String, String>> metricPairs, final String limit) {
     // SolrParams should be a ModifiableParams instead of a map
-    Map<String, String> solrParams = new HashMap<>();
-    solrParams.put(CommonParams.OMIT_HEADER, "true");
+    ModifiableSolrParams solrParams = new ModifiableSolrParams();
+    solrParams.add(CommonParams.OMIT_HEADER, "true");
 
     if (query == null) {
-      solrParams.put(CommonParams.Q, DEFAULT_QUERY);
+      solrParams.add(CommonParams.Q, DEFAULT_QUERY);
     } else {
-      solrParams.put(CommonParams.Q, DEFAULT_QUERY + " AND " + query);
+      solrParams.add(CommonParams.Q, DEFAULT_QUERY + " AND " + query);
     }
 
     // List<String> doesn't have add so must make a new ArrayList
-    List<String> fieldsList = new ArrayList<>(fields);
-    List<String> orderList = new ArrayList<>(order);
-
+    List<String> fieldsList = new ArrayList<>(fields.size());
+    fieldsList.addAll(fields.stream().map(Map.Entry::getKey).collect(Collectors.toList()));
+    List<Pair<String, String>> ordersList = new ArrayList<>(orders);
     List<Metric> metrics = buildMetrics(metricPairs);
+    List<Bucket> bucketsList = buckets.stream().map(Bucket::new).collect(Collectors.toList());
+
+    for(int i = buckets.size()-1; i >= 0; i--) {
+      ordersList.add(0, new Pair<>(buckets.get(i), "asc"));
+    }
+
+    for(Metric metric : metrics) {
+      String metricIdentifier = metric.getIdentifier();
 
-    if (!metrics.isEmpty()) {
-      for(String bucket : buckets) {
-        orderList.add(bucket + " desc");
+      List<Pair<String, String>> newOrders= new ArrayList<>();
+      for(Pair<String, String> order : ordersList) {
+        String column = order.getKey();
+        if(!column.startsWith(metricIdentifier)) {
+          newOrders.add(order);
+        }
       }
+      ordersList = newOrders;
 
-      for(Metric metric : metrics) {
-        List<String> newOrderList = new ArrayList<>();
-        for(String orderItem : orderList) {
-          if(!orderItem.startsWith(metric.getIdentifier())) {
-            newOrderList.add(orderItem);
-          }
+      if(fieldsList.contains(metricIdentifier)) {
+        fieldsList.remove(metricIdentifier);
+      }
+
+      for(String column : metric.getColumns()) {
+        if (!fieldsList.contains(column)) {
+          fieldsList.add(column);
         }
-        orderList = newOrderList;
 
-        for(String column : metric.getColumns()) {
-          if (!fieldsList.contains(column)) {
-            fieldsList.add(column);
-          }
+        Pair<String, String> order = new Pair<>(column, "asc");
+        if(!ordersList.contains(order)) {
+          ordersList.add(order);
         }
       }
     }
 
-    if (orderList.isEmpty()) {
-      orderList.add(DEFAULT_VERSION_FIELD + " desc");
+    ordersList.add(new Pair<>(DEFAULT_VERSION_FIELD, "desc"));
 
-      // Make sure the default sort field is in the field list
-      if (!fieldsList.contains(DEFAULT_VERSION_FIELD)) {
-        fieldsList.add(DEFAULT_VERSION_FIELD);
-      }
+    // Make sure the default sort field is in the field list
+    if (!fieldsList.contains(DEFAULT_VERSION_FIELD)) {
+      fieldsList.add(DEFAULT_VERSION_FIELD);
     }
 
-    if(!orderList.isEmpty()) {
-      solrParams.put(CommonParams.SORT, String.join(",", orderList));
+    if(!ordersList.isEmpty()) {
+      List<String> orderList = new ArrayList<>(ordersList.size());
+      for(Pair<String, String> order : ordersList) {
+        String column = order.getKey();
+        if(!fieldsList.contains(column)) {
+          fieldsList.add(column);
+        }
+        orderList.add(column + " " + order.getValue());
+      }
+      solrParams.add(CommonParams.SORT, String.join(",", orderList));
     }
 
     if (fieldsList.isEmpty()) {
-      solrParams.put(CommonParams.FL, "*");
+      solrParams.add(CommonParams.FL, "*");
     } else {
-      solrParams.put(CommonParams.FL, String.join(",", fieldsList));
+      solrParams.add(CommonParams.FL, String.join(",", fieldsList));
     }
 
     TupleStream tupleStream;
@@ -147,33 +164,24 @@ class SolrTable extends AbstractQueryableTable implements TranslatableTable {
     try {
       if (metrics.isEmpty()) {
         if (limit == null) {
-          solrParams.put(CommonParams.QT, "/export");
+          solrParams.add(CommonParams.QT, "/export");
           tupleStream = new CloudSolrStream(zk, collection, solrParams);
         } else {
-          solrParams.put(CommonParams.ROWS, limit);
+          solrParams.add(CommonParams.ROWS, limit);
           tupleStream = new LimitStream(new CloudSolrStream(zk, collection, solrParams), Integer.parseInt(limit));
         }
       } else {
         Metric[] metricsArray = metrics.toArray(new Metric[metrics.size()]);
-        if(buckets.isEmpty()) {
+        if(bucketsList.isEmpty()) {
           solrParams.remove(CommonParams.FL);
           solrParams.remove(CommonParams.SORT);
           tupleStream = new StatsStream(zk, collection, solrParams, metricsArray);
         } else {
-          List<Bucket> bucketsList = new ArrayList<>();
-          for(String bucket : buckets) {
-            bucketsList.add(new Bucket(bucket));
-          }
-
-          solrParams.put(CommonParams.QT, "/export");
-          for(Metric metric : metrics) {
-            fieldsList.remove(metric.getIdentifier());
-          }
-          solrParams.put(CommonParams.FL, String.join(",", fieldsList));
+          solrParams.add(CommonParams.QT, "/export");
           tupleStream = new CloudSolrStream(zk, collection, solrParams);
           tupleStream = new RollupStream(tupleStream, bucketsList.toArray(new Bucket[bucketsList.size()]), metricsArray);
 
-          String sortDirection = getSortDirection(orderList);
+          String sortDirection = getSortDirection(ordersList);
 
           int numWorkers = Integer.parseInt(properties.getProperty("numWorkers", "1"));
           if(numWorkers > 1) {
@@ -198,9 +206,9 @@ class SolrTable extends AbstractQueryableTable implements TranslatableTable {
             tupleStream = parallelStream;
           }
 
-          if (!sortsEqual(bucketsList, sortDirection, orderList)) {
+          if (!sortsEqual(bucketsList, sortDirection, ordersList)) {
             int limitVal = limit == null ? 100 : Integer.parseInt(limit);
-            StreamComparator comp = getComp(orderList);
+            StreamComparator comp = getComp(ordersList);
             //Rank the Tuples
             //If parallel stream is used ALL the Rolled up tuples from the workers will be ranked
             //Providing a true Top or Bottom.
@@ -209,7 +217,7 @@ class SolrTable extends AbstractQueryableTable implements TranslatableTable {
             // Sort is the same as the same as the underlying stream
             // Only need to limit the result, not Rank the result
             if (limit != null) {
-              solrParams.put(CommonParams.ROWS, limit);
+              solrParams.add(CommonParams.ROWS, limit);
               tupleStream = new LimitStream(new CloudSolrStream(zk, collection, solrParams), Integer.parseInt(limit));
             }
           }
@@ -244,20 +252,20 @@ class SolrTable extends AbstractQueryableTable implements TranslatableTable {
     }
   }
 
-  private boolean sortsEqual(List<Bucket> buckets, String direction, List<String> orderList) {
-    if(buckets.size() != orderList.size()) {
+  private boolean sortsEqual(List<Bucket> buckets, String direction, List<Pair<String, String>> orders) {
+    if(buckets.size() != orders.size()) {
       return false;
     }
 
     for(int i=0; i< buckets.size(); i++) {
       Bucket bucket = buckets.get(i);
-      String orderItem = orderList.get(i);
-      if(!bucket.toString().equals(getSortField(orderItem))) {
+      Pair<String, String> order = orders.get(i);
+      if(!bucket.toString().equals(getSortField(order))) {
         return false;
       }
 
 
-      if(!getSortDirection(orderItem).equalsIgnoreCase(direction)) {
+      if(!getSortDirection(order).equalsIgnoreCase(direction)) {
         return false;
       }
     }
@@ -266,32 +274,30 @@ class SolrTable extends AbstractQueryableTable implements TranslatableTable {
   }
 
 
-  private String getSortDirection(List<String> orderList) {
-    for(String orderItem : orderList) {
-      return getSortDirection(orderItem);
+  private String getSortDirection(List<Pair<String, String>> orders) {
+    for(Pair<String, String> order : orders) {
+      return getSortDirection(order);
     }
 
     return "asc";
   }
 
-  private String getSortField(String orderItem) {
-    String[] orderParts = orderItem.split(" ", 2);
-    return orderParts[0];
+  private String getSortField(Pair<String, String> order) {
+    return order.getKey();
   }
 
-  private String getSortDirection(String orderItem) {
-    String[] orderParts = orderItem.split(" ", 2);
-    String direction = orderParts[1];
+  private String getSortDirection(Pair<String, String> order) {
+    String direction = order.getValue();
     return direction == null ? "asc" : direction;
   }
 
-  private StreamComparator getComp(List<String> orderList) {
-    FieldComparator[] comps = new FieldComparator[orderList.size()];
-    for(int i = 0; i < orderList.size(); i++) {
-      String orderItem = orderList.get(i);
-      String direction = getSortDirection(orderItem);
+  private StreamComparator getComp(List<Pair<String, String>> orders) {
+    FieldComparator[] comps = new FieldComparator[orders.size()];
+    for(int i = 0; i < orders.size(); i++) {
+      Pair<String, String> order = orders.get(i);
+      String direction = getSortDirection(order);
       ComparatorOrder comparatorOrder = ComparatorOrder.fromString(direction);
-      String sortKey = getSortField(orderItem);
+      String sortKey = getSortField(order);
       comps[i] = new FieldComparator(sortKey, comparatorOrder);
     }
 
@@ -304,9 +310,7 @@ class SolrTable extends AbstractQueryableTable implements TranslatableTable {
 
   private List<Metric> buildMetrics(List<Pair<String, String>> metricPairs) {
     List<Metric> metrics = new ArrayList<>(metricPairs.size());
-    for(Pair<String, String> metricPair : metricPairs) {
-      metrics.add(getMetric(metricPair));
-    }
+    metrics.addAll(metricPairs.stream().map(this::getMetric).collect(Collectors.toList()));
     return metrics;
   }
 
@@ -362,8 +366,8 @@ class SolrTable extends AbstractQueryableTable implements TranslatableTable {
      * @see SolrMethod#SOLR_QUERYABLE_QUERY
      */
     @SuppressWarnings("UnusedDeclaration")
-    public Enumerable<Object> query(List<String> fields, String query, List<String> order, List<String> buckets,
-                                    List<Pair<String, String>> metricPairs, String limit) {
+    public Enumerable<Object> query(List<Map.Entry<String, Class>> fields, String query, List<Pair<String, String>> order,
+                                    List<String> buckets, List<Pair<String, String>> metricPairs, String limit) {
       return getTable().query(getProperties(), fields, query, order, buckets, metricPairs, limit);
     }
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/073fecfa/solr/core/src/java/org/apache/solr/handler/sql/SolrToEnumerableConverter.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/sql/SolrToEnumerableConverter.java b/solr/core/src/java/org/apache/solr/handler/sql/SolrToEnumerableConverter.java
index e6aea05..6737977 100644
--- a/solr/core/src/java/org/apache/solr/handler/sql/SolrToEnumerableConverter.java
+++ b/solr/core/src/java/org/apache/solr/handler/sql/SolrToEnumerableConverter.java
@@ -31,6 +31,7 @@ import org.apache.calcite.runtime.Hook;
 import org.apache.calcite.util.BuiltInMethod;
 import org.apache.calcite.util.Pair;
 
+import java.util.AbstractList;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -61,15 +62,27 @@ class SolrToEnumerableConverter extends ConverterImpl implements EnumerableRel {
     final RelDataType rowType = getRowType();
     final PhysType physType = PhysTypeImpl.of(implementor.getTypeFactory(), rowType, pref.prefer(JavaRowFormat.ARRAY));
     final Expression table = list.append("table", solrImplementor.table.getExpression(SolrTable.SolrQueryable.class));
-    final Expression fields = list.append("fields",
-        constantArrayList(generateFields(SolrRules.solrFieldNames(rowType), solrImplementor.fieldMappings), String.class));
+    final Expression fields =
+        list.append("fields",
+            constantArrayList(
+                Pair.zip(generateFields(SolrRules.solrFieldNames(rowType), solrImplementor.fieldMappings),
+                    new AbstractList<Class>() {
+                      @Override public Class get(int index) {
+                        return physType.fieldClass(index);
+                      }
+
+                      @Override public int size() {
+                        return rowType.getFieldCount();
+                      }
+                    }),
+                Pair.class));
     final Expression query = list.append("query", Expressions.constant(solrImplementor.query, String.class));
-    final Expression order = list.append("order", constantArrayList(solrImplementor.order, String.class));
+    final Expression orders = list.append("orders", constantArrayList(solrImplementor.orders, Pair.class));
     final Expression buckets = list.append("buckets", constantArrayList(solrImplementor.buckets, String.class));
     final Expression metricPairs = list.append("metricPairs", constantArrayList(solrImplementor.metricPairs, Pair.class));
     final Expression limit = list.append("limit", Expressions.constant(solrImplementor.limitValue));
     Expression enumerable = list.append("enumerable", Expressions.call(table, SolrMethod.SOLR_QUERYABLE_QUERY.method,
-        fields, query, order, buckets, metricPairs, limit));
+        fields, query, orders, buckets, metricPairs, limit));
     Hook.QUERY_PLAN.run(query);
     list.add(Expressions.return_(null, enumerable));
     return implementor.result(physType, list.toBlock());
@@ -81,12 +94,25 @@ class SolrToEnumerableConverter extends ConverterImpl implements EnumerableRel {
     } else {
       List<String> fields = new ArrayList<>();
       for(String field : queryFields) {
-        fields.add(fieldMappings.getOrDefault(field, field));
+        fields.add(getField(fieldMappings, field));
       }
       return fields;
     }
   }
 
+  private String getField(Map<String, String> fieldMappings, String field) {
+    String retField = field;
+    while(fieldMappings.containsKey(field)) {
+      field = fieldMappings.getOrDefault(field, retField);
+      if(retField.equals(field)) {
+        break;
+      } else {
+        retField = field;
+      }
+    }
+    return retField;
+  }
+
   /**
    * E.g. {@code constantArrayList("x", "y")} returns
    * "Arrays.asList('x', 'y')".