'Update an already indexed typed JSON document.
The update happens client-side, i.e. the current document is retrieved,
updated locally and finally pushed to the server. This may repeat up to
``attempts`` times in case of version conflicts.
:param update_func: A callable ``update_func(current_doc, extra_doc)``
that computes and returns the updated doc. Alternatively it may
update ``current_doc`` in place and return None. The default
``update_func`` is ``dict.update``.
:param attempts: How many times to retry in case of version conflict.'

def update_by_function(self, extra_doc, index, doc_type, id, querystring_args=None, update_func=None, attempts=2):

    if (querystring_args is None):
        querystring_args = {}
    if (update_func is None):
        update_func = dict.update
    for attempt in range((attempts - 1), (-1), (-1)):
        current_doc = self.get(index, doc_type, id, **querystring_args)
        new_doc = update_func(current_doc, extra_doc)
        if (new_doc is None):
            new_doc = current_doc
        try:
            return self.index(new_doc, index, doc_type, id, version=current_doc._meta.version, querystring_args=querystring_args)
        except VersionConflictEngineException:
            if (attempt <= 0):
                raise
            self.refresh(index)
