diff --git a/README.md b/README.md index 0ec8d4ee..90d520ca 100644 --- a/README.md +++ b/README.md @@ -153,5 +153,3 @@ http://localhost:9200/_sql/_explain?sql=select * from indexName limit 10 * ES GEO_DISTANCE * ES GEOHASH_GRID aggregation - - diff --git a/es-sql.md b/es-sql.md new file mode 100644 index 00000000..2af83aa9 --- /dev/null +++ b/es-sql.md @@ -0,0 +1,176 @@ +# es-sql + +## 编译和安装 + +### 编译 + + mvn clean package assembly:single -DskipTests + +### 安装方法: + + unzip elasticsearch-sql-2.3.1.1.zip + mv elasticsearch-sql-2.3.1.1 sql + rm elasticsearch-sql-2.3.1.1.zip + +或者 + + ./bin/plugin -u file:///home/omershelef/IdeaProjects/elasticsearch-sql/target/elasticsearch-sql-1.3.2.zip --install sql + +### es启动方法: + + ../bin/elasticsearch -Des.security.manager.enabled=false + + +## 特性 + +### SQL 特性 + + * SQL Select + * SQL Where + * SQL Order By + * SQL Group By + * SQL AND & OR + * SQL Like + * SQL COUNT distinct + * SQL In + * SQL Between + * SQL Aliases + * SQL Not Null + * SQL(ES) Date + * SQL avg() + * SQL count() + * SQL last() + * SQL max() + * SQL min() + * SQL sum() + * SQL Nulls + * SQL isnull() + * SQL now() + + +## 增强SQL特性 + + * ES nested + * ES seg + * ES TopHits + * ES MISSING + * ES STATS + * ES GEO_INTERSECTS + * ES GEO_BOUNDING_BOX + * ES GEO_DISTANCE + * ES GEOHASH_GRID aggregation + + +## 接口说明: + +### 解析sql 2 sql 接口: + + http://192.168.25.11:9688/_sql/_seg?sql=select * from test where province="河北省" + + +### 解析sql 2 es 接口: + + http://localhost:9200/_sql/_explain?sql=select * from indexName limit 10 + +### 执行sql地址: + + http://localhost:9200/_sql?sql=select * from indexName limit 10 + +### UI + + http://192.168.25.11:9688/_plugin/sql/ + +## 例子: + +### 不使用函数 + +当不使用任何的函数时,默认是使用的match查询,match则会进行分析器处理,分析器中的分词器会将搜索关键字分割成单独的词(terms)或者标记(tokens) 。 +该match的type是phrase,phrase表示确切的匹配若干个单词或短语, 如title: “brown dog”, 则查询title中包含brown和dog, 且两个是连接在一起的 + +例子: + + SELECT * FROM test_csdn_user_profile_12_201512_v4 where title = "brown dog" + +### matchQuery() + +matchQuery()使用的type是boolean。 +函数中可指定两个参数,不可指定operator和minimum_should_match,除了可以指定分词器外,和不使用函数没有区别: + +* 第一个参数是:查询词 +* 第二个参数是:analyzer,有三个值可供选择:query_ansj、dic_ansj、index_ansj + +例子: + + select * from user_metric where province = matchQuery("河北") + + +### term() + +term查找时内容精确匹配,只有一个参数,即需要查询的词 + +例子: + + select * from user_metric where province = term("河北") + +### IN_TERMS() + +在函数中可以指定多个词,进行查询 + +例子: + + SELECT * FROM test_csdn_user_profile_12_201512_v4 where province = IN_TERMS("河北省","北京市") + + +### 根据Id查询 IDS_QUERY() + +可指定多个id进行查询,函数中第一个参数是type,后边是需要指定的id,对于type加不加双引号均可 + +例子: + + select * from %s/dog where _id = IDS_QUERY(dog,1,2,3) + SELECT * FROM test_csdn_user_profile_12_201512_v4 where _id = IDS_QUERY("user","azjw1989","jslp1990") + +### 模糊匹配 like + +* *和%代表任意个字符(包括空字符) +* ?问号是单个字符 +请注意,此查询可能很慢,因为它需要迭代许多项。 为了防止极慢的通配符查询,通配符术语不应以通配符*或?开头。 + +例子: + + SELECT * FROM user_metric WHERE province LIKE "邯郸%%" + +### in + +使用match进行多个值查询,各个值间是或的关系 + +例子: + + SELECT * FROM user_profile_12_201512_v4 where province in ("河北省","北京市") + +### 嵌套类型 nested() + +nested方法可在where和order by中使用。 +在where中有两个参数: +* 第一个参数是:父field +* 第二个参数是:子filed的表达式 + +在order by中有三个参数: +* 第一个参数是:父field +* 第二个参数是:指定排序字段,以及排序函数,可使用sum、min、max、avg四个函数 +* 第三个参数是:子filed的表达式 + +例子: + + SELECT * FROM elasticsearch-sql_test_index where nested(message,message.info=term("c")) and nested(message,message.info=term("a")) order by nested(message, sum(message.dayOfWeek),message.info=term("a") and message.info=term("c")) desc + +### object对象 + +非嵌套的object,直接使用 父field.子field 即可查询 + +例子: + SELECT * FROM elasticsearch-sql_test_index where message.info=term("c") + + +http://192.168.25.11:9688/_sql/_seg?sql=select%20*%20from%20awhere nested(a,(a.b="x" or a.b=seg"y")and a.b=seg"z") + diff --git a/pom.xml b/pom.xml index 2c3f4ddd..fb020fe4 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.nlpcn elasticsearch-sql - 2.3.1.1 + 2.3.1.2 jar Query elasticsearch using SQL elasticsearch-sql diff --git a/src/main/java/org/elasticsearch/plugin/nlpcn/RestSqlAction.java b/src/main/java/org/elasticsearch/plugin/nlpcn/RestSqlAction.java index e5d5fe28..9ec0fb38 100644 --- a/src/main/java/org/elasticsearch/plugin/nlpcn/RestSqlAction.java +++ b/src/main/java/org/elasticsearch/plugin/nlpcn/RestSqlAction.java @@ -5,10 +5,11 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.plugin.nlpcn.executors.ActionRequestRestExecuterFactory; import org.elasticsearch.plugin.nlpcn.executors.RestExecutor; +import org.elasticsearch.plugin.nlpcn.preAnalyzer.AnsjAnalyzerImpl; +import org.elasticsearch.plugin.nlpcn.preAnalyzer.SqlParseAnalyzer; import org.elasticsearch.rest.*; import org.nlpcn.es4sql.SearchDao; import org.nlpcn.es4sql.query.QueryAction; -import org.nlpcn.es4sql.query.SqlElasticRequestBuilder; import java.util.Map; @@ -22,6 +23,7 @@ public RestSqlAction(Settings settings, Client client, RestController restContro restController.registerHandler(RestRequest.Method.GET, "/_sql/_explain", this); restController.registerHandler(RestRequest.Method.POST, "/_sql", this); restController.registerHandler(RestRequest.Method.GET, "/_sql", this); + restController.registerHandler(RestRequest.Method.GET, "/_sql/_seg", this); } @Override @@ -31,11 +33,17 @@ protected void handleRequest(RestRequest request, RestChannel channel, final Cli if (sql == null) { sql = request.content().toUtf8(); } + //ananlyze + SqlParseAnalyzer sqlParseAnalyzer = new SqlParseAnalyzer(new AnsjAnalyzerImpl()); + sql = sqlParseAnalyzer.seg(sql); SearchDao searchDao = new SearchDao(client); QueryAction queryAction= searchDao.explain(sql); // TODO add unittests to explain. (rest level?) - if (request.path().endsWith("/_explain")) { + if(request.path().endsWith("_seg")){ + BytesRestResponse bytesRestResponse = new BytesRestResponse(RestStatus.OK, sql); + channel.sendResponse(bytesRestResponse); + } else if (request.path().endsWith("/_explain")) { String jsonExplanation = queryAction.explain().explain(); BytesRestResponse bytesRestResponse = new BytesRestResponse(RestStatus.OK, jsonExplanation); channel.sendResponse(bytesRestResponse); @@ -45,4 +53,4 @@ protected void handleRequest(RestRequest request, RestChannel channel, final Cli restExecutor.execute(client,params,queryAction,channel); } } -} \ No newline at end of file +} diff --git a/src/main/java/org/elasticsearch/plugin/nlpcn/SqlPlug.java b/src/main/java/org/elasticsearch/plugin/nlpcn/SqlPlug.java index ad1fb096..a27350d0 100644 --- a/src/main/java/org/elasticsearch/plugin/nlpcn/SqlPlug.java +++ b/src/main/java/org/elasticsearch/plugin/nlpcn/SqlPlug.java @@ -1,9 +1,15 @@ package org.elasticsearch.plugin.nlpcn; +import org.elasticsearch.common.inject.AbstractModule; +import org.elasticsearch.common.inject.Module; +import org.elasticsearch.plugin.nlpcn.preAnalyzer.AnsjAnalyzerAction; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.rest.RestModule; +import java.util.Collection; +import java.util.Collections; + public class SqlPlug extends Plugin { public SqlPlug() { @@ -18,6 +24,16 @@ public String name() { public String description() { return "Use sql to query elasticsearch."; } + @Override + public Collection nodeModules() { + return Collections. singletonList(new AnsjModule()); + } + public static class AnsjModule extends AbstractModule { + @Override + protected void configure() { + bind(AnsjAnalyzerAction.class).asEagerSingleton(); + } + } public void onModule(RestModule module) { diff --git a/src/main/java/org/elasticsearch/plugin/nlpcn/preAnalyzer/Analyzer.java b/src/main/java/org/elasticsearch/plugin/nlpcn/preAnalyzer/Analyzer.java new file mode 100644 index 00000000..43f2da9a --- /dev/null +++ b/src/main/java/org/elasticsearch/plugin/nlpcn/preAnalyzer/Analyzer.java @@ -0,0 +1,8 @@ +package org.elasticsearch.plugin.nlpcn.preAnalyzer; + +/** + * Created by linxueqing on 2016/12/13. + */ +public interface Analyzer { + public String[] analyzer(String term) throws Exception; +} diff --git a/src/main/java/org/elasticsearch/plugin/nlpcn/preAnalyzer/AnsjAnalyzerAction.java b/src/main/java/org/elasticsearch/plugin/nlpcn/preAnalyzer/AnsjAnalyzerAction.java new file mode 100644 index 00000000..0338c203 --- /dev/null +++ b/src/main/java/org/elasticsearch/plugin/nlpcn/preAnalyzer/AnsjAnalyzerAction.java @@ -0,0 +1,16 @@ +package org.elasticsearch.plugin.nlpcn.preAnalyzer; + +import org.elasticsearch.common.component.AbstractComponent; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.settings.Settings; + +/** + * Created by fangbb on 2016-12-6. + */ +public class AnsjAnalyzerAction extends AbstractComponent { + @Inject + public AnsjAnalyzerAction(final Settings settings){ + super(settings); + AnsjElasticConfigurator.init(settings); + } +} diff --git a/src/main/java/org/elasticsearch/plugin/nlpcn/preAnalyzer/AnsjAnalyzerImpl.java b/src/main/java/org/elasticsearch/plugin/nlpcn/preAnalyzer/AnsjAnalyzerImpl.java new file mode 100644 index 00000000..289e3793 --- /dev/null +++ b/src/main/java/org/elasticsearch/plugin/nlpcn/preAnalyzer/AnsjAnalyzerImpl.java @@ -0,0 +1,61 @@ +package org.elasticsearch.plugin.nlpcn.preAnalyzer; + +import org.elasticsearch.plugin.nlpcn.request.HttpRequester; +import org.elasticsearch.plugin.nlpcn.request.HttpResponse; + +import java.net.URLEncoder; + +/** + * Created by fangbb on 2016-12-12. + */ +public class AnsjAnalyzerImpl implements Analyzer { + + public String[] analyzer(String term) throws Exception { + //TODO done + HttpRequester request = new HttpRequester(); + HttpResponse response = null; + String sourceTerms = ""; +// http://192.168.25.11:9688/_cat/analyze?text=大数据&analyzer=query_ansj + try { + //String ip = InetAddress.getLocalHost().getHostAddress(); + String ip = AnsjElasticConfigurator.ES_IP; + String port = AnsjElasticConfigurator.ES_PORT; + String midUrl = "/_cat/analyze?analyzer=query_ansj&text="; + String preUrl = "http://" + ip + ":" + port + midUrl; + //String preUrl = "http://192.168.25.11:9688/_cat/analyze?analyzer=query_ansj&text="; + //System.out.println(preUrl); + String enTerm = URLEncoder.encode(term, "UTF-8"); + String url = preUrl + enTerm; + //System.out.println(url); + response = request.sendGet(url); + if (response.getCode() == 200) { + if (response != null && response.getContent().length() > 10) { + sourceTerms = response.getContent(); + } + } + + } catch (Exception e) { + throw new Exception("There is an error in the word segmentation"); + } + return getTerms(sourceTerms).split(","); + } + + private static String getTerms(String sourceTerms) { + StringBuffer sb = new StringBuffer(); + String[] lines = sourceTerms.split("\n"); + int lineLen = lines.length; + for (int i = 0; i < lineLen; i++) { + String[] terms = lines[i].split("\t"); + String term = terms[0].trim(); + int size = terms.length; + if (i == 0) { + //sb.append("\"").append(term).append("\""); + sb.append(term); + } else { + sb.append(",").append(term); + } + } + return sb.toString(); + } + +} diff --git a/src/main/java/org/elasticsearch/plugin/nlpcn/preAnalyzer/AnsjElasticConfigurator.java b/src/main/java/org/elasticsearch/plugin/nlpcn/preAnalyzer/AnsjElasticConfigurator.java new file mode 100644 index 00000000..a8c5bcff --- /dev/null +++ b/src/main/java/org/elasticsearch/plugin/nlpcn/preAnalyzer/AnsjElasticConfigurator.java @@ -0,0 +1,37 @@ +package org.elasticsearch.plugin.nlpcn.preAnalyzer; + + +import org.elasticsearch.common.logging.ESLogger; +import org.elasticsearch.common.logging.Loggers; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.env.Environment; + +/** + * Created by fangbb on 2016-11-25. + */ +public class AnsjElasticConfigurator { + public static ESLogger logger = Loggers.getLogger("sql-init"); + public static Environment environment; + public static String ES_IP = ""; + public static String ES_PORT = ""; + + public static void init(Settings settings) { + try { + ES_IP = settings.get("network.host"); + ES_PORT = settings.get("http.port"); + if (ES_IP == null || ES_IP.equals("")) { + logger.error("network.host获取失败"); + } else { + logger.info("network.host:" + ES_IP); + } + if (ES_PORT == null || ES_PORT.equals("")) { + ES_PORT = "9200"; + logger.error("http.port获取失败,使用默认端口:9200"); + } else { + logger.info("http.port:"+ ES_PORT); + } + } catch (Exception e) { + logger.error(e.getMessage()); + } + } +} diff --git a/src/main/java/org/elasticsearch/plugin/nlpcn/preAnalyzer/Method.java b/src/main/java/org/elasticsearch/plugin/nlpcn/preAnalyzer/Method.java new file mode 100644 index 00000000..fd3ca88d --- /dev/null +++ b/src/main/java/org/elasticsearch/plugin/nlpcn/preAnalyzer/Method.java @@ -0,0 +1,66 @@ +package org.elasticsearch.plugin.nlpcn.preAnalyzer; + +/** + * Created by fangbb on 2016-12-8. + */ +public class Method { + String parentMethod; + String childenMethod; + String params; + + public Method(String parentMethod, String childenMethod, String params) { + this.parentMethod = parentMethod; + this.childenMethod = childenMethod; + this.params = params; + } + + public Method() { + } + + public String getParentMethod() { + return parentMethod; + } + + public void setParentMethod(String parentMethod) { + this.parentMethod = parentMethod; + } + + public String getChildenMethod() { + return childenMethod; + } + + public void setChildenMethod(String childenMethod) throws Exception{ + if (this.parentMethod.equals("seg") && childenMethod != null) { + throw new Exception("seg("+childenMethod+"()) is erro"); + } else { + this.childenMethod = childenMethod; + } + + } + + public String getParams() { + return params; + } + + public void setParams(String params) { + this.params = params; + } + + public String getFunName() { + String name = ""; + if (this.parentMethod != null && !this.parentMethod.equals("seg")) { + name = this.parentMethod; + } else if (this.childenMethod != null && !this.childenMethod.equals("seg")) { + name = this.childenMethod; + } + return name; + } + + public boolean containSeg() { + if (this.parentMethod != null && this.parentMethod.equals("seg") + ||this.childenMethod!=null && this.childenMethod.equals("seg")) { + return true; + } + return false; + } +} diff --git a/src/main/java/org/elasticsearch/plugin/nlpcn/preAnalyzer/SqlParseAnalyzer.java b/src/main/java/org/elasticsearch/plugin/nlpcn/preAnalyzer/SqlParseAnalyzer.java new file mode 100644 index 00000000..0af7031f --- /dev/null +++ b/src/main/java/org/elasticsearch/plugin/nlpcn/preAnalyzer/SqlParseAnalyzer.java @@ -0,0 +1,420 @@ +package org.elasticsearch.plugin.nlpcn.preAnalyzer; + +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLObject; +import com.alibaba.druid.sql.ast.SQLOrderBy; +import com.alibaba.druid.sql.ast.expr.*; +import com.alibaba.druid.sql.ast.statement.SQLSelect; +import com.alibaba.druid.sql.ast.statement.SQLSelectOrderByItem; +import com.alibaba.druid.sql.ast.statement.SQLSelectQuery; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock; +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlOutputVisitor; +import com.alibaba.druid.util.JdbcConstants; +import org.nlpcn.es4sql.parse.ElasticLexer; +import org.nlpcn.es4sql.parse.ElasticSqlExprParser; + +import java.sql.SQLFeatureNotSupportedException; +import java.util.ArrayList; +import java.util.List; + + +/** + * Created by fangbb on 2016-12-6. + */ +public class SqlParseAnalyzer { +// public String dbType = JdbcConstants.MYSQL; + private Analyzer analyzer; + + public SqlParseAnalyzer(Analyzer analyzer) { + this.analyzer = analyzer; + } + + public String seg(String sql) throws Exception { + if (sql.contains("seg(")) { + MySqlSelectQueryBlock query = getQueryBlock(sql); + parseWhere(query.getWhere()); + parseOrderBys(query.getOrderBy()); + sql = printSql(query); + } + return sql; + } + + private MySqlSelectQueryBlock getQueryBlock(String sql) { + ElasticLexer lexer = new ElasticLexer(sql); + lexer.nextToken(); + ElasticSqlExprParser elasticSqlExprParser = new ElasticSqlExprParser(lexer); + SQLExpr expr = elasticSqlExprParser.expr(); + SQLQueryExpr sqlExpr = (SQLQueryExpr) expr; + SQLSelect sqlSelect = sqlExpr.getSubQuery(); + //获取SQLSelectQuery + SQLSelectQuery sqlSelectQuery = sqlSelect.getQuery(); + MySqlSelectQueryBlock query = (MySqlSelectQueryBlock) sqlSelectQuery; + return query; + } + + private void parseWhere(SQLExpr where) throws Exception { + if (where == null) { + return; + } + preTraverse(where); + } + + private void parseOrderBys(SQLOrderBy sqlOrderBy) throws Exception { + if (sqlOrderBy == null) { + return; + } + List items = sqlOrderBy.getItems(); + for (SQLSelectOrderByItem item : items) { + SQLExpr sqlExpr = item.getExpr(); + if (sqlExpr instanceof SQLMethodInvokeExpr) { + String nested = ((SQLMethodInvokeExpr) sqlExpr).getMethodName(); + List params = ((SQLMethodInvokeExpr) sqlExpr).getParameters(); + if (nested.equals("nested") && params.size() == 3) { + parseWhere(params.get(2)); + } else { + throw new Exception("Nested sorting must be 3 parameters"); + } + } + } + } + + //先序遍历获取叶子节点 + private void preTraverse(SQLExpr sqlExpr) throws Exception { + if (sqlExpr instanceof SQLBinaryOpExpr) { + SQLBinaryOpExpr sqlBinaryOpExpr = (SQLBinaryOpExpr) sqlExpr; + SQLExpr left = sqlBinaryOpExpr.getLeft(); + SQLExpr right = sqlBinaryOpExpr.getRight(); + SQLBinaryOperator sqlBinaryOperator = sqlBinaryOpExpr.getOperator(); + if (isLeaf(sqlBinaryOperator)) { + // 普通叶节点替换 + replaceLeafNode(sqlBinaryOpExpr); + } else { + preTraverse(left); + preTraverse(right); + } + } else if (isNested(sqlExpr)) { + //nested 嵌套叶节点 + replaceNestedLeafNode(sqlExpr); + } else { + throw new SQLFeatureNotSupportedException(); + } + } + + //TODO 对Nested叶节点拆分 + //TODO 分词 + //TODO 构造新节点 + private void replaceNestedLeafNode(SQLExpr sqlExpr) throws Exception { + SQLObject sqlObject = sqlExpr.getParent(); + SQLExpr newExpr = parseNested(sqlExpr); + if (sqlObject instanceof MySqlSelectQueryBlock) { + ((MySqlSelectQueryBlock) sqlObject).setWhere(newExpr); + } else if (sqlObject instanceof SQLBinaryOpExpr) { + if (sqlExpr.equals(((SQLBinaryOpExpr) sqlObject).getRight())) { + ((SQLBinaryOpExpr) sqlObject).setRight(newExpr); + } else { + ((SQLBinaryOpExpr) sqlObject).setLeft(newExpr); + } + } + } + + //TODO 解析nested的叶节点,返回新构造的叶节点 + private SQLExpr parseNested(SQLExpr sqlExpr) throws Exception { + String methodName = ((SQLMethodInvokeExpr) sqlExpr).getMethodName(); + if (((SQLMethodInvokeExpr) sqlExpr).getParameters().size() != 2) { + throw new Exception("Nested query must be 2 parameters"); + } + SQLExpr pathName = ((SQLMethodInvokeExpr) sqlExpr).getParameters().get(0); + SQLExpr where = ((SQLMethodInvokeExpr) sqlExpr).getParameters().get(1); + SQLExpr retExpr = null; + if (where != null) { + preTraverseNested(methodName, pathName, where); + if (isLeaf(where)) { + SQLObject parent = where.getParent(); + if (parent instanceof SQLMethodInvokeExpr) { + SQLMethodInvokeExpr tmp = ((SQLMethodInvokeExpr) parent); + if (tmp.getParameters().size() == 3) { + retExpr = tmp.getParameters().get(2); + } else { + retExpr = tmp; + } + } + } else if (where instanceof SQLBinaryOpExpr) { + retExpr = where; + } + + } + return retExpr; + } + + private void preTraverseNested(String methodName, SQLExpr pathName, SQLExpr sqlExpr) throws Exception { + if (sqlExpr instanceof SQLBinaryOpExpr) { + SQLBinaryOpExpr sqlBinaryOpExpr = (SQLBinaryOpExpr) sqlExpr; + SQLExpr left = sqlBinaryOpExpr.getLeft(); + SQLExpr right = sqlBinaryOpExpr.getRight(); + SQLBinaryOperator sqlBinaryOperator = sqlBinaryOpExpr.getOperator(); + //left和right都是a=b这种形式 + if (isLeaf(sqlBinaryOperator)) { + generateNestedLeafNode(methodName, pathName, sqlBinaryOpExpr); + } else { + preTraverseNested(methodName, pathName, left); + preTraverseNested(methodName, pathName, right); + } + } + } + + private boolean isNested(SQLExpr sqlExpr) { + if (sqlExpr instanceof SQLMethodInvokeExpr) { + String mName = ((SQLMethodInvokeExpr) sqlExpr).getMethodName(); + if (mName.equals("nested")) { + return true; + } + } + return false; + } + + private boolean isLeaf(SQLBinaryOperator sqlBinaryOperator) { + if (sqlBinaryOperator.equals(SQLBinaryOperator.BooleanOr) || sqlBinaryOperator.equals(SQLBinaryOperator.BooleanAnd)) { + return false; + } + return true; + } + + private boolean isLeaf(SQLExpr sqlExpr) { + if (sqlExpr instanceof SQLBinaryOpExpr) { + SQLBinaryOperator sqlBinaryOperator = ((SQLBinaryOpExpr) sqlExpr).getOperator(); + if (sqlBinaryOperator.equals(SQLBinaryOperator.BooleanOr) || sqlBinaryOperator.equals(SQLBinaryOperator.BooleanAnd)) { + return false; + } + } + return true; + } + + private Method parseMethod(SQLExpr right) throws Exception { + Method retMethod = new Method(); + if (right instanceof SQLMethodInvokeExpr) { + SQLMethodInvokeExpr methodInvokeExpr = ((SQLMethodInvokeExpr) right); + String methodName = methodInvokeExpr.getMethodName(); + retMethod.setParentMethod(methodName); + List childMethod = methodInvokeExpr.getParameters(); + SQLExpr sqlExpr = childMethod.get(0); + if (sqlExpr instanceof SQLMethodInvokeExpr) { + retMethod.setChildenMethod(((SQLMethodInvokeExpr) sqlExpr).getMethodName()); + SQLExpr nSqlExpr = ((SQLMethodInvokeExpr) sqlExpr).getParameters().get(0); + if (nSqlExpr instanceof SQLCharExpr) { + retMethod.setParams(((SQLCharExpr) nSqlExpr).getText()); + } else if (nSqlExpr instanceof SQLIdentifierExpr) { + if (retMethod.getChildenMethod().equals("seg")) { + SQLIdentifierExpr identifierExpr = (SQLIdentifierExpr) nSqlExpr; + retMethod.setParams(identifierExpr.getName()); + childMethod.clear(); + childMethod.add(0, identifierExpr); + retMethod.setChildenMethod(null); + } + } + } else if (sqlExpr instanceof SQLCharExpr) { + retMethod.setParams(((SQLCharExpr) sqlExpr).getText()); + } else if (sqlExpr instanceof SQLIdentifierExpr) { + retMethod.setParams(((SQLIdentifierExpr) sqlExpr).getName()); + } + } + return retMethod; + } + + private boolean segNoQuota(SQLMethodInvokeExpr methodInvokeExpr) { + List params = methodInvokeExpr.getParameters(); + if (params.get(0) instanceof SQLIdentifierExpr) { + return true; + } + return false; + } + + private void removeSegFun(SQLBinaryOpExpr binaryOpExpr, SQLExpr right, Method method) { + //当seg内没有引号时,去掉seg() + if (segNoQuota((SQLMethodInvokeExpr) right) && method.containSeg()) { + method.setParentMethod(null); + List params = ((SQLMethodInvokeExpr) right).getParameters(); + binaryOpExpr.setRight(params.get(0)); + } + } + + private void generateNestedLeafNode(String methodName, SQLExpr pathName, SQLBinaryOpExpr binaryOpExpr) throws Exception { + //right:a.b = "d" + SQLExpr left = binaryOpExpr.getLeft(); + SQLExpr right = binaryOpExpr.getRight(); + SQLBinaryOperator operator = binaryOpExpr.getOperator(); + String filed = ((SQLIdentifierExpr) left).getName(); + if (right instanceof SQLMethodInvokeExpr) { + Method method = parseMethod(right); + String sourceTerm = method.getParams(); + removeSegFun(binaryOpExpr, right, method); + //seg(term("abc")) exception + if (method.containSeg() && sourceTerm != null) { + String[] terms = analyzer.analyzer(sourceTerm); + //String[] terms = "a,b".split(","); + String funName = method.getFunName(); + List allNewNode = new ArrayList(); + for (String term : terms) { + if (!funName.equals("")) { + SQLMethodInvokeExpr methodInvokeExpr = new SQLMethodInvokeExpr(); + methodInvokeExpr.setMethodName(funName); + methodInvokeExpr.addParameter(new SQLCharExpr(term)); + SQLBinaryOpExpr opNode = createOpNode(filed, methodInvokeExpr, operator); + SQLMethodInvokeExpr nestedNode = createNestedNode(methodName, pathName, opNode); + allNewNode.add(nestedNode); + } else { + SQLCharExpr charExpr = new SQLCharExpr(); + charExpr.setText(term); + SQLBinaryOpExpr opNode = createOpNode(filed, charExpr, operator); + SQLMethodInvokeExpr nestedNode = createNestedNode(methodName, pathName, opNode); + allNewNode.add(nestedNode); + } + } + conNestedTree(binaryOpExpr, allNewNode); + } else { + List allNewNode = new ArrayList(); + SQLBinaryOpExpr opNode = createOpNode(filed, right, operator); + SQLMethodInvokeExpr nestedNode = createNestedNode(methodName, pathName, opNode); + allNewNode.add(nestedNode); + conNestedTree(binaryOpExpr, allNewNode); + } + } else { + List allNewNode = new ArrayList(); + SQLBinaryOpExpr opNode = createOpNode(filed, right, operator); + SQLMethodInvokeExpr nestedNode = createNestedNode(methodName, pathName, opNode); + allNewNode.add(nestedNode); + conNestedTree(binaryOpExpr, allNewNode); + } + } + + private void replaceOldNode(SQLExpr sqlExpr,SQLExpr newExpr) throws Exception { + SQLObject sqlObject = sqlExpr.getParent(); + if (sqlObject instanceof MySqlSelectQueryBlock) { + ((MySqlSelectQueryBlock) sqlObject).setWhere(newExpr); + } else if (sqlObject instanceof SQLBinaryOpExpr) { + if (sqlExpr.equals(((SQLBinaryOpExpr) sqlObject).getRight())) { + ((SQLBinaryOpExpr) sqlObject).setRight(newExpr); + } else { + ((SQLBinaryOpExpr) sqlObject).setLeft(newExpr); + } + } else if (sqlObject instanceof SQLMethodInvokeExpr) { + ((SQLMethodInvokeExpr) sqlObject).addParameter(newExpr); + } + } + + //构造新的二叉树替换原有节点 + private void conNestedTree(SQLBinaryOpExpr retExpr, List sqlExprs) throws Exception{ + int size = sqlExprs.size(); + int andNum = size - 1; + List allNode = new ArrayList(); + if (andNum == 0) { + replaceOldNode(retExpr, sqlExprs.get(0)); + } else { + for (int i = 0; i < andNum; i++) { + if (i == 0) { + retExpr.setOperator(SQLBinaryOperator.BooleanAnd); + //retExpr做为顶点 + allNode.add(retExpr); + } else { + allNode.add(createOpNode(null, null, SQLBinaryOperator.BooleanAnd)); + } + } + allNode.addAll(sqlExprs); + //共有n-1个And,n个node,每一个节点从0开始进行编号,那么第i个节点的左孩子的编号为2*i+1,右孩子为2*i+2。 + for (int parentIndex = 0; parentIndex < andNum; parentIndex++) { + if (allNode.get(parentIndex) instanceof SQLBinaryOpExpr) { + ((SQLBinaryOpExpr) allNode.get(parentIndex)).setLeft(allNode.get(parentIndex * 2 + 1)); + ((SQLBinaryOpExpr) allNode.get(parentIndex)).setRight(allNode.get(parentIndex * 2 + 2)); + } + } + } + } + + //对叶节点分词,构造新节点 + private void replaceLeafNode(SQLBinaryOpExpr binaryOpExpr) throws Exception { + SQLExpr left = binaryOpExpr.getLeft(); + SQLExpr right = binaryOpExpr.getRight(); + SQLBinaryOperator operator = binaryOpExpr.getOperator(); + String filed = ((SQLIdentifierExpr) left).getName(); + if (right instanceof SQLMethodInvokeExpr) { + Method method = parseMethod(right); + String sourceTerm = method.getParams(); + removeSegFun(binaryOpExpr, right, method); + //seg(term("abc")) exception + if (method.containSeg() && sourceTerm != null) { + String[] terms = analyzer.analyzer(sourceTerm); + //String[] terms = "a,b".split(","); + String funName = method.getFunName(); + List allNewNode = new ArrayList(); + for (String term : terms) { + if (!funName.equals("")) { + SQLMethodInvokeExpr methodInvokeExpr = new SQLMethodInvokeExpr(); + methodInvokeExpr.setMethodName(funName); + methodInvokeExpr.addParameter(new SQLCharExpr(term)); + allNewNode.add(createOpNode(filed, methodInvokeExpr, operator)); + } else { + SQLCharExpr charExpr = new SQLCharExpr(); + charExpr.setText(term); + allNewNode.add(createOpNode(filed, charExpr, operator)); + } + + } + conTree(binaryOpExpr, allNewNode); + } + } + } + + //构造新的二叉树替换原有节点 + private void conTree(SQLBinaryOpExpr retExpr, List SQLBinaryOpNode) { + int size = SQLBinaryOpNode.size(); + int andNum = size - 1; + List allNode = new ArrayList(); + if (andNum == 0) { + retExpr.setRight(SQLBinaryOpNode.get(0).getRight()); + } else { + for (int i = 0; i < andNum; i++) { + if (i == 0) { + retExpr.setOperator(SQLBinaryOperator.BooleanAnd); + //retExpr做为顶点 + allNode.add(retExpr); + } else { + allNode.add(createOpNode(null, null, SQLBinaryOperator.BooleanAnd)); + } + } + allNode.addAll(SQLBinaryOpNode); + //共有n-1个And,n个node,每一个节点从0开始进行编号,那么第i个节点的左孩子的编号为2*i+1,右孩子为2*i+2。 + for (int parentIndex = 0; parentIndex < andNum; parentIndex++) { + allNode.get(parentIndex).setLeft(allNode.get(parentIndex * 2 + 1)); + allNode.get(parentIndex).setRight(allNode.get(parentIndex * 2 + 2)); + } + } + } + + + //TODO 构造一个节点 + private SQLBinaryOpExpr createOpNode(String filed, SQLExpr value, SQLBinaryOperator operator) { + SQLBinaryOpExpr retWhere = new SQLBinaryOpExpr(); + SQLIdentifierExpr ileft = new SQLIdentifierExpr(); + ileft.setName(filed); + retWhere.setLeft(ileft); + retWhere.setOperator(operator); + retWhere.setRight(value); + return retWhere; + } + + //TODO 构造一个nested节点 + private SQLMethodInvokeExpr createNestedNode(String name, SQLExpr pathName, SQLBinaryOpExpr sqlBinaryOpExpr) { + SQLMethodInvokeExpr sqlMethodInvokeExpr = new SQLMethodInvokeExpr(); + sqlMethodInvokeExpr.setMethodName(name); + sqlMethodInvokeExpr.addParameter(pathName); + sqlMethodInvokeExpr.addParameter(sqlBinaryOpExpr); + return sqlMethodInvokeExpr; + } + + private String printSql(MySqlSelectQueryBlock query) { + StringBuilder out = new StringBuilder(); + MySqlOutputVisitor visitor = new MySqlOutputVisitor(out); + query.accept0(visitor); + return out.toString(); + } + +} diff --git a/src/main/java/org/elasticsearch/plugin/nlpcn/request/HttpRequester.java b/src/main/java/org/elasticsearch/plugin/nlpcn/request/HttpRequester.java new file mode 100644 index 00000000..f5e98dc1 --- /dev/null +++ b/src/main/java/org/elasticsearch/plugin/nlpcn/request/HttpRequester.java @@ -0,0 +1,299 @@ +package org.elasticsearch.plugin.nlpcn.request; + + +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.Charset; +import java.util.Map; +import java.util.Vector; + +/** + * HTTP请求对象 + * + */ +public class HttpRequester { + + + private String defaultContentEncoding; + + private int defaultTimeout = 5000; + + public HttpRequester() { + this.defaultContentEncoding = Charset.defaultCharset().name(); + } + + /** + * 发送GET请求 + * + * @param urlString + * URL地址 + * @return 响应对象 + * @throws java.io.IOException + */ + public HttpResponse sendGet(String urlString) throws IOException { + return this.send(urlString, "GET", null, null); + } + + /** + * 发送GET请求 + * + * @param urlString + * URL地址 + * @param params + * 参数集合 + * @return 响应对象 + * @throws java.io.IOException + */ + public HttpResponse sendGet(String urlString, Map params) + throws IOException { + return this.send(urlString, "GET", params, null); + } + + /** + * 发送GET请求 + * + * @param urlString + * URL地址 + * @param params + * 参数集合 + * @param propertys + * 请求属性 + * @return 响应对象 + * @throws java.io.IOException + */ + public HttpResponse sendGet(String urlString, Map params, + Map propertys) throws IOException { + return this.send(urlString, "GET", params, propertys); + } + + /** + * 发送POST请求 + * + * @param urlString + * URL地址 + * @return 响应对象 + * @throws java.io.IOException + */ + public HttpResponse sendPost(String urlString) throws IOException { + return this.send(urlString, "POST", null, null); + } + + /** + * 发送POST请求 + * + * @param urlString + * URL地址 + * @param params + * 参数集合 + * @return 响应对象 + * @throws java.io.IOException + */ + public HttpResponse sendPost(String urlString, Map params) + throws IOException { + return this.send(urlString, "POST", params, null); + } + + /** + * 发送POST请求 + * + * @param urlString + * URL地址 + * @param params + * 参数集合 + * @param propertys + * 请求属性 + * @return 响应对象 + * @throws java.io.IOException + */ + public HttpResponse sendPost(String urlString, Map params, + Map propertys) throws IOException { + return this.send(urlString, "POST", params, propertys); + } + + /* + * POST + */ + public HttpResponse sendPostRawData(String urlString, Map parameters, + Map propertys, String rawData) throws IOException { + + String method = "POST"; + + HttpURLConnection urlConnection = null; + + if (method.equalsIgnoreCase("POST") && parameters != null) { + StringBuffer param = new StringBuffer(); + int i = 0; + for (String key : parameters.keySet()) { + if (i == 0) + param.append("?"); + else + param.append("&"); + param.append(key).append("=").append(parameters.get(key)); + i++; + } + urlString += param; + } + URL url = new URL(urlString); + urlConnection = (HttpURLConnection) url.openConnection(); + + urlConnection.setRequestMethod(method); + urlConnection.setDoOutput(true); + urlConnection.setDoInput(true); + urlConnection.setUseCaches(false); + urlConnection.setConnectTimeout(defaultTimeout); + urlConnection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)"); + + urlConnection.setRequestProperty("Content-Type", "text/plain;charset=UTF-8"); + + if (propertys != null) + for (String key : propertys.keySet()) { + urlConnection.addRequestProperty(key, propertys.get(key)); + } + + if (method.equalsIgnoreCase("POST") && rawData != null) { + + OutputStream outStream = urlConnection.getOutputStream(); + outStream.write(rawData.getBytes()); + + outStream.flush(); + outStream.close(); + } + + return this.makeContent(urlString, urlConnection); + } + + /** + * 发送HTTP请求 + * + * @param urlString + * @return 响映对象 + * @throws java.io.IOException + */ + private HttpResponse send(String urlString, String method, + Map parameters, Map propertys) + throws IOException { + HttpURLConnection urlConnection = null; + + if (method.equalsIgnoreCase("GET") && parameters != null) { + StringBuffer param = new StringBuffer(); + int i = 0; + for (String key : parameters.keySet()) { + if (i == 0) + param.append("?"); + else + param.append("&"); + param.append(key).append("=").append(parameters.get(key)); + i++; + } + urlString += param; + } + URL url = new URL(urlString); + urlConnection = (HttpURLConnection) url.openConnection(); + + urlConnection.setRequestMethod(method); + urlConnection.setDoOutput(true); + urlConnection.setDoInput(true); + urlConnection.setUseCaches(false); + urlConnection.setConnectTimeout(defaultTimeout); + urlConnection.setReadTimeout(defaultTimeout*2); + urlConnection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)"); + if (propertys != null) + for (String key : propertys.keySet()) { + urlConnection.addRequestProperty(key, propertys.get(key)); + } + + if (method.equalsIgnoreCase("POST") && parameters != null) { + StringBuffer param = new StringBuffer(); + for (String key : parameters.keySet()) { + param.append("&"); + param.append(key).append("=").append(parameters.get(key)); + } + urlConnection.getOutputStream().write(param.toString().getBytes()); + urlConnection.getOutputStream().flush(); + urlConnection.getOutputStream().close(); + } + + return this.makeContent(urlString, urlConnection); + } + + /** + * 得到响应对象 + * + * @param urlConnection + * @return 响应对象 + * @throws java.io.IOException + */ + private HttpResponse makeContent(String urlString, + HttpURLConnection urlConnection) throws IOException { + HttpResponse httpResponser = new HttpResponse(); + try { + InputStream in = urlConnection.getInputStream(); + BufferedReader bufferedReader = new BufferedReader( + new InputStreamReader(in)); + httpResponser.contentCollection = new Vector(); + StringBuffer temp = new StringBuffer(); + String line = bufferedReader.readLine(); + while (line != null) { + httpResponser.contentCollection.add(line); + temp.append(line).append("\r\n"); + line = bufferedReader.readLine(); + } + bufferedReader.close(); + + String ecod = urlConnection.getContentEncoding(); + if (ecod == null) + ecod = this.defaultContentEncoding; + + httpResponser.urlString = urlString; + + httpResponser.defaultPort = urlConnection.getURL().getDefaultPort(); + httpResponser.file = urlConnection.getURL().getFile(); + httpResponser.host = urlConnection.getURL().getHost(); + httpResponser.path = urlConnection.getURL().getPath(); + httpResponser.port = urlConnection.getURL().getPort(); + httpResponser.protocol = urlConnection.getURL().getProtocol(); + httpResponser.query = urlConnection.getURL().getQuery(); + httpResponser.ref = urlConnection.getURL().getRef(); + httpResponser.userInfo = urlConnection.getURL().getUserInfo(); + + httpResponser.content = new String(temp.toString().getBytes(), ecod); + httpResponser.contentEncoding = ecod; + httpResponser.code = urlConnection.getResponseCode(); + httpResponser.message = urlConnection.getResponseMessage(); + httpResponser.contentType = urlConnection.getContentType(); + httpResponser.method = urlConnection.getRequestMethod(); + httpResponser.connectTimeout = urlConnection.getConnectTimeout(); + httpResponser.readTimeout = urlConnection.getReadTimeout(); + + return httpResponser; + } catch (IOException e) { + throw e; + } finally { + if (urlConnection != null) + urlConnection.disconnect(); + } + } + + /** + * 默认的响应字符集 + */ + public String getDefaultContentEncoding() { + return this.defaultContentEncoding; + } + + /** + * 设置默认的响应字符集 + */ + public void setDefaultContentEncoding(String defaultContentEncoding) { + this.defaultContentEncoding = defaultContentEncoding; + } + + public int getDefaultTimeout() { + return defaultTimeout; + } + + public void setDefaultTimeout(int defaultTimeout) { + this.defaultTimeout = defaultTimeout; + } +} \ No newline at end of file diff --git a/src/main/java/org/elasticsearch/plugin/nlpcn/request/HttpResponse.java b/src/main/java/org/elasticsearch/plugin/nlpcn/request/HttpResponse.java new file mode 100644 index 00000000..04cbe5e2 --- /dev/null +++ b/src/main/java/org/elasticsearch/plugin/nlpcn/request/HttpResponse.java @@ -0,0 +1,124 @@ +package org.elasticsearch.plugin.nlpcn.request; +import java.util.Vector; + + +/** + * 响应对象 + */ +public class HttpResponse { + + String urlString; + + int defaultPort; + + String file; + + String host; + + String path; + + int port; + + String protocol; + + String query; + + String ref; + + String userInfo; + + String contentEncoding; + + String content; + + String contentType; + + int code; + + String message; + + String method; + + int connectTimeout; + + int readTimeout; + + Vector contentCollection; + + public String getContent() { + return content; + } + + public String getContentType() { + return contentType; + } + + public int getCode() { + return code; + } + + public String getMessage() { + return message; + } + + public Vector getContentCollection() { + return contentCollection; + } + + public String getContentEncoding() { + return contentEncoding; + } + + public String getMethod() { + return method; + } + + public int getConnectTimeout() { + return connectTimeout; + } + + public int getReadTimeout() { + return readTimeout; + } + + public String getUrlString() { + return urlString; + } + + public int getDefaultPort() { + return defaultPort; + } + + public String getFile() { + return file; + } + + public String getHost() { + return host; + } + + public String getPath() { + return path; + } + + public int getPort() { + return port; + } + + public String getProtocol() { + return protocol; + } + + public String getQuery() { + return query; + } + + public String getRef() { + return ref; + } + + public String getUserInfo() { + return userInfo; + } + +} \ No newline at end of file diff --git a/src/main/java/org/nlpcn/es4sql/domain/Field.java b/src/main/java/org/nlpcn/es4sql/domain/Field.java index 465f2f10..b2c96ff7 100644 --- a/src/main/java/org/nlpcn/es4sql/domain/Field.java +++ b/src/main/java/org/nlpcn/es4sql/domain/Field.java @@ -16,6 +16,33 @@ public class Field implements Cloneable{ private NestedType nested; private ChildrenType children; + private Where where; + private String mode; + private String sortName; + + public Where getWhere() { + return where; + } + + public void setWhere(Where where) { + this.where = where; + } + public String getMode() { + return mode; + } + + public void setMode(String mode) { + this.mode = mode; + } + + public String getSortName() { + return sortName; + } + + public void setSortName(String sortName) { + this.sortName = sortName; + } + public Field(String name, String alias) { this.name = name; this.alias = alias; diff --git a/src/main/java/org/nlpcn/es4sql/domain/Order.java b/src/main/java/org/nlpcn/es4sql/domain/Order.java index de445ccf..27ccec6b 100644 --- a/src/main/java/org/nlpcn/es4sql/domain/Order.java +++ b/src/main/java/org/nlpcn/es4sql/domain/Order.java @@ -8,12 +8,26 @@ public class Order { private String name; private String type; + private boolean isNested = false; + private String mode; + private String path; + private Where condition; public Order(String name, String type) { this.name = name; this.type = type; } + public Order(boolean isNested, String mode, String path, + Where condition, String name, String type) { + this.isNested = isNested; + this.mode = mode; + this.path = path; + this.condition = condition; + this.name = name; + this.type = type; + } + public String getName() { return name; } @@ -30,4 +44,35 @@ public void setType(String type) { this.type = type; } + public boolean isNested() { + return isNested; + } + + public void setNested(boolean nested) { + isNested = nested; + } + + public String getMode() { + return mode; + } + + public void setMode(String mode) { + this.mode = mode; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public Where getCondition() { + return condition; + } + + public void setCondition(Where condition) { + this.condition = condition; + } } diff --git a/src/main/java/org/nlpcn/es4sql/domain/Query.java b/src/main/java/org/nlpcn/es4sql/domain/Query.java index 67708205..7a95bb61 100644 --- a/src/main/java/org/nlpcn/es4sql/domain/Query.java +++ b/src/main/java/org/nlpcn/es4sql/domain/Query.java @@ -57,4 +57,8 @@ public String[] getTypeArr() { return list.toArray(new String[list.size()]); } +// public boolean replaceWhere(Where where){ +// this.where = where; +// return true; +// } } diff --git a/src/main/java/org/nlpcn/es4sql/domain/Select.java b/src/main/java/org/nlpcn/es4sql/domain/Select.java index b4db1938..62f4db1b 100644 --- a/src/main/java/org/nlpcn/es4sql/domain/Select.java +++ b/src/main/java/org/nlpcn/es4sql/domain/Select.java @@ -3,7 +3,6 @@ import org.nlpcn.es4sql.domain.hints.Hint; import org.nlpcn.es4sql.parse.SubQueryExpression; -import java.sql.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -79,6 +78,14 @@ public void addOrderBy(String name, String type) { this.orderBys.add(new Order(name, type)); } + //TODO new add + public void addOrderBy(boolean isNested, String mode, String path, + Where condition, String name, String type) { + if ("_score".equals(name)) { + isQuery = true; + } + this.orderBys.add(new Order(isNested, mode, path, condition, name, type)); + } public void addField(Field field) { if (field == null ) { @@ -88,7 +95,7 @@ public void addField(Field field) { this.selectAll = true; } - if(field instanceof MethodField && aggsFunctions.contains(field.getName().toUpperCase())) { + if(field instanceof MethodField && aggsFunctions.contains(field.getName().toUpperCase())) { isAgg = true; } @@ -145,5 +152,10 @@ public boolean isOrderdSelect(){ public boolean isSelectAll() { return selectAll; } + +// public boolean replaceOrderBys(List orderBys){ +// this.orderBys = orderBys; +// return true; +// } } diff --git a/src/main/java/org/nlpcn/es4sql/parse/FieldMaker.java b/src/main/java/org/nlpcn/es4sql/parse/FieldMaker.java index eb46060c..d56c6b6b 100644 --- a/src/main/java/org/nlpcn/es4sql/parse/FieldMaker.java +++ b/src/main/java/org/nlpcn/es4sql/parse/FieldMaker.java @@ -91,6 +91,11 @@ private static Field handleIdentifier(NestedType nestedType, String alias, Strin Field field = handleIdentifier(new SQLIdentifierExpr(nestedType.field), alias, tableAlias); field.setNested(nestedType); field.setChildren(null); + if (nestedType.mode != null) { + field.setWhere(nestedType.where); + field.setMode(nestedType.mode); + field.setSortName(nestedType.field); + } return field; } diff --git a/src/main/java/org/nlpcn/es4sql/parse/NestedType.java b/src/main/java/org/nlpcn/es4sql/parse/NestedType.java index f1109c48..302f8e87 100644 --- a/src/main/java/org/nlpcn/es4sql/parse/NestedType.java +++ b/src/main/java/org/nlpcn/es4sql/parse/NestedType.java @@ -1,10 +1,7 @@ package org.nlpcn.es4sql.parse; import com.alibaba.druid.sql.ast.SQLExpr; -import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; -import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; -import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr; -import com.alibaba.druid.sql.ast.expr.SQLTextLiteralExpr; +import com.alibaba.druid.sql.ast.expr.*; import org.nlpcn.es4sql.Util; import org.nlpcn.es4sql.domain.Where; import org.nlpcn.es4sql.exception.SqlParseException; @@ -15,9 +12,12 @@ * Created by Eliran on 12/11/2015. */ public class NestedType { + //字段名称 public String field; + //排序时的路径和条件 public String path; public Where where; + public String mode; private boolean reverse; private boolean simple; @@ -30,7 +30,7 @@ public boolean tryFillFromExpr(SQLExpr expr) throws SqlParseException { reverse = methodNameLower.equals("reverse_nested"); List parameters = method.getParameters(); - if (parameters.size() != 2 && parameters.size() != 1) + if (parameters.size() != 3 && parameters.size() != 2 && parameters.size() != 1) throw new SqlParseException("on nested object only allowed 2 parameters (field,path)/(path,conditions..) or 1 parameter (field) "); String field = Util.extendedToString(parameters.get(0)); @@ -52,25 +52,37 @@ public boolean tryFillFromExpr(SQLExpr expr) throws SqlParseException { } } else if (parameters.size() == 2) { - SQLExpr secondParameter = parameters.get(1); - if(secondParameter instanceof SQLTextLiteralExpr || secondParameter instanceof SQLIdentifierExpr || secondParameter instanceof SQLPropertyExpr) { - - String pathString = Util.extendedToString(secondParameter); - if(pathString.equals("")) + SQLExpr lastParameter = parameters.get(1); + if (lastParameter instanceof SQLTextLiteralExpr || lastParameter instanceof SQLIdentifierExpr || lastParameter instanceof SQLPropertyExpr) { + String pathString = Util.extendedToString(lastParameter); + if (pathString.equals("")) this.path = null; else this.path = pathString; this.simple = true; - } - else { + } else { this.path = field; Where where = Where.newInstance(); - new SqlParser().parseWhere(secondParameter,where); - if(where.getWheres().size() == 0) + new SqlParser().parseWhere(lastParameter, where); + if (where.getWheres().size() == 0) throw new SqlParseException("unable to parse filter where."); this.where = where; simple = false; } + } else if (parameters.size() == 3) { + this.path = field; + SQLExpr secondParameter = parameters.get(1); + if (secondParameter instanceof SQLAggregateExpr){ + this.mode = ((SQLAggregateExpr) secondParameter).getMethodName(); + this.field = ((SQLAggregateExpr) secondParameter).getArguments().get(0).toString(); + } + SQLExpr lastParameter = parameters.get(2); + Where where = Where.newInstance(); + new SqlParser().parseWhere(lastParameter, where); + if (where.getWheres().size() == 0) + throw new SqlParseException("unable to parse filter where."); + this.where = where; + simple = false; } return true; diff --git a/src/main/java/org/nlpcn/es4sql/parse/SqlParser.java b/src/main/java/org/nlpcn/es4sql/parse/SqlParser.java index 60df3739..67cb53f6 100644 --- a/src/main/java/org/nlpcn/es4sql/parse/SqlParser.java +++ b/src/main/java/org/nlpcn/es4sql/parse/SqlParser.java @@ -1,15 +1,13 @@ package org.nlpcn.es4sql.parse; -import java.util.*; - +import com.alibaba.druid.sql.ast.SQLCommentHint; +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLOrderBy; +import com.alibaba.druid.sql.ast.SQLOrderingSpecification; import com.alibaba.druid.sql.ast.expr.*; import com.alibaba.druid.sql.ast.statement.*; -import com.alibaba.druid.sql.ast.*; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlSelectGroupByExpr; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock; - - -import org.nlpcn.es4sql.Util; import org.nlpcn.es4sql.domain.*; import org.nlpcn.es4sql.domain.Where.CONN; import org.nlpcn.es4sql.domain.hints.Hint; @@ -17,11 +15,16 @@ import org.nlpcn.es4sql.exception.SqlParseException; import org.nlpcn.es4sql.spatial.SpatialParamsFactory; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + /** * es sql support - * + * * @author ansj - * + * */ public class SqlParser { @@ -51,7 +54,8 @@ private Select parseSelect(MySqlSelectQueryBlock query) throws SqlParseException select.getHints().addAll(parseHints(query.getHints())); findLimit(query.getLimit(), select); - + //TODO 修改 + //select.setOrderBys(findOrderBy(query.getOrderBy())); findOrderBy(query, select); findGroupBy(query, select); @@ -150,7 +154,7 @@ private void explanCond(String opear, SQLExpr expr, Where where) throws SqlParse if(Condition.OPEAR.methodNameToOpear.containsKey(methodName)){ Object[] methodParametersValue = getMethodValuesWithSubQueries(method); - Condition condition = null; + Condition condition = null; if(isNested) condition = new Condition(CONN.valueOf(opear) ,soExpr.getLeft().toString(), Condition.OPEAR.methodNameToOpear.get(methodName),methodParametersValue, nestedType); @@ -197,7 +201,7 @@ else if(isChildren) isChildren = true; } - Condition condition = null; + Condition condition = null; if(isNested) condition = new Condition(CONN.valueOf(opear), leftSide, siExpr.isNot() ? "NOT IN" : "IN", parseValue(siExpr.getTargetList()), nestedType); @@ -217,19 +221,19 @@ else if(isChildren) NestedType nestedType = new NestedType(); if(nestedType.tryFillFromExpr(between.getTestExpr())){ leftSide = nestedType.field; - + isNested = true; } ChildrenType childrenType = new ChildrenType(); if(childrenType.tryFillFromExpr(between.getTestExpr())){ leftSide = childrenType.field; - + isChildren = true; } - Condition condition = null; - + Condition condition = null; + if(isNested) condition = new Condition(CONN.valueOf(opear), leftSide, between.isNot() ? "NOT BETWEEN" : "BETWEEN", new Object[]{parseValue(between.beginExpr), parseValue(between.endExpr)}, nestedType); else if(isChildren) @@ -247,21 +251,21 @@ else if (expr instanceof SQLMethodInvokeExpr) { String methodName = methodExpr.getMethodName(); if(SpatialParamsFactory.isAllowedMethod(methodName)){ String fieldName = methodParameters.get(0).toString(); - + boolean isNested = false; boolean isChildren = false; NestedType nestedType = new NestedType(); if (nestedType.tryFillFromExpr(methodParameters.get(0))) { fieldName = nestedType.field; - + isNested = true; } ChildrenType childrenType = new ChildrenType(); if (childrenType.tryFillFromExpr(methodParameters.get(0))) { fieldName = childrenType.field; - + isChildren = true; } @@ -313,12 +317,12 @@ else if (methodName.toLowerCase().equals("script")){ } } else if (expr instanceof SQLInSubQueryExpr){ SQLInSubQueryExpr sqlIn = (SQLInSubQueryExpr) expr; - + Select innerSelect = parseSelect((MySqlSelectQueryBlock) sqlIn.getSubQuery().getQuery()); - + if(innerSelect.getFields() == null || innerSelect.getFields().size()!=1) throw new SqlParseException("should only have one return field in subQuery"); - + SubQueryExpression subQueryExpression = new SubQueryExpression(innerSelect); String leftSide = sqlIn.getExpr().toString(); @@ -329,18 +333,18 @@ else if (methodName.toLowerCase().equals("script")){ NestedType nestedType = new NestedType(); if(nestedType.tryFillFromExpr(sqlIn.getExpr())){ leftSide = nestedType.field; - + isNested = true; } - + ChildrenType childrenType = new ChildrenType(); if(childrenType.tryFillFromExpr(sqlIn.getExpr())){ leftSide = childrenType.field; - + isChildren = true; } - Condition condition = null; + Condition condition = null; if(isNested) condition = new Condition(CONN.valueOf(opear), leftSide, sqlIn.isNot() ? "NOT IN" : "IN", subQueryExpression, nestedType); @@ -402,7 +406,7 @@ private Object parseValue(SQLExpr expr) throws SqlParseException { } - private void findSelect(MySqlSelectQueryBlock query, Select select,String tableAlias) throws SqlParseException { + private void findSelect(MySqlSelectQueryBlock query, Select select, String tableAlias) throws SqlParseException { List selectList = query.getSelectList(); for (SQLSelectItem sqlSelectItem : selectList) { Field field = FieldMaker.makeField(sqlSelectItem.getExpr(), sqlSelectItem.getAlias(),tableAlias); @@ -486,6 +490,14 @@ private String sameAliasWhere(Where where, String... aliases) throws SqlParseExc return firstAlias; } + private List findOrderBy(SQLOrderBy orderBy){ + List items = orderBy.getItems(); + for (SQLSelectOrderByItem sqlSelectOrderByItem : items) { + SQLExpr expr = sqlSelectOrderByItem.getExpr(); + } + return new ArrayList(); + } + private void findOrderBy(MySqlSelectQueryBlock query, Select select) throws SqlParseException { SQLOrderBy orderBy = query.getOrderBy(); @@ -501,8 +513,9 @@ private void findOrderBy(MySqlSelectQueryBlock query, Select select) throws SqlP private void addOrderByToSelect(Select select, List items, String alias) throws SqlParseException { for (SQLSelectOrderByItem sqlSelectOrderByItem : items) { SQLExpr expr = sqlSelectOrderByItem.getExpr(); - String orderByName = FieldMaker.makeField(expr, null, null).toString(); - + Field field = FieldMaker.makeField(expr, null, null); + String orderByName = field.getName(); + //String orderByName = FieldMaker.makeField(expr, null, null).toString(); if (sqlSelectOrderByItem.getType() == null) { sqlSelectOrderByItem.setType(SQLOrderingSpecification.ASC); } @@ -510,8 +523,16 @@ private void addOrderByToSelect(Select select, List items, orderByName = orderByName.replace("`", ""); if(alias!=null) orderByName = orderByName.replaceFirst(alias+"\\.",""); - select.addOrderBy(orderByName, type); - + if (field.isNested()) { + String path = field.getNestedPath(); + String mode = field.getMode(); + String sortName = field.getSortName(); + Where condition = field.getWhere(); + select.addOrderBy(true, mode, path, condition, sortName, type); + } else { + select.addOrderBy(orderByName, type); + } + //select.addOrderBy(orderByName, type); } } diff --git a/src/main/java/org/nlpcn/es4sql/query/DefaultQueryAction.java b/src/main/java/org/nlpcn/es4sql/query/DefaultQueryAction.java index 19310d0c..3c96e9e0 100644 --- a/src/main/java/org/nlpcn/es4sql/query/DefaultQueryAction.java +++ b/src/main/java/org/nlpcn/es4sql/query/DefaultQueryAction.java @@ -10,12 +10,14 @@ import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptService; +import org.elasticsearch.search.sort.SortBuilder; import org.elasticsearch.search.sort.SortOrder; import org.nlpcn.es4sql.domain.*; import org.nlpcn.es4sql.domain.hints.Hint; import org.nlpcn.es4sql.domain.hints.HintType; import org.nlpcn.es4sql.exception.SqlParseException; import org.nlpcn.es4sql.query.maker.QueryMaker; +import org.nlpcn.es4sql.query.maker.SortMaker; /** * Transform SQL query to standard Elasticsearch search query @@ -149,8 +151,15 @@ private void setWhere(Where where) throws SqlParseException { * list of Order object */ private void setSorts(List orderBys) { + //TODO 需要修改 for (Order order : orderBys) { - request.addSort(order.getName(), SortOrder.valueOf(order.getType())); + boolean isNested = order.isNested(); + if (isNested) { + SortBuilder sort = SortMaker.explan(order); + request.addSort(sort); + } else { + request.addSort(order.getName(), SortOrder.valueOf(order.getType())); + } } } diff --git a/src/main/java/org/nlpcn/es4sql/query/ESActionFactory.java b/src/main/java/org/nlpcn/es4sql/query/ESActionFactory.java index 95cda676..0e31c6b7 100644 --- a/src/main/java/org/nlpcn/es4sql/query/ESActionFactory.java +++ b/src/main/java/org/nlpcn/es4sql/query/ESActionFactory.java @@ -29,69 +29,66 @@ public class ESActionFactory { - /** - * Create the compatible Query object - * based on the SQL query. - * - * @param sql The SQL query. - * @return Query object. - */ - public static QueryAction create(Client client, String sql) throws SqlParseException, SQLFeatureNotSupportedException { - sql = sql.replaceAll("\n"," "); + /** + * Create the compatible Query object + * based on the SQL query. + * + * @param sql The SQL query. + * @return Query object. + */ + public static QueryAction create(Client client, String sql) throws SqlParseException, SQLFeatureNotSupportedException { + sql = sql.replaceAll("\n", " "); String firstWord = sql.substring(0, sql.indexOf(' ')); switch (firstWord.toUpperCase()) { - case "SELECT": - SQLQueryExpr sqlExpr = (SQLQueryExpr) toSqlExpr(sql); - if(isJoin(sqlExpr,sql)){ + case "SELECT": + SQLQueryExpr sqlExpr = (SQLQueryExpr) toSqlExpr(sql); + if (isJoin(sqlExpr, sql)) { JoinSelect joinSelect = new SqlParser().parseJoinSelect(sqlExpr); handleSubQueries(client, joinSelect.getFirstTable()); handleSubQueries(client, joinSelect.getSecondTable()); return ESJoinQueryActionFactory.createJoinAction(client, joinSelect); - } - else { + } else { Select select = new SqlParser().parseSelect(sqlExpr); handleSubQueries(client, select); return handleSelect(client, select); } - case "DELETE": - SQLStatementParser parser = createSqlStatementParser(sql); - SQLDeleteStatement deleteStatement = parser.parseDeleteStatement(); - Delete delete = new SqlParser().parseDelete(deleteStatement); - return new DeleteQueryAction(client, delete); +// case "DELETE": +// SQLStatementParser parser = createSqlStatementParser(sql); +// SQLDeleteStatement deleteStatement = parser.parseDeleteStatement(); +// Delete delete = new SqlParser().parseDelete(deleteStatement); +// return new DeleteQueryAction(client, delete); case "SHOW": - return new ShowQueryAction(client,sql); - default: - throw new SQLFeatureNotSupportedException(String.format("Unsupported query: %s", sql)); - } - } + return new ShowQueryAction(client, sql); + default: + throw new SQLFeatureNotSupportedException(String.format("Unsupported query: %s", sql)); + } + } private static void handleSubQueries(Client client, Select select) throws SqlParseException { - if (select.containsSubQueries()) - { - for(SubQueryExpression subQueryExpression : select.getSubQueries()){ + if (select.containsSubQueries()) { + for (SubQueryExpression subQueryExpression : select.getSubQueries()) { QueryAction queryAction = handleSelect(client, subQueryExpression.getSelect()); - executeAndFillSubQuery(client , subQueryExpression,queryAction); + executeAndFillSubQuery(client, subQueryExpression, queryAction); } } } - private static void executeAndFillSubQuery(Client client , SubQueryExpression subQueryExpression,QueryAction queryAction) throws SqlParseException { + private static void executeAndFillSubQuery(Client client, SubQueryExpression subQueryExpression, QueryAction queryAction) throws SqlParseException { List values = new ArrayList<>(); Object queryResult; try { - queryResult = QueryActionElasticExecutor.executeAnyAction(client,queryAction); + queryResult = QueryActionElasticExecutor.executeAnyAction(client, queryAction); } catch (Exception e) { - throw new SqlParseException("could not execute SubQuery: " + e.getMessage()); + throw new SqlParseException("could not execute SubQuery: " + e.getMessage()); } String returnField = subQueryExpression.getReturnField(); - if(queryResult instanceof SearchHits) { + if (queryResult instanceof SearchHits) { SearchHits hits = (SearchHits) queryResult; for (SearchHit hit : hits) { - values.add(ElasticResultHandler.getFieldValue(hit,returnField)); + values.add(ElasticResultHandler.getFieldValue(hit, returnField)); } - } - else { + } else { throw new SqlParseException("on sub queries only support queries that return Hits and not aggregations"); } subQueryExpression.setValues(values.toArray()); @@ -111,9 +108,9 @@ private static SQLStatementParser createSqlStatementParser(String sql) { return new MySqlStatementParser(lexer); } - private static boolean isJoin(SQLQueryExpr sqlExpr,String sql) { + private static boolean isJoin(SQLQueryExpr sqlExpr, String sql) { MySqlSelectQueryBlock query = (MySqlSelectQueryBlock) sqlExpr.getSubQuery().getQuery(); - return query.getFrom() instanceof SQLJoinTableSource && sql.toLowerCase().contains("join"); + return query.getFrom() instanceof SQLJoinTableSource && sql.toLowerCase().contains("join"); } private static SQLExpr toSqlExpr(String sql) { @@ -128,5 +125,4 @@ private static SQLExpr toSqlExpr(String sql) { } - } diff --git a/src/main/java/org/nlpcn/es4sql/query/maker/SortMaker.java b/src/main/java/org/nlpcn/es4sql/query/maker/SortMaker.java new file mode 100644 index 00000000..60d63cfb --- /dev/null +++ b/src/main/java/org/nlpcn/es4sql/query/maker/SortMaker.java @@ -0,0 +1,112 @@ +package org.nlpcn.es4sql.query.maker; + +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.expr.SQLCharExpr; +import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; +import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; +import com.alibaba.druid.sql.ast.expr.SQLTextLiteralExpr; +import com.alibaba.druid.sql.visitor.functions.Char; +import org.elasticsearch.index.query.BoolQueryBuilder; +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.search.sort.SortBuilder; +import org.elasticsearch.search.sort.SortBuilders; +import org.elasticsearch.search.sort.SortOrder; +import org.nlpcn.es4sql.domain.Condition; +import org.nlpcn.es4sql.domain.Order; +import org.nlpcn.es4sql.domain.Where; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Created by fangbb on 2016-11-22. + */ +public class SortMaker extends Maker { + public SortMaker() { + super(true); + } + + public static SortBuilder explan(Order order) { + String flag = "_last"; + flag = order.getType().equals("DESC") ? "_last" : "_first"; + Where where = order.getCondition(); + String filedName = order.getName(); + String path = order.getPath(); + String mode = order.getMode() == null ? "sum" : order.getMode(); + String type = order.getType(); + BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery(); + List conditions = where.getWheres(); + setCondition(conditions, queryBuilder); + SortBuilder sort = SortBuilders + .fieldSort(filedName) + .setNestedFilter(queryBuilder) + .setNestedPath(path) + .sortMode(mode) + .order(SortOrder.valueOf(type)) + .missing(flag); + return sort; + } + + public static void setCondition(List conditions, BoolQueryBuilder queryBuilder) { + String key = ""; + String val = ""; + String methodName = ""; + for (Where con : conditions) { + if (con instanceof Condition) { + key = ((Condition) con).getName(); + Object vals = ((Condition) con).getValue(); + if (vals.getClass().isArray()) { + Object[] md = (Object[]) vals; + val = md[0].toString(); + if (((Condition) con).getOpear().name().equals("TERM")){ + methodName = "term"; + } + } else if (vals instanceof SQLMethodInvokeExpr) { + //matchQuery + methodName = ((SQLMethodInvokeExpr) vals).getMethodName(); + val = getValue(((SQLMethodInvokeExpr) vals).getParameters().get(0)); + } else { + //match_phrase + methodName = ""; + val = vals.toString(); + } + queryBuilder = explanSort(queryBuilder, methodName, key, val); + } else if (con instanceof Where) { + List conWhere = con.getWheres(); + setCondition(conWhere, queryBuilder); + } + + } + } + public static String getValue(SQLExpr sqlExpr){ + String retStr=""; + if (sqlExpr instanceof SQLCharExpr) { + retStr = ((SQLCharExpr) sqlExpr).getValue().toString(); + } else if (sqlExpr instanceof SQLIdentifierExpr) { + retStr = ((SQLIdentifierExpr) sqlExpr).getName().toString(); + } + return retStr; + } + public static BoolQueryBuilder explanSort(BoolQueryBuilder queryBuilder, String methodName, String key, String value) { + if (methodName.equals("")) { + QueryBuilder termQueryBuilder = QueryBuilders.matchPhraseQuery(key, value); + queryBuilder = queryBuilder.should(termQueryBuilder); + } else if (methodName.equals("matchQuery")) { + QueryBuilder termQueryBuilder = QueryBuilders.matchQuery(key, value); + queryBuilder = queryBuilder.should(termQueryBuilder); + } else if (methodName.equals("term")) { + QueryBuilder termQueryBuilder = QueryBuilders.termQuery(key, value); + queryBuilder = queryBuilder.should(termQueryBuilder); + } + return queryBuilder; + } + + public static BoolQueryBuilder explanSort(BoolQueryBuilder queryBuilder, String key, String value) { + QueryBuilder termQueryBuilder = QueryBuilders.termQuery(key, value); + queryBuilder = queryBuilder.should(termQueryBuilder); + return queryBuilder; + } + +} diff --git a/src/test/java/org/elasticsesarch/plugin/nlpcn/SqlParseAnalyzerTest.java b/src/test/java/org/elasticsesarch/plugin/nlpcn/SqlParseAnalyzerTest.java new file mode 100644 index 00000000..2f9e3aed --- /dev/null +++ b/src/test/java/org/elasticsesarch/plugin/nlpcn/SqlParseAnalyzerTest.java @@ -0,0 +1,84 @@ +package org.elasticsesarch.plugin.nlpcn; + +import org.elasticsearch.plugin.nlpcn.preAnalyzer.Analyzer; +import org.elasticsearch.plugin.nlpcn.preAnalyzer.SqlParseAnalyzer; +import org.junit.Test; +import static org.junit.Assert.assertEquals; + +/** + * Created by linxueqing on 2016/12/13. + */ +public class SqlParseAnalyzerTest { + public class MockAnalyzer implements Analyzer { + @Override + public String[] analyzer(String term) throws Exception { + return new String[]{"大数据", "持续集成"}; + } + } + + @Test + public void withoutSegKeyword() throws Exception { + SqlParseAnalyzer sqlParseAnalyzer = new SqlParseAnalyzer(new MockAnalyzer()); + String inputSql = "select * from a"; + String expectedOutputSql = inputSql; + String outputSql = sqlParseAnalyzer.seg(inputSql); + assertEquals(expectedOutputSql, outputSql); + } + + @Test + public void segOneTerm() throws Exception { + SqlParseAnalyzer sqlParseAnalyzer = new SqlParseAnalyzer(new MockAnalyzer()); + String inputSql = "select * from a where name=seg(\"大数据持续集成\") and age > 18 order by age desc"; + String outputSql = sqlParseAnalyzer.seg(inputSql); + String expectedOutputSql = "SELECT *\n" + + "FROM a\n" + + "WHERE name = '大数据'\n" + + "\tAND name = '持续集成'\n" + + "\tAND age > 18\n" + + "ORDER BY age DESC"; + assertEquals(expectedOutputSql, outputSql); + } + + @Test + public void segOneNestedTerm() throws Exception { + SqlParseAnalyzer sqlParseAnalyzer = new SqlParseAnalyzer(new MockAnalyzer()); + String inputSql = "select * from a where nested(b, b.name=seg(\"大数据持续集成\")) and age > 18 order by age desc"; + String outputSql = sqlParseAnalyzer.seg(inputSql); + String expectedOutputSql = "SELECT *\n" + + "FROM a\n" + + "WHERE nested(b, b.name = '大数据')\n" + + "\tAND nested(b, b.name = '持续集成')\n" + + "\tAND age > 18\n" + + "ORDER BY age DESC"; + assertEquals(expectedOutputSql, outputSql); + } + + @Test + public void segNestedOrder() throws Exception { + SqlParseAnalyzer sqlParseAnalyzer = new SqlParseAnalyzer(new MockAnalyzer()); + String inputSql = "SELECT * FROM a where nested(b,b.c=\"abc\") order by nested(a,sum(a.b),a.c=term(seg(\"大数据持续集成\")))"; + String outputSql = sqlParseAnalyzer.seg(inputSql); + String expectedOutputSql = "SELECT *\n" + + "FROM a\n" + + "WHERE nested(b, b.c = 'abc')\n" + + "ORDER BY nested(a, SUM(a.b), a.c = term('大数据')\n" + + "AND a.c = term('持续集成'))"; + assertEquals(expectedOutputSql, outputSql); + } + + @Test + public void segMoreThanOneTerm() throws Exception { + SqlParseAnalyzer sqlParseAnalyzer = new SqlParseAnalyzer(new MockAnalyzer()); + String inputSql = "SELECT * FROM test where nested(a,a.c=term(seg(\"大数据持续集成\")) and a.c=\"中国\") or nested(a,a.c=term(seg(\"大数据持续集成\")))"; + String outputSql = sqlParseAnalyzer.seg(inputSql); + String expectedOutputSql = "SELECT *\n" + + "FROM test\n" + + "WHERE nested(a, a.c = term('大数据'))\n" + + "\tAND nested(a, a.c = term('持续集成'))\n" + + "\tAND nested(a, a.c = '中国')\n" + + "\tOR nested(a, a.c = term('大数据'))\n" + + "\tAND nested(a, a.c = term('持续集成'))"; + assertEquals(expectedOutputSql, outputSql); + System.out.println(outputSql); + } +} diff --git a/src/test/java/org/nlpcn/es4sql/NestedSortTest.java b/src/test/java/org/nlpcn/es4sql/NestedSortTest.java new file mode 100644 index 00000000..c49d1ec3 --- /dev/null +++ b/src/test/java/org/nlpcn/es4sql/NestedSortTest.java @@ -0,0 +1,313 @@ +package org.nlpcn.es4sql; + +import org.elasticsearch.client.transport.TransportClient; +import org.elasticsearch.common.transport.InetSocketTransportAddress; +import org.elasticsearch.plugin.deletebyquery.DeleteByQueryPlugin; +import org.junit.Assert; +import org.junit.Test; +import org.nlpcn.es4sql.query.QueryAction; +import org.nlpcn.es4sql.query.SqlElasticRequestBuilder; + +import java.net.InetAddress; +import java.net.UnknownHostException; + + +/** + * Created by fangbb on 2016-12-12. + */ +public class NestedSortTest { + + @Test + public void nestedSort4MatchQueryTest(){ + String sql = "SELECT * FROM test order by nested(a,sum(a.b),a.c=matchQuery(\"百度和谷歌\"))"; + String actual = exec(sql); + String expected = "{\n" + + " \"from\" : 0,\n" + + " \"size\" : 200,\n" + + " \"sort\" : [ {\n" + + " \"a.b\" : {\n" + + " \"order\" : \"asc\",\n" + + " \"missing\" : \"_first\",\n" + + " \"mode\" : \"SUM\",\n" + + " \"nested_filter\" : {\n" + + " \"bool\" : {\n" + + " \"should\" : {\n" + + " \"match\" : {\n" + + " \"a.c\" : {\n" + + " \"query\" : \"百度和谷歌\",\n" + + " \"type\" : \"boolean\"\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " },\n" + + " \"nested_path\" : \"a\"\n" + + " }\n" + + " } ]\n" + + "}"; + Assert.assertEquals(expected,actual); + } + + @Test + public void nestedSort4TermTest(){ + String sql = "SELECT * FROM test order by nested(a,sum(a.b),a.c=term(\"百度和谷歌\"))"; + String actual = exec(sql); + String expected = "{\n" + + " \"from\" : 0,\n" + + " \"size\" : 200,\n" + + " \"sort\" : [ {\n" + + " \"a.b\" : {\n" + + " \"order\" : \"asc\",\n" + + " \"missing\" : \"_first\",\n" + + " \"mode\" : \"SUM\",\n" + + " \"nested_filter\" : {\n" + + " \"bool\" : {\n" + + " \"should\" : {\n" + + " \"term\" : {\n" + + " \"a.c\" : \"百度和谷歌\"\n" + + " }\n" + + " }\n" + + " }\n" + + " },\n" + + " \"nested_path\" : \"a\"\n" + + " }\n" + + " } ]\n" + + "}"; + Assert.assertEquals(expected,actual); + } + + @Test + public void nestedSortDefaultTest(){ + String sql = "SELECT * FROM test order by nested(a,sum(a.b),a.c=\"百度和谷歌\")"; + String actual = exec(sql); + String expected = "{\n" + + " \"from\" : 0,\n" + + " \"size\" : 200,\n" + + " \"sort\" : [ {\n" + + " \"a.b\" : {\n" + + " \"order\" : \"asc\",\n" + + " \"missing\" : \"_first\",\n" + + " \"mode\" : \"SUM\",\n" + + " \"nested_filter\" : {\n" + + " \"bool\" : {\n" + + " \"should\" : {\n" + + " \"match\" : {\n" + + " \"a.c\" : {\n" + + " \"query\" : \"百度和谷歌\",\n" + + " \"type\" : \"phrase\"\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " },\n" + + " \"nested_path\" : \"a\"\n" + + " }\n" + + " } ]\n" + + "}"; + Assert.assertEquals(expected,actual); + } + @Test + public void nestedSortRealTest(){ + String sql = "select * from test where nested(info,info.name=term(\"java\") and info.name=term(\"python\") or info.name=term(\"Hadoop\")) order by nested(info,sum(info.age),info.name=matchQuery(\"java\") and info.name=term(\"python\") and info.name=\"Hadoop\") desc,score desc"; + String actual = exec(sql); + String expected = "{\n" + + " \"from\" : 0,\n" + + " \"size\" : 200,\n" + + " \"query\" : {\n" + + " \"bool\" : {\n" + + " \"must\" : {\n" + + " \"nested\" : {\n" + + " \"query\" : {\n" + + " \"bool\" : {\n" + + " \"must\" : {\n" + + " \"bool\" : {\n" + + " \"should\" : [ {\n" + + " \"bool\" : {\n" + + " \"must\" : [ {\n" + + " \"term\" : {\n" + + " \"info.name\" : \"java\"\n" + + " }\n" + + " }, {\n" + + " \"term\" : {\n" + + " \"info.name\" : \"python\"\n" + + " }\n" + + " } ]\n" + + " }\n" + + " }, {\n" + + " \"term\" : {\n" + + " \"info.name\" : \"Hadoop\"\n" + + " }\n" + + " } ]\n" + + " }\n" + + " }\n" + + " }\n" + + " },\n" + + " \"path\" : \"info\"\n" + + " }\n" + + " }\n" + + " }\n" + + " },\n" + + " \"sort\" : [ {\n" + + " \"info.age\" : {\n" + + " \"order\" : \"desc\",\n" + + " \"missing\" : \"_last\",\n" + + " \"mode\" : \"SUM\",\n" + + " \"nested_filter\" : {\n" + + " \"bool\" : {\n" + + " \"should\" : [ {\n" + + " \"match\" : {\n" + + " \"info.name\" : {\n" + + " \"query\" : \"java\",\n" + + " \"type\" : \"boolean\"\n" + + " }\n" + + " }\n" + + " }, {\n" + + " \"term\" : {\n" + + " \"info.name\" : \"python\"\n" + + " }\n" + + " }, {\n" + + " \"match\" : {\n" + + " \"info.name\" : {\n" + + " \"query\" : \"Hadoop\",\n" + + " \"type\" : \"phrase\"\n" + + " }\n" + + " }\n" + + " } ]\n" + + " }\n" + + " },\n" + + " \"nested_path\" : \"info\"\n" + + " }\n" + + " }, {\n" + + " \"score\" : {\n" + + " \"order\" : \"desc\"\n" + + " }\n" + + " } ]\n" + + "}"; + Assert.assertEquals(expected,actual); + } + + @Test + public void nestedSortFactTest(){ + String sql = "SELECT * FROM fact_index where nested(interest_keyword,interest_keyword.keyword=matchQuery(\"黑白\")) and nested(interest_keyword,interest_keyword.keyword=matchQuery(\"纪实\")) and province = \"北京\" order by nested(interest_keyword, sum(interest_keyword.score),interest_keyword.keyword=matchQuery(\"黑白\") and interest_keyword.keyword=matchQuery(\"纪实\")) desc"; + String actual = exec(sql); + String expected = "{\n" + + " \"from\" : 0,\n" + + " \"size\" : 200,\n" + + " \"query\" : {\n" + + " \"bool\" : {\n" + + " \"must\" : {\n" + + " \"bool\" : {\n" + + " \"must\" : [ {\n" + + " \"nested\" : {\n" + + " \"query\" : {\n" + + " \"bool\" : {\n" + + " \"must\" : {\n" + + " \"match\" : {\n" + + " \"interest_keyword.keyword\" : {\n" + + " \"query\" : \"黑白\",\n" + + " \"type\" : \"boolean\"\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " },\n" + + " \"path\" : \"interest_keyword\"\n" + + " }\n" + + " }, {\n" + + " \"nested\" : {\n" + + " \"query\" : {\n" + + " \"bool\" : {\n" + + " \"must\" : {\n" + + " \"match\" : {\n" + + " \"interest_keyword.keyword\" : {\n" + + " \"query\" : \"纪实\",\n" + + " \"type\" : \"boolean\"\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " },\n" + + " \"path\" : \"interest_keyword\"\n" + + " }\n" + + " }, {\n" + + " \"match\" : {\n" + + " \"province\" : {\n" + + " \"query\" : \"北京\",\n" + + " \"type\" : \"phrase\"\n" + + " }\n" + + " }\n" + + " } ]\n" + + " }\n" + + " }\n" + + " }\n" + + " },\n" + + " \"sort\" : [ {\n" + + " \"interest_keyword.score\" : {\n" + + " \"order\" : \"desc\",\n" + + " \"missing\" : \"_last\",\n" + + " \"mode\" : \"SUM\",\n" + + " \"nested_filter\" : {\n" + + " \"bool\" : {\n" + + " \"should\" : [ {\n" + + " \"match\" : {\n" + + " \"interest_keyword.keyword\" : {\n" + + " \"query\" : \"'黑白'\",\n" + + " \"type\" : \"boolean\"\n" + + " }\n" + + " }\n" + + " }, {\n" + + " \"match\" : {\n" + + " \"interest_keyword.keyword\" : {\n" + + " \"query\" : \"'纪实'\",\n" + + " \"type\" : \"boolean\"\n" + + " }\n" + + " }\n" + + " } ]\n" + + " }\n" + + " },\n" + + " \"nested_path\" : \"interest_keyword\"\n" + + " }\n" + + " } ]\n" + + "}"; + Assert.assertEquals(expected,actual); + } + + + public static String exec(String sql){ + String jsonExplanation = ""; + try { + TransportClient client; + client = TransportClient.builder().addPlugin(DeleteByQueryPlugin.class).build().addTransportAddress(getTransportAddress()); + //Long now = System.currentTimeMillis(); + SearchDao searchDao = new SearchDao(client); + QueryAction queryAction = searchDao.explain(sql); + SqlElasticRequestBuilder xx = queryAction.explain(); + jsonExplanation = xx.explain(); + //System.out.println(System.currentTimeMillis() - now + " ms"); + //System.out.println(jsonExplanation); + } catch (Exception e) { + e.printStackTrace(); + } + return jsonExplanation; + } + + public static InetSocketTransportAddress getTransportAddress() throws UnknownHostException { + + String host = "192.168.25.11"; + String port = "9300"; + if (host == null) { + host = "localhost"; + System.out.println("ES_TEST_HOST enviroment variable does not exist. choose default 'localhost'"); + } + + if (port == null) { + port = "9300"; + System.out.println("ES_TEST_PORT enviroment variable does not exist. choose default '9300'"); + } + + System.out.println(String.format("Connection details: host: %s. port:%s.", host, port)); + return new InetSocketTransportAddress(InetAddress.getByName(host), Integer.parseInt(port)); + } + + +} diff --git a/src/test/java/org/nlpcn/es4sql/PreAnalyzer.java b/src/test/java/org/nlpcn/es4sql/PreAnalyzer.java new file mode 100644 index 00000000..8fdc446f --- /dev/null +++ b/src/test/java/org/nlpcn/es4sql/PreAnalyzer.java @@ -0,0 +1,32 @@ +package org.nlpcn.es4sql; + +import org.elasticsearch.plugin.nlpcn.preAnalyzer.AnsjAnalyzerImpl; +import org.elasticsearch.plugin.nlpcn.preAnalyzer.SqlParseAnalyzer; +import org.junit.Test; + +/** + * Created by fangbb on 2016-12-12. + */ +public class PreAnalyzer { + @Test + public void SqlParseAnalyzerTest(){ + try { + String sql = "SELECT * FROM test where nested(a,a.c=term(seg(\"百度和谷歌\")) and a.c=\"中国\") or nested(a,a.c=term(seg(\"java hadoop\")))"; + //String sql = "SELECT * FROM test where nested(a,a.c=term(seg(\"百度和谷歌\")) and a.c=seg(\"中国\")) and a=\"hadop\""; + //String sql = "SELECT * FROM test where nested(a,a.c=term(seg(\"百度\"))) "; + //String sql = "SELECT * FROM test where nested(a,a.c=seg(\"百度\"))"; + //String sql = "SELECT * FROM test where nested(a,a.c=term(\"中国\"))"; + //String sql = "SELECT * FROM test where a is not null and b=\"xxx\" and nested(a,a.c=term(seg(\"百度\")))"; + //String sql = "SELECT * FROM test where a is not null and b=\"xxx\" and nested(a,a.c=term(seg(\"百度和谷歌\")) and a.c=term(seg(\"hadoop java\")) and a.c=term(seg(\"test\")))"; + //String sql = "SELECT * FROM test where a is not null and b=\"xxx\" and nested(a,a.c=\"abc\") order by nested(a,sum(a.b),a.c=term(seg(\"百度和谷歌\")))"; + //String sql = "SELECT * FROM test where nested(a,a.c=\"abc\") order by nested(a,sum(a.b),a.c=term(seg(\"百度和谷歌\")))"; + //String sql = "SELECT * FROM test where nested(a,a.c=term(seg(\"百度和谷歌\"))) "; + + SqlParseAnalyzer sqlParseAnalyzer = new SqlParseAnalyzer(new AnsjAnalyzerImpl()); + String ret = sqlParseAnalyzer.seg(sql); + System.out.println(ret); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/src/test/java/org/nlpcn/es4sql/SegTest.java b/src/test/java/org/nlpcn/es4sql/SegTest.java new file mode 100644 index 00000000..f2b5d5d5 --- /dev/null +++ b/src/test/java/org/nlpcn/es4sql/SegTest.java @@ -0,0 +1,124 @@ +package org.nlpcn.es4sql; + +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLOrderBy; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock; +import com.alibaba.druid.sql.dialect.mysql.parser.MySqlExprParser; +import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlOutputVisitor; +import org.elasticsearch.plugin.nlpcn.preAnalyzer.AnsjAnalyzerImpl; +import org.elasticsearch.plugin.nlpcn.preAnalyzer.SqlParseAnalyzer; +import org.nlpcn.es4sql.parse.ElasticLexer; +import org.nlpcn.es4sql.parse.ElasticSqlExprParser; +import org.junit.Test; +import java.net.InetAddress; +import java.util.List; + +/** + * Created by fangbb on 2016-12-4. + */ +public class SegTest { + @Test + public void StrTest() { + //String sql = "select * from test where nested(info,info.name = term(seg(\"大数据云计算\")) and info.name=seg(\"python|Hbase|Hive\") or info.name=term(seg(\"Hadoop\"))) and city=seg(\"hah\") and province=\"河北省\" order by nested(info,sum(info.age), info.name=seg(\"java\") and info.name=terms(seg(\"python\")) and info.name=seg(\"Hadoop\")) desc,score desc"; + String sql = "select * from a where name=seg(\"大数据云计算\") and age > 18 order by age desc"; + //System.out.println(sql); + try { + SqlParseAnalyzer sqlParseAnalyzer = new SqlParseAnalyzer(new AnsjAnalyzerImpl()); + sql = sqlParseAnalyzer.seg(sql); + } catch (Exception e) { + System.out.print(e); + } + + System.out.println("-------------"); + System.out.println(sql); + System.out.println("-------------"); + } + + @Test + public void Str() throws Exception { + String ip = InetAddress.getLocalHost().getHostAddress(); + System.out.println(ip); + } + + @Test + public void SqlExprParser() { + try { + String sql = "select * from a where name=seg(\"大数据云计算\") and age > 18 order by age desc"; + ElasticSqlExprParser parser1 = new ElasticSqlExprParser(sql); + SQLExpr expr = parser1.expr(); + SQLQueryExpr sqlExpr = (SQLQueryExpr) expr; + MySqlSelectQueryBlock query = (MySqlSelectQueryBlock) sqlExpr.getSubQuery().getQuery(); + SQLOrderBy orderBy = query.getOrderBy(); + SQLExpr sqlExpr1 = query.getWhere(); + System.out.println("===="); + } catch (Exception e) { + e.printStackTrace(); + } + + } + + @Test + public void StatementParser() { + String sql = "select * from a where name=seg(\"大数据云计算\") and age > 18 order by age desc"; + ElasticLexer lexer = new ElasticLexer(sql); + lexer.nextToken(); + MySqlStatementParser parser = new MySqlStatementParser(lexer); + List statementList = parser.parseStatementList(); + StringBuilder out = new StringBuilder(); + MySqlOutputVisitor visitor = new MySqlOutputVisitor(out); + for (SQLStatement statement : statementList) { + statement.accept(visitor); + //visitor.println(); + } + System.out.println(out.toString()); + } + + @Test + public void myStatementParser() { + String sql = "select * from a-b where name=seg(\"大数据云计算\") and age > 18 order by age desc"; + ElasticLexer lexer = new ElasticLexer(sql); + lexer.nextToken(); + MySqlStatementParser mySqlStatementParser = new MySqlStatementParser(lexer); + + MySqlExprParser mySqlExprParser = mySqlStatementParser.getExprParser(); + SQLExpr expr = mySqlExprParser.expr(); + SQLQueryExpr sqlExpr = (SQLQueryExpr) expr; + MySqlSelectQueryBlock query = (MySqlSelectQueryBlock) sqlExpr.getSubQuery().getQuery(); + + + List statementList = mySqlStatementParser.parseStatementList(); + StringBuilder out = new StringBuilder(); + MySqlOutputVisitor visitor = new MySqlOutputVisitor(out); + for (SQLStatement statement : statementList) { + statement.accept(visitor); + //visitor.println(); + } + System.out.println(out.toString()); + } + + + @Test + public void pdStr() { + String tmp = "hello w o !"; + char[] ch = tmp.toCharArray(); + int idx = ch.length - 1; + int len = 0; + char term; + for (int i = idx; i >= 0; i--) { + term = ch[i]; + if (Character.isAlphabetic(term)) { + len += 1; + } else if (term == ' ' && len != 0) { + break; + } + System.out.println(term); + } + System.out.println("======"); + System.out.println(len); + System.out.println("======"); + } + +}