How can I define a runtime field which shows the time elapsed since the value of a date field

I have an index and I want a field called durationv2. If the field command_details.endTime exists, I want durationv2 to be the difference between command_details.startTime and command_details.endTime. Otherwise, I want it to be the difference between command_details.startTime and now. The goal is for a live viewer to either see how long a command took or how long it is taking. Would something like the following work?

Right now I'm getting a resource_already_exists_exception error on the index. I'm guessing that I'm not using the right syntax to add a new mapping to my index.

Also, it feels like it would be more efficient to calculate the current time once and to pass it as a parameter, but I'm not sure how to do that.

PUT transform-my-test-7/
{
  "mappings": {
    "runtime":{
      "durationv2": {
        "type": "long",
        "script": {
          "source": "emit(doc.['command_details.endTime'].empty ? Instant.ofEpochMilli(new Date()).getMillis() - doc['command_details.startTime'].getMillis() : doc['command_details.endTime'].getMillis() - doc['command_details.startTime'].getMillis())  "
        }
      }
    }
  }
}

I think I figured it out. First, when adding a mapping, I needed to use the POST verb instead of PUT. Here's what I ended with. I'll need to circle back and account for time zones in the future, but this serves my needs for now.

PUT transform-my-test-7/_mapping
{
  "runtime":{
    "durationv2": {
      "type": "long",
      "script": {
        "source": """
          if (!doc['command_details.endTime'].empty && !doc['command_details.startTime'].empty ) {
            emit(doc['command_details.endTime'].value.toInstant().toEpochMilli() - doc['command_details.startTime'].value.toInstant().toEpochMilli())
          }
          else if (!doc['command_details.startTime'].empty) {
            emit(System.currentTimeMillis() - doc['command_details.startTime'].value.toInstant().toEpochMilli())
          }
        """
      }
    }
  }
}

Hi @nsouth, thanks for posting your solution.

I'll need to circle back and account for time zones in the future, but this serves my needs for now.

Check out the help doc Using Datetime in Painless.

The strategy will be:

  1. Create a ZonedDateTime from an Instant, as seen in the example on that page:
long milliSinceEpoch = 434931330000L;
Instant instant = Instant.ofEpochMilli(milliSinceEpoch);
ZonedDateTime zdt1 = ZonedDateTime.ofInstant(instant, ZoneId.of('Z'));
  1. Then use ChronoUnit.MILLIS.between(zdt1, zdt2)
long differenceInMillis = ChronoUnit.MILLIS.between(zdt1, zdt2);