手写一个简单的ElasticSearch SQL转换器(一)
一.前言
之前有个需求,是使ElasticSearch支持使用SQL进行简单查询,较新版本的ES已经支持该特性(不过貌似还是实验性质的?) ,而且git上也有elasticsearch-sql
插件,之所以决定手写一个,主要有两点原因:
1. 目前用的ES版本较老
2. elasticsearch-sql虽好,但比较复杂,代码也不易维护
3. 练练手
二.技术选型
目前主流软件中通常使用ANTLR做词法语法分析,诸如著名的Hibernate,Spark,Hive等项目,之前因为工作原因也有所接触,不过如果只是解析标准SQL的话,
其实还有更好的选择,如使用Hibernate或阿里巴巴的数据库Druid(Druid采用了手写词法语法分析器的方案,这种方式当然比自动ANTLR生成的解析器性能高得多), 这里
我选择了第二种方案。
开始之前先看下我们可以通过Druid拿到的SQL语言的抽象语法树:
图片:upload/201911011031552434.gif" alt="复制代码" style="margin: 0px; padding: 0px; max-width: 800px; height: auto; border: none !important;" />
1 /** 2 * 3 * @author fred 4 * 5 */ 6 public class SqlParser { 7 private final static String dbType = JdbcConstants.MYSQL; 8 private final static Logger logger = LoggerFactory.getLogger(SqlParser.class); 9 private SearchSourceBuilder builder; 10 11 public SqlParser(SearchSourceBuilder builder) { 12 this.builder = builder; 13 } 14 /**15 * 将SQL解析为ES查询 16 */17 public SearchSourceBuilder parse(String sql) throws Exception { 18 if (Objects.isNull(sql)) { 19 throw new IllegalArgumentException("输入语句不得为空"); 20 } 21 sql = sql.trim().toLowerCase(); 22 List<SQLStatement> stmtList = SQLUtils.parseStatements(sql, dbType); 23 if (Objects.isNull(stmtList) || stmtList.size() != 1) { 24 throw new IllegalArgumentException("必须输入一句查询语句"); 25 } 26 // 使用Parser解析生成AST27 SQLStatement stmt = stmtList.get(0); 28 if (!(stmt instanceof SQLSelectStatement)) { 29 throw new IllegalArgumentException("输入语句须为Select语句"); 30 } 31 SQLSelectStatement sqlSelectStatement = (SQLSelectStatement) stmt; 32 SQLSelectQuery sqlSelectQuery = sqlSelectStatement.getSelect().getQuery(); 33 SQLSelectQueryBlock sqlSelectQueryBlock = (SQLSelectQueryBlock) sqlSelectQuery; 34 35 SQLExpr whereExpr = sqlSelectQueryBlock.getWhere(); 36 37