Skip to content

Commit aa87f46

Browse files
Optimize date rounding (#128687)
1 parent 15dd896 commit aa87f46

File tree

11 files changed

+137
-64
lines changed

11 files changed

+137
-64
lines changed

modules/aggregations/src/main/java/org/elasticsearch/aggregations/bucket/histogram/AutoDateHistogramAggregationBuilder.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public class AutoDateHistogramAggregationBuilder extends ValuesSourceAggregation
6464
entry(Rounding.DateTimeUnit.MONTH_OF_YEAR, "month"),
6565
entry(Rounding.DateTimeUnit.DAY_OF_MONTH, "day"),
6666
entry(Rounding.DateTimeUnit.HOUR_OF_DAY, "hour"),
67-
entry(Rounding.DateTimeUnit.MINUTES_OF_HOUR, "minute"),
67+
entry(Rounding.DateTimeUnit.MINUTE_OF_HOUR, "minute"),
6868
entry(Rounding.DateTimeUnit.SECOND_OF_MINUTE, "second")
6969
);
7070

@@ -84,7 +84,7 @@ static RoundingInfo[] buildRoundings(ZoneId timeZone, String minimumInterval) {
8484

8585
RoundingInfo[] roundings = new RoundingInfo[6];
8686
roundings[0] = new RoundingInfo(Rounding.DateTimeUnit.SECOND_OF_MINUTE, timeZone, 1000L, "s", 1, 5, 10, 30);
87-
roundings[1] = new RoundingInfo(Rounding.DateTimeUnit.MINUTES_OF_HOUR, timeZone, 60 * 1000L, "m", 1, 5, 10, 30);
87+
roundings[1] = new RoundingInfo(Rounding.DateTimeUnit.MINUTE_OF_HOUR, timeZone, 60 * 1000L, "m", 1, 5, 10, 30);
8888
roundings[2] = new RoundingInfo(Rounding.DateTimeUnit.HOUR_OF_DAY, timeZone, 60 * 60 * 1000L, "h", 1, 3, 12);
8989
roundings[3] = new RoundingInfo(Rounding.DateTimeUnit.DAY_OF_MONTH, timeZone, 24 * 60 * 60 * 1000L, "d", 1, 7);
9090
roundings[4] = new RoundingInfo(Rounding.DateTimeUnit.MONTH_OF_YEAR, timeZone, 30 * 24 * 60 * 60 * 1000L, "M", 1, 3);

modules/aggregations/src/test/java/org/elasticsearch/aggregations/bucket/histogram/InternalAutoDateHistogramTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ public void testGetAppropriateRoundingUsesCorrectIntervals() {
108108
// an innerInterval that is quite large, such that targetBuckets * roundings[i].getMaximumInnerInterval()
109109
// will be larger than the estimate.
110110
roundings[0] = new RoundingInfo(Rounding.DateTimeUnit.SECOND_OF_MINUTE, timeZone, 1000L, "s", 1000);
111-
roundings[1] = new RoundingInfo(Rounding.DateTimeUnit.MINUTES_OF_HOUR, timeZone, 60 * 1000L, "m", 1, 5, 10, 30);
111+
roundings[1] = new RoundingInfo(Rounding.DateTimeUnit.MINUTE_OF_HOUR, timeZone, 60 * 1000L, "m", 1, 5, 10, 30);
112112
roundings[2] = new RoundingInfo(Rounding.DateTimeUnit.HOUR_OF_DAY, timeZone, 60 * 60 * 1000L, "h", 1, 3, 12);
113113

114114
OffsetDateTime timestamp = Instant.parse("2018-01-01T00:00:01.000Z").atOffset(ZoneOffset.UTC);

server/src/main/java/org/elasticsearch/common/Rounding.java

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ public enum DateTimeUnit {
6161

6262
@Override
6363
long roundFloor(long utcMillis, int multiplier) {
64-
return DateUtils.roundWeekIntervalOfWeekYear(utcMillis, multiplier);
64+
assert multiplier == 1;
65+
return DateUtils.roundWeekOfWeekYear(utcMillis);
6566
}
6667

6768
@Override
@@ -74,7 +75,8 @@ long extraLocalOffsetLookup() {
7475

7576
@Override
7677
long roundFloor(long utcMillis, int multiplier) {
77-
return multiplier == 1 ? DateUtils.roundYear(utcMillis) : DateUtils.roundYearInterval(utcMillis, multiplier);
78+
assert multiplier == 1;
79+
return DateUtils.roundYear(utcMillis);
7880
}
7981

8082
@Override
@@ -87,9 +89,8 @@ long extraLocalOffsetLookup() {
8789

8890
@Override
8991
long roundFloor(long utcMillis, int multiplier) {
90-
return multiplier == 1
91-
? DateUtils.roundQuarterOfYear(utcMillis)
92-
: DateUtils.roundIntervalMonthOfYear(utcMillis, multiplier * 3);
92+
assert multiplier == 1;
93+
return DateUtils.roundQuarterOfYear(utcMillis);
9394
}
9495

9596
@Override
@@ -102,7 +103,8 @@ long extraLocalOffsetLookup() {
102103

103104
@Override
104105
long roundFloor(long utcMillis, int multiplier) {
105-
return multiplier == 1 ? DateUtils.roundMonthOfYear(utcMillis) : DateUtils.roundIntervalMonthOfYear(utcMillis, multiplier);
106+
assert multiplier == 1;
107+
return DateUtils.roundMonthOfYear(utcMillis);
106108
}
107109

108110
@Override
@@ -113,7 +115,8 @@ long extraLocalOffsetLookup() {
113115
DAY_OF_MONTH((byte) 5, "day", ChronoField.DAY_OF_MONTH, true, ChronoField.DAY_OF_MONTH.getBaseUnit().getDuration().toMillis()) {
114116
@Override
115117
long roundFloor(long utcMillis, int multiplier) {
116-
return DateUtils.roundFloor(utcMillis, this.ratio * multiplier);
118+
assert multiplier == 1;
119+
return DateUtils.roundFloor(utcMillis, this.ratio);
117120
}
118121

119122
@Override
@@ -124,15 +127,16 @@ long extraLocalOffsetLookup() {
124127
HOUR_OF_DAY((byte) 6, "hour", ChronoField.HOUR_OF_DAY, true, ChronoField.HOUR_OF_DAY.getBaseUnit().getDuration().toMillis()) {
125128
@Override
126129
long roundFloor(long utcMillis, int multiplier) {
127-
return DateUtils.roundFloor(utcMillis, ratio * multiplier);
130+
assert multiplier == 1;
131+
return DateUtils.roundFloor(utcMillis, ratio);
128132
}
129133

130134
@Override
131135
long extraLocalOffsetLookup() {
132136
return ratio;
133137
}
134138
},
135-
MINUTES_OF_HOUR(
139+
MINUTE_OF_HOUR(
136140
(byte) 7,
137141
"minute",
138142
ChronoField.MINUTE_OF_HOUR,
@@ -141,7 +145,8 @@ long extraLocalOffsetLookup() {
141145
) {
142146
@Override
143147
long roundFloor(long utcMillis, int multiplier) {
144-
return DateUtils.roundFloor(utcMillis, ratio * multiplier);
148+
assert multiplier == 1;
149+
return DateUtils.roundFloor(utcMillis, ratio);
145150
}
146151

147152
@Override
@@ -158,13 +163,40 @@ long extraLocalOffsetLookup() {
158163
) {
159164
@Override
160165
long roundFloor(long utcMillis, int multiplier) {
161-
return DateUtils.roundFloor(utcMillis, ratio * multiplier);
166+
assert multiplier == 1;
167+
return DateUtils.roundFloor(utcMillis, ratio);
162168
}
163169

164170
@Override
165171
long extraLocalOffsetLookup() {
166172
return ratio;
167173
}
174+
},
175+
YEARS_OF_CENTURY((byte) 9, "years", ChronoField.YEAR_OF_ERA, false, 12) {
176+
private final long extraLocalOffsetLookup = TimeUnit.DAYS.toMillis(366);
177+
178+
@Override
179+
long roundFloor(long utcMillis, int multiplier) {
180+
return multiplier == 1 ? DateUtils.roundYear(utcMillis) : DateUtils.roundYearInterval(utcMillis, multiplier);
181+
}
182+
183+
@Override
184+
long extraLocalOffsetLookup() {
185+
return extraLocalOffsetLookup;
186+
}
187+
},
188+
MONTHS_OF_YEAR((byte) 10, "months", ChronoField.MONTH_OF_YEAR, false, 1) {
189+
private final long extraLocalOffsetLookup = TimeUnit.DAYS.toMillis(31);
190+
191+
@Override
192+
long roundFloor(long utcMillis, int multiplier) {
193+
return multiplier == 1 ? DateUtils.roundMonthOfYear(utcMillis) : DateUtils.roundIntervalMonthOfYear(utcMillis, multiplier);
194+
}
195+
196+
@Override
197+
long extraLocalOffsetLookup() {
198+
return extraLocalOffsetLookup;
199+
}
168200
};
169201

170202
private final byte id;
@@ -222,8 +254,10 @@ public static DateTimeUnit resolve(byte id) {
222254
case 4 -> MONTH_OF_YEAR;
223255
case 5 -> DAY_OF_MONTH;
224256
case 6 -> HOUR_OF_DAY;
225-
case 7 -> MINUTES_OF_HOUR;
257+
case 7 -> MINUTE_OF_HOUR;
226258
case 8 -> SECOND_OF_MINUTE;
259+
case 9 -> YEARS_OF_CENTURY;
260+
case 10 -> MONTHS_OF_YEAR;
227261
default -> throw new ElasticsearchException("Unknown date time unit id [" + id + "]");
228262
};
229263
}
@@ -480,7 +514,7 @@ private LocalDateTime truncateLocalDateTime(LocalDateTime localDateTime) {
480514
case SECOND_OF_MINUTE:
481515
return localDateTime.withNano(0);
482516

483-
case MINUTES_OF_HOUR:
517+
case MINUTE_OF_HOUR:
484518
return LocalDateTime.of(
485519
localDateTime.getYear(),
486520
localDateTime.getMonthValue(),
@@ -517,6 +551,12 @@ private LocalDateTime truncateLocalDateTime(LocalDateTime localDateTime) {
517551
case YEAR_OF_CENTURY:
518552
return LocalDateTime.of(LocalDate.of(localDateTime.getYear(), 1, 1), LocalTime.MIDNIGHT);
519553

554+
case YEARS_OF_CENTURY:
555+
return LocalDateTime.of(LocalDate.of(localDateTime.getYear(), 1, 1), LocalTime.MIDNIGHT);
556+
557+
case MONTHS_OF_YEAR:
558+
return LocalDateTime.of(localDateTime.getYear(), localDateTime.getMonthValue(), 1, 0, 0);
559+
520560
default:
521561
throw new IllegalArgumentException("NOT YET IMPLEMENTED for unit " + unit);
522562
}
@@ -879,6 +919,8 @@ private LocalDateTime nextRelevantMidnight(LocalDateTime localMidnight) {
879919
case MONTH_OF_YEAR -> localMidnight.plus(1, ChronoUnit.MONTHS);
880920
case QUARTER_OF_YEAR -> localMidnight.plus(3, ChronoUnit.MONTHS);
881921
case YEAR_OF_CENTURY -> localMidnight.plus(1, ChronoUnit.YEARS);
922+
case YEARS_OF_CENTURY -> localMidnight.plus(1, ChronoUnit.YEARS);
923+
case MONTHS_OF_YEAR -> localMidnight.plus(1, ChronoUnit.MONTHS);
882924
default -> throw new IllegalArgumentException("Unknown round-to-midnight unit: " + unit);
883925
};
884926
}

server/src/main/java/org/elasticsearch/common/time/DateUtils.java

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -479,24 +479,7 @@ public static long roundYearInterval(final long utcMillis, final int yearInterva
479479
* @return The milliseconds since the epoch rounded down to the beginning of the week based on week year
480480
*/
481481
public static long roundWeekOfWeekYear(final long utcMillis) {
482-
return roundWeekIntervalOfWeekYear(utcMillis, 1);
483-
}
484-
485-
/**
486-
* Round down to the beginning of the nearest multiple of the specified week interval based on week year
487-
* <p>
488-
* Consider Sun Dec 29 1969 00:00:00.000 as the start of the first week.
489-
* @param utcMillis the milliseconds since the epoch
490-
* @param weekInterval the interval in weeks to round down to
491-
*
492-
* @return The milliseconds since the epoch rounded down to the beginning of the nearest multiple of the
493-
* specified week interval based on week year
494-
*/
495-
public static long roundWeekIntervalOfWeekYear(final long utcMillis, final int weekInterval) {
496-
if (weekInterval <= 0) {
497-
throw new IllegalArgumentException("week interval must be strictly positive, got [" + weekInterval + "]");
498-
}
499-
return roundFloor(utcMillis + 3 * 86400 * 1000L, 604800000L * weekInterval) - 3 * 86400 * 1000L;
482+
return roundFloor(utcMillis + 3 * 86400 * 1000L, 604800000L) - 3 * 86400 * 1000L;
500483
}
501484

502485
/**

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,12 @@ public class DateHistogramAggregationBuilder extends ValuesSourceAggregationBuil
6767
entry("1d", Rounding.DateTimeUnit.DAY_OF_MONTH),
6868
entry("hour", Rounding.DateTimeUnit.HOUR_OF_DAY),
6969
entry("1h", Rounding.DateTimeUnit.HOUR_OF_DAY),
70-
entry("minute", Rounding.DateTimeUnit.MINUTES_OF_HOUR),
71-
entry("1m", Rounding.DateTimeUnit.MINUTES_OF_HOUR),
70+
entry("minute", Rounding.DateTimeUnit.MINUTE_OF_HOUR),
71+
entry("1m", Rounding.DateTimeUnit.MINUTE_OF_HOUR),
7272
entry("second", Rounding.DateTimeUnit.SECOND_OF_MINUTE),
73-
entry("1s", Rounding.DateTimeUnit.SECOND_OF_MINUTE)
73+
entry("1s", Rounding.DateTimeUnit.SECOND_OF_MINUTE),
74+
entry("years", Rounding.DateTimeUnit.YEARS_OF_CENTURY),
75+
entry("months", Rounding.DateTimeUnit.MONTHS_OF_YEAR)
7476
);
7577

7678
public static final ObjectParser<DateHistogramAggregationBuilder, String> PARSER = ObjectParser.fromBuilder(

0 commit comments

Comments
 (0)