spuerx 9 роки тому
батько
коміт
99edf27692
28 змінених файлів з 886 додано та 460 видалено
  1. 6 0
      pom.xml
  2. 32 0
      src/main/java/org/elasticsearch/dsl/bean/AtomFilter.java
  3. 55 16
      src/main/java/org/elasticsearch/dsl/bean/ElasticSqlParseResult.java
  4. 9 9
      src/main/java/org/elasticsearch/dsl/bean/ElasticSqlQueryFields.java
  5. 22 0
      src/main/java/org/elasticsearch/dsl/bean/QueryFieldReferenceNode.java
  6. 27 0
      src/main/java/org/elasticsearch/dsl/bean/QueryFieldReferencePath.java
  7. 29 0
      src/main/java/org/elasticsearch/dsl/bean/RangeSegment.java
  8. 22 16
      src/main/java/org/elasticsearch/dsl/bean/SqlCondition.java
  9. 2 0
      src/main/java/org/elasticsearch/dsl/enums/QueryFieldType.java
  10. 5 0
      src/main/java/org/elasticsearch/dsl/enums/SQLBoolOperator.java
  11. 0 1
      src/main/java/org/elasticsearch/dsl/enums/SQLConditionOperator.java
  12. 6 0
      src/main/java/org/elasticsearch/dsl/enums/SQLConditionType.java
  13. 45 0
      src/main/java/org/elasticsearch/dsl/enums/SortOption.java
  14. 7 9
      src/main/java/org/elasticsearch/dsl/parser/ElasticSql2DslParser.java
  15. 9 5
      src/main/java/org/elasticsearch/dsl/parser/helper/ElasticSqlArgTransferHelper.java
  16. 0 166
      src/main/java/org/elasticsearch/dsl/parser/helper/ElasticSqlIdentifierHelper.java
  17. 41 47
      src/main/java/org/elasticsearch/dsl/parser/helper/ElasticSqlMethodInvokeHelper.java
  18. 1 1
      src/main/java/org/elasticsearch/dsl/parser/listener/ParseActionListener.java
  19. 1 1
      src/main/java/org/elasticsearch/dsl/parser/listener/ParseActionListenerAdapter.java
  20. 168 0
      src/main/java/org/elasticsearch/dsl/parser/syntax/QueryFieldParser.java
  21. 5 2
      src/main/java/org/elasticsearch/dsl/parser/syntax/QueryFromParser.java
  22. 171 0
      src/main/java/org/elasticsearch/dsl/parser/syntax/QueryGroupByParser.java
  23. 1 1
      src/main/java/org/elasticsearch/dsl/parser/syntax/QueryLimitSizeParser.java
  24. 35 86
      src/main/java/org/elasticsearch/dsl/parser/syntax/QueryOrderConditionParser.java
  25. 1 1
      src/main/java/org/elasticsearch/dsl/parser/syntax/QueryRoutingValParser.java
  26. 72 17
      src/main/java/org/elasticsearch/dsl/parser/syntax/QuerySelectFieldListParser.java
  27. 113 81
      src/main/java/org/elasticsearch/dsl/parser/syntax/QueryWhereConditionParser.java
  28. 1 1
      src/test/java/org/elasticsearch/SqlParserSelectFieldTest.java

+ 6 - 0
pom.xml

@@ -52,6 +52,12 @@
         </dependency>
 
         <dependency>
+            <groupId>joda-time</groupId>
+            <artifactId>joda-time</artifactId>
+            <version>2.3</version>
+        </dependency>
+
+        <dependency>
             <groupId>com.google.code.gson</groupId>
             <artifactId>gson</artifactId>
             <version>2.3.1</version>

+ 32 - 0
src/main/java/org/elasticsearch/dsl/bean/AtomFilter.java

@@ -0,0 +1,32 @@
+package org.elasticsearch.dsl.bean;
+
+import org.elasticsearch.index.query.FilterBuilder;
+
+public class AtomFilter {
+    private FilterBuilder filter;
+    private Boolean isNestedFilter;
+    private String nestedFilterPathContext;
+
+    public AtomFilter(FilterBuilder filter) {
+        this.filter = filter;
+        this.isNestedFilter = Boolean.FALSE;
+    }
+
+    public AtomFilter(FilterBuilder filter, String nestedFilterPathContext) {
+        this.filter = filter;
+        this.isNestedFilter = Boolean.TRUE;
+        this.nestedFilterPathContext = nestedFilterPathContext;
+    }
+
+    public FilterBuilder getFilter() {
+        return filter;
+    }
+
+    public Boolean getNestedFilter() {
+        return isNestedFilter;
+    }
+
+    public String getNestedFilterPathContext() {
+        return nestedFilterPathContext;
+    }
+}

+ 55 - 16
src/main/java/org/elasticsearch/dsl/bean/ElasticSqlParseResult.java

@@ -6,11 +6,13 @@ import org.elasticsearch.action.search.SearchRequestBuilder;
 import org.elasticsearch.client.Client;
 import org.elasticsearch.index.query.BoolFilterBuilder;
 import org.elasticsearch.index.query.QueryBuilders;
+import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
+import org.elasticsearch.search.aggregations.AggregationBuilder;
 import org.elasticsearch.search.sort.SortBuilder;
 import org.elasticsearch.util.ElasticMockClient;
 
 import java.util.List;
-import java.util.function.Consumer;
+
 
 public class ElasticSqlParseResult {
     /*取数开始位置*/
@@ -18,7 +20,7 @@ public class ElasticSqlParseResult {
     /*取数大小*/
     private int size = 15;
     /*查询索引*/
-    private String index;
+    private List<String> indices;
     /*查询文档类*/
     private String type;
     /*查询索引别名*/
@@ -32,6 +34,10 @@ public class ElasticSqlParseResult {
     /*SQL的order by条件*/
     private transient List<SortBuilder> orderBy;
 
+    private transient boolean isTopStatsAgg = false;
+
+    private transient List<AbstractAggregationBuilder> groupBy;
+
     public List<String> getQueryFieldList() {
         return queryFieldList;
     }
@@ -40,12 +46,12 @@ public class ElasticSqlParseResult {
         this.queryFieldList = queryFieldList;
     }
 
-    public String getIndex() {
-        return index;
+    public List<String> getIndices() {
+        return indices;
     }
 
-    public void setIndex(String index) {
-        this.index = index;
+    public void setIndices(List<String> indices) {
+        this.indices = indices;
     }
 
     public String getType() {
@@ -104,12 +110,28 @@ public class ElasticSqlParseResult {
         this.routingBy = routingBy;
     }
 
+    public void setGroupBy(List<AbstractAggregationBuilder> groupBy) {
+        this.groupBy = groupBy;
+    }
+
+    public List<AbstractAggregationBuilder> getGroupBy() {
+        return groupBy;
+    }
+
+    public void setTopStatsAgg() {
+        this.isTopStatsAgg = true;
+    }
+
+    public boolean getIsTopStatsAgg() {
+        return isTopStatsAgg;
+    }
+
     public SearchRequestBuilder toRequest(Client client) {
-        final SearchRequestBuilder requestBuilder = new SearchRequestBuilder(client);
+        SearchRequestBuilder requestBuilder = new SearchRequestBuilder(client);
         requestBuilder.setFrom(from).setSize(size);
 
-        if (StringUtils.isNotBlank(index)) {
-            requestBuilder.setIndices(index);
+        if (CollectionUtils.isNotEmpty(indices)) {
+            requestBuilder.setIndices(indices.toArray(new String[indices.size()]));
         }
 
         if (StringUtils.isNotBlank(type)) {
@@ -123,12 +145,9 @@ public class ElasticSqlParseResult {
         }
 
         if (CollectionUtils.isNotEmpty(orderBy)) {
-            orderBy.stream().forEach(new Consumer<SortBuilder>() {
-                @Override
-                public void accept(SortBuilder sortBuilder) {
-                    requestBuilder.addSort(sortBuilder);
-                }
-            });
+            for (SortBuilder sortBuilder : orderBy) {
+                requestBuilder.addSort(sortBuilder);
+            }
         }
 
         if (CollectionUtils.isNotEmpty(queryFieldList)) {
@@ -138,6 +157,26 @@ public class ElasticSqlParseResult {
         if (CollectionUtils.isNotEmpty(routingBy)) {
             requestBuilder.setRouting(routingBy.toArray(new String[routingBy.size()]));
         }
+
+        if (CollectionUtils.isNotEmpty(groupBy)) {
+            if (!getIsTopStatsAgg()) {
+                AggregationBuilder preAgg = null;
+                for (AbstractAggregationBuilder aggItem : groupBy) {
+                    if (preAgg == null) {
+                        preAgg = (AggregationBuilder) aggItem;
+                        continue;
+                    }
+                    preAgg.subAggregation(aggItem);
+                    preAgg = (AggregationBuilder) aggItem;
+                }
+                requestBuilder.addAggregation(groupBy.get(0));
+            } else {
+                for (AbstractAggregationBuilder aggItem : groupBy) {
+                    requestBuilder.addAggregation(aggItem);
+                }
+            }
+        }
+
         return requestBuilder;
     }
 
@@ -152,6 +191,6 @@ public class ElasticSqlParseResult {
     @Override
     public String toString() {
         String ptn = "index:%s,type:%s,query_as:%s,from:%s,size:%s,routing:%s,dsl:%s";
-        return String.format(ptn, index, type, queryAs, from, size, routingBy != null ? routingBy.toString() : "[]", toDsl());
+        return String.format(ptn, indices, type, queryAs, from, size, routingBy != null ? routingBy.toString() : "[]", toDsl());
     }
 }

+ 9 - 9
src/main/java/org/elasticsearch/dsl/bean/ElasticSqlQueryFields.java

@@ -7,25 +7,25 @@ public class ElasticSqlQueryFields {
         // private constructor
     }
 
-    public static ElasticSqlQueryField newMatchAllField(String prefixPath) {
-        String matchAllField = String.format("%s.%s", prefixPath, "*");
-        return new ElasticSqlQueryField(null, matchAllField, matchAllField, QueryFieldType.MatchAllField);
+    public static ElasticSqlQueryField newSqlSelectField(String fullPathFieldName) {
+        return new ElasticSqlQueryField(null, fullPathFieldName, fullPathFieldName, QueryFieldType.SqlSelectField);
     }
 
-    public static ElasticSqlQueryField newRootDocQueryField(String rootDocFieldName) {
-        return new ElasticSqlQueryField(null, rootDocFieldName, rootDocFieldName, QueryFieldType.RootDocField);
+    public static ElasticSqlQueryField newMatchAllRootDocField() {
+        return new ElasticSqlQueryField(null, "*", "*", QueryFieldType.MatchAllField);
     }
 
-    public static ElasticSqlQueryField newInnerDocQueryField(String innerDocQueryFieldFullName) {
-        return new ElasticSqlQueryField(null, innerDocQueryFieldFullName, innerDocQueryFieldFullName, QueryFieldType.InnerDocField);
+    public static ElasticSqlQueryField newRootDocQueryField(String rootDocFieldName) {
+        return new ElasticSqlQueryField(null, rootDocFieldName, rootDocFieldName, QueryFieldType.RootDocField);
     }
 
     public static ElasticSqlQueryField newInnerDocQueryField(String innerDocFieldPrefix, String innerDocFieldName) {
         String innerDocQueryFieldFullName = String.format("%s.%s", innerDocFieldPrefix, innerDocFieldName);
-        return newInnerDocQueryField(innerDocQueryFieldFullName);
+        return new ElasticSqlQueryField(null, innerDocFieldName, innerDocQueryFieldFullName, QueryFieldType.InnerDocField);
     }
 
     public static ElasticSqlQueryField newNestedDocQueryField(String nestedDocContextPath, String simpleQueryFieldName) {
-        return new ElasticSqlQueryField(nestedDocContextPath, simpleQueryFieldName, String.format("%s.%s", nestedDocContextPath, simpleQueryFieldName), QueryFieldType.NestedDocField);
+        String nestedDocQueryFieldFullName = String.format("%s.%s", nestedDocContextPath, simpleQueryFieldName);
+        return new ElasticSqlQueryField(nestedDocContextPath, simpleQueryFieldName, nestedDocQueryFieldFullName, QueryFieldType.NestedDocField);
     }
 }

+ 22 - 0
src/main/java/org/elasticsearch/dsl/bean/QueryFieldReferenceNode.java

@@ -0,0 +1,22 @@
+package org.elasticsearch.dsl.bean;
+
+public class QueryFieldReferenceNode {
+
+    private boolean isNestedDocReference;
+
+    private String referenceNodeName;
+
+
+    public QueryFieldReferenceNode(String referenceNodeName, boolean isNestedDocReference) {
+        this.isNestedDocReference = isNestedDocReference;
+        this.referenceNodeName = referenceNodeName;
+    }
+
+    public boolean isNestedDocReference() {
+        return isNestedDocReference;
+    }
+
+    public String getReferenceNodeName() {
+        return referenceNodeName;
+    }
+}

+ 27 - 0
src/main/java/org/elasticsearch/dsl/bean/QueryFieldReferencePath.java

@@ -0,0 +1,27 @@
+package org.elasticsearch.dsl.bean;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import org.apache.commons.collections.CollectionUtils;
+
+import java.util.Collections;
+import java.util.List;
+
+public class QueryFieldReferencePath {
+
+    private List<QueryFieldReferenceNode> referenceNodes;
+
+    public void addReferenceNode(QueryFieldReferenceNode referenceNode) {
+        if (referenceNodes == null) {
+            referenceNodes = Lists.newLinkedList();
+        }
+        referenceNodes.add(referenceNode);
+    }
+
+    public List<QueryFieldReferenceNode> getReferenceNodes() {
+        if (CollectionUtils.isEmpty(referenceNodes)) {
+            return Collections.emptyList();
+        }
+        return ImmutableList.copyOf(referenceNodes);
+    }
+}

+ 29 - 0
src/main/java/org/elasticsearch/dsl/bean/RangeSegment.java

@@ -0,0 +1,29 @@
+package org.elasticsearch.dsl.bean;
+
+public class RangeSegment {
+    private Object from;
+    private Object to;
+    private SegmentType segmentType;
+
+    public RangeSegment(Object from, Object to, SegmentType segmentType) {
+        this.from = from;
+        this.to = to;
+        this.segmentType = segmentType;
+    }
+
+    public Object getFrom() {
+        return from;
+    }
+
+    public Object getTo() {
+        return to;
+    }
+
+    public SegmentType getSegmentType() {
+        return segmentType;
+    }
+
+    public enum SegmentType {
+        Date, Numeric
+    }
+}

+ 22 - 16
src/main/java/org/elasticsearch/dsl/bean/SqlCondition.java

@@ -1,39 +1,45 @@
 package org.elasticsearch.dsl.bean;
 
-import com.alibaba.druid.sql.ast.expr.SQLBinaryOperator;
 import com.google.common.collect.Lists;
-import org.elasticsearch.index.query.FilterBuilder;
+import org.elasticsearch.dsl.enums.SQLBoolOperator;
+import org.elasticsearch.dsl.enums.SQLConditionType;
 
 import java.util.List;
 
-public class SqlCondition {
-    //是否AND/OR运算
-    private boolean isAndOr = false;
+public class SQLCondition {
+    //条件类型
+    private SQLConditionType conditionType;
     //运算符
-    private SQLBinaryOperator operator;
+    private SQLBoolOperator operator;
     //条件集合
-    private List<FilterBuilder> filterList;
+    private List<AtomFilter> filterList;
 
-    public SqlCondition(FilterBuilder atomFilter) {
-        filterList = Lists.newArrayList(atomFilter);
-        isAndOr = false;
+    public SQLCondition(AtomFilter atomFilter) {
+        this.filterList = Lists.newArrayList(atomFilter);
+        this.conditionType = SQLConditionType.Atom;
     }
 
-    public SqlCondition(List<FilterBuilder> filterList, SQLBinaryOperator operator) {
+    public SQLCondition(AtomFilter atomFilter, SQLConditionType SQLConditionType) {
+        this.filterList = Lists.newArrayList(atomFilter);
+        this.conditionType = SQLConditionType;
+    }
+
+    public SQLCondition(List<AtomFilter> filterList, SQLBoolOperator operator) {
         this.filterList = filterList;
-        isAndOr = true;
         this.operator = operator;
+        this.conditionType = SQLConditionType.Combine;
     }
 
-    public boolean isAndOr() {
-        return isAndOr;
+
+    public SQLConditionType getSQLConditionType() {
+        return conditionType;
     }
 
-    public SQLBinaryOperator getOperator() {
+    public SQLBoolOperator getOperator() {
         return operator;
     }
 
-    public List<FilterBuilder> getFilterList() {
+    public List<AtomFilter> getFilterList() {
         return filterList;
     }
 }

+ 2 - 0
src/main/java/org/elasticsearch/dsl/enums/QueryFieldType.java

@@ -2,6 +2,8 @@ package org.elasticsearch.dsl.enums;
 
 public enum QueryFieldType {
     MatchAllField,
+    SqlSelectField,
+
     RootDocField,
     InnerDocField,
     NestedDocField,

+ 5 - 0
src/main/java/org/elasticsearch/dsl/enums/SQLBoolOperator.java

@@ -0,0 +1,5 @@
+package org.elasticsearch.dsl.enums;
+
+public enum SQLBoolOperator {
+    AND, OR
+}

+ 0 - 1
src/main/java/org/elasticsearch/dsl/enums/SQLConditionOperator.java

@@ -1,6 +1,5 @@
 package org.elasticsearch.dsl.enums;
 
-
 public enum SQLConditionOperator {
     Equality,
     NotEqual,

+ 6 - 0
src/main/java/org/elasticsearch/dsl/enums/SQLConditionType.java

@@ -0,0 +1,6 @@
+package org.elasticsearch.dsl.enums;
+
+
+public enum SQLConditionType {
+    Atom, Combine
+}

+ 45 - 0
src/main/java/org/elasticsearch/dsl/enums/SortOption.java

@@ -0,0 +1,45 @@
+package org.elasticsearch.dsl.enums;
+
+public enum SortOption {
+    SUM {
+        @Override
+        public String mode() {
+            return "sum";
+        }
+    },
+    MIN {
+        @Override
+        public String mode() {
+            return "min";
+        }
+    },
+    MAX {
+        @Override
+        public String mode() {
+            return "max";
+        }
+    },
+    AVG {
+        @Override
+        public String mode() {
+            return "avg";
+        }
+    };
+
+    public abstract String mode();
+
+    @Override
+    public String toString() {
+        return mode();
+    }
+
+    public static SortOption get(String mode) {
+        SortOption op = null;
+        for (SortOption option : SortOption.values()) {
+            if (option.mode().equalsIgnoreCase(mode)) {
+                op = option;
+            }
+        }
+        return op;
+    }
+}

+ 7 - 9
src/main/java/org/elasticsearch/dsl/parser/ElasticSql2DslParser.java

@@ -5,9 +5,9 @@ import com.alibaba.druid.sql.ast.expr.SQLQueryExpr;
 import com.alibaba.druid.sql.parser.ParserException;
 import com.alibaba.druid.sql.parser.Token;
 import com.google.common.collect.ImmutableList;
-import org.elasticsearch.dsl.exception.ElasticSql2DslException;
 import org.elasticsearch.dsl.bean.ElasticDslContext;
 import org.elasticsearch.dsl.bean.ElasticSqlParseResult;
+import org.elasticsearch.dsl.exception.ElasticSql2DslException;
 import org.elasticsearch.dsl.parser.listener.ParseActionListener;
 import org.elasticsearch.dsl.parser.listener.ParseActionListenerAdapter;
 import org.elasticsearch.dsl.parser.syntax.*;
@@ -17,7 +17,6 @@ import org.elasticsearch.sql.ElasticSqlSelectQueryBlock;
 import java.lang.reflect.Array;
 import java.util.Collection;
 import java.util.List;
-import java.util.function.Consumer;
 
 public class ElasticSql2DslParser {
 
@@ -46,12 +45,9 @@ public class ElasticSql2DslParser {
 
         final ElasticDslContext elasticDslContext = new ElasticDslContext(queryExpr, sqlArgs);
         if (queryExpr.getSubQuery().getQuery() instanceof ElasticSqlSelectQueryBlock) {
-            buildSqlParserChain(parseActionListener).stream().forEach(new Consumer<QueryParser>() {
-                @Override
-                public void accept(QueryParser sqlParser) {
-                    sqlParser.parse(elasticDslContext);
-                }
-            });
+            for (QueryParser sqlParser : buildSqlParserChain(parseActionListener)) {
+                sqlParser.parse(elasticDslContext);
+            }
         } else {
             throw new ElasticSql2DslException("[syntax error] Sql only support Select Sql");
         }
@@ -64,7 +60,7 @@ public class ElasticSql2DslParser {
         }
 
         if (!(sqlQueryExpr instanceof SQLQueryExpr)) {
-            throw new ElasticSql2DslException("[syntax error] Sql is not select syntax");
+            throw new ElasticSql2DslException("[syntax error] Sql is not select sql");
         }
 
         if (sqlArgs != null && sqlArgs.length > 0) {
@@ -87,6 +83,8 @@ public class ElasticSql2DslParser {
                 new QueryOrderConditionParser(parseActionListener),
                 //解析路由参数
                 new QueryRoutingValParser(parseActionListener),
+                //解析分组统计
+                new QueryGroupByParser(parseActionListener),
                 //解析SQL查询指定的字段
                 new QuerySelectFieldListParser(parseActionListener),
                 //解析SQL的分页条数

+ 9 - 5
src/main/java/org/elasticsearch/dsl/parser/helper/ElasticSqlArgTransferHelper.java

@@ -11,12 +11,16 @@ public class ElasticSqlArgTransferHelper {
     public static Object[] transferSqlArgs(List<SQLExpr> exprList, Object[] sqlArgs) {
         Object[] values = new Object[exprList.size()];
         for (int idx = 0; idx < exprList.size(); idx++) {
-            values[idx] = transferSqlArg(exprList.get(idx), sqlArgs);
+            values[idx] = transferSqlArg(exprList.get(idx), sqlArgs, true);
         }
         return values;
     }
 
     public static Object transferSqlArg(SQLExpr expr, Object[] sqlArgs) {
+        return transferSqlArg(expr, sqlArgs, true);
+    }
+
+    public static Object transferSqlArg(SQLExpr expr, Object[] sqlArgs, boolean recognizeDateArg) {
         if (expr instanceof SQLVariantRefExpr) {
             SQLVariantRefExpr varRefExpr = (SQLVariantRefExpr) expr;
             if (sqlArgs == null || sqlArgs.length == 0) {
@@ -26,7 +30,7 @@ public class ElasticSqlArgTransferHelper {
                 throw new ElasticSql2DslException("[syntax error] Sql args out of index: " + varRefExpr.getIndex());
             }
             //解析date类型
-            if (ElasticSqlDateParseHelper.isDateArgObjectValue(sqlArgs[varRefExpr.getIndex()])) {
+            if (recognizeDateArg && ElasticSqlDateParseHelper.isDateArgObjectValue(sqlArgs[varRefExpr.getIndex()])) {
                 return ElasticSqlDateParseHelper.formatDefaultEsDateObjectValue(sqlArgs[varRefExpr.getIndex()]);
             }
             return sqlArgs[varRefExpr.getIndex()];
@@ -40,7 +44,7 @@ public class ElasticSqlArgTransferHelper {
         if (expr instanceof SQLCharExpr) {
             Object textObject = ((SQLCharExpr) expr).getValue();
             //解析date类型
-            if (textObject instanceof String && ElasticSqlDateParseHelper.isDateArgStringValue((String) textObject)) {
+            if (recognizeDateArg && (textObject instanceof String) && ElasticSqlDateParseHelper.isDateArgStringValue((String) textObject)) {
                 return ElasticSqlDateParseHelper.formatDefaultEsDateStringValue((String) textObject);
             }
             return textObject;
@@ -51,8 +55,8 @@ public class ElasticSqlArgTransferHelper {
             //解析date函数
             if (ElasticSqlDateParseHelper.isDateMethod(methodExpr)) {
                 ElasticSqlMethodInvokeHelper.checkDateMethod(methodExpr);
-                String patternArg = (String) ElasticSqlArgTransferHelper.transferSqlArg(methodExpr.getParameters().get(0), sqlArgs);
-                String timeValArg = (String) ElasticSqlArgTransferHelper.transferSqlArg(methodExpr.getParameters().get(1), sqlArgs);
+                String patternArg = (String) ElasticSqlArgTransferHelper.transferSqlArg(methodExpr.getParameters().get(0), sqlArgs, false);
+                String timeValArg = (String) ElasticSqlArgTransferHelper.transferSqlArg(methodExpr.getParameters().get(1), sqlArgs, false);
                 return ElasticSqlDateParseHelper.formatDefaultEsDate(patternArg, timeValArg);
             }
         }

+ 0 - 166
src/main/java/org/elasticsearch/dsl/parser/helper/ElasticSqlIdentifierHelper.java

@@ -1,166 +0,0 @@
-package org.elasticsearch.dsl.parser.helper;
-
-import com.alibaba.druid.sql.ast.SQLExpr;
-import com.alibaba.druid.sql.ast.expr.SQLAllColumnExpr;
-import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
-import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;
-import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr;
-import com.google.common.collect.Lists;
-import org.apache.commons.collections.CollectionUtils;
-import org.apache.commons.lang.StringUtils;
-import org.elasticsearch.dsl.bean.ElasticSqlQueryField;
-import org.elasticsearch.dsl.bean.ElasticSqlQueryFields;
-import org.elasticsearch.dsl.enums.QueryFieldType;
-import org.elasticsearch.dsl.exception.ElasticSql2DslException;
-
-import java.util.List;
-
-public class ElasticSqlIdentifierHelper {
-
-    public static ElasticSqlQueryField parseSqlQueryField(SQLExpr queryFieldExpr, String queryAs,
-                                                          SQLFlatFieldFunc flatFieldFunc, SQLNestedFieldFunc nestedFieldFunc) {
-
-        return null;
-    }
-
-    /**
-     * 字段标识符处理
-     *
-     * @param propertyNameExpr 待处理字段表达式
-     * @param queryAsAlias     文档类型别名
-     * @param flatFieldFunc    非内嵌类型处理策略(inner/property)
-     * @param nestedFieldFunc  内嵌类型处理逻辑(nested)
-     * @return 标识符类型
-     */
-    public static ElasticSqlQueryField parseSqlIdentifier(SQLExpr propertyNameExpr, String queryAsAlias,
-                                                          SQLFlatFieldFunc flatFieldFunc,
-                                                          SQLNestedFieldFunc nestedFieldFunc) {
-        StringBuilder propertyPathBuilder = new StringBuilder();
-        StringBuilder propertyNameBuilder = new StringBuilder();
-        if (propertyNameExpr instanceof SQLMethodInvokeExpr) {
-            //如果指定是inner doc类型
-            SQLMethodInvokeExpr innerObjExpr = (SQLMethodInvokeExpr) propertyNameExpr;
-            if (ElasticSqlMethodInvokeHelper.INNER_DOC_METHOD.equalsIgnoreCase(innerObjExpr.getMethodName())) {
-                ElasticSqlMethodInvokeHelper.checkInnerDocMethod(innerObjExpr);
-
-                ElasticSqlIdentifierHelper.parseSqlIdf(innerObjExpr.getParameters().get(0), queryAsAlias, new SQLFlatFieldFunc() {
-                    @Override
-                    public void parse(String flatFieldName) {
-                        throw new ElasticSql2DslException("[syntax error] The arg of method inner_doc must contain property reference path");
-                    }
-                }, new SQLNestedFieldFunc() {
-                    @Override
-                    public void parse(String nestedDocPath, String fieldName) {
-                        propertyPathBuilder.append(nestedDocPath);
-                        propertyNameBuilder.append(fieldName);
-                        flatFieldFunc.parse(String.format("%s.%s", nestedDocPath, fieldName));
-                    }
-                });
-                //return new ElasticSqlQueryField(propertyPathBuilder.toString(), propertyNameBuilder.toString(), QueryFieldType.InnerDocField);
-                return ElasticSqlQueryFields.newInnerDocQueryField(propertyPathBuilder.toString(), propertyNameBuilder.toString());
-            }
-
-            //如果执行是nested doc类型
-            if (ElasticSqlMethodInvokeHelper.NESTED_DOC_METHOD.equalsIgnoreCase(innerObjExpr.getMethodName())) {
-                ElasticSqlMethodInvokeHelper.checkNestedDocMethod(innerObjExpr);
-
-                ElasticSqlIdentifierHelper.parseSqlIdf(innerObjExpr.getParameters().get(0), queryAsAlias, new SQLFlatFieldFunc() {
-                    @Override
-                    public void parse(String flatFieldName) {
-                        throw new ElasticSql2DslException("[syntax error] The arg of method nested_doc must contain property reference path");
-                    }
-                }, new SQLNestedFieldFunc() {
-                    @Override
-                    public void parse(String nestedDocPath, String fieldName) {
-                        propertyPathBuilder.append(nestedDocPath);
-                        propertyNameBuilder.append(fieldName);
-                        nestedFieldFunc.parse(nestedDocPath, fieldName);
-                    }
-                });
-                //return new ElasticSqlQueryField(propertyPathBuilder.toString(), propertyNameBuilder.toString(), QueryFieldType.NestedDocField);
-                return ElasticSqlQueryFields.newNestedDocQueryField(propertyPathBuilder.toString(), propertyNameBuilder.toString());
-            }
-
-            throw new ElasticSql2DslException("[syntax error] Sql identifier method only support nested_doc and inner_doc");
-        }
-
-        //默认按照inner doc或者property name来处理
-        final List<Boolean> isInnerDocProperty = Lists.newLinkedList();
-        ElasticSqlIdentifierHelper.parseSqlIdf(propertyNameExpr, queryAsAlias, new SQLFlatFieldFunc() {
-            @Override
-            public void parse(String flatFieldName) {
-                isInnerDocProperty.add(Boolean.FALSE);
-                propertyNameBuilder.append(flatFieldName);
-                flatFieldFunc.parse(flatFieldName);
-            }
-        }, new SQLNestedFieldFunc() {
-            @Override
-            public void parse(String nestedDocPath, String fieldName) {
-                isInnerDocProperty.add(Boolean.TRUE);
-                propertyPathBuilder.append(nestedDocPath);
-                propertyNameBuilder.append(fieldName);
-                flatFieldFunc.parse(String.format("%s.%s", nestedDocPath, fieldName));
-            }
-        });
-        if (CollectionUtils.isNotEmpty(isInnerDocProperty)) {
-            if (isInnerDocProperty.get(0)) {
-                //return new ElasticSqlQueryField(propertyPathBuilder.toString(), propertyNameBuilder.toString(), QueryFieldType.InnerDocField);
-                return ElasticSqlQueryFields.newInnerDocQueryField(propertyPathBuilder.toString(), propertyNameBuilder.toString());
-            }
-            //return new ElasticSqlQueryField(propertyNameBuilder.toString());
-            return ElasticSqlQueryFields.newRootDocQueryField(propertyNameBuilder.toString());
-        }
-        //return new ElasticSqlQueryField(propertyPathBuilder.toString(), propertyNameBuilder.toString(), QueryFieldType.MatchAllField);
-        return ElasticSqlQueryFields.newMatchAllField(propertyPathBuilder.toString());
-    }
-
-    private static void parseSqlIdf(final SQLExpr propertyNameExpr, final String queryAsAlias,
-                                    final SQLFlatFieldFunc singlePropertyFunc, final SQLNestedFieldFunc pathPropertyFunc) {
-        //查询所有字段
-        if (propertyNameExpr instanceof SQLAllColumnExpr) {
-            return;
-        }
-
-        if (propertyNameExpr instanceof SQLIdentifierExpr) {
-            singlePropertyFunc.parse(((SQLIdentifierExpr) propertyNameExpr).getName());
-        } else if (propertyNameExpr instanceof SQLPropertyExpr) {
-            SQLPropertyExpr propertyExpr = (SQLPropertyExpr) propertyNameExpr;
-            StringBuffer ownerIdfNameBuilder = new StringBuffer();
-            propertyExpr.getOwner().output(ownerIdfNameBuilder);
-
-            if (StringUtils.isNotBlank(queryAsAlias)) {
-                String ownerIdf = ownerIdfNameBuilder.toString();
-
-                if (StringUtils.startsWithIgnoreCase(ownerIdf, queryAsAlias)) {
-                    if (ownerIdf.length() > queryAsAlias.length()) {
-                        //别名+path+属性名
-                        pathPropertyFunc.parse(ownerIdf.substring(queryAsAlias.length() + 1, ownerIdf.length()), propertyExpr.getName());
-                    } else if (queryAsAlias.equalsIgnoreCase(ownerIdf)) {
-                        //别名+属性名
-                        singlePropertyFunc.parse(propertyExpr.getName());
-                    }
-                } else {
-                    //使用别名,但不以别名开头
-                    pathPropertyFunc.parse(ownerIdfNameBuilder.toString(), propertyExpr.getName());
-                }
-            } else {
-                //如果使用未使用别名
-                pathPropertyFunc.parse(ownerIdfNameBuilder.toString(), propertyExpr.getName());
-            }
-        } else {
-            throw new ElasticSql2DslException("[syntax error] Sql unSupport Identifier type: " + propertyNameExpr.getClass());
-        }
-    }
-
-
-    @FunctionalInterface
-    public interface SQLFlatFieldFunc {
-        void parse(String flatFieldName);
-    }
-
-    @FunctionalInterface
-    public interface SQLNestedFieldFunc {
-        void parse(String nestedDocPath, String fieldName);
-    }
-
-}

+ 41 - 47
src/main/java/org/elasticsearch/dsl/parser/helper/ElasticSqlMethodInvokeHelper.java

@@ -3,15 +3,50 @@ package org.elasticsearch.dsl.parser.helper;
 import com.alibaba.druid.sql.ast.SQLExpr;
 import com.alibaba.druid.sql.ast.expr.*;
 import org.apache.commons.collections.CollectionUtils;
+import org.elasticsearch.dsl.enums.SortOption;
 import org.elasticsearch.dsl.exception.ElasticSql2DslException;
-import org.elasticsearch.dsl.parser.syntax.QueryOrderConditionParser;
 
 public class ElasticSqlMethodInvokeHelper {
 
     public static final String DATE_METHOD = "date";
     public static final String NVL_METHOD = "nvl";
-    public static final String INNER_DOC_METHOD = "inner_doc";
-    public static final String NESTED_DOC_METHOD = "nested_doc";
+
+    public static final String AGG_TERMS_METHOD = "terms";
+    public static final String AGG_RANGE_METHOD = "range";
+    public static final String AGG_RANGE_SEGMENT_METHOD = "segment";
+
+    public static final String AGG_MIN_METHOD = "min";
+    public static final String AGG_MAX_METHOD = "max";
+    public static final String AGG_AVG_METHOD = "avg";
+    public static final String AGG_SUM_METHOD = "sum";
+
+
+    public static void checkTermsAggMethod(SQLMethodInvokeExpr aggInvokeExpr) {
+        if (!AGG_TERMS_METHOD.equalsIgnoreCase(aggInvokeExpr.getMethodName())) {
+            throw new ElasticSql2DslException("[syntax error] Sql not support method:" + aggInvokeExpr.getMethodName());
+        }
+    }
+
+    public static void checkRangeAggMethod(SQLMethodInvokeExpr aggInvokeExpr) {
+        if (!AGG_RANGE_METHOD.equalsIgnoreCase(aggInvokeExpr.getMethodName())) {
+            throw new ElasticSql2DslException("[syntax error] Sql not support method:" + aggInvokeExpr.getMethodName());
+        }
+    }
+
+    public static void checkRangeItemAggMethod(SQLMethodInvokeExpr aggInvokeExpr) {
+        if (!AGG_RANGE_SEGMENT_METHOD.equalsIgnoreCase(aggInvokeExpr.getMethodName())) {
+            throw new ElasticSql2DslException("[syntax error] Sql not support method:" + aggInvokeExpr.getMethodName());
+        }
+    }
+
+    public static void checkStatAggMethod(SQLAggregateExpr statAggExpr) {
+        if (!AGG_MIN_METHOD.equalsIgnoreCase(statAggExpr.getMethodName()) &&
+                !AGG_MAX_METHOD.equalsIgnoreCase(statAggExpr.getMethodName()) &&
+                !AGG_AVG_METHOD.equalsIgnoreCase(statAggExpr.getMethodName()) &&
+                !AGG_SUM_METHOD.equalsIgnoreCase(statAggExpr.getMethodName())) {
+            throw new ElasticSql2DslException("[syntax error] Sql not support method:" + statAggExpr.getMethodName());
+        }
+    }
 
     public static void checkDateMethod(SQLMethodInvokeExpr dateInvokeExpr) {
         if (!DATE_METHOD.equalsIgnoreCase(dateInvokeExpr.getMethodName())) {
@@ -35,40 +70,6 @@ public class ElasticSqlMethodInvokeHelper {
         }
     }
 
-    public static void checkInnerDocMethod(SQLMethodInvokeExpr innerDocInvokeExpr) {
-        if (!INNER_DOC_METHOD.equalsIgnoreCase(innerDocInvokeExpr.getMethodName())) {
-            throw new ElasticSql2DslException("[syntax error] Sql not support method:" + innerDocInvokeExpr.getMethodName());
-        }
-
-        if (CollectionUtils.isEmpty(innerDocInvokeExpr.getParameters()) || innerDocInvokeExpr.getParameters().size() != 1) {
-            throw new ElasticSql2DslException(String.format("[syntax error] There is no %s args method named inner_doc",
-                    innerDocInvokeExpr.getParameters() != null ? innerDocInvokeExpr.getParameters().size() : 0));
-        }
-
-        SQLExpr innerPropertyName = innerDocInvokeExpr.getParameters().get(0);
-
-        if (!(innerPropertyName instanceof SQLPropertyExpr) && !(innerPropertyName instanceof SQLIdentifierExpr)) {
-            throw new ElasticSql2DslException("[syntax error] The arg of inner_doc method should be field param name");
-        }
-    }
-
-    public static void checkNestedDocMethod(SQLMethodInvokeExpr nestedDocInvokeExpr) {
-        if (!NESTED_DOC_METHOD.equalsIgnoreCase(nestedDocInvokeExpr.getMethodName())) {
-            throw new ElasticSql2DslException("[syntax error] ElasticSql not support method:" + nestedDocInvokeExpr.getMethodName());
-        }
-
-        if (CollectionUtils.isEmpty(nestedDocInvokeExpr.getParameters()) || nestedDocInvokeExpr.getParameters().size() != 1) {
-            throw new ElasticSql2DslException(String.format("[syntax error] There is no %s args method named nested_doc",
-                    nestedDocInvokeExpr.getParameters() != null ? nestedDocInvokeExpr.getParameters().size() : 0));
-        }
-
-        SQLExpr innerPropertyName = nestedDocInvokeExpr.getParameters().get(0);
-
-        if (!(innerPropertyName instanceof SQLPropertyExpr) && !(innerPropertyName instanceof SQLIdentifierExpr)) {
-            throw new ElasticSql2DslException("[syntax error] The arg of nested_doc method should be field param name");
-        }
-    }
-
     public static void checkNvlMethod(SQLMethodInvokeExpr nvlInvokeExpr) {
         if (!NVL_METHOD.equalsIgnoreCase(nvlInvokeExpr.getMethodName())) {
             throw new ElasticSql2DslException("[syntax error] Sql sort condition only support nvl method invoke");
@@ -82,14 +83,7 @@ public class ElasticSqlMethodInvokeHelper {
         SQLExpr fieldArg = nvlInvokeExpr.getParameters().get(0);
         SQLExpr valueArg = nvlInvokeExpr.getParameters().get(1);
 
-        if (fieldArg instanceof SQLMethodInvokeExpr) {
-            if (INNER_DOC_METHOD.equalsIgnoreCase(((SQLMethodInvokeExpr) fieldArg).getMethodName())) {
-                checkInnerDocMethod((SQLMethodInvokeExpr) fieldArg);
-            }
-            if (NESTED_DOC_METHOD.equalsIgnoreCase(((SQLMethodInvokeExpr) fieldArg).getMethodName())) {
-                checkNestedDocMethod((SQLMethodInvokeExpr) fieldArg);
-            }
-        } else if (!(fieldArg instanceof SQLPropertyExpr) && !(fieldArg instanceof SQLIdentifierExpr)) {
+        if (!(fieldArg instanceof SQLPropertyExpr) && !(fieldArg instanceof SQLIdentifierExpr)) {
             throw new ElasticSql2DslException("[syntax error] The first arg of nvl method should be field param name");
         }
 
@@ -103,8 +97,8 @@ public class ElasticSqlMethodInvokeHelper {
                 throw new ElasticSql2DslException("[syntax error] The third arg of nvl method should be string");
             }
             String sortModeText = ((SQLCharExpr) sortModArg).getText();
-            if (!QueryOrderConditionParser.SortOption.AVG.mode().equalsIgnoreCase(sortModeText) && !QueryOrderConditionParser.SortOption.MIN.mode().equalsIgnoreCase(sortModeText)
-                    && !QueryOrderConditionParser.SortOption.MAX.mode().equalsIgnoreCase(sortModeText) && !QueryOrderConditionParser.SortOption.SUM.mode().equalsIgnoreCase(sortModeText)) {
+            if (!SortOption.AVG.mode().equalsIgnoreCase(sortModeText) && !SortOption.MIN.mode().equalsIgnoreCase(sortModeText)
+                    && !SortOption.MAX.mode().equalsIgnoreCase(sortModeText) && !SortOption.SUM.mode().equalsIgnoreCase(sortModeText)) {
                 throw new ElasticSql2DslException("[syntax error] The third arg of nvl method should be one of the string[min,max,avg,sum]");
             }
         }

+ 1 - 1
src/main/java/org/elasticsearch/dsl/parser/listener/ParseActionListener.java

@@ -1,7 +1,7 @@
 package org.elasticsearch.dsl.parser.listener;
 
-import org.elasticsearch.dsl.enums.SQLConditionOperator;
 import org.elasticsearch.dsl.bean.ElasticSqlQueryField;
+import org.elasticsearch.dsl.enums.SQLConditionOperator;
 
 public interface ParseActionListener {
 

+ 1 - 1
src/main/java/org/elasticsearch/dsl/parser/listener/ParseActionListenerAdapter.java

@@ -1,7 +1,7 @@
 package org.elasticsearch.dsl.parser.listener;
 
-import org.elasticsearch.dsl.enums.SQLConditionOperator;
 import org.elasticsearch.dsl.bean.ElasticSqlQueryField;
+import org.elasticsearch.dsl.enums.SQLConditionOperator;
 
 public class ParseActionListenerAdapter implements ParseActionListener {
 

+ 168 - 0
src/main/java/org/elasticsearch/dsl/parser/syntax/QueryFieldParser.java

@@ -0,0 +1,168 @@
+package org.elasticsearch.dsl.parser.syntax;
+
+import com.alibaba.druid.sql.ast.SQLExpr;
+import com.alibaba.druid.sql.ast.expr.SQLAllColumnExpr;
+import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
+import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr;
+import com.google.common.collect.Lists;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang.StringUtils;
+import org.elasticsearch.dsl.bean.ElasticSqlQueryField;
+import org.elasticsearch.dsl.bean.ElasticSqlQueryFields;
+import org.elasticsearch.dsl.bean.QueryFieldReferenceNode;
+import org.elasticsearch.dsl.bean.QueryFieldReferencePath;
+import org.elasticsearch.dsl.exception.ElasticSql2DslException;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+public class QueryFieldParser {
+    private static final String NESTED_DOC_IDF = "$";
+    private static final String FIELD_REF_PATH_DOT = ".";
+
+    public ElasticSqlQueryField parseSelectQueryField(SQLExpr queryFieldExpr, String queryAs) {
+        if (queryFieldExpr instanceof SQLAllColumnExpr) {
+            return ElasticSqlQueryFields.newMatchAllRootDocField();
+        }
+        QueryFieldReferencePath referencePath = buildQueryFieldRefPath(queryFieldExpr, queryAs);
+
+        StringBuilder fullPathQueryFieldNameBuilder = new StringBuilder();
+        for (QueryFieldReferenceNode referenceNode : referencePath.getReferenceNodes()) {
+            fullPathQueryFieldNameBuilder.append(referenceNode.getReferenceNodeName());
+            fullPathQueryFieldNameBuilder.append(FIELD_REF_PATH_DOT);
+        }
+        if (fullPathQueryFieldNameBuilder.length() > 0) {
+            fullPathQueryFieldNameBuilder.deleteCharAt(fullPathQueryFieldNameBuilder.length() - 1);
+        }
+
+        return ElasticSqlQueryFields.newSqlSelectField(fullPathQueryFieldNameBuilder.toString());
+    }
+
+    public ElasticSqlQueryField parseConditionQueryField(SQLExpr queryFieldExpr, String queryAs) {
+        QueryFieldReferencePath referencePath = buildQueryFieldRefPath(queryFieldExpr, queryAs);
+        StringBuilder queryFieldPrefixBuilder = new StringBuilder();
+
+        String longestNestedDocContextPrefix = StringUtils.EMPTY;
+        String longestInnerDocContextPrefix = StringUtils.EMPTY;
+
+        for (Iterator<QueryFieldReferenceNode> nodeIt = referencePath.getReferenceNodes().iterator(); nodeIt.hasNext(); ) {
+            QueryFieldReferenceNode referenceNode = nodeIt.next();
+            queryFieldPrefixBuilder.append(referenceNode.getReferenceNodeName());
+
+            if (referenceNode.isNestedDocReference()) {
+                longestNestedDocContextPrefix = queryFieldPrefixBuilder.toString();
+            }
+
+            if (nodeIt.hasNext()) {
+                longestInnerDocContextPrefix = queryFieldPrefixBuilder.toString();
+            }
+
+            queryFieldPrefixBuilder.append(FIELD_REF_PATH_DOT);
+        }
+        if (queryFieldPrefixBuilder.length() > 0) {
+            queryFieldPrefixBuilder.deleteCharAt(queryFieldPrefixBuilder.length() - 1);
+        }
+
+        String queryFieldFullRefPath = queryFieldPrefixBuilder.toString();
+
+        //nested doc field
+        if (StringUtils.isNotBlank(longestNestedDocContextPrefix)) {
+            if (longestNestedDocContextPrefix.length() < queryFieldFullRefPath.length()) {
+                String queryFieldName = queryFieldFullRefPath.substring(longestNestedDocContextPrefix.length() + 1);
+                return ElasticSqlQueryFields.newNestedDocQueryField(longestNestedDocContextPrefix, queryFieldName);
+            }
+            throw new ElasticSql2DslException(String.format("[syntax error] nested doc field[%s] parse error!", queryFieldFullRefPath));
+        }
+
+        //root doc field
+        if (referencePath.getReferenceNodes().size() == 1) {
+            if (StringUtils.isNotBlank(queryAs) && queryAs.equalsIgnoreCase(queryFieldFullRefPath)) {
+                throw new ElasticSql2DslException(String.format("[syntax error] ambiguous query field[%s], queryAs[%s]", queryFieldFullRefPath, queryAs));
+            }
+            return ElasticSqlQueryFields.newRootDocQueryField(queryFieldFullRefPath);
+        }
+
+        //inner doc field
+        if (longestInnerDocContextPrefix.length() < queryFieldFullRefPath.length()) {
+            String innerDocFieldName = queryFieldFullRefPath.substring(longestInnerDocContextPrefix.length() + 1);
+            return ElasticSqlQueryFields.newInnerDocQueryField(longestInnerDocContextPrefix, innerDocFieldName);
+        }
+        throw new ElasticSql2DslException(String.format("[syntax error] query field[%s] parse error!", queryFieldFullRefPath));
+    }
+
+    private QueryFieldReferencePath buildQueryFieldRefPath(SQLExpr queryFieldExpr, String queryAs) {
+        QueryFieldReferencePath referencePath = new QueryFieldReferencePath();
+
+        List<QueryFieldReferenceNode> referenceNodeList = parseQueryFieldExprToRefPath(queryFieldExpr);
+        if (CollectionUtils.isEmpty(referenceNodeList)) {
+            throw new ElasticSql2DslException("[parse_query_field] referenceNodes is empty!");
+        }
+        QueryFieldReferenceNode firstRefNode = referenceNodeList.get(0);
+
+        if (referenceNodeList.size() == 1) {
+            referencePath.addReferenceNode(firstRefNode);
+            return referencePath;
+        }
+
+        if (StringUtils.isNotBlank(queryAs) && !firstRefNode.isNestedDocReference() && queryAs.equalsIgnoreCase(firstRefNode.getReferenceNodeName())) {
+            referenceNodeList.remove(0);
+        }
+
+        for (QueryFieldReferenceNode referenceNode : referenceNodeList) {
+            referencePath.addReferenceNode(referenceNode);
+        }
+
+        return referencePath;
+    }
+
+    private static List<QueryFieldReferenceNode> parseQueryFieldExprToRefPath(SQLExpr queryFieldExpr) {
+        List<QueryFieldReferenceNode> referencePathNodes = Lists.newLinkedList();
+
+        if (queryFieldExpr instanceof SQLIdentifierExpr) {
+            String idfName = ((SQLIdentifierExpr) queryFieldExpr).getName();
+            QueryFieldReferenceNode referenceNode = buildReferenceNode(idfName);
+            referencePathNodes.add(referenceNode);
+
+            return referencePathNodes;
+        }
+
+        if (queryFieldExpr instanceof SQLPropertyExpr) {
+            List<String> queryFieldTextList = Lists.newLinkedList();
+
+            SQLExpr tmpLoopExpr = queryFieldExpr;
+
+            while ((tmpLoopExpr != null) && (tmpLoopExpr instanceof SQLPropertyExpr)) {
+                queryFieldTextList.add(((SQLPropertyExpr) tmpLoopExpr).getName());
+                tmpLoopExpr = ((SQLPropertyExpr) tmpLoopExpr).getOwner();
+            }
+
+            if (tmpLoopExpr instanceof SQLIdentifierExpr) {
+                queryFieldTextList.add(((SQLIdentifierExpr) tmpLoopExpr).getName());
+            }
+
+            Collections.reverse(queryFieldTextList);
+            for (String strRefNode : queryFieldTextList) {
+                QueryFieldReferenceNode referenceNode = buildReferenceNode(strRefNode);
+                referencePathNodes.add(referenceNode);
+            }
+
+            return referencePathNodes;
+        }
+
+        throw new ElasticSql2DslException(String.format("[syntax error] can not support query field type[%s]", queryFieldExpr.toString()));
+    }
+
+    private static QueryFieldReferenceNode buildReferenceNode(String strRefNodeName) {
+        QueryFieldReferenceNode referenceNode = null;
+        if (strRefNodeName.startsWith(NESTED_DOC_IDF)) {
+            if(NESTED_DOC_IDF.equals(strRefNodeName)) {
+                throw new ElasticSql2DslException("[syntax error] nested doc query field can not be blank");
+            }
+            referenceNode = new QueryFieldReferenceNode(strRefNodeName.substring(1), true);
+        } else {
+            referenceNode = new QueryFieldReferenceNode(strRefNodeName, false);
+        }
+        return referenceNode;
+    }
+}

+ 5 - 2
src/main/java/org/elasticsearch/dsl/parser/syntax/QueryFromParser.java

@@ -3,6 +3,7 @@ package org.elasticsearch.dsl.parser.syntax;
 import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
 import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr;
 import com.alibaba.druid.sql.ast.statement.SQLExprTableSource;
+import com.google.common.collect.Lists;
 import org.elasticsearch.dsl.bean.ElasticDslContext;
 import org.elasticsearch.dsl.parser.QueryParser;
 import org.elasticsearch.dsl.parser.listener.ParseActionListener;
@@ -25,7 +26,8 @@ public class QueryFromParser implements QueryParser {
             dslContext.getParseResult().setQueryAs(tableSource.getAlias());
 
             if (tableSource.getExpr() instanceof SQLIdentifierExpr) {
-                dslContext.getParseResult().setIndex(((SQLIdentifierExpr) tableSource.getExpr()).getName());
+                String index = ((SQLIdentifierExpr) tableSource.getExpr()).getName();
+                dslContext.getParseResult().setIndices(Lists.newArrayList(index));
                 return;
             }
             if (tableSource.getExpr() instanceof SQLPropertyExpr) {
@@ -34,7 +36,8 @@ public class QueryFromParser implements QueryParser {
                 if (!(idxExpr.getOwner() instanceof SQLIdentifierExpr)) {
                     throw new ElasticSql2DslException("[syntax error] From table should like [index].[type]");
                 }
-                dslContext.getParseResult().setIndex(((SQLIdentifierExpr) idxExpr.getOwner()).getName());
+                String index = ((SQLIdentifierExpr) idxExpr.getOwner()).getName();
+                dslContext.getParseResult().setIndices(Lists.newArrayList(index));
                 dslContext.getParseResult().setType(idxExpr.getName());
                 return;
             }

+ 171 - 0
src/main/java/org/elasticsearch/dsl/parser/syntax/QueryGroupByParser.java

@@ -0,0 +1,171 @@
+package org.elasticsearch.dsl.parser.syntax;
+
+import com.alibaba.druid.sql.ast.SQLExpr;
+import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;
+import com.alibaba.druid.sql.ast.statement.SQLSelectGroupByClause;
+import com.google.common.collect.Lists;
+import org.apache.commons.collections.CollectionUtils;
+import org.elasticsearch.dsl.bean.ElasticDslContext;
+import org.elasticsearch.dsl.bean.ElasticSqlQueryField;
+import org.elasticsearch.dsl.bean.RangeSegment;
+import org.elasticsearch.dsl.enums.QueryFieldType;
+import org.elasticsearch.dsl.exception.ElasticSql2DslException;
+import org.elasticsearch.dsl.parser.QueryParser;
+import org.elasticsearch.dsl.parser.helper.ElasticSqlArgTransferHelper;
+import org.elasticsearch.dsl.parser.helper.ElasticSqlMethodInvokeHelper;
+import org.elasticsearch.dsl.parser.listener.ParseActionListener;
+import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
+import org.elasticsearch.search.aggregations.AggregationBuilder;
+import org.elasticsearch.search.aggregations.AggregationBuilders;
+import org.elasticsearch.search.aggregations.bucket.range.AbstractRangeBuilder;
+import org.elasticsearch.search.aggregations.bucket.range.RangeBuilder;
+import org.elasticsearch.search.aggregations.bucket.range.date.DateRangeBuilder;
+import org.elasticsearch.search.aggregations.bucket.terms.Terms;
+import org.elasticsearch.search.aggregations.bucket.terms.TermsBuilder;
+import org.elasticsearch.sql.ElasticSqlSelectQueryBlock;
+import org.joda.time.DateTime;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+
+import java.util.Date;
+import java.util.List;
+
+public class QueryGroupByParser implements QueryParser {
+
+    private static final Integer MAX_GROUP_BY_SIZE = 500;
+
+    private static final String AGG_BUCKET_KEY_PREFIX = "agg_";
+
+    private ParseActionListener parseActionListener;
+
+    public QueryGroupByParser(ParseActionListener parseActionListener) {
+        this.parseActionListener = parseActionListener;
+    }
+
+    @Override
+    public void parse(ElasticDslContext dslContext) {
+
+        ElasticSqlSelectQueryBlock queryBlock = (ElasticSqlSelectQueryBlock) dslContext.getQueryExpr().getSubQuery().getQuery();
+        SQLSelectGroupByClause sqlGroupBy = queryBlock.getGroupBy();
+        if (sqlGroupBy != null && CollectionUtils.isNotEmpty(sqlGroupBy.getItems())) {
+            String queryAs = dslContext.getParseResult().getQueryAs();
+
+            List<AbstractAggregationBuilder> aggregationList = Lists.newArrayList();
+            for (SQLExpr groupByItem : sqlGroupBy.getItems()) {
+                if (!(groupByItem instanceof SQLMethodInvokeExpr)) {
+                    throw new ElasticSql2DslException("[syntax error] group by item must be an agg method call");
+                }
+                SQLMethodInvokeExpr aggMethodExpr = (SQLMethodInvokeExpr) groupByItem;
+
+                //Terms Aggregation
+                if (ElasticSqlMethodInvokeHelper.AGG_TERMS_METHOD.equalsIgnoreCase(aggMethodExpr.getMethodName())) {
+                    ElasticSqlMethodInvokeHelper.checkTermsAggMethod(aggMethodExpr);
+
+                    SQLExpr termsFieldExpr = aggMethodExpr.getParameters().get(0);
+
+                    AggregationBuilder termsBuilder = parseTermsAggregation(queryAs, termsFieldExpr);
+                    aggregationList.add(termsBuilder);
+                }
+
+
+                //Range Aggregation
+                if (ElasticSqlMethodInvokeHelper.AGG_RANGE_METHOD.equalsIgnoreCase(aggMethodExpr.getMethodName())) {
+                    ElasticSqlMethodInvokeHelper.checkRangeAggMethod(aggMethodExpr);
+
+                    List<RangeSegment> rangeSegments = parseRangeSegments(aggMethodExpr, dslContext.getSqlArgs());
+                    SQLExpr rangeFieldExpr = aggMethodExpr.getParameters().get(0);
+
+                    AggregationBuilder rangeBuilder = parseRangeAggregation(queryAs, rangeFieldExpr, rangeSegments);
+                    aggregationList.add(rangeBuilder);
+                }
+            }
+            dslContext.getParseResult().setGroupBy(aggregationList);
+        }
+
+    }
+
+    private AggregationBuilder parseTermsAggregation(String queryAs, SQLExpr termsFieldExpr) {
+        QueryFieldParser queryFieldParser = new QueryFieldParser();
+
+        ElasticSqlQueryField queryField = queryFieldParser.parseConditionQueryField(termsFieldExpr, queryAs);
+        if(queryField.getQueryFieldType() != QueryFieldType.RootDocField && queryField.getQueryFieldType() != QueryFieldType.InnerDocField) {
+            throw new ElasticSql2DslException(String.format("[syntax error] can not support terms aggregation for field type[%s]", queryField.getQueryFieldType()));
+        }
+
+        return createTermsBuilder(queryField.getQueryFieldFullName());
+    }
+
+    private AggregationBuilder parseRangeAggregation(String queryAs, SQLExpr rangeFieldExpr, List<RangeSegment> rangeSegments) {
+
+        QueryFieldParser queryFieldParser = new QueryFieldParser();
+
+        ElasticSqlQueryField queryField = queryFieldParser.parseConditionQueryField(rangeFieldExpr, queryAs);
+        if(queryField.getQueryFieldType() != QueryFieldType.RootDocField && queryField.getQueryFieldType() != QueryFieldType.InnerDocField) {
+            throw new ElasticSql2DslException(String.format("[syntax error] can not support range aggregation for field type[%s]", queryField.getQueryFieldType()));
+        }
+
+        return createRangeBuilder(queryField.getQueryFieldFullName(), rangeSegments);
+    }
+
+    private List<RangeSegment> parseRangeSegments(SQLMethodInvokeExpr rangeMethodExpr, Object[] args) {
+        List<RangeSegment> rangeSegmentList = Lists.newArrayList();
+        for (int pIdx = 1; pIdx < rangeMethodExpr.getParameters().size(); pIdx++) {
+            SQLMethodInvokeExpr segMethodExpr = (SQLMethodInvokeExpr) rangeMethodExpr.getParameters().get(pIdx);
+
+            ElasticSqlMethodInvokeHelper.checkRangeItemAggMethod(segMethodExpr);
+
+            Object from = ElasticSqlArgTransferHelper.transferSqlArg(segMethodExpr.getParameters().get(0), args, true);
+            Object to = ElasticSqlArgTransferHelper.transferSqlArg(segMethodExpr.getParameters().get(1), args, true);
+
+            rangeSegmentList.add(new RangeSegment(from, to,
+                    from instanceof Number ? RangeSegment.SegmentType.Numeric : RangeSegment.SegmentType.Date));
+        }
+        return rangeSegmentList;
+    }
+
+    private TermsBuilder createTermsBuilder(String termsFieldName) {
+        return AggregationBuilders.terms(AGG_BUCKET_KEY_PREFIX + termsFieldName)
+                .field(termsFieldName)
+                .minDocCount(1).shardMinDocCount(1)
+                .shardSize(MAX_GROUP_BY_SIZE << 1).size(MAX_GROUP_BY_SIZE).order(Terms.Order.count(false));
+    }
+
+    private AbstractRangeBuilder createRangeBuilder(String rangeFieldName, List<RangeSegment> rangeSegments) {
+        AbstractRangeBuilder rangeBuilder = null;
+        RangeSegment.SegmentType segType = rangeSegments.get(0).getSegmentType();
+
+        if (segType == RangeSegment.SegmentType.Numeric) {
+            RangeBuilder numericRangeBuilder = AggregationBuilders.range(AGG_BUCKET_KEY_PREFIX + rangeFieldName).field(rangeFieldName);
+            for (RangeSegment segment : rangeSegments) {
+                String key = String.format("%s-%s", segment.getFrom().toString(), segment.getTo().toString());
+                numericRangeBuilder.addRange(key, Double.valueOf(segment.getFrom().toString()), Double.valueOf(segment.getTo().toString()));
+            }
+            rangeBuilder = numericRangeBuilder;
+        }
+
+        if (segType == RangeSegment.SegmentType.Date) {
+            DateRangeBuilder dateRangeBuilder = AggregationBuilders.dateRange(AGG_BUCKET_KEY_PREFIX + rangeFieldName).field(rangeFieldName);
+            for (RangeSegment segment : rangeSegments) {
+
+                Date fromDate = getDateRangeVal(segment.getFrom().toString());
+                Date toDate = getDateRangeVal(segment.getTo().toString());
+
+                String key = String.format("[%s]-[%s]", formatDateRangeAggKey(fromDate), formatDateRangeAggKey(toDate));
+                dateRangeBuilder.addRange(key, segment.getFrom(), segment.getTo());
+            }
+            rangeBuilder = dateRangeBuilder;
+        }
+        return rangeBuilder;
+    }
+
+    private String formatDateRangeAggKey(Date date) {
+        final String dateRangeKeyPattern = "yyyy-MM-dd HH:mm:ss";
+        return new DateTime(date).toString(dateRangeKeyPattern);
+    }
+
+    public static Date getDateRangeVal(String date) {
+        final String dateRangeValPattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
+        DateTimeFormatter formatter = DateTimeFormat.forPattern(dateRangeValPattern);
+        return formatter.parseDateTime(date).toDate();
+    }
+}

+ 1 - 1
src/main/java/org/elasticsearch/dsl/parser/syntax/QueryLimitSizeParser.java

@@ -4,8 +4,8 @@ import com.alibaba.druid.sql.ast.SQLExpr;
 import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr;
 import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr;
 import org.elasticsearch.dsl.bean.ElasticDslContext;
-import org.elasticsearch.dsl.parser.QueryParser;
 import org.elasticsearch.dsl.parser.helper.ElasticSqlArgTransferHelper;
+import org.elasticsearch.dsl.parser.QueryParser;
 import org.elasticsearch.dsl.parser.listener.ParseActionListener;
 import org.elasticsearch.dsl.exception.ElasticSql2DslException;
 import org.elasticsearch.sql.ElasticSqlSelectQueryBlock;

+ 35 - 86
src/main/java/org/elasticsearch/dsl/parser/syntax/QueryOrderConditionParser.java

@@ -11,12 +11,14 @@ import com.alibaba.druid.sql.ast.statement.SQLSelectOrderByItem;
 import com.google.common.collect.Lists;
 import org.apache.commons.collections.CollectionUtils;
 import org.elasticsearch.dsl.bean.ElasticDslContext;
+import org.elasticsearch.dsl.bean.ElasticSqlQueryField;
+import org.elasticsearch.dsl.enums.QueryFieldType;
+import org.elasticsearch.dsl.enums.SortOption;
+import org.elasticsearch.dsl.exception.ElasticSql2DslException;
 import org.elasticsearch.dsl.parser.QueryParser;
 import org.elasticsearch.dsl.parser.helper.ElasticSqlArgTransferHelper;
-import org.elasticsearch.dsl.parser.listener.ParseActionListener;
-import org.elasticsearch.dsl.exception.ElasticSql2DslException;
-import org.elasticsearch.dsl.parser.helper.ElasticSqlIdentifierHelper;
 import org.elasticsearch.dsl.parser.helper.ElasticSqlMethodInvokeHelper;
+import org.elasticsearch.dsl.parser.listener.ParseActionListener;
 import org.elasticsearch.search.sort.FieldSortBuilder;
 import org.elasticsearch.search.sort.SortBuilder;
 import org.elasticsearch.search.sort.SortBuilders;
@@ -49,128 +51,75 @@ public class QueryOrderConditionParser implements QueryParser {
         }
     }
 
-    private SortBuilder parseOrderCondition(final SQLSelectOrderByItem orderByItem, String queryAs, Object[] sqlArgs) {
+    private SortBuilder parseOrderCondition(SQLSelectOrderByItem orderByItem, String queryAs, Object[] sqlArgs) {
         if (orderByItem.getExpr() instanceof SQLPropertyExpr || orderByItem.getExpr() instanceof SQLIdentifierExpr) {
             return parseCondition(orderByItem.getExpr(), queryAs, new ConditionSortBuilder() {
                 @Override
-                public FieldSortBuilder buildSort(String idfName) {
+                public FieldSortBuilder buildSort(String queryFieldName) {
                     if (SQLOrderingSpecification.ASC == orderByItem.getType()) {
-                        return SortBuilders.fieldSort(idfName).order(SortOrder.ASC);
+                        return SortBuilders.fieldSort(queryFieldName).order(SortOrder.ASC);
                     } else {
-                        return SortBuilders.fieldSort(idfName).order(SortOrder.DESC);
+                        return SortBuilders.fieldSort(queryFieldName).order(SortOrder.DESC);
                     }
                 }
             });
         }
         if (orderByItem.getExpr() instanceof SQLMethodInvokeExpr) {
-            final SQLMethodInvokeExpr methodInvokeExpr = (SQLMethodInvokeExpr) orderByItem.getExpr();
+            SQLMethodInvokeExpr methodInvokeExpr = (SQLMethodInvokeExpr) orderByItem.getExpr();
             //nvl method
             if (ElasticSqlMethodInvokeHelper.NVL_METHOD.equalsIgnoreCase(methodInvokeExpr.getMethodName())) {
                 ElasticSqlMethodInvokeHelper.checkNvlMethod(methodInvokeExpr);
-                final Object valueArg = ElasticSqlArgTransferHelper.transferSqlArg(methodInvokeExpr.getParameters().get(1), sqlArgs);
+                Object valueArg = ElasticSqlArgTransferHelper.transferSqlArg(methodInvokeExpr.getParameters().get(1), sqlArgs);
                 return parseCondition(methodInvokeExpr.getParameters().get(0), queryAs, new ConditionSortBuilder() {
                     @Override
                     public FieldSortBuilder buildSort(String idfName) {
                         FieldSortBuilder fieldSortBuilder = null;
+
                         if (SQLOrderingSpecification.ASC == orderByItem.getType()) {
                             fieldSortBuilder = SortBuilders.fieldSort(idfName).order(SortOrder.ASC).missing(valueArg);
                         } else {
                             fieldSortBuilder = SortBuilders.fieldSort(idfName).order(SortOrder.DESC).missing(valueArg);
                         }
+
                         if (methodInvokeExpr.getParameters().size() == 3) {
                             SQLExpr sortModArg = methodInvokeExpr.getParameters().get(2);
                             String sortModeText = ((SQLCharExpr) sortModArg).getText();
                             fieldSortBuilder.sortMode(SortOption.get(sortModeText).mode());
                         }
-                        return fieldSortBuilder;
-                    }
-                });
-            }
 
-            //nested或者inner doc类型
-            if (ElasticSqlMethodInvokeHelper.NESTED_DOC_METHOD.equalsIgnoreCase(methodInvokeExpr.getMethodName())
-                    || ElasticSqlMethodInvokeHelper.INNER_DOC_METHOD.equalsIgnoreCase(methodInvokeExpr.getMethodName())) {
-                return parseCondition(methodInvokeExpr, queryAs, new ConditionSortBuilder() {
-                    @Override
-                    public FieldSortBuilder buildSort(String idfName) {
-                        return SortBuilders.fieldSort(idfName).order(
-                                SQLOrderingSpecification.ASC == orderByItem.getType() ? SortOrder.ASC : SortOrder.DESC
-                        );
+                        return fieldSortBuilder;
                     }
                 });
             }
         }
-        throw new ElasticSql2DslException("[syntax error] Sql cannot support sort type: " + orderByItem.getExpr().getClass());
+        throw new ElasticSql2DslException("[syntax error] can not support sort type: " + orderByItem.getExpr().getClass());
     }
 
-    private SortBuilder parseCondition(SQLExpr sqlExpr, String queryAs, final ConditionSortBuilder sortBuilder) {
-        final List<SortBuilder> tmpSortList = Lists.newLinkedList();
-        ElasticSqlIdentifierHelper.parseSqlIdentifier(sqlExpr, queryAs, new ElasticSqlIdentifierHelper.SQLFlatFieldFunc() {
-            @Override
-            public void parse(String flatFieldName) {
-                FieldSortBuilder originalSort = sortBuilder.buildSort(flatFieldName);
-                tmpSortList.add(originalSort);
-            }
-        }, new ElasticSqlIdentifierHelper.SQLNestedFieldFunc() {
-            @Override
-            public void parse(String nestedDocPath, String fieldName) {
-                FieldSortBuilder originalSort = sortBuilder.buildSort(fieldName);
-                originalSort.setNestedPath(nestedDocPath);
-                tmpSortList.add(originalSort);
-            }
-        });
-        if (CollectionUtils.isNotEmpty(tmpSortList)) {
-            return tmpSortList.get(0);
+    private SortBuilder parseCondition(SQLExpr sqlExpr, String queryAs, ConditionSortBuilder sortBuilder) {
+        QueryFieldParser queryFieldParser = new QueryFieldParser();
+        ElasticSqlQueryField sortField = queryFieldParser.parseConditionQueryField(sqlExpr, queryAs);
+
+        SortBuilder rtnSortBuilder = null;
+        if(sortField.getQueryFieldType() == QueryFieldType.RootDocField || sortField.getQueryFieldType() == QueryFieldType.InnerDocField) {
+            rtnSortBuilder = sortBuilder.buildSort(sortField.getQueryFieldFullName());
         }
-        return null;
-    }
 
-    @FunctionalInterface
-    private interface ConditionSortBuilder {
-        FieldSortBuilder buildSort(String idfName);
-    }
+        if(sortField.getQueryFieldType() == QueryFieldType.NestedDocField) {
+            FieldSortBuilder originalSort = sortBuilder.buildSort(sortField.getQueryFieldFullName());
+            originalSort.setNestedPath(sortField.getNestedDocContextPath());
+            rtnSortBuilder = originalSort;
+        }
 
-    public enum SortOption {
-        SUM {
-            @Override
-            public String mode() {
-                return "sum";
-            }
-        },
-        MIN {
-            @Override
-            public String mode() {
-                return "min";
-            }
-        },
-        MAX {
-            @Override
-            public String mode() {
-                return "max";
-            }
-        },
-        AVG {
-            @Override
-            public String mode() {
-                return "avg";
-            }
-        };
+        if(rtnSortBuilder == null) {
+            throw new ElasticSql2DslException(String.format("[syntax error] sort condition field can not support type[%s]", sortField.getQueryFieldType()));
+        }
 
-        public abstract String mode();
+        return rtnSortBuilder;
+    }
 
-        @Override
-        public String toString() {
-            return mode();
-        }
 
-        public static SortOption get(String mode) {
-            SortOption op = null;
-            for (SortOption option : SortOption.values()) {
-                if (option.mode().equalsIgnoreCase(mode)) {
-                    op = option;
-                }
-            }
-            return op;
-        }
+    @FunctionalInterface
+    private interface ConditionSortBuilder {
+        FieldSortBuilder buildSort(String idfName);
     }
 }

+ 1 - 1
src/main/java/org/elasticsearch/dsl/parser/syntax/QueryRoutingValParser.java

@@ -6,8 +6,8 @@ import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr;
 import com.google.common.collect.Lists;
 import org.apache.commons.collections.CollectionUtils;
 import org.elasticsearch.dsl.bean.ElasticDslContext;
-import org.elasticsearch.dsl.parser.QueryParser;
 import org.elasticsearch.dsl.parser.helper.ElasticSqlArgTransferHelper;
+import org.elasticsearch.dsl.parser.QueryParser;
 import org.elasticsearch.dsl.parser.listener.ParseActionListener;
 import org.elasticsearch.dsl.exception.ElasticSql2DslException;
 import org.elasticsearch.sql.ElasticSqlSelectQueryBlock;

+ 72 - 17
src/main/java/org/elasticsearch/dsl/parser/syntax/QuerySelectFieldListParser.java

@@ -1,20 +1,26 @@
 package org.elasticsearch.dsl.parser.syntax;
 
+import com.alibaba.druid.sql.ast.SQLExpr;
+import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr;
 import com.alibaba.druid.sql.ast.statement.SQLSelectItem;
 import com.google.common.collect.Lists;
+import org.apache.commons.collections.CollectionUtils;
 import org.elasticsearch.dsl.bean.ElasticDslContext;
 import org.elasticsearch.dsl.bean.ElasticSqlQueryField;
+import org.elasticsearch.dsl.enums.QueryFieldType;
+import org.elasticsearch.dsl.exception.ElasticSql2DslException;
 import org.elasticsearch.dsl.parser.QueryParser;
+import org.elasticsearch.dsl.parser.helper.ElasticSqlMethodInvokeHelper;
 import org.elasticsearch.dsl.parser.listener.ParseActionListener;
-import org.elasticsearch.dsl.parser.helper.ElasticSqlIdentifierHelper;
+import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
+import org.elasticsearch.search.aggregations.AggregationBuilder;
+import org.elasticsearch.search.aggregations.AggregationBuilders;
 import org.elasticsearch.sql.ElasticSqlSelectQueryBlock;
 
 import java.util.List;
 
 public class QuerySelectFieldListParser implements QueryParser {
 
-    public static final String SQL_FIELD_MATCH_ALL = "*";
-
     private ParseActionListener parseActionListener;
 
     public QuerySelectFieldListParser(ParseActionListener parseActionListener) {
@@ -25,26 +31,75 @@ public class QuerySelectFieldListParser implements QueryParser {
     public void parse(ElasticDslContext dslContext) {
         ElasticSqlSelectQueryBlock queryBlock = (ElasticSqlSelectQueryBlock) dslContext.getQueryExpr().getSubQuery().getQuery();
 
-        final List<String> selectFields = Lists.newLinkedList();
+        List<String> selectFields = Lists.newLinkedList();
+        QueryFieldParser queryFieldParser = new QueryFieldParser();
+        String queryAs = dslContext.getParseResult().getQueryAs();
+
+        List<AbstractAggregationBuilder> aggregations = Lists.newLinkedList();
         for (SQLSelectItem selectField : queryBlock.getSelectList()) {
-            ElasticSqlQueryField sqlIdentifier = ElasticSqlIdentifierHelper.parseSqlIdentifier(selectField.getExpr(), dslContext.getParseResult().getQueryAs(), new ElasticSqlIdentifierHelper.SQLFlatFieldFunc() {
-                @Override
-                public void parse(String flatFieldName) {
-                    if (!SQL_FIELD_MATCH_ALL.equals(flatFieldName)) {
-                        selectFields.add(flatFieldName);
-                    }
-                }
-            }, new ElasticSqlIdentifierHelper.SQLNestedFieldFunc() {
-                @Override
-                public void parse(String nestedDocPath, String fieldName) {
-                    selectFields.add(String.format("%s.%s", nestedDocPath, fieldName));
+
+            // agg method
+            if (selectField.getExpr() instanceof SQLAggregateExpr) {
+
+                SQLAggregateExpr aggExpr = (SQLAggregateExpr) selectField.getExpr();
+                SQLExpr aggFieldExpr = aggExpr.getArguments().get(0);
+
+                ElasticSqlQueryField aggField = queryFieldParser.parseConditionQueryField(aggFieldExpr, queryAs);
+                AbstractAggregationBuilder statsAgg = parseStatsAggregation(aggExpr, aggField.getQueryFieldFullName());
+
+                aggregations.add(statsAgg);
+                continue;
+            }
+
+            // select field
+            ElasticSqlQueryField sqlSelectField = queryFieldParser.parseSelectQueryField(selectField.getExpr(), queryAs);
+
+            if (sqlSelectField.getQueryFieldType() == QueryFieldType.SqlSelectField) {
+                selectFields.add(sqlSelectField.getQueryFieldFullName());
+
+                onSelectFieldParse(sqlSelectField);
+            }
+        }
+
+        if (CollectionUtils.isNotEmpty(aggregations)) {
+            List<AbstractAggregationBuilder> groupByList = dslContext.getParseResult().getGroupBy();
+
+            if (CollectionUtils.isNotEmpty(groupByList)) {
+                AggregationBuilder lastLevelAggItem = (AggregationBuilder) groupByList.get(groupByList.size() - 1);
+                for (AbstractAggregationBuilder aggItem : aggregations) {
+                    lastLevelAggItem.subAggregation(aggItem);
                 }
-            });
-            onSelectFieldParse(sqlIdentifier);
+            } else {
+                dslContext.getParseResult().setTopStatsAgg();
+                dslContext.getParseResult().setGroupBy(aggregations);
+            }
         }
+
         dslContext.getParseResult().setQueryFieldList(selectFields);
     }
 
+    private AbstractAggregationBuilder parseStatsAggregation(SQLAggregateExpr aggExpr, String fieldName) {
+        ElasticSqlMethodInvokeHelper.checkStatAggMethod(aggExpr);
+
+        String methodName = aggExpr.getMethodName();
+        if (ElasticSqlMethodInvokeHelper.AGG_MIN_METHOD.equalsIgnoreCase(methodName)) {
+            return AggregationBuilders.min(String.format("%s_%s", ElasticSqlMethodInvokeHelper.AGG_MIN_METHOD, fieldName)).field(fieldName);
+        }
+
+        if (ElasticSqlMethodInvokeHelper.AGG_MAX_METHOD.equalsIgnoreCase(methodName)) {
+            return AggregationBuilders.max(String.format("%s_%s", ElasticSqlMethodInvokeHelper.AGG_MAX_METHOD, fieldName)).field(fieldName);
+        }
+
+        if (ElasticSqlMethodInvokeHelper.AGG_AVG_METHOD.equalsIgnoreCase(methodName)) {
+            return AggregationBuilders.avg(String.format("%s_%s", ElasticSqlMethodInvokeHelper.AGG_AVG_METHOD, fieldName)).field(fieldName);
+        }
+
+        if (ElasticSqlMethodInvokeHelper.AGG_SUM_METHOD.equalsIgnoreCase(methodName)) {
+            return AggregationBuilders.sum(String.format("%s_%s", ElasticSqlMethodInvokeHelper.AGG_SUM_METHOD, fieldName)).field(fieldName);
+        }
+        throw new ElasticSql2DslException(String.format("[syntax error] UnSupport agg method call[%s]", methodName));
+    }
+
     private void onSelectFieldParse(ElasticSqlQueryField field) {
         try {
             parseActionListener.onSelectFieldParse(field);

+ 113 - 81
src/main/java/org/elasticsearch/dsl/parser/syntax/QueryWhereConditionParser.java

@@ -2,24 +2,28 @@ package org.elasticsearch.dsl.parser.syntax;
 
 import com.alibaba.druid.sql.ast.SQLExpr;
 import com.alibaba.druid.sql.ast.expr.*;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Lists;
 import org.apache.commons.collections.CollectionUtils;
+import org.elasticsearch.dsl.bean.AtomFilter;
 import org.elasticsearch.dsl.bean.ElasticDslContext;
 import org.elasticsearch.dsl.bean.ElasticSqlQueryField;
-import org.elasticsearch.dsl.bean.SqlCondition;
+import org.elasticsearch.dsl.bean.SQLCondition;
+import org.elasticsearch.dsl.enums.QueryFieldType;
+import org.elasticsearch.dsl.enums.SQLBoolOperator;
 import org.elasticsearch.dsl.enums.SQLConditionOperator;
+import org.elasticsearch.dsl.enums.SQLConditionType;
 import org.elasticsearch.dsl.exception.ElasticSql2DslException;
 import org.elasticsearch.dsl.parser.QueryParser;
-import org.elasticsearch.dsl.parser.helper.ElasticSqlIdentifierHelper;
-import org.elasticsearch.dsl.parser.listener.ParseActionListener;
 import org.elasticsearch.dsl.parser.helper.ElasticSqlArgTransferHelper;
+import org.elasticsearch.dsl.parser.listener.ParseActionListener;
 import org.elasticsearch.index.query.BoolFilterBuilder;
 import org.elasticsearch.index.query.FilterBuilder;
 import org.elasticsearch.index.query.FilterBuilders;
 import org.elasticsearch.sql.ElasticSqlSelectQueryBlock;
 
 import java.util.List;
-import java.util.function.Consumer;
 
 public class QueryWhereConditionParser implements QueryParser {
 
@@ -32,77 +36,106 @@ public class QueryWhereConditionParser implements QueryParser {
     @Override
     public void parse(ElasticDslContext dslContext) {
         ElasticSqlSelectQueryBlock queryBlock = (ElasticSqlSelectQueryBlock) dslContext.getQueryExpr().getSubQuery().getQuery();
-        BoolFilterBuilder whereCondition = FilterBuilders.boolFilter();
+
         if (queryBlock.getWhere() != null) {
-            SqlCondition sqlCondition = parseFilterCondition(dslContext, queryBlock.getWhere());
-            if (!sqlCondition.isAndOr()) {
-                whereCondition.must(sqlCondition.getFilterList().get(0));
-            } else {
-                for (FilterBuilder filter : sqlCondition.getFilterList()) {
-                    if (sqlCondition.getOperator() == SQLBinaryOperator.BooleanAnd) {
-                        whereCondition.must(filter);
-                    }
-                    if (sqlCondition.getOperator() == SQLBinaryOperator.BooleanOr) {
-                        whereCondition.should(filter);
-                    }
-                }
+            SQLCondition whereCondition = parseFilterCondition(dslContext, queryBlock.getWhere());
+
+            SQLBoolOperator operator = whereCondition.getOperator();
+            if(SQLConditionType.Atom == whereCondition.getSQLConditionType()) {
+                operator = SQLBoolOperator.AND;
             }
+
+            BoolFilterBuilder boolFilter = mergeAtomFilter(whereCondition.getFilterList(), operator);
+            dslContext.getParseResult().setWhereCondition(boolFilter);
         }
-        dslContext.getParseResult().setWhereCondition(whereCondition);
     }
 
-    private SqlCondition parseFilterCondition(ElasticDslContext dslContext, SQLExpr sqlExpr) {
+    private SQLCondition parseFilterCondition(ElasticDslContext dslContext, SQLExpr sqlExpr) {
         if (sqlExpr instanceof SQLBinaryOpExpr) {
             SQLBinaryOpExpr sqlBinOpExpr = (SQLBinaryOpExpr) sqlExpr;
             SQLBinaryOperator binaryOperator = sqlBinOpExpr.getOperator();
             if (SQLBinaryOperator.BooleanAnd == binaryOperator || SQLBinaryOperator.BooleanOr == binaryOperator) {
-                final SqlCondition leftCondition = parseFilterCondition(dslContext, sqlBinOpExpr.getLeft());
-                final SqlCondition rightCondition = parseFilterCondition(dslContext, sqlBinOpExpr.getRight());
 
-                List<FilterBuilder> curFilterList = Lists.newArrayList();
+                SQLBoolOperator operator = SQLBinaryOperator.BooleanAnd == binaryOperator ? SQLBoolOperator.AND : SQLBoolOperator.OR;
 
-                if (!leftCondition.isAndOr() || leftCondition.getOperator() == binaryOperator) {
-                    curFilterList.addAll(leftCondition.getFilterList());
-                } else {
-                    final BoolFilterBuilder subBoolFilter = FilterBuilders.boolFilter();
-                    leftCondition.getFilterList().stream().forEach(new Consumer<FilterBuilder>() {
-                        @Override
-                        public void accept(FilterBuilder filter) {
-                            if (leftCondition.getOperator() == SQLBinaryOperator.BooleanAnd) {
-                                subBoolFilter.must(filter);
-                            }
-                            if (leftCondition.getOperator() == SQLBinaryOperator.BooleanOr) {
-                                subBoolFilter.should(filter);
-                            }
-                        }
-                    });
-                    curFilterList.add(subBoolFilter);
+                SQLCondition leftCondition = parseFilterCondition(dslContext, sqlBinOpExpr.getLeft());
+                SQLCondition rightCondition = parseFilterCondition(dslContext, sqlBinOpExpr.getRight());
+
+                List<AtomFilter> curFilterList = Lists.newArrayList();
+                combineFilterBuilder(curFilterList, leftCondition, operator);
+                combineFilterBuilder(curFilterList, rightCondition, operator);
+
+
+                return new SQLCondition(curFilterList, operator);
+            }
+        }
+        return new SQLCondition(parseAtomFilterCondition(dslContext, sqlExpr), SQLConditionType.Atom);
+    }
+
+    private void combineFilterBuilder(List<AtomFilter> combiner, SQLCondition SQLCondition, SQLBoolOperator binOperator) {
+        if (SQLConditionType.Atom == SQLCondition.getSQLConditionType() || SQLCondition.getOperator() == binOperator) {
+            combiner.addAll(SQLCondition.getFilterList());
+        }
+        else {
+            BoolFilterBuilder subBoolFilter = mergeAtomFilter(SQLCondition.getFilterList(), binOperator);
+            combiner.add(new AtomFilter(subBoolFilter));
+        }
+    }
+
+    private BoolFilterBuilder mergeAtomFilter(List<AtomFilter> atomFilterList, SQLBoolOperator operator) {
+        BoolFilterBuilder subBoolFilter = FilterBuilders.boolFilter();
+        ListMultimap<String, FilterBuilder> listMultiMap = ArrayListMultimap.create();
+
+        for (AtomFilter atomFilter : atomFilterList) {
+            if(Boolean.FALSE == atomFilter.getNestedFilter()) {
+                if (operator == SQLBoolOperator.AND) {
+                    subBoolFilter.must(atomFilter.getFilter());
+                }
+                if (operator == SQLBoolOperator.OR) {
+                    subBoolFilter.should(atomFilter.getFilter());
                 }
+            }
+            else {
+                String nestedDocPrefix = atomFilter.getNestedFilterPathContext();
+                listMultiMap.put(nestedDocPrefix, atomFilter.getFilter());
+            }
+        }
 
-                if (!rightCondition.isAndOr() || rightCondition.getOperator() == binaryOperator) {
-                    curFilterList.addAll(rightCondition.getFilterList());
-                } else {
-                    final BoolFilterBuilder subBoolFilter = FilterBuilders.boolFilter();
-                    rightCondition.getFilterList().stream().forEach(new Consumer<FilterBuilder>() {
-                        @Override
-                        public void accept(FilterBuilder filter) {
-                            if (rightCondition.getOperator() == SQLBinaryOperator.BooleanAnd) {
-                                subBoolFilter.must(filter);
-                            }
-                            if (rightCondition.getOperator() == SQLBinaryOperator.BooleanOr) {
-                                subBoolFilter.should(filter);
-                            }
-                        }
-                    });
-                    curFilterList.add(subBoolFilter);
+        for (String nestedDocPrefix : listMultiMap.keySet()) {
+            List<FilterBuilder> nestedFilterList = listMultiMap.get(nestedDocPrefix);
+
+            if(nestedFilterList.size() == 1) {
+                if (operator == SQLBoolOperator.AND) {
+                    subBoolFilter.must(FilterBuilders.nestedFilter(nestedDocPrefix, nestedFilterList.get(0)));
+                }
+                if (operator == SQLBoolOperator.OR) {
+                    subBoolFilter.should(FilterBuilders.nestedFilter(nestedDocPrefix, nestedFilterList.get(0)));
+                }
+                continue;
+            }
+
+            BoolFilterBuilder boolNestedFilter = FilterBuilders.boolFilter();
+            for (FilterBuilder nestedFilterItem : nestedFilterList) {
+                if (operator == SQLBoolOperator.AND) {
+                    boolNestedFilter.must(nestedFilterItem);
+                }
+                if (operator == SQLBoolOperator.OR) {
+                    boolNestedFilter.should(nestedFilterItem);
                 }
-                return new SqlCondition(curFilterList, binaryOperator);
             }
+
+            if (operator == SQLBoolOperator.AND) {
+                subBoolFilter.must(FilterBuilders.nestedFilter(nestedDocPrefix, boolNestedFilter));
+            }
+            if (operator == SQLBoolOperator.OR) {
+                subBoolFilter.should(FilterBuilders.nestedFilter(nestedDocPrefix, boolNestedFilter));
+            }
+
         }
-        return new SqlCondition(parseAtomFilterCondition(dslContext, sqlExpr));
+        return subBoolFilter;
     }
 
-    private FilterBuilder parseAtomFilterCondition(ElasticDslContext dslContext, SQLExpr sqlExpr) {
+    private AtomFilter parseAtomFilterCondition(ElasticDslContext dslContext, SQLExpr sqlExpr) {
         if (sqlExpr instanceof SQLBinaryOpExpr) {
             SQLBinaryOpExpr sqlBinOpExpr = (SQLBinaryOpExpr) sqlExpr;
             final SQLBinaryOperator binaryOperator = sqlBinOpExpr.getOperator();
@@ -179,7 +212,7 @@ public class QueryWhereConditionParser implements QueryParser {
                 }
             }
         } else if (sqlExpr instanceof SQLInListExpr) {
-            final SQLInListExpr inListExpr = (SQLInListExpr) sqlExpr;
+            SQLInListExpr inListExpr = (SQLInListExpr) sqlExpr;
             if (CollectionUtils.isEmpty(inListExpr.getTargetList())) {
                 throw new ElasticSql2DslException("[syntax error] In list expr target list cannot be blank");
             }
@@ -215,29 +248,28 @@ public class QueryWhereConditionParser implements QueryParser {
         throw new ElasticSql2DslException("[syntax error] Can not support syntax type: " + sqlExpr.toString());
     }
 
-    private FilterBuilder parseCondition(SQLExpr leftIdentifierExpr, SQLConditionOperator operator, Object[] rightParamValues, String queryAs, final ConditionFilterBuilder filterBuilder) {
-        final List<FilterBuilder> conditionCollector = Lists.newLinkedList();
-        final Object[] pRightParamValues = rightParamValues;
-        final SQLConditionOperator pOperator = operator;
-        ElasticSqlQueryField sqlIdentifier = ElasticSqlIdentifierHelper.parseSqlIdentifier(leftIdentifierExpr, queryAs, new ElasticSqlIdentifierHelper.SQLFlatFieldFunc() {
-            @Override
-            public void parse(String flatFieldName) {
-                FilterBuilder originalFilter = filterBuilder.buildFilter(flatFieldName, pOperator, pRightParamValues);
-                conditionCollector.add(originalFilter);
-            }
-        }, new ElasticSqlIdentifierHelper.SQLNestedFieldFunc() {
-            @Override
-            public void parse(String nestedDocPath, String fieldName) {
-                FilterBuilder originalFilter = filterBuilder.buildFilter(fieldName, pOperator, pRightParamValues);
-                FilterBuilder nestFilter = FilterBuilders.nestedFilter(nestedDocPath, originalFilter);
-                conditionCollector.add(nestFilter);
-            }
-        });
-        if (CollectionUtils.isNotEmpty(conditionCollector)) {
-            onAtomConditionParse(sqlIdentifier, rightParamValues, operator);
-            return conditionCollector.get(0);
+    private AtomFilter parseCondition(SQLExpr leftQueryFieldExpr, SQLConditionOperator operator, Object[] rightParamValues, String queryAs, ConditionFilterBuilder filterBuilder) {
+        QueryFieldParser queryFieldParser = new QueryFieldParser();
+        ElasticSqlQueryField queryField = queryFieldParser.parseConditionQueryField(leftQueryFieldExpr, queryAs);
+
+        AtomFilter atomFilter = null;
+        if (queryField.getQueryFieldType() == QueryFieldType.RootDocField || queryField.getQueryFieldType() == QueryFieldType.InnerDocField) {
+            FilterBuilder originalFilter = filterBuilder.buildFilter(queryField.getQueryFieldFullName(), operator, rightParamValues);
+            atomFilter = new AtomFilter(originalFilter);
         }
-        return null;
+
+        if (queryField.getQueryFieldType() == QueryFieldType.NestedDocField) {
+            FilterBuilder originalFilter = filterBuilder.buildFilter(queryField.getQueryFieldFullName(), operator, rightParamValues);
+            atomFilter = new AtomFilter(originalFilter, queryField.getNestedDocContextPath());
+        }
+
+        if (atomFilter == null) {
+            throw new ElasticSql2DslException(String.format("[syntax error] where condition field can not support type[%s]", queryField.getQueryFieldType()));
+        }
+
+        onAtomConditionParse(queryField, rightParamValues, operator);
+
+        return atomFilter;
     }
 
     private void onAtomConditionParse(ElasticSqlQueryField paramName, Object[] paramValues, SQLConditionOperator operator) {
@@ -257,7 +289,7 @@ public class QueryWhereConditionParser implements QueryParser {
         FilterBuilder buildFilter(String leftIdfName, SQLConditionOperator operator, Object[] rightParamValues);
     }
 
-    private boolean isValidBinOperator(SQLBinaryOperator binaryOperator) {
+    public boolean isValidBinOperator(SQLBinaryOperator binaryOperator) {
         return binaryOperator == SQLBinaryOperator.Equality
                 || binaryOperator == SQLBinaryOperator.NotEqual
                 || binaryOperator == SQLBinaryOperator.LessThanOrGreater

+ 1 - 1
src/test/java/org/elasticsearch/SqlParserSelectFieldTest.java

@@ -15,7 +15,7 @@ public class SqlParserSelectFieldTest {
         ElasticSql2DslParser sql2DslParser = new ElasticSql2DslParser();
         ElasticSqlParseResult parseResult = sql2DslParser.parse(sql);
 
-        Assert.assertEquals(parseResult.getIndex(), "index");
+        Assert.assertEquals(parseResult.getIndices().get(0), "index");
         Assert.assertEquals(parseResult.getType(), "trx_order");
         Assert.assertEquals(parseResult.getQueryAs(), "trx");
     }