11

I have required to remove object from array that satisfies the condition, I am able to update the object of array on the basis of condition, which is as follow:

PUT twitter/twit/1
{"list": 
     [
        {
            "tweet_id": "1",
            "a": "b"
        },
        {
            "tweet_id": "123",
            "a": "f"
        }
    ]
}

POST /twitter/twit/1/_update
{"script":"foreach (item :ctx._source.list) {
                if item['tweet_id'] == tweet_id) {
                      item['new_field'] = 'ghi';
                }
           }",
 "params": {tweet_id": 123"}
}

this is working

for remove i am doing this

POST /twitter/twit/1/_update
{ "script": "foreach (item : ctx._source.list) {
                    if item['tweet_id'] == tweet_id) {
                          ctx._source.list.remove(item); 
                    }
            }",
  "params": { tweet_id": "123" }
}

but this is not working and giving this error,

ElasticsearchIllegalArgumentException[failed to execute script]; nested: ConcurrentModificationException; Error: ElasticsearchIllegalArgumentException[failed to execute script]; nested: ConcurrentModificationException

I am able to remove whole array or whole field using

"script": "ctx._source.remove('list')"

I am also able to remove object from array by specifying all the keys of an object using

"script":"ctx._source.list.remove(tag)",
     "params" : {
        "tag" : {"tweet_id": "123","a": "f"}

my node module elastic search version is 2.4.2 elastic search server is 1.3.2

xlecoustillier
  • 15,451
  • 14
  • 56
  • 79
Rajit Garg
  • 499
  • 8
  • 21

2 Answers2

19

You get that because you are trying to modify a list while iterating through it, meaning you want to change a list of object and, at the same time, listing those objects.

You instead need to do this:

POST /twitter/twit/1/_update
{
  "script": "item_to_remove = nil; foreach (item : ctx._source.list) { if (item['tweet_id'] == tweet_id) { item_to_remove=item; } } if (item_to_remove != nil) ctx._source.list.remove(item_to_remove);",
  "params": {"tweet_id": "123"}
}

If you have more than one item that matches the criteria, use a list instead:

POST /twitter/twit/1/_update
{
  "script": "items_to_remove = []; foreach (item : ctx._source.list) { if (item['tweet_id'] == tweet_id) { items_to_remove.add(item); } } foreach (item : items_to_remove) {ctx._source.list.remove(item);}",
  "params": {"tweet_id": "123"}
}
Andrei Stefan
  • 48,348
  • 5
  • 84
  • 82
  • 1
    Thanks Andrei Stefan, This is working, if list contain only one object of tweet_id = 123, but This is not working , if I want to remove multiple object having same tweet_id = 123, What I have required to do for that??? – Rajit Garg Oct 09 '14 at 05:01
  • Also, I would appreciate if you could upvote the answer. – Andrei Stefan Oct 09 '14 at 06:23
  • Thank you very much Andrei Stefan, This is now working, after this I am very happy, as yesterday I have spend almost complete day on this , Thank you once again.... – Rajit Garg Oct 09 '14 at 09:30
  • how can i know the list have ` remove` method, does it have `append` – jiamo Aug 24 '15 at 09:25
8

For people that need this working in elasticsearch 2.0 and up, the nil and foreach don't get recognized by groovy.

So here's a updated version, including a option to replace a item with the same id by a new object.

and also passing it the upsert will make sure the item gets added even if the document doesn't exist yet

{
  "script": "item_to_remove = null; ctx._source.delivery.each { elem -> if (elem.id == item_to_add.id) { item_to_remove=elem; } }; if (item_to_remove != null) ctx._source.delivery.remove(item_to_remove); if (item_to_add.size() > 1) ctx._source.delivery += item_to_add;",
  "params": {"item_to_add": {"id": "5", "title": "New item"}},
  "upsert": [{"id": "5", "title": "New item"}]
}
Ludo - Off the record
  • 4,280
  • 4
  • 29
  • 23
  • 1
    Can you please also add in groove language to remove more than one values as list instead of single remove? – Joshua I Oct 24 '16 at 21:40