sql select 语句 转 Json

最近有个Es查询的需求,用户在前端输入sql语句直接拼条件,然后后台去查询。

因为es本身带有类sql查询,刚开始打算用sql查的,但是分页的limit只有一个查询条数,没有from和size,比如es可以通过类sql 的 limit 1000 一次查出来1000条数据,但是没法通过limit 900,100查出来第900到1000的数据,想实现就得先limit 1000再去截取后100条,没办法了,只能解析sql再去拼接条件了。这块的解析实现需要感谢以下两篇贴子,

灵感来源

https://elasticsearch.cn/article/114

用二叉树简直是神设计,当时为了最终结构困扰了我半天
在这里插入图片描述

https://blog.csdn.net/qzshiyongjie123/article/details/54341472

这篇关于具体代码实现讲的挺详细的,唯一的缺点是代码不全,我也是在这篇的基础上修改来的

代码实现

实体结构

这块本质上还是二叉树,但是新加了几个属性,以如下语句举例说明

 a = 1 or (b = 2 and not c != 3)
 
 left:     英文字段名 // a、b、c
 right:   字段对应值 // 1、2、3
 inRelat: 字段和值的关系 // =!=
 outRelat  (字段-值)之间的连接关系  // and、or
 hasNot    当前(字段-值)是否包含not  
 sqlSon   括号圈起来的算一个整体

实体属性如下:

@Data
public class SqlNode {

    // 字段名
    private String left;

    // 值
    private String right;

    // 内关系符 > < =
    private String inRelat;

    // 外关系符 and or
    private String outRelat;

    // 是否not
    private boolean hasNot;

    // 子关系式
    private List<SqlNode> sqlSon;

    public SqlNode() {
    }

    public SqlNode(String left, String right, String inRelat, boolean hasNot) {
        this.left = left;
        this.right = right;
        this.inRelat = inRelat;
        this.hasNot = hasNot;
    }
}

1.将sql语句分解成数组

public static String[] handSpace(String sql) {
        sql = sql.trim();
        String tempStr = "";
        boolean hasFirstSpace = false;
        boolean hasDoublePoint = false;

        List<String> list = new ArrayList<>();
        for (int i = 0; i < sql.length(); i++) {
            char curChar = sql.charAt(i);
            String charStr = Convert.toStr(curChar);

            switch (curChar) {
                case '"':
                    hasDoublePoint = hasDoublePoint == false ? true : false;
                    if (i == sql.length() - 1) {
                        list.add(tempStr);
                    }
                    break;
                case '(':
                    if (hasDoublePoint == false) {
                        // 不在字符串内直接 添加 (
                        list.add(charStr);
                    } else {
                        // 字符串内继续拼接
                        tempStr += charStr;
                    }
                    break;
                case ')':
                    // 不在字符串内
                    if (hasDoublePoint == false) {
                        if (StringUtils.isNotBlank(tempStr)) {
                            // 前面有字符串
                            list.add(tempStr);
                            tempStr = "";
                        }
                        // 遍历到最后一位 -> 直接添加
                        if (i == sql.length() - 1) {
                            list.add(")");
                        } else {
                            list.add(")");
                        }
                    } else {
                        // 字符串内继续拼接
                        tempStr += charStr;
                    }
                    break;
                case ' ':
                    // 不在字符串内
                    if (hasDoublePoint == false) {
                        // 第一个空格 -> 添加前一个词组
                        if (hasFirstSpace == false) {
                            if (StringUtils.isNotBlank(tempStr)) {
                                list.add(tempStr);
                                tempStr = "";
                                hasFirstSpace = true;
                            }
                        }
                    } else {
                        // 字符串内继续拼接
                        tempStr += curChar;
                    }
                    break;
                default:
                    // 普通字符继续拼接
                    tempStr += curChar;
                    hasFirstSpace = false;
                    // 遍历到最后一位 -> 直接添加
                    if (i == sql.length() - 1) {
                        list.add(tempStr);
                    }
                    break;
            }
        }
        String[] array = new String[list.size()];

        return list.toArray(array);
    }

这步主要是为了拆分数据,便于下一步更好进行条件转换,写个sql看下实际效果在这里插入图片描述

2.对数组完成树结构的转换

public static void paraseSql(String[] arr, List<SqlNode> nodes) throws Exception {
        Queue<String> queue = new ArrayDeque<String>();
        int bracketsStart = 0;
        int bracketsEnd = 0;
        for (int i = 0; i < arr.length; i++) {
            String left;
            String right = null;
            String outRelat;
            String inRelat;
            boolean hasNot;

            String curStr = arr[i];
            if (curStr.equals("(")) {
                if (queue.size() == 0) {
                    // 只记录首次下标
                    bracketsStart = i;
                }
                queue.add("(");
            } else if (curStr.equals(")")) {
                queue.poll();
                if (queue.size() == 0) {
                    // 括号闭环了 才能处理
                    // 判断and not / or not
                    hasNot = bracketsStart - 1 > 0 ? (arr[bracketsStart - 1].matches(NOT)) : false;
                    int andOrIndex;
                    // 前面是否有 and / or
                    if (bracketsStart - 1 > 0) {
                        andOrIndex = hasNot ? bracketsStart - 2 : bracketsStart - 1;
                        outRelat = arr[andOrIndex].matches(AND) ? AND : OR;
                    } else {
                        // 后面 and / or
                        outRelat = i + 1 < arr.length ? (arr[i + 1].matches(AND) ? AND : OR) : AND;
                    }
                    bracketsEnd = i;

                    SqlNode sqlNode = new SqlNode();
                    List<SqlNode> sonNodes = new ArrayList<>();
                    String[] noBracketsArr = Arrays.copyOfRange(arr, bracketsStart + 1, bracketsEnd);
                    // 接着递归处理
                    paraseSql(noBracketsArr, sonNodes);
                    sqlNode.setSqlSon(sonNodes);
                    sqlNode.setOutRelat(outRelat);
                    sqlNode.setHasNot(hasNot);
                    nodes.add(sqlNode);
                }
            } else if (curStr.matches("(" + AND + ")|(" + OR + ")")) {
                // 括号遍历完了才能加
                if (queue.size() == 0) {
                    if (!arr[i - 1].equals(")")) {
                        boolean hasExist = (arr[i - 1].equals(EXIST) || arr[i - 1].equals(NOT_EXIST)) ? true : false;
                        if (hasExist) {
                            hasNot = i - 3 >= 0 ? (arr[i - 3].equals(NOT) ? true : false) : false;
                            // 第一个条件以当前and / or 为准,语句中间的以前面的条件为准
                            outRelat = i - 3 > 0 ? (arr[i - 3].matches("(" + AND + ")|(" + OR + ")") ? arr[i - 3] : arr[i - 4]) : curStr;
                            left = arr[i - 2];
                            inRelat = arr[i - 1];
                        } else {
                            hasNot = i - 4 >= 0 ? (arr[i - 4].equals(NOT) ? true : false) : false;
                            outRelat = i - 4 > 0 ? (arr[i - 4].matches("(" + AND + ")|(" + OR + ")") ? arr[i - 4] : arr[i - 5]) : curStr;
                            left = arr[i - 3];
                            inRelat = arr[i - 2];
                            right = arr[i - 1];
                        }
                        SqlNode sqlNode = new SqlNode();
                        sqlNode.setLeft(left);
                        sqlNode.setHasNot(hasNot);
                        sqlNode.setRight(right);
                        sqlNode.setInRelat(inRelat);
                        sqlNode.setOutRelat(outRelat);
                        nodes.add(sqlNode);
                    }
                }
            } else if (i == arr.length - 1) {
                // 最后一个了
                // 判断是不是 exist
                boolean hasExist = (arr[i].equals(EXIST) || arr[i].equals(NOT_EXIST)) ? true : false;
                if (hasExist) {
                    hasNot = i - 2 > 0 ? (arr[i - 2].equals(NOT) ? true : false) : false;
                    outRelat = i - 2 > 0 ? (arr[i - 2].matches("(" + AND + ")|(" + OR + ")") ? arr[i - 2] : arr[i - 3]) : AND;
                    left = arr[i - 1];
                    inRelat = arr[i];
                } else {
                    // 有 and / or
                    hasNot = i - 3 > 0 ? (arr[i - 3].equals(NOT) ? true : false) : false;
                    outRelat = i - 3 > 0 ? (arr[i - 3].matches("(" + AND + ")|(" + OR + ")") ? arr[i - 3] : arr[i - 4]) : AND;
                    left = arr[i - 2];
                    right = arr[i];
                    inRelat = arr[i - 1];
                }
                if (!outRelat.matches("(" + AND + ")|(" + OR + ")")) {
                    throw new Exception("sql语法有误,请修改后重试!");
                }
                SqlNode sqlNode = new SqlNode();
                sqlNode.setLeft(left);
                sqlNode.setRight(right);
                sqlNode.setInRelat(inRelat);
                sqlNode.setOutRelat(outRelat);
                sqlNode.setHasNot(hasNot);
                nodes.add(sqlNode);
            }
        }
    }

传一个空list看下实际效果
在这里插入图片描述
成功分离出来了,现在只要遍历SqlNode集合就能按照关系组装出想要的数据了。

下面贴上全部代码

package com.event.entity.param;

import com.common.util.text.Convert;
import com.event.util.SqlNode;
import lombok.Builder;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;

import java.util.*;

/**
 * @author fjl
 * @date 2022/9/26
 */
@Data
@Builder
public class util {
    private static final String AND = "and";
    private static final String OR = "or";
    private static final String NOT = "not";
    private static final String EXIST = "exist";

    private static final String NOT_EXIST = "not_exist";

    public static void main(String[] args) throws Exception {

        String str = "a = 1 and ( b = 3 or c = 4 )";
        String[] fieldsArr = handSpace(str);
        List<SqlNode> nodes = new ArrayList<>();
        paraseSql(fieldsArr, nodes);
    }

    public static void paraseSql(String[] arr, List<SqlNode> nodes) throws Exception {
        Queue<String> queue = new ArrayDeque<String>();
        int bracketsStart = 0;
        int bracketsEnd = 0;
        for (int i = 0; i < arr.length; i++) {
            String left;
            String right = null;
            String outRelat;
            String inRelat;
            boolean hasNot;

            String curStr = arr[i];
            if (curStr.equals("(")) {
                if (queue.size() == 0) {
                    // 只记录首次下标
                    bracketsStart = i;
                }
                queue.add("(");
            } else if (curStr.equals(")")) {
                queue.poll();
                if (queue.size() == 0) {
                    // 括号闭环了 才能处理
                    // 判断and not / or not
                    hasNot = bracketsStart - 1 > 0 ? (arr[bracketsStart - 1].matches(NOT)) : false;
                    int andOrIndex;
                    // 前面是否有 and / or
                    if (bracketsStart - 1 > 0) {
                        andOrIndex = hasNot ? bracketsStart - 2 : bracketsStart - 1;
                        outRelat = arr[andOrIndex].matches(AND) ? AND : OR;
                    } else {
                        // 后面 and / or
                        outRelat = i + 1 < arr.length ? (arr[i + 1].matches(AND) ? AND : OR) : AND;
                    }
                    bracketsEnd = i;

                    SqlNode sqlNode = new SqlNode();
                    List<SqlNode> sonNodes = new ArrayList<>();
                    String[] noBracketsArr = Arrays.copyOfRange(arr, bracketsStart + 1, bracketsEnd);
                    // 接着递归处理
                    paraseSql(noBracketsArr, sonNodes);
                    sqlNode.setSqlSon(sonNodes);
                    sqlNode.setOutRelat(outRelat);
                    sqlNode.setHasNot(hasNot);
                    nodes.add(sqlNode);
                }
            } else if (curStr.matches("(" + AND + ")|(" + OR + ")")) {
                // 括号遍历完了才能加
                if (queue.size() == 0) {
                    if (!arr[i - 1].equals(")")) {
                        boolean hasExist = (arr[i - 1].equals(EXIST) || arr[i - 1].equals(NOT_EXIST)) ? true : false;
                        if (hasExist) {
                            hasNot = i - 3 >= 0 ? (arr[i - 3].equals(NOT) ? true : false) : false;
                            // 第一个条件以当前and / or 为准,语句中间的以前面的条件为准
                            outRelat = i - 3 > 0 ? (arr[i - 3].matches("(" + AND + ")|(" + OR + ")") ? arr[i - 3] : arr[i - 4]) : curStr;
                            left = arr[i - 2];
                            inRelat = arr[i - 1];
                        } else {
                            hasNot = i - 4 >= 0 ? (arr[i - 4].equals(NOT) ? true : false) : false;
                            outRelat = i - 4 > 0 ? (arr[i - 4].matches("(" + AND + ")|(" + OR + ")") ? arr[i - 4] : arr[i - 5]) : curStr;
                            left = arr[i - 3];
                            inRelat = arr[i - 2];
                            right = arr[i - 1];
                        }
                        SqlNode sqlNode = new SqlNode();
                        sqlNode.setLeft(left);
                        sqlNode.setHasNot(hasNot);
                        sqlNode.setRight(right);
                        sqlNode.setInRelat(inRelat);
                        sqlNode.setOutRelat(outRelat);
                        nodes.add(sqlNode);
                    }
                }
            } else if (i == arr.length - 1) {
                // 最后一个了
                // 判断是不是 exist
                boolean hasExist = (arr[i].equals(EXIST) || arr[i].equals(NOT_EXIST)) ? true : false;
                if (hasExist) {
                    hasNot = i - 2 > 0 ? (arr[i - 2].equals(NOT) ? true : false) : false;
                    outRelat = i - 2 > 0 ? (arr[i - 2].matches("(" + AND + ")|(" + OR + ")") ? arr[i - 2] : arr[i - 3]) : AND;
                    left = arr[i - 1];
                    inRelat = arr[i];
                } else {
                    // 有 and / or
                    hasNot = i - 3 > 0 ? (arr[i - 3].equals(NOT) ? true : false) : false;
                    outRelat = i - 3 > 0 ? (arr[i - 3].matches("(" + AND + ")|(" + OR + ")") ? arr[i - 3] : arr[i - 4]) : AND;
                    left = arr[i - 2];
                    right = arr[i];
                    inRelat = arr[i - 1];
                }
                if (!outRelat.matches("(" + AND + ")|(" + OR + ")")) {
                    throw new Exception("sql语法有误,请修改后重试!");
                }
                SqlNode sqlNode = new SqlNode();
                sqlNode.setLeft(left);
                sqlNode.setRight(right);
                sqlNode.setInRelat(inRelat);
                sqlNode.setOutRelat(outRelat);
                sqlNode.setHasNot(hasNot);
                nodes.add(sqlNode);
            }
        }
    }

    public static String[] handSpace(String sql) {
        sql = sql.trim();
        String tempStr = "";
        boolean hasFirstSpace = false;
        boolean hasDoublePoint = false;

        List<String> list = new ArrayList<>();
        for (int i = 0; i < sql.length(); i++) {
            char curChar = sql.charAt(i);
            String charStr = Convert.toStr(curChar);

            switch (curChar) {
                case '"':
                    hasDoublePoint = hasDoublePoint == false ? true : false;
                    if (i == sql.length() - 1) {
                        list.add(tempStr);
                    }
                    break;
                case '(':
                    if (hasDoublePoint == false) {
                        // 不在字符串内直接 添加 (
                        list.add(charStr);
                    } else {
                        // 字符串内继续拼接
                        tempStr += charStr;
                    }
                    break;
                case ')':
                    // 不在字符串内
                    if (hasDoublePoint == false) {
                        if (StringUtils.isNotBlank(tempStr)) {
                            // 前面有字符串
                            list.add(tempStr);
                            tempStr = "";
                        }
                        // 遍历到最后一位 -> 直接添加
                        if (i == sql.length() - 1) {
                            list.add(")");
                        } else {
                            list.add(")");
                        }
                    } else {
                        // 字符串内继续拼接
                        tempStr += charStr;
                    }
                    break;
                case ' ':
                    // 不在字符串内
                    if (hasDoublePoint == false) {
                        // 第一个空格 -> 添加前一个词组
                        if (hasFirstSpace == false) {
                            if (StringUtils.isNotBlank(tempStr)) {
                                list.add(tempStr);
                                tempStr = "";
                                hasFirstSpace = true;
                            }
                        }
                    } else {
                        // 字符串内继续拼接
                        tempStr += curChar;
                    }
                    break;
                default:
                    // 普通字符继续拼接
                    tempStr += curChar;
                    hasFirstSpace = false;
                    // 遍历到最后一位 -> 直接添加
                    if (i == sql.length() - 1) {
                        list.add(tempStr);
                    }
                    break;
            }
        }
        String[] array = new String[list.size()];
        return list.toArray(array);
    }
 }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值