22
22
import org .elasticsearch .client .ValidationException ;
23
23
import org .elasticsearch .common .Nullable ;
24
24
import org .elasticsearch .common .ParseField ;
25
+ import org .elasticsearch .common .unit .TimeValue ;
25
26
import org .elasticsearch .common .xcontent .ConstructingObjectParser ;
26
27
import org .elasticsearch .common .xcontent .ToXContentObject ;
27
28
import org .elasticsearch .common .xcontent .XContentBuilder ;
30
31
import org .joda .time .DateTimeZone ;
31
32
32
33
import java .io .IOException ;
34
+ import java .util .Collections ;
35
+ import java .util .HashSet ;
33
36
import java .util .Objects ;
34
37
import java .util .Optional ;
38
+ import java .util .Set ;
35
39
36
40
import static org .elasticsearch .common .xcontent .ConstructingObjectParser .constructorArg ;
37
41
import static org .elasticsearch .common .xcontent .ConstructingObjectParser .optionalConstructorArg ;
@@ -59,14 +63,63 @@ public class DateHistogramGroupConfig implements Validatable, ToXContentObject {
59
63
private static final String TIME_ZONE = "time_zone" ;
60
64
private static final String DELAY = "delay" ;
61
65
private static final String DEFAULT_TIMEZONE = "UTC" ;
66
+ private static final String CALENDAR_INTERVAL = "calendar_interval" ;
67
+ private static final String FIXED_INTERVAL = "fixed_interval" ;
68
+
69
+ // From DateHistogramAggregationBuilder in core, transplanted and modified to a set
70
+ // so we don't need to import a dependency on the class
71
+ private static final Set <String > DATE_FIELD_UNITS ;
72
+ static {
73
+ Set <String > dateFieldUnits = new HashSet <>();
74
+ dateFieldUnits .add ("year" );
75
+ dateFieldUnits .add ("1y" );
76
+ dateFieldUnits .add ("quarter" );
77
+ dateFieldUnits .add ("1q" );
78
+ dateFieldUnits .add ("month" );
79
+ dateFieldUnits .add ("1M" );
80
+ dateFieldUnits .add ("week" );
81
+ dateFieldUnits .add ("1w" );
82
+ dateFieldUnits .add ("day" );
83
+ dateFieldUnits .add ("1d" );
84
+ dateFieldUnits .add ("hour" );
85
+ dateFieldUnits .add ("1h" );
86
+ dateFieldUnits .add ("minute" );
87
+ dateFieldUnits .add ("1m" );
88
+ dateFieldUnits .add ("second" );
89
+ dateFieldUnits .add ("1s" );
90
+ DATE_FIELD_UNITS = Collections .unmodifiableSet (dateFieldUnits );
91
+ }
62
92
63
93
private static final ConstructingObjectParser <DateHistogramGroupConfig , Void > PARSER ;
64
94
static {
65
- PARSER = new ConstructingObjectParser <>(NAME , true , a ->
66
- new DateHistogramGroupConfig ((String ) a [0 ], (DateHistogramInterval ) a [1 ], (DateHistogramInterval ) a [2 ], (String ) a [3 ]));
95
+ PARSER = new ConstructingObjectParser <>(NAME , true , a -> {
96
+ DateHistogramInterval oldInterval = (DateHistogramInterval ) a [1 ];
97
+ DateHistogramInterval calendarInterval = (DateHistogramInterval ) a [2 ];
98
+ DateHistogramInterval fixedInterval = (DateHistogramInterval ) a [3 ];
99
+
100
+ if (oldInterval != null ) {
101
+ if (calendarInterval != null || fixedInterval != null ) {
102
+ throw new IllegalArgumentException ("Cannot use [interval] with [fixed_interval] or [calendar_interval] " +
103
+ "configuration options." );
104
+ }
105
+ return new DateHistogramGroupConfig ((String ) a [0 ], oldInterval , (DateHistogramInterval ) a [4 ], (String ) a [5 ]);
106
+ } else if (calendarInterval != null && fixedInterval == null ) {
107
+ return new CalendarInterval ((String ) a [0 ], calendarInterval , (DateHistogramInterval ) a [4 ], (String ) a [5 ]);
108
+ } else if (calendarInterval == null && fixedInterval != null ) {
109
+ return new FixedInterval ((String ) a [0 ], fixedInterval , (DateHistogramInterval ) a [4 ], (String ) a [5 ]);
110
+ } else if (calendarInterval != null && fixedInterval != null ) {
111
+ throw new IllegalArgumentException ("Cannot set both [fixed_interval] and [calendar_interval] at the same time" );
112
+ } else {
113
+ throw new IllegalArgumentException ("An interval is required. Use [fixed_interval] or [calendar_interval]." );
114
+ }
115
+ });
67
116
PARSER .declareString (constructorArg (), new ParseField (FIELD ));
68
- PARSER .declareField (constructorArg (), p -> new DateHistogramInterval (p .text ()), new ParseField (INTERVAL ), ValueType .STRING );
69
- PARSER .declareField (optionalConstructorArg (), p -> new DateHistogramInterval (p .text ()), new ParseField (DELAY ), ValueType .STRING );
117
+ PARSER .declareField (optionalConstructorArg (), p -> new DateHistogramInterval (p .text ()), new ParseField (INTERVAL ), ValueType .STRING );
118
+ PARSER .declareField (optionalConstructorArg (), p -> new DateHistogramInterval (p .text ()),
119
+ new ParseField (CALENDAR_INTERVAL ), ValueType .STRING );
120
+ PARSER .declareField (optionalConstructorArg (), p -> new DateHistogramInterval (p .text ()),
121
+ new ParseField (FIXED_INTERVAL ), ValueType .STRING );
122
+ PARSER .declareField (optionalConstructorArg (), p -> new DateHistogramInterval (p .text ()), new ParseField (DELAY ), ValueType .STRING );
70
123
PARSER .declareString (optionalConstructorArg (), new ParseField (TIME_ZONE ));
71
124
}
72
125
@@ -75,27 +128,81 @@ public class DateHistogramGroupConfig implements Validatable, ToXContentObject {
75
128
private final DateHistogramInterval delay ;
76
129
private final String timeZone ;
77
130
131
+ /**
132
+ * FixedInterval is a {@link DateHistogramGroupConfig} that uses a fixed time interval for rolling up data.
133
+ * The fixed time interval is one or multiples of SI units and has no calendar-awareness (e.g. doesn't account
134
+ * for leap corrections, does not have variable length months, etc).
135
+ *
136
+ * For calendar-aware rollups, use {@link CalendarInterval}
137
+ */
138
+ public static class FixedInterval extends DateHistogramGroupConfig {
139
+ public FixedInterval (String field , DateHistogramInterval interval ) {
140
+ this (field , interval , null , null );
141
+ }
142
+
143
+ public FixedInterval (String field , DateHistogramInterval interval , DateHistogramInterval delay , String timeZone ) {
144
+ super (field , interval , delay , timeZone );
145
+ // validate fixed time
146
+ TimeValue .parseTimeValue (interval .toString (), NAME + ".FixedInterval" );
147
+ }
148
+ }
149
+
150
+ /**
151
+ * CalendarInterval is a {@link DateHistogramGroupConfig} that uses calendar-aware intervals for rolling up data.
152
+ * Calendar time intervals understand leap corrections and contextual differences in certain calendar units (e.g.
153
+ * months are variable length depending on the month). Calendar units are only available in singular quantities:
154
+ * 1s, 1m, 1h, 1d, 1w, 1q, 1M, 1y
155
+ *
156
+ * For fixed time rollups, use {@link FixedInterval}
157
+ */
158
+ public static class CalendarInterval extends DateHistogramGroupConfig {
159
+ public CalendarInterval (String field , DateHistogramInterval interval ) {
160
+ this (field , interval , null , null );
161
+
162
+ }
163
+
164
+ public CalendarInterval (String field , DateHistogramInterval interval , DateHistogramInterval delay , String timeZone ) {
165
+ super (field , interval , delay , timeZone );
166
+ if (DATE_FIELD_UNITS .contains (interval .toString ()) == false ) {
167
+ throw new IllegalArgumentException ("The supplied interval [" + interval +"] could not be parsed " +
168
+ "as a calendar interval." );
169
+ }
170
+ }
171
+
172
+ }
173
+
78
174
/**
79
175
* Create a new {@link DateHistogramGroupConfig} using the given field and interval parameters.
176
+ *
177
+ * @deprecated Build a DateHistoConfig using {@link DateHistogramGroupConfig.CalendarInterval}
178
+ * or {@link DateHistogramGroupConfig.FixedInterval} instead
179
+ *
180
+ * @since 7.2.0
80
181
*/
182
+ @ Deprecated
81
183
public DateHistogramGroupConfig (final String field , final DateHistogramInterval interval ) {
82
184
this (field , interval , null , null );
83
185
}
84
186
85
187
/**
86
188
* Create a new {@link DateHistogramGroupConfig} using the given configuration parameters.
87
189
* <p>
88
- * The {@code field} and {@code interval} are required to compute the date histogram for the rolled up documents.
89
- * The {@code delay} is optional and can be set to {@code null}. It defines how long to wait before rolling up new documents.
90
- * The {@code timeZone} is optional and can be set to {@code null}. When configured, the time zone value is resolved using
91
- * ({@link DateTimeZone#forID(String)} and must match a time zone identifier provided by the Joda Time library.
190
+ * The {@code field} and {@code interval} are required to compute the date histogram for the rolled up documents.
191
+ * The {@code delay} is optional and can be set to {@code null}. It defines how long to wait before rolling up new documents.
192
+ * The {@code timeZone} is optional and can be set to {@code null}. When configured, the time zone value is resolved using
193
+ * ({@link DateTimeZone#forID(String)} and must match a time zone identifier provided by the Joda Time library.
92
194
* </p>
93
- *
94
- * @param field the name of the date field to use for the date histogram (required)
195
+ * @param field the name of the date field to use for the date histogram (required)
95
196
* @param interval the interval to use for the date histogram (required)
96
- * @param delay the time delay (optional)
197
+ * @param delay the time delay (optional)
97
198
* @param timeZone the id of time zone to use to calculate the date histogram (optional). When {@code null}, the UTC timezone is used.
199
+ *
200
+ * @deprecated Build a DateHistoConfig using {@link DateHistogramGroupConfig.CalendarInterval}
201
+ * or {@link DateHistogramGroupConfig.FixedInterval} instead
202
+ *
203
+ * @since 7.2.0
98
204
*/
205
+ @ Deprecated
99
206
public DateHistogramGroupConfig (final String field ,
100
207
final DateHistogramInterval interval ,
101
208
final @ Nullable DateHistogramInterval delay ,
@@ -153,7 +260,13 @@ public String getTimeZone() {
153
260
public XContentBuilder toXContent (final XContentBuilder builder , final Params params ) throws IOException {
154
261
builder .startObject ();
155
262
{
156
- builder .field (INTERVAL , interval .toString ());
263
+ if (this .getClass ().equals (CalendarInterval .class )) {
264
+ builder .field (CALENDAR_INTERVAL , interval .toString ());
265
+ } else if (this .getClass ().equals (FixedInterval .class )) {
266
+ builder .field (FIXED_INTERVAL , interval .toString ());
267
+ } else {
268
+ builder .field (INTERVAL , interval .toString ());
269
+ }
157
270
builder .field (FIELD , field );
158
271
if (delay != null ) {
159
272
builder .field (DELAY , delay .toString ());
0 commit comments