# Painlessly turning a string of multiple numbers into multiple fields of numbers

**URL:** <https://discuss.elastic.co/t/painlessly-turning-a-string-of-multiple-numbers-into-multiple-fields-of-numbers/326800>\
**Category:** Elasticsearch\
**Tags:** painless, language-clients, runtime-fields\
**Created:** [March 1, 2023, 6:57pm UTC](https://discuss.elastic.co/t/painlessly-turning-a-string-of-multiple-numbers-into-multiple-fields-of-numbers/326800 "2023-03-01T18:57:22Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![Mike\_Williams](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/mike_williams/32/81420_2.png) [@Mike\_Williams](https://discuss.elastic.co/u/Mike_Williams)\
**Post date:** [March 1, 2023, 6:57pm UTC](https://discuss.elastic.co/t/painlessly-turning-a-string-of-multiple-numbers-into-multiple-fields-of-numbers/326800/1 "2023-03-01T18:57:22Z")

</div>

Hey all,

I'm ingesting JSON formatted logs from nginx. The JSON is all ECS style nested fields.

I've currently got a need to visualize the upstream response time, http.upstream.response.time. Fundamentally this is a float/double (0.166, 10.0, etc). In some simple tests I succeeded with the following.

```auto
PUT logs-my-datastream/_mapping
{
  "runtime": {
    "http.upstream.response.time_int": {
      "type": "double",
      "script": {
        "source": "if (doc.containsKey('http.upstream.response.time')) { if (doc['http.upstream.response.time'].size() != 0 && doc['http.upstream.response.time'].value.length() > 0) {emit(Double.parseDouble(doc['http.upstream.response.time'].value)) } }"
      }
    }
  }
}

```

The problem I've got is that http.upstream.response.time is quite literally a string, not a text representation of a number an actual string.  
It is expected behavior for nginx to try more than one upstream, in such cases it returns a comma separated list of the time taken for _each_ of the upstreams tried, i.e. "10.000, 0.166" (where 10 = the timeout communicating with the upstream).

My only sensible idea here to have a meaningful value is to convert each number then add them all up, as that is the time the client sees, but I'm struggling to understand how to do that. My best attempt so far results in;

```auto
class_cast_exception: Cannot cast from [java.lang.Double] to [double].

```

```auto
if (doc.containsKey('http.upstream.response.time')) {
  if (doc['http.upstream.response.time'].size() != 0 && doc['http.upstream.response.time'].value.length() > 0) {
    String[] times = doc['http.upstream.response.time'].value.split(", ");
    def total=0.0;
    for(Double time:times){
      total = total + (double)time;
    }
    emit(total)
  }
}

```

I am in no way a Java developer _at all_, today is literally the first day I have written any, so please don't be too horrified at my potentially horrible java.  
I've tried various combinations of Double, double, (double), String and nothing, the best I can do is reverse the order of [java.lang.Double] and [double] in the cast exception.

If someone has a better idea for doing this I'd love to hear, if not I'd be super appreciative of help fixing the java/painless.

Thanks  
Mike

---

<div class="post-metadata">

**Author:** ![vincenbr](https://avatars.discourse-cdn.com/v4/letter/v/8edcca/32.png) [@vincenbr](https://discuss.elastic.co/u/vincenbr)\
**Post date:** [March 2, 2023, 7:15pm UTC](https://discuss.elastic.co/t/painlessly-turning-a-string-of-multiple-numbers-into-multiple-fields-of-numbers/326800/2 "2023-03-02T19:15:46Z")

</div>

Hi,  
here is something you can do. I do not guarantee that it is _painlessly_ readable , but it should do the trick

```auto
GET logtest/_search
{
  "runtime_mappings": {
    "yournewfield": {
      "type": "double",
      "script": {
        "source": """
        if (doc.containsKey('http.upstream.response.time')) {
          if (doc['http.upstream.response.time.keyword'].size() != 0 && doc['http.upstream.response.time.keyword'].value.length() > 0) {
            def res = Arrays.asList(doc['http.upstream.response.time.keyword'].value.splitOnToken(",")).stream()
                  .map(String::trim)
                  .mapToDouble(x -> Double.parseDouble(x))
                  .sum();
            emit(res);
          }
        }
        """
      }
    }
  },
  "fields": [
    "yournewfield"
  ]
}

```

Reading again your code, you were quite close, just missing an explicit cast.

---

<div class="post-metadata">

**Author:** ![Mike\_Williams](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/mike_williams/32/81420_2.png) [@Mike\_Williams](https://discuss.elastic.co/u/Mike_Williams)\
**Post date:** [March 3, 2023, 10:38am UTC](https://discuss.elastic.co/t/painlessly-turning-a-string-of-multiple-numbers-into-multiple-fields-of-numbers/326800/3 "2023-03-03T10:38:24Z")

</div>

Thanks Vincent.

I have to say I don't understand your code at all, but with some tiny modification the below worked great.

```auto
if (doc.containsKey('http.upstream.response.time')) {
  if (doc['http.upstream.response.time'].size() != 0 && doc['http.upstream.response.time'].value.length() > 0) {
    def res = Arrays.asList(doc['http.upstream.response.time'].value.splitOnToken(",")).stream()
      .map(String::trim)
      .mapToDouble(x -> Double.parseDouble(x))
      .sum();
    emit(res);
  }
}

```

Using `.keyword` just doesn't work with this data set, it always fails somewhere. http.upstream.response.time is fixed by mapping as a keyword, but can be numbers as a string, a single "-", or an empty string, maybe that throws off elasticsearch? Whatever, the above works. Thanks.

You mentioned I was quite close, just missing an explicit cast. Out of curiosity, what cast was I missing?

Cheers  
Mike

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/elastic/original/3X/1/a/1ac57faf039f6b580b3f104ef42a2a89e41014de.png) [@system](https://discuss.elastic.co/u/system)\
**Post date:** [March 31, 2023, 10:39am UTC](https://discuss.elastic.co/t/painlessly-turning-a-string-of-multiple-numbers-into-multiple-fields-of-numbers/326800/4 "2023-03-31T10:39:13Z")

</div>

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.
