手写一个简单的ElasticSearch SQL转换器(一)

本文介绍了为何要手动实现一个简单的ElasticSearch SQL转换器,选择了Druid作为解析工具,并详细讲解了如何处理SQL的where、order by子句,以及不同类型的运算符转换成ES DSL的过程。最后通过示例展示了转换后的查询语句。
摘要由CSDN通过智能技术生成

   一.前言

   之前有个需求,是使ElasticSearch支持使用SQL进行简单查询,较新版本的ES已经支持该特性(不过貌似还是实验性质的?) ,而且git上也有elasticsearch-sql

插件,之所以决定手写一个,主要有两点原因:

      1. 目前用的ES版本较老

      2. elasticsearch-sql虽好,但比较复杂,代码也不易维护

      3. 练练手

 二.技术选型

   目前主流软件中通常使用ANTLR做词法语法分析,诸如著名的Hibernate,Spark,Hive等项目,之前因为工作原因也有所接触,不过如果只是解析标准SQL的话,

其实还有更好的选择,如使用Hibernate或阿里巴巴的 数据库Druid(Druid采用了手写词法语法分析器的方案,这种方式当然比自动ANTLR生成的解析器性能高得多), 这里

 我选择了第二种方案。

     开始之前先看下我们可以通过Druid拿到的SQL语言的抽象语法树:

                                                  图片:https://www.jianshu.com/p/437aa22ea3ca

 三.技术实现

     首先我们创建一个SqlParser类,主流程都在parse方法中,该方法负责将一个SQL字符串解析(顺便说一句,Druid支持多种SQL方言,这里我选择了MySQL),

 并返回SearchSourceBuilder对象,这是一个ElasticSearch提供的DSL构建器,以该对象作为参数,ES client端即可发起对ES 服务端搜索请求。

 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解析生成AST
27         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         // 生成ES查询条件
38         BoolQueryBuilder bridge = QueryBuilders.boolQuery();
39         bridge.must();
40 
41         QueryBuilder whereBuilder = whereHelper(whereExpr); // 处理where
42         bridge.must(whereBuilder);
43         SQLOrderBy orderByExpr = sqlSelectQueryBlock.getOrderBy(); // 处理order by
44         if (Objects.nonNull(orderByExpr)) {
45             orderByHelper(orderByExpr, bridge);
46         }
47         builder.query(bridge);
48         return builder;
49     }

    主流程很简单,拿到SQL字符串后,直接通过Druid API将其转换为抽象语法树,我们要求输入语句必须为Select语句。接下来是对where语句和order by语句的处理,

  目前的难点其实主要在于如何将where语句映射到ES查询中。

     先从简单的看起,如何处理order by呢?SQL语句中 order by显然可以允许用户根据多字段排序,所以排序字段肯定是一个List<排序字段>,我们要做的就是将这个List映射到

SearchSourceBuilder对象中。见下面代码:

 1     /**
 2 
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值