def update_by_function(self, extra_doc, index, doc_type, id, querystring_args=None,<EOL>update_func=None, attempts=<NUM_LIT:2>):
if querystring_args is None:<EOL><INDENT>querystring_args = {}<EOL><DEDENT>if update_func is None:<EOL><INDENT>update_func = dict.update<EOL><DEDENT>for attempt in range(attempts - <NUM_LIT:1>, -<NUM_LIT:1>, -<NUM_LIT:1>):<EOL><INDENT>current_doc = self.get(index, doc_type, id, **querystring_args)<EOL>new_doc = update_func(current_doc, extra_doc)<EOL>if new_doc is None:<EOL><INDENT>new_doc = current_doc<EOL><DEDENT>try:<EOL><INDENT>return self.index(new_doc, index, doc_type, id,<EOL>version=current_doc._meta.version, querystring_args=querystring_args)<EOL><DEDENT>except VersionConflictEngineException:<EOL><INDENT>if attempt <= <NUM_LIT:0>:<EOL><INDENT>raise<EOL><DEDENT>self.refresh(index)<EOL><DEDENT><DEDENT>
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.