org.nlpcn.es4sql.parse.CaseWhenParser Maven / Gradle / Ivy
package org.nlpcn.es4sql.parse;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.expr.SQLCaseExpr;
import com.alibaba.druid.sql.ast.expr.SQLNullExpr;
import com.google.common.base.Joiner;
import org.nlpcn.es4sql.Util;
import org.nlpcn.es4sql.domain.Condition;
import org.nlpcn.es4sql.domain.Condition.OPEAR;
import org.nlpcn.es4sql.domain.Where;
import org.nlpcn.es4sql.exception.SqlParseException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/**
* Created by allwefantasy on 9/3/16.
*/
public class CaseWhenParser {
private SQLCaseExpr caseExpr;
//zhongshu-comment 以下这两个属性貌似没有被使用
private String alias;
private String tableAlias;
public CaseWhenParser(SQLCaseExpr caseExpr, String alias, String tableAlias) {
this.alias = alias;
this.tableAlias = tableAlias;
this.caseExpr = caseExpr;
}
public String parse() throws SqlParseException {
List result = new ArrayList();
for (SQLCaseExpr.Item item : caseExpr.getItems()) {
SQLExpr conditionExpr = item.getConditionExpr();
WhereParser parser = new WhereParser(new SqlParser(), conditionExpr);
/*
zhongshu-comment 将case when的各种条件判断转换为script的if-else判断,举例如下
case when:
CASE
WHEN platform_id = 'PC' AND os NOT IN ('全部') THEN 'unknown'
ELSE os
script的if-else:
将上文case when例子中的WHEN platform_id = 'PC' AND os NOT IN ('全部') THEN 'unknown' 解析成如下的script:
(doc['platform_id'].value=='PC') && (doc['os'].value != '全部' )
*/
String scriptCode = explain(parser.findWhere());
if (scriptCode.startsWith(" &&")) {
scriptCode = scriptCode.substring(3);
}
if (result.size() == 0) {
result.add("if(" + scriptCode + ")" + "{" + Util.getScriptValueWithQuote(item.getValueExpr(), "'") + "}");
} else {
result.add("else if(" + scriptCode + ")" + "{" + Util.getScriptValueWithQuote(item.getValueExpr(), "'") + "}");
}
}
SQLExpr elseExpr = caseExpr.getElseExpr();
if (elseExpr == null) {
result.add("else { null }");
} else {
result.add("else {" + Util.getScriptValueWithQuote(elseExpr, "'") + "}");
}
return Joiner.on(" ").join(result);
}
/**
* zhongshu-comment
* 1、该方法的作用:将在where子句中的case when解析为es script
* 2、该种情况的es script和select、group by、order by等子句中的case when的es script不一样,
* 因为在where子句中script的返回值是布尔类型,所以script中需要有个布尔判断,
* 而其他情况的script返回值就是转换后的值,该值一般是字符串、数值
* @author zhongshu
* @return
* @throws SqlParseException
*/
public String parseCaseWhenInWhere(Object[] valueArr) throws SqlParseException {
List result = new ArrayList();
String TMP = "tmp";
result.add("String " + TMP + " = '';");
for (SQLCaseExpr.Item item : caseExpr.getItems()) {
SQLExpr conditionExpr = item.getConditionExpr();
WhereParser parser = new WhereParser(new SqlParser(), conditionExpr);
String scriptCode = explain(parser.findWhere());
if (scriptCode.startsWith(" &&")) {
scriptCode = scriptCode.substring(3);
}
if (result.size() == 1) { //zhongshu-comment 在for循环之前就已经先add了一个元素
result.add("if(" + scriptCode + ")" + "{" + TMP + "=" + Util.getScriptValueWithQuote(item.getValueExpr(), "'") + "}");
} else {
result.add("else if(" + scriptCode + ")" + "{" + TMP + "=" + Util.getScriptValueWithQuote(item.getValueExpr(), "'") + "}");
}
}
SQLExpr elseExpr = caseExpr.getElseExpr();
if (elseExpr == null) {
result.add("else { null }");
} else {
result.add("else {" + TMP + "=" + Util.getScriptValueWithQuote(elseExpr, "'") + "}");
}
/*
zhongshu-comment
1、第一种情况in
field in (a, b, c) --> field == a || field == b || field == c
2、第二种情况not in
field not in (a, b, c) --> field != a && field != b && field != c
等价于 --> !(field == a || field == b || field == c) 即对第一种情况取反,
(field == a || field == b || field == c)里的a、b、c要全部为false,!(field == a || field == b || field == c)才为true
3、这里只拼接第一种情况,不拼接第一种情况,
如果要需要第二种情况,那就调用该方法得到返回值后自行拼上取反符号和括号: !(${该方法的返回值})
*/
String judgeStatement = parseInNotInJudge(valueArr, TMP, "==", "||", true);
result.add("return " + judgeStatement + ";");
return Joiner.on(" ").join(result);
}
/**
* zhongshu-comment 这个方法应该设为private比较合适,因为只在上文的parse()方法中被调用了
* zhongshu-comment 将case when的各种条件判断转换为script的if-else判断,举例如下
case when:
CASE
WHEN platform_id = 'PC' AND os NOT IN ('全部') THEN 'unknown'
ELSE os
script的if-else:
将上文case when例子中的WHEN platform_id = 'PC' AND os NOT IN ('全部') THEN 'unknown' 解析成如下的script:
(doc['platform_id'].value=='PC') && (doc['os'].value != '全部' )
* @param where
* @return
* @throws SqlParseException
*/
public String explain(Where where) throws SqlParseException {
List codes = new ArrayList();
while (where.getWheres().size() == 1) {
where = where.getWheres().getFirst();
}
explainWhere(codes, where);
String relation = where.getConn().name().equals("AND") ? " && " : " || ";
return Joiner.on(relation).join(codes);
}
private void explainWhere(List codes, Where where) throws SqlParseException {
if (where instanceof Condition) {
Condition condition = (Condition) where;
if (condition.getValue() instanceof ScriptFilter) {
codes.add(String.format("Function.identity().compose((o)->{%s}).apply(null)", ((ScriptFilter) condition.getValue()).getScript()));
} else if (condition.getOpear() == OPEAR.BETWEEN) {
Object[] objs = (Object[]) condition.getValue();
codes.add("(" + "doc['" + condition.getName() + "'].value >= " + objs[0] + " && doc['"
+ condition.getName() + "'].value <=" + objs[1] + ")");
} else if (condition.getOpear() == OPEAR.IN) {// in
//zhongshu-comment 增加该分支,可以解析case when判断语句中的in、not in判断语句
codes.add(parseInNotInJudge(condition, "==", "||", false));
} else if (condition.getOpear() == OPEAR.NIN) { // not in
codes.add(parseInNotInJudge(condition, "!=", "&&", false));
} else {
SQLExpr nameExpr = condition.getNameExpr();
SQLExpr valueExpr = condition.getValueExpr();
if(valueExpr instanceof SQLNullExpr) {
//zhongshu-comment 空值查询的意思吗?例如:查a字段没有值的那些文档,是这个意思吗
codes.add("(" + "doc['" + nameExpr.toString() + "']" + ".empty)");
} else {
//zhongshu-comment 该分支示例:(doc['c'].value==1)
codes.add("(" + Util.getScriptValueWithQuote(nameExpr, "'") + condition.getOpertatorSymbol() + Util.getScriptValueWithQuote(valueExpr, "'") + ")");
}
}
} else {
for (Where subWhere : where.getWheres()) {
List subCodes = new ArrayList();
explainWhere(subCodes, subWhere);
String relation = subWhere.getConn().name().equals("AND") ? "&&" : "||";
codes.add(Joiner.on(relation).join(subCodes));
}
}
}
/**
* @author zhongshu
* @param condition
* @param judgeOperator
* @param booleanOperator
* @throws SqlParseException
*/
private String parseInNotInJudge(Condition condition, String judgeOperator, String booleanOperator, boolean flag) throws SqlParseException {
Object[] objArr = (Object[]) condition.getValue();
if (objArr.length == 0)
throw new SqlParseException("you should assign some value in bracket!!");
String script = "(";
String template = "doc['" + condition.getName() + "'].value " + judgeOperator + " %s " + booleanOperator + " "; //结尾这个空格就只空一格
if (flag) {
template = condition.getName() + " " + judgeOperator + " %s " + booleanOperator + " "; //结尾这个空格就只空一格;
}
for (Object obj : objArr) {
script = script + String.format(template, parseInNotInValueWithQuote(obj));
}
script = script.substring(0, script.lastIndexOf(booleanOperator));//去掉末尾的&&
script += ")"; //zhongshu-comment script结果示例 (doc['a'].value == 1 && doc['a'].value == 2 && doc['a'].value == 3 )
return script;
}
private String parseInNotInJudge(Object value, String fieldName, String judgeOperator, String booleanOperator, boolean flag) throws SqlParseException {
Condition cond = new Condition(null);
cond.setValue(value);
cond.setName(fieldName);
return parseInNotInJudge(cond, judgeOperator, booleanOperator, flag);
}
/**
* @author zhongshu
* @param obj
* @return
*/
private Object parseInNotInValueWithQuote(Object obj) {
//zhongshu-comment 因为我们的表就只有String 和 double两种类型,所以就只判断了这两种情况
if (obj instanceof String) {
return "'" + obj + "'";
} else {
return obj;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy