|
@@ -19,6 +19,7 @@
|
|
|
|
|
|
package org.elasticsearch.search.aggregations.bucket.histogram;
|
|
|
|
|
|
+import org.elasticsearch.Version;
|
|
|
import org.elasticsearch.common.ParseField;
|
|
|
import org.elasticsearch.common.Rounding;
|
|
|
import org.elasticsearch.common.io.stream.StreamInput;
|
|
@@ -48,42 +49,65 @@ import java.util.Arrays;
|
|
|
import java.util.Map;
|
|
|
import java.util.Objects;
|
|
|
|
|
|
+import static java.util.Map.entry;
|
|
|
+
|
|
|
public class AutoDateHistogramAggregationBuilder
|
|
|
extends ValuesSourceAggregationBuilder<ValuesSource.Numeric, AutoDateHistogramAggregationBuilder> {
|
|
|
|
|
|
public static final String NAME = "auto_date_histogram";
|
|
|
|
|
|
private static final ParseField NUM_BUCKETS_FIELD = new ParseField("buckets");
|
|
|
+ private static final ParseField MINIMUM_INTERVAL_FIELD = new ParseField("minimum_interval");
|
|
|
|
|
|
private static final ObjectParser<AutoDateHistogramAggregationBuilder, Void> PARSER;
|
|
|
static {
|
|
|
PARSER = new ObjectParser<>(AutoDateHistogramAggregationBuilder.NAME);
|
|
|
ValuesSourceParserHelper.declareNumericFields(PARSER, true, true, true);
|
|
|
-
|
|
|
PARSER.declareInt(AutoDateHistogramAggregationBuilder::setNumBuckets, NUM_BUCKETS_FIELD);
|
|
|
+ PARSER.declareStringOrNull(AutoDateHistogramAggregationBuilder::setMinimumIntervalExpression, MINIMUM_INTERVAL_FIELD);
|
|
|
}
|
|
|
|
|
|
+ public static final Map<Rounding.DateTimeUnit, String> ALLOWED_INTERVALS = Map.ofEntries(
|
|
|
+ entry(Rounding.DateTimeUnit.YEAR_OF_CENTURY, "year"),
|
|
|
+ entry(Rounding.DateTimeUnit.MONTH_OF_YEAR, "month"),
|
|
|
+ entry(Rounding.DateTimeUnit.DAY_OF_MONTH, "day"),
|
|
|
+ entry( Rounding.DateTimeUnit.HOUR_OF_DAY, "hour"),
|
|
|
+ entry(Rounding.DateTimeUnit.MINUTES_OF_HOUR, "minute"),
|
|
|
+ entry(Rounding.DateTimeUnit.SECOND_OF_MINUTE, "second")
|
|
|
+ );
|
|
|
+
|
|
|
/**
|
|
|
*
|
|
|
* Build roundings, computed dynamically as roundings are time zone dependent.
|
|
|
* The current implementation probably should not be invoked in a tight loop.
|
|
|
* @return Array of RoundingInfo
|
|
|
*/
|
|
|
- static RoundingInfo[] buildRoundings(ZoneId timeZone) {
|
|
|
+ static RoundingInfo[] buildRoundings(ZoneId timeZone, String minimumInterval) {
|
|
|
+
|
|
|
+ int indexToSliceFrom = 0;
|
|
|
+
|
|
|
RoundingInfo[] roundings = new RoundingInfo[6];
|
|
|
- roundings[0] = new RoundingInfo(createRounding(Rounding.DateTimeUnit.SECOND_OF_MINUTE, timeZone),
|
|
|
- 1000L, "s", 1, 5, 10, 30);
|
|
|
- roundings[1] = new RoundingInfo(createRounding(Rounding.DateTimeUnit.MINUTES_OF_HOUR, timeZone),
|
|
|
+ roundings[0] = new RoundingInfo(Rounding.DateTimeUnit.SECOND_OF_MINUTE,
|
|
|
+ timeZone, 1000L, "s",1, 5, 10, 30);
|
|
|
+ roundings[1] = new RoundingInfo(Rounding.DateTimeUnit.MINUTES_OF_HOUR, timeZone,
|
|
|
60 * 1000L, "m", 1, 5, 10, 30);
|
|
|
- roundings[2] = new RoundingInfo(createRounding(Rounding.DateTimeUnit.HOUR_OF_DAY, timeZone),
|
|
|
- 60 * 60 * 1000L, "h",1, 3, 12);
|
|
|
- roundings[3] = new RoundingInfo(createRounding(Rounding.DateTimeUnit.DAY_OF_MONTH, timeZone),
|
|
|
+ roundings[2] = new RoundingInfo(Rounding.DateTimeUnit.HOUR_OF_DAY, timeZone,
|
|
|
+ 60 * 60 * 1000L, "h", 1, 3, 12);
|
|
|
+ roundings[3] = new RoundingInfo(Rounding.DateTimeUnit.DAY_OF_MONTH, timeZone,
|
|
|
24 * 60 * 60 * 1000L, "d", 1, 7);
|
|
|
- roundings[4] = new RoundingInfo(createRounding(Rounding.DateTimeUnit.MONTH_OF_YEAR, timeZone),
|
|
|
+ roundings[4] = new RoundingInfo(Rounding.DateTimeUnit.MONTH_OF_YEAR, timeZone,
|
|
|
30 * 24 * 60 * 60 * 1000L, "M", 1, 3);
|
|
|
- roundings[5] = new RoundingInfo(createRounding(Rounding.DateTimeUnit.YEAR_OF_CENTURY, timeZone),
|
|
|
+ roundings[5] = new RoundingInfo(Rounding.DateTimeUnit.YEAR_OF_CENTURY, timeZone,
|
|
|
365 * 24 * 60 * 60 * 1000L, "y", 1, 5, 10, 20, 50, 100);
|
|
|
- return roundings;
|
|
|
+
|
|
|
+ for (int i = 0; i < roundings.length; i++) {
|
|
|
+ RoundingInfo roundingInfo = roundings[i];
|
|
|
+ if (roundingInfo.getDateTimeUnit().equals(minimumInterval)) {
|
|
|
+ indexToSliceFrom = i;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return Arrays.copyOfRange(roundings, indexToSliceFrom, roundings.length);
|
|
|
}
|
|
|
|
|
|
public static AutoDateHistogramAggregationBuilder parse(String aggregationName, XContentParser parser) throws IOException {
|
|
@@ -92,6 +116,22 @@ public class AutoDateHistogramAggregationBuilder
|
|
|
|
|
|
private int numBuckets = 10;
|
|
|
|
|
|
+ private String minimumIntervalExpression;
|
|
|
+
|
|
|
+ public String getMinimumIntervalExpression() {
|
|
|
+ return minimumIntervalExpression;
|
|
|
+ }
|
|
|
+
|
|
|
+ public AutoDateHistogramAggregationBuilder setMinimumIntervalExpression(String minimumIntervalExpression) {
|
|
|
+ if (minimumIntervalExpression != null && !ALLOWED_INTERVALS.containsValue(minimumIntervalExpression)) {
|
|
|
+ throw new IllegalArgumentException(MINIMUM_INTERVAL_FIELD.getPreferredName() +
|
|
|
+ " must be one of [" + ALLOWED_INTERVALS.values().toString() + "]");
|
|
|
+ }
|
|
|
+ this.minimumIntervalExpression = minimumIntervalExpression;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
/** Create a new builder with the given name. */
|
|
|
public AutoDateHistogramAggregationBuilder(String name) {
|
|
|
super(name, ValuesSourceType.NUMERIC, ValueType.DATE);
|
|
@@ -101,12 +141,17 @@ public class AutoDateHistogramAggregationBuilder
|
|
|
public AutoDateHistogramAggregationBuilder(StreamInput in) throws IOException {
|
|
|
super(in, ValuesSourceType.NUMERIC, ValueType.DATE);
|
|
|
numBuckets = in.readVInt();
|
|
|
+ //TODO[PCS] update after backport
|
|
|
+ if (in.getVersion().onOrAfter(Version.V_8_0_0)) {
|
|
|
+ minimumIntervalExpression = in.readOptionalString();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
protected AutoDateHistogramAggregationBuilder(AutoDateHistogramAggregationBuilder clone, Builder factoriesBuilder,
|
|
|
Map<String, Object> metaData) {
|
|
|
super(clone, factoriesBuilder, metaData);
|
|
|
this.numBuckets = clone.numBuckets;
|
|
|
+ this.minimumIntervalExpression = clone.minimumIntervalExpression;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
@@ -117,6 +162,9 @@ public class AutoDateHistogramAggregationBuilder
|
|
|
@Override
|
|
|
protected void innerWriteTo(StreamOutput out) throws IOException {
|
|
|
out.writeVInt(numBuckets);
|
|
|
+ if (out.getVersion().onOrAfter(Version.V_8_0_0)) {
|
|
|
+ out.writeOptionalString(minimumIntervalExpression);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
@Override
|
|
@@ -139,7 +187,7 @@ public class AutoDateHistogramAggregationBuilder
|
|
|
@Override
|
|
|
protected ValuesSourceAggregatorFactory<Numeric, ?> innerBuild(SearchContext context, ValuesSourceConfig<Numeric> config,
|
|
|
AggregatorFactory<?> parent, Builder subFactoriesBuilder) throws IOException {
|
|
|
- RoundingInfo[] roundings = buildRoundings(timeZone());
|
|
|
+ RoundingInfo[] roundings = buildRoundings(timeZone(), getMinimumIntervalExpression());
|
|
|
int maxRoundingInterval = Arrays.stream(roundings,0, roundings.length-1)
|
|
|
.map(rounding -> rounding.innerIntervals)
|
|
|
.flatMapToInt(Arrays::stream)
|
|
@@ -152,7 +200,9 @@ public class AutoDateHistogramAggregationBuilder
|
|
|
throw new IllegalArgumentException(NUM_BUCKETS_FIELD.getPreferredName()+
|
|
|
" must be less than " + bucketCeiling);
|
|
|
}
|
|
|
- return new AutoDateHistogramAggregatorFactory(name, config, numBuckets, roundings, context, parent, subFactoriesBuilder, metaData);
|
|
|
+ return new AutoDateHistogramAggregatorFactory(name, config, numBuckets, roundings, context, parent,
|
|
|
+ subFactoriesBuilder,
|
|
|
+ metaData);
|
|
|
}
|
|
|
|
|
|
static Rounding createRounding(Rounding.DateTimeUnit interval, ZoneId timeZone) {
|
|
@@ -167,18 +217,19 @@ public class AutoDateHistogramAggregationBuilder
|
|
|
@Override
|
|
|
protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException {
|
|
|
builder.field(NUM_BUCKETS_FIELD.getPreferredName(), numBuckets);
|
|
|
+ builder.field(MINIMUM_INTERVAL_FIELD.getPreferredName(), minimumIntervalExpression);
|
|
|
return builder;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
protected int innerHashCode() {
|
|
|
- return Objects.hash(numBuckets);
|
|
|
+ return Objects.hash(numBuckets, minimumIntervalExpression);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
protected boolean innerEquals(Object obj) {
|
|
|
AutoDateHistogramAggregationBuilder other = (AutoDateHistogramAggregationBuilder) obj;
|
|
|
- return Objects.equals(numBuckets, other.numBuckets);
|
|
|
+ return Objects.equals(numBuckets, other.numBuckets) && Objects.equals(minimumIntervalExpression, other.minimumIntervalExpression);
|
|
|
}
|
|
|
|
|
|
public static class RoundingInfo implements Writeable {
|
|
@@ -186,12 +237,22 @@ public class AutoDateHistogramAggregationBuilder
|
|
|
final int[] innerIntervals;
|
|
|
final long roughEstimateDurationMillis;
|
|
|
final String unitAbbreviation;
|
|
|
-
|
|
|
- public RoundingInfo(Rounding rounding, long roughEstimateDurationMillis, String unitAbbreviation, int... innerIntervals) {
|
|
|
- this.rounding = rounding;
|
|
|
+ final String dateTimeUnit;
|
|
|
+
|
|
|
+ public RoundingInfo(Rounding.DateTimeUnit dateTimeUnit,
|
|
|
+ ZoneId timeZone,
|
|
|
+ long roughEstimateDurationMillis,
|
|
|
+ String unitAbbreviation,
|
|
|
+ int... innerIntervals) {
|
|
|
+ this.rounding = createRounding(dateTimeUnit, timeZone);
|
|
|
this.roughEstimateDurationMillis = roughEstimateDurationMillis;
|
|
|
this.unitAbbreviation = unitAbbreviation;
|
|
|
this.innerIntervals = innerIntervals;
|
|
|
+ Objects.requireNonNull(dateTimeUnit, "dateTimeUnit cannot be null");
|
|
|
+ if (!ALLOWED_INTERVALS.containsKey(dateTimeUnit)) {
|
|
|
+ throw new IllegalArgumentException("dateTimeUnit must be one of " + ALLOWED_INTERVALS.keySet().toString());
|
|
|
+ }
|
|
|
+ this.dateTimeUnit = ALLOWED_INTERVALS.get(dateTimeUnit);
|
|
|
}
|
|
|
|
|
|
public RoundingInfo(StreamInput in) throws IOException {
|
|
@@ -199,6 +260,7 @@ public class AutoDateHistogramAggregationBuilder
|
|
|
roughEstimateDurationMillis = in.readVLong();
|
|
|
innerIntervals = in.readIntArray();
|
|
|
unitAbbreviation = in.readString();
|
|
|
+ dateTimeUnit = in.readString();
|
|
|
}
|
|
|
|
|
|
@Override
|
|
@@ -207,19 +269,22 @@ public class AutoDateHistogramAggregationBuilder
|
|
|
out.writeVLong(roughEstimateDurationMillis);
|
|
|
out.writeIntArray(innerIntervals);
|
|
|
out.writeString(unitAbbreviation);
|
|
|
+ out.writeString(dateTimeUnit);
|
|
|
}
|
|
|
|
|
|
public int getMaximumInnerInterval() {
|
|
|
return innerIntervals[innerIntervals.length - 1];
|
|
|
}
|
|
|
|
|
|
+ public String getDateTimeUnit() { return this.dateTimeUnit; }
|
|
|
+
|
|
|
public long getRoughEstimateDurationMillis() {
|
|
|
return roughEstimateDurationMillis;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public int hashCode() {
|
|
|
- return Objects.hash(rounding, Arrays.hashCode(innerIntervals));
|
|
|
+ return Objects.hash(rounding, Arrays.hashCode(innerIntervals), dateTimeUnit);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
@@ -231,8 +296,10 @@ public class AutoDateHistogramAggregationBuilder
|
|
|
return false;
|
|
|
}
|
|
|
RoundingInfo other = (RoundingInfo) obj;
|
|
|
- return Objects.equals(rounding, other.rounding) &&
|
|
|
- Objects.deepEquals(innerIntervals, other.innerIntervals);
|
|
|
+ return Objects.equals(rounding, other.rounding)
|
|
|
+ && Objects.deepEquals(innerIntervals, other.innerIntervals)
|
|
|
+ && Objects.equals(dateTimeUnit, other.dateTimeUnit)
|
|
|
+ ;
|
|
|
}
|
|
|
}
|
|
|
}
|