Elasticsearch 6 painless query exception

I am using Elasticsearch 6.2 which uses painless for the inline scripting. One of the fields in my doc has mapping:

"gap_days": {"type": "integer"}

And I have a painless script for search and the few lines are:

int gap = 10; //initialize to a default value
if (doc.containsKey('gap_days')) {
  if (doc['gap_days'].value != null) {
    gap = doc['gap_days'].value;
  }
}

But this keeps throwing an error:

script_stack: [
  "gap = doc['gap_days'].value; } } ",
  " ^---- HERE"
],
caused_by: {
  reason: "cannot convert MethodHandle(Longs)long to (Object)int",
  type: "wrong_method_type_exception"
},
reason: "runtime error"

I tried to look into all unique doc['gap_days'] values in the index, and you can see all of them are integer in all documents

"aggregations": {
  "uniq_gaps": {
    "doc_count_error_upper_bound": 0,
    "sum_other_doc_count": 342,
    "buckets": [
      {
        "key": 0,
        "doc_count": 31607
      },
      {
        "key": 365,
        "doc_count": 15119
      },
      {
        "key": 5,
        "doc_count": 2639
      },
      {
        "key": 21,
        "doc_count": 1784
      },
      {
        "key": 14,
        "doc_count": 1229
      },
      {
        "key": 3,
        "doc_count": 1073
      },
      {
        "key": 7,
        "doc_count": 979
      },
      {
        "key": 2,
        "doc_count": 728
      },
      {
        "key": 4,
        "doc_count": 291
      },
      {
        "key": 10,
        "doc_count": 170
      }
    ]
  }
}

Then why does it throw an exception saying cannot convert MethodHandle(Longs)long to (Object)int and my script stops working. Any idea how to fix this problem?

There are a few problems here. It should be long gap instead of int gap and I don't think your check for missing value is not going to work for numbers. The script is also missing return statement.

I just want to mention that using scripts should be the last resort because they are quite slow. In most cases missing values can be handled by for example specifying the missing_value parameter on the aggregation or using exists query, etc...

1 Like

Thanks, @Igor_Motov for your help.

  1. You mentioned gap_days should be data type long not integer, Just wondering why so? In our system, gap_days maximum value will never exceed 999, so does it still need to be defined as long ? I have the liberty to define it as data type long in the mapping and I can make the change. But was curious the reasoning behind that change? Could you please explain.

  2. The script code which I posted is just a few lines of snippets of my big script, that's why you don't see return and hence the existence check is part of script and not using exists

  3. You also said - I don't think your check for missing value is not going to work for numbers, do you mean there is something wrong with this condition? if (doc['gap_days'].value != null) ?

I appreciate your time helping me out :slight_smile:

FWIW, I also found an old discussion, Long field is returned as integer if the value is less in Java API where they discussed a reverse problem. long to integer while in my case it is integer to long and I don't use Java API

You mentioned gap_days should be data type long not integer, Just wondering why so? In our system, gap_days maximum value will never exceed 999, so does it still need to be defined as long ? I have the liberty to define it as data type long in the mapping and I can make the change. But was curious the reasoning behind that change? Could you please explain.

In the index it is still defined as integer and the index is actually optimized to take advantage of the low cardinality of this field. However, when this field is accessed in the scripts, for simplicity sake all integral data types are represented as longs and all real data types are represented as doubles.

The issue with Java API is a completely different issue since the value in the linked example was extracted from source (json document) and in your example the value is obtained from docvalues.

You also said - I don't think your check for missing value is not going to work for numbers, do you mean there is something wrong with this condition? if (doc['gap_days'].value != null) ?

This condition will be always true. Try this:

PUT test
{
  "mappings": {
    "doc": {
      "properties": {
        "gap_days": {"type": "integer"}
      }
    }
  }
}

PUT test/doc/1
{
  "gap_days": 1
}

PUT test/doc/2
{

}

GET test/doc/_search
{
  "script_fields": {
    "is_empty": {
      "script": {
        "source": "doc['gap_days'].isEmpty()"
      }
    },
    "is_not_null": {
      "script": {
        "source": "doc['gap_days'].value != null"
      }
    },
    "value": {
      "script": {
        "source": "doc['gap_days'].value"
      } 
    }
  }
}

Thank you @Igor_Motov . I understood now, so in the script, I should always initialize a numerical variable as long not an integer even if it is defined as integer in the mapping?

I should always initialize a numerical variable as long not an integer even if it is defined as integer in the mapping?

I wouldn't say "always", you can cast it to integer, if you prefer. I am just saying that doc values that you are going to get for integral data types are going to be longs and you need to accommodate for that one way or another.

Perfect, well understood now. Thank you so much for your help @Igor_Motov . By any chance do you have an answer for my other question? Accessing the value of a dynamic key in an object

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