Normally the recommendation is to do that in the application talking to Elasticsearch.
It is worth noting that Elasticsearch differs in a lot of ways from a relational database so saying "row" is kind of a red flag. Not that "row" and "document" are all that different, its just that if you expect Elasticsearch to do relational database things you'll be disappointed. It makes other architectural choices.
As far as how to do it with bulk you'd have to use
doc and not use
doc_as_upsert. That is less than ideal, especially in 2.x because of the script sandboxing problem. 5.0 will be nicer where you can safely specify a
painless language script as part of the upsert. But still, you have to write a script which is a bit of a if all you want to do is merge documents.
As far as "locking" a field, no, that isn't a thing Elasticsearch does. Constraints and validation is one of those things relational databases do that Elasticsearch would prefer you handle in your application. Partially it is because we haven't implemented it and partially it is because we figure you are better off enforcing constraints on the application side because it is typically cheaper to scale application servers than it is to scale Elasticsearch servers.
For what it is worth we are talking about adding a
_last_modified field over here. It might make sense to add a
_created field as well, I don't know. I know what you are trying to do is fairly cumbersome with the tools that we have now and not super rare. But have a look at the debate on that issue. We go through a lot before we add a feature like this because we want to make it easy to use, and that requires that we have some idea of how it is going to be used which is difficult.