Skip to content

Commit 573c307

Browse files
authored
Javadoc for ValuesSource related work (#53427)
1 parent 42a03a1 commit 573c307

File tree

10 files changed

+155
-26
lines changed

10 files changed

+155
-26
lines changed

server/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramAggregatorFactory.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator;
3030
import org.elasticsearch.search.aggregations.support.AggregatorSupplier;
3131
import org.elasticsearch.search.aggregations.support.CoreValuesSourceType;
32-
import org.elasticsearch.search.aggregations.support.HistogramAggregatorSupplier;
3332
import org.elasticsearch.search.aggregations.support.ValuesSource;
3433
import org.elasticsearch.search.aggregations.support.ValuesSourceAggregatorFactory;
3534
import org.elasticsearch.search.aggregations.support.ValuesSourceConfig;

server/src/main/java/org/elasticsearch/search/aggregations/support/HistogramAggregatorSupplier.java renamed to server/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramAggregatorSupplier.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,16 @@
1616
* specific language governing permissions and limitations
1717
* under the License.
1818
*/
19-
package org.elasticsearch.search.aggregations.support;
19+
package org.elasticsearch.search.aggregations.bucket.histogram;
2020

2121
import org.elasticsearch.common.Nullable;
2222
import org.elasticsearch.search.DocValueFormat;
2323
import org.elasticsearch.search.aggregations.Aggregator;
2424
import org.elasticsearch.search.aggregations.AggregatorFactories;
2525
import org.elasticsearch.search.aggregations.BucketOrder;
2626
import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator;
27+
import org.elasticsearch.search.aggregations.support.AggregatorSupplier;
28+
import org.elasticsearch.search.aggregations.support.ValuesSource;
2729
import org.elasticsearch.search.internal.SearchContext;
2830

2931
import java.io.IOException;

server/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorFactory.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ public class TermsAggregatorFactory extends ValuesSourceAggregatorFactory {
6262
private final TermsAggregator.BucketCountThresholds bucketCountThresholds;
6363
private final boolean showTermDocCountError;
6464

65-
// TODO: Registration should happen on the actual aggregator classes
6665
static void registerAggregators(ValuesSourceRegistry valuesSourceRegistry) {
6766
valuesSourceRegistry.register(TermsAggregationBuilder.NAME,
6867
List.of(CoreValuesSourceType.BYTES, CoreValuesSourceType.IP),

server/src/main/java/org/elasticsearch/search/aggregations/support/AggregatorSupplier.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,24 @@
1818
*/
1919
package org.elasticsearch.search.aggregations.support;
2020

21+
/**
22+
* {@link AggregatorSupplier} serves as a marker for what the {@link ValuesSourceRegistry} holds to construct aggregator instances.
23+
* The aggregators for each aggregation should all share a signature, and that signature should be used to create an AggregatorSupplier for
24+
* that aggregation. Alternatively, if an existing supplier has a matching signature, please re-use that.
25+
*
26+
* In many cases, this can be a simple wrapper over the aggregator constructor. If that is sufficient, please consider the "typecast
27+
* lambda" syntax:
28+
*
29+
* {@code
30+
* (GeoCentroidAggregatorSupplier) (name, context, parent, valuesSource, pipelineAggregators, metaData) ->
31+
* new GeoCentroidAggregator(name, context, parent, (ValuesSource.GeoPoint) valuesSource, pipelineAggregators, metaData));
32+
* }
33+
*
34+
* The suppliers are responsible for any casting of {@link ValuesSource} that needs to happen. They must accept a base {@link ValuesSource}
35+
* instance. The suppliers may perform additional logic to configure the aggregator as needed, such as in
36+
* {@link org.elasticsearch.search.aggregations.bucket.terms.TermsAggregatorFactory} deciding the execution mode.
37+
*
38+
* There is ongoing work to normalize aggregator constructor signatures, and thus reduce the number of AggregatorSupplier interfaces.
39+
*/
2140
public interface AggregatorSupplier {
2241
}

server/src/main/java/org/elasticsearch/search/aggregations/support/CoreValuesSourceType.java

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ public ValuesSource getScript(AggregationScript.LeafFactory script, ValueType sc
5757
public ValuesSource getField(FieldContext fieldContext, AggregationScript.LeafFactory script) {
5858

5959
if ((fieldContext.indexFieldData() instanceof IndexNumericFieldData) == false) {
60-
// TODO: Is this the correct exception type here?
6160
throw new IllegalArgumentException("Expected numeric type on field [" + fieldContext.field() +
6261
"], but got [" + fieldContext.fieldType().typeName() + "]");
6362
}
@@ -71,8 +70,9 @@ public ValuesSource getField(FieldContext fieldContext, AggregationScript.LeafFa
7170
}
7271

7372
@Override
74-
public ValuesSource replaceMissing(ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat, LongSupplier now) {
75-
Number missing = docValueFormat.parseDouble(rawMissing.toString(), false, now);
73+
public ValuesSource replaceMissing(ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat,
74+
LongSupplier nowSupplier) {
75+
Number missing = docValueFormat.parseDouble(rawMissing.toString(), false, nowSupplier);
7676
return MissingValues.replaceMissing((ValuesSource.Numeric) valuesSource, missing);
7777
}
7878
},
@@ -104,7 +104,8 @@ public ValuesSource getField(FieldContext fieldContext, AggregationScript.LeafFa
104104
}
105105

106106
@Override
107-
public ValuesSource replaceMissing(ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat, LongSupplier now) {
107+
public ValuesSource replaceMissing(ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat,
108+
LongSupplier nowSupplier) {
108109
final BytesRef missing = docValueFormat.parseBytesRef(rawMissing.toString());
109110
if (valuesSource instanceof ValuesSource.Bytes.WithOrdinals) {
110111
return MissingValues.replaceMissing((ValuesSource.Bytes.WithOrdinals) valuesSource, missing);
@@ -127,7 +128,6 @@ public ValuesSource getScript(AggregationScript.LeafFactory script, ValueType sc
127128
@Override
128129
public ValuesSource getField(FieldContext fieldContext, AggregationScript.LeafFactory script) {
129130
if (!(fieldContext.indexFieldData() instanceof IndexGeoPointFieldData)) {
130-
// TODO: Is this the correct exception type here?
131131
throw new IllegalArgumentException("Expected geo_point type on field [" + fieldContext.field() +
132132
"], but got [" + fieldContext.fieldType().typeName() + "]");
133133
}
@@ -136,7 +136,8 @@ public ValuesSource getField(FieldContext fieldContext, AggregationScript.LeafFa
136136
}
137137

138138
@Override
139-
public ValuesSource replaceMissing(ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat, LongSupplier now) {
139+
public ValuesSource replaceMissing(ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat,
140+
LongSupplier nowSupplier) {
140141
// TODO: also support the structured formats of geo points
141142
final GeoPoint missing = new GeoPoint(rawMissing.toString());
142143
return MissingValues.replaceMissing((ValuesSource.GeoPoint) valuesSource, missing);
@@ -150,7 +151,6 @@ public DocValueFormat getFormatter(String format, ZoneId tz) {
150151
RANGE() {
151152
@Override
152153
public ValuesSource getEmpty() {
153-
// TODO: Is this the correct exception type here?
154154
throw new IllegalArgumentException("Can't deal with unmapped ValuesSource type " + this.value());
155155
}
156156

@@ -164,15 +164,15 @@ public ValuesSource getField(FieldContext fieldContext, AggregationScript.LeafFa
164164
MappedFieldType fieldType = fieldContext.fieldType();
165165

166166
if (fieldType instanceof RangeFieldMapper.RangeFieldType == false) {
167-
// TODO: Is this the correct exception type here?
168-
throw new IllegalStateException("Asked for range ValuesSource, but field is of type " + fieldType.name());
167+
throw new IllegalArgumentException("Asked for range ValuesSource, but field is of type " + fieldType.name());
169168
}
170169
RangeFieldMapper.RangeFieldType rangeFieldType = (RangeFieldMapper.RangeFieldType) fieldType;
171170
return new ValuesSource.Range(fieldContext.indexFieldData(), rangeFieldType.rangeType());
172171
}
173172

174173
@Override
175-
public ValuesSource replaceMissing(ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat, LongSupplier now) {
174+
public ValuesSource replaceMissing(ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat,
175+
LongSupplier nowSupplier) {
176176
throw new IllegalArgumentException("Can't apply missing values on a " + valuesSource.getClass());
177177
}
178178
},
@@ -193,8 +193,9 @@ public ValuesSource getField(FieldContext fieldContext, AggregationScript.LeafFa
193193
}
194194

195195
@Override
196-
public ValuesSource replaceMissing(ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat, LongSupplier now) {
197-
return BYTES.replaceMissing(valuesSource, rawMissing, docValueFormat, now);
196+
public ValuesSource replaceMissing(ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat,
197+
LongSupplier nowSupplier) {
198+
return BYTES.replaceMissing(valuesSource, rawMissing, docValueFormat, nowSupplier);
198199
}
199200

200201
@Override
@@ -219,8 +220,9 @@ public ValuesSource getField(FieldContext fieldContext, AggregationScript.LeafFa
219220
}
220221

221222
@Override
222-
public ValuesSource replaceMissing(ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat, LongSupplier now) {
223-
return NUMERIC.replaceMissing(valuesSource, rawMissing, docValueFormat, now);
223+
public ValuesSource replaceMissing(ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat,
224+
LongSupplier nowSupplier) {
225+
return NUMERIC.replaceMissing(valuesSource, rawMissing, docValueFormat, nowSupplier);
224226
}
225227

226228
@Override
@@ -250,8 +252,9 @@ public ValuesSource getField(FieldContext fieldContext, AggregationScript.LeafFa
250252
}
251253

252254
@Override
253-
public ValuesSource replaceMissing(ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat, LongSupplier now) {
254-
return NUMERIC.replaceMissing(valuesSource, rawMissing, docValueFormat, now);
255+
public ValuesSource replaceMissing(ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat,
256+
LongSupplier nowSupplier) {
257+
return NUMERIC.replaceMissing(valuesSource, rawMissing, docValueFormat, nowSupplier);
255258
}
256259

257260
@Override

server/src/main/java/org/elasticsearch/search/aggregations/support/ValueType.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import java.time.ZoneOffset;
3131
import java.util.Set;
3232

33+
@Deprecated
3334
public enum ValueType implements Writeable {
3435

3536
STRING((byte) 1, "string", "string", CoreValuesSourceType.BYTES,
@@ -42,7 +43,6 @@ public enum ValueType implements Writeable {
4243
new DocValueFormat.DateTime(DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER, ZoneOffset.UTC,
4344
DateFieldMapper.Resolution.MILLISECONDS)),
4445
IP((byte) 6, "ip", "ip", CoreValuesSourceType.IP, DocValueFormat.IP),
45-
// TODO: what is the difference between "number" and "numeric"?
4646
NUMERIC((byte) 7, "numeric", "numeric", CoreValuesSourceType.NUMERIC, DocValueFormat.RAW),
4747
GEOPOINT((byte) 8, "geo_point", "geo_point", CoreValuesSourceType.GEOPOINT, DocValueFormat.GEOHASH),
4848
BOOLEAN((byte) 9, "boolean", "boolean", CoreValuesSourceType.BOOLEAN, DocValueFormat.BOOLEAN),

server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSource.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@
5252
import java.io.IOException;
5353
import java.util.function.LongUnaryOperator;
5454

55+
/**
56+
* Note on subclassing ValuesSources: Generally, direct subclasses of ValuesSource should also be abstract, representing types. These
57+
* subclasses are free to add new methods specific to that type (e.g. {@link Numeric#isFloatingPoint()}). Subclasses of these should, in
58+
* turn, be concrete and implement specific ways of reading the given values (e.g. script and field based sources). It is also possible
59+
* to see sub-sub-classes of ValuesSource that act as wrappers on other concrete values sources to add functionality, such as the
60+
* anonymous subclasses returned from {@link MissingValues} or the GeoPoint to Numeric conversion logic in
61+
* {@link org.elasticsearch.search.aggregations.bucket.geogrid.CellIdSource}
62+
*/
5563
public abstract class ValuesSource {
5664

5765
/**

server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceAggregationBuilder.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,15 @@ protected final ValuesSourceAggregatorFactory doBuild(QueryShardContext querySha
334334
*/
335335
protected abstract ValuesSourceType defaultValueSourceType();
336336

337+
/**
338+
* Aggregations should override this if they need non-standard logic for resolving where to get values from. For example, join
339+
* aggregations (like Parent and Child) ask the user to specify one side of the join and then look up the other field to read values
340+
* from.
341+
*
342+
* The default implementation just uses the field and/or script the user provided.
343+
*
344+
* @return A {@link ValuesSourceConfig} configured based on the parsed field and/or script.
345+
*/
337346
protected ValuesSourceConfig resolveConfig(QueryShardContext queryShardContext) {
338347
return ValuesSourceConfig.resolve(queryShardContext,
339348
this.userValueTypeHint, field, script, missing, timeZone, format, this.defaultValueSourceType(), this.getType());

server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceType.java

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,23 @@
2626
import java.util.function.LongSupplier;
2727

2828
/**
29-
* ValuesSourceType wraps the creation of specific per-source instances each {@link ValuesSource} needs to provide. Every top-level
30-
* subclass of {@link ValuesSource} should have a corresponding implementation of this interface. In general, new data types seeking
31-
* aggregation support should create a top level {@link ValuesSource}, then implement this to return wrappers for the specific sources of
32-
* values.
29+
* {@link ValuesSourceType} represents a collection of fields that share a common set of operations, for example all numeric fields.
30+
* Aggregations declare their support for a given ValuesSourceType (via {@link ValuesSourceRegistry#register}), and should then not need
31+
* to care about the fields which use that ValuesSourceType.
32+
*
33+
* ValuesSourceTypes provide a set of methods to instantiate concrete {@link ValuesSource} instances, based on the actual source of the
34+
* data for the aggregations. In general, aggregations should not call these methods, but rather rely on {@link ValuesSourceConfig} to have
35+
* selected the correct implementation.
36+
*
37+
* ValuesSourceTypes should be stateless. We recommend that plugins define an enum for their ValuesSourceTypes, even if the plugin only
38+
* intends to define one ValuesSourceType. ValuesSourceTypes are not serialized as part of the aggregations framework.
39+
*
40+
* Prefer reusing an existing ValuesSourceType (ideally from {@link CoreValuesSourceType}) over creating a new type. There are some cases
41+
* where creating a new type is necessary however. In particular, consider a new ValuesSourceType if the field has custom encoding/decoding
42+
* requirements; if the field needs to expose additional information to the aggregation (e.g. {@link ValuesSource.Range#rangeType()}); or
43+
* if logically the type needs a more restricted use (e.g. even though dates are stored as numbers, it doesn't make sense to pass them to
44+
* a sum aggregation). When adding a new ValuesSourceType, new aggregators should be added and registered at the same time, to add support
45+
* for the new type to existing aggregations, as appropriate.
3346
*/
3447
public interface ValuesSourceType {
3548
/**
@@ -66,11 +79,11 @@ public interface ValuesSourceType {
6679
* @param valuesSource - The original {@link ValuesSource}
6780
* @param rawMissing - The missing value we got from the parser, typically a string or number
6881
* @param docValueFormat - The format to use for further parsing the user supplied value, e.g. a date format
69-
* @param now - Used in conjunction with the formatter, should return the current time in milliseconds
82+
* @param nowSupplier - Used in conjunction with the formatter, should return the current time in milliseconds
7083
* @return - Wrapper over the provided {@link ValuesSource} to apply the given missing value
7184
*/
7285
ValuesSource replaceMissing(ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat,
73-
LongSupplier now);
86+
LongSupplier nowSupplier);
7487

7588
/**
7689
* This method provides a hook for specifying a type-specific formatter. When {@link ValuesSourceConfig} can resolve a

0 commit comments

Comments
 (0)