Search code examples
elasticsearchelasticsearch-painless

Elasticsearch painless sorting null_pointer_error


I'm trying to create a sorting script with painless, filtering nested documents. The reason I'm doing this with a script, is because I need to emulate a COALESCE statement.

My documents have titles stored like:

{
  title: [
    {
      type: MainTitle,
      value: [
        {
          language: eng,
          label: The title 
        },
        {
          language: ger,
          label: Das title 
        }
      ]
    },
    {
      type: AvailabilityTitle,
      value: [
        {
          language: eng,
          label: New thing! 
        }
      ]
    }
  ]
}

title and title.value are nested documents.

I want to sort documents primarily by their english MainTitle and by their german MainTitle only if no english MainTitle exists - even if the german title gave a higher score.

I'm trying to simply sort by the english MainTitle first to try it out and this is the script:

def source = params._source;

def titles = source.title;
if (titles != null && titles.length > 0) {
    for(int i=0; i < titles.length; i++) {
        def t = titles[i];
        if (t.type == 'MainTitle') {
          
          def values = t.value;
          if (values != null && values.length > 0) {
            for (int j = 0; j < values.length; j++) {
              def v = values[j];
              if (v.language == 'eng') {

                return v.label;

              }
            }
          }
        
        }
    }
}

return \"\";

For some reason I'm getting a null_pointer_exception

"script_stack": [
  "if (values != null && values.length > 0) {       ",
  "                            ^---- HERE"
],

I don't get how values can be null at that point since I'm specifically checking for null just before it.


Solution

  • The null_pointer_exception is thrown, not because values is null, but because values does not have a method/function called length. That is because for some reason values is an ArrayList even though titles earlier is an Array. Apparently they both have the method/function size() so I can just use that.

    So this works:

    def source = params._source;
    
    def titles = source.title;
    if (titles != null && titles.size() > 0) {
        for(int i=0; i < titles.size(); i++) {
            def t = titles[i];
            if (t.type == 'MainTitle') {
              
              def values = t.value;
              if (values != null && values.size() > 0) {
                for (int j = 0; j < values.size(); j++) {
                  def v = values[j];
                  if (v != null && v.language == 'fin') {
    
                    return v.label;
    
                  }
                }
              }
            
            }
        }
    }
    
    return '';