0

I am using painless script in ES and encountering a null pointer exception, here is my script and also I have added the response for better understanding, I use ES v7.3.

POST test_script/_update/1
{
  "scripted_upsert": true,
  "script": {
    "lang": "painless",
    "source": "int getBrowsersObjectIndex(def x, def y){for (int i = 0; i < x.length; i++) {if (x[i].deviceType == y.deviceType) {if (x[i].osName == y.osName) {if(x[i].browserName == y.browserName) {return i}}}}return -1} int getDevicesObjectIndex(def x, def y){for (int i = 0; i < x.length; i++) {if (x[i].deviceId == y.deviceId) {if (x[i].appName == y.appName) {if (x[i].appNameSpace == y.appNameSpace){return i}}}}return -1}if (!ctx._source.containsKey('browsers')) {ctx._source['browsers'] = []}if (!ctx._source.containsKey('devices')) {ctx._source['devices'] = []}for (int index = 0; index < params.iterate.length; index++) {if (params.iterate[index].browserObject) {int browserIndex = getBrowsersObjectIndex(ctx._source.browsers, params.iterate[index].browserObject);if (browserIndex >= 0) {ctx._source.browsers.remove(browserIndex);}ctx._source.browsers.add(params.iterate[index].browserObject);}if (params.iterate[index].deviceObject) {int deviceIndex = getDevicesObjectIndex(ctx._source.devices, params.iterate[index].deviceObject);if (deviceIndex >= 0) {ctx._source.devices.remove(deviceIndex);}ctx._source.devices.add(params.iterate[index].deviceObject);}}",
    "params": {
      "iterate": [
        {
          "deviceObject": {
            "deviceId": "162c04e48832e338",
            "appName": "test_app",
            "appNameSpace": "com.test",
            "appBuild": 55,
            "appVersion": "4.6.3",
            "deviceName": "OP4B80L1",
            "manufacturer": "OPPO",
            "model": "CPH1937",
            "networkCarrier": "Jio",
            "os": "Android",
            "platform": "Android",
            "timezone": "Asia/Kolkata",
            "version": "10"
          }
        },
        {
          "browserObject": {
            "deviceType": "mobile",
            "osName": "Android",
            "browserName": "Chrome",
            "osVersion": 10,
            "browserVersion": "80.0.3987.99"
          }
        }
      ]
    }
  },
  "upsert": {}
}

Error is:

{
  "error": {
    "root_cause": [
      {
        "type": "remote_transport_exception",
        "reason": "[connecto][192.168.36.235: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": [
        "if (params.iterate[index].browserObject) {int ",
        "                         ^---- HERE"
      ],
      "script": "int getBrowsersObjectIndex(def x, def y){for (int i = 0; i < x.length; i++) {if (x[i].deviceType == y.deviceType) {if (x[i].osName == y.osName) {if(x[i].browserName == y.browserName) {return i}}}}return -1} int getDevicesObjectIndex(def x, def y){for (int i = 0; i < x.length; i++) {if (x[i].deviceId == y.deviceId) {if (x[i].appName == y.appName) {if (x[i].appNameSpace == y.appNameSpace){return i}}}}return -1}if (!ctx._source.containsKey('browsers')) {ctx._source['browsers'] = []}if (!ctx._source.containsKey('devices')) {ctx._source['devices'] = []}for (int index = 0; index < params.iterate.length; index++) {if (params.iterate[index].browserObject) {int browserIndex = getBrowsersObjectIndex(ctx._source.browsers, params.iterate[index].browserObject);if (browserIndex >= 0) {ctx._source.browsers.remove(browserIndex);}ctx._source.browsers.add(params.iterate[index].browserObject);}if (params.iterate[index].deviceObject) {int deviceIndex = getDevicesObjectIndex(ctx._source.devices, params.iterate[index].deviceObject);if (deviceIndex >= 0) {ctx._source.devices.remove(deviceIndex);}ctx._source.devices.add(params.iterate[index].deviceObject);}}",
      "lang": "painless",
      "caused_by": {
        "type": "null_pointer_exception",
        "reason": null
      }
    }
  },
  "status": 400
}

Seems like the error is in the if statement where I am checking if either of browserObject or deviceObject exists inside the iterate array of objects.

1 Answer 1

4

You need to perform an explicit null-check, i.e.

if (params.iterate[index].browserObject != null)

Besides, in Kibana Dev Tools, you can use triple quotes """ to properly format your code so it's more legible and easier to read/maintain:

POST test_script/_update/1
{
  "scripted_upsert": true,
  "script": {
    "lang": "painless",
    "source": """
    int getBrowsersObjectIndex(def x, def y){
      for (int i = 0; i < x.length; i++) {
        if (x[i].deviceType == y.deviceType) {
          if (x[i].osName == y.osName) {
            if(x[i].browserName == y.browserName) {
              return i
            }
          }
        }
      }
      return -1
    }
    int getDevicesObjectIndex(def x, def y){
      for (int i = 0; i < x.length; i++) {
        if (x[i].deviceId == y.deviceId) {
          if (x[i].appName == y.appName) {
            if (x[i].appNameSpace == y.appNameSpace){
              return i
            }
          }
        }
      }
      return -1
    }
    if (!ctx._source.containsKey('browsers')) {
      ctx._source['browsers'] = []
    }
    if (!ctx._source.containsKey('devices')) {
      ctx._source['devices'] = []
    }
    for (int index = 0; index < params.iterate.length; index++) {
      if (params.iterate[index].browserObject != null) {
        int browserIndex = getBrowsersObjectIndex(ctx._source.browsers, params.iterate[index].browserObject);
        if (browserIndex >= 0) {
          ctx._source.browsers.remove(browserIndex);
        }
        ctx._source.browsers.add(params.iterate[index].browserObject);
      }
      if (params.iterate[index].deviceObject != null) {
        int deviceIndex = getDevicesObjectIndex(ctx._source.devices, params.iterate[index].deviceObject);
        if (deviceIndex >= 0) {
          ctx._source.devices.remove(deviceIndex);
        }
        ctx._source.devices.add(params.iterate[index].deviceObject);
      }
    }
    """,
    "params": {
      "iterate": [
        {
          "deviceObject": {
            "deviceId": "162c04e48832e338",
            "appName": "test_app",
            "appNameSpace": "com.test",
            "appBuild": 55,
            "appVersion": "4.6.3",
            "deviceName": "OP4B80L1",
            "manufacturer": "OPPO",
            "model": "CPH1937",
            "networkCarrier": "Jio",
            "os": "Android",
            "platform": "Android",
            "timezone": "Asia/Kolkata",
            "version": "10"
          }
        },
        {
          "browserObject": {
            "deviceType": "mobile",
            "osName": "Android",
            "browserName": "Chrome",
            "osVersion": 10,
            "browserVersion": "80.0.3987.99"
          }
        }
      ]
    }
  },
  "upsert": {}
}
Sign up to request clarification or add additional context in comments.

5 Comments

is there any more efficient way to override the nested objects based on some conditions apart from painless scripts?
First, you should store the script so as not to have to recompile it on each execution. That would contribute to execute it faster
Also do we have any way of printing the variables value in painless script?
What I usually do is to use Debug.explain()as described here: elastic.co/guide/en/elasticsearch/painless/current/…
man, explicit checking null pointers... where is this in docs... thank you!!!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.