將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;
}