7777import java .util .Map ;
7878import java .util .Objects ;
7979import java .util .Set ;
80+ import java .util .concurrent .TimeUnit ;
8081import java .util .function .BiFunction ;
8182import java .util .function .Function ;
8283import java .util .function .LongSupplier ;
@@ -116,6 +117,11 @@ public long convert(TimeValue timeValue) {
116117 return timeValue .millis ();
117118 }
118119
120+ @ Override
121+ public long convert (long epochMillis ) {
122+ return epochMillis ;
123+ }
124+
119125 @ Override
120126 public Instant toInstant (long value ) {
121127 return Instant .ofEpochMilli (value );
@@ -147,6 +153,11 @@ public long convert(TimeValue timeValue) {
147153 return timeValue .nanos ();
148154 }
149155
156+ @ Override
157+ public long convert (long epochMillis ) {
158+ return TimeUnit .MILLISECONDS .toNanos (epochMillis );
159+ }
160+
150161 @ Override
151162 public Instant toInstant (long value ) {
152163 return DateUtils .toInstant (value );
@@ -210,6 +221,11 @@ ToScriptFieldFactory<SortedNumericDocValues> getDefaultToScriptFieldFactory() {
210221 */
211222 public abstract long convert (TimeValue timeValue );
212223
224+ /**
225+ * Convert an epoch millis timestamp into a long value in this resolution.
226+ */
227+ public abstract long convert (long epochMillis );
228+
213229 /**
214230 * Decode the points representation of this field as milliseconds.
215231 */
@@ -1234,17 +1250,18 @@ protected String contentType() {
12341250
12351251 @ Override
12361252 protected void parseCreateField (DocumentParserContext context ) throws IOException {
1237- String dateAsString = context .parser ().textOrNull ();
12381253
12391254 long timestamp ;
1240- if (dateAsString == null ) {
1255+ if (context . parser (). currentToken () == XContentParser . Token . VALUE_NULL ) {
12411256 if (nullValue == null ) {
12421257 return ;
12431258 }
12441259 timestamp = nullValue ;
1260+ } else if (isEpochMillis (context )) {
1261+ timestamp = resolution .convert (context .parser ().longValue ());
12451262 } else {
12461263 try {
1247- timestamp = fieldType ().parse (dateAsString );
1264+ timestamp = fieldType ().parse (context . parser (). text () );
12481265 } catch (IllegalArgumentException | ElasticsearchParseException | DateTimeException | ArithmeticException e ) {
12491266 if (ignoreMalformed ) {
12501267 context .addIgnoredField (mappedFieldType .name ());
@@ -1262,6 +1279,21 @@ protected void parseCreateField(DocumentParserContext context) throws IOExceptio
12621279 indexValue (context , timestamp );
12631280 }
12641281
1282+ /*
1283+ If the value is a long and the date formater parses epoch millis, we can index the value directly.
1284+ This avoids the overhead of converting the long to a string and then parsing it back to a long via a date formatter.
1285+ Note that checking for the date formatter containing "epoch_millis" is not sufficient,
1286+ as there may be other formats compatible with a long value before "epoch_millis" (e.g., "epoch_second||epoch_millis").
1287+ */
1288+ private boolean isEpochMillis (DocumentParserContext context ) throws IOException {
1289+ DateFormatter dateFormatter = fieldType ().dateTimeFormatter ();
1290+ return context .parser ().currentToken () == XContentParser .Token .VALUE_NUMBER
1291+ && context .parser ().numberType () == XContentParser .NumberType .LONG
1292+ && (dateFormatter .equals (DEFAULT_DATE_TIME_FORMATTER )
1293+ || dateFormatter .equals (DEFAULT_DATE_TIME_NANOS_FORMATTER )
1294+ || dateFormatter .equals (EPOCH_MILLIS_PARSER ));
1295+ }
1296+
12651297 private void indexValue (DocumentParserContext context , long timestamp ) {
12661298 // DataStreamTimestampFieldMapper and TsidExtractingFieldMapper need to use timestamp value,
12671299 // so when this is true we store it in a well-known place
0 commit comments