2020package org .elasticsearch .client .dataframe .transforms .pivot ;
2121
2222import org .elasticsearch .common .ParseField ;
23- import org .elasticsearch .common .unit .TimeValue ;
2423import org .elasticsearch .common .xcontent .ConstructingObjectParser ;
2524import org .elasticsearch .common .xcontent .ObjectParser ;
25+ import org .elasticsearch .common .xcontent .ToXContentFragment ;
2626import org .elasticsearch .common .xcontent .ToXContentObject ;
2727import org .elasticsearch .common .xcontent .XContentBuilder ;
2828import org .elasticsearch .common .xcontent .XContentParser ;
3131import java .io .IOException ;
3232import java .time .ZoneId ;
3333import java .time .ZoneOffset ;
34+ import java .util .Arrays ;
35+ import java .util .Collections ;
36+ import java .util .HashSet ;
3437import java .util .Objects ;
38+ import java .util .Set ;
3539
3640import static org .elasticsearch .common .xcontent .ConstructingObjectParser .optionalConstructorArg ;
3741
@@ -43,32 +47,164 @@ public class DateHistogramGroupSource extends SingleGroupSource implements ToXCo
4347 private static final ParseField TIME_ZONE = new ParseField ("time_zone" );
4448 private static final ParseField FORMAT = new ParseField ("format" );
4549
50+ // From DateHistogramAggregationBuilder in core, transplanted and modified to a set
51+ // so we don't need to import a dependency on the class
52+ private static final Set <String > DATE_FIELD_UNITS = Collections .unmodifiableSet (new HashSet <>(Arrays .asList (
53+ "year" ,
54+ "1y" ,
55+ "quarter" ,
56+ "1q" ,
57+ "month" ,
58+ "1M" ,
59+ "week" ,
60+ "1w" ,
61+ "day" ,
62+ "1d" ,
63+ "hour" ,
64+ "1h" ,
65+ "minute" ,
66+ "1m" ,
67+ "second" ,
68+ "1s" )));
69+
70+ /**
71+ * Interval can be specified in 2 ways:
72+ *
73+ * fixed_interval fixed intervals like 1h, 1m, 1d
74+ * calendar_interval calendar aware intervals like 1M, 1Y, ...
75+ *
76+ * Note: data frames do not support the deprecated interval option
77+ */
78+ public interface Interval extends ToXContentFragment {
79+ String getName ();
80+ DateHistogramInterval getInterval ();
81+ }
82+
83+ public static class FixedInterval implements Interval {
84+ private static final String NAME = "fixed_interval" ;
85+ private final DateHistogramInterval interval ;
86+
87+ public FixedInterval (DateHistogramInterval interval ) {
88+ this .interval = interval ;
89+ }
90+
91+ @ Override
92+ public String getName () {
93+ return NAME ;
94+ }
95+
96+ @ Override
97+ public DateHistogramInterval getInterval () {
98+ return interval ;
99+ }
100+
101+ @ Override
102+ public XContentBuilder toXContent (XContentBuilder builder , Params params ) throws IOException {
103+ builder .field (NAME );
104+ interval .toXContent (builder , params );
105+ return builder ;
106+ }
107+
108+ @ Override
109+ public boolean equals (Object other ) {
110+ if (this == other ) {
111+ return true ;
112+ }
113+
114+ if (other == null || getClass () != other .getClass ()) {
115+ return false ;
116+ }
117+
118+ final FixedInterval that = (FixedInterval ) other ;
119+ return Objects .equals (this .interval , that .interval );
120+ }
121+
122+ @ Override
123+ public int hashCode () {
124+ return Objects .hash (interval );
125+ }
126+ }
127+
128+ public static class CalendarInterval implements Interval {
129+ private static final String NAME = "calendar_interval" ;
130+ private final DateHistogramInterval interval ;
131+
132+ public CalendarInterval (DateHistogramInterval interval ) {
133+ this .interval = interval ;
134+ if (DATE_FIELD_UNITS .contains (interval .toString ()) == false ) {
135+ throw new IllegalArgumentException ("The supplied interval [" + interval + "] could not be parsed " +
136+ "as a calendar interval." );
137+ }
138+ }
139+
140+ @ Override
141+ public String getName () {
142+ return NAME ;
143+ }
144+
145+ @ Override
146+ public DateHistogramInterval getInterval () {
147+ return interval ;
148+ }
149+
150+ @ Override
151+ public XContentBuilder toXContent (XContentBuilder builder , Params params ) throws IOException {
152+ builder .field (NAME );
153+ interval .toXContent (builder , params );
154+ return builder ;
155+ }
156+
157+ @ Override
158+ public boolean equals (Object other ) {
159+ if (this == other ) {
160+ return true ;
161+ }
162+
163+ if (other == null || getClass () != other .getClass ()) {
164+ return false ;
165+ }
166+
167+ final CalendarInterval that = (CalendarInterval ) other ;
168+ return Objects .equals (this .interval , that .interval );
169+ }
170+
171+ @ Override
172+ public int hashCode () {
173+ return Objects .hash (interval );
174+ }
175+ }
176+
46177 private static final ConstructingObjectParser <DateHistogramGroupSource , Void > PARSER =
47178 new ConstructingObjectParser <>("date_histogram_group_source" ,
48179 true ,
49180 (args ) -> {
50181 String field = (String )args [0 ];
51- long interval = 0 ;
52- DateHistogramInterval dateHistogramInterval = null ;
53- if (args [1 ] instanceof Long ) {
54- interval = (Long )args [1 ];
182+ String fixedInterval = (String ) args [1 ];
183+ String calendarInterval = (String ) args [2 ];
184+
185+ Interval interval = null ;
186+
187+ if (fixedInterval != null && calendarInterval != null ) {
188+ throw new IllegalArgumentException ("You must specify either fixed_interval or calendar_interval, found both" );
189+ } else if (fixedInterval != null ) {
190+ interval = new FixedInterval (new DateHistogramInterval (fixedInterval ));
191+ } else if (calendarInterval != null ) {
192+ interval = new CalendarInterval (new DateHistogramInterval (calendarInterval ));
55193 } else {
56- dateHistogramInterval = ( DateHistogramInterval ) args [ 1 ] ;
194+ throw new IllegalArgumentException ( "You must specify either fixed_interval or calendar_interval, found none" ) ;
57195 }
58- ZoneId zoneId = (ZoneId ) args [2 ];
59- String format = (String ) args [3 ];
60- return new DateHistogramGroupSource (field , interval , dateHistogramInterval , format , zoneId );
196+
197+ ZoneId zoneId = (ZoneId ) args [3 ];
198+ String format = (String ) args [4 ];
199+ return new DateHistogramGroupSource (field , interval , format , zoneId );
61200 });
62201
63202 static {
64203 PARSER .declareString (optionalConstructorArg (), FIELD );
65- PARSER .declareField (optionalConstructorArg (), p -> {
66- if (p .currentToken () == XContentParser .Token .VALUE_NUMBER ) {
67- return p .longValue ();
68- } else {
69- return new DateHistogramInterval (p .text ());
70- }
71- }, HistogramGroupSource .INTERVAL , ObjectParser .ValueType .LONG );
204+
205+ PARSER .declareString (optionalConstructorArg (), new ParseField (FixedInterval .NAME ));
206+ PARSER .declareString (optionalConstructorArg (), new ParseField (CalendarInterval .NAME ));
207+
72208 PARSER .declareField (optionalConstructorArg (), p -> {
73209 if (p .currentToken () == XContentParser .Token .VALUE_STRING ) {
74210 return ZoneId .of (p .text ());
@@ -84,15 +220,13 @@ public static DateHistogramGroupSource fromXContent(final XContentParser parser)
84220 return PARSER .apply (parser , null );
85221 }
86222
87- private final long interval ;
88- private final DateHistogramInterval dateHistogramInterval ;
223+ private final Interval interval ;
89224 private final String format ;
90225 private final ZoneId timeZone ;
91226
92- DateHistogramGroupSource (String field , long interval , DateHistogramInterval dateHistogramInterval , String format , ZoneId timeZone ) {
227+ DateHistogramGroupSource (String field , Interval interval , String format , ZoneId timeZone ) {
93228 super (field );
94229 this .interval = interval ;
95- this .dateHistogramInterval = dateHistogramInterval ;
96230 this .format = format ;
97231 this .timeZone = timeZone ;
98232 }
@@ -102,14 +236,10 @@ public Type getType() {
102236 return Type .DATE_HISTOGRAM ;
103237 }
104238
105- public long getInterval () {
239+ public Interval getInterval () {
106240 return interval ;
107241 }
108242
109- public DateHistogramInterval getDateHistogramInterval () {
110- return dateHistogramInterval ;
111- }
112-
113243 public String getFormat () {
114244 return format ;
115245 }
@@ -124,11 +254,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
124254 if (field != null ) {
125255 builder .field (FIELD .getPreferredName (), field );
126256 }
127- if (dateHistogramInterval == null ) {
128- builder .field (HistogramGroupSource .INTERVAL .getPreferredName (), interval );
129- } else {
130- builder .field (HistogramGroupSource .INTERVAL .getPreferredName (), dateHistogramInterval .toString ());
131- }
257+ interval .toXContent (builder , params );
132258 if (timeZone != null ) {
133259 builder .field (TIME_ZONE .getPreferredName (), timeZone .toString ());
134260 }
@@ -152,15 +278,14 @@ public boolean equals(Object other) {
152278 final DateHistogramGroupSource that = (DateHistogramGroupSource ) other ;
153279
154280 return Objects .equals (this .field , that .field ) &&
155- Objects .equals (interval , that .interval ) &&
156- Objects .equals (dateHistogramInterval , that .dateHistogramInterval ) &&
157- Objects .equals (timeZone , that .timeZone ) &&
158- Objects .equals (format , that .format );
281+ Objects .equals (this .interval , that .interval ) &&
282+ Objects .equals (this .timeZone , that .timeZone ) &&
283+ Objects .equals (this .format , that .format );
159284 }
160285
161286 @ Override
162287 public int hashCode () {
163- return Objects .hash (field , interval , dateHistogramInterval , timeZone , format );
288+ return Objects .hash (field , interval , timeZone , format );
164289 }
165290
166291 public static Builder builder () {
@@ -170,8 +295,7 @@ public static Builder builder() {
170295 public static class Builder {
171296
172297 private String field ;
173- private long interval = 0 ;
174- private DateHistogramInterval dateHistogramInterval ;
298+ private Interval interval ;
175299 private String format ;
176300 private ZoneId timeZone ;
177301
@@ -187,41 +311,14 @@ public Builder setField(String field) {
187311
188312 /**
189313 * Set the interval for the DateHistogram grouping
190- * @param interval the time interval in milliseconds
314+ * @param interval a fixed or calendar interval
191315 * @return the {@link Builder} with the interval set.
192316 */
193- public Builder setInterval (long interval ) {
194- if (interval < 1 ) {
195- throw new IllegalArgumentException ("[interval] must be greater than or equal to 1." );
196- }
317+ public Builder setInterval (Interval interval ) {
197318 this .interval = interval ;
198319 return this ;
199320 }
200321
201- /**
202- * Set the interval for the DateHistogram grouping
203- * @param timeValue The time value to use as the interval
204- * @return the {@link Builder} with the interval set.
205- */
206- public Builder setInterval (TimeValue timeValue ) {
207- return setInterval (timeValue .getMillis ());
208- }
209-
210- /**
211- * Sets the interval of the DateHistogram grouping
212- *
213- * If this DateHistogramInterval is set, it supersedes the #{@link DateHistogramGroupSource#getInterval()}
214- * @param dateHistogramInterval the DateHistogramInterval to set
215- * @return The {@link Builder} with the dateHistogramInterval set.
216- */
217- public Builder setDateHistgramInterval (DateHistogramInterval dateHistogramInterval ) {
218- if (dateHistogramInterval == null ) {
219- throw new IllegalArgumentException ("[dateHistogramInterval] must not be null" );
220- }
221- this .dateHistogramInterval = dateHistogramInterval ;
222- return this ;
223- }
224-
225322 /**
226323 * Set the optional String formatting for the time interval.
227324 * @param format The format of the output for the time interval key
@@ -243,7 +340,7 @@ public Builder setTimeZone(ZoneId timeZone) {
243340 }
244341
245342 public DateHistogramGroupSource build () {
246- return new DateHistogramGroupSource (field , interval , dateHistogramInterval , format , timeZone );
343+ return new DateHistogramGroupSource (field , interval , format , timeZone );
247344 }
248345 }
249346}
0 commit comments