How do I update elements in an array in elasticsearch?

20 Views Asked by At

I have an index where one of the fields is a list of objects. I can't make each one its own field or the number of fields would explode.

Updates to the document come along as items to add or update one of the objects in the list. How can I craft an update to achieve this?

1

There are 1 best solutions below

0
Julian On BEST ANSWER

You can do this with scripts and update_by_query.

Let's create an index:

PUT test-index/_doc/1
{
  "id": 3
}

After a new sub-document comes along we'd like it to look like this:

GET test-index/_doc/1

returns:

{
  "id": 1,
  "items": [{
    "id": "q1",
    "colour": "brown"
  }]
}

We can do this by storing the following script and then invoking it:

POST _scripts/upsert_item_in_list
{
  "script":{  
    "lang": "painless", 
    "source": """
    if (ctx._source[params.field] == null) {
      ctx._source[params.field] = [];
    }
    def items = ctx._source[params.field];
    def index = -1;
    for (int i = 0; i < items.length; i++)
    { 
        if (items[i][params.key] == params.item[params.key]) 
        {
          index = i
        } 
    }
   if (index>=0) {
     items[index] = params.item;
   } else {
     items.add(params.item)
   }
    """
  }
}

It takes three parameters:

  • the name of the field where the array is
  • the name of the unique key field in the array items
  • the value to store.

We can invoke it as follows:

POST test-index/_update_by_query
{
  "script":{  
    "params": {
      "item": { "id": "q1", "colour": "brown" },
      "field": "items",
      "key": "id"
    },
    "id": "upsert_item_in_list"
  },
  "query":{
    "match":{
      "id": 1
    } 
  }
}