Skip to content

Commit dfbe642

Browse files
authored
[Transform] Support "offset" parameter in DateHistogramGroupSource (#93203)
1 parent 0ddbd25 commit dfbe642

File tree

10 files changed

+290
-36
lines changed

10 files changed

+290
-36
lines changed

docs/changelog/93203.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 93203
2+
summary: Support "offset" parameter in `DateHistogramGroupSource`
3+
area: Transform
4+
type: enhancement
5+
issues: []

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/transforms/pivot/DateHistogramGroupSource.java

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@
66
*/
77
package org.elasticsearch.xpack.core.transform.transforms.pivot;
88

9+
import org.elasticsearch.Version;
910
import org.elasticsearch.common.Rounding;
1011
import org.elasticsearch.common.io.stream.StreamInput;
1112
import org.elasticsearch.common.io.stream.StreamOutput;
1213
import org.elasticsearch.common.io.stream.Writeable;
1314
import org.elasticsearch.core.TimeValue;
1415
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramAggregationBuilder;
1516
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
17+
import org.elasticsearch.search.aggregations.bucket.histogram.Histogram;
1618
import org.elasticsearch.xcontent.ConstructingObjectParser;
1719
import org.elasticsearch.xcontent.ObjectParser;
1820
import org.elasticsearch.xcontent.ParseField;
@@ -197,26 +199,41 @@ private void writeInterval(Interval anInterval, StreamOutput out) throws IOExcep
197199

198200
private static final String NAME = "data_frame_date_histogram_group";
199201
private static final ParseField TIME_ZONE = new ParseField("time_zone");
202+
private static final ParseField OFFSET = Histogram.OFFSET_FIELD;
200203

201204
private static final ConstructingObjectParser<DateHistogramGroupSource, Void> STRICT_PARSER = createParser(false);
202205
private static final ConstructingObjectParser<DateHistogramGroupSource, Void> LENIENT_PARSER = createParser(true);
203206

204207
private final Interval interval;
205208
private final ZoneId timeZone;
206209
private final Rounding.Prepared rounding;
207-
208-
public DateHistogramGroupSource(String field, ScriptConfig scriptConfig, boolean missingBucket, Interval interval, ZoneId timeZone) {
210+
private final long offset;
211+
212+
public DateHistogramGroupSource(
213+
String field,
214+
ScriptConfig scriptConfig,
215+
boolean missingBucket,
216+
Interval interval,
217+
ZoneId timeZone,
218+
Long offset
219+
) {
209220
super(field, scriptConfig, missingBucket);
210221
this.interval = interval;
211222
this.timeZone = timeZone;
212-
rounding = buildRounding();
223+
this.offset = offset != null ? offset : 0;
224+
this.rounding = buildRounding();
213225
}
214226

215227
public DateHistogramGroupSource(StreamInput in) throws IOException {
216228
super(in);
217229
this.interval = readInterval(in);
218230
this.timeZone = in.readOptionalZoneId();
219-
rounding = buildRounding();
231+
if (in.getVersion().onOrAfter(Version.V_8_7_0)) {
232+
this.offset = in.readLong();
233+
} else {
234+
this.offset = 0;
235+
}
236+
this.rounding = buildRounding();
220237
}
221238

222239
private Rounding.Prepared buildRounding() {
@@ -231,6 +248,7 @@ private Rounding.Prepared buildRounding() {
231248
if (timeZone != null) {
232249
roundingBuilder.timeZone(timeZone);
233250
}
251+
roundingBuilder.offset(offset);
234252
return roundingBuilder.build().prepareForUnknown();
235253
}
236254

@@ -242,6 +260,7 @@ private static ConstructingObjectParser<DateHistogramGroupSource, Void> createPa
242260
String fixedInterval = (String) args[3];
243261
String calendarInterval = (String) args[4];
244262
ZoneId zoneId = (ZoneId) args[5];
263+
Long offset = (Long) args[6];
245264

246265
Interval interval = null;
247266

@@ -255,7 +274,7 @@ private static ConstructingObjectParser<DateHistogramGroupSource, Void> createPa
255274
throw new IllegalArgumentException("You must specify either fixed_interval or calendar_interval, found none");
256275
}
257276

258-
return new DateHistogramGroupSource(field, scriptConfig, missingBucket, interval, zoneId);
277+
return new DateHistogramGroupSource(field, scriptConfig, missingBucket, interval, zoneId, offset);
259278
});
260279

261280
declareValuesSourceFields(parser, lenient);
@@ -271,6 +290,14 @@ private static ConstructingObjectParser<DateHistogramGroupSource, Void> createPa
271290
}
272291
}, TIME_ZONE, ObjectParser.ValueType.LONG);
273292

293+
parser.declareField(optionalConstructorArg(), p -> {
294+
if (p.currentToken() == XContentParser.Token.VALUE_NUMBER) {
295+
return p.longValue();
296+
} else {
297+
return DateHistogramAggregationBuilder.parseStringOffset(p.text());
298+
}
299+
}, OFFSET, ObjectParser.ValueType.LONG);
300+
274301
return parser;
275302
}
276303

@@ -295,11 +322,18 @@ public Rounding.Prepared getRounding() {
295322
return rounding;
296323
}
297324

325+
public long getOffset() {
326+
return offset;
327+
}
328+
298329
@Override
299330
public void writeTo(StreamOutput out) throws IOException {
300331
super.writeTo(out);
301332
writeInterval(interval, out);
302333
out.writeOptionalZoneId(timeZone);
334+
if (out.getVersion().onOrAfter(Version.V_8_7_0)) {
335+
out.writeLong(offset);
336+
}
303337
}
304338

305339
@Override
@@ -310,6 +344,9 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
310344
if (timeZone != null) {
311345
builder.field(TIME_ZONE.getPreferredName(), timeZone.toString());
312346
}
347+
if (offset != 0) {
348+
builder.field(OFFSET.getPreferredName(), offset);
349+
}
313350
builder.endObject();
314351
return builder;
315352
}
@@ -330,11 +367,12 @@ public boolean equals(Object other) {
330367
&& Objects.equals(this.field, that.field)
331368
&& Objects.equals(this.scriptConfig, that.scriptConfig)
332369
&& Objects.equals(this.interval, that.interval)
333-
&& Objects.equals(this.timeZone, that.timeZone);
370+
&& Objects.equals(this.timeZone, that.timeZone)
371+
&& this.offset == that.offset;
334372
}
335373

336374
@Override
337375
public int hashCode() {
338-
return Objects.hash(field, scriptConfig, missingBucket, interval, timeZone);
376+
return Objects.hash(field, scriptConfig, missingBucket, interval, timeZone, offset);
339377
}
340378
}

x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/transforms/pivot/DateHistogramGroupSourceTests.java

Lines changed: 136 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@
88
package org.elasticsearch.xpack.core.transform.transforms.pivot;
99

1010
import org.elasticsearch.Version;
11+
import org.elasticsearch.common.Rounding;
1112
import org.elasticsearch.common.io.stream.BytesStreamOutput;
1213
import org.elasticsearch.common.io.stream.StreamInput;
1314
import org.elasticsearch.common.io.stream.Writeable.Reader;
1415
import org.elasticsearch.common.time.DateFormatter;
1516
import org.elasticsearch.common.time.DateFormatters;
17+
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramAggregationBuilder;
1618
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
1719
import org.elasticsearch.test.AbstractXContentSerializingTestCase;
1820
import org.elasticsearch.test.VersionUtils;
@@ -23,6 +25,7 @@
2325
import java.time.temporal.TemporalAccessor;
2426

2527
import static org.hamcrest.Matchers.equalTo;
28+
import static org.hamcrest.Matchers.notNullValue;
2629

2730
public class DateHistogramGroupSourceTests extends AbstractXContentSerializingTestCase<DateHistogramGroupSource> {
2831

@@ -58,6 +61,7 @@ public static DateHistogramGroupSource randomDateHistogramGroupSource(Version ve
5861
field = fieldPrefix + randomAlphaOfLengthBetween(1, 20);
5962
}
6063
boolean missingBucket = version.onOrAfter(Version.V_7_10_0) ? randomBoolean() : false;
64+
Long offset = version.onOrAfter(Version.V_8_7_0) ? randomOffset() : null;
6165

6266
DateHistogramGroupSource dateHistogramGroupSource;
6367
if (randomBoolean()) {
@@ -66,7 +70,8 @@ public static DateHistogramGroupSource randomDateHistogramGroupSource(Version ve
6670
scriptConfig,
6771
missingBucket,
6872
new DateHistogramGroupSource.FixedInterval(new DateHistogramInterval(randomTimeValue(1, 100, "d", "h", "ms", "s", "m"))),
69-
randomBoolean() ? randomZone() : null
73+
randomBoolean() ? randomZone() : null,
74+
randomBoolean() ? offset : null
7075
);
7176
} else {
7277
dateHistogramGroupSource = new DateHistogramGroupSource(
@@ -76,7 +81,8 @@ public static DateHistogramGroupSource randomDateHistogramGroupSource(Version ve
7681
new DateHistogramGroupSource.CalendarInterval(
7782
new DateHistogramInterval(randomTimeValue(1, 1, "m", "h", "d", "w", "M", "q", "y"))
7883
),
79-
randomBoolean() ? randomZone() : null
84+
randomBoolean() ? randomZone() : null,
85+
randomBoolean() ? offset : null
8086
);
8187
}
8288

@@ -115,58 +121,161 @@ protected Reader<DateHistogramGroupSource> instanceReader() {
115121
return DateHistogramGroupSource::new;
116122
}
117123

124+
public void testOffset() {
125+
{
126+
DateHistogramGroupSource dateHistogramGroupSource = new DateHistogramGroupSource(
127+
null,
128+
null,
129+
false,
130+
new DateHistogramGroupSource.FixedInterval(new DateHistogramInterval("1d")),
131+
null,
132+
null
133+
);
134+
assertThat(dateHistogramGroupSource.getOffset(), equalTo(0L));
135+
}
136+
{
137+
DateHistogramGroupSource dateHistogramGroupSource = new DateHistogramGroupSource(
138+
null,
139+
null,
140+
false,
141+
new DateHistogramGroupSource.FixedInterval(new DateHistogramInterval("1d")),
142+
null,
143+
0L
144+
);
145+
assertThat(dateHistogramGroupSource.getOffset(), equalTo(0L));
146+
}
147+
{
148+
DateHistogramGroupSource dateHistogramGroupSource = new DateHistogramGroupSource(
149+
null,
150+
null,
151+
false,
152+
new DateHistogramGroupSource.FixedInterval(new DateHistogramInterval("1d")),
153+
null,
154+
DateHistogramAggregationBuilder.parseStringOffset("-1h")
155+
);
156+
assertThat(dateHistogramGroupSource.getOffset(), equalTo(-3_600_000L));
157+
}
158+
{
159+
DateHistogramGroupSource dateHistogramGroupSource = new DateHistogramGroupSource(
160+
null,
161+
null,
162+
false,
163+
new DateHistogramGroupSource.FixedInterval(new DateHistogramInterval("1d")),
164+
null,
165+
DateHistogramAggregationBuilder.parseStringOffset("+1h")
166+
);
167+
assertThat(dateHistogramGroupSource.getOffset(), equalTo(3_600_000L));
168+
}
169+
}
170+
118171
public void testRoundingDateHistogramFixedInterval() {
119-
String field = randomBoolean() ? null : randomAlphaOfLengthBetween(1, 20);
120172
DateHistogramGroupSource dateHistogramGroupSource = new DateHistogramGroupSource(
121-
field,
173+
randomBoolean() ? null : randomAlphaOfLengthBetween(1, 20),
122174
null,
123175
randomBoolean(),
124176
new DateHistogramGroupSource.FixedInterval(new DateHistogramInterval("1d")),
177+
null,
125178
null
126179
);
127180

128-
// not meant to be complete rounding tests, see {@link RoundingTests} for more
129-
assertNotNull(dateHistogramGroupSource.getRounding());
181+
Rounding.Prepared rounding = dateHistogramGroupSource.getRounding();
182+
assertThat(rounding, notNullValue());
130183

131-
assertThat(
132-
dateHistogramGroupSource.getRounding().round(time("2020-03-26T23:59:59.000Z")),
133-
equalTo(time("2020-03-26T00:00:00.000Z"))
134-
);
135-
assertThat(
136-
dateHistogramGroupSource.getRounding().round(time("2020-03-26T00:00:01.000Z")),
137-
equalTo(time("2020-03-26T00:00:00.000Z"))
138-
);
184+
// not meant to be complete rounding tests, see {@link RoundingTests} for more
185+
assertThat(rounding.round(time("2020-03-25T23:59:59.000Z")), equalTo(time("2020-03-25T00:00:00.000Z")));
186+
assertThat(rounding.round(time("2020-03-26T00:00:00.000Z")), equalTo(time("2020-03-26T00:00:00.000Z")));
187+
assertThat(rounding.round(time("2020-03-26T00:00:01.000Z")), equalTo(time("2020-03-26T00:00:00.000Z")));
188+
assertThat(rounding.round(time("2020-03-26T23:59:59.000Z")), equalTo(time("2020-03-26T00:00:00.000Z")));
189+
assertThat(rounding.round(time("2020-03-27T00:00:00.000Z")), equalTo(time("2020-03-27T00:00:00.000Z")));
139190
}
140191

141192
public void testRoundingDateHistogramCalendarInterval() {
142-
String field = randomBoolean() ? null : randomAlphaOfLengthBetween(1, 20);
143193
DateHistogramGroupSource dateHistogramGroupSource = new DateHistogramGroupSource(
144-
field,
194+
randomBoolean() ? null : randomAlphaOfLengthBetween(1, 20),
145195
null,
146196
randomBoolean(),
147197
new DateHistogramGroupSource.CalendarInterval(new DateHistogramInterval("1w")),
198+
null,
148199
null
149200
);
150201

202+
Rounding.Prepared rounding = dateHistogramGroupSource.getRounding();
203+
assertThat(rounding, notNullValue());
204+
151205
// not meant to be complete rounding tests, see {@link RoundingTests} for more
152-
assertNotNull(dateHistogramGroupSource.getRounding());
206+
assertThat(rounding.round(time("2020-03-21T23:59:59.000Z")), equalTo(time("2020-03-16T00:00:00.000Z")));
207+
assertThat(rounding.round(time("2020-03-22T00:00:00.000Z")), equalTo(time("2020-03-16T00:00:00.000Z")));
208+
assertThat(rounding.round(time("2020-03-22T23:59:59.000Z")), equalTo(time("2020-03-16T00:00:00.000Z")));
209+
assertThat(rounding.round(time("2020-03-23T00:00:00.000Z")), equalTo(time("2020-03-23T00:00:00.000Z")));
210+
assertThat(rounding.round(time("2020-03-23T00:00:01.000Z")), equalTo(time("2020-03-23T00:00:00.000Z")));
211+
assertThat(rounding.round(time("2020-03-24T00:00:00.000Z")), equalTo(time("2020-03-23T00:00:00.000Z")));
212+
assertThat(rounding.round(time("2020-03-26T23:59:59.000Z")), equalTo(time("2020-03-23T00:00:00.000Z")));
213+
assertThat(rounding.round(time("2020-03-28T23:59:59.000Z")), equalTo(time("2020-03-23T00:00:00.000Z")));
214+
assertThat(rounding.round(time("2020-03-29T00:00:00.000Z")), equalTo(time("2020-03-23T00:00:00.000Z")));
215+
assertThat(rounding.round(time("2020-03-29T23:59:59.000Z")), equalTo(time("2020-03-23T00:00:00.000Z")));
216+
assertThat(rounding.round(time("2020-03-30T00:00:00.000Z")), equalTo(time("2020-03-30T00:00:00.000Z")));
217+
}
153218

154-
assertThat(
155-
dateHistogramGroupSource.getRounding().round(time("2020-03-26T23:59:59.000Z")),
156-
equalTo(time("2020-03-23T00:00:00.000Z"))
157-
);
158-
assertThat(
159-
dateHistogramGroupSource.getRounding().round(time("2020-03-29T23:59:59.000Z")),
160-
equalTo(time("2020-03-23T00:00:00.000Z"))
219+
public void testRoundingDateHistogramCalendarIntervalWithNegativeOffset() {
220+
DateHistogramGroupSource dateHistogramGroupSource = new DateHistogramGroupSource(
221+
randomBoolean() ? null : randomAlphaOfLengthBetween(1, 20),
222+
null,
223+
randomBoolean(),
224+
new DateHistogramGroupSource.CalendarInterval(new DateHistogramInterval("1w")),
225+
null,
226+
DateHistogramAggregationBuilder.parseStringOffset("-1d")
161227
);
162-
assertThat(
163-
dateHistogramGroupSource.getRounding().round(time("2020-03-23T00:00:01.000Z")),
164-
equalTo(time("2020-03-23T00:00:00.000Z"))
228+
229+
Rounding.Prepared rounding = dateHistogramGroupSource.getRounding();
230+
assertThat(rounding, notNullValue());
231+
232+
// not meant to be complete rounding tests, see {@link RoundingTests} for more
233+
assertThat(rounding.round(time("2020-03-21T23:59:59.000Z")), equalTo(time("2020-03-15T00:00:00.000Z")));
234+
assertThat(rounding.round(time("2020-03-22T00:00:00.000Z")), equalTo(time("2020-03-22T00:00:00.000Z")));
235+
assertThat(rounding.round(time("2020-03-22T23:59:59.000Z")), equalTo(time("2020-03-22T00:00:00.000Z")));
236+
assertThat(rounding.round(time("2020-03-23T00:00:00.000Z")), equalTo(time("2020-03-22T00:00:00.000Z")));
237+
assertThat(rounding.round(time("2020-03-23T00:00:01.000Z")), equalTo(time("2020-03-22T00:00:00.000Z")));
238+
assertThat(rounding.round(time("2020-03-24T00:00:00.000Z")), equalTo(time("2020-03-22T00:00:00.000Z")));
239+
assertThat(rounding.round(time("2020-03-26T23:59:59.000Z")), equalTo(time("2020-03-22T00:00:00.000Z")));
240+
assertThat(rounding.round(time("2020-03-28T23:59:59.000Z")), equalTo(time("2020-03-22T00:00:00.000Z")));
241+
assertThat(rounding.round(time("2020-03-29T00:00:00.000Z")), equalTo(time("2020-03-29T00:00:00.000Z")));
242+
assertThat(rounding.round(time("2020-03-29T23:59:59.000Z")), equalTo(time("2020-03-29T00:00:00.000Z")));
243+
assertThat(rounding.round(time("2020-03-30T00:00:00.000Z")), equalTo(time("2020-03-29T00:00:00.000Z")));
244+
}
245+
246+
public void testRoundingDateHistogramCalendarIntervalWithPositiveOffset() {
247+
DateHistogramGroupSource dateHistogramGroupSource = new DateHistogramGroupSource(
248+
randomBoolean() ? null : randomAlphaOfLengthBetween(1, 20),
249+
null,
250+
randomBoolean(),
251+
new DateHistogramGroupSource.CalendarInterval(new DateHistogramInterval("1w")),
252+
null,
253+
DateHistogramAggregationBuilder.parseStringOffset("+1d")
165254
);
255+
256+
Rounding.Prepared rounding = dateHistogramGroupSource.getRounding();
257+
assertThat(rounding, notNullValue());
258+
259+
// not meant to be complete rounding tests, see {@link RoundingTests} for more
260+
assertThat(rounding.round(time("2020-03-21T23:59:59.000Z")), equalTo(time("2020-03-17T00:00:00.000Z")));
261+
assertThat(rounding.round(time("2020-03-22T00:00:00.000Z")), equalTo(time("2020-03-17T00:00:00.000Z")));
262+
assertThat(rounding.round(time("2020-03-22T23:59:59.000Z")), equalTo(time("2020-03-17T00:00:00.000Z")));
263+
assertThat(rounding.round(time("2020-03-23T00:00:00.000Z")), equalTo(time("2020-03-17T00:00:00.000Z")));
264+
assertThat(rounding.round(time("2020-03-23T00:00:01.000Z")), equalTo(time("2020-03-17T00:00:00.000Z")));
265+
assertThat(rounding.round(time("2020-03-24T00:00:00.000Z")), equalTo(time("2020-03-24T00:00:00.000Z")));
266+
assertThat(rounding.round(time("2020-03-26T23:59:59.000Z")), equalTo(time("2020-03-24T00:00:00.000Z")));
267+
assertThat(rounding.round(time("2020-03-28T23:59:59.000Z")), equalTo(time("2020-03-24T00:00:00.000Z")));
268+
assertThat(rounding.round(time("2020-03-29T00:00:00.000Z")), equalTo(time("2020-03-24T00:00:00.000Z")));
269+
assertThat(rounding.round(time("2020-03-29T23:59:59.000Z")), equalTo(time("2020-03-24T00:00:00.000Z")));
270+
assertThat(rounding.round(time("2020-03-30T00:00:00.000Z")), equalTo(time("2020-03-24T00:00:00.000Z")));
166271
}
167272

168273
private static long time(String time) {
169274
TemporalAccessor accessor = DateFormatter.forPattern("date_optional_time").withZone(ZoneOffset.UTC).parse(time);
170275
return DateFormatters.from(accessor).toInstant().toEpochMilli();
171276
}
277+
278+
private static long randomOffset() {
279+
return randomLongBetween(-1_000_000, 1_000_000);
280+
}
172281
}

0 commit comments

Comments
 (0)