Skip to content

Commit e3f77b4

Browse files
authored
Replace AggregatorParsers with namedObject (#22397)
Removes `AggregatorParsers`, replacing all of its functionality with `XContentParser#namedObject`. This is the third bit of payoff from #22003, one less thing to pass around the entire application.
1 parent f75ef7a commit e3f77b4

File tree

28 files changed

+527
-461
lines changed

28 files changed

+527
-461
lines changed

core/src/main/java/org/elasticsearch/rest/action/search/RestMultiSearchAction.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,7 @@ public static MultiSearchRequest parseRequest(RestRequest restRequest, boolean a
9090
parseMultiLineRequest(restRequest, multiRequest.indicesOptions(), allowExplicitIndex, (searchRequest, parser) -> {
9191
try {
9292
final QueryParseContext queryParseContext = new QueryParseContext(parser, parseFieldMatcher);
93-
searchRequest.source(SearchSourceBuilder.fromXContent(queryParseContext,
94-
searchRequestParsers.aggParsers, searchRequestParsers.suggesters));
93+
searchRequest.source(SearchSourceBuilder.fromXContent(queryParseContext, searchRequestParsers.suggesters));
9594
multiRequest.add(searchRequest);
9695
} catch (IOException e) {
9796
throw new ElasticsearchParseException("Exception when parsing search request", e);

core/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public static void parseSearchRequest(SearchRequest searchRequest, RestRequest r
9393
searchRequest.indices(Strings.splitStringByCommaToArray(request.param("index")));
9494
if (requestContentParser != null) {
9595
QueryParseContext context = new QueryParseContext(requestContentParser, parseFieldMatcher);
96-
searchRequest.source().parseXContent(context, searchRequestParsers.aggParsers, searchRequestParsers.suggesters);
96+
searchRequest.source().parseXContent(context, searchRequestParsers.suggesters);
9797
}
9898

9999
// do not allow 'query_and_fetch' or 'dfs_query_and_fetch' search types

core/src/main/java/org/elasticsearch/search/SearchModule.java

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@
9494
import org.elasticsearch.plugins.SearchPlugin.SearchExtSpec;
9595
import org.elasticsearch.plugins.SearchPlugin.SearchExtensionSpec;
9696
import org.elasticsearch.search.aggregations.AggregationBuilder;
97-
import org.elasticsearch.search.aggregations.Aggregator;
98-
import org.elasticsearch.search.aggregations.AggregatorParsers;
97+
import org.elasticsearch.search.aggregations.AggregatorFactories;
98+
import org.elasticsearch.search.aggregations.BaseAggregationBuilder;
9999
import org.elasticsearch.search.aggregations.InternalAggregation;
100100
import org.elasticsearch.search.aggregations.PipelineAggregationBuilder;
101101
import org.elasticsearch.search.aggregations.bucket.children.ChildrenAggregationBuilder;
@@ -268,10 +268,6 @@ public class SearchModule {
268268
private final boolean transportClient;
269269
private final Map<String, Highlighter> highlighters;
270270
private final Map<String, Suggester<?>> suggesters;
271-
private final ParseFieldRegistry<Aggregator.Parser> aggregationParserRegistry = new ParseFieldRegistry<>("aggregation");
272-
private final ParseFieldRegistry<PipelineAggregator.Parser> pipelineAggregationParserRegistry = new ParseFieldRegistry<>(
273-
"pipline_aggregation");
274-
private final AggregatorParsers aggregatorParsers = new AggregatorParsers(aggregationParserRegistry, pipelineAggregationParserRegistry);
275271
private final ParseFieldRegistry<SignificanceHeuristicParser> significanceHeuristicParserRegistry = new ParseFieldRegistry<>(
276272
"significance_heuristic");
277273
private final ParseFieldRegistry<MovAvgModel.AbstractModelParser> movingAverageModelParserRegistry = new ParseFieldRegistry<>(
@@ -301,7 +297,7 @@ public SearchModule(Settings settings, boolean transportClient, List<SearchPlugi
301297
registerFetchSubPhases(plugins);
302298
registerSearchExts(plugins);
303299
registerShapes();
304-
searchRequestParsers = new SearchRequestParsers(aggregatorParsers, getSuggesters());
300+
searchRequestParsers = new SearchRequestParsers(getSuggesters());
305301
}
306302

307303
public List<NamedWriteableRegistry.Entry> getNamedWriteables() {
@@ -341,13 +337,6 @@ public ParseFieldRegistry<MovAvgModel.AbstractModelParser> getMovingAverageModel
341337
return movingAverageModelParserRegistry;
342338
}
343339

344-
/**
345-
* Parsers for {@link AggregationBuilder}s and {@link PipelineAggregationBuilder}s.
346-
*/
347-
public AggregatorParsers getAggregatorParsers() {
348-
return aggregatorParsers;
349-
}
350-
351340
private void registerAggregations(List<SearchPlugin> plugins) {
352341
registerAggregation(new AggregationSpec(AvgAggregationBuilder.NAME, AvgAggregationBuilder::new, AvgAggregationBuilder::parse)
353342
.addResultReader(InternalAvg::new));
@@ -433,7 +422,10 @@ private void registerAggregations(List<SearchPlugin> plugins) {
433422

434423
private void registerAggregation(AggregationSpec spec) {
435424
if (false == transportClient) {
436-
aggregationParserRegistry.register(spec.getParser(), spec.getName());
425+
namedXContents.add(new NamedXContentRegistry.Entry(BaseAggregationBuilder.class, spec.getName(), (p, c) -> {
426+
AggregatorFactories.AggParseContext context = (AggregatorFactories.AggParseContext) c;
427+
return spec.getParser().parse(context.name, context.queryParseContext);
428+
}));
437429
}
438430
namedWriteables.add(
439431
new NamedWriteableRegistry.Entry(AggregationBuilder.class, spec.getName().getPreferredName(), spec.getReader()));
@@ -527,7 +519,10 @@ private void registerPipelineAggregations(List<SearchPlugin> plugins) {
527519

528520
private void registerPipelineAggregation(PipelineAggregationSpec spec) {
529521
if (false == transportClient) {
530-
pipelineAggregationParserRegistry.register(spec.getParser(), spec.getName());
522+
namedXContents.add(new NamedXContentRegistry.Entry(BaseAggregationBuilder.class, spec.getName(), (p, c) -> {
523+
AggregatorFactories.AggParseContext context = (AggregatorFactories.AggParseContext) c;
524+
return spec.getParser().parse(context.name, context.queryParseContext);
525+
}));
531526
}
532527
namedWriteables.add(
533528
new NamedWriteableRegistry.Entry(PipelineAggregationBuilder.class, spec.getName().getPreferredName(), spec.getReader()));

core/src/main/java/org/elasticsearch/search/SearchRequestParsers.java

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919

2020
package org.elasticsearch.search;
2121

22-
import org.elasticsearch.index.query.QueryParseContext;
23-
import org.elasticsearch.search.aggregations.AggregatorParsers;
2422
import org.elasticsearch.search.suggest.Suggesters;
2523

2624
/**
@@ -32,25 +30,14 @@ public class SearchRequestParsers {
3230
// methods split across RestSearchAction and SearchSourceBuilder should be moved here
3331
// TODO: make all members private once parsing functions are moved here
3432

35-
// TODO: AggregatorParsers should be removed and the underlying maps of agg
36-
// and pipeline agg parsers should be here
37-
/**
38-
* Agg and pipeline agg parsers that may be used in search requests.
39-
* @see org.elasticsearch.search.builder.SearchSourceBuilder#fromXContent(QueryParseContext, AggregatorParsers,
40-
* Suggesters)
41-
*/
42-
public final AggregatorParsers aggParsers;
43-
4433
// TODO: Suggesters should be removed and the underlying map moved here
4534
/**
4635
* Suggesters that may be used in search requests.
47-
* @see org.elasticsearch.search.builder.SearchSourceBuilder#fromXContent(QueryParseContext, AggregatorParsers,
48-
* Suggesters)
36+
* @see org.elasticsearch.search.builder.SearchSourceBuilder#fromXContent(QueryParseContext, Suggesters)
4937
*/
5038
public final Suggesters suggesters;
5139

52-
public SearchRequestParsers(AggregatorParsers aggParsers, Suggesters suggesters) {
53-
this.aggParsers = aggParsers;
40+
public SearchRequestParsers(Suggesters suggesters) {
5441
this.suggesters = suggesters;
5542
}
5643
}

core/src/main/java/org/elasticsearch/search/aggregations/AbstractAggregationBuilder.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ public AB setMetaData(Map<String, Object> metaData) {
117117
return (AB) this;
118118
}
119119

120+
@Override
120121
public String getType() {
121122
return type.name();
122123
}

core/src/main/java/org/elasticsearch/search/aggregations/AggregationBuilder.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.elasticsearch.common.ParseField;
2424
import org.elasticsearch.common.io.stream.NamedWriteable;
2525
import org.elasticsearch.common.xcontent.ToXContent;
26+
import org.elasticsearch.index.query.QueryParseContext;
2627
import org.elasticsearch.search.aggregations.InternalAggregation.Type;
2728
import org.elasticsearch.search.internal.SearchContext;
2829

@@ -34,7 +35,7 @@
3435
*/
3536
public abstract class AggregationBuilder
3637
extends ToXContentToBytes
37-
implements NamedWriteable, ToXContent {
38+
implements NamedWriteable, ToXContent, BaseAggregationBuilder {
3839

3940
protected final String name;
4041
protected final Type type;
@@ -66,6 +67,7 @@ public String getName() {
6667
protected abstract AggregatorFactory<?> build(SearchContext context, AggregatorFactory<?> parent) throws IOException;
6768

6869
/** Associate metadata with this {@link AggregationBuilder}. */
70+
@Override
6971
public abstract AggregationBuilder setMetaData(Map<String, Object> metaData);
7072

7173
/** Add a sub aggregation to this builder. */
@@ -77,13 +79,14 @@ public String getName() {
7779
/**
7880
* Internal: Registers sub-factories with this factory. The sub-factory will be
7981
* responsible for the creation of sub-aggregators under the aggregator
80-
* created by this factory. This is only for use by {@link AggregatorParsers}.
82+
* created by this factory. This is only for use by {@link AggregatorFactories#parseAggregators(QueryParseContext)}.
8183
*
8284
* @param subFactories
8385
* The sub-factories
8486
* @return this factory (fluent interface)
8587
*/
86-
protected abstract AggregationBuilder subAggregations(AggregatorFactories.Builder subFactories);
88+
@Override
89+
public abstract AggregationBuilder subAggregations(AggregatorFactories.Builder subFactories);
8790

8891
/** Common xcontent fields shared among aggregator builders */
8992
public static final class CommonFields extends ParseField.CommonFields {

core/src/main/java/org/elasticsearch/search/aggregations/AggregatorFactories.java

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,13 @@
1919
package org.elasticsearch.search.aggregations;
2020

2121
import org.elasticsearch.action.support.ToXContentToBytes;
22+
import org.elasticsearch.common.ParsingException;
2223
import org.elasticsearch.common.io.stream.StreamInput;
2324
import org.elasticsearch.common.io.stream.StreamOutput;
2425
import org.elasticsearch.common.io.stream.Writeable;
2526
import org.elasticsearch.common.xcontent.XContentBuilder;
27+
import org.elasticsearch.common.xcontent.XContentParser;
28+
import org.elasticsearch.index.query.QueryParseContext;
2629
import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator;
2730
import org.elasticsearch.search.aggregations.support.AggregationPath;
2831
import org.elasticsearch.search.aggregations.support.AggregationPath.PathElement;
@@ -40,8 +43,126 @@
4043
import java.util.Map;
4144
import java.util.Objects;
4245
import java.util.Set;
46+
import java.util.regex.Matcher;
47+
import java.util.regex.Pattern;
4348

4449
public class AggregatorFactories {
50+
public static final Pattern VALID_AGG_NAME = Pattern.compile("[^\\[\\]>]+");
51+
52+
/**
53+
* Parses the aggregation request recursively generating aggregator factories in turn.
54+
*
55+
* @param parseContext The parse context.
56+
*
57+
* @return The parsed aggregator factories.
58+
*
59+
* @throws IOException When parsing fails for unknown reasons.
60+
*/
61+
public static AggregatorFactories.Builder parseAggregators(QueryParseContext parseContext) throws IOException {
62+
return parseAggregators(parseContext, 0);
63+
}
64+
65+
private static AggregatorFactories.Builder parseAggregators(QueryParseContext parseContext, int level) throws IOException {
66+
Matcher validAggMatcher = VALID_AGG_NAME.matcher("");
67+
AggregatorFactories.Builder factories = new AggregatorFactories.Builder();
68+
69+
XContentParser.Token token = null;
70+
XContentParser parser = parseContext.parser();
71+
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
72+
if (token != XContentParser.Token.FIELD_NAME) {
73+
throw new ParsingException(parser.getTokenLocation(),
74+
"Unexpected token " + token + " in [aggs]: aggregations definitions must start with the name of the aggregation.");
75+
}
76+
final String aggregationName = parser.currentName();
77+
if (!validAggMatcher.reset(aggregationName).matches()) {
78+
throw new ParsingException(parser.getTokenLocation(), "Invalid aggregation name [" + aggregationName
79+
+ "]. Aggregation names must be alpha-numeric and can only contain '_' and '-'");
80+
}
81+
82+
token = parser.nextToken();
83+
if (token != XContentParser.Token.START_OBJECT) {
84+
throw new ParsingException(parser.getTokenLocation(), "Aggregation definition for [" + aggregationName + " starts with a ["
85+
+ token + "], expected a [" + XContentParser.Token.START_OBJECT + "].");
86+
}
87+
88+
BaseAggregationBuilder aggBuilder = null;
89+
AggregatorFactories.Builder subFactories = null;
90+
91+
Map<String, Object> metaData = null;
92+
93+
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
94+
if (token != XContentParser.Token.FIELD_NAME) {
95+
throw new ParsingException(
96+
parser.getTokenLocation(), "Expected [" + XContentParser.Token.FIELD_NAME + "] under a ["
97+
+ XContentParser.Token.START_OBJECT + "], but got a [" + token + "] in [" + aggregationName + "]",
98+
parser.getTokenLocation());
99+
}
100+
final String fieldName = parser.currentName();
101+
102+
token = parser.nextToken();
103+
if (token == XContentParser.Token.START_OBJECT) {
104+
switch (fieldName) {
105+
case "meta":
106+
metaData = parser.map();
107+
break;
108+
case "aggregations":
109+
case "aggs":
110+
if (subFactories != null) {
111+
throw new ParsingException(parser.getTokenLocation(),
112+
"Found two sub aggregation definitions under [" + aggregationName + "]");
113+
}
114+
subFactories = parseAggregators(parseContext, level + 1);
115+
break;
116+
default:
117+
if (aggBuilder != null) {
118+
throw new ParsingException(parser.getTokenLocation(), "Found two aggregation type definitions in ["
119+
+ aggregationName + "]: [" + aggBuilder.getType() + "] and [" + fieldName + "]");
120+
}
121+
122+
aggBuilder = parser.namedObject(BaseAggregationBuilder.class, fieldName,
123+
new AggParseContext(aggregationName, parseContext));
124+
}
125+
} else {
126+
throw new ParsingException(parser.getTokenLocation(), "Expected [" + XContentParser.Token.START_OBJECT + "] under ["
127+
+ fieldName + "], but got a [" + token + "] in [" + aggregationName + "]");
128+
}
129+
}
130+
131+
if (aggBuilder == null) {
132+
throw new ParsingException(parser.getTokenLocation(), "Missing definition for aggregation [" + aggregationName + "]",
133+
parser.getTokenLocation());
134+
} else {
135+
if (metaData != null) {
136+
aggBuilder.setMetaData(metaData);
137+
}
138+
139+
if (subFactories != null) {
140+
aggBuilder.subAggregations(subFactories);
141+
}
142+
143+
if (aggBuilder instanceof AggregationBuilder) {
144+
factories.addAggregator((AggregationBuilder) aggBuilder);
145+
} else {
146+
factories.addPipelineAggregator((PipelineAggregationBuilder) aggBuilder);
147+
}
148+
}
149+
}
150+
151+
return factories;
152+
}
153+
154+
/**
155+
* Context to parse and aggregation. This should eventually be removed and replaced with a String.
156+
*/
157+
public static final class AggParseContext {
158+
public final String name;
159+
public final QueryParseContext queryParseContext;
160+
161+
public AggParseContext(String name, QueryParseContext queryParseContext) {
162+
this.name = name;
163+
this.queryParseContext = queryParseContext;
164+
}
165+
}
45166

46167
public static final AggregatorFactories EMPTY = new AggregatorFactories(null, new AggregatorFactory<?>[0],
47168
new ArrayList<PipelineAggregationBuilder>());
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.search.aggregations;
21+
22+
import org.elasticsearch.common.xcontent.XContentParser;
23+
import org.elasticsearch.search.aggregations.AggregatorFactories.Builder;
24+
25+
import java.util.Map;
26+
27+
/**
28+
* Interface shared by {@link AggregationBuilder} and {@link PipelineAggregationBuilder} so they can conveniently share the same namespace
29+
* for {@link XContentParser#namedObject(Class, String, Object)}.
30+
*/
31+
public interface BaseAggregationBuilder {
32+
/**
33+
* The name of the type of aggregation built by this builder.
34+
*/
35+
String getType();
36+
37+
/**
38+
* Set the aggregation's metadata. Returns {@code this} for chaining.
39+
*/
40+
BaseAggregationBuilder setMetaData(Map<String, Object> metaData);
41+
42+
/**
43+
* Set the sub aggregations if this aggregation supports sub aggregations. Returns {@code this} for chaining.
44+
*/
45+
BaseAggregationBuilder subAggregations(Builder subFactories);
46+
}

core/src/main/java/org/elasticsearch/search/aggregations/PipelineAggregationBuilder.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
import org.elasticsearch.action.support.ToXContentToBytes;
2222
import org.elasticsearch.common.io.stream.NamedWriteable;
23-
import org.elasticsearch.search.aggregations.AggregatorFactory;
23+
import org.elasticsearch.search.aggregations.AggregatorFactories.Builder;
2424
import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator;
2525

2626
import java.io.IOException;
@@ -32,7 +32,7 @@
3232
* specific type.
3333
*/
3434
public abstract class PipelineAggregationBuilder extends ToXContentToBytes
35-
implements NamedWriteable {
35+
implements NamedWriteable, BaseAggregationBuilder {
3636

3737
protected final String name;
3838
protected final String[] bucketsPaths;
@@ -79,6 +79,11 @@ protected abstract void validate(AggregatorFactory<?> parent, AggregatorFactory<
7979
protected abstract PipelineAggregator create() throws IOException;
8080

8181
/** Associate metadata with this {@link PipelineAggregationBuilder}. */
82+
@Override
8283
public abstract PipelineAggregationBuilder setMetaData(Map<String, Object> metaData);
8384

85+
@Override
86+
public PipelineAggregationBuilder subAggregations(Builder subFactories) {
87+
throw new IllegalArgumentException("Aggregation [" + name + "] cannot define sub-aggregations");
88+
}
8489
}

0 commit comments

Comments
 (0)