Updating an Object field

I'm trying to figure out how to use the Update API to update an object field in my document. Let's say my _source field looks like this:

   {
	"documentID": 123,
	"originalFilename": "Build a Better Post.pdf",
	"modDate": "2017-11-16T18:22:54.48",
	"documentType": "pdf",
	"keySystem": "web",
	"title": "Build a Better Post",
	"createPreview": false,
	"uploadedBy": "DA5208B3-2198-44C6-8256-0AEBC4DD1588",
	"streamItemData": {
		"itemID": 800,
		"author": {
			"employeeID": 9,
			"authorName": {
				"firstName": "Joseph",
				"preferredName": "Joe",
				"lastName": "Smith"
			},
			"title": "manager"
		}
	}
}

I would like to update the "streamItemData" field in its entirety. Using the Update API, partial document updates don't work because I want to replace the entire object, not select fields:

POST test/type1/123/_update
{
    "doc" : {
        "streamItemData": {
    		"itemID": 340,
    		"author": {
    			"employeeID": 32,
    			"authorName": {
    				"firstName": "Bill",
    				"lastName": "Johnson"
    			}
    		}
    	}
    }
}

You'll notice that Bill Johnson doesn't have a "preferredName" or "title", but the old values from Joe Smith are still left in there after the update. So that doesn't work for me.

Is it possible to do what I want using a script?

I know that this works:

POST test/type1/123/_update
{
    "script" : "Map a = new HashMap(); a.put('employeeID', 32); a.put('firstName', 'Bill'); a.put('lastName', 'Johnson'); Map si = new HashMap(); si.put('itemID', 340); si.put('author', a); ctx._source.streamItemData = si;"
}

But it seems like there has to be a better way. In reality, my "streamItemData" object is much larger and has many more nested objects. Is there a way to pass in the whole object as json to the script instead?

There are two approaches that I can think of.

You can put the value of streamItemData between square brackets and treat it like an array with one element. The update API replaces the entire array each time your update a document, so this will allow you to replace the entire object. There should be no other impact on your applications, as arrays with one element are the same thing as a single object.

POST test/type1/123/_update
{
  "doc": {
    "streamItemData": [
      {
        "itemID": 340,
        "author": {
          "employeeID": 32,
          "authorName": {
            "firstName": "Bill",
            "lastName": "Johnson"
          }
        }
      }
    ]
  }
}

Or, if you want to use a script, the following is a nice shorthand:

POST test/type1/123/_update
{
  "script": """  
    ctx._source.streamItemData = [ "author": [ "employeeID": 32, "authorName" : ["firstName": "Bill", "lastName": "Johnson" ] ] ]
    """
}

Thank you for the response. It looks like your second option does exactly what I want it to do. The first option changes the streamItemData field to an array, which, while elasticsearch doesn't care,
would mean that any application consuming that json would have to parse that field as an array. But the scripted option seems to work great!

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.