Skip to content

Commit 67f450d

Browse files
committed
nested types aggregation support
1 parent 212c0b8 commit 67f450d

File tree

7 files changed

+147
-31
lines changed

7 files changed

+147
-31
lines changed

src/main/java/org/nlpcn/es4sql/domain/Field.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ public class Field implements Cloneable{
1010

1111
protected String name;
1212
private String alias;
13+
private boolean isNested;
14+
private String nestedPath;
1315

1416
public Field(String name, String alias) {
1517
this.name = name;
@@ -32,7 +34,23 @@ public void setAlias(String alias) {
3234
this.alias = alias;
3335
}
3436

35-
@Override
37+
public boolean isNested() {
38+
return isNested;
39+
}
40+
41+
public void setNested(boolean isNested) {
42+
this.isNested = isNested;
43+
}
44+
45+
public String getNestedPath() {
46+
return nestedPath;
47+
}
48+
49+
public void setNestedPath(String nestedPath) {
50+
this.nestedPath = nestedPath;
51+
}
52+
53+
@Override
3654
public String toString() {
3755
return this.name;
3856
}

src/main/java/org/nlpcn/es4sql/parse/FieldMaker.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ public static Field makeField(SQLExpr expr, String alias,String tableAlias) thro
3030
} else if (expr instanceof SQLAllColumnExpr) {
3131
} else if (expr instanceof SQLMethodInvokeExpr) {
3232
SQLMethodInvokeExpr mExpr = (SQLMethodInvokeExpr) expr;
33+
NestedType nestedType = new NestedType();
34+
if(nestedType.tryFillFromExpr(mExpr)){
35+
return handleIdentifier(nestedType,alias,tableAlias);
36+
}
3337
return makeMethodField(mExpr.getMethodName(), mExpr.getParameters(), null, alias);
3438
} else if (expr instanceof SQLAggregateExpr) {
3539
SQLAggregateExpr sExpr = (SQLAggregateExpr) expr;
@@ -40,6 +44,13 @@ public static Field makeField(SQLExpr expr, String alias,String tableAlias) thro
4044
return null;
4145
}
4246

47+
private static Field handleIdentifier(NestedType nestedType, String alias, String tableAlias) {
48+
Field field = handleIdentifier(new SQLIdentifierExpr(nestedType.field), alias, tableAlias);
49+
field.setNested(true);
50+
field.setNestedPath(nestedType.path);
51+
return field;
52+
}
53+
4354
private static Field makeScriptMethodField(SQLBinaryOpExpr binaryExpr, String alias) throws SqlParseException {
4455
List<SQLExpr> params = new ArrayList<>();
4556

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,39 @@
11
package org.nlpcn.es4sql.parse;
22

3+
import com.alibaba.druid.sql.ast.SQLExpr;
4+
import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;
5+
import org.nlpcn.es4sql.exception.SqlParseException;
6+
7+
import java.util.List;
8+
39
/**
410
* Created by Eliran on 12/11/2015.
511
*/
612
public class NestedType {
713
public String field;
814
public String path;
15+
16+
public boolean tryFillFromExpr(SQLExpr expr) throws SqlParseException {
17+
if (!(expr instanceof SQLMethodInvokeExpr)) return false;
18+
SQLMethodInvokeExpr method = (SQLMethodInvokeExpr) expr;
19+
if (!method.getMethodName().toLowerCase().equals("nested")) return false;
20+
21+
List<SQLExpr> parameters = method.getParameters();
22+
if (parameters.size() != 2 && parameters.size() != 1)
23+
throw new SqlParseException("on nested object only allowed 2 parameters (field,path) or 1 parameter (field) ");
24+
25+
String field = parameters.get(0).toString();
26+
this.field = field;
27+
if (parameters.size() == 1) {
28+
//calc path myself..
29+
if (!field.contains("."))
30+
throw new SqlParseException("nested should contain . on their field name");
31+
int lastDot = field.lastIndexOf(".");
32+
this.path = field.substring(0, lastDot);
33+
} else if (parameters.size() == 2) {
34+
this.path = parameters.get(1).toString();
35+
}
36+
37+
return true;
38+
}
939
}

src/main/java/org/nlpcn/es4sql/parse/SqlParser.java

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ private void explanCond(String opear, SQLExpr expr, Where where) throws SqlParse
132132
boolean nestedFieldCondition = false;
133133
String nestedPath = null ;
134134
NestedType nestedType = new NestedType();
135-
if(tryFillNested(soExpr.getLeft(),nestedType)){
135+
if(nestedType.tryFillFromExpr(soExpr.getLeft())){
136136
soExpr.setLeft(new SQLIdentifierExpr(nestedType.field));
137137
nestedFieldCondition = true;
138138
nestedPath = nestedType.path ;
@@ -157,7 +157,7 @@ private void explanCond(String opear, SQLExpr expr, Where where) throws SqlParse
157157
SQLInListExpr siExpr = (SQLInListExpr) expr;
158158
NestedType nestedType = new NestedType();
159159
String leftSide = siExpr.getExpr().toString();
160-
if(tryFillNested(siExpr.getExpr(),nestedType)){
160+
if(nestedType.tryFillFromExpr(siExpr.getExpr())){
161161
leftSide = nestedType.field;
162162
}
163163
Condition condition = new Condition(CONN.valueOf(opear), leftSide, siExpr.isNot() ? "NOT IN" : "IN", parseValue(siExpr.getTargetList()),nestedType.field!=null,nestedType.path);
@@ -166,7 +166,7 @@ private void explanCond(String opear, SQLExpr expr, Where where) throws SqlParse
166166
SQLBetweenExpr between = ((SQLBetweenExpr) expr);
167167
String leftSide = between.getTestExpr().toString();
168168
NestedType nestedType = new NestedType();
169-
if(tryFillNested(between.getTestExpr(),nestedType)){
169+
if(nestedType.tryFillFromExpr(between.getTestExpr())){
170170
leftSide = nestedType.field;
171171
}
172172
Condition condition = new Condition(CONN.valueOf(opear), leftSide, between.isNot() ? "NOT BETWEEN" : "BETWEEN", new Object[]{parseValue(between.beginExpr),
@@ -180,7 +180,7 @@ else if (expr instanceof SQLMethodInvokeExpr) {
180180
String methodName = methodExpr.getMethodName();
181181
String fieldName = methodParameters.get(0).toString();
182182
NestedType nestedType = new NestedType();
183-
if(tryFillNested(methodParameters.get(0),nestedType)){
183+
if(nestedType.tryFillFromExpr(methodParameters.get(0))){
184184
fieldName = nestedType.field;
185185
}
186186

@@ -196,7 +196,7 @@ else if (expr instanceof SQLMethodInvokeExpr) {
196196
SubQueryExpression subQueryExpression = new SubQueryExpression(innerSelect);
197197
String leftSide = sqlIn.getExpr().toString();
198198
NestedType nestedType = new NestedType();
199-
if(tryFillNested(sqlIn.getExpr(),nestedType)){
199+
if(nestedType.tryFillFromExpr(sqlIn.getExpr())){
200200
leftSide = nestedType.field;
201201
}
202202
Condition condition = new Condition(CONN.valueOf(opear), leftSide, sqlIn.isNot() ? "NOT IN" : "IN",subQueryExpression,nestedType.field!=null,nestedType.path);
@@ -206,29 +206,7 @@ else if (expr instanceof SQLMethodInvokeExpr) {
206206
}
207207
}
208208

209-
private boolean tryFillNested(SQLExpr expr,NestedType nestedType) throws SqlParseException {
210-
if (!(expr instanceof SQLMethodInvokeExpr)) return false;
211-
SQLMethodInvokeExpr method = (SQLMethodInvokeExpr) expr;
212-
if (!method.getMethodName().toLowerCase().equals("nested")) return false;
213-
214-
List<SQLExpr> parameters = method.getParameters();
215-
if (parameters.size() != 2 && parameters.size() != 1)
216-
throw new SqlParseException("on nested object only allowed 2 parameters (field,path) or 1 parameter (field) ");
217-
218-
String field = parameters.get(0).toString();
219-
nestedType.field = field;
220-
if (parameters.size() == 1) {
221-
//calc path myself..
222-
if (!field.contains("."))
223-
throw new SqlParseException("nested should contain . on their field name");
224-
int lastDot = field.lastIndexOf(".");
225-
nestedType.path = field.substring(0, lastDot);
226-
} else if (parameters.size() == 2) {
227-
nestedType.path = parameters.get(1).toString();
228-
}
229209

230-
return true;
231-
}
232210

233211
private Object[] getMethodValuesWithSubQueries(SQLMethodInvokeExpr method) throws SqlParseException {
234212
List<Object> values = new ArrayList<>();

src/main/java/org/nlpcn/es4sql/query/AggregationQueryAction.java

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
import org.elasticsearch.index.query.QueryBuilders;
1212
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
1313
import org.elasticsearch.search.aggregations.AggregationBuilder;
14+
import org.elasticsearch.search.aggregations.AggregationBuilders;
15+
import org.elasticsearch.search.aggregations.bucket.nested.NestedBuilder;
1416
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
1517
import org.elasticsearch.search.aggregations.bucket.terms.TermsBuilder;
1618
import org.elasticsearch.search.sort.SortOrder;
@@ -56,7 +58,16 @@ public SqlElasticSearchRequestBuilder explain() throws SqlParseException {
5658
((TermsBuilder) lastAgg).size(select.getRowCount());
5759
}
5860

59-
request.addAggregation(lastAgg);
61+
if(field.isNested()){
62+
NestedBuilder nestedBuilder = AggregationBuilders.nested(field.getName() + "Nested")
63+
.path(field.getNestedPath())
64+
.subAggregation(lastAgg);
65+
request.addAggregation(nestedBuilder);
66+
}
67+
else {
68+
request.addAggregation(lastAgg);
69+
}
70+
6071

6172
for (int i = 1; i < groupBy.size(); i++) {
6273
field = groupBy.get(i);
@@ -65,7 +76,17 @@ public SqlElasticSearchRequestBuilder explain() throws SqlParseException {
6576
((TermsBuilder) subAgg).size(0);
6677
}
6778

68-
lastAgg.subAggregation(subAgg);
79+
if(field.isNested()){
80+
NestedBuilder nestedBuilder = AggregationBuilders.nested(field.getName() + "Nested")
81+
.path(field.getNestedPath())
82+
.subAggregation(subAgg);
83+
lastAgg.subAggregation(nestedBuilder);
84+
85+
}
86+
else {
87+
lastAgg.subAggregation(subAgg);
88+
}
89+
6990
lastAgg = subAgg;
7091
}
7192
}
@@ -111,7 +132,16 @@ public SqlElasticSearchRequestBuilder explain() throws SqlParseException {
111132
return sqlElasticRequestBuilder;
112133
}
113134

114-
private boolean isASC(Order order) {
135+
private AggregationBuilder<?> updateAggIfNested(AggregationBuilder<?> lastAgg, Field field) {
136+
if(field.isNested()){
137+
lastAgg = AggregationBuilders.nested(field.getName() + "Nested")
138+
.path(field.getNestedPath())
139+
.subAggregation(lastAgg);
140+
}
141+
return lastAgg;
142+
}
143+
144+
private boolean isASC(Order order) {
115145
return "ASC".equals(order.getType());
116146
}
117147

src/test/java/org/nlpcn/es4sql/AggregationTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.elasticsearch.search.aggregations.Aggregations;
1010
import org.elasticsearch.search.aggregations.bucket.geogrid.GeoHashGrid;
1111
import org.elasticsearch.search.aggregations.bucket.geogrid.InternalGeoHashGrid;
12+
import org.elasticsearch.search.aggregations.bucket.nested.InternalNested;
1213
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
1314
import org.elasticsearch.search.aggregations.metrics.avg.Avg;
1415
import org.elasticsearch.search.aggregations.metrics.max.Max;
@@ -407,4 +408,29 @@ public void geoHashGrid() throws SQLFeatureNotSupportedException, SqlParseExcept
407408
}
408409
}
409410

411+
@Test
412+
public void groupByOnNestedFieldTest() throws Exception {
413+
Aggregations result = query(String.format("SELECT COUNT(*) FROM %s/nestedType GROUP BY nested(message.info)", TEST_INDEX));
414+
InternalNested nested = result.get("message.infoNested");
415+
Terms infos = nested.getAggregations().get("message.info");
416+
Assert.assertEquals(3,infos.getBuckets().size());
417+
for(Terms.Bucket bucket : infos.getBuckets()) {
418+
String key = bucket.getKey();
419+
long count = ((ValueCount) bucket.getAggregations().get("COUNT(*)")).getValue();
420+
if(key.equalsIgnoreCase("a")) {
421+
Assert.assertEquals(2, count);
422+
}
423+
else if(key.equalsIgnoreCase("c")) {
424+
Assert.assertEquals(2, count);
425+
}
426+
else if(key.equalsIgnoreCase("b")) {
427+
Assert.assertEquals(1, count);
428+
}
429+
else {
430+
throw new Exception(String.format("Unexpected key. expected: a OR b OR c . found: %s", key));
431+
}
432+
}
433+
}
434+
435+
410436
}

src/test/java/org/nlpcn/es4sql/SqlParserTests.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,29 @@ public void nestedFieldOnWhereGivenPath() throws SqlParseException {
558558
Assert.assertEquals("message.name",condition.getName());
559559
}
560560

561+
@Test
562+
public void nestedFieldOnGroupByNoPath() throws SqlParseException {
563+
String query = "select * from myIndex group by nested(message.name)";
564+
SQLExpr sqlExpr = queryToExpr(query);
565+
Select select = parser.parseSelect((SQLQueryExpr) sqlExpr);
566+
Field field = select.getGroupBys().get(0).get(0);
567+
Assert.assertTrue("condition should be nested",field.isNested());
568+
Assert.assertEquals("message",field.getNestedPath());
569+
Assert.assertEquals("message.name",field.getName());
570+
}
571+
572+
@Test
573+
public void nestedFieldOnGroupByWithPath() throws SqlParseException {
574+
String query = "select * from myIndex group by nested(message.name,message)";
575+
SQLExpr sqlExpr = queryToExpr(query);
576+
Select select = parser.parseSelect((SQLQueryExpr) sqlExpr);
577+
Field field = select.getGroupBys().get(0).get(0);
578+
Assert.assertTrue("condition should be nested",field.isNested());
579+
Assert.assertEquals("message",field.getNestedPath());
580+
Assert.assertEquals("message.name",field.getName());
581+
}
582+
583+
561584

562585
private SQLExpr queryToExpr(String query) {
563586
return new ElasticSqlExprParser(query).expr();

0 commit comments

Comments
 (0)