Skip to content

Commit eeeb355

Browse files
authored
Configurable output format for date processor (#61324) (#62175)
1 parent e181e24 commit eeeb355

File tree

3 files changed

+76
-3
lines changed

3 files changed

+76
-3
lines changed

modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/DateProcessor.java

+24-3
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,24 @@ public final class DateProcessor extends AbstractProcessor {
4343

4444
public static final String TYPE = "date";
4545
static final String DEFAULT_TARGET_FIELD = "@timestamp";
46-
private static final DateFormatter FORMATTER = DateFormatter.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
46+
static final String DEFAULT_OUTPUT_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";
4747

48+
private final DateFormatter formatter;
4849
private final TemplateScript.Factory timezone;
4950
private final TemplateScript.Factory locale;
5051
private final String field;
5152
private final String targetField;
5253
private final List<String> formats;
5354
private final List<Function<Map<String, Object>, Function<String, ZonedDateTime>>> dateParsers;
55+
private final String outputFormat;
5456

5557
DateProcessor(String tag, String description, @Nullable TemplateScript.Factory timezone, @Nullable TemplateScript.Factory locale,
5658
String field, List<String> formats, String targetField) {
59+
this(tag, description, timezone, locale, field, formats, targetField, DEFAULT_OUTPUT_FORMAT);
60+
}
61+
62+
DateProcessor(String tag, String description, @Nullable TemplateScript.Factory timezone, @Nullable TemplateScript.Factory locale,
63+
String field, List<String> formats, String targetField, String outputFormat) {
5764
super(tag, description);
5865
this.timezone = timezone;
5966
this.locale = locale;
@@ -65,6 +72,8 @@ public final class DateProcessor extends AbstractProcessor {
6572
DateFormat dateFormat = DateFormat.fromString(format);
6673
dateParsers.add((params) -> dateFormat.getFunction(format, newDateTimeZone(params), newLocale(params)));
6774
}
75+
this.outputFormat = outputFormat;
76+
formatter = DateFormatter.forPattern(this.outputFormat);
6877
}
6978

7079
private ZoneId newDateTimeZone(Map<String, Object> params) {
@@ -99,7 +108,7 @@ public IngestDocument execute(IngestDocument ingestDocument) {
99108
throw new IllegalArgumentException("unable to parse date [" + value + "]", lastException);
100109
}
101110

102-
ingestDocument.setFieldValue(targetField, FORMATTER.format(dateTime));
111+
ingestDocument.setFieldValue(targetField, formatter.format(dateTime));
103112
return ingestDocument;
104113
}
105114

@@ -128,6 +137,10 @@ List<String> getFormats() {
128137
return formats;
129138
}
130139

140+
String getOutputFormat() {
141+
return outputFormat;
142+
}
143+
131144
public static final class Factory implements Processor.Factory {
132145

133146
private final ScriptService scriptService;
@@ -153,8 +166,16 @@ public DateProcessor create(Map<String, Processor.Factory> registry, String proc
153166
"locale", localeString, scriptService);
154167
}
155168
List<String> formats = ConfigurationUtils.readList(TYPE, processorTag, config, "formats");
169+
String outputFormat =
170+
ConfigurationUtils.readStringProperty(TYPE, processorTag, config, "output_format", DEFAULT_OUTPUT_FORMAT);
171+
try {
172+
DateFormatter.forPattern(outputFormat);
173+
} catch (Exception e) {
174+
throw new IllegalArgumentException("invalid output format [" + outputFormat + "]", e);
175+
}
176+
156177
return new DateProcessor(processorTag, description, compiledTimezoneTemplate, compiledLocaleTemplate, field, formats,
157-
targetField);
178+
targetField, outputFormat);
158179
}
159180
}
160181
}

modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/DateProcessorFactoryTests.java

+38
Original file line numberDiff line numberDiff line change
@@ -146,4 +146,42 @@ public void testParseTargetField() throws Exception {
146146
DateProcessor processor = factory.create(null, null, null, config);
147147
assertThat(processor.getTargetField(), equalTo(targetField));
148148
}
149+
150+
public void testParseOutputFormat() throws Exception {
151+
final String outputFormat = "dd:MM:yyyy";
152+
Map<String, Object> config = new HashMap<>();
153+
String sourceField = randomAlphaOfLengthBetween(1, 10);
154+
String targetField = randomAlphaOfLengthBetween(1, 10);
155+
config.put("field", sourceField);
156+
config.put("target_field", targetField);
157+
config.put("formats", Arrays.asList("dd/MM/yyyy", "dd-MM-yyyy"));
158+
config.put("output_format", outputFormat);
159+
DateProcessor processor = factory.create(null, null, null, config);
160+
assertThat(processor.getOutputFormat(), equalTo(outputFormat));
161+
}
162+
163+
public void testDefaultOutputFormat() throws Exception {
164+
Map<String, Object> config = new HashMap<>();
165+
String sourceField = randomAlphaOfLengthBetween(1, 10);
166+
String targetField = randomAlphaOfLengthBetween(1, 10);
167+
config.put("field", sourceField);
168+
config.put("target_field", targetField);
169+
config.put("formats", Arrays.asList("dd/MM/yyyy", "dd-MM-yyyy"));
170+
DateProcessor processor = factory.create(null, null, null, config);
171+
assertThat(processor.getOutputFormat(), equalTo(DateProcessor.DEFAULT_OUTPUT_FORMAT));
172+
}
173+
174+
public void testInvalidOutputFormatRejected() throws Exception {
175+
final String outputFormat = "invalid_date_format";
176+
Map<String, Object> config = new HashMap<>();
177+
String sourceField = randomAlphaOfLengthBetween(1, 10);
178+
String targetField = randomAlphaOfLengthBetween(1, 10);
179+
config.put("field", sourceField);
180+
config.put("target_field", targetField);
181+
config.put("formats", Arrays.asList("dd/MM/yyyy", "dd-MM-yyyy"));
182+
config.put("output_format", outputFormat);
183+
184+
IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> factory.create(null, null, null, config));
185+
assertThat(e.getMessage(), containsString("invalid output format [" + outputFormat + "]"));
186+
}
149187
}

modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/DateProcessorTests.java

+14
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.elasticsearch.script.TemplateScript;
2626
import org.elasticsearch.test.ESTestCase;
2727

28+
import java.time.Instant;
2829
import java.time.ZoneId;
2930
import java.time.ZoneOffset;
3031
import java.time.ZonedDateTime;
@@ -224,4 +225,17 @@ null, templatize(ZoneOffset.UTC), new TestTemplateService.MockTemplateScript.Fac
224225
assertThat(e.getMessage(), equalTo("unable to parse date [2010]"));
225226
assertThat(e.getCause().getMessage(), equalTo("Unknown language: invalid"));
226227
}
228+
229+
public void testOutputFormat() {
230+
long nanosAfterEpoch = randomLongBetween(1, 999999);
231+
DateProcessor processor = new DateProcessor(randomAlphaOfLength(10), null, null, null,
232+
"date_as_string", Collections.singletonList("iso8601"), "date_as_date", "HH:mm:ss.SSSSSSSSS");
233+
Map<String, Object> document = new HashMap<>();
234+
document.put("date_as_string", Instant.EPOCH.plusNanos(nanosAfterEpoch).toString());
235+
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), document);
236+
processor.execute(ingestDocument);
237+
// output format is time only with nanosecond precision
238+
String expectedDate = "00:00:00." + String.format(Locale.ROOT, "%09d", nanosAfterEpoch);
239+
assertThat(ingestDocument.getFieldValue("date_as_date", String.class), equalTo(expectedDate));
240+
}
227241
}

0 commit comments

Comments
 (0)