Search code examples
elasticsearchelasticsearch-painless

Painless Script Error - "maximum number of statements that can be executed in a loop has been reached"


I have a painless script I'm trying to execute in elasticsearch to add new fields to an existing document based on the value of an existing field. Here is the source document I'm trying to update -

"_source" : {
    "mpmNumber" : "4004749",
    "parentTitles" : [
      {
        "parentUri" : "entities/YYYYY",
        "parentType" : "configuration/entityTypes/Title",
        "parentLabel" : "Gilmore Girls - YR01 00/01 - Season 1 - XXXXX",
        "parentDirectionalLabel" : "is parent of",
        "originallyAiredAs" : "5",
        "uri" : "relations/ZZZZZ",
        "type" : "configuration/entityTypes/Title",
        "label" : "Cinnamon's Wake - Episode 5 - ZZZZZ",
        "directionalLabel" : "is child of"
      }
    ]
  }

I'm basically trying to parse the contents of the parentLabel string and extract the individual values, so the individual values can be used to create new fields. Here is my script -

{
  "script":{
    "lang": "painless",
    "source":"""

      def splitText(def str, def separator)
      {
        def pos=str.indexOf(separator);
        def result=[];
        while (pos>0)
        {
          def split=str.substring(0,pos);
          def rest=str.substring(pos+2,str.length());
          result.add(split);
          pos=rest.indexOf(separator);
          if(pos==-1)
          {
            result.add(rest);
          }
        }
        return result;
      }

      def parentLabelArray=splitText(ctx._source.parentTitles[0].parentLabel,'-');

      String parentTitle=parentLabelArray[0]+parentLabelArray[1];
      String parentType=parentLabelArray[2];
      String parentId=parentLabelArray[3];

      ctx._source.parentTitles[0].parentTitle=parentTitle;
      ctx._source.parentTitles[0].parentType=parentType.substring(0,parentType.length()-3);
      ctx._source.parentTitles[0].parentAiringOrder=parentType.substring(parentType.length()-3,parentType.length());
      ctx._source.parentTitles[0].parentId=parentId;

  """
  }
}

However, i keep getting the following error when i try to execute this script -

{
  "error": {
    "root_cause": [
      {
        "type": "remote_transport_exception",
        "reason": "[xDrtBOW][x.x.x.x:9300][indices:data/write/update[s]]"
      }
    ],
    "type": "illegal_argument_exception",
    "reason": "failed to execute script",
    "caused_by": {
      "type": "script_exception",
      "reason": "runtime error",
      "script_stack": [
        "while (pos>0)\n        {\n          def ",
        "^---- HERE"
      ],
      "script": "      def splitText(def str, def separator)\n      {\n        def pos=str.indexOf(separator);\n        def result=[];\n        while (pos>0)\n        {\n          def split=str.substring(0,pos);\n          def rest=str.substring(pos+2,str.length());\n          result.add(split);\n          pos=rest.indexOf(separator);\n          if(pos==-1)\n          {\n            result.add(rest);\n          }\n        }\n        return result;\n      }\n      \n      def parentLabelArray=splitText(ctx._source.parentTitles[0].parentLabel,'-');\n      \n      String parentTitle=parentLabelArray[0]+parentLabelArray[1];\n      String parentType=parentLabelArray[2];\n      String parentId=parentLabelArray[3];\n      \n      ctx._source.parentTitles[0].parentTitle=parentTitle;\n      ctx._source.parentTitles[0].parentType=parentType.substring(0,parentType.length()-3);\n      ctx._source.parentTitles[0].parentAiringOrder=parentType.substring(parentType.length()-3,parentType.length());\n      ctx._source.parentTitles[0].parentId=parentId;",
      "lang": "painless",
      "caused_by": {
        "type": "painless_error",
        "reason": "The maximum number of statements that can be executed in a loop has been reached."
      }
    }
  },
  "status": 400
}

Any idea why I'm getting this and how it can be fixed?


Solution

  • You've basically reimplemented the split method. It's much easier to use it.

    I've made a few changes to your script as follows:

      def parentLabelArray=/\-/.split(ctx._source.parentTitles[0].parentLabel);
    
      String parentTitle=parentLabelArray[0].trim()+parentLabelArray[1].trim();
      String parentType=parentLabelArray[2].trim();
      String parentId=parentLabelArray[3].trim();
    
      ctx._source.parentTitles[0].parentTitle=parentTitle;
      ctx._source.parentTitles[0].parentType=parentType.substring(0,parentType.length());
      ctx._source.parentTitles[0].parentAiringOrder=parentType.substring(parentType.length(),parentType.length());
      ctx._source.parentTitles[0].parentId=parentId;
    

    When updating your document with that fix I get this:

    {
    "mpmNumber" : "4004749",
    "parentTitles" : [
      {
        "parentAiringOrder" : "",
        "parentUri" : "entities/YYYYY",
        "parentLabel" : "Gilmore Girls - YR01 00/01 - Season 1 - XXXXX",
        "parentDirectionalLabel" : "is parent of",
        "directionalLabel" : "is child of",
        "label" : "Cinnamon's Wake - Episode 5 - ZZZZZ",
        "originallyAiredAs" : "5",
        "type" : "configuration/entityTypes/Title",
        "uri" : "relations/ZZZZZ",
        "parentTitle" : "Gilmore GirlsYR01 00/01",
        "parentType" : "Season 1",
        "parentId" : "XXXXX"
      }
    ]
    }
    

    UPDATE:

    For the unlucky ones running on AWS, here is another version of the splitText method without having to enable regular expressions:

      def splitText(def str, def separator)
      {
         def tokenizer = new StringTokenizer(str, separator);
         def results = [];
    
         while(tokenizer.hasMoreTokens())
            results.add(tokenizer.nextToken());
    
         return results;
      }