1. 程式人生 > >將sql轉換為JSON Array

將sql轉換為JSON Array

演算法需求描述

condition:string型別,JSON Array格式,可為空,當做查詢條件,與介面1中的index配合使用。Array中各元素間為“或”關係,元素內的各屬性間為“且”關係。元素內的屬性定義:key為介面1中index設定的屬性,value為JSON Object,該JSON Object的key為比較符,value為某數值(包括字串)。舉例說明:
假設某2條記錄appendData時,index的值分別為:
{“gender”:”男”,”age”:26,”income”:10001}
{“gender”:”女”,”age”:30,”income”:9999}
現在需要查詢收入大於10000的男性或年齡大於等於30歲的女性,則condition為:
[{“income”:{“gt”:10000},”gender”:{“eq”:”男”}},{“age”:{“ge”:30},”gender”:{“eq”:”女”}}]
若對應於SQL的條件則為 :
(income >10000 and gender=’男’) or (age>=30 and gender=’女’)
比較符 含義
eq 等於
ne 不等於
gt 大於
ge 大於等於
lt 小於
le 小於等於

演算法分析

本文是將常用的sql轉換成json的方式,sql中可以任意的and or,以及小括號巢狀,比如

(
    (income >= 10000 OR income < 100)
    AND (
        gender = '女'
        OR monent = 12
        OR NAME = 'l.isi'
    )
)
OR (NAME = '張三' AND ID = 1)

為了方便拆串,將上述sql轉換為一行,可以有任意的and or 邏輯運算操作,不包含 order page pagecount 欄位。小括號以及運算子需要有前後空格 比如( name = ‘張三’ or id = 1 )and ( monent = 12 or name = ‘l.isi’ )

( ( income >= 10000 or income < 100 ) and ( gender = '女' or monent = 12 or name = 'l.isi' ) ) or ( name = '張三' and id = 1 ) 

擷取括號裡邊的內容

大體分析一下,第一個演算法是擷取括號裡邊的內容,比如上述內容,擷取最外層的第一個括號,上述sql可表示為

左邊:( income >= 10000 or income < 100 ) and ( gender = '女' or monent = 12 or name = 'l.isi' )
右邊:or ( name = '張三'
and id = 1 )

演算法程式碼描述
擷取括號最重要的是使用棧,遇到”(”入棧,第一個需要記錄位置,遇到”)”出棧,出棧以後判斷棧中元素個數,如果為0,表示第一個括號擷取成功,把第一個括號中的內容截取出來,剩下的放到另一個地方。本事例用二叉樹作為資料結構,左節點是第一個括號裡邊的,右節點為擷取第一個括號後剩下的內容

  /**
     * 對第一層括號進行拆分
     *  左邊節點放置去掉第一層括號的資料,右邊節點放置sql - 左節點sql
     * @param sql
     * @return
     */
    private static TreeNode subBrackets(String sql) {
        TreeNode node = new TreeNode();
        Queue<String> queue = new ArrayDeque<String>();
        int left = 0;
        for(int i=0;i<sql.length();i++){
            char temchar = sql.charAt(i);
            if( temchar =='('){
                if(queue.size() ==0){
                    left = i;
                }
                queue.add("(");
            }
            if( temchar ==')'){
                queue.poll();
                if(queue.size() == 0){
                    node.value = sql;
                    node.left = new TreeNode(sql.substring(1,i-1));
                    node.right = new TreeNode(sql.substring(i+1,sql.length()).trim());
                    return node;
                }
            }
        }
        return node;
    }

TreeNode,是普通的二叉樹,資料結構如下邊的程式碼

 static class TreeNode {
        TreeNode left;
        TreeNode right;
        Object value;
        public TreeNode() {
        }
        public TreeNode(String value) {
            this.value = value;
        }

        public TreeNode getLeft() {
            return left;
        }

        public void setLeft(TreeNode left) {
            this.left = left;
        }

        public TreeNode getRight() {
            return right;
        }

        public void setRight(TreeNode right) {
            this.right = right;
        }

        public Object getValue() {
            return value;
        }

        public void setValue(Object value) {
            this.value = value;
        }

        @Override
        public String toString() {
            return value.toString();
        }
    }

以and or ,比較運算(>,< ,=,……),將sql轉換為樹

為什麼要將sql轉換成樹,計算機不好識別 name = ‘張三’,此時要轉換成

"left": {
          "left": {
              "value": "gender"
          },
           " right": {
               "value": "’男’"
           },
           "value": "="
       }

演算法表示

/**
     * 構建二叉樹,葉子節點為資料值,父節點為運算子,如果最後構建的資料為非滿二叉樹,則sql資料格式錯誤
     *  "left": {
     *      "left": {
     *          "value": "gender"
     *       },
     *      " right": {
     *          "value": "’男’"
     *      },
     *      "value": "="
     *  },
     * 左新增,右遞迴
     * @param sql
     * @return
     */
    private static  TreeNode paraseSqlTree(String sql) {
        sql =sql.trim();
        TreeNode tempNode = new TreeNode();
        if(sql.startsWith("(")){
            //構建括號裡的資料
            TreeNode braketsNode = subBrackets(sql);
            if(braketsNode != null){
                if(braketsNode.right != null && !(braketsNode.right.value.equals(""))){
                    tempNode.left = paraseSqlTree(braketsNode.left.value.toString());
                    TreeNode tempRightNode = paraseSqlTree(braketsNode.right.value.toString());
                    if(tempRightNode.left != null && tempRightNode.left.value != null){
                        tempNode.right = tempRightNode;
                    }else {
                        tempNode.right = tempRightNode.right;
                        tempNode.value = tempRightNode.value;
                    }
                }else {
                    tempNode = paraseSqlTree(braketsNode.left.value.toString());
                }
                return tempNode;
            }
        }else {
            return paraseLogicSql(sql);
        }
        return null;
    }
    /**
     * 括號以外的邏輯處理資料
     *   第一個邏輯符號 (and|or)左邊部分新增新的節點,右邊部分遞迴呼叫
     * sql 邏輯處理 比如 and or > =
     * @param sql
     * 要處理的sql語句
     */
    private static TreeNode paraseLogicSql(String sql) {
        String[] stre =sql.split(" ");
        TreeNode tempNode = new TreeNode();
        for(int i = 0;i<stre.length;i++){
            String temstr = stre[i];
            if(temstr.matches("(and)|(or)")){
                TreeNode parentNode = new  TreeNode();
                parentNode.value = temstr;
                if(tempNode != null){
                    parentNode.left = tempNode;
                    parentNode.right = paraseSqlTree(subleaftsql(stre,i).trim());
                }
                return parentNode;
            }else {
                if(Compare.matchMean(temstr) != null){
                    tempNode.left = new TreeNode(stre[i-1].trim());
                    TreeNode rightNode = new TreeNode();
                    String trimValue = stre[i+1].trim();
                    if(trimValue.startsWith("\'")) {
                        rightNode.value = trimValue.substring(1,trimValue.length()-1);
                    }else {
                        try{
                            rightNode.value = Double.valueOf(trimValue);
                        }catch (Exception e){
                            rightNode.value = trimValue;
                        }
                    }
                    tempNode.right = rightNode;
                    tempNode.value = temstr;
                    if(i+2 == stre.length){
                        return  tempNode;
                    }
                }
            }
        }
        return tempNode;
    }

把樹中的內容轉換成JsonArray,此時想到了離散數學的合取正規化

由於化為合取正規化時需要排除重複的元素,用Set標識
為了遞迴方便,如果是or連線的,Map中”or”為key,value為or拼接完以後的Set資料
程式碼標識

static Map<String,Object> paraseLimitSqlCondition(TreeNode node){
        Map<String,Object> nodeMap = new HashedMap();
        if(node.value.equals("and")){
            Map<String,Object>  left = paraseLimitSqlCondition(node.left);
            Map<String,Object>  right = paraseLimitSqlCondition(node.right);
            //都是 and連線
            if(left.get("or")==null && right.get("or")==null ) {
                left.putAll(right);
                return left;
            }
            //左邊and,右邊or
            if(left.get("or")==null && right.get("or")!=null ){
                Set<Map<String,Object>> rightOr = (Set<Map<String,Object>>)right.get("or");
                Set<Map<String,Object>> resultSet =   new HashSet<>();
                for(Map<String,Object> rightEach:rightOr){
                    rightEach.putAll(left);
                    resultSet.add(rightEach);
                }
                nodeMap.put("or",resultSet);
                return nodeMap;
            }
            //右邊and,左邊or
            if(right.get("or")==null && left.get("or")!=null ){
                Set<Map<String,Object>> leftOr = (Set<Map<String,Object>>)left.get("or");
                Set<Map<String,Object>> resultSet =   new HashSet<>();
                for(Map<String,Object> leftEach:leftOr){
                    leftEach.putAll(right);
                    resultSet.add(leftEach);
                }
                nodeMap.put("or",resultSet);
                return nodeMap;
            }
            //兩邊都是or
            if(left.get("or")!=null && right.get("or")!=null ){
                Set<Map<String,Object>> leftOr = (Set<Map<String,Object>>)left.get("or");
                Set<Map<String,Object>> rightOr = (Set<Map<String,Object>>)right.get("or");
                Set<Map<String,Object>> resultSet =   new HashSet<>();
                for(Map<String,Object> leftEach:leftOr){
                    for(Map<String,Object> rightEach:rightOr){
                        Map<String,Object> ecahMap= new HashMap<>();
                        ecahMap.putAll(leftEach);
                        ecahMap.putAll(rightEach);
                        resultSet.add(ecahMap);
                    }
                }
                nodeMap.put("or",resultSet);
                return nodeMap;
            }
        }
        if(node.value.equals("or")){
            Set< Map<String,Object>> resultSet = new HashSet<>();
            Map<String,Object>  left = paraseLimitSqlCondition(node.left);
            Map<String,Object>  right = paraseLimitSqlCondition(node.right);
            if(left.get("or") == null && right.get("or") == null){
                resultSet.add(left);
                resultSet.add(right);
                nodeMap.put("or",resultSet);
                return nodeMap;
            }
            if(left.get("or") != null && right.get("or") == null){
                Set< Map<String,Object>> set = (Set< Map<String,Object>>)left.get("or");
                set.add(right);
                nodeMap.put("or",set);
                return nodeMap;
            }
            if(left.get("or") == null && right.get("or") != null){
                Set< Map<String,Object>> set = (Set< Map<String,Object>>)right.get("or");
                set.add(left);
                nodeMap.put("or",set);
                return nodeMap;
            }
            if(left.get("or") == null && right.get("or") == null){
                Set< Map<String,Object>> leftset = (Set< Map<String,Object>>)left.get("or");
                Set< Map<String,Object>> rightset = (Set< Map<String,Object>>)left.get("or");
                leftset.addAll(rightset);
                nodeMap.put("or",leftset);
                return nodeMap;
            }
        }
        if(Compare.matchMean(node.value.toString())!=null){
            return ( createBaseCondition(node));
        }
        return null;
    }