Elasticsearch update nested field with painless script in ElasticSearch API vs java API

1.5k views Asked by At

Could someone explain what's wrong with ES java api? I'm making query to update by script with ES api and it works perfectly (below)

POST user/ut/520411_5752/_update
{
  "script": {
    "source": "ctx._source.cars.add(params.car)",
    "params": {
      "car": {
        "pub_id":155,
        "name":"qwerty"
      }
    }
  }
}

and java

HashMap<String, Object> params = new HashMap<>();
params.put("car", GSON.toJson(car));

Script inline = new Script(ScriptType.INLINE, "painless",
"ctx._source.cars.add(params.car)", 
params);
    
UpdateRequest request = new UpdateRequest(USER, UT, id).script(inline).retryOnConflict(1);
UpdateResponse update = elasticClient.update(request, RequestOptions.DEFAULT);

As you may guess java is failing with exception

ElasticsearchStatusException[Elasticsearch exception [type=mapper_parsing_exception, reason=object mapping for [cars] tried to parse field [null] as object, but found a concrete value]]

Tried different options, with empty car field, with filled car field, everytime getting an exception above.

2

There are 2 answers

0
viq On BEST ANSWER

Seems like i surrender with RestHighLevelClient. But i remember that i have LowLevelClient and rewrite my update using it and it's working fine. Since i don't need to check the response, i don't need to parse it too.

5
Joe - Check out my books On

Your java code LGTM and the problem comes from a "stale" version of your mapping that wouldn't be compatible with what your script would end up adjusting. Check my other answer from a while ago to gain more insight into this cryptic-sounding error.

Simply put, make sure your mapping looks something like this:

{
  "mappings": {
    "properties": {
      "cars": {
        "type": "object",
        "properties": {
          "pub_id": {
            "type": "integer"
          },
          "name": {
            "type": "text"
          }
        }
      }
    }
  }
}

that is, cars as an object and its contents clearly defined too. After that your script should work just fine.

BTW just to be perfectly safe, I'd add two more checks before I called .add:

if (ctx._source.cars != null && ctx._source.cars instanceof ArrayList) { ctx._source.cars.add(params.car) }