3

Given the document

curl -XPUT 'localhost:9200/test/me/here' -d '{
  "top" : [
    { "searchkey" : "change"},
    { "searchkey" : "keep"}
  ]
}'

I need an update query that will add new field to sub-document with searchkey equal to change and will keep any other sub-document intact. The expected result is then:

{
  "top" : [
    { "searchkey" : "change", "newfield" : "newvalue"},
    { "searchkey" : "keep"}
  ]
}

Running query that selects inner document by index works, but I do not know the inner order in advance and it is quite fragile anyways:

curl -XPOST 'localhost:9200/test/me/here/_update' -d '{
    "script" : "ctx._source.top[0].newfield = v",
    "params" : {
      "v" : "newvalue"
    }
}'

Is there a way to tell ES to add the new field to the inner document that matches some condition? Something like:

curl -XPOST 'localhost:9200/test/me/here/_update' -d '{
    "script" : "ctx._source.top[ctx._source.top.searchkey == s].newfield = v",
    "params" : {
      "s" : "change",
      "v" : "newvalue"
    }
}'

Or, will I do better and save some headache if I eliminate the array and transform the document to:

{
"change" : {},
"keep" : {}
}
3
  • Have you looked at stackoverflow.com/questions/18360225/… , Commented Aug 18, 2014 at 11:32
  • Yes. That is not my case, I want to add new info to existing nested document which is itself in array. Commented Aug 18, 2014 at 11:34
  • Yaa, I got it, you have to reindex that document with updated value. Commented Aug 18, 2014 at 11:39

2 Answers 2

3

You can use the update with script. See example:

PUT test/data/3/
{
   "source": [
     {
       "name": "A",
       "count": 1
     },
     {
       "name": "B",
       "count": 2
     },
     {
       "name": "c",
       "count": 3
     }
   ]
}

GET test/data/3

POST test/data/3/_update
{
 "script": " for (int i = 0; i < source.size(); i++) {boolean f = false;for (int j = 0; j < ctx._source.source.size(); j++) {if (ctx._source.source[j].name == source[i].name) {ctx._source.source[j].count = source[i].count;f=true;break;}}\nif(!f){ctx._source.source.add(source[i]);}}",
 "params": {
   "source": [
     {
       "name": "A",
       "count": 10
     },
     {
       "name": "B",
       "count": 30
     },
     {
       "name": "D",
       "count": 50
     }
   ]
 }
}

GET test/data/3
Sign up to request clarification or add additional context in comments.

Comments

2

We can use update_by_query API to get the documents that matches the condition and then scroll through the array of json to update the object within the array.

POST test/data/_update_by_query
{
    "script": {
        "lang":"painless",
        "source":"""
            for(int i=0;i<ctx._source.top.length;i++){
                if(ctx._source.top[i].searchkey == params.match_value){
                    ctx._source.top[i].new_field = params.new_value;
                }
            }
        """,
        "params" : {
             "match_value" : "change",
             "new_value" : "new_value"
         }
    },
    "query" : {
        "match" : {
            "top.searchkey": "change"
        }
    }

}

1 Comment

Thanks Shyamala Rao If you get an for error invalid content-type due to multi-line script you should use single quotes and single line, e.g. "source":"for(int i=0;i<ctx._source.top.length;i++){ if(ctx._source.top[i].searchkey == params.match_value){ ctx._source.top[i].new_field = params.new_value; } }"

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.