docs: def update_by_function(self, extra_doc, index, doc_type, id, querystring_args=None,
                             update_func=None, attempts=2):
          """
          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.
          """
          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)