package com.midea.cloudSearch.druid.prase; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; import com.alibaba.druid.sql.ast.expr.SQLAggregateOption; import com.alibaba.druid.sql.ast.expr.SQLAllColumnExpr; import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; 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.expr.SQLQueryExpr; import com.alibaba.druid.sql.ast.expr.SQLValuableExpr; import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; import com.midea.cloudSearch.druid.segment.*; import com.midea.cloudSearch.exception.SqlParseException; public class FieldMaker { public static Field makeField(SQLExpr expr, String alias,String tableAlias) throws SqlParseException { if (expr instanceof SQLIdentifierExpr || expr instanceof SQLPropertyExpr || expr instanceof SQLVariantRefExpr) { return handleIdentifier(expr, alias, tableAlias); } else if (expr instanceof SQLQueryExpr) { throw new SqlParseException("unknow field name : " + expr); } else if (expr instanceof SQLBinaryOpExpr) { //make a SCRIPT method field; return makeScriptMethodField((SQLBinaryOpExpr) expr, alias); } else if (expr instanceof SQLAllColumnExpr) { } else if (expr instanceof SQLMethodInvokeExpr) { SQLMethodInvokeExpr mExpr = (SQLMethodInvokeExpr) expr; String methodName = mExpr.getMethodName(); if(methodName.toLowerCase().equals("nested") ||methodName.toLowerCase().equals("reverse_nested") ){ NestedType nestedType = new NestedType(); if(nestedType.tryFillFromExpr(mExpr)){ return handleIdentifier(nestedType, alias, tableAlias); } } else if (methodName.toLowerCase().equals("filter")){ return makeFilterMethodField(mExpr,alias); } return makeMethodField(methodName, mExpr.getParameters(), null, alias); } else if (expr instanceof SQLAggregateExpr) { SQLAggregateExpr sExpr = (SQLAggregateExpr) expr; return makeMethodField(sExpr.getMethodName(), sExpr.getArguments(), sExpr.getOption(), alias); } else { throw new SqlParseException("unknown field name : " + expr); } return null; } private static Field makeFilterMethodField(SQLMethodInvokeExpr filterMethod,String alias) throws SqlParseException { List<SQLExpr> parameters = filterMethod.getParameters(); int parametersSize = parameters.size(); if(parametersSize != 1 && parametersSize !=2){ throw new SqlParseException("filter group by field should only have one or 2 parameters filter(Expr) or filter(name,Expr)"); } String filterAlias = filterMethod.getMethodName(); SQLExpr exprToCheck = null; if(parametersSize == 1){ exprToCheck = parameters.get(0); filterAlias = "filter(" + exprToCheck.toString().replaceAll("\n"," ") +")"; } if(parametersSize == 2){ filterAlias = Util.extendedToString(parameters.get(0)); exprToCheck = parameters.get(1); } Where where = Where.newInstance(); new SqlParser().parseWhere(exprToCheck,where); if(where.getWheres().size() == 0) throw new SqlParseException("unable to parse filter where."); List<KVValue> methodParameters = new ArrayList<>(); methodParameters.add(new KVValue("where",where)); methodParameters.add(new KVValue("alias",filterAlias+"@FILTER")); return new MethodField("filter", methodParameters, null, alias); } private static Field handleIdentifier(NestedType nestedType, String alias, String tableAlias) { Field field = handleIdentifier(new SQLIdentifierExpr(nestedType.field), alias, tableAlias); field.setNested(nestedType); return field; } private static Field makeScriptMethodField(SQLBinaryOpExpr binaryExpr, String alias) throws SqlParseException { List<SQLExpr> params = new ArrayList<>(); String scriptFieldAlias; if(alias == null || alias.equals("")) scriptFieldAlias = binaryExpr.toString(); else scriptFieldAlias = alias; params.add(new SQLCharExpr(scriptFieldAlias)); Object left = getScriptValue(binaryExpr.getLeft()); Object right = getScriptValue(binaryExpr.getRight()); String script = String.format("%s %s %s" , left ,binaryExpr.getOperator().getName() , right); params.add(new SQLCharExpr(script)); return makeMethodField("script",params,null,null); } private static Object getScriptValue(SQLExpr expr) throws SqlParseException { if (expr instanceof SQLIdentifierExpr || expr instanceof SQLPropertyExpr || expr instanceof SQLVariantRefExpr) { return "doc['" + expr.toString() + "'].value"; } else if (expr instanceof SQLValuableExpr){ return ((SQLValuableExpr)expr).getValue(); } throw new SqlParseException("could not parse sqlBinaryOpExpr need to be identifier/valuable got" + expr.getClass().toString() + " with value:" +expr.toString() ); } private static Field handleIdentifier(SQLExpr expr, String alias, String tableAlias) { String name = expr.toString().replace("`", ""); if(tableAlias==null) return new Field(name, alias); else if(tableAlias!=null){ String aliasPrefix = tableAlias + "."; if(name.startsWith(aliasPrefix)) { name = name.replaceFirst(aliasPrefix,""); return new Field(name, alias); } } return null; } private static MethodField makeMethodField(String name, List<SQLExpr> arguments, SQLAggregateOption option, String alias) throws SqlParseException { List<KVValue> paramers = new LinkedList<>(); for (SQLExpr object : arguments) { if (object instanceof SQLBinaryOpExpr) { SQLBinaryOpExpr binaryOpExpr = (SQLBinaryOpExpr) object; if(!binaryOpExpr.getOperator().getName().equals("=")){ paramers.add(new KVValue("script", makeScriptMethodField(binaryOpExpr,null))); } else { SQLExpr right = binaryOpExpr.getRight(); Object value = Util.expr2Object(right); paramers.add(new KVValue(binaryOpExpr.getLeft().toString(), value)); } } else if(object instanceof SQLMethodInvokeExpr) { SQLMethodInvokeExpr mExpr = (SQLMethodInvokeExpr) object; String methodName = mExpr.getMethodName().toLowerCase(); if(methodName.equals("script")){ KVValue script = new KVValue("script", makeMethodField(mExpr.getMethodName(), mExpr.getParameters(), null, alias)); paramers.add(script); } else if(methodName.equals("nested") || methodName.equals("reverse_nested")){ NestedType nestedType = new NestedType(); if(!nestedType.tryFillFromExpr(object)){ throw new SqlParseException("failed parsing nested expr " + object); } paramers.add(new KVValue("nested",nestedType)); } else throw new SqlParseException("only support script/nested as inner functions"); }else { paramers.add(new KVValue(Util.expr2Object(object))); } } return new MethodField(name, paramers, option == null ? null : option.name(), alias); } }