spuerx преди 9 години
родител
ревизия
44454e1918

+ 47 - 14
README.md

@@ -12,36 +12,70 @@ master | 2.4.4
 
 介绍
 -------------
-> `elasticsearch-query-tookit`是一款elasticsearch查询编程工具包,提供Java编程接口,支持SQL解析生成DSL,支持JDBC驱动,支持和spring、ibatis集成
+> `elasticsearch-query-tookit`是一款基于SQL查询elasticsearch编程工具包,支持SQL解析生成DSL,支持JDBC驱动,支持和Spring、MyBatis集成,提供Java编程接口可基于此工具包二次开发
 
 
 ## 一、SQL解析生成DSL使用示例
 
-
+**[SQL语法搜索戳这里: https://github.com/gitchennan/elasticsearch-query-toolkit/wiki/elasticsearch-query-toolkit-help-doc](https://github.com/gitchennan/elasticsearch-query-toolkit/wiki/elasticsearch-query-toolkit-help-doc)**
 ```java
-String sql = "select * from index.order where status='SUCCESS' order by nvl(pride, 0) asc routing by 'JD' limit 0, 20";
+String sql = "select * from index.order where status='SUCCESS' and price > 100 order by nvl(pride, 0) asc routing by 'JD' limit 0, 20";
 
 ElasticSql2DslParser sql2DslParser = new ElasticSql2DslParser();
 //解析SQL
 ElasticSqlParseResult parseResult = sql2DslParser.parse(sql);
-//生成DSL,可用于rest api调用
+//生成DSL(可用于rest api调用)
 String dsl = parseResult.toDsl();
 
-//toRequest方法接收一个clinet对象参数,用于生成SearchRequestBuilder
+//toRequest方法接收一个clinet对象参数
 SearchRequestBuilder searchReq = parseResult.toRequest(esClient);
 //执行查询
 SearchResponse response = searchReq.execute().actionGet();
 ```
+生成的DSL如下:
+```bash
+{
+  "from" : 0,
+  "size" : 20,
+  "query" : {
+    "bool" : {
+      "filter" : {
+        "bool" : {
+          "must" : [ {
+            "term" : {
+              "status" : "SUCCESS"
+            }
+          }, {
+            "range" : {
+              "price" : {
+                "from" : 100,
+                "to" : null,
+                "include_lower" : false,
+                "include_upper" : true
+              }
+            }
+          } ]
+        }
+      }
+    }
+  },
+  "sort" : [ {
+    "pride" : {
+      "order" : "asc",
+      "missing" : 0
+    }
+  } ]
+}
+```
 
-
-## 二、集成IBatis、Spring
+## 二、集成MyBatis、Spring
 首先在Spring配置文件中增加如下代码
-1. 指定driverClassName:org.elasticsearch.jdbc.ElasticDriver
+1. 指定driverClassName:org.elasticsearch.jdbc.api.ElasticDriver
 2. 指定连接ES的连接串:jdbc:elastic:192.168.0.109:9300/product_cluster
 3. 创建一个SqlMapClient对象,并指定sqlMapConfig.xml路径
 ```bash
-<bean id="elasticDataSource" class="org.elasticsearch.jdbc.ElasticSingleConnectionDataSource" destroy-method="destroy">
-    <property name="driverClassName" value="org.elasticsearch.jdbc.ElasticDriver" />
+<bean id="elasticDataSource" class="org.elasticsearch.jdbc.api.ElasticSingleConnectionDataSource" destroy-method="destroy">
+    <property name="driverClassName" value="org.elasticsearch.jdbc.api.ElasticDriver" />
     <property name="url" value="jdbc:elastic:192.168.0.109:9300/product_cluster" />
 </bean>
 
@@ -101,7 +135,8 @@ public class ProductDao {
         paramMap.put("matchWord", matchWord);
         paramMap.put("prefixWord", matchWord);
         String responseGson = (String) sqlMapClient.queryForObject("PRODUCT.getProductByCodeAndMatchWord", paramMap);
-
+        
+        //反序列化查询结果
         JdbcSearchResponseResolver responseResolver = new JdbcSearchResponseResolver(responseGson);
         JdbcSearchResponse<Product> searchResponse = responseResolver.resolveSearchResponse(Product.class);
 
@@ -115,11 +150,9 @@ public class ProductDao {
 @Test
 public void testProductQuery() throws Exception {
     BeanFactory factory = new ClassPathXmlApplicationContext("application-context.xml");
-
     ProductDao productDao = factory.getBean(ProductDao.class);
-
+    
     List<Product> productList = productDao.getProductByCodeAndMatchWord("iphone 6s", "IP_6S");
-
     for (Product product : productList) {
         System.out.println(product.getProductName());
     }

+ 330 - 0
docs/help.md

@@ -0,0 +1,330 @@
+# elasticsearch-query-toolkit 使用手册
+
+---
+### 一、SQL基本结构
+```bash
+## 搜索请求SQL结构
+SELECT [fields] FROM [index.type] QUERY [score query condition] WHERE [bool query condition] ORDER BY [sort field] LIMIT [0, 10]
+
+## 聚合查询SQL结构
+SELECT [MIN(field),MAX(field),SUM(field),AVG(field)] from [index.type] QUERY [score query condition] WHERE [bool query condition] GROUP BY [agg methods]
+```
+
+以上两种SQL结构分别对应ES的搜索请求和聚合请求,下面大概看看这两种结构,后续会详细介绍。
+1、搜索请求SQL
+
+ - SELECT项,可以是通配符`*` 也可以是具体指定需要返回的字段(针对inner/nested文档直接通过`.`引用)
+ - FROM项,后面跟的是索引名和文档类型如:product.car 表示查询product这个索引下的car类型
+ - QUERY项,关键字QUERY和WHERE是同级的,不同之处是QUERY后面跟的查询条件会进行`打分`
+ - WHERE项,和QUERY同级,后面跟的查询条件不会参与打分,只是进行简单的`bool匹配`
+ - ORDER BY项,跟SQL一样后面跟排序条件
+ - LIMIT 项,指定分页参数对应ES的from,size参数
+
+2、 聚合请求SQL
+
+ - SELECT/FROM/QUERY/WHERE 项同上
+ - GROUP BY项,后面跟的是需要分组查询的条件,如前支持只支持:terms、range
+
+--------
+
+为了能更好的说明SQL语法的使用,我们假设已经存在一个名叫`product`的索引,索引下有一个`apple`的类型,具体的mapping像下面这样,其中provider是一个inner doc表示供应商,buyers是一个nested doc表示购买者。
+```bash
+"mappings": {
+	"apple": {
+		"properties": {
+			"productName": {
+				"type": "string",
+				"index": "not_analyzed"
+			},
+			"productCode": {
+				"type": "string",
+				"index": "not_analyzed"
+			},
+			"minPrice": {
+				"type": "double"
+			},
+			"advicePrice": {
+				"type": "double"
+			},
+			"provider": {
+				"type": "object",
+				"properties": {
+					"providerName": {
+						"type": "string",
+						"index": "not_analyzed"
+					},
+					"providerLevel" : {
+						"type": "integer"
+					}
+				}
+			},
+			"buyers": {
+				"type": "nested",
+				"properties": {
+					"buyerName": {
+						"type": "string",
+						"index": "not_analyzed"
+					},
+					"productPrice": {
+						"type": "double"
+					}
+				}
+			}
+		}
+	}
+}
+```
+
+### 二、搜索请求SQL查询语法
+**注意:由于Inner Doc和Root Doc的对应关系是一对一,Nested Doc和Root Doc的对应关系是多对一,所以为了区分这种关系在引用Nesetd Doc时需要在类型前加上 \$ 符号,如:\$buyers.buyerName(Nested Doc引用需要加 \$ 符号),provider.privideName(Inner Doc直接引用)**
+
+#### 1. SELECT的使用
+```bash
+# 支持通配符
+SELECT * FROM product.apple
+
+# 指定具体返回的字段
+SELECT productName,productCode FROM product.apple
+
+# 对Inner Doc字段的引用直接通过 “.”
+SELECT provider.providerName FROM product.apple
+# 当然也可对Inner Doc字段通配
+SELECT productName,provider.* FROM product.apple
+
+# 对于Nested Doc的引用需要需要在类型名前加上 “$”
+SELECT $buyers.buyerName FROM product.apple
+```
+
+#### 2. WHERE条件的使用
+在SQL中WHERE项后面指定的是bool条件,不会参与打分,这些条件最终会被放到`bool query`中的`filter`中提交给ES指定
+
+##### 1) 基本条件支持 <、>、<=、>=、between...and、in、not in、is null、not null
+```bash
+SELECT * FROM product.apple WHERE minPrice >= 100 and minPrice <= 200
+SELECT * FROM product.apple WHERE minPrice BETWEEN 100 AND 200
+
+SELECT * FROM product.apple WHERE provider.providerLevel in (1, 2, 3) and provider.providerLevel not in (4, 5)
+
+SELECT * FROM product.apple WHERE productName is not null and productCode is null
+```
+
+**注意:上面SQL中`WHERE`可以完全替换成`QUERY`返回的结果一模一样,唯一不同的是`QUERY`会计算得分,`WHERE`只是单纯的bool匹配**
+
+
+##### 2) 内嵌查询patch_context自动识别
+我们都知道在针对nested doc做查询时有专门的nest查询语法,且必须指定一个nested_path才能将多个子查询放到同一个nested query context中,在使用SQL对nested doc查询时不需要显示指定某个或多个条件是存在同一个context中,程序会自动识别,看下面例子:
+
+两个条件同级都是针对内嵌文档的,会被识别存在于同一个neste context里
+```bash
+SELECT * FROM product.apple WHERE $buyers.buyerName = 'usa' and $buyers.productPrice < 200
+{
+  "query" : {
+    "bool" : {
+      "filter" : {
+        "bool" : {
+          "must" : {
+            "nested" : {
+              "query" : {
+                "bool" : {
+                  "must" : [ {
+                    "term" : {
+                      "buyers.buyerName" : "usa"
+                    }
+                  }, {
+                    "range" : {
+                      "buyers.productPrice" : {
+                        "from" : null,
+                        "to" : 200,
+                        "include_lower" : true,
+                        "include_upper" : false
+                      }
+                    }
+                  } ]
+                }
+              },
+              "path" : "buyers"
+            }
+          }
+        }
+      }
+    }
+  }
+}
+```
+两个内嵌条件不同级放到不同nested context里面 
+
+```bash
+SELECT * FROM product.apple WHERE ($buyers.buyerName = 'usa' or minPrice > 100)  and $buyers.productPrice < 200
+
+{
+  "query" : {
+    "bool" : {
+      "filter" : {
+        "bool" : {
+          "must" : [ {
+            "bool" : {
+              "should" : [ {
+                "range" : {
+                  "minPrice" : {
+                    "from" : 100,
+                    "to" : null,
+                    "include_lower" : false,
+                    "include_upper" : true
+                  }
+                }
+              }, {
+                "nested" : {
+                  "query" : {
+                    "term" : {
+                      "buyers.buyerName" : "usa"
+                    }
+                  },
+                  "path" : "buyers"
+                }
+              } ]
+            }
+          }, {
+            "nested" : {
+              "query" : {
+                "range" : {
+                  "buyers.productPrice" : {
+                    "from" : null,
+                    "to" : 200,
+                    "include_lower" : true,
+                    "include_upper" : false
+                  }
+                }
+              },
+              "path" : "buyers"
+            }
+          } ]
+        }
+      }
+    }
+  }
+}
+
+```
+##### 3) Inner Doc查询直接引用即可
+```bash
+SELECT * FROM product.apple WHERE provider.providerLevel in (1, 2, 3)
+SELECT * FROM product.apple WHERE provider.providerName = 'usa'
+```
+##### 4) term 查询
+```bash
+# 在WHERE条件中指定仅仅作为bool查询
+SELECT * FROM product.apple WHERE term(productName, 'iphone6s')
+
+{
+  "query" : {
+    "bool" : {
+      "filter" : {
+        "bool" : {
+          "must" : {
+            "term" : {
+              "productName" : "iphone6s"
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+# 在QUERY条件中指定根据文档打分排序,并能指定可选参数, 指定权重
+SELECT * FROM product.apple QUERY term(productName, 'iphone6s', 'boost:2.0f')
+{
+  "query" : {
+    "bool" : {
+      "must" : {
+        "term" : {
+          "productName" : {
+            "value" : "iphone6s",
+            "boost" : 2.0
+          }
+        }
+      }
+    }
+  }
+}
+```
+**注意1:这里的可选参数是一个以key:value组成的键值对,多个键值对之间用逗号隔开**
+**注意2:terms查询和term类似,这里不再赘述**
+**注意3:term查询其实和SQL中的 “=” 操作一样**
+
+##### 5) match query使用
+```bash
+SELECT * FROM product.apple QUERY match(productName, 'iphone6s', 'boost:2.0f,type:boolean,operator:or,minimum_should_match:75%') WHERE minPrice > 100
+
+{
+  "query" : {
+    "bool" : {
+      "must" : {
+        "match" : {
+          "productName" : {
+            "query" : "iphone6s",
+            "type" : "boolean",
+            "operator" : "OR",
+            "boost" : 2.0,
+            "minimum_should_match" : "75%"
+          }
+        }
+      },
+      "filter" : {
+        "bool" : {
+          "must" : {
+            "range" : {
+              "minPrice" : {
+                "from" : 100,
+                "to" : null,
+                "include_lower" : false,
+                "include_upper" : true
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
+```
+**注意1:这里使用了QUERY和WHERE两种条件配合使用, WHERE达到过滤的效果, QUERY在过滤结果基础上再进行match匹配**
+**注意2: match查询所有的可选参数请参看官网的参数说明:https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-match-query.html**
+**注意3:match查询支持多个函数名:match、match_query、matchQuery**
+
+##### 6) multi match使用
+```bash
+SELECT * FROM product.apple QUERY multiMatch('productName^3, productCode', 'iphone6s') WHERE minPrice > 100
+
+{
+  "query" : {
+    "bool" : {
+      "must" : {
+        "multi_match" : {
+          "query" : "iphone6s",
+          "fields" : [ "productName^3", " productCode" ]
+        }
+      },
+      "filter" : {
+        "bool" : {
+          "must" : {
+            "range" : {
+              "minPrice" : {
+                "from" : 100,
+                "to" : null,
+                "include_lower" : false,
+                "include_upper" : true
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
+```
+
+##### 7) 
+```bash
+SELECT * FROM product.apple QUERY  WHERE minPrice > 100
+```

+ 9 - 3
src/main/java/org/elasticsearch/dsl/helper/ElasticSqlArgTransferHelper.java

@@ -29,30 +29,36 @@ public class ElasticSqlArgTransferHelper {
             if (varRefExpr.getIndex() >= sqlArgs.length) {
                 throw new ElasticSql2DslException("[syntax error] Sql args out of index: " + varRefExpr.getIndex());
             }
-            //解析date类型
+            //parse date
             if (recognizeDateArg && ElasticSqlDateParseHelper.isDateArgObjectValue(sqlArgs[varRefExpr.getIndex()])) {
                 return ElasticSqlDateParseHelper.formatDefaultEsDateObjectValue(sqlArgs[varRefExpr.getIndex()]);
             }
             return sqlArgs[varRefExpr.getIndex()];
         }
+
+        //numbers
         if (expr instanceof SQLIntegerExpr) {
             return ((SQLIntegerExpr) expr).getNumber().longValue();
         }
         if (expr instanceof SQLNumberExpr) {
             return ((SQLNumberExpr) expr).getNumber().doubleValue();
         }
+
+        //string
         if (expr instanceof SQLCharExpr) {
             Object textObject = ((SQLCharExpr) expr).getValue();
-            //解析date类型
+            //parse date
             if (recognizeDateArg && (textObject instanceof String) && ElasticSqlDateParseHelper.isDateArgStringValue((String) textObject)) {
                 return ElasticSqlDateParseHelper.formatDefaultEsDateStringValue((String) textObject);
             }
             return textObject;
         }
+
+        //method call
         if (expr instanceof SQLMethodInvokeExpr) {
             SQLMethodInvokeExpr methodExpr = (SQLMethodInvokeExpr) expr;
 
-            //解析date函数
+            //parse date method
             if (ElasticSqlDateParseHelper.isDateMethod(methodExpr)) {
                 ElasticSqlMethodInvokeHelper.checkDateMethod(methodExpr);
                 String patternArg = (String) ElasticSqlArgTransferHelper.transferSqlArg(methodExpr.getParameters().get(0), sqlArgs, false);

+ 1 - 1
src/main/java/org/elasticsearch/dsl/parser/query/method/AbstractAtomMethodQueryParser.java

@@ -82,7 +82,7 @@ public abstract class AbstractAtomMethodQueryParser {
                 extraParamMap.put(paramPairArr[0].trim(), paramPairArr[1].trim());
             }
             else {
-                throw new ElasticSql2DslException("[fulltext_query] Fulltext match query method param format error!");
+                throw new ElasticSql2DslException("Failed to parse query method params!");
             }
         }
         return extraParamMap;

+ 1 - 1
src/main/java/org/elasticsearch/dsl/parser/query/method/fulltext/SimpleQueryStringAtomQueryParser.java

@@ -34,7 +34,7 @@ public class SimpleQueryStringAtomQueryParser extends AbstractAtomMethodQueryPar
 
     @Override
     protected void checkQueryMethod(SQLMethodInvokeExpr methodQueryExpr, String queryAs, Object[] sqlArgs) {
-        if (Boolean.FALSE == "simpleQueryString".equalsIgnoreCase(methodQueryExpr.getMethodName())) {
+        if (Boolean.FALSE == isSimpleQueryStringQuery(methodQueryExpr)) {
             throw new ElasticSql2DslException(String.format("[syntax error] Expected simpleQueryString query method name is [simpleQueryString],but get [%s]", methodQueryExpr.getMethodName()));
         }
 

+ 21 - 1
src/main/java/org/elasticsearch/dsl/parser/query/method/script/ScriptAtomQueryParser.java

@@ -3,7 +3,9 @@ package org.elasticsearch.dsl.parser.query.method.script;
 import com.alibaba.druid.sql.ast.SQLExpr;
 import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
 import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.math.NumberUtils;
 import org.elasticsearch.dsl.bean.AtomQuery;
 import org.elasticsearch.dsl.exception.ElasticSql2DslException;
 import org.elasticsearch.dsl.helper.ElasticSqlArgTransferHelper;
@@ -12,8 +14,10 @@ import org.elasticsearch.dsl.listener.ParseActionListener;
 import org.elasticsearch.dsl.parser.query.method.AbstractAtomMethodQueryParser;
 import org.elasticsearch.index.query.QueryBuilders;
 import org.elasticsearch.script.Script;
+import org.elasticsearch.script.ScriptService;
 
 import java.util.List;
+import java.util.Map;
 
 public class ScriptAtomQueryParser extends AbstractAtomMethodQueryParser {
 
@@ -34,7 +38,7 @@ public class ScriptAtomQueryParser extends AbstractAtomMethodQueryParser {
         }
 
         int paramCount = methodQueryExpr.getParameters().size();
-        if (paramCount != 1) {
+        if (paramCount != 1 && paramCount != 2) {
             throw new ElasticSql2DslException(String.format("[syntax error] There's no %s args method: fuzzy", paramCount));
         }
 
@@ -48,6 +52,22 @@ public class ScriptAtomQueryParser extends AbstractAtomMethodQueryParser {
     @Override
     protected AtomQuery parseMethodQueryExpr(SQLMethodInvokeExpr methodQueryExpr, String queryAs, Object[] sqlArgs) {
         String script = (String) ElasticSqlArgTransferHelper.transferSqlArg(methodQueryExpr.getParameters().get(0), sqlArgs, false);
+        int paramCount = methodQueryExpr.getParameters().size();
+        if (paramCount == 2) {
+            String scriptParams = (String) ElasticSqlArgTransferHelper.transferSqlArg(methodQueryExpr.getParameters().get(1), sqlArgs, false);
+            Map<String, Object> scriptParamMap = buildScriptParamMap(scriptParams);
+            return new AtomQuery(QueryBuilders.scriptQuery(new Script(script, ScriptService.ScriptType.INLINE, null, scriptParamMap)));
+        }
         return new AtomQuery(QueryBuilders.scriptQuery(new Script(script)));
     }
+
+    private Map<String, Object> buildScriptParamMap(String scriptStrParams) {
+        Map<String, String> scriptStrParamMap = buildExtraMethodQueryParamsMap(scriptStrParams);
+        return Maps.transformEntries(scriptStrParamMap, new Maps.EntryTransformer<String, String, Object>() {
+            @Override
+            public Object transformEntry(String key, String value) {
+                return NumberUtils.isNumber(value) ? NumberUtils.createNumber(value) : value;
+            }
+        });
+    }
 }

+ 1 - 1
src/main/java/org/elasticsearch/dsl/parser/query/method/term/PrefixAtomQueryParser.java

@@ -33,7 +33,7 @@ public class PrefixAtomQueryParser extends AbstractAtomMethodQueryParser {
 
     @Override
     protected void checkQueryMethod(SQLMethodInvokeExpr methodQueryExpr, String queryAs, Object[] sqlArgs) {
-        if (Boolean.FALSE == "prefix".equalsIgnoreCase(methodQueryExpr.getMethodName())) {
+        if (Boolean.FALSE == isPrefixQuery(methodQueryExpr)) {
             throw new ElasticSql2DslException(String.format("[syntax error] Expected prefix query method name is [prefix],but get [%s]", methodQueryExpr.getMethodName()));
         }
 

+ 16 - 5
src/main/java/org/elasticsearch/dsl/parser/sql/QueryGroupByParser.java

@@ -67,8 +67,11 @@ public class QueryGroupByParser implements QueryParser {
                     ElasticSqlMethodInvokeHelper.checkTermsAggMethod(aggMethodExpr);
 
                     SQLExpr termsFieldExpr = aggMethodExpr.getParameters().get(0);
-
-                    AggregationBuilder termsBuilder = parseTermsAggregation(queryAs, termsFieldExpr);
+                    SQLExpr shardSizeExpr = null;
+                    if (aggMethodExpr.getParameters().size() == 2) {
+                        shardSizeExpr = aggMethodExpr.getParameters().get(1);
+                    }
+                    AggregationBuilder termsBuilder = parseTermsAggregation(queryAs, dslContext.getSqlArgs(), termsFieldExpr, shardSizeExpr);
                     aggregationList.add(termsBuilder);
                 }
 
@@ -89,7 +92,7 @@ public class QueryGroupByParser implements QueryParser {
 
     }
 
-    private AggregationBuilder parseTermsAggregation(String queryAs, SQLExpr termsFieldExpr) {
+    private AggregationBuilder parseTermsAggregation(String queryAs, Object[] args, SQLExpr termsFieldExpr, SQLExpr shardSizeExpr) {
         QueryFieldParser queryFieldParser = new QueryFieldParser();
 
         ElasticSqlQueryField queryField = queryFieldParser.parseConditionQueryField(termsFieldExpr, queryAs);
@@ -97,6 +100,10 @@ public class QueryGroupByParser implements QueryParser {
             throw new ElasticSql2DslException(String.format("[syntax error] can not support terms aggregation for field type[%s]", queryField.getQueryFieldType()));
         }
 
+        if(shardSizeExpr != null) {
+            Number termBuckets = (Number) ElasticSqlArgTransferHelper.transferSqlArg(shardSizeExpr, args);
+            return createTermsBuilder(queryField.getQueryFieldFullName(), termBuckets.intValue());
+        }
         return createTermsBuilder(queryField.getQueryFieldFullName());
     }
 
@@ -128,11 +135,15 @@ public class QueryGroupByParser implements QueryParser {
         return rangeSegmentList;
     }
 
-    private TermsBuilder createTermsBuilder(String termsFieldName) {
+    private TermsBuilder createTermsBuilder(String termsFieldName, int termBuckets) {
         return AggregationBuilders.terms(AGG_BUCKET_KEY_PREFIX + termsFieldName)
                 .field(termsFieldName)
                 .minDocCount(1).shardMinDocCount(1)
-                .shardSize(MAX_GROUP_BY_SIZE << 1).size(MAX_GROUP_BY_SIZE).order(Terms.Order.count(false));
+                .shardSize(termBuckets << 1).size(termBuckets).order(Terms.Order.count(false));
+    }
+
+    private TermsBuilder createTermsBuilder(String termsFieldName) {
+        return createTermsBuilder(termsFieldName, MAX_GROUP_BY_SIZE);
     }
 
     private AbstractRangeBuilder createRangeBuilder(String rangeFieldName, List<RangeSegment> rangeSegments) {

+ 1 - 0
src/main/java/org/elasticsearch/jdbc/es/JdbcSearchActionExecutor.java

@@ -41,6 +41,7 @@ public class JdbcSearchActionExecutor {
         return new ActionListener<Response>() {
             @Override
             public void onResponse(Response response) {
+                //System.out.println(requestBuilder.toString());
                 Logger.debug(this, String.format("[Search_Request] %s", requestBuilder.toString()));
                 Logger.debug(this, String.format("[Search_Response] %s", response.toString()));
             }

+ 2 - 2
src/main/java/org/elasticsearch/jdbc/es/JdbcSearchResponseResolver.java

@@ -92,7 +92,7 @@ public class JdbcSearchResponseResolver {
             });
         }
         catch (Exception ex) {
-            throw new ResolveSearchResponseException("Failed to resolve gson from response");
+            throw new ResolveSearchResponseException("Failed to resolve gson from response", ex);
         }
         return resolvedList;
     }
@@ -103,7 +103,7 @@ public class JdbcSearchResponseResolver {
             searchRespStrGson = new Gson().fromJson(oriSearchResponseGson, new TypeToken<JdbcSearchResponse<String>>() {}.getType());
         }
         catch (Exception exp) {
-            throw new ResolveSearchResponseException(String.format("Failed to parse responseGson[%s] to JdbcSearchResponse", oriSearchResponseGson));
+            throw new ResolveSearchResponseException(String.format("Failed to parse responseGson[%s] to JdbcSearchResponse", oriSearchResponseGson), exp);
         }
         return searchRespStrGson;
     }

+ 1 - 67
src/test/java/org/elasticsearch/jdbc/ElasticDriverTest.java

@@ -4,7 +4,6 @@ package org.elasticsearch.jdbc;
 import org.elasticsearch.jdbc.api.ElasticConnection;
 import org.elasticsearch.jdbc.api.ElasticSingleConnectionDataSource;
 import org.elasticsearch.jdbc.bean.Product;
-import org.elasticsearch.jdbc.bean.ProductAggResult;
 import org.elasticsearch.jdbc.es.JdbcSearchResponse;
 import org.elasticsearch.jdbc.es.JdbcSearchResponseResolver;
 import org.junit.Test;
@@ -14,7 +13,7 @@ import java.util.Enumeration;
 
 public class ElasticDriverTest {
     private static final String driver = "org.elasticsearch.jdbc.api.ElasticDriver";
-    private static final String url = "jdbc:elastic:192.168.0.109:9300/judge_cluster";
+    private static final String url = "jdbc:elastic:192.168.0.109:9300/lu-search-cluster";
 
     @Test
     public void testLoadDriver() throws Exception {
@@ -85,69 +84,4 @@ public class ElasticDriverTest {
             System.out.println(product.getProductName());
         }
     }
-
-    @Test
-    public void testPrefixAndNestedQuery() throws Exception {
-        ElasticSingleConnectionDataSource dataSource = new ElasticSingleConnectionDataSource(url, true);
-        dataSource.setDriverClassName(driver);
-
-        Connection connection = dataSource.getConnection();
-        String sql = "select * from index.product where prefix(productName, 'iphone') and $buyers.productPrice > 1000";
-
-        PreparedStatement preparedStatement = connection.prepareStatement(sql);
-        ResultSet resultSet = preparedStatement.executeQuery();
-
-        String responseGson = resultSet.getString(1);
-        JdbcSearchResponseResolver jdbcSearchResponseResolver = new JdbcSearchResponseResolver(responseGson);
-        JdbcSearchResponse<Product> jdbcSearchResponse = jdbcSearchResponseResolver.resolveSearchResponse(Product.class);
-
-        for (Product product : jdbcSearchResponse.getResultList()) {
-            System.out.println(product.getProductName());
-        }
-    }
-
-    @Test
-    public void testGroupBy() throws Exception {
-        ElasticSingleConnectionDataSource dataSource = new ElasticSingleConnectionDataSource(url, true);
-        dataSource.setDriverClassName(driver);
-
-        Connection connection = dataSource.getConnection();
-        String sql = "select min(advicePrice),max(provider.providerLevel) from index.product group by terms(productCode)";
-
-        PreparedStatement preparedStatement = connection.prepareStatement(sql);
-        ResultSet resultSet = preparedStatement.executeQuery();
-
-        String responseGson = resultSet.getString(1);
-
-        JdbcSearchResponseResolver jdbcSearchResponseResolver = new JdbcSearchResponseResolver(responseGson);
-        JdbcSearchResponse<ProductAggResult> jdbcSearchResponse = jdbcSearchResponseResolver.resolveSearchResponse(ProductAggResult.class);
-
-        System.out.println("resp total count: " + jdbcSearchResponse.getTotalCount());
-        for (ProductAggResult aggItem : jdbcSearchResponse.getResultList()) {
-            System.out.println(String.format("code:%s, count:%s, minPrice:%s, providerLevel:%s",
-                    aggItem.getProductCode(), aggItem.getDocCount(), aggItem.getMinAdvicePrice(), aggItem.getProviderLevel()));
-        }
-    }
-
-
-    @Test
-    public void testScriptQuery() throws Exception {
-        ElasticSingleConnectionDataSource dataSource = new ElasticSingleConnectionDataSource(url, true);
-        dataSource.setDriverClassName(driver);
-
-        Connection connection = dataSource.getConnection();
-        String script = "if(doc[\"advicePrice\"].empty) return true; if(doc[\"minPrice\"].value/doc[\"advicePrice\"].value > 0.363) return true; else return false;";
-        String sql = String.format("select * from index.product where script_query('%s')", script);
-
-        PreparedStatement preparedStatement = connection.prepareStatement(sql);
-        ResultSet resultSet = preparedStatement.executeQuery();
-
-        String responseGson = resultSet.getString(1);
-        JdbcSearchResponseResolver jdbcSearchResponseResolver = new JdbcSearchResponseResolver(responseGson);
-        JdbcSearchResponse<Product> jdbcSearchResponse = jdbcSearchResponseResolver.resolveSearchResponse(Product.class);
-
-        for (Product product : jdbcSearchResponse.getResultList()) {
-            System.out.println(String.format("productName:%s, minPrice:%s, advicePrice:%s", product.getProductName(), product.getMinPrice(), product.getAdvicePrice()));
-        }
-    }
 }

+ 40 - 0
src/test/java/org/elasticsearch/jdbc/GroupByTest.java

@@ -0,0 +1,40 @@
+package org.elasticsearch.jdbc;
+
+
+import org.elasticsearch.jdbc.api.ElasticSingleConnectionDataSource;
+import org.elasticsearch.jdbc.bean.ProductAggResult;
+import org.elasticsearch.jdbc.es.JdbcSearchResponse;
+import org.elasticsearch.jdbc.es.JdbcSearchResponseResolver;
+import org.junit.Test;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+
+public class GroupByTest {
+    private static final String driver = "org.elasticsearch.jdbc.api.ElasticDriver";
+    private static final String url = "jdbc:elastic:192.168.0.109:9300/lu-search-cluster";
+
+    @Test
+    public void testGroupBy() throws Exception {
+        ElasticSingleConnectionDataSource dataSource = new ElasticSingleConnectionDataSource(url, true);
+        dataSource.setDriverClassName(driver);
+
+        Connection connection = dataSource.getConnection();
+        String sql = "select min(advicePrice),max(provider.providerLevel) from index.product group by terms(productCode, 200)";
+
+        PreparedStatement preparedStatement = connection.prepareStatement(sql);
+        ResultSet resultSet = preparedStatement.executeQuery();
+
+        String responseGson = resultSet.getString(1);
+
+        JdbcSearchResponseResolver jdbcSearchResponseResolver = new JdbcSearchResponseResolver(responseGson);
+        JdbcSearchResponse<ProductAggResult> jdbcSearchResponse = jdbcSearchResponseResolver.resolveSearchResponse(ProductAggResult.class);
+
+        System.out.println("resp total count: " + jdbcSearchResponse.getTotalCount());
+        for (ProductAggResult aggItem : jdbcSearchResponse.getResultList()) {
+            System.out.println(String.format("code:%s, count:%s, minPrice:%s, providerLevel:%s",
+                    aggItem.getProductCode(), aggItem.getDocCount(), aggItem.getMinAdvicePrice(), aggItem.getProviderLevel()));
+        }
+    }
+}

+ 37 - 0
src/test/java/org/elasticsearch/jdbc/MethodQueryTest.java

@@ -0,0 +1,37 @@
+package org.elasticsearch.jdbc;
+
+
+import org.elasticsearch.jdbc.api.ElasticSingleConnectionDataSource;
+import org.elasticsearch.jdbc.bean.Product;
+import org.elasticsearch.jdbc.es.JdbcSearchResponse;
+import org.elasticsearch.jdbc.es.JdbcSearchResponseResolver;
+import org.junit.Test;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+
+public class MethodQueryTest {
+    private static final String driver = "org.elasticsearch.jdbc.api.ElasticDriver";
+    private static final String url = "jdbc:elastic:192.168.0.109:9300/lu-search-cluster";
+
+    @Test
+    public void testPrefixAndNestedQuery() throws Exception {
+        ElasticSingleConnectionDataSource dataSource = new ElasticSingleConnectionDataSource(url, true);
+        dataSource.setDriverClassName(driver);
+
+        Connection connection = dataSource.getConnection();
+        String sql = "select * from index.product where prefix(productName, 'iphone') and $buyers.productPrice > 1000";
+
+        PreparedStatement preparedStatement = connection.prepareStatement(sql);
+        ResultSet resultSet = preparedStatement.executeQuery();
+
+        String responseGson = resultSet.getString(1);
+        JdbcSearchResponseResolver jdbcSearchResponseResolver = new JdbcSearchResponseResolver(responseGson);
+        JdbcSearchResponse<Product> jdbcSearchResponse = jdbcSearchResponseResolver.resolveSearchResponse(Product.class);
+
+        for (Product product : jdbcSearchResponse.getResultList()) {
+            System.out.println(product.getProductName());
+        }
+    }
+}

+ 48 - 0
src/test/java/org/elasticsearch/jdbc/ScriptQueryTest.java

@@ -0,0 +1,48 @@
+package org.elasticsearch.jdbc;
+
+
+import org.elasticsearch.dsl.bean.ElasticSqlParseResult;
+import org.elasticsearch.dsl.parser.ElasticSql2DslParser;
+import org.elasticsearch.jdbc.api.ElasticSingleConnectionDataSource;
+import org.elasticsearch.jdbc.bean.Product;
+import org.elasticsearch.jdbc.es.JdbcSearchResponse;
+import org.elasticsearch.jdbc.es.JdbcSearchResponseResolver;
+import org.junit.Test;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+
+public class ScriptQueryTest {
+    private static final String driver = "org.elasticsearch.jdbc.api.ElasticDriver";
+    private static final String url = "jdbc:elastic:192.168.0.109:9300/lu-search-cluster";
+
+    @Test
+    public void testScriptQuery() throws Exception {
+        ElasticSingleConnectionDataSource dataSource = new ElasticSingleConnectionDataSource(url, true);
+        dataSource.setDriverClassName(driver);
+
+        Connection connection = dataSource.getConnection();
+
+        String script = "if(doc[\"advicePrice\"].empty) return false; if(my_var * doc[\"minPrice\"].value/doc[\"advicePrice\"].value > 0.363) return true; else return false;";
+        String sql = String.format("select * from index.product where script_query('%s', 'my_var:2.1f')", script);
+
+        PreparedStatement preparedStatement = connection.prepareStatement(sql);
+        ResultSet resultSet = preparedStatement.executeQuery();
+
+        String responseGson = resultSet.getString(1);
+        JdbcSearchResponseResolver jdbcSearchResponseResolver = new JdbcSearchResponseResolver(responseGson);
+        JdbcSearchResponse<Product> jdbcSearchResponse = jdbcSearchResponseResolver.resolveSearchResponse(Product.class);
+
+        for (Product product : jdbcSearchResponse.getResultList()) {
+            System.out.println(String.format("productName:%s, minPrice:%s, advicePrice:%s", product.getProductName(), product.getMinPrice(), product.getAdvicePrice()));
+        }
+
+
+//        sql = "";
+//        ElasticSql2DslParser sql2DslParser = new ElasticSql2DslParser();
+//        ElasticSqlParseResult parseResult = sql2DslParser.parse(sql);
+//        String dsl = parseResult.toDsl();
+//        System.out.println(dsl);
+    }
+}

+ 2 - 0
src/test/java/org/elasticsearch/query/SqlParserWhereConditionTest.java

@@ -12,6 +12,7 @@ import org.elasticsearch.search.aggregations.AggregationBuilders;
 import org.elasticsearch.search.aggregations.bucket.terms.TermsBuilder;
 import org.elasticsearch.utils.ElasticMockClient;
 import org.junit.Assert;
+import org.junit.Ignore;
 import org.junit.Test;
 
 
@@ -156,6 +157,7 @@ public class SqlParserWhereConditionTest {
     }
 
     @Test
+    @Ignore
     public void testCreateAggDsl() {
         SearchRequestBuilder searchReq = new SearchRequestBuilder(new ElasticMockClient(), SearchAction.INSTANCE);
         searchReq.setSize(0);