Search code examples
elasticsearchelasticsearch-painless

Elasticsearch partial update of Object(multi=True)


How to update document with field mapping Object(multi=True), when a document can have both single (dictionary) and multiple values (list of dictionaries).

Example of documents in the same index:

A single value in items:

{
    "title": "Some title",
    "items": {
        "id": 123,
        "key": "foo"
    }
}

Multiple values in items:

{
    "title": "Some title",
    "items": [{
        "id": 456,
        "key": "foo"
    }, {
        "id": 789,
        "key": "bar"
    }]
}

Solution

  • You can try to use the following script.
    I intentionally formatted inline attribute to show what's inside.

    POST index_name/_update_by_query
    {
        "search": {
            "term": {
                "items.key": "foo"
            }
        },
        "script": {
            "inline": "
                if (ctx._source.items instanceof List) {
                    for (item in ctx.source.items) {
                        if (item.key == params.old_value) {
                            item.key = params.new_value;
                            break;
                        }
                    }
                } else {
                    ctx._source.items.key = params.new_value;
                }
            ",
            "params": {"old_value": "foo", "new_value": "bar"},
            "lang": "painless'
        }
    }
    

    And to make it work, replace inline attribute with a single line value.

    "inline": "if (ctx._source.items instanceof List) {for (item in ctx.source.items) {if (item.key == params.old_value) {item.key = params.new_value;break;}}} else {ctx._source.items.key = params.new_value;}"