def scan(
    client: Elasticsearch,
    query: Optional[Any] = None,
    scroll: str = "5m",
    raise_on_error: bool = True,
    preserve_order: bool = False,
    size: int = 1000,
    request_timeout: Optional[float] = None,
    clear_scroll: bool = True,
    scroll_kwargs: Optional[MutableMapping[str, Any]] = None,
    **kwargs: Any,
) -> Iterable[Dict[str, Any]]:
    """
    Simple abstraction on top of the
    :meth:`~elasticsearch.Elasticsearch.scroll` api - a simple iterator that
    yields all hits as returned by underlining scroll requests.

    By default scan does not return results in any pre-determined order. To
    have a standard order in the returned documents (either by score or
    explicit sort definition) when scrolling, use ``preserve_order=True``. This
    may be an expensive operation and will negate the performance benefits of
    using ``scan``.

    :arg client: instance of :class:`~elasticsearch.Elasticsearch` to use
    :arg query: body for the :meth:`~elasticsearch.Elasticsearch.search` api
    :arg scroll: Specify how long a consistent view of the index should be
        maintained for scrolled search
    :arg raise_on_error: raises an exception (``ScanError``) if an error is
        encountered (some shards fail to execute). By default we raise.
    :arg preserve_order: don't set the ``search_type`` to ``scan`` - this will
        cause the scroll to paginate with preserving the order. Note that this
        can be an extremely expensive operation and can easily lead to
        unpredictable results, use with caution.
    :arg size: size (per shard) of the batch send at each iteration.
    :arg request_timeout: explicit timeout for each call to ``scan``
    :arg clear_scroll: explicitly calls delete on the scroll id via the clear
        scroll API at the end of the method on completion or error, defaults
        to true.
    :arg scroll_kwargs: additional kwargs to be passed to
        :meth:`~elasticsearch.Elasticsearch.scroll`

    Any additional keyword arguments will be passed to the initial
    :meth:`~elasticsearch.Elasticsearch.search` call::

        scan(client,
            query={"query": {"match": {"title": "python"}}},
            index="orders-*",
            doc_type="books"
        )

    """
    scroll_kwargs = scroll_kwargs or {}
    if not preserve_order:
        query = query.copy() if query else {}
        query["sort"] = "_doc"

    def pop_transport_kwargs(kw: MutableMapping[str, Any]) -> Dict[str, Any]:
        # Grab options that should be propagated to every
        # API call within this helper instead of just 'search()'
        transport_kwargs = {}
        for key in (
            "headers",
            "api_key",
            "http_auth",
            "basic_auth",
            "bearer_auth",
            "opaque_id",
        ):
            try:
                value = kw.pop(key)
                if key == "http_auth":
                    key = "basic_auth"
                transport_kwargs[key] = value
            except KeyError:
                pass
        return transport_kwargs

    client = client.options(
        request_timeout=request_timeout, **pop_transport_kwargs(kwargs)
    )
    client._client_meta = (("h", "s"),)

    # Setting query={"from": ...} would make 'from' be used
    # as a keyword argument instead of 'from_'. We handle that here.
    def normalize_from_keyword(kw: MutableMapping[str, Any]) -> None:
        if "from" in kw:
            kw["from_"] = kw.pop("from")

    normalize_from_keyword(kwargs)
    try:
        search_kwargs = query.copy() if query else {}
        normalize_from_keyword(search_kwargs)
        search_kwargs.update(kwargs)
        search_kwargs["scroll"] = scroll
        search_kwargs["size"] = size
        resp = client.search(**search_kwargs)

    # Try the old deprecated way if we fail immediately on parameters.
    except TypeError:
        search_kwargs = kwargs.copy()
        search_kwargs["scroll"] = scroll
        search_kwargs["size"] = size
        resp = client.search(body=query, **search_kwargs)

    scroll_id = resp.get("_scroll_id")
    scroll_transport_kwargs = pop_transport_kwargs(scroll_kwargs)
    if scroll_transport_kwargs:
        scroll_client = client.options(**scroll_transport_kwargs)
    else:
        scroll_client = client

    try:
        while scroll_id and resp["hits"]["hits"]:
            yield from resp["hits"]["hits"]

            # Default to 0 if the value isn't included in the response
            shards_info: Dict[str, int] = resp["_shards"]
            shards_successful = shards_info.get("successful", 0)
            shards_skipped = shards_info.get("skipped", 0)
            shards_total = shards_info.get("total", 0)

            # check if we have any errors
            if (shards_successful + shards_skipped) < shards_total:
                shards_message = "Scroll request has only succeeded on %d (+%d skipped) shards out of %d."
                logger.warning(
                    shards_message,
                    shards_successful,
                    shards_skipped,
                    shards_total,
                )
                if raise_on_error:
                    raise ScanError(
                        scroll_id,
                        shards_message
                        % (
                            shards_successful,
                            shards_skipped,
                            shards_total,
                        ),
                    )
            resp = scroll_client.scroll(
                scroll_id=scroll_id, scroll=scroll, **scroll_kwargs
            )
            scroll_id = resp.get("_scroll_id")

    finally:
        if scroll_id and clear_scroll:
            client.options(ignore_status=404).clear_scroll(scroll_id=scroll_id)

    Simple abstraction on top of the
    :meth:`~elasticsearch.Elasticsearch.scroll` api - a simple iterator that
    yields all hits as returned by underlining scroll requests.

    By default scan does not return results in any pre-determined order. To
    have a standard order in the returned documents (either by score or
    explicit sort definition) when scrolling, use ``preserve_order=True``. This
    may be an expensive operation and will negate the performance benefits of
    using ``scan``.

    :arg client: instance of :class:`~elasticsearch.Elasticsearch` to use
    :arg query: body for the :meth:`~elasticsearch.Elasticsearch.search` api
    :arg scroll: Specify how long a consistent view of the index should be
        maintained for scrolled search
    :arg raise_on_error: raises an exception (``ScanError``) if an error is
        encountered (some shards fail to execute). By default we raise.
    :arg preserve_order: don't set the ``search_type`` to ``scan`` - this will
        cause the scroll to paginate with preserving the order. Note that this
        can be an extremely expensive operation and can easily lead to
        unpredictable results, use with caution.
    :arg size: size (per shard) of the batch send at each iteration.
    :arg request_timeout: explicit timeout for each call to ``scan``
    :arg clear_scroll: explicitly calls delete on the scroll id via the clear
        scroll API at the end of the method on completion or error, defaults
        to true.
    :arg scroll_kwargs: additional kwargs to be passed to
        :meth:`~elasticsearch.Elasticsearch.scroll`

    Any additional keyword arguments will be passed to the initial
    :meth:`~elasticsearch.Elasticsearch.search` call::

        scan(client,
            query={"query": {"match": {"title": "python"}}},
            index="orders-*",
            doc_type="books"
        )

    
elasticsearch/helpers/actions.py