Elasticsearch: Unable to use String.format() in Painless

Created on 15 Feb 2018  路  7Comments  路  Source: elastic/elasticsearch

Elasticsearch version (bin/elasticsearch --version):
Elasticsearch 5.5.3

Plugins installed: []
None

JVM version (java -version):
% java -version
java version "1.7.0_151"
Java(TM) SE Runtime Environment (build 1.7.0_151-b15)
Java HotSpot(TM) 64-Bit Server VM (build 24.151-b15, mixed mode)

OS version (uname -a if on a Unix-like system):
% uname -a
Linux [HOSTNAME HERE] 2.6.32-696.10.2.el6.x86_64 #1 SMP Sun Sep 10 11:10:44 EDT 2017 x86_64 x86_64 x86_64 GNU/Linux

Description of the problem including expected versus actual behavior:
I'm having string format issues similar to this issue
https://stackoverflow.com/questions/45435212/painless-script-cast-string-to-def

Like that user, I'm looking to perform a sort on text that contain integers by zero padding them. When running Painless code that contains any combination of the following, I get errors.

def foo = String.format('%010d', 10);
String foo = String.format('%010d', 10);
def foo = String.format('%010d', Integer.parseInt('123'));
String foo = String.format('%010d', Integer.parseInt('123'));

return foo;

All produce:
Caused by: java.lang.ClassCastException: Cannot cast from [String] to [def[]].
Steps to reproduce:

Please include a minimal but complete recreation of the problem, including
(e.g.) index creation, mappings, settings, query etc. The easier you make for
us to reproduce it, the more likely that somebody will take the time to look at it.

  1. in python,
es_connection.put_script(
                lang="painless", id="my_script",
                body={"script": "any painless code that contains the String.format() examples above here"}
)
  1. query similar to the following
{'query': {'bool': {'filter': [{'term': {'seq': 'seq10'}}]}},
 'sort': [{'_script': {'order': 'asc',
                       'script': {'id': 'my_script',
                                  'lang': 'painless',
                                  'params': {}},
                       'type': 'string'}}]}
  1. observe the shell running ES blow up upon query.

Provide logs (if relevant):

:CorInfrScripting >bug

Most helpful comment

@synthesize No need to apologize! String.format in Painless accepts an array of def instead of a standard list of arguments. Something along the lines of String.format('%d %f %s', new def[] {1, 2.0, 'string'}) should work.

All 7 comments

This is a pure reproduction via the console

GET foo/_search
{
  "script_fields": {
    "foo": {
      "script": {
        "source" : "return String.format(Locale.ROOT, '%s', 'test')"
      }
    }
  }
}

@jdconrad can you take a look?

I saw the definition in modules/lang-painless/java.lang.txt is String format(Locale,String,def[]) - I did not see this in any other definition in the resources for painless.

So, there are two forms of String format as part of our API

static String format(String, def[]) (java 9)
static String format(Locale, String, def[]) (java 9)

found here (https://www.elastic.co/guide/en/elasticsearch/painless/master/painless-api-reference.html)

The unfortunate part is we do not support variadic methods in Painless. All variadic methods have the ... replaced with def[] instead. Variadic function support is likely something Painless should support in the future; however, the work around for now is for the user to create an array with the appropriate arguments him/herself. I realize this is a bummer for now.

I'm going to close this as it's not technically a bug.

the work around for now is for the user to create an array with the appropriate arguments him/herself. I realize this is a bummer for now.

Apologies, but could you elaborate on this? My work-around was similar to the following:
String s = ("0000000000" + integerString).substring(integerString.length());

Are you saying I could create an array to pass to String.format() to get the desired results?

@synthesize No need to apologize! String.format in Painless accepts an array of def instead of a standard list of arguments. Something along the lines of String.format('%d %f %s', new def[] {1, 2.0, 'string'}) should work.

Brilliant! That works perfectly!
String s = String.format('%010d', new def[] {Integer.parseInt(integerString)});
is a much better solution than
String s = ("0000000000" + integerString).substring(integerString.length());

I know it isn't much, but this makes me so happy that it can be done. Thank you so much!

It seems that the syntax
String s = String.format('%010d', new def[] {Integer.parseInt(integerString)}); causes intellij to mark the code [using the groovy parser] as invalid.

The code however runs correctly in ES.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rjernst picture rjernst  路  3Comments

rpalsaxena picture rpalsaxena  路  3Comments

ttaranov picture ttaranov  路  3Comments

clintongormley picture clintongormley  路  3Comments

clintongormley picture clintongormley  路  3Comments