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);
}
}