背景

开发es查询程序,每次都要进行编码,大部分代码都是重复代码,实际上最后生成的就是json,简单🤔下,有没有开发人员都会的一种表达语言,将表达语言转换成json,

很明显sql可以,开发人员都会,然后生成项目时,只需用sql来描述,你想要的查询,然后把日期等参数在json中完成替换即可,即可生成查询结果。

es json 如下

{
    "query":{
        "term":{
            "city":{
                "value":"chengdou",
                "boost":1
            }
        }
    },
    "_source":{
        "includes":[
            "name"
        ],
        "excludes":[

        ]
    }
}

antlr4

ANTLR 能够根据用户定义的语法文件自动生成词法分析器和语法分析器,并将输入文本处理为语法分析树。ANTLR 自动生成的编译器高效,能够将开发者从繁杂的编译理论中解放出来,集中精力处理自己的业务逻辑。

ANTRL 4 引入的自动语法分析树创建与遍历机制,极大地提高了语言识别程序的开发效率。我们广为熟知的大数据计算框架的 SQL 解析就是基于 ANTLR,比如 Hive(ANTLR 3)、Spark 2.x(ANTLR 4)、presto(ANTLR 4)等。

定义语法文件

grammar Es;      // 定义一个名为ES的语法,名字与文件名一致



//语法结构
sql: selectoperation;  //定义sql是一个查询操作
selectoperation:
  'select'  field 'from' table where ?  ;   //定义查询语句
field:    ANYTHING                     # ANYTHING  //子规则 *
        |  ID                          # id   //子规则 匹配字母
    ;

table:ID;
where: 'where'  expression ;
expression:  ID '=' ID ;


//词法结构
ID  :   [a-zA-Z]+ ;      // 匹配字母
ANYTHING :   '*' ; // 查询 任务字符

查看生成的语法树

测试SQL
select name from a where city='chengdou'

image.png

生成代码

idea中可以用插件来生成, 只需定义好语法文件即可

image.png

最后生成的结构如下

image.png

测试

// Generated from ANTLR 4.9.2
package com.example.spring.estest;

import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;

import java.util.Arrays;
import java.util.List;

/**
 * This class provides an empty implementation of {@link EsVisitor},
 * which can be extended to create a visitor which only needs to handle a subset
 * of the available methods.
 *
 * @param <T> The return type of the visit operation. Use {@link Void} for
 *            operations with no return type.
 */
public class EsBaseVisitor<T> extends AbstractParseTreeVisitor<T> implements EsVisitor<T> {
    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public T visitSql(EsParser.SqlContext ctx) {
        return visitChildren(ctx);
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public T visitSelectoperation(EsParser.SelectoperationContext ctx) {
        return visitChildren(ctx);
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public T visitANYTHING(EsParser.ANYTHINGContext ctx) {
        return visitChildren(ctx);
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public T visitId(EsParser.IdContext ctx) {
        return visitChildren(ctx);
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public T visitTable(EsParser.TableContext ctx) {
        return visitChildren(ctx);
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public T visitWhere(EsParser.WhereContext ctx) {
        return visitChildren(ctx);
    }

    /**
     * {@inheritDoc}
     *
     * <p>The default implementation returns the result of calling
     * {@link #visitChildren} on {@code ctx}.</p>
     */
    @Override
    public T visitExpression(EsParser.ExpressionContext ctx) {
        return visitChildren(ctx);
    }

    public static void main(String[] args) {

        // 对每一个输入的字符串,构造一个 ANTLRStringStream 流 in
        ANTLRInputStream input = new ANTLRInputStream("select name from a where city='chengdou'");
        // 用 in 构造词法分析器 lexer,词法分析的作用是将字符聚集成单词或者符号
        EsLexer lexer = new EsLexer(input);
        // 用词法分析器 lexer 构造一个记号流 tokens
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        // 再使用 tokens 构造语法分析器 parser,至此已经完成词法分析和语法分析的准备工作
        EsParser parser = new EsParser(tokens);
        EsParser.SqlContext sql = parser.sql();

      //找到查询子规则
        EsParser.SelectoperationContext selectOperation = sql.selectoperation();


      //找到查询列
        String[] queryField = (String[]) Arrays.asList(selectOperation.field().getText()).toArray();


      //查看where 表达式
        List<TerminalNode> id = selectOperation.where().expression().ID();

      //查看where 表达式里的内容
        System.out.println(id.get(0).getText());
        System.out.println(id.get(1).getText());


      //生成es的查询语句
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.fetchSource(queryField, new String[0]);
      TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery(id.get(0).getText(), id.get(1).getText());
      searchSourceBuilder.query(termQueryBuilder);
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.source(searchSourceBuilder);
      System.out.println(searchRequest.source());

    }
}

结果

{
    "query":{
        "term":{
            "city":{
                "value":"chengdou",
                "boost":1
            }
        }
    },
    "_source":{
        "includes":[
            "name"
        ],
        "excludes":[

        ]
    }
}
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐