Skip to content

Commit 5431c27

Browse files
committed
reversed_nested aggregation support
1 parent 777731d commit 5431c27

File tree

9 files changed

+288
-38
lines changed

9 files changed

+288
-38
lines changed

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

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.nlpcn.es4sql.domain;
22

3+
import org.nlpcn.es4sql.parse.NestedType;
4+
35
/**
46
* 搜索域
57
*
@@ -10,15 +12,22 @@ public class Field implements Cloneable{
1012

1113
protected String name;
1214
private String alias;
13-
private boolean isNested;
14-
private String nestedPath;
15+
private NestedType nested;
16+
1517

1618
public Field(String name, String alias) {
1719
this.name = name;
1820
this.alias = alias;
21+
this.nested = null;
1922
}
2023

21-
public String getName() {
24+
public Field(String name, String alias, NestedType nested) {
25+
this.name = name;
26+
this.alias = alias;
27+
this.nested = nested;
28+
}
29+
30+
public String getName() {
2231
return name;
2332
}
2433

@@ -35,20 +44,22 @@ public void setAlias(String alias) {
3544
}
3645

3746
public boolean isNested() {
38-
return isNested;
47+
return this.nested!=null;
48+
}
49+
public boolean isReverseNested() {
50+
return this.nested!=null && this.nested.isReverse();
3951
}
4052

41-
public void setNested(boolean isNested) {
42-
this.isNested = isNested;
53+
public void setNested(NestedType nested){
54+
this.nested = nested;
4355
}
4456

57+
4558
public String getNestedPath() {
46-
return nestedPath;
59+
if(this.nested == null ) return null;
60+
return this.nested.path;
4761
}
4862

49-
public void setNestedPath(String nestedPath) {
50-
this.nestedPath = nestedPath;
51-
}
5263

5364
@Override
5465
public String toString() {

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

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,13 @@ public MethodField(String name, List<KVValue> params, String option, String alia
2121
this.params = params;
2222
this.option = option;
2323
if (alias==null||alias.trim().length()==0) {
24-
this.setAlias(this.toString());
24+
Map<String, Object> paramsAsMap = this.getParamsAsMap();
25+
if(paramsAsMap.containsKey("alias")){
26+
this.setAlias(paramsAsMap.get("alias").toString());
27+
}
28+
else {
29+
this.setAlias(this.toString());
30+
}
2531
}
2632
}
2733

@@ -31,6 +37,7 @@ public List<KVValue> getParams() {
3137

3238
public Map<String,Object> getParamsAsMap(){
3339
Map<String,Object> paramsAsMap = new HashMap<>();
40+
if(this.params == null ) return paramsAsMap;
3441
for(KVValue kvValue : this.params){
3542
paramsAsMap.put(kvValue.key,kvValue.value);
3643
}
@@ -55,12 +62,23 @@ public void setOption(String option) {
5562

5663
@Override
5764
public boolean isNested() {
58-
return this.getParamsAsMap().containsKey("nested");
65+
Map<String, Object> paramsAsMap = this.getParamsAsMap();
66+
return paramsAsMap.containsKey("nested") || paramsAsMap.containsKey("reverse_nested");
67+
}
68+
69+
@Override
70+
public boolean isReverseNested() {
71+
return this.getParamsAsMap().containsKey("reverse_nested");
72+
5973
}
6074

6175
@Override
6276
public String getNestedPath() {
6377
if(!this.isNested()) return null;
78+
if(this.isReverseNested()){
79+
String reverseNestedPath = this.getParamsAsMap().get("reverse_nested").toString();
80+
return reverseNestedPath.isEmpty() ? null : reverseNestedPath;
81+
}
6482
return this.getParamsAsMap().get("nested").toString();
6583
}
6684
}

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public static Field makeField(SQLExpr expr, String alias,String tableAlias) thro
3232
} else if (expr instanceof SQLMethodInvokeExpr) {
3333
SQLMethodInvokeExpr mExpr = (SQLMethodInvokeExpr) expr;
3434
String methodName = mExpr.getMethodName();
35-
if(methodName.toLowerCase().equals("nested")){
35+
if(methodName.toLowerCase().equals("nested") ||methodName.toLowerCase().equals("reverse_nested") ){
3636
NestedType nestedType = new NestedType();
3737
if(nestedType.tryFillFromExpr(mExpr)){
3838
return handleIdentifier(nestedType, alias, tableAlias);
@@ -82,8 +82,7 @@ private static Field makeFilterMethodField(SQLMethodInvokeExpr filterMethod,Stri
8282

8383
private static Field handleIdentifier(NestedType nestedType, String alias, String tableAlias) {
8484
Field field = handleIdentifier(new SQLIdentifierExpr(nestedType.field), alias, tableAlias);
85-
field.setNested(true);
86-
field.setNestedPath(nestedType.path);
85+
field.setNested(nestedType);
8786
return field;
8887
}
8988

@@ -151,7 +150,7 @@ private static MethodField makeMethodField(String name, List<SQLExpr> arguments,
151150
KVValue script = new KVValue("script", makeMethodField(mExpr.getMethodName(), mExpr.getParameters(), null, alias));
152151
paramers.add(script);
153152
}
154-
else if(methodName.equals("nested")){
153+
else if(methodName.equals("nested") || methodName.equals("reverse_nested")){
155154
NestedType nestedType = new NestedType();
156155
if(!nestedType.tryFillFromExpr(object)){
157156
throw new SqlParseException("failed parsing nested expr " + object);

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

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,16 @@ public class NestedType {
1818
public String field;
1919
public String path;
2020
public Where where;
21+
private boolean reverse;
2122
private boolean simple;
2223

2324
public boolean tryFillFromExpr(SQLExpr expr) throws SqlParseException {
2425
if (!(expr instanceof SQLMethodInvokeExpr)) return false;
2526
SQLMethodInvokeExpr method = (SQLMethodInvokeExpr) expr;
26-
if (!method.getMethodName().toLowerCase().equals("nested")) return false;
27+
String methodNameLower = method.getMethodName().toLowerCase();
28+
if (!(methodNameLower.equals("nested") || methodNameLower.equals("reverse_nested"))) return false;
29+
30+
reverse = methodNameLower.equals("reverse_nested");
2731

2832
List<SQLExpr> parameters = method.getParameters();
2933
if (parameters.size() != 2 && parameters.size() != 1)
@@ -33,15 +37,29 @@ public boolean tryFillFromExpr(SQLExpr expr) throws SqlParseException {
3337
this.field = field;
3438
if (parameters.size() == 1) {
3539
//calc path myself..
36-
if (!field.contains("."))
37-
throw new SqlParseException("nested should contain . on their field name");
38-
int lastDot = field.lastIndexOf(".");
39-
this.path = field.substring(0, lastDot);
40-
this.simple = true;
40+
if (!field.contains(".")) {
41+
if (!reverse)
42+
throw new SqlParseException("nested should contain . on their field name");
43+
else {
44+
this.path = null;
45+
this.simple = true;
46+
}
47+
} else {
48+
int lastDot = field.lastIndexOf(".");
49+
this.path = field.substring(0, lastDot);
50+
this.simple = true;
51+
52+
}
53+
4154
} else if (parameters.size() == 2) {
4255
SQLExpr secondParameter = parameters.get(1);
4356
if(secondParameter instanceof SQLTextLiteralExpr || secondParameter instanceof SQLIdentifierExpr || secondParameter instanceof SQLPropertyExpr) {
44-
this.path = Util.extendedToString(secondParameter);
57+
58+
String pathString = Util.extendedToString(secondParameter);
59+
if(pathString.equals(""))
60+
this.path = null;
61+
else
62+
this.path = pathString;
4563
this.simple = true;
4664
}
4765
else {
@@ -61,4 +79,8 @@ public boolean tryFillFromExpr(SQLExpr expr) throws SqlParseException {
6179
public boolean isSimple() {
6280
return simple;
6381
}
82+
83+
public boolean isReverse() {
84+
return reverse;
85+
}
6486
}

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

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010
import org.elasticsearch.index.query.BoolFilterBuilder;
1111
import org.elasticsearch.index.query.QueryBuilders;
1212
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
13+
import org.elasticsearch.search.aggregations.Aggregation;
1314
import org.elasticsearch.search.aggregations.AggregationBuilder;
1415
import org.elasticsearch.search.aggregations.AggregationBuilders;
1516
import org.elasticsearch.search.aggregations.bucket.nested.NestedBuilder;
17+
import org.elasticsearch.search.aggregations.bucket.nested.ReverseNested;
18+
import org.elasticsearch.search.aggregations.bucket.nested.ReverseNestedBuilder;
1619
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
1720
import org.elasticsearch.search.aggregations.bucket.terms.TermsBuilder;
1821
import org.elasticsearch.search.sort.SortOrder;
@@ -59,15 +62,15 @@ public SqlElasticSearchRequestBuilder explain() throws SqlParseException {
5962
}
6063

6164
if(field.isNested()){
62-
NestedBuilder nestedBuilder = AggregationBuilders.nested(getNestedAggName(field))
63-
.path(field.getNestedPath());
65+
66+
AggregationBuilder nestedBuilder = createNestedAggregation(field);
6467
if(insertFilterIfExistsAfter(lastAgg, groupBy, nestedBuilder,1)){
6568
groupBy.remove(1);
6669
}
6770
else {
6871
nestedBuilder.subAggregation(lastAgg);
6972
}
70-
request.addAggregation(nestedBuilder);
73+
request.addAggregation(wrapNestedIfNeeded(nestedBuilder,field.isReverseNested()));
7174
}
7275
else {
7376
request.addAggregation(lastAgg);
@@ -81,17 +84,15 @@ public SqlElasticSearchRequestBuilder explain() throws SqlParseException {
8184
}
8285

8386
if(field.isNested()){
84-
85-
NestedBuilder nestedBuilder = AggregationBuilders.nested(getNestedAggName(field))
86-
.path(field.getNestedPath());
87+
AggregationBuilder nestedBuilder = createNestedAggregation(field);
8788
if(insertFilterIfExistsAfter(subAgg, groupBy, nestedBuilder,i+1)){
8889
groupBy.remove(i+1);
8990
i++;
9091
}
9192
else {
9293
nestedBuilder.subAggregation(subAgg);
9394
}
94-
lastAgg.subAggregation(nestedBuilder);
95+
lastAgg.subAggregation(wrapNestedIfNeeded(nestedBuilder,field.isReverseNested()));
9596

9697
}
9798
else {
@@ -144,10 +145,35 @@ public SqlElasticSearchRequestBuilder explain() throws SqlParseException {
144145
return sqlElasticRequestBuilder;
145146
}
146147

148+
private AbstractAggregationBuilder wrapNestedIfNeeded(AggregationBuilder nestedBuilder, boolean reverseNested) {
149+
if(!reverseNested) return nestedBuilder;
150+
if(reverseNested && ! (nestedBuilder instanceof NestedBuilder)) return nestedBuilder;
151+
//we need to jump back to root
152+
return AggregationBuilders.reverseNested(nestedBuilder.getName()+"_REVERSED").subAggregation(nestedBuilder);
153+
}
154+
155+
private AggregationBuilder createNestedAggregation(Field field) {
156+
AggregationBuilder nestedBuilder;
157+
String nestedPath = field.getNestedPath();
158+
if(field.isReverseNested() ) {
159+
if(nestedPath == null || !nestedPath.startsWith("~"))
160+
return AggregationBuilders.reverseNested(getNestedAggName(field)).path(nestedPath);
161+
nestedPath = nestedPath.substring(1);
162+
}
163+
nestedBuilder = AggregationBuilders.nested(getNestedAggName(field)).path(nestedPath);
164+
return nestedBuilder;
165+
}
166+
147167
private String getNestedAggName(Field field) {
148168
String prefix;
149169
if(field instanceof MethodField){
150-
prefix = field.getNestedPath();
170+
String nestedPath = field.getNestedPath();
171+
if(nestedPath != null){
172+
prefix = nestedPath;
173+
}
174+
else {
175+
prefix = field.getAlias();
176+
}
151177
}
152178
else {
153179
prefix = field.getName();
@@ -156,7 +182,7 @@ private String getNestedAggName(Field field) {
156182
}
157183

158184

159-
private boolean insertFilterIfExistsAfter(AggregationBuilder<?> agg, List<Field> groupBy, NestedBuilder builder, int nextPosition) throws SqlParseException {
185+
private boolean insertFilterIfExistsAfter(AggregationBuilder<?> agg, List<Field> groupBy, AggregationBuilder builder, int nextPosition) throws SqlParseException {
160186
if(groupBy.size() <= nextPosition) return false;
161187
Field filterFieldCandidate = groupBy.get(nextPosition);
162188
if(! (filterFieldCandidate instanceof MethodField)) return false;

src/main/java/org/nlpcn/es4sql/query/maker/AggMaker.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,26 @@ private AbstractAggregationBuilder addFieldOrScriptToAggregation(MethodField fie
119119
//todo: support different lang script
120120
return builder.script(((MethodField)kvValue.value).getParams().get(1).toString());
121121
}
122-
else if (kvValue.key!=null && kvValue.key.equals("nested")){
122+
else if (kvValue.key!=null && ( kvValue.key.equals("nested") || kvValue.key.equals("reverse_nested")) ){
123123
NestedType nestedType = (NestedType) kvValue.value;
124124
builder.field(nestedType.field);
125-
return AggregationBuilders.nested(nestedType.field+"@NESTED").path(nestedType.path).subAggregation(builder);
125+
AggregationBuilder nestedBuilder;
126+
String nestedAggName = nestedType.field + "@NESTED";
127+
if(nestedType.isReverse()) {
128+
if(nestedType.path!=null && nestedType.path.startsWith("~")){
129+
String realPath = nestedType.path.substring(1);
130+
nestedBuilder = AggregationBuilders.nested(nestedAggName).path(realPath);
131+
nestedBuilder = nestedBuilder.subAggregation(builder);
132+
return AggregationBuilders.reverseNested(nestedAggName+"_REVERSED").subAggregation(nestedBuilder);
133+
}
134+
else{
135+
nestedBuilder = AggregationBuilders.reverseNested(nestedAggName).path(nestedType.path);
136+
}
137+
}
138+
else {
139+
nestedBuilder = AggregationBuilders.nested(nestedAggName).path(nestedType.path);
140+
}
141+
return nestedBuilder.subAggregation(builder);
126142
}
127143
return builder.field(kvValue.toString());
128144
}
@@ -210,6 +226,7 @@ private AbstractAggregationBuilder scriptedMetric(MethodField field) throws SqlP
210226
break;
211227
case "alias":
212228
case "nested":
229+
case "reverse_nested":
213230
break;
214231
default:
215232
throw new SqlParseException("scripted_metric err or not define field " + param.getKey());
@@ -246,6 +263,7 @@ private AggregationBuilder<?> geohashGrid(MethodField field) throws SqlParseExce
246263
break;
247264
case "alias":
248265
case "nested":
266+
case "reverse_nested":
249267
break;
250268
default:
251269
throw new SqlParseException("geohash grid err or not define field " + kv.toString());
@@ -328,6 +346,7 @@ private DateHistogramBuilder dateHistogram(MethodField field) throws SqlParseExc
328346
break;
329347
case "alias":
330348
case "nested":
349+
case "reverse_nested":
331350
break;
332351
default:
333352
throw new SqlParseException("date range err or not define field " + kv.toString());
@@ -368,6 +387,7 @@ private HistogramBuilder histogram(MethodField field) throws SqlParseException {
368387
break;
369388
case "alias":
370389
case "nested":
390+
case "reverse_nested":
371391
break;
372392
case "order":
373393
Histogram.Order order = null;
@@ -462,6 +482,7 @@ private AbstractAggregationBuilder makeTopHitsAgg(MethodField field) {
462482
break;
463483
case "alias":
464484
case "nested":
485+
case "reverse_nested":
465486
break;
466487
default:
467488
topHits.addSort(kv.key, SortOrder.valueOf(kv.value.toString().toUpperCase()));

0 commit comments

Comments
 (0)