Elasticsearch upserting and appending to array

Seventh Helix picture Seventh Helix · Aug 3, 2013 · Viewed 25.1k times · Source

I'm trying to write a script that will upsert a new user record to ElasticSearch, updating any information if the user already exists, and appending a new PaymentInfo object to the user's Payments array if it exists in the update object. Here's a simplified version of what I'm working with so far:

curl -XPOST 'http://localhost:9200/usrtest/usr/1/_update' -d '
{
    "doc_as_upsert": true, 
    "doc": {
        "customerId": "1",
        "firstName": "Mark",
        "lastName": "Z",
        "emailAddress": "[email protected]",
        "paymentInfo": {
            "pid": "1",
            "amt": "10"
        }
    }
}'

This almost does what I want in that it inserts the doc properly, or updates the doc if a user exists with the same ID, but it's missing the aspect to add this paymentInfo to the user's paymentInfos array if the user exists already. As it is right now, it just overrides the paymentInfo object. I've tried adding this script to the update JSON:

"script": "if (ctx._source.containsKey(\"paymentInfos\")) {ctx._source.paymentInfos += paymentInfo;} else {ctx._source.paymentInfos = paymentInfo}"

but elasticsearch ignores doc elements when the script element is specified.

I feel like I'm missing something silly here, but I'm not sure. Can anyone here help me out?

Edit: I've tried the following as well:

curl -XPOST 'http://localhost:9200/usrtest/usr/1/_update' -d '
{    
    "script": "if (ctx._source.containsKey(\"paymentInfos\")) {ctx._source.paymentInfos += paymentInfo;} else {ctx._source.paymentInfos = paymentInfo}",
    "upsert": {
        "customerId": "1",
        "firstName": "Mark",
        "lastName": "Z",
        "emailAddress": "[email protected]",
        "paymentInfo": {
            "pid": "1",
            "amt": "10"
         }
    },
    "params": {
         "paymentInfo": {
            "pid": "1",
                "amt": "10"
         }
    }
}'

Which also almost does what I want it to, in that it appends the paymentInfo objects when I run the script several times, but otherwise it doesn't update the document itself (i.e. if I run the script again, changing Mark to Mindy, it doesn't update since upsert elements are only used if the doc doesn't exist already).

Answer

Paul Blakey picture Paul Blakey · Dec 3, 2013

you'll want to add some array brackets to the insert part of the script.

"script": "if (ctx._source.containsKey(\"paymentInfos\")) {ctx._source.paymentInfos += paymentInfo;} else {ctx._source.paymentInfos = [paymentInfo]}"

the 'paymentInfos' property in the first section is defined as an object, so that may also be causing you to fall down.