Decorator that manages smart defaults or transforms for common find methods:

     - fields/projection: list of fields to be returned. Contrary to pymongo, _id won't be added automatically
     - json: performs a json_clone on the results. Beware of performance!
     - timeout
     - return_document

def find_method(func):
  """
    Decorator that manages smart defaults or transforms for common find methods:

     - fields/projection: list of fields to be returned. Contrary to pymongo, _id won't be added automatically
     - json: performs a json_clone on the results. Beware of performance!
     - timeout
     - return_document
  """
  def wrapped(*args, **kwargs):

    # Normalize the fields argument if passed as a positional param.
    if len(args) == 3 and func.__name__ in ("find", "find_one", "find_by_id", "find_by_ids"):
      _param_fields(kwargs, args[2])
      args = (args[0], args[1])
    elif "fields" in kwargs:
      _param_fields(kwargs, kwargs["fields"])
      del kwargs["fields"]
    elif "projection" in kwargs:
      _param_fields(kwargs, kwargs["projection"])

    if "timeout" in kwargs:
      kwargs["no_cursor_timeout"] = not bool(kwargs["timeout"])
      del kwargs["timeout"]

    if "spec" in kwargs:
      kwargs["filter"] = kwargs["spec"]
      del kwargs["spec"]

    if kwargs.get("return_document") == "after":
        kwargs["return_document"] = ReturnDocument.AFTER
    elif kwargs.get("return_document") == "before":
        kwargs["return_document"] = ReturnDocument.BEFORE

    ret = func(*args, **kwargs)

    if kwargs.get("json"):
      ret = json_clone(ret)

    return ret

  return wrapped