spuerx 9 år sedan
förälder
incheckning
60c1c06c96
24 ändrade filer med 807 tillägg och 207 borttagningar
  1. 2 0
      src/main/java/org/elasticsearch/dsl/ElasticSql2DslParser.java
  2. 25 2
      src/main/java/org/elasticsearch/dsl/ElasticSqlParseResult.java
  3. 2 1
      src/main/java/org/elasticsearch/dsl/ElasticSqlParseUtil.java
  4. 22 8
      src/main/java/org/elasticsearch/dsl/parser/QueryLimitSizeParser.java
  5. 45 60
      src/main/java/org/elasticsearch/dsl/parser/QueryOrderConditionParser.java
  6. 37 0
      src/main/java/org/elasticsearch/dsl/parser/QueryRoutingValParser.java
  7. 7 7
      src/main/java/org/elasticsearch/dsl/parser/QuerySelectFieldListParser.java
  8. 7 12
      src/main/java/org/elasticsearch/dsl/parser/QueryWhereConditionParser.java
  9. 1 28
      src/main/java/org/elasticsearch/dsl/parser/helper/ElasticSqlDateParseHelper.java
  10. 98 26
      src/main/java/org/elasticsearch/dsl/parser/helper/ElasticSqlIdentifierHelper.java
  11. 112 0
      src/main/java/org/elasticsearch/dsl/parser/helper/ElasticSqlMethodInvokeHelper.java
  12. 22 4
      src/main/java/org/elasticsearch/sql/ElasticSqlExprParser.java
  13. 5 0
      src/main/java/org/elasticsearch/sql/ElasticSqlLexer.java
  14. 12 1
      src/main/java/org/elasticsearch/sql/ElasticSqlSelectParser.java
  15. 34 4
      src/main/java/org/elasticsearch/sql/ElasticSqlSelectQueryBlock.java
  16. 2 1
      src/main/java/org/elasticsearch/ElasticMockClient.java
  17. 0 12
      src/main/java/org/elasticsearch/utils/GsonHelper.java
  18. 0 20
      src/main/java/org/elasticsearch/utils/StringUtils.java
  19. 30 0
      src/test/java/org/elasticsearch/SqlParserLimitTest.java
  20. 60 0
      src/test/java/org/elasticsearch/SqlParserOrderByTest.java
  21. 42 0
      src/test/java/org/elasticsearch/SqlParserRoutingTest.java
  22. 104 0
      src/test/java/org/elasticsearch/SqlParserSelectFieldTest.java
  23. 0 21
      src/test/java/org/elasticsearch/SqlParserTest.java
  24. 138 0
      src/test/java/org/elasticsearch/SqlParserWhereConditionTest.java

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

@@ -89,6 +89,8 @@ public class ElasticSql2DslParser {
                 new QueryWhereConditionParser(),
                 //解析SQL排序条件
                 new QueryOrderConditionParser(),
+                //解析路由参数
+                new QueryRoutingValParser(),
                 //解析SQL查询指定的字段
                 new QuerySelectFieldListParser(),
                 //解析SQL的分页条数

+ 25 - 2
src/main/java/org/elasticsearch/dsl/ElasticSqlParseResult.java

@@ -2,13 +2,13 @@ package org.elasticsearch.dsl;
 
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang.StringUtils;
-import org.elasticsearch.ElasticMockClient;
 import org.elasticsearch.action.search.SearchRequestBuilder;
 import org.elasticsearch.client.Client;
 import org.elasticsearch.index.query.BoolFilterBuilder;
 import org.elasticsearch.index.query.FilterBuilders;
 import org.elasticsearch.index.query.QueryBuilders;
 import org.elasticsearch.search.sort.SortBuilder;
+import org.elasticsearch.util.ElasticMockClient;
 
 import java.util.LinkedList;
 import java.util.List;
@@ -25,6 +25,8 @@ public class ElasticSqlParseResult {
     private String type;
     /*查询索引别名*/
     private String queryAs;
+    /*查询路由值*/
+    private List<String> routingBy;
     /*查询字段列表*/
     private List<String> queryFieldList;
     /*SQL的where条件*/
@@ -96,6 +98,22 @@ public class ElasticSqlParseResult {
         this.queryAs = queryAs;
     }
 
+    public BoolFilterBuilder getFilterBuilder() {
+        return filterBuilder;
+    }
+
+    public List<SortBuilder> getSortBuilderList() {
+        return sortBuilderList;
+    }
+
+    public List<String> getRoutingBy() {
+        return routingBy;
+    }
+
+    public void setRoutingBy(List<String> routingBy) {
+        this.routingBy = routingBy;
+    }
+
     public SearchRequestBuilder toRequest(Client client) {
         final SearchRequestBuilder requestBuilder = new SearchRequestBuilder(client);
 
@@ -131,6 +149,10 @@ public class ElasticSqlParseResult {
         if (CollectionUtils.isNotEmpty(queryFieldList)) {
             requestBuilder.setFetchSource(queryFieldList.toArray(new String[queryFieldList.size()]), null);
         }
+
+        if(CollectionUtils.isNotEmpty(routingBy)) {
+            requestBuilder.setRouting(routingBy.toArray(new String[routingBy.size()]));
+        }
         return requestBuilder;
     }
 
@@ -144,6 +166,7 @@ public class ElasticSqlParseResult {
 
     @Override
     public String toString() {
-        return toDsl();
+        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.toString(), toDsl());
     }
 }

+ 2 - 1
src/main/java/org/elasticsearch/dsl/ElasticSqlParseUtil.java

@@ -4,6 +4,7 @@ import com.alibaba.druid.sql.ast.SQLExpr;
 import com.alibaba.druid.sql.ast.expr.*;
 import org.elasticsearch.dsl.exception.ElasticSql2DslException;
 import org.elasticsearch.dsl.parser.helper.ElasticSqlDateParseHelper;
+import org.elasticsearch.dsl.parser.helper.ElasticSqlMethodInvokeHelper;
 
 import java.util.List;
 
@@ -51,7 +52,7 @@ public class ElasticSqlParseUtil {
 
             //解析date函数
             if (ElasticSqlDateParseHelper.isDateMethod(methodExpr)) {
-                ElasticSqlDateParseHelper.checkDateMethod(methodExpr);
+                ElasticSqlMethodInvokeHelper.checkDateMethod(methodExpr);
                 String patternArg = (String) ElasticSqlParseUtil.transferSqlArg(methodExpr.getParameters().get(0), sqlArgs);
                 String timeValArg = (String) ElasticSqlParseUtil.transferSqlArg(methodExpr.getParameters().get(1), sqlArgs);
                 return ElasticSqlDateParseHelper.formatDefaultEsDate(patternArg, timeValArg);

+ 22 - 8
src/main/java/org/elasticsearch/dsl/parser/QueryLimitSizeParser.java

@@ -1,7 +1,10 @@
 package org.elasticsearch.dsl.parser;
 
+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.ElasticDslContext;
+import org.elasticsearch.dsl.ElasticSqlParseUtil;
 import org.elasticsearch.dsl.exception.ElasticSql2DslException;
 import org.elasticsearch.sql.ElasticSqlSelectQueryBlock;
 
@@ -10,18 +13,29 @@ public class QueryLimitSizeParser implements QueryParser {
     public void parse(ElasticDslContext dslContext) {
         ElasticSqlSelectQueryBlock queryBlock = (ElasticSqlSelectQueryBlock) dslContext.getQueryExpr().getSubQuery().getQuery();
         if (queryBlock.getLimit() != null) {
-            if (!(queryBlock.getLimit().getOffset() instanceof SQLIntegerExpr)) {
-                throw new ElasticSql2DslException("[syntax error] Sql limit expr offset should be a non-negative number");
-            }
-            dslContext.getParseResult().setFrom(((SQLIntegerExpr) queryBlock.getLimit().getOffset()).getNumber().intValue());
+            Integer from = parseLimitInteger(queryBlock.getLimit().getOffset(), dslContext.getSqlArgs());
+            dslContext.getParseResult().setFrom(from);
 
-            if (!(queryBlock.getLimit().getRowCount() instanceof SQLIntegerExpr)) {
-                throw new ElasticSql2DslException("[syntax error] Sql limit expr row count should be a non-negative number");
-            }
-            dslContext.getParseResult().setSize(((SQLIntegerExpr) queryBlock.getLimit().getRowCount()).getNumber().intValue());
+            Integer size = parseLimitInteger(queryBlock.getLimit().getRowCount(), dslContext.getSqlArgs());
+            dslContext.getParseResult().setSize(size);
         } else {
             dslContext.getParseResult().setFrom(0);
             dslContext.getParseResult().setSize(15);
         }
     }
+
+    public Integer parseLimitInteger(SQLExpr limitInt, Object[] args) {
+        if (limitInt instanceof SQLIntegerExpr) {
+            return ((SQLIntegerExpr) limitInt).getNumber().intValue();
+        } else if (limitInt instanceof SQLVariantRefExpr) {
+            SQLVariantRefExpr varLimitExpr = (SQLVariantRefExpr) limitInt;
+            final Object targetVal = ElasticSqlParseUtil.transferSqlArg(varLimitExpr, args);
+            if (!(targetVal instanceof Integer)) {
+                throw new ElasticSql2DslException("[syntax error] Sql limit expr should be a non-negative number");
+            }
+            return Integer.valueOf(targetVal.toString());
+        } else {
+            throw new ElasticSql2DslException("[syntax error] Sql limit expr should be a non-negative number");
+        }
+    }
 }

+ 45 - 60
src/main/java/org/elasticsearch/dsl/parser/QueryOrderConditionParser.java

@@ -3,7 +3,10 @@ package org.elasticsearch.dsl.parser;
 import com.alibaba.druid.sql.ast.SQLExpr;
 import com.alibaba.druid.sql.ast.SQLOrderBy;
 import com.alibaba.druid.sql.ast.SQLOrderingSpecification;
-import com.alibaba.druid.sql.ast.expr.*;
+import com.alibaba.druid.sql.ast.expr.SQLCharExpr;
+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.alibaba.druid.sql.ast.statement.SQLSelectOrderByItem;
 import com.google.common.collect.Lists;
 import org.apache.commons.collections.CollectionUtils;
@@ -11,6 +14,7 @@ import org.elasticsearch.dsl.ElasticDslContext;
 import org.elasticsearch.dsl.ElasticSqlParseUtil;
 import org.elasticsearch.dsl.exception.ElasticSql2DslException;
 import org.elasticsearch.dsl.parser.helper.ElasticSqlIdentifierHelper;
+import org.elasticsearch.dsl.parser.helper.ElasticSqlMethodInvokeHelper;
 import org.elasticsearch.search.sort.FieldSortBuilder;
 import org.elasticsearch.search.sort.SortBuilder;
 import org.elasticsearch.search.sort.SortBuilders;
@@ -49,77 +53,58 @@ public class QueryOrderConditionParser implements QueryParser {
         }
         if (orderByItem.getExpr() instanceof SQLMethodInvokeExpr) {
             final SQLMethodInvokeExpr methodInvokeExpr = (SQLMethodInvokeExpr) orderByItem.getExpr();
-            checkNvlMethod(methodInvokeExpr);
-
-            final Object valueArg = ElasticSqlParseUtil.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());
+            //nvl method
+            if (ElasticSqlMethodInvokeHelper.NVL_METHOD.equalsIgnoreCase(methodInvokeExpr.getMethodName())) {
+                ElasticSqlMethodInvokeHelper.checkNvlMethod(methodInvokeExpr);
+                final Object valueArg = ElasticSqlParseUtil.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;
                     }
-                    return fieldSortBuilder;
-                }
-            });
-        }
-        throw new ElasticSql2DslException("[syntax error] ElasticSql cannot support sort type: " + orderByItem.getExpr().getClass());
-    }
-
-    private void checkNvlMethod(SQLMethodInvokeExpr nvlInvokeExpr) {
-        if (!"nvl".equalsIgnoreCase(nvlInvokeExpr.getMethodName())) {
-            throw new ElasticSql2DslException("[syntax error] ElasticSql sort condition only support nvl method invoke");
-        }
-
-        if (CollectionUtils.isEmpty(nvlInvokeExpr.getParameters()) || nvlInvokeExpr.getParameters().size() > 3) {
-            throw new ElasticSql2DslException(String.format("[syntax error] There is no %s args method named nvl",
-                    nvlInvokeExpr.getParameters() != null ? nvlInvokeExpr.getParameters().size() : 0));
-        }
-
-        SQLExpr fieldArg = nvlInvokeExpr.getParameters().get(0);
-        SQLExpr valueArg = nvlInvokeExpr.getParameters().get(1);
-
-        if (!(fieldArg instanceof SQLPropertyExpr) && !(fieldArg instanceof SQLIdentifierExpr)) {
-            throw new ElasticSql2DslException("[syntax error] The first arg of nvl method should be field param name");
-        }
-
-        if (!(valueArg instanceof SQLCharExpr) && !(valueArg instanceof SQLIntegerExpr) && !(valueArg instanceof SQLNumberExpr)) {
-            throw new ElasticSql2DslException("[syntax error] The second arg of nvl method should be number or string");
-        }
-
-        if (nvlInvokeExpr.getParameters().size() == 3) {
-            SQLExpr sortModArg = nvlInvokeExpr.getParameters().get(2);
-            if (!(sortModArg instanceof SQLCharExpr)) {
-                throw new ElasticSql2DslException("[syntax error] The third arg of nvl method should be string");
+                });
             }
-            String sortModeText = ((SQLCharExpr) sortModArg).getText();
-            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]");
+
+            //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
+                        );
+                    }
+                });
             }
         }
+        throw new ElasticSql2DslException("[syntax error] ElasticSql cannot 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.ElasticSqlTopIdfFunc() {
+        ElasticSqlIdentifierHelper.parseSqlIdentifier(sqlExpr, queryAs, new ElasticSqlIdentifierHelper.ElasticSqlSinglePropertyFunc() {
             @Override
-            public void parse(String idfName) {
-                FieldSortBuilder originalSort = sortBuilder.buildSort(idfName);
+            public void parse(String propertyName) {
+                FieldSortBuilder originalSort = sortBuilder.buildSort(propertyName);
                 tmpSortList.add(originalSort);
             }
-        }, new ElasticSqlIdentifierHelper.ElasticSqlNestIdfFunc() {
+        }, new ElasticSqlIdentifierHelper.ElasticSqlPathPropertyFunc() {
             @Override
-            public void parse(String nestPath, String idfName) {
-                FieldSortBuilder originalSort = sortBuilder.buildSort(idfName);
-                originalSort.setNestedPath(nestPath);
+            public void parse(String propertyPath, String propertyName) {
+                FieldSortBuilder originalSort = sortBuilder.buildSort(propertyName);
+                originalSort.setNestedPath(propertyPath);
                 tmpSortList.add(originalSort);
             }
         });

+ 37 - 0
src/main/java/org/elasticsearch/dsl/parser/QueryRoutingValParser.java

@@ -0,0 +1,37 @@
+package org.elasticsearch.dsl.parser;
+
+import com.alibaba.druid.sql.ast.SQLExpr;
+import com.alibaba.druid.sql.ast.expr.SQLCharExpr;
+import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr;
+import com.google.common.collect.Lists;
+import org.apache.commons.collections.CollectionUtils;
+import org.elasticsearch.dsl.ElasticDslContext;
+import org.elasticsearch.dsl.ElasticSqlParseUtil;
+import org.elasticsearch.dsl.exception.ElasticSql2DslException;
+import org.elasticsearch.sql.ElasticSqlSelectQueryBlock;
+
+import java.util.List;
+
+public class QueryRoutingValParser implements QueryParser {
+    @Override
+    public void parse(ElasticDslContext dslContext) {
+        ElasticSqlSelectQueryBlock queryBlock = (ElasticSqlSelectQueryBlock) dslContext.getQueryExpr().getSubQuery().getQuery();
+        if (queryBlock.getRouting() != null && CollectionUtils.isNotEmpty(queryBlock.getRouting().getRoutingValues())) {
+            List<String> routingStringValues = Lists.newLinkedList();
+            for (SQLExpr routingVal : queryBlock.getRouting().getRoutingValues()) {
+                if (routingVal instanceof SQLCharExpr) {
+                    routingStringValues.add(((SQLCharExpr) routingVal).getText());
+                } else if (routingVal instanceof SQLVariantRefExpr) {
+                    Object targetVal = ElasticSqlParseUtil.transferSqlArg(routingVal, dslContext.getSqlArgs());
+                    if (!(targetVal instanceof String)) {
+                        throw new ElasticSql2DslException("[syntax error] Index routing val must be a string");
+                    }
+                    routingStringValues.add((String) targetVal);
+                } else {
+                    throw new ElasticSql2DslException("[syntax error] Index routing val must be a string");
+                }
+            }
+            dslContext.getParseResult().setRoutingBy(routingStringValues);
+        }
+    }
+}

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

@@ -18,17 +18,17 @@ public class QuerySelectFieldListParser implements QueryParser {
 
         final List<String> selectFields = Lists.newLinkedList();
         for (SQLSelectItem selectField : queryBlock.getSelectList()) {
-            ElasticSqlIdentifierHelper.parseSqlIdentifier(selectField.getExpr(), dslContext.getParseResult().getQueryAs(), new ElasticSqlIdentifierHelper.ElasticSqlTopIdfFunc() {
+            ElasticSqlIdentifierHelper.parseSqlIdentifier(selectField.getExpr(), dslContext.getParseResult().getQueryAs(), new ElasticSqlIdentifierHelper.ElasticSqlSinglePropertyFunc() {
                 @Override
-                public void parse(String idfName) {
-                    if (!SQL_FIELD_MATCH_ALL.equals(idfName)) {
-                        selectFields.add(idfName);
+                public void parse(String propertyName) {
+                    if (!SQL_FIELD_MATCH_ALL.equals(propertyName)) {
+                        selectFields.add(propertyName);
                     }
                 }
-            }, new ElasticSqlIdentifierHelper.ElasticSqlNestIdfFunc() {
+            }, new ElasticSqlIdentifierHelper.ElasticSqlPathPropertyFunc() {
                 @Override
-                public void parse(String nestPath, String idfName) {
-                    selectFields.add(String.format("%s.%s", nestPath, idfName));
+                public void parse(String propertyPath, String propertyName) {
+                    selectFields.add(String.format("%s.%s", propertyPath, propertyName));
                 }
             });
         }

+ 7 - 12
src/main/java/org/elasticsearch/dsl/parser/QueryWhereConditionParser.java

@@ -94,11 +94,6 @@ public class QueryWhereConditionParser implements QueryParser {
             final SQLBinaryOperator binaryOperator = sqlBinOpExpr.getOperator();
 
             if (ElasticSqlParseUtil.isValidBinOperator(binaryOperator)) {
-                //以下是二元运算,左边必须是变量
-                if (!(sqlBinOpExpr.getLeft() instanceof SQLPropertyExpr || sqlBinOpExpr.getLeft() instanceof SQLIdentifierExpr)) {
-                    throw new ElasticSql2DslException("[syntax error] Binary operation expr left part should be a param name");
-                }
-
                 //EQ NEQ
                 if (SQLBinaryOperator.Equality == binaryOperator || SQLBinaryOperator.LessThanOrGreater == binaryOperator || SQLBinaryOperator.NotEqual == binaryOperator) {
                     final Object targetVal = ElasticSqlParseUtil.transferSqlArg(sqlBinOpExpr.getRight(), dslContext.getSqlArgs());
@@ -192,17 +187,17 @@ public class QueryWhereConditionParser implements QueryParser {
 
     private FilterBuilder parseCondition(SQLExpr sqlExpr, String queryAs, final ConditionFilterBuilder filterBuilder) {
         final List<FilterBuilder> tmpFilterList = Lists.newLinkedList();
-        ElasticSqlIdentifierHelper.parseSqlIdentifier(sqlExpr, queryAs, new ElasticSqlIdentifierHelper.ElasticSqlTopIdfFunc() {
+        ElasticSqlIdentifierHelper.parseSqlIdentifier(sqlExpr, queryAs, new ElasticSqlIdentifierHelper.ElasticSqlSinglePropertyFunc() {
             @Override
-            public void parse(String idfName) {
-                FilterBuilder originalFilter = filterBuilder.buildFilter(idfName);
+            public void parse(String propertyName) {
+                FilterBuilder originalFilter = filterBuilder.buildFilter(propertyName);
                 tmpFilterList.add(originalFilter);
             }
-        }, new ElasticSqlIdentifierHelper.ElasticSqlNestIdfFunc() {
+        }, new ElasticSqlIdentifierHelper.ElasticSqlPathPropertyFunc() {
             @Override
-            public void parse(String nestPath, String idfName) {
-                FilterBuilder originalFilter = filterBuilder.buildFilter(idfName);
-                FilterBuilder nestFilter = FilterBuilders.nestedFilter(nestPath, originalFilter);
+            public void parse(String propertyPath, String propertyName) {
+                FilterBuilder originalFilter = filterBuilder.buildFilter(propertyName);
+                FilterBuilder nestFilter = FilterBuilders.nestedFilter(propertyPath, originalFilter);
                 tmpFilterList.add(nestFilter);
             }
         });

+ 1 - 28
src/main/java/org/elasticsearch/dsl/parser/helper/ElasticSqlDateParseHelper.java

@@ -1,10 +1,6 @@
 package org.elasticsearch.dsl.parser.helper;
 
-import com.alibaba.druid.sql.ast.SQLExpr;
-import com.alibaba.druid.sql.ast.expr.SQLCharExpr;
 import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;
-import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr;
-import org.apache.commons.collections.CollectionUtils;
 import org.elasticsearch.dsl.exception.ElasticSql2DslException;
 
 import java.text.ParseException;
@@ -52,7 +48,6 @@ public class ElasticSqlDateParseHelper {
             String getPatternString() {
                 return "yyyy-MM-dd";
             }
-
         };
 
         abstract Pattern getPattern();
@@ -61,7 +56,7 @@ public class ElasticSqlDateParseHelper {
     }
 
     public static boolean isDateMethod(SQLMethodInvokeExpr dateMethodExpr) {
-        return "date".equalsIgnoreCase(dateMethodExpr.getMethodName());
+        return ElasticSqlMethodInvokeHelper.DATE_METHOD.equalsIgnoreCase(dateMethodExpr.getMethodName());
     }
 
     public static boolean isDateArgStringValue(String date) {
@@ -109,26 +104,4 @@ public class ElasticSqlDateParseHelper {
             throw new ElasticSql2DslException("[syntax error] Parse time arg error: " + timeValArg);
         }
     }
-
-    public static void checkDateMethod(SQLMethodInvokeExpr dateInvokeExpr) {
-        if (!"date".equalsIgnoreCase(dateInvokeExpr.getMethodName())) {
-            throw new ElasticSql2DslException("[syntax error] ElasticSql not support method:" + dateInvokeExpr.getMethodName());
-        }
-
-        if (CollectionUtils.isEmpty(dateInvokeExpr.getParameters()) || dateInvokeExpr.getParameters().size() != 2) {
-            throw new ElasticSql2DslException(String.format("[syntax error] There is no %s args method named nvl",
-                    dateInvokeExpr.getParameters() != null ? dateInvokeExpr.getParameters().size() : 0));
-        }
-
-        SQLExpr patternArg = dateInvokeExpr.getParameters().get(0);
-        SQLExpr timeValArg = dateInvokeExpr.getParameters().get(1);
-
-        if (!(patternArg instanceof SQLCharExpr) && !(patternArg instanceof SQLVariantRefExpr)) {
-            throw new ElasticSql2DslException("[syntax error] The first arg of date method should be a time pattern");
-        }
-
-        if (!(timeValArg instanceof SQLCharExpr) && !(timeValArg instanceof SQLVariantRefExpr)) {
-            throw new ElasticSql2DslException("[syntax error] The second arg of date method should be a string of time");
-        }
-    }
 }

+ 98 - 26
src/main/java/org/elasticsearch/dsl/parser/helper/ElasticSqlIdentifierHelper.java

@@ -2,49 +2,121 @@ package org.elasticsearch.dsl.parser.helper;
 
 import com.alibaba.druid.sql.ast.SQLExpr;
 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 org.apache.commons.lang.StringUtils;
 import org.elasticsearch.dsl.exception.ElasticSql2DslException;
-import org.elasticsearch.utils.StringUtils;
 
 public class ElasticSqlIdentifierHelper {
 
-    public static void parseSqlIdentifier(SQLExpr idfSqlExpr, String queryAsAlias, ElasticSqlTopIdfFunc topIdfFunc, ElasticSqlNestIdfFunc nestIdfFunc) {
-        if (idfSqlExpr instanceof SQLIdentifierExpr) {
-            topIdfFunc.parse(((SQLIdentifierExpr) idfSqlExpr).getName());
+    /**
+     * 字段标识符处理
+     *
+     * @param propertyNameExpr   待处理字段表达式
+     * @param queryAsAlias       文档类型别名
+     * @param singlePropertyFunc 非内嵌类型处理策略(inner/property)
+     * @param pathPropertyFunc   内嵌类型处理逻辑(nested)
+     */
+    public static void parseSqlIdentifier(final SQLExpr propertyNameExpr, final String queryAsAlias,
+                                          final ElasticSqlSinglePropertyFunc singlePropertyFunc, final ElasticSqlPathPropertyFunc pathPropertyFunc) {
+        if (propertyNameExpr instanceof SQLMethodInvokeExpr) {
+            //如果指定是inner doc类型
+            SQLMethodInvokeExpr innerObjExpr = (SQLMethodInvokeExpr) propertyNameExpr;
+            if ("inner_doc".equalsIgnoreCase(innerObjExpr.getMethodName())) {
+                ElasticSqlMethodInvokeHelper.checkInnerDocMethod(innerObjExpr);
+
+                ElasticSqlIdentifierHelper.parseSqlIdf(innerObjExpr.getParameters().get(0), queryAsAlias, new ElasticSqlSinglePropertyFunc() {
+                    @Override
+                    public void parse(String propertyName) {
+                        throw new ElasticSql2DslException("[syntax error] The arg of method inner_doc must contain property reference path");
+                    }
+                }, new ElasticSqlPathPropertyFunc() {
+                    @Override
+                    public void parse(String propertyPath, String propertyName) {
+                        singlePropertyFunc.parse(String.format("%s.%s", propertyPath, propertyName));
+                    }
+                });
+                return;
+            }
+
+            //如果执行是nested doc类型
+            if ("nested_doc".equalsIgnoreCase(innerObjExpr.getMethodName())) {
+                ElasticSqlMethodInvokeHelper.checkNestedDocMethod(innerObjExpr);
+
+                ElasticSqlIdentifierHelper.parseSqlIdf(innerObjExpr.getParameters().get(0), queryAsAlias, new ElasticSqlSinglePropertyFunc() {
+                    @Override
+                    public void parse(String propertyName) {
+                        throw new ElasticSql2DslException("[syntax error] The arg of method nested_doc must contain property reference path");
+                    }
+                }, new ElasticSqlPathPropertyFunc() {
+                    @Override
+                    public void parse(String propertyPath, String propertyName) {
+                        pathPropertyFunc.parse(propertyPath, propertyName);
+                    }
+                });
+                return;
+            }
+
+            throw new ElasticSql2DslException("[syntax error] ElasticSql identifier method only support nested_doc and inner_doc");
         }
-        if (idfSqlExpr instanceof SQLPropertyExpr) {
-            SQLPropertyExpr propertyExpr = (SQLPropertyExpr) idfSqlExpr;
 
-            if (propertyExpr.getOwner() instanceof SQLPropertyExpr) {
-                SQLPropertyExpr ownerPropertyExpr = (SQLPropertyExpr) propertyExpr.getOwner();
-                if (!(ownerPropertyExpr.getOwner() instanceof SQLIdentifierExpr)) {
-                    throw new ElasticSql2DslException("[syntax error] Select field ref level should <= 3");
-                }
-                SQLIdentifierExpr superOwnerIdfExpr = (SQLIdentifierExpr) ownerPropertyExpr.getOwner();
-                if (StringUtils.isNotBlank(queryAsAlias) && queryAsAlias.equalsIgnoreCase(superOwnerIdfExpr.getName())) {
-                    nestIdfFunc.parse(ownerPropertyExpr.getName(), propertyExpr.getName());
-                } else {
-                    throw new ElasticSql2DslException("[syntax error] Select field qualifier not support: " + superOwnerIdfExpr.getName());
-                }
-            } else if (propertyExpr.getOwner() instanceof SQLIdentifierExpr) {
-                SQLIdentifierExpr ownerIdfExpr = (SQLIdentifierExpr) propertyExpr.getOwner();
-                if (StringUtils.isNotBlank(queryAsAlias) && queryAsAlias.equalsIgnoreCase(ownerIdfExpr.getName())) {
-                    topIdfFunc.parse(propertyExpr.getName());
+        //默认按照inner doc或者property name来处理
+        ElasticSqlIdentifierHelper.parseSqlIdf(propertyNameExpr, queryAsAlias, new ElasticSqlSinglePropertyFunc() {
+            @Override
+            public void parse(String propertyName) {
+                singlePropertyFunc.parse(propertyName);
+            }
+        }, new ElasticSqlPathPropertyFunc() {
+            @Override
+            public void parse(String propertyPath, String propertyName) {
+                singlePropertyFunc.parse(String.format("%s.%s", propertyPath, propertyName));
+            }
+        });
+    }
+
+    private static void parseSqlIdf(final SQLExpr propertyNameExpr, final String queryAsAlias,
+                                    final ElasticSqlSinglePropertyFunc singlePropertyFunc, final ElasticSqlPathPropertyFunc pathPropertyFunc) {
+        //如果标识符直接返回
+        if (propertyNameExpr instanceof SQLIdentifierExpr) {
+            singlePropertyFunc.parse(((SQLIdentifierExpr) propertyNameExpr).getName());
+        }
+
+        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 {
-                    nestIdfFunc.parse(ownerIdfExpr.getName(), propertyExpr.getName());
+                    //使用别名,但不以别名开头
+                    pathPropertyFunc.parse(ownerIdfNameBuilder.toString(), propertyExpr.getName());
                 }
+            } else {
+                //如果使用未使用别名
+                pathPropertyFunc.parse(ownerIdfNameBuilder.toString(), propertyExpr.getName());
             }
         }
     }
 
+
     @FunctionalInterface
-    public interface ElasticSqlTopIdfFunc {
-        void parse(String idfName);
+    public interface ElasticSqlSinglePropertyFunc {
+        void parse(String propertyName);
     }
 
     @FunctionalInterface
-    public interface ElasticSqlNestIdfFunc {
-        void parse(String nestPath, String idfName);
+    public interface ElasticSqlPathPropertyFunc {
+        void parse(String propertyPath, String propertyName);
     }
 
 }

+ 112 - 0
src/main/java/org/elasticsearch/dsl/parser/helper/ElasticSqlMethodInvokeHelper.java

@@ -0,0 +1,112 @@
+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.exception.ElasticSql2DslException;
+import org.elasticsearch.dsl.parser.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 void checkDateMethod(SQLMethodInvokeExpr dateInvokeExpr) {
+        if (!DATE_METHOD.equalsIgnoreCase(dateInvokeExpr.getMethodName())) {
+            throw new ElasticSql2DslException("[syntax error] ElasticSql not support method:" + dateInvokeExpr.getMethodName());
+        }
+
+        if (CollectionUtils.isEmpty(dateInvokeExpr.getParameters()) || dateInvokeExpr.getParameters().size() != 2) {
+            throw new ElasticSql2DslException(String.format("[syntax error] There is no %s args method named date",
+                    dateInvokeExpr.getParameters() != null ? dateInvokeExpr.getParameters().size() : 0));
+        }
+
+        SQLExpr patternArg = dateInvokeExpr.getParameters().get(0);
+        SQLExpr timeValArg = dateInvokeExpr.getParameters().get(1);
+
+        if (!(patternArg instanceof SQLCharExpr) && !(patternArg instanceof SQLVariantRefExpr)) {
+            throw new ElasticSql2DslException("[syntax error] The first arg of date method should be a time pattern");
+        }
+
+        if (!(timeValArg instanceof SQLCharExpr) && !(timeValArg instanceof SQLVariantRefExpr)) {
+            throw new ElasticSql2DslException("[syntax error] The second arg of date method should be a string of time");
+        }
+    }
+
+    public static void checkInnerDocMethod(SQLMethodInvokeExpr innerDocInvokeExpr) {
+        if (!INNER_DOC_METHOD.equalsIgnoreCase(innerDocInvokeExpr.getMethodName())) {
+            throw new ElasticSql2DslException("[syntax error] ElasticSql 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] ElasticSql sort condition only support nvl method invoke");
+        }
+
+        if (CollectionUtils.isEmpty(nvlInvokeExpr.getParameters()) || nvlInvokeExpr.getParameters().size() > 3) {
+            throw new ElasticSql2DslException(String.format("[syntax error] There is no %s args method named nvl",
+                    nvlInvokeExpr.getParameters() != null ? nvlInvokeExpr.getParameters().size() : 0));
+        }
+
+        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)) {
+            throw new ElasticSql2DslException("[syntax error] The first arg of nvl method should be field param name");
+        }
+
+        if (!(valueArg instanceof SQLCharExpr) && !(valueArg instanceof SQLIntegerExpr) && !(valueArg instanceof SQLNumberExpr)) {
+            throw new ElasticSql2DslException("[syntax error] The second arg of nvl method should be number or string");
+        }
+
+        if (nvlInvokeExpr.getParameters().size() == 3) {
+            SQLExpr sortModArg = nvlInvokeExpr.getParameters().get(2);
+            if (!(sortModArg instanceof SQLCharExpr)) {
+                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)) {
+                throw new ElasticSql2DslException("[syntax error] The third arg of nvl method should be one of the string[min,max,avg,sum]");
+            }
+        }
+    }
+}

+ 22 - 4
src/main/java/org/elasticsearch/sql/ElasticSqlExprParser.java

@@ -1,10 +1,10 @@
 package org.elasticsearch.sql;
 
 import com.alibaba.druid.sql.ast.SQLExpr;
-import com.alibaba.druid.sql.parser.Lexer;
-import com.alibaba.druid.sql.parser.SQLExprParser;
-import com.alibaba.druid.sql.parser.SQLSelectParser;
-import com.alibaba.druid.sql.parser.Token;
+import com.alibaba.druid.sql.parser.*;
+import com.google.common.collect.Lists;
+
+import java.util.List;
 
 public class ElasticSqlExprParser extends SQLExprParser {
 
@@ -45,4 +45,22 @@ public class ElasticSqlExprParser extends SQLExprParser {
 
         return null;
     }
+
+    public ElasticSqlSelectQueryBlock.Routing parseRoutingBy() {
+        if(lexer.token() == Token.INDEX && "ROUTING".equalsIgnoreCase(lexer.stringVal())) {
+            lexer.nextToken();
+
+            accept(Token.BY);
+
+            List<SQLExpr> routingValues = Lists.newLinkedList();
+            routingValues.add(this.expr());
+
+            while(lexer.token() == (Token.COMMA)) {
+                lexer.nextToken();
+                routingValues.add(this.expr());
+            }
+            return new ElasticSqlSelectQueryBlock.Routing(routingValues);
+        }
+        return null;
+    }
 }

+ 5 - 0
src/main/java/org/elasticsearch/sql/ElasticSqlLexer.java

@@ -47,6 +47,11 @@ public class ElasticSqlLexer extends Lexer {
         map.put("XOR", Token.XOR);
         map.put("COMMENT", Token.COMMENT);
 
+        map.put("ROUTING", Token.INDEX);
+        map.put("NESTED", Token.INDEX);
+        map.put("INNER", Token.INNER);
+        map.put("JOIN", Token.JOIN);
+
         DEFAULT_ELASTIC_SQL_KEYWORDS = new Keywords(map);
     }
 

+ 12 - 1
src/main/java/org/elasticsearch/sql/ElasticSqlSelectParser.java

@@ -1,7 +1,10 @@
 package org.elasticsearch.sql;
 
 import com.alibaba.druid.sql.ast.SQLSetQuantifier;
-import com.alibaba.druid.sql.ast.statement.*;
+import com.alibaba.druid.sql.ast.statement.SQLExprTableSource;
+import com.alibaba.druid.sql.ast.statement.SQLJoinTableSource;
+import com.alibaba.druid.sql.ast.statement.SQLSelectQuery;
+import com.alibaba.druid.sql.ast.statement.SQLTableSource;
 import com.alibaba.druid.sql.parser.ParserException;
 import com.alibaba.druid.sql.parser.SQLExprParser;
 import com.alibaba.druid.sql.parser.SQLSelectParser;
@@ -17,6 +20,10 @@ public class ElasticSqlSelectParser extends SQLSelectParser {
         return ((ElasticSqlExprParser) this.exprParser).parseLimit();
     }
 
+    public ElasticSqlSelectQueryBlock.Routing parseRoutingBy() {
+        return ((ElasticSqlExprParser) this.exprParser).parseRoutingBy();
+    }
+
     @Override
     public SQLSelectQuery query() {
         if (lexer.token() == Token.LPAREN) {
@@ -51,6 +58,10 @@ public class ElasticSqlSelectParser extends SQLSelectParser {
         parseGroupBy(queryBlock);
         queryBlock.setOrderBy(this.exprParser.parseOrderBy());
 
+        if (lexer.token() == Token.INDEX && "ROUTING".equalsIgnoreCase(lexer.stringVal())) {
+            queryBlock.setRouting(parseRoutingBy());
+        }
+
         if (lexer.token() == Token.LIMIT) {
             queryBlock.setLimit(parseLimit());
         }

+ 34 - 4
src/main/java/org/elasticsearch/sql/ElasticSqlSelectQueryBlock.java

@@ -5,11 +5,16 @@ import com.alibaba.druid.sql.ast.SQLObject;
 import com.alibaba.druid.sql.ast.SQLObjectImpl;
 import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock;
 import com.alibaba.druid.sql.visitor.SQLASTVisitor;
+import org.apache.commons.collections.CollectionUtils;
+
+import java.util.List;
+import java.util.function.Consumer;
 
 public class ElasticSqlSelectQueryBlock extends SQLSelectQueryBlock implements SQLObject {
     /*DSL: from to*/
     private Limit limit;
 
+    private Routing routing;
 
     public Limit getLimit() {
         return limit;
@@ -19,15 +24,40 @@ public class ElasticSqlSelectQueryBlock extends SQLSelectQueryBlock implements S
         this.limit = limit;
     }
 
-    public static class Limit extends SQLObjectImpl {
-        public Limit() {
+    public Routing getRouting() {
+        return routing;
+    }
+
+    public void setRouting(Routing routing) {
+        this.routing = routing;
+    }
+
+    public static class Routing extends SQLObjectImpl {
+        private List<SQLExpr> routingValues;
+
+        public Routing(List<SQLExpr> routingValues) {
+            this.routingValues = routingValues;
+            if (CollectionUtils.isNotEmpty(routingValues)) {
+                routingValues.stream().forEach(new Consumer<SQLExpr>() {
+                    @Override
+                    public void accept(SQLExpr sqlExpr) {
+                        sqlExpr.setParent(ElasticSqlSelectQueryBlock.Routing.this);
+                    }
+                });
+            }
+        }
 
+        @Override
+        protected void accept0(SQLASTVisitor visitor) {
+            throw new UnsupportedOperationException("ElasticSql un-support method : accept0(SQLASTVisitor visitor)");
         }
 
-        public Limit(SQLExpr rowCount) {
-            this.setRowCount(rowCount);
+        public List<SQLExpr> getRoutingValues() {
+            return routingValues;
         }
+    }
 
+    public static class Limit extends SQLObjectImpl {
         private SQLExpr rowCount;
         private SQLExpr offset;
 

+ 2 - 1
src/main/java/org/elasticsearch/ElasticMockClient.java

@@ -1,5 +1,6 @@
-package org.elasticsearch;
+package org.elasticsearch.util;
 
+import org.elasticsearch.ElasticsearchException;
 import org.elasticsearch.action.*;
 import org.elasticsearch.action.bulk.BulkRequest;
 import org.elasticsearch.action.bulk.BulkRequestBuilder;

+ 0 - 12
src/main/java/org/elasticsearch/utils/GsonHelper.java

@@ -1,12 +0,0 @@
-package org.elasticsearch.utils;
-
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-
-public class GsonHelper {
-    private GsonHelper() {
-
-    }
-
-    public static final Gson gson = new GsonBuilder().create();
-}

+ 0 - 20
src/main/java/org/elasticsearch/utils/StringUtils.java

@@ -1,20 +0,0 @@
-package org.elasticsearch.utils;
-
-public class StringUtils {
-    public static boolean isBlank(String src) {
-        if (src == null) {
-            return true;
-        }
-        int len = src.length();
-        for (int idx = 0; idx < len; idx++) {
-            if (' ' != src.charAt(idx)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    public static boolean isNotBlank(String src) {
-        return !isBlank(src);
-    }
-}

+ 30 - 0
src/test/java/org/elasticsearch/SqlParserLimitTest.java

@@ -0,0 +1,30 @@
+package org.elasticsearch;
+
+import org.elasticsearch.dsl.ElasticSql2DslParser;
+import org.elasticsearch.dsl.ElasticSqlParseResult;
+import org.junit.Assert;
+import org.junit.Test;
+
+
+public class SqlParserLimitTest {
+    @Test
+    public void testParseLimitExpr() {
+        String sql = "select id,productStatus from index.trx_order trx where trx.status='SUCCESS' limit 5,15";
+        ElasticSql2DslParser sql2DslParser = new ElasticSql2DslParser();
+        ElasticSqlParseResult parseResult = sql2DslParser.parse(sql);
+
+        Assert.assertEquals(parseResult.getFrom(), 5);
+        Assert.assertEquals(parseResult.getSize(), 15);
+    }
+
+    @Test
+    public void testParseLimitExprWithArgs() {
+        String sql = "select id,productStatus from index.trx_order trx where trx.status='SUCCESS' limit ?,?";
+        ElasticSql2DslParser sql2DslParser = new ElasticSql2DslParser();
+        ElasticSqlParseResult parseResult = sql2DslParser.parse(sql, new Object[]{5, 15});
+
+        Assert.assertEquals(parseResult.getFrom(), 5);
+        Assert.assertEquals(parseResult.getSize(), 15);
+    }
+
+}

+ 60 - 0
src/test/java/org/elasticsearch/SqlParserOrderByTest.java

@@ -0,0 +1,60 @@
+package org.elasticsearch;
+
+import org.elasticsearch.dsl.ElasticSql2DslParser;
+import org.elasticsearch.dsl.ElasticSqlParseResult;
+import org.elasticsearch.search.sort.SortBuilder;
+import org.elasticsearch.search.sort.SortBuilders;
+import org.elasticsearch.search.sort.SortOrder;
+import org.junit.Assert;
+import org.junit.Test;
+
+
+public class SqlParserOrderByTest {
+    @Test
+    public void testParseEqExpr() {
+        String sql = "select id,productStatus from index.trx_order order by price asc,id desc,updatedAt asc";
+        ElasticSql2DslParser sql2DslParser = new ElasticSql2DslParser();
+        ElasticSqlParseResult parseResult = sql2DslParser.parse(sql);
+
+        SortBuilder targetSort = SortBuilders.fieldSort("price").order(SortOrder.ASC);
+        Assert.assertEquals(parseResult.getSortBuilderList().get(0).toString(), targetSort.toString());
+        targetSort = SortBuilders.fieldSort("id").order(SortOrder.DESC);
+        Assert.assertEquals(parseResult.getSortBuilderList().get(1).toString(), targetSort.toString());
+        targetSort = SortBuilders.fieldSort("updatedAt").order(SortOrder.ASC);
+        Assert.assertEquals(parseResult.getSortBuilderList().get(2).toString(), targetSort.toString());
+
+        sql = "select id,productStatus from index.trx_order order by nvl(price, 0) asc";
+        sql2DslParser = new ElasticSql2DslParser();
+        parseResult = sql2DslParser.parse(sql);
+        targetSort = SortBuilders.fieldSort("price").order(SortOrder.ASC).missing(0);
+        Assert.assertEquals(parseResult.getSortBuilderList().get(0).toString(), targetSort.toString());
+
+        sql = "select id,productStatus from index.trx_order order by nvl(inner_doc(product.price), 0) asc";
+        sql2DslParser = new ElasticSql2DslParser();
+        parseResult = sql2DslParser.parse(sql);
+        targetSort = SortBuilders.fieldSort("product.price").order(SortOrder.ASC).missing(0);
+        Assert.assertEquals(parseResult.getSortBuilderList().get(0).toString(), targetSort.toString());
+
+        sql = "select id,productStatus from index.trx_order order by nvl(nested_doc(product.price), 0) asc";
+        sql2DslParser = new ElasticSql2DslParser();
+        parseResult = sql2DslParser.parse(sql);
+        targetSort = SortBuilders.fieldSort("price").order(SortOrder.ASC).missing(0).setNestedPath("product");
+        Assert.assertEquals(parseResult.getSortBuilderList().get(0).toString(), targetSort.toString());
+
+
+        sql = "select id,productStatus from index.trx_order order by nvl(product.price, 0) asc";
+        sql2DslParser = new ElasticSql2DslParser();
+        parseResult = sql2DslParser.parse(sql);
+        targetSort = SortBuilders.fieldSort("product.price").order(SortOrder.ASC).missing(0);
+        Assert.assertEquals(parseResult.getSortBuilderList().get(0).toString(), targetSort.toString());
+
+        sql = "select id,productStatus from index.trx_order order by product.price asc,nested_doc(productTags.sortNo) desc";
+        sql2DslParser = new ElasticSql2DslParser();
+        parseResult = sql2DslParser.parse(sql);
+        targetSort = SortBuilders.fieldSort("product.price").order(SortOrder.ASC);
+        Assert.assertEquals(parseResult.getSortBuilderList().get(0).toString(), targetSort.toString());
+        targetSort = SortBuilders.fieldSort("sortNo").order(SortOrder.DESC).setNestedPath("productTags");
+        Assert.assertEquals(parseResult.getSortBuilderList().get(1).toString(), targetSort.toString());
+    }
+
+}

+ 42 - 0
src/test/java/org/elasticsearch/SqlParserRoutingTest.java

@@ -0,0 +1,42 @@
+package org.elasticsearch;
+
+import org.elasticsearch.dsl.ElasticSql2DslParser;
+import org.elasticsearch.dsl.ElasticSqlParseResult;
+import org.junit.Assert;
+import org.junit.Test;
+
+
+public class SqlParserRoutingTest {
+    @Test
+    public void testParseRoutingExpr() {
+        String sql = "select * from index.trx_order trx where trx.status='SUCCESS' order by id asc routing by '801','802' limit 5,15";
+        ElasticSql2DslParser sql2DslParser = new ElasticSql2DslParser();
+        ElasticSqlParseResult parseResult = sql2DslParser.parse(sql);
+
+        Assert.assertTrue("801".equalsIgnoreCase(parseResult.getRoutingBy().get(0)));
+        Assert.assertTrue("802".equalsIgnoreCase(parseResult.getRoutingBy().get(1)));
+
+        sql = "select * from index.trx_order trx where trx.status='SUCCESS' order by id asc routing by '801' limit 5,15";
+        sql2DslParser = new ElasticSql2DslParser();
+        parseResult = sql2DslParser.parse(sql);
+        Assert.assertTrue("801".equalsIgnoreCase(parseResult.getRoutingBy().get(0)));
+    }
+
+    @Test
+    public void testParseRoutingExprWithArgs() {
+        String sql = "select * from index.trx_order trx where trx.status='SUCCESS' order by id asc routing by ?,? limit 5,15";
+        ElasticSql2DslParser sql2DslParser = new ElasticSql2DslParser();
+        ElasticSqlParseResult parseResult = sql2DslParser.parse(sql, new Object[]{"801", "802"});
+
+        Assert.assertTrue("801".equalsIgnoreCase(parseResult.getRoutingBy().get(0)));
+        Assert.assertTrue("802".equalsIgnoreCase(parseResult.getRoutingBy().get(1)));
+    }
+//
+//    @Test
+//    public void testJoin() {
+//        String sql = "select * from index.trx_order trx nested join product.xx x";
+//        ElasticSql2DslParser sql2DslParser = new ElasticSql2DslParser();
+//        ElasticSqlParseResult parseResult = sql2DslParser.parse(sql);
+//    }
+
+}

+ 104 - 0
src/test/java/org/elasticsearch/SqlParserSelectFieldTest.java

@@ -0,0 +1,104 @@
+package org.elasticsearch;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.elasticsearch.dsl.ElasticSql2DslParser;
+import org.elasticsearch.dsl.ElasticSqlParseResult;
+import org.junit.Assert;
+import org.junit.Test;
+
+
+public class SqlParserSelectFieldTest {
+
+    @Test
+    public void testParseFromSource() {
+        String sql = "select id,productStatus from index.trx_order trx";
+        ElasticSql2DslParser sql2DslParser = new ElasticSql2DslParser();
+        ElasticSqlParseResult parseResult = sql2DslParser.parse(sql);
+
+        Assert.assertEquals(parseResult.getIndex(), "index");
+        Assert.assertEquals(parseResult.getType(), "trx_order");
+        Assert.assertEquals(parseResult.getQueryAs(), "trx");
+    }
+
+    @Test
+    public void testParseDefaultLimit() {
+        String sql = "select id,productStatus from index.trx_order";
+        ElasticSql2DslParser sql2DslParser = new ElasticSql2DslParser();
+        ElasticSqlParseResult parseResult = sql2DslParser.parse(sql);
+
+        Assert.assertEquals(parseResult.getFrom(), 0);
+        Assert.assertEquals(parseResult.getSize(), 15);
+    }
+
+    @Test
+    public void testParseOriSelectField() {
+        String sql = "select id,productStatus from index.trx_order";
+        ElasticSql2DslParser sql2DslParser = new ElasticSql2DslParser();
+        ElasticSqlParseResult parseResult = sql2DslParser.parse(sql);
+
+        Assert.assertTrue(parseResult.getQueryFieldList().size() == 2);
+        Assert.assertEquals(parseResult.getQueryFieldList().get(0), "id");
+        Assert.assertEquals(parseResult.getQueryFieldList().get(1), "productStatus");
+    }
+
+    @Test
+    public void testSelectAllField() {
+        String sql = "select * from index.trx_order order by id asc routing by '801','802','803' limit 1,2";
+        ElasticSql2DslParser sql2DslParser = new ElasticSql2DslParser();
+        ElasticSqlParseResult parseResult = sql2DslParser.parse(sql);
+        System.out.println(parseResult.toString());
+        Assert.assertTrue(CollectionUtils.isEmpty(parseResult.getQueryFieldList()));
+    }
+
+    @Test
+    public void testInnerDocField() {
+        String sql = "select id,inner_doc(product.productStatus) from index.trx_order";
+        ElasticSql2DslParser sql2DslParser = new ElasticSql2DslParser();
+        ElasticSqlParseResult parseResult = sql2DslParser.parse(sql);
+        Assert.assertEquals(parseResult.getQueryFieldList().get(0), "id");
+        Assert.assertEquals(parseResult.getQueryFieldList().get(1), "product.productStatus");
+
+        sql = "select inner_doc(trx.product.productStatus) from index.trx_order trx";
+        parseResult = sql2DslParser.parse(sql);
+        Assert.assertEquals(parseResult.getQueryFieldList().get(0), "product.productStatus");
+
+        sql = "select inner_doc(tmp.product.productStatus) from index.trx_order trx";
+        parseResult = sql2DslParser.parse(sql);
+        Assert.assertEquals(parseResult.getQueryFieldList().get(0), "tmp.product.productStatus");
+
+        sql = "select product.productStatus from index.trx_order trx";
+        parseResult = sql2DslParser.parse(sql);
+        Assert.assertEquals(parseResult.getQueryFieldList().get(0), "product.productStatus");
+
+        sql = "select id,product.* from index.trx_order trx";
+        parseResult = sql2DslParser.parse(sql);
+        Assert.assertEquals(parseResult.getQueryFieldList().get(0), "id");
+        Assert.assertEquals(parseResult.getQueryFieldList().get(1), "product.*");
+    }
+
+    @Test
+    public void testNestedDocField() {
+        String sql = "select id,nested_doc(product.productStatus) from index.trx_order";
+        ElasticSql2DslParser sql2DslParser = new ElasticSql2DslParser();
+        ElasticSqlParseResult parseResult = sql2DslParser.parse(sql);
+        Assert.assertEquals(parseResult.getQueryFieldList().get(0), "id");
+        Assert.assertEquals(parseResult.getQueryFieldList().get(1), "product.productStatus");
+
+        sql = "select nested_doc(trx.product.productStatus) from index.trx_order trx";
+        parseResult = sql2DslParser.parse(sql);
+        Assert.assertEquals(parseResult.getQueryFieldList().get(0), "product.productStatus");
+
+        sql = "select nested_doc(tmp.product.productStatus) from index.trx_order trx";
+        parseResult = sql2DslParser.parse(sql);
+        Assert.assertEquals(parseResult.getQueryFieldList().get(0), "tmp.product.productStatus");
+
+        sql = "select product.productStatus from index.trx_order trx";
+        parseResult = sql2DslParser.parse(sql);
+        Assert.assertEquals(parseResult.getQueryFieldList().get(0), "product.productStatus");
+
+        sql = "select id,product.* from index.trx_order trx";
+        parseResult = sql2DslParser.parse(sql);
+        Assert.assertEquals(parseResult.getQueryFieldList().get(0), "id");
+        Assert.assertEquals(parseResult.getQueryFieldList().get(1), "product.*");
+    }
+}

+ 0 - 21
src/test/java/org/elasticsearch/SqlParserTest.java

@@ -1,21 +0,0 @@
-package org.elasticsearch;
-
-import org.elasticsearch.dsl.ElasticSql2DslParser;
-import org.elasticsearch.dsl.ElasticSqlParseResult;
-import org.junit.Test;
-
-
-public class SqlParserTest {
-
-    @Test
-    public void testParseSelectFieldWithQueryAs() {
-        String sql = "select id,productStatus from index.trx_order where "
-                + " productCategory in ('801', '802') "
-                + " and (updatedAt > date('yyyy-MM-dd hh:mm', ?) or updatedAt < '2017-01-01') "
-                + " and price > ?";
-
-        ElasticSql2DslParser sql2DslParser = new ElasticSql2DslParser();
-        ElasticSqlParseResult parseResult = sql2DslParser.parse(sql, new Object[]{"2016-12-30 14:58", 100.12});
-        System.out.println(parseResult.toDsl());
-    }
-}

+ 138 - 0
src/test/java/org/elasticsearch/SqlParserWhereConditionTest.java

@@ -0,0 +1,138 @@
+package org.elasticsearch;
+
+import org.elasticsearch.dsl.ElasticSql2DslParser;
+import org.elasticsearch.dsl.ElasticSqlParseResult;
+import org.elasticsearch.index.query.FilterBuilder;
+import org.elasticsearch.index.query.FilterBuilders;
+import org.junit.Assert;
+import org.junit.Test;
+
+
+public class SqlParserWhereConditionTest  {
+    @Test
+    public void testParseEqExpr() {
+        String sql = "select id,productStatus from index.trx_order trx where trx.status='SUCCESS'";
+        ElasticSql2DslParser sql2DslParser = new ElasticSql2DslParser();
+        ElasticSqlParseResult parseResult = sql2DslParser.parse(sql);
+        FilterBuilder targetFilter = FilterBuilders.boolFilter().must(FilterBuilders.termFilter("status", "SUCCESS"));
+        Assert.assertEquals(parseResult.getFilterBuilder().toString(), targetFilter.toString());
+
+        sql = "select id,productStatus from index.trx_order trx where trx.price='123.4'";
+        parseResult = sql2DslParser.parse(sql);
+        targetFilter = FilterBuilders.boolFilter().must(FilterBuilders.termFilter("price", "123.4"));
+        Assert.assertEquals(parseResult.getFilterBuilder().toString(), targetFilter.toString());
+
+        sql = "select id,productStatus from index.trx_order trx where inner_doc(product.price)='123.4'";
+        parseResult = sql2DslParser.parse(sql);
+        targetFilter = FilterBuilders.boolFilter().must(FilterBuilders.termFilter("product.price", "123.4"));
+        Assert.assertEquals(parseResult.getFilterBuilder().toString(), targetFilter.toString());
+
+        sql = "select id,productStatus from index.trx_order trx where nested_doc(product.price)='123.4'";
+        parseResult = sql2DslParser.parse(sql);
+        targetFilter = FilterBuilders.boolFilter().must(FilterBuilders.nestedFilter("product", FilterBuilders.termFilter("price", "123.4")));
+        Assert.assertEquals(parseResult.getFilterBuilder().toString(), targetFilter.toString());
+
+        sql = "select id,productStatus from index.trx_order trx where nested_doc(trx.product.price)='123.4'";
+        parseResult = sql2DslParser.parse(sql);
+        targetFilter = FilterBuilders.boolFilter().must(FilterBuilders.nestedFilter("product", FilterBuilders.termFilter("price", "123.4")));
+        Assert.assertEquals(parseResult.getFilterBuilder().toString(), targetFilter.toString());
+
+        sql = "select id,productStatus from index.trx_order trx where nested_doc(abc.trx.product.price)='123.4'";
+        parseResult = sql2DslParser.parse(sql);
+        targetFilter = FilterBuilders.boolFilter().must(FilterBuilders.nestedFilter("abc.trx.product", FilterBuilders.termFilter("price", "123.4")));
+        Assert.assertEquals(parseResult.getFilterBuilder().toString(), targetFilter.toString());
+
+        sql = "select id,productStatus from index.trx_order trx where trx.product.price='123.4'";
+        parseResult = sql2DslParser.parse(sql);
+        targetFilter = FilterBuilders.boolFilter().must(FilterBuilders.termFilter("product.price", "123.4"));
+        Assert.assertEquals(parseResult.getFilterBuilder().toString(), targetFilter.toString());
+    }
+
+    @Test
+    public void testParseGtExpr() {
+
+        ElasticSql2DslParser sql2DslParser = new ElasticSql2DslParser();
+
+        String sql = "select id,productStatus from index.trx_order trx where trx.price > 123.4";
+        ElasticSqlParseResult parseResult = sql2DslParser.parse(sql);
+        FilterBuilder targetFilter = FilterBuilders.boolFilter().must(FilterBuilders.rangeFilter("price").gt(123.4));
+        Assert.assertEquals(parseResult.getFilterBuilder().toString(), targetFilter.toString());
+
+        sql = "select id,productStatus from index.trx_order trx where inner_doc(product.price) > 123.4";
+        parseResult = sql2DslParser.parse(sql);
+        targetFilter = FilterBuilders.boolFilter().must(FilterBuilders.rangeFilter("product.price").gt(123.4));
+        Assert.assertEquals(parseResult.getFilterBuilder().toString(), targetFilter.toString());
+
+        sql = "select id,productStatus from index.trx_order trx where nested_doc(product.price) > 123.4";
+        parseResult = sql2DslParser.parse(sql);
+        targetFilter = FilterBuilders.boolFilter().must(FilterBuilders.nestedFilter("product", FilterBuilders.rangeFilter("price").gt(123.4)));
+        Assert.assertEquals(parseResult.getFilterBuilder().toString(), targetFilter.toString());
+
+    }
+
+    @Test
+    public void testParseDateExpr() {
+
+        ElasticSql2DslParser sql2DslParser = new ElasticSql2DslParser();
+
+        String sql = "select id,productStatus from index.trx_order trx where trx.updatedAt > '2017-01-25'";
+        ElasticSqlParseResult parseResult = sql2DslParser.parse(sql);
+        FilterBuilder targetFilter = FilterBuilders.boolFilter().must(FilterBuilders.rangeFilter("updatedAt").gt("2017-01-25T00:00:00.000+0800"));
+        Assert.assertEquals(parseResult.getFilterBuilder().toString(), targetFilter.toString());
+
+        sql = "select id,productStatus from index.trx_order trx where trx.updatedAt > '2017-01-25 13:32'";
+        parseResult = sql2DslParser.parse(sql);
+        targetFilter = FilterBuilders.boolFilter().must(FilterBuilders.rangeFilter("updatedAt").gt("2017-01-25T13:32:00.000+0800"));
+        Assert.assertEquals(parseResult.getFilterBuilder().toString(), targetFilter.toString());
+
+        sql = "select id,productStatus from index.trx_order trx where trx.updatedAt > '2017-01-25 13:32:59'";
+        parseResult = sql2DslParser.parse(sql);
+        targetFilter = FilterBuilders.boolFilter().must(FilterBuilders.rangeFilter("updatedAt").gt("2017-01-25T13:32:59.000+0800"));
+        Assert.assertEquals(parseResult.getFilterBuilder().toString(), targetFilter.toString());
+
+        sql = "select id,productStatus from index.trx_order trx where trx.updatedAt > date('yyyy/MM/dd hh:mm:ss', '2017/01/25 13:32:59')";
+        parseResult = sql2DslParser.parse(sql);
+        targetFilter = FilterBuilders.boolFilter().must(FilterBuilders.rangeFilter("updatedAt").gt("2017-01-25T13:32:59.000+0800"));
+        Assert.assertEquals(parseResult.getFilterBuilder().toString(), targetFilter.toString());
+
+
+        sql = "select id,productStatus from index.trx_order trx where trx.updatedAt > date('yyyy/MM/dd hh-mm', '2017/01/25 13-32')";
+        parseResult = sql2DslParser.parse(sql);
+        targetFilter = FilterBuilders.boolFilter().must(FilterBuilders.rangeFilter("updatedAt").gt("2017-01-25T13:32:00.000+0800"));
+        Assert.assertEquals(parseResult.getFilterBuilder().toString(), targetFilter.toString());
+
+
+        sql = "select id,productStatus from index.trx_order trx where trx.updatedAt between date('yyyy/MM/dd hh-mm', '2017/01/25 13-32') and '2018-10-25'";
+        parseResult = sql2DslParser.parse(sql);
+        targetFilter = FilterBuilders.boolFilter().must(
+                FilterBuilders.rangeFilter("updatedAt")
+                        .gte("2017-01-25T13:32:00.000+0800")
+                        .lte("2018-10-25T00:00:00.000+0800")
+        );
+        Assert.assertEquals(parseResult.getFilterBuilder().toString(), targetFilter.toString());
+    }
+
+    @Test
+    public void testMixFilter() {
+        String sql = "select p.displayName,p.id,p.code,p.trxPrice,p.investPeriod,p.investPeriodUnit,p.product.*"
+                + " from trx_index.product_multiple_trx as p"
+                + " where p.status = 'SUCCESS'"
+                + " and p.price > 48000"
+                + " and p.price < 60000"
+                + " and inner_doc(p.product.productType) = 'TRANSFER_REQUEST'"
+                + " and inner_doc(p.product.productCategory) in ('203', '204', '903')"
+                + " and inner_doc(p.product.interestRate) >= 0.041"
+                + " and inner_doc(p.product.investPeriodInDays) between 90 and 180"
+                + " and p.updatedAt >= date('yyyy-MM-dd', '2016-01-01')"
+                + " and p.updatedAt < '2017-01-01'"
+                + " and (p.product.salesArea in ('2', '3', '4') or p.product.productCategory in ('901', '902'))"
+                + " and (p.salesRegion is null or p.salesRegion = '2')"
+                + " and nested_doc(p.productTags.type) in (?, ?)"
+                + " order by nvl(p.price, 0) asc, nvl(inner_doc(p.product.interestRate), 0) desc";
+
+
+        ElasticSql2DslParser sql2DslParser = new ElasticSql2DslParser();
+        ElasticSqlParseResult parseResult = sql2DslParser.parse(sql, new Object[]{"RM", "CX"});
+        System.out.println(parseResult.toString());
+    }
+}