Skip to content

Commit bcb8ca0

Browse files
authored
Optimize date_historam's hard_bounds (backport of #66051) (#66061)
This allows `date_histogram`s with `hard_bounds` and `extended_bounds` to use the "as range" style optimizations introducedin #63643. There isn't any work to do for `exended_bounds` besides add a test. For `hard_bounds` we have to be careful when constructing the ranges that to filter.
1 parent cb1f603 commit bcb8ca0

File tree

3 files changed

+68
-10
lines changed

3 files changed

+68
-10
lines changed

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

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,6 @@ private static FromDateRange adaptIntoRangeOrNull(
148148
CardinalityUpperBound cardinality,
149149
Map<String, Object> metadata
150150
) throws IOException {
151-
if (hardBounds != null || extendedBounds != null) {
152-
return null;
153-
}
154151
long[] fixedRoundingPoints = preparedRounding.fixedRoundingPoints();
155152
if (fixedRoundingPoints == null) {
156153
return null;
@@ -169,11 +166,7 @@ private static FromDateRange adaptIntoRangeOrNull(
169166
if (rangeSupplier == null) {
170167
return null;
171168
}
172-
RangeAggregator.Range[] ranges = new RangeAggregator.Range[fixedRoundingPoints.length];
173-
for (int i = 0; i < fixedRoundingPoints.length - 1; i++) {
174-
ranges[i] = new RangeAggregator.Range(null, (double) fixedRoundingPoints[i], (double) fixedRoundingPoints[i + 1]);
175-
}
176-
ranges[ranges.length - 1] = new RangeAggregator.Range(null, (double) fixedRoundingPoints[fixedRoundingPoints.length - 1], null);
169+
RangeAggregator.Range[] ranges = ranges(hardBounds, fixedRoundingPoints);
177170
return new DateHistogramAggregator.FromDateRange(
178171
parent,
179172
factories,
@@ -200,6 +193,27 @@ private static FromDateRange adaptIntoRangeOrNull(
200193
);
201194
}
202195

196+
private static RangeAggregator.Range[] ranges(LongBounds hardBounds, long[] fixedRoundingPoints) {
197+
if (hardBounds == null) {
198+
RangeAggregator.Range[] ranges = new RangeAggregator.Range[fixedRoundingPoints.length];
199+
for (int i = 0; i < fixedRoundingPoints.length - 1; i++) {
200+
ranges[i] = new RangeAggregator.Range(null, (double) fixedRoundingPoints[i], (double) fixedRoundingPoints[i + 1]);
201+
}
202+
ranges[ranges.length - 1] = new RangeAggregator.Range(null, (double) fixedRoundingPoints[fixedRoundingPoints.length - 1], null);
203+
return ranges;
204+
}
205+
List<RangeAggregator.Range> ranges = new ArrayList<>(fixedRoundingPoints.length);
206+
for (int i = 0; i < fixedRoundingPoints.length - 1; i++) {
207+
if (hardBounds.contain(fixedRoundingPoints[i])) {
208+
ranges.add(new RangeAggregator.Range(null, (double) fixedRoundingPoints[i], (double) fixedRoundingPoints[i + 1]));
209+
}
210+
}
211+
if (hardBounds.contain(fixedRoundingPoints[fixedRoundingPoints.length - 1])) {
212+
ranges.add(new RangeAggregator.Range(null, (double) fixedRoundingPoints[fixedRoundingPoints.length - 1], null));
213+
}
214+
return ranges.toArray(new RangeAggregator.Range[0]);
215+
}
216+
203217
private final ValuesSource.Numeric valuesSource;
204218
private final DocValueFormat formatter;
205219
private final Rounding rounding;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ private void addEmptyBuckets(List<Bucket> list, ReduceContext reduceContext) {
366366
LongBounds bounds = emptyBucketInfo.bounds;
367367
ListIterator<Bucket> iter = list.listIterator();
368368

369-
// first adding all the empty buckets *before* the actual data (based on th extended_bounds.min the user requested)
369+
// first adding all the empty buckets *before* the actual data (based on the extended_bounds.min the user requested)
370370
InternalAggregations reducedEmptySubAggs = InternalAggregations.reduce(Collections.singletonList(emptyBucketInfo.subAggregations),
371371
reduceContext);
372372
if (bounds != null) {

server/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramAggregatorTests.java

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,10 @@
3636
import org.elasticsearch.search.aggregations.AggregationBuilder;
3737
import org.elasticsearch.search.aggregations.Aggregator;
3838
import org.elasticsearch.search.aggregations.BucketOrder;
39+
import org.elasticsearch.search.aggregations.InternalAggregation.ReduceContext;
3940
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
4041
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
42+
import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator.PipelineTree;
4143
import org.elasticsearch.search.aggregations.support.AggregationContext;
4244
import org.elasticsearch.search.aggregations.support.AggregationInspectionHelper;
4345
import org.hamcrest.Matcher;
@@ -1200,9 +1202,44 @@ public void testMissingValueDoesNotUseFromRange() throws IOException {
12001202
);
12011203
}
12021204

1205+
public void testExtendedBoundsUsesFromRange() throws IOException {
1206+
aggregationImplementationChoiceTestCase(
1207+
aggregableDateFieldType(false, true, DateFormatter.forPattern("yyyy")),
1208+
org.elasticsearch.common.collect.List.of("2017", "2018"),
1209+
org.elasticsearch.common.collect.List.of("2016", "2017", "2018", "2019"),
1210+
new DateHistogramAggregationBuilder("test").field(AGGREGABLE_DATE)
1211+
.calendarInterval(DateHistogramInterval.YEAR)
1212+
.extendedBounds(new LongBounds("2016", "2019"))
1213+
.minDocCount(0),
1214+
true
1215+
);
1216+
}
1217+
1218+
public void testHardBoundsUsesFromRange() throws IOException {
1219+
aggregationImplementationChoiceTestCase(
1220+
aggregableDateFieldType(false, true, DateFormatter.forPattern("yyyy")),
1221+
org.elasticsearch.common.collect.List.of("2016", "2017", "2018", "2019"),
1222+
org.elasticsearch.common.collect.List.of("2017", "2018"),
1223+
new DateHistogramAggregationBuilder("test").field(AGGREGABLE_DATE)
1224+
.calendarInterval(DateHistogramInterval.YEAR)
1225+
.hardBounds(new LongBounds("2017", "2019")),
1226+
true
1227+
);
1228+
}
1229+
1230+
private void aggregationImplementationChoiceTestCase(
1231+
DateFieldMapper.DateFieldType ft,
1232+
List<String> data,
1233+
DateHistogramAggregationBuilder builder,
1234+
boolean usesFromRange
1235+
) throws IOException {
1236+
aggregationImplementationChoiceTestCase(ft, data, data, builder, usesFromRange);
1237+
}
1238+
12031239
private void aggregationImplementationChoiceTestCase(
12041240
DateFieldMapper.DateFieldType ft,
12051241
List<String> data,
1242+
List<String> resultingBucketKeys,
12061243
DateHistogramAggregationBuilder builder,
12071244
boolean usesFromRange
12081245
) throws IOException {
@@ -1227,7 +1264,14 @@ private void aggregationImplementationChoiceTestCase(
12271264
agg.preCollection();
12281265
context.searcher().search(context.query(), agg);
12291266
InternalDateHistogram result = (InternalDateHistogram) agg.buildTopLevel();
1230-
assertThat(result.getBuckets().stream().map(InternalDateHistogram.Bucket::getKeyAsString).collect(toList()), equalTo(data));
1267+
result = (InternalDateHistogram) result.reduce(
1268+
org.elasticsearch.common.collect.List.of(result),
1269+
ReduceContext.forFinalReduction(context.bigArrays(), null, context.multiBucketConsumer(), PipelineTree.EMPTY)
1270+
);
1271+
assertThat(
1272+
result.getBuckets().stream().map(InternalDateHistogram.Bucket::getKeyAsString).collect(toList()),
1273+
equalTo(resultingBucketKeys)
1274+
);
12311275
}
12321276
}
12331277
}

0 commit comments

Comments
 (0)