0

GET _doc/1

"_source": {
"documents": [
    {
        "docid": "ID001",
        "added_vals": [
            {
                "code": "123",
                "label": "Abc"
            },
            {
                "code": "113",
                "label": "Xyz"
            }
        ]
    },
    {
        "docid": "ID002",
        "added_vals": [
            {
                "code": "123",
                "label": "Abc"
            }
        ]
    }
],
"id": "1"
}

POST /_bulk

{ "update": { "_id": "1"}}
{ "doc": { "documents": [ { "docid": "ID001", "status" : "cancelled" } ], "id": "1" }, "doc_as_upsert": true }

The problem above is when I run my bulk update script it replaces that document field, removing the added_vals list. Would I be able to achieve this using painless script? Thank you.

Amit
  • 30,756
  • 6
  • 57
  • 88
Sid
  • 765
  • 5
  • 29
  • 57

1 Answers1

0

Using elasticsearch painless scripting

POST /_bulk

{ "update": { "_id": "1"} }
{ "scripted_upsert":true, "script" :{ "source": "if(ctx._version == null) { ctx._source = params; } else { def param = params; def src = ctx._source; for(s in src.documents) { boolean found = false; for(p in param.documents) { if (p.docid == s.docid) { found = true; if(s.added_vals != null) { p.added_vals = s.added_vals; } } } if(!found) param.documents.add(s); } ctx._source = param; }", "lang": "painless", "params" : { "documents": [ { "docid": "ID001", "status" : "cancelled" } ], "id": "1" } }, "upsert" : {  } } 

well, this one worked for me. I need to tweak a few more things that I require, but I will just leave it here for someone who may need it. Didnt know it was this simple. If there is any other answer that might be easier, please do submit so. Thanks.

"script" :

if(ctx._version == null)
{
    ctx._source = params;
}
else
{
    def param = params;    
    def src = ctx._source;
    for(s in src.documents)
    {
        boolean found = false;
        for(p in param.documents)
        {
            if (p.docid == s.docid) 
            {
                found = true;
                if(s.added_vals != null)
                {
                    p.added_vals = s.added_vals;                    
                }
            }
        }
        if(!found) param.documents.add(s);
    }
    ctx._source = param;        
}

I am not sure if I should modify the params directly so I used and pass the params to the param variable. I also used scripted_upsert: true with a ctx._version not null check.

Sid
  • 765
  • 5
  • 29
  • 57
  • pardon the 1 line 'if' with extra brackets, I removed them on my actual script, i just want to show which statement it properly falls to. – Sid Mar 03 '20 at 08:19
  • the problem with this approach... it uses the params as the new document, it removes the other fields. with some tweaking you may include the other fields. – Sid Mar 03 '20 at 08:39
  • change [ ctx._source = param; ] to [ ctx._source.documents = param.documents; ] to only apply change the change on document list – Sid Mar 03 '20 at 09:38