Bladeren bron

script sort

spuerx 9 jaren geleden
bovenliggende
commit
555a807834

+ 1 - 1
src/main/java/org/es/sql/dsl/parser/query/method/MethodInvocation.java

@@ -41,7 +41,7 @@ public class MethodInvocation {
         return methodInvokeExpr.getParameters().size();
     }
 
-    public SQLExpr getFirstParameter(int index) {
+    public SQLExpr getFirstParameter() {
         return getParameter(0);
     }
 

+ 22 - 85
src/main/java/org/es/sql/dsl/parser/sql/QueryOrderConditionParser.java

@@ -1,12 +1,10 @@
 package org.es.sql.dsl.parser.sql;
 
-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.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.ImmutableList;
 import com.google.common.collect.Lists;
 import org.apache.commons.collections.CollectionUtils;
 import org.elasticsearch.search.sort.FieldSortBuilder;
@@ -16,12 +14,10 @@ import org.elasticsearch.search.sort.SortOrder;
 import org.es.sql.druid.ElasticSqlSelectQueryBlock;
 import org.es.sql.dsl.bean.ElasticDslContext;
 import org.es.sql.dsl.bean.ElasticSqlQueryField;
-import org.es.sql.dsl.enums.QueryFieldType;
-import org.es.sql.dsl.enums.SortOption;
 import org.es.sql.dsl.exception.ElasticSql2DslException;
-import org.es.sql.dsl.helper.ElasticSqlMethodInvokeHelper;
 import org.es.sql.dsl.listener.ParseActionListener;
 import org.es.sql.dsl.parser.query.method.MethodInvocation;
+import org.es.sql.dsl.parser.sql.sort.*;
 
 import java.util.List;
 
@@ -29,8 +25,15 @@ public class QueryOrderConditionParser implements QueryParser {
 
     private ParseActionListener parseActionListener;
 
+    private List<MethodSortParser> methodSortParsers;
+
     public QueryOrderConditionParser(ParseActionListener parseActionListener) {
         this.parseActionListener = parseActionListener;
+
+        methodSortParsers = ImmutableList.of(
+                new NvlMethodSortParser(),
+                new ScriptMethodSortParser()
+        );
     }
 
     @Override
@@ -50,94 +53,28 @@ public class QueryOrderConditionParser implements QueryParser {
     }
 
     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() {
+        SortOrder order = orderByItem.getType() == SQLOrderingSpecification.ASC ? SortOrder.ASC : SortOrder.DESC;
+
+        if (ParseSortBuilderHelper.isFieldExpr(orderByItem.getExpr())) {
+            QueryFieldParser fieldParser = new QueryFieldParser();
+            ElasticSqlQueryField sortField = fieldParser.parseConditionQueryField(orderByItem.getExpr(), queryAs);
+            return ParseSortBuilderHelper.parseBasedOnFieldSortBuilder(sortField, new ConditionSortBuilder() {
                 @Override
                 public FieldSortBuilder buildSort(String queryFieldName) {
-                    if (SQLOrderingSpecification.ASC == orderByItem.getType()) {
-                        return SortBuilders.fieldSort(queryFieldName).order(SortOrder.ASC);
-                    }
-                    else {
-                        return SortBuilders.fieldSort(queryFieldName).order(SortOrder.DESC);
-                    }
+                    return SortBuilders.fieldSort(queryFieldName).order(order);
                 }
             });
         }
-        if (orderByItem.getExpr() instanceof SQLMethodInvokeExpr) {
-            MethodInvocation sortMethodInvocation = new MethodInvocation((SQLMethodInvokeExpr) orderByItem.getExpr(), queryAs, sqlArgs);
-
-            //nvl method
-            if (ElasticSqlMethodInvokeHelper.isMethodOf(ElasticSqlMethodInvokeHelper.NVL_METHOD, sortMethodInvocation.getMethodName())) {
-                return buildNvlSortBuilder(sortMethodInvocation, orderByItem);
-            }
-
-            //script sort
-            if (ElasticSqlMethodInvokeHelper.isMethodOf(ElasticSqlMethodInvokeHelper.SCRIPT_SORT_METHOD, sortMethodInvocation.getMethodName())) {
-                return buildScriptSortBuilder(sortMethodInvocation, orderByItem);
-            }
-        }
-        throw new ElasticSql2DslException("[syntax error] can not support sort type: " + orderByItem.getExpr().getClass());
-    }
-
-    private SortBuilder buildScriptSortBuilder(MethodInvocation scriptSortMethodInvocation, SQLSelectOrderByItem orderByItem) {
-        ElasticSqlMethodInvokeHelper.checkScriptSortMethod(scriptSortMethodInvocation);
-
-        String script = scriptSortMethodInvocation.getParameterAsString(0);
-        String type = scriptSortMethodInvocation.getParameterAsString(1);
-
-        if (SQLOrderingSpecification.ASC == orderByItem.getType()) {
-            return SortBuilders.scriptSort(script, type).order(SortOrder.ASC);
-        }
-        return SortBuilders.scriptSort(script, type).order(SortOrder.DESC);
-    }
 
-    private SortBuilder buildNvlSortBuilder(MethodInvocation sortMethodInvocation, SQLSelectOrderByItem orderByItem) {
-        ElasticSqlMethodInvokeHelper.checkNvlMethod(sortMethodInvocation);
-
-        Object valueArg = sortMethodInvocation.getParameterAsObject(1);
-        return parseCondition(sortMethodInvocation.getParameter(0), sortMethodInvocation.getQueryAs(), 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 (sortMethodInvocation.getParameterCount() == 3) {
-                    String sortModeText = sortMethodInvocation.getParameterAsString(2);
-                    fieldSortBuilder.sortMode(SortOption.get(sortModeText).mode());
+        if (ParseSortBuilderHelper.isMethodInvokeExpr(orderByItem.getExpr())) {
+            MethodInvocation sortMethodInvocation = new MethodInvocation((SQLMethodInvokeExpr) orderByItem.getExpr(), queryAs, sqlArgs);
+            for (MethodSortParser methodSortParser : methodSortParsers) {
+                if (methodSortParser.isMatchMethodInvocation(sortMethodInvocation)) {
+                    return methodSortParser.parseMethodSortBuilder(sortMethodInvocation, order);
                 }
-                return fieldSortBuilder;
             }
-        });
-    }
-
-    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());
         }
 
-        if (sortField.getQueryFieldType() == QueryFieldType.NestedDocField) {
-            FieldSortBuilder originalSort = sortBuilder.buildSort(sortField.getQueryFieldFullName());
-            originalSort.setNestedPath(sortField.getNestedDocContextPath());
-            rtnSortBuilder = originalSort;
-        }
-
-        if (rtnSortBuilder == null) {
-            throw new ElasticSql2DslException(String.format("[syntax error] sort condition field can not support type[%s]", sortField.getQueryFieldType()));
-        }
-
-        return rtnSortBuilder;
-    }
-
-    private interface ConditionSortBuilder {
-        FieldSortBuilder buildSort(String idfName);
+        throw new ElasticSql2DslException("[syntax error] can not support sort type: " + orderByItem.getExpr().getClass());
     }
 }

+ 48 - 0
src/main/java/org/es/sql/dsl/parser/sql/sort/AbstractMethodSortParser.java

@@ -0,0 +1,48 @@
+package org.es.sql.dsl.parser.sql.sort;
+
+import org.elasticsearch.common.lang3.StringUtils;
+import org.elasticsearch.search.sort.FieldSortBuilder;
+import org.elasticsearch.search.sort.SortBuilder;
+import org.elasticsearch.search.sort.SortOrder;
+import org.es.sql.dsl.bean.ElasticSqlQueryField;
+import org.es.sql.dsl.enums.QueryFieldType;
+import org.es.sql.dsl.exception.ElasticSql2DslException;
+import org.es.sql.dsl.helper.ElasticSqlMethodInvokeHelper;
+import org.es.sql.dsl.parser.query.method.MethodInvocation;
+import org.es.sql.dsl.parser.query.method.expr.AbstractParameterizedMethodExpression;
+
+import java.util.Map;
+
+public abstract class AbstractMethodSortParser extends AbstractParameterizedMethodExpression implements MethodSortParser {
+
+    protected abstract SortBuilder parseMethodSortBuilderWithExtraParams(
+            MethodInvocation invocation, SortOrder order, Map<String, Object> extraParamMap) throws ElasticSql2DslException;
+
+    @Override
+    protected String defineExtraParamString(MethodInvocation invocation) {
+        return StringUtils.EMPTY;
+    }
+
+    @Override
+    public void checkMethodInvocation(MethodInvocation invocation) throws ElasticSql2DslException {
+
+    }
+
+    @Override
+    public boolean isMatchMethodInvocation(MethodInvocation invocation) {
+        return ElasticSqlMethodInvokeHelper.isMethodOf(defineMethodNames(), invocation.getMethodName());
+    }
+
+    @Override
+    public SortBuilder parseMethodSortBuilder(MethodInvocation invocation, SortOrder order) throws ElasticSql2DslException {
+        if (!isMatchMethodInvocation(invocation)) {
+            throw new ElasticSql2DslException(
+                    String.format("[syntax error] Expected method name is one of [%s],but get [%s]",
+                            defineMethodNames(), invocation.getMethodName()));
+        }
+        checkMethodInvocation(invocation);
+
+        Map<String, Object> extraParamMap = generateRawTypeParameterMap(invocation);
+        return parseMethodSortBuilderWithExtraParams(invocation, order, extraParamMap);
+    }
+}

+ 7 - 0
src/main/java/org/es/sql/dsl/parser/sql/sort/ConditionSortBuilder.java

@@ -0,0 +1,7 @@
+package org.es.sql.dsl.parser.sql.sort;
+
+import org.elasticsearch.search.sort.FieldSortBuilder;
+
+public interface ConditionSortBuilder {
+    FieldSortBuilder buildSort(String idfName);
+}

+ 11 - 0
src/main/java/org/es/sql/dsl/parser/sql/sort/MethodSortParser.java

@@ -0,0 +1,11 @@
+package org.es.sql.dsl.parser.sql.sort;
+
+import org.elasticsearch.search.sort.SortBuilder;
+import org.elasticsearch.search.sort.SortOrder;
+import org.es.sql.dsl.exception.ElasticSql2DslException;
+import org.es.sql.dsl.parser.query.method.MethodInvocation;
+import org.es.sql.dsl.parser.query.method.expr.MethodExpression;
+
+public interface MethodSortParser extends MethodExpression {
+    SortBuilder parseMethodSortBuilder(MethodInvocation invocation, SortOrder order) throws ElasticSql2DslException;
+}

+ 87 - 0
src/main/java/org/es/sql/dsl/parser/sql/sort/NvlMethodSortParser.java

@@ -0,0 +1,87 @@
+package org.es.sql.dsl.parser.sql.sort;
+
+import com.alibaba.druid.sql.ast.SQLExpr;
+import com.alibaba.druid.sql.ast.expr.*;
+import com.google.common.collect.ImmutableList;
+import org.elasticsearch.search.sort.FieldSortBuilder;
+import org.elasticsearch.search.sort.SortBuilder;
+import org.elasticsearch.search.sort.SortBuilders;
+import org.elasticsearch.search.sort.SortOrder;
+import org.es.sql.dsl.bean.ElasticSqlQueryField;
+import org.es.sql.dsl.enums.SortOption;
+import org.es.sql.dsl.exception.ElasticSql2DslException;
+import org.es.sql.dsl.parser.query.method.MethodInvocation;
+import org.es.sql.dsl.parser.sql.QueryFieldParser;
+
+import java.util.List;
+import java.util.Map;
+
+public class NvlMethodSortParser extends AbstractMethodSortParser {
+
+    public static final List<String> NVL_METHOD = ImmutableList.of("nvl", "is_null", "isnull");
+
+    @Override
+    public List<String> defineMethodNames() {
+        return NVL_METHOD;
+    }
+
+    @Override
+    public void checkMethodInvocation(MethodInvocation nvlMethodInvocation) throws ElasticSql2DslException {
+        if (!isMatchMethodInvocation(nvlMethodInvocation)) {
+            throw new ElasticSql2DslException("[syntax error] Sql sort condition only support nvl method invoke");
+        }
+
+        int methodParameterCount = nvlMethodInvocation.getParameterCount();
+        if (methodParameterCount == 0 || methodParameterCount > 3) {
+            throw new ElasticSql2DslException(String.format("[syntax error] There is no %s args method named nvl", methodParameterCount));
+        }
+
+        SQLExpr fieldArg = nvlMethodInvocation.getParameter(0);
+        SQLExpr valueArg = nvlMethodInvocation.getParameter(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 (methodParameterCount == 3) {
+            SQLExpr sortModArg = nvlMethodInvocation.getParameter(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]");
+            }
+        }
+    }
+
+    @Override
+    protected SortBuilder parseMethodSortBuilderWithExtraParams(
+            MethodInvocation sortMethodInvocation, SortOrder order, Map<String, Object> extraParamMap) throws ElasticSql2DslException {
+
+        String queryAs = sortMethodInvocation.getQueryAs();
+        SQLExpr fieldExpr = sortMethodInvocation.getParameter(0);
+        Object valueArg = sortMethodInvocation.getParameterAsObject(1);
+
+        QueryFieldParser queryFieldParser = new QueryFieldParser();
+        ElasticSqlQueryField sortField = queryFieldParser.parseConditionQueryField(fieldExpr, queryAs);
+
+        return ParseSortBuilderHelper.parseBasedOnFieldSortBuilder(sortField, new ConditionSortBuilder() {
+            @Override
+            public FieldSortBuilder buildSort(String idfName) {
+                FieldSortBuilder fieldSortBuilder = SortBuilders.fieldSort(idfName).order(order).missing(valueArg);
+
+                if (sortMethodInvocation.getParameterCount() == 3) {
+                    String sortModeText = sortMethodInvocation.getParameterAsString(2);
+                    fieldSortBuilder.sortMode(SortOption.get(sortModeText).mode());
+                }
+                return fieldSortBuilder;
+            }
+        });
+    }
+}

+ 41 - 0
src/main/java/org/es/sql/dsl/parser/sql/sort/ParseSortBuilderHelper.java

@@ -0,0 +1,41 @@
+package org.es.sql.dsl.parser.sql.sort;
+
+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.elasticsearch.search.sort.FieldSortBuilder;
+import org.elasticsearch.search.sort.SortBuilder;
+import org.es.sql.dsl.bean.ElasticSqlQueryField;
+import org.es.sql.dsl.enums.QueryFieldType;
+import org.es.sql.dsl.exception.ElasticSql2DslException;
+
+public class ParseSortBuilderHelper {
+
+    public static boolean isFieldExpr(SQLExpr expr) {
+        return expr instanceof SQLPropertyExpr || expr instanceof SQLIdentifierExpr;
+    }
+
+    public static boolean isMethodInvokeExpr(SQLExpr expr) {
+        return expr instanceof SQLMethodInvokeExpr;
+    }
+
+    public static SortBuilder parseBasedOnFieldSortBuilder(ElasticSqlQueryField sortField, ConditionSortBuilder sortBuilder) {
+        SortBuilder rtnSortBuilder = null;
+        if (sortField.getQueryFieldType() == QueryFieldType.RootDocField || sortField.getQueryFieldType() == QueryFieldType.InnerDocField) {
+            rtnSortBuilder = sortBuilder.buildSort(sortField.getQueryFieldFullName());
+        }
+
+        if (sortField.getQueryFieldType() == QueryFieldType.NestedDocField) {
+            FieldSortBuilder originalSort = sortBuilder.buildSort(sortField.getQueryFieldFullName());
+            originalSort.setNestedPath(sortField.getNestedDocContextPath());
+            rtnSortBuilder = originalSort;
+        }
+
+        if (rtnSortBuilder == null) {
+            throw new ElasticSql2DslException(String.format("[syntax error] sort condition field can not support type[%s]", sortField.getQueryFieldType()));
+        }
+
+        return rtnSortBuilder;
+    }
+}

+ 58 - 0
src/main/java/org/es/sql/dsl/parser/sql/sort/ScriptMethodSortParser.java

@@ -0,0 +1,58 @@
+package org.es.sql.dsl.parser.sql.sort;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.commons.collections.MapUtils;
+import org.elasticsearch.common.lang3.StringUtils;
+import org.elasticsearch.search.sort.SortBuilder;
+import org.elasticsearch.search.sort.SortBuilders;
+import org.elasticsearch.search.sort.SortOrder;
+import org.es.sql.dsl.exception.ElasticSql2DslException;
+import org.es.sql.dsl.parser.query.method.MethodInvocation;
+
+import java.util.List;
+import java.util.Map;
+
+public class ScriptMethodSortParser extends AbstractMethodSortParser {
+
+    public static final List<String> SCRIPT_SORT_METHOD = ImmutableList.of("script_sort", "scriptSort");
+
+    @Override
+    public List<String> defineMethodNames() {
+        return SCRIPT_SORT_METHOD;
+    }
+
+    @Override
+    protected String defineExtraParamString(MethodInvocation invocation) {
+        if (invocation.getParameterCount() == 3) {
+            return invocation.getParameterAsString(2);
+        }
+        return StringUtils.EMPTY;
+    }
+
+    @Override
+    public void checkMethodInvocation(MethodInvocation nvlMethodInvocation) throws ElasticSql2DslException {
+        if (!isMatchMethodInvocation(nvlMethodInvocation)) {
+            throw new ElasticSql2DslException("[syntax error] Sql sort condition only support script_query method invoke");
+        }
+
+        int methodParameterCount = nvlMethodInvocation.getParameterCount();
+        if (methodParameterCount != 2 && methodParameterCount != 3) {
+            throw new ElasticSql2DslException(String.format("[syntax error] There is no %s args method named script_sort", methodParameterCount));
+        }
+    }
+
+    @Override
+    protected SortBuilder parseMethodSortBuilderWithExtraParams(
+            MethodInvocation scriptSortMethodInvocation, SortOrder order, Map<String, Object> extraParamMap) throws ElasticSql2DslException {
+
+        String script = scriptSortMethodInvocation.getParameterAsString(0);
+        String type = scriptSortMethodInvocation.getParameterAsString(1);
+
+        if (MapUtils.isNotEmpty(extraParamMap)) {
+            Map<String, Object> scriptParamMap = generateRawTypeParameterMap(scriptSortMethodInvocation);
+            return SortBuilders.scriptSort(script, type).order(order).setParams(scriptParamMap);
+        }
+
+        return SortBuilders.scriptSort(script, type).order(order);
+    }
+}

+ 1 - 1
src/test/java/org/es/SqlParserOrderByTest.java

@@ -63,7 +63,7 @@ public class SqlParserOrderByTest {
     public void testX() {
         ElasticMockClient esClient = new ElasticMockClient();
 
-        String sql = "select * from index.order where status='SUCCESS' order by nvl(pride, 0) asc routing by 'CA','CB' limit 0, 20";
+        String sql = "select * from index.order where status='SUCCESS' order by nvl(pride, 0) asc, script_sort('doc[\"price\"].value * 1.5 / vp', 'number', 'vp:2.1') desc routing by 'CA','CB' limit 0, 20";
 
         ElasticSql2DslParser sql2DslParser = new ElasticSql2DslParser();
         //解析SQL