以前寫過一篇博客,是關於如何解析相似sql之類的解析器實現參考:http://www.javashuo.com/article/p-bqxebcca-nu.htmlhtml
以前的解析器,更多的是是作語言的翻譯轉換工做,並不涉及具體的數據運算。並且拋棄了許多上下文關聯語法處理,因此相對仍是簡單的。java
那麼,若是咱們想作一下數據運算呢?好比我給你一些值,而後給你一個表達式,你能夠給出其運算結果嗎?sql
好比,已知表達式爲, field1 > 0 and field2 > 0, 而後已知道值 field1 = 1, field2 = 2; 那麼,此運算結果必當爲true。這很理所固然!apache
但以上,僅爲人工處理,本身用大腦作了下運算,獲得結果。若是轉換爲代碼,又當如何?mvc
我想,咱們至少要作這麼幾件事:app
1. 解析出全部字段有field1, field2;
2. 解析出比較運算符 >;
3. 解析出右邊具體的比較值;
4. 解析出鏈接運算符and;
5. 作全部的比較運算;
6. 關聯優先級獲得最終結果;ide
怎麼樣?如今還以爲很簡單嗎?若是是,請收下個人膝蓋!單元測試
可是,若是真要作這種泛化的場景,那就至關至關複雜了,要知道相似於HIVE之類的重量級產品,語法解析都是其中重要的組成部分。實際上,這可能涉及到至關多的語言規範須要作了。因此,必然超出咱們的簡化理解範圍。測試
因此,我這裏僅挑一個簡單場景作解析:即如題所說,case..when..的解析。this
因此,咱們能夠範圍縮減爲,給定表達式:case when field1 > 0 then 'f1' else 'fn' end; 的判斷解析。好比給定值 field1=1, 則應獲得結果 f1, 若是給定值 field1=0, 則應獲得結果 fn.
在劃定範圍以後,好像更有了目標感了。可是問題真的簡單了嗎?實際上,仍是有至關多的分支須要處理的,由於case..when..中能夠嵌套其餘語法。因此,咱們只能盡力而爲了。
命題確立以後,咱們能夠開始着手如何實現了。如上描述,咱們有兩個已知條件:表達式和基礎值。
基於上一篇文章的解析,咱們基本能夠快速獲得全部組成case when 的元素token信息了。這就爲咱們省去了很多事。這裏,我着重給一個如何獲取整個case..when..詞句的實現,使其可造成一個獨立的詞組。
// 將case..when.. 歸結爲sql類關鍵詞的實現中 public SqlKeywordAstHandler(TokenDescriptor masterToken, Iterator<TokenDescriptor> candidates, TokenTypeEnum tokenType) { super(masterToken, candidates, tokenType); String word = masterToken.getRawWord().toLowerCase(); if("case".equals(word)) { completeCaseWhenTokens(candidates); } } /** * 實例case...when... 詞彙列表 * * @param candidates 待用詞彙 */ private void completeCaseWhenTokens(Iterator<TokenDescriptor> candidates) { boolean syntaxClosed = false; while (candidates.hasNext()) { TokenDescriptor token = candidates.next(); addExtendToken(token); if("end".equalsIgnoreCase(token.getRawWord())) { syntaxClosed = true; break; } } if(!syntaxClosed) { throw new SyntaxException("語法錯誤:case..when..未半閉合"); } }
以上,就是獲取case..when..詞組的方法了,主要就是從case開始,到end結束,中間的全部詞根,都被劃做其範圍。固然,還有一個重要的點,是將數據字段找出來,放到可取到的地方。
有了一個個獨立的元素,咱們就能夠進行語義分析了。該分析能夠放在該解析器中,但也許並不會太通用,因此,此處我將其抽象爲一個單獨的值運算類。在須要的地方,再實例化該運算類,便可。核心問題如上一節中描述,具體實現代碼以下:
import com.my.mvc.app.common.exception.SyntaxException; import com.my.mvc.app.common.helper.parser.SyntaxStatement; import com.my.mvc.app.common.helper.parser.TokenDescriptor; import com.my.mvc.app.common.helper.parser.TokenTypeEnum; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import java.math.BigDecimal; import java.util.*; import java.util.stream.Collectors; /** * 功能描述: case..when.. 真實數據運算幫助類 * */ @Slf4j public class CaseWhenElDataCalcHelper { /** * case when 完整語法 */ private SyntaxStatement caseWhenStmt; public CaseWhenElDataCalcHelper(SyntaxStatement caseWhenStmt) { this.caseWhenStmt = caseWhenStmt; } /** * 計算case..when的結果 * * @param suppliers 原始全部值 * @return 最終計算出的值 */ public String calcCaseWhenData(Map<String, String> suppliers) { List<TokenDescriptor> allTokens = caseWhenStmt.getAllTokens(); TokenDescriptor masterToken = allTokens.get(0); if(!"case".equalsIgnoreCase(masterToken.getRawWord())) { throw new SyntaxException("不是case..when..表達式"); } int tokenLen = allTokens.size(); if(tokenLen < 3) { throw new SyntaxException("case..when..表達式語法錯誤"); } TokenDescriptor closureToken = allTokens.get(tokenLen - 1); if(!"end".equalsIgnoreCase(closureToken.getRawWord())) { throw new SyntaxException("case..when..表達式未閉合"); } // 暫只支持 case when xxx then xxx... end 語法 // 不支持 case field_name when 1 then '1'... end, 即單字段斷定不支持 List<TokenDescriptor> whenExpressionCandidates; for (int i = 1; i < tokenLen - 1; i++) { whenExpressionCandidates = new ArrayList<>(); TokenDescriptor currentToken = allTokens.get(i); if("when".equalsIgnoreCase(currentToken.getRawWord())) { // 需走各分支邏輯 while (i + 1 < tokenLen) { TokenDescriptor nextToken = allTokens.get(i + 1); if("then".equalsIgnoreCase(nextToken.getRawWord())) { break; } whenExpressionCandidates.add(nextToken); ++i; } if(judgeWhenExpression(whenExpressionCandidates, suppliers)) { List<TokenDescriptor> resultCandidates = scrapeCaseWhenResultCandidates(allTokens, i + 1); return calcExpressionData(resultCandidates, suppliers); } // 直接進入下一輪迭代,then後面爲空迭代 } if("else".equalsIgnoreCase(currentToken.getRawWord())) { List<TokenDescriptor> resultCandidates = scrapeCaseWhenResultCandidates(allTokens, i); return calcExpressionData(resultCandidates, suppliers); } } return null; } /** * 撈出全部的結果運算token列表 * * @param allTokens 全局token表 * @param start 偏移量 * @return 獲取到的全部結果運算token */ private List<TokenDescriptor> scrapeCaseWhenResultCandidates(List<TokenDescriptor> allTokens, int start) { List<TokenDescriptor> resultCandidates = new ArrayList<>(); while (start + 1 < allTokens.size()) { TokenDescriptor nextToken = allTokens.get(start + 1); String word = nextToken.getRawWord(); if("when".equalsIgnoreCase(word) || "else".equalsIgnoreCase(word) || "end".equalsIgnoreCase(word)) { break; } resultCandidates.add(nextToken); ++start; } return resultCandidates; } /** * 判斷when條件是否成立 * * @param operatorCandidates 可供運算的表達式token列表 * @param suppliers 原始字段取值來源 * @return true:符合該斷定,false:斷定失敗 */ private boolean judgeWhenExpression(List<TokenDescriptor> operatorCandidates, Map<String, String> suppliers) { List<AndOrOperatorSupervisor> supervisors = partitionByPriority(operatorCandidates); boolean prevJudgeSuccess = false; for (AndOrOperatorSupervisor calc1 : supervisors) { Map<String, List<TokenDescriptor>> unitGroup = calc1.getUnitGroupTokens(); String leftValue = calcExpressionData(unitGroup.get("LEFT"), suppliers); String op = unitGroup.get("OP").get(0).getRawWord(); TokenTypeEnum resultType = getPriorDataTypeByTokenList(unitGroup.get("RIGHT")); boolean myJudgeSuccess; if("in".equals(op)) { myJudgeSuccess = checkExistsIn(leftValue, unitGroup.get("RIGHT"), resultType); } else if("notin".equals(op)) { myJudgeSuccess = !checkExistsIn(leftValue, unitGroup.get("RIGHT"), resultType); } else { String rightValue = calcExpressionData(unitGroup.get("RIGHT"), suppliers); myJudgeSuccess = checkCompareTrue(leftValue, op, rightValue, resultType); } TokenDescriptor prevType = calc1.getPrevType(); TokenDescriptor nextType = calc1.getNextType(); // 單條件斷定 if(prevType == null && nextType == null) { return myJudgeSuccess; } if(nextType == null) { return myJudgeSuccess; } prevJudgeSuccess = myJudgeSuccess; if("and".equalsIgnoreCase(nextType.getRawWord())) { if(!myJudgeSuccess) { return false; } continue; } if("or".equalsIgnoreCase(nextType.getRawWord())) { if(myJudgeSuccess) { return true; } continue; } log.warn("解析到未知的next鏈接斷定符:{}", nextType); throw new SyntaxException("語法解析錯誤"); } log.warn("未斷定出結果,使用默認返回,請檢查"); return false; } /** * 根據值信息推斷運算數據類型 * * @param tokenList 結果列表(待運算) * @return 計算的數據類型,數字或字符串 */ private TokenTypeEnum getPriorDataTypeByTokenList(List<TokenDescriptor> tokenList) { for (TokenDescriptor token : tokenList) { if(token.getTokenType() == TokenTypeEnum.WORD_STRING) { return TokenTypeEnum.WORD_STRING; } } return TokenTypeEnum.WORD_NUMBER; } /** * 運算返回具體的 斷定值 * * @param resultCandidates 結果表達式token列表 * @param suppliers 原始字段取值來源 * @return true:符合該斷定,false:斷定失敗 */ private String calcExpressionData(List<TokenDescriptor> resultCandidates, Map<String, String> suppliers) { // 暫時假設結果中再也不提供運算處理 TokenDescriptor first = resultCandidates.get(0); if(first.getTokenType() == TokenTypeEnum.WORD_NORMAL) { if("null".equalsIgnoreCase(first.getRawWord())) { return null; } return suppliers.get(first.getRawWord()); } return unwrapStringToken(first.getRawWord()); } /** * 判斷給定值是否在列表中 * * @param aValue 要斷定的值 * @param itemList 範圍表 * @return true:成立, false:不在其中 */ private boolean checkExistsIn(String aValue, List<TokenDescriptor> itemList, TokenTypeEnum valueType) { if(aValue == null) { return false; } BigDecimal aValueNumber = null; for (TokenDescriptor tk1 : itemList) { if(valueType == TokenTypeEnum.WORD_NUMBER) { if(aValueNumber == null) { aValueNumber = new BigDecimal(aValue); } if(aValueNumber.compareTo( new BigDecimal(tk1.getRawWord())) == 0) { return true; } continue; } if(aValue.equals(unwrapStringToken(tk1.getRawWord()))) { return true; } } return false; } /** * 將字符串兩邊的引號去除,保持字符串屬性 * * @param wrappedStr 含引號的字符串,如 'abc',"abc" * @return abc 無引號包裹的字符串 */ private String unwrapStringToken(String wrappedStr) { if(wrappedStr == null || wrappedStr.length() == 0) { return null; } char[] values = wrappedStr.toCharArray(); int i = 0; while (i < values.length - 1 && (values[i] == '"' || values[i] == '\'')) { i++; } int j = values.length - 1; while (j > 0 && (values[j] == '"' || values[j] == '\'')) { j--; } return new String(values, i, j - i + 1); } /** * 比較兩個值ab是否基於op成立 * * @param aValue 左值 * @param op 比較運算符 * @param bValue 右值 * @param valueType 值類型, 主要是區分數字與字符 * @return 是否等式成立, true:成立, false:不成立 */ private boolean checkCompareTrue(String aValue, String op, String bValue, TokenTypeEnum valueType) { // 首先進行相生性斷定 if("null".equals(bValue)) { bValue = null; } switch(op) { case "=": if(bValue == null) { return aValue == null; } return bValue.equals(aValue); case "!=": case "<>": if(bValue == null) { return aValue != null; } return !bValue.equals(aValue); } if(bValue == null) { log.warn("非null值不能用比較符號運算"); throw new SyntaxException("語法錯誤"); } // >=,<=,>,< 斷定 int compareResult = compareTwoData(aValue, bValue, valueType); switch(op) { case ">": return compareResult > 0; case ">=": return compareResult >= 0; case "<=": return compareResult <= 0; case "<": return compareResult < 0; } throw new SyntaxException("未知的運算符"); } // 比較兩個值大小ab private int compareTwoData(String aValue, String bValue, TokenTypeEnum tokenType) { bValue = unwrapStringToken(bValue); if(bValue == null) { // 按任意值大於null 規則處理 return aValue == null ? 0 : 1; } if(tokenType == TokenTypeEnum.WORD_NUMBER) { return new BigDecimal(aValue).compareTo( new BigDecimal(bValue)); } return aValue.compareTo(unwrapStringToken(bValue)); } // 將token從新分組,以即可以作原子運算 private List<AndOrOperatorSupervisor> partitionByPriority(List<TokenDescriptor> tokens) { // 1. 取左等式token列表 // 2. 取等式表達式 // 3. 取右等式token列表 // 4. 構建一個表達式,作最小分組 // 5. 檢查是否有下一運算符,若有則一定爲and|or|( // 6. 保存上一鏈接斷定符,新開一個分組 // 7. 重複步驟1-6,直到取完全部token // 前置運算符,決定是否要運算本節點,以及結果的合併方式 // 好比 and, 則當前點必須參與運算,若是前節點結果爲false,則直接返回false // 不然先計算本節點 TokenDescriptor preType = null; // 當前節點計算完成後,判斷下一運算是否有必要觸發 // 爲and時則當前爲true時必須觸發,爲or時當前爲false觸發 TokenDescriptor nextType = null; // key 爲 left, op, right, 各value爲細分tks Map<String, List<TokenDescriptor>> unitGroup = new HashMap<>(); String currentReadPos = "LEFT"; List<TokenDescriptor> smallGroupTokenList = new ArrayList<>(); // 以上爲描述單個運算的字符,使用一個list就能夠描述無括號的表達式了 List<AndOrOperatorSupervisor> bracketGroup = new ArrayList<>(); AndOrOperatorSupervisor supervisor = new AndOrOperatorSupervisor(null, unitGroup); bracketGroup.add(supervisor); for (int i = 0; i < tokens.size(); i++) { TokenDescriptor token = tokens.get(i); String word = token.getRawWord().toLowerCase(); TokenTypeEnum tokenType = token.getTokenType(); // 忽略分隔符,假設只有一級運算,忽略空格帶來的複雜優先級問題 if(tokenType == TokenTypeEnum.CLAUSE_SEPARATOR) { continue; } // 字段直接斷定 if(tokenType == TokenTypeEnum.COMPARE_OPERATOR && !",".equals(word)) { unitGroup.put("OP", Collections.singletonList(token)); currentReadPos = "RIGHT"; continue; } // is null, is not null 解析 if("is".equals(word)) { while (i + 1 < tokens.size()) { TokenDescriptor nextToken = tokens.get(i + 1); if("null".equalsIgnoreCase(nextToken.getRawWord())) { TokenDescriptor opToken = new TokenDescriptor("=", TokenTypeEnum.COMPARE_OPERATOR); unitGroup.put("OP", Collections.singletonList(opToken)); currentReadPos = "RIGHT"; List<TokenDescriptor> curTokenList = unitGroup.computeIfAbsent( currentReadPos, r -> new ArrayList<>()); curTokenList.add(nextToken); // 跳過1個token i += 1; break; } if("not".equalsIgnoreCase(nextToken.getRawWord())) { if(i + 2 >= tokens.size()) { throw new SyntaxException("語法錯誤3: is"); } nextToken = tokens.get(i + 2); if(!"null".equalsIgnoreCase(nextToken.getRawWord())) { throw new SyntaxException("語法錯誤4: is"); } TokenDescriptor opToken = new TokenDescriptor("!=", TokenTypeEnum.COMPARE_OPERATOR); unitGroup.put("OP", Collections.singletonList(opToken)); currentReadPos = "RIGHT"; List<TokenDescriptor> curTokenList = unitGroup.computeIfAbsent( currentReadPos, r -> new ArrayList<>()); curTokenList.add(nextToken); // 跳過2個token i += 2; break; } } continue; } // in (x,x,xx) 語法解析 if("in".equals(word)) { TokenDescriptor opToken = new TokenDescriptor("in", TokenTypeEnum.COMPARE_OPERATOR); unitGroup.put("OP", Collections.singletonList(opToken)); currentReadPos = "RIGHT"; List<TokenDescriptor> curTokenList = unitGroup.computeIfAbsent( currentReadPos, r -> new ArrayList<>()); i = parseInItems(tokens, curTokenList, i); continue; } // not in (x,xxx,xx) 語法解析 if("not".equals(word)) { if(i + 1 > tokens.size()) { throw new SyntaxException("語法錯誤:not"); } TokenDescriptor nextToken = tokens.get(i + 1); // 暫不支持 not exists 等語法 if(!"in".equalsIgnoreCase(nextToken.getRawWord())) { throw new SyntaxException("不支持的語法:not"); } TokenDescriptor opToken = new TokenDescriptor("notin", TokenTypeEnum.COMPARE_OPERATOR); unitGroup.put("OP", Collections.singletonList(opToken)); currentReadPos = "RIGHT"; List<TokenDescriptor> curTokenList = unitGroup.computeIfAbsent( currentReadPos, r -> new ArrayList<>()); i = parseInItems(tokens, curTokenList, i + 1); continue; } // 暫只解析一級,無括號狀況 if("and".equals(word) || "or".equals(word)) { supervisor.setNextType(token); // 滾動到下一運算分支 unitGroup = new HashMap<>(); supervisor = new AndOrOperatorSupervisor(token, unitGroup); bracketGroup.add(supervisor); currentReadPos = "LEFT"; continue; } List<TokenDescriptor> curTokenList = unitGroup.computeIfAbsent( currentReadPos, r -> new ArrayList<>()); curTokenList.add(token); } return bracketGroup; } /** * 解析in中的全部元素到結果中 * * @param tokens 全部token * @param curTokenList 當前結果表 * @param start in 開始的地方 * @return in 語法結束位置 */ private int parseInItems(List<TokenDescriptor> tokens, List<TokenDescriptor> curTokenList, int start) { while (start + 1 < tokens.size()) { TokenDescriptor nextToken = tokens.get(++start); String nextWord = nextToken.getRawWord(); if("(".equals(nextWord) || ",".equals(nextWord)) { // in 開始 continue; } if(")".equals(nextWord)) { break; } curTokenList.add(nextToken); } return start; } /** * 最小運算單元描述符 */ private class AndOrOperatorSupervisor { // 前置運算符,決定是否要運算本節點,以及結果的合併方式 // 好比 and, 則當前點必須參與運算,若是前節點結果爲false,則直接返回false // 不然先計算本節點 TokenDescriptor prevType; // 當前節點計算完成後,判斷下一運算是否有必要觸發 // 爲and時則當前爲true時必須觸發,爲or時當前爲false觸發 TokenDescriptor nextType; // key 爲 left, op, right, 各value爲細分tks Map<String, List<TokenDescriptor>> unitGroupTokens; public AndOrOperatorSupervisor(TokenDescriptor prevType, Map<String, List<TokenDescriptor>> unitGroupTokens) { this.prevType = prevType; this.unitGroupTokens = unitGroupTokens; } public void setNextType(TokenDescriptor nextType) { this.nextType = nextType; } public TokenDescriptor getPrevType() { return prevType; } public TokenDescriptor getNextType() { return nextType; } public Map<String, List<TokenDescriptor>> getUnitGroupTokens() { return unitGroupTokens; } @Override public String toString() { return StringUtils.join( unitGroupTokens.get("LEFT").stream() .map(TokenDescriptor::getRawWord) .collect(Collectors.toList()), ' ') + unitGroupTokens.get("OP").get(0).getRawWord() + StringUtils.join( unitGroupTokens.get("RIGHT").stream() .map(TokenDescriptor::getRawWord) .collect(Collectors.toList()), ' ') + ", prev=" + prevType + ", next=" + nextType ; } } }
每使用時,傳入case..when..的語句構造出一個新的計算實例,而後調用 calcCaseWhenData(rawData), 帶入已知參數信息,便可運算出最終的case..when..值。
爲使處理簡單起見,這裏並無深刻各類邏輯嵌套處理,直接忽略掉括號的處理了。另外,對於數值類的運算也暫時被忽略,如 field1 > 1+1 這種運算,並不會計算出2來。這些東西,須要的同窗,徹底能夠稍加完善,便可支持處理這些邏輯。
因 case when 的語法仍是比較清晰的,因此咱們只是作了順序地讀取,斷定即得出結果。另外對於 case when 的單值斷定並不支持,因此實現並不複雜。但這徹底不影響咱們理解整個語法處理的思想。相信須要的同窗定能有所啓發。
以上僅實現代碼,須要附加上各類場景測試,纔算能夠work的東西。主要就是針對種 and/or, in, is null 等的處理。以下:
import com.my.mvc.app.common.helper.CaseWhenElDataCalcHelper; import com.my.mvc.app.common.helper.SimpleSyntaxParser; import com.my.mvc.app.common.helper.parser.ParsedClauseAst; import lombok.extern.slf4j.Slf4j; import org.junit.Assert; import org.junit.Test; import java.util.HashMap; import java.util.Map; @Slf4j public class CaseWhenElDataCalcHelperTest { @Test public void testCaseWhenSimple1() { String condition; ParsedClauseAst parsedClause; CaseWhenElDataCalcHelper helper; Map<String, String> rawData; condition = "case \n" + "\twhen (kehu_phone is null or field1 != 'c') then m_phone \n" + "\telse kehu_phone\n" + "end"; parsedClause = SimpleSyntaxParser.parse(condition); helper = new CaseWhenElDataCalcHelper(parsedClause.getAst().get(0)); rawData = new HashMap<>(); rawData.put("kehu_phone", "kehu_phone_v1"); rawData.put("field1", "field1_v"); rawData.put("m_phone", "m_phone_v"); Assert.assertEquals("case..when..中解析字段信息不正確", 3, parsedClause.getIdMapping().size()); Assert.assertEquals("case..when..解析結果錯誤", rawData.get("m_phone"), helper.calcCaseWhenData(rawData)); condition = "case \n" + "\twhen (kehu_phone is null) then m_phone \n" + "\telse kehu_phone\n" + "end"; parsedClause = SimpleSyntaxParser.parse(condition); helper = new CaseWhenElDataCalcHelper(parsedClause.getAst().get(0)); rawData = new HashMap<>(); rawData.put("kehu_phone", "kehu_phone_v1"); rawData.put("field1", "field1_v"); rawData.put("m_phone", "m_phone_v"); Assert.assertEquals("case..when..中解析字段信息不正確", 2, parsedClause.getIdMapping().size()); Assert.assertEquals("case..when..解析結果錯誤", rawData.get("kehu_phone"), helper.calcCaseWhenData(rawData)); rawData.remove("kehu_phone"); Assert.assertEquals("case..when..解析結果錯誤", rawData.get("m_phone"), helper.calcCaseWhenData(rawData)); condition = " case \n" + " \twhen is_sx_emp='Y' then 'Y1' \n" + " \twhen is_sx_new_custom!='Y' then 'Y2' \n" + " \twhen is_sx_fort_promot_custom='Y' then 'Y3' \n" + " \twhen promotion_role_chn in ('10','11') and first_tenthousand_dt is not null then 'Y4' \n" + " \telse 'N' \n" + " end"; parsedClause = SimpleSyntaxParser.parse(condition); helper = new CaseWhenElDataCalcHelper(parsedClause.getAst().get(0)); rawData = new HashMap<>(); rawData.put("is_sx_emp", "N"); rawData.put("is_sx_new_custom", "Y"); rawData.put("is_sx_fortune_promot_custom", "N"); rawData.put("promotion_role_chn", "10"); rawData.put("first_tenthousand_dt", "10"); Assert.assertEquals("case..when..中解析字段信息不正確", 5, parsedClause.getIdMapping().size()); Assert.assertEquals("case..when..in解析結果錯誤", "Y4", helper.calcCaseWhenData(rawData)); rawData = new HashMap<>(); rawData.put("is_sx_emp", "N"); rawData.put("is_sx_new_custom", "Y"); rawData.put("is_sx_fortune_promot_custom", "N"); rawData.put("first_tenthousand_dt", "10"); rawData.put("promotion_role_chn", "9"); Assert.assertEquals("case..when..else解析結果錯誤", "N", helper.calcCaseWhenData(rawData)); rawData = new HashMap<>(); rawData.put("is_sx_new_custom", "Y"); rawData.put("is_sx_fortune_promot_custom", "N"); rawData.put("first_tenthousand_dt", "10"); rawData.put("promotion_role_chn", "9"); rawData.put("is_sx_emp", "Y"); Assert.assertEquals("case..when..=解析結果錯誤", "Y1", helper.calcCaseWhenData(rawData)); rawData = new HashMap<>(); rawData.put("is_sx_emp", "N"); rawData.put("is_sx_new_custom", "N"); rawData.put("is_sx_fortune_promot_custom", "N"); rawData.put("first_tenthousand_dt", "10"); rawData.put("promotion_role_chn", "9"); Assert.assertEquals("case..when..!=解析結果錯誤", "Y2", helper.calcCaseWhenData(rawData)); rawData = new HashMap<>(); rawData.put("is_sx_emp", "N"); rawData.put("is_sx_new_custom", "Y"); rawData.put("is_sx_fortune_promot_custom", "N"); // rawData.put("first_tenthousand_dt", "10"); rawData.put("promotion_role_chn", "9"); Assert.assertEquals("case..when..in+and+null解析結果錯誤", "N", helper.calcCaseWhenData(rawData)); condition = " case \n" + " \twhen is_sx_emp='Y' then 'Y1' \n" + " \twhen or_emp != null or or_emp2 > 3 then 'Y2_OR' \n" + " \twhen and_emp != null and and_emp2 > 3 or or_tmp3 <= 10 then 'Y3_OR' \n" + " \twhen promotion_role_chn not in ('10','11') and first_tenthousand_dt is not null then 'Y4' \n" + " \twhen promotion_role_chn not in ('10') then 'Y5_NOTIN' \n" + " \telse 'N_ELSE' \n" + " end"; parsedClause = SimpleSyntaxParser.parse(condition); helper = new CaseWhenElDataCalcHelper(parsedClause.getAst().get(0)); rawData = new HashMap<>(); rawData.put("is_sx_emp", "N"); rawData.put("or_emp", "Y"); Assert.assertEquals("case..when..中解析字段信息不正確", 8, parsedClause.getIdMapping().size()); Assert.assertEquals("case..when..in解析結果錯誤", "Y2_OR", helper.calcCaseWhenData(rawData)); rawData = new HashMap<>(); rawData.put("is_sx_emp", "N"); // rawData.put("or_emp", "Y"); rawData.put("or_emp2", "2"); Assert.assertEquals("case..when..or>2解析結果錯誤", "Y5_NOTIN", helper.calcCaseWhenData(rawData)); rawData = new HashMap<>(); rawData.put("is_sx_emp", "N"); // rawData.put("or_emp", "Y"); rawData.put("or_emp2", "2"); rawData.put("promotion_role_chn", "10"); Assert.assertEquals("case..when..notin解析結果錯誤", "N_ELSE", helper.calcCaseWhenData(rawData)); condition = " case \n" + " \twhen (is_sx_emp='Y' or a_field=3) then 'Y1' \n" + " \telse 'N_ELSE' \n" + " end"; parsedClause = SimpleSyntaxParser.parse(condition); helper = new CaseWhenElDataCalcHelper(parsedClause.getAst().get(0)); rawData = new HashMap<>(); rawData.put("is_sx_emp", "N"); rawData.put("or_emp", "Y"); Assert.assertEquals("case..when..中解析字段信息不正確", 2, parsedClause.getIdMapping().size()); Assert.assertEquals("case..when..()號解析結果錯誤", "N_ELSE", helper.calcCaseWhenData(rawData)); rawData = new HashMap<>(); rawData.put("is_sx_emp", "Y"); rawData.put("or_emp", "Y"); Assert.assertEquals("case..when..中解析字段信息不正確", 2, parsedClause.getIdMapping().size()); Assert.assertEquals("case..when..()號解析結果錯誤2", "Y1", helper.calcCaseWhenData(rawData)); } }
若是有更多場景,咱們只需添加測試,而後完善相應邏輯便可。這裏全部的測試,均可以基於sql協議進行,若有空缺則應彌補相應功能,而非要求用戶按本身的標準來,畢竟標準是個好東西。
實際上,對錶達式計算這東西,咱們也許不必定非要本身去實現。畢竟太費力。有開源產品支持的,好比:aviator: https://www.oschina.net/p/aviator?hmsr=aladdin1e1 https://www.jianshu.com/p/02403dd1f4c4
若是該語法不支持,則能夠先轉換成支持的語法,再使用其引擎計算便可。
本質上,咱們都是在作翻譯工做!