package org.nlpcn.es4sql.query; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement; import com.alibaba.druid.sql.ast.statement.SQLJoinTableSource; import com.alibaba.druid.sql.ast.statement.SQLUnionQuery; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock; import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; import com.alibaba.druid.sql.parser.*; import org.elasticsearch.client.Client; import org.elasticsearch.plugin.nlpcn.ElasticResultHandler; import org.elasticsearch.plugin.nlpcn.QueryActionElasticExecutor; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHits; import org.nlpcn.es4sql.domain.Delete; import org.nlpcn.es4sql.domain.JoinSelect; import org.nlpcn.es4sql.domain.Select; import org.nlpcn.es4sql.exception.SqlParseException; import org.nlpcn.es4sql.parse.ElasticLexer; import org.nlpcn.es4sql.parse.ElasticSqlExprParser; import org.nlpcn.es4sql.parse.SqlParser; import org.nlpcn.es4sql.parse.SubQueryExpression; import org.nlpcn.es4sql.query.join.ESJoinQueryActionFactory; import org.nlpcn.es4sql.query.multi.MultiQueryAction; import org.nlpcn.es4sql.query.multi.MultiQuerySelect; import java.sql.SQLFeatureNotSupportedException; import java.util.ArrayList; import java.util.List; public class ESActionFactory { /** * Create the compatible Query object * based on the SQL query. * * @param sql The SQL query. * @return Query object. */ public static QueryAction create(Client client, String sql) throws SqlParseException, SQLFeatureNotSupportedException { sql = sql.replaceAll("\n"," "); String firstWord = sql.substring(0, sql.indexOf(' ')); switch (firstWord.toUpperCase()) { case "SELECT": SQLQueryExpr sqlExpr = (SQLQueryExpr) toSqlExpr(sql); if(isMulti(sqlExpr)){ MultiQuerySelect multiSelect = new SqlParser().parseMultiSelect((SQLUnionQuery) sqlExpr.getSubQuery().getQuery()); handleSubQueries(client,multiSelect.getFirstSelect()); handleSubQueries(client,multiSelect.getSecondSelect()); return new MultiQueryAction(client, multiSelect); } else if(isJoin(sqlExpr,sql)){ JoinSelect joinSelect = new SqlParser().parseJoinSelect(sqlExpr); handleSubQueries(client, joinSelect.getFirstTable()); handleSubQueries(client, joinSelect.getSecondTable()); return ESJoinQueryActionFactory.createJoinAction(client, joinSelect); } else { Select select = new SqlParser().parseSelect(sqlExpr); handleSubQueries(client, select); return handleSelect(client, select); } case "DELETE": SQLStatementParser parser = createSqlStatementParser(sql); SQLDeleteStatement deleteStatement = parser.parseDeleteStatement(); Delete delete = new SqlParser().parseDelete(deleteStatement); return new DeleteQueryAction(client, delete); case "SHOW": return new ShowQueryAction(client,sql); default: throw new SQLFeatureNotSupportedException(String.format("Unsupported query: %s", sql)); } } private static boolean isMulti(SQLQueryExpr sqlExpr) { return sqlExpr.getSubQuery().getQuery() instanceof SQLUnionQuery; } private static void handleSubQueries(Client client, Select select) throws SqlParseException { if (select.containsSubQueries()) { for(SubQueryExpression subQueryExpression : select.getSubQueries()){ QueryAction queryAction = handleSelect(client, subQueryExpression.getSelect()); executeAndFillSubQuery(client , subQueryExpression,queryAction); } } } private static void executeAndFillSubQuery(Client client , SubQueryExpression subQueryExpression,QueryAction queryAction) throws SqlParseException { List<Object> values = new ArrayList<>(); Object queryResult; try { queryResult = QueryActionElasticExecutor.executeAnyAction(client,queryAction); } catch (Exception e) { throw new SqlParseException("could not execute SubQuery: " + e.getMessage()); } String returnField = subQueryExpression.getReturnField(); if(queryResult instanceof SearchHits) { SearchHits hits = (SearchHits) queryResult; for (SearchHit hit : hits) { values.add(ElasticResultHandler.getFieldValue(hit,returnField)); } } else { throw new SqlParseException("on sub queries only support queries that return Hits and not aggregations"); } subQueryExpression.setValues(values.toArray()); } private static QueryAction handleSelect(Client client, Select select) { if (select.isAgg) { return new AggregationQueryAction(client, select); } else { return new DefaultQueryAction(client, select); } } private static SQLStatementParser createSqlStatementParser(String sql) { ElasticLexer lexer = new ElasticLexer(sql); lexer.nextToken(); return new MySqlStatementParser(lexer); } private static boolean isJoin(SQLQueryExpr sqlExpr,String sql) { MySqlSelectQueryBlock query = (MySqlSelectQueryBlock) sqlExpr.getSubQuery().getQuery(); return query.getFrom() instanceof SQLJoinTableSource && sql.toLowerCase().contains("join"); } private static SQLExpr toSqlExpr(String sql) { SQLExprParser parser = new ElasticSqlExprParser(sql); SQLExpr expr = parser.expr(); if (parser.getLexer().token() != Token.EOF) { throw new ParserException("illegal sql expr : " + sql); } return expr; } }