From 6d4ea4987aa6e1bb4fbe3b428cac93ce465359bc Mon Sep 17 00:00:00 2001 From: Miguel Grinberg Date: Thu, 23 Oct 2025 15:55:38 +0100 Subject: [PATCH 1/5] Pass explicit field list in document searches --- elasticsearch/dsl/_async/document.py | 3 ++- elasticsearch/dsl/_sync/document.py | 3 ++- test_elasticsearch/test_dsl/_async/test_document.py | 7 +++---- test_elasticsearch/test_dsl/_sync/test_document.py | 7 +++---- .../test_dsl/test_integration/_async/test_document.py | 8 +++++--- .../test_dsl/test_integration/_sync/test_document.py | 8 +++++--- 6 files changed, 20 insertions(+), 16 deletions(-) diff --git a/elasticsearch/dsl/_async/document.py b/elasticsearch/dsl/_async/document.py index 53b4f12c3..0366e0a74 100644 --- a/elasticsearch/dsl/_async/document.py +++ b/elasticsearch/dsl/_async/document.py @@ -126,9 +126,10 @@ def search( Create an :class:`~elasticsearch.dsl.Search` instance that will search over this ``Document``. """ - return AsyncSearch( + s = AsyncSearch( using=cls._get_using(using), index=cls._default_index(index), doc_type=[cls] ) + return s.source(cls._get_field_names()) @classmethod async def get( diff --git a/elasticsearch/dsl/_sync/document.py b/elasticsearch/dsl/_sync/document.py index 07bda6ec1..f8f857ed8 100644 --- a/elasticsearch/dsl/_sync/document.py +++ b/elasticsearch/dsl/_sync/document.py @@ -120,9 +120,10 @@ def search( Create an :class:`~elasticsearch.dsl.Search` instance that will search over this ``Document``. """ - return Search( + s = Search( using=cls._get_using(using), index=cls._default_index(index), doc_type=[cls] ) + return s.source(cls._get_field_names()) @classmethod def get( diff --git a/test_elasticsearch/test_dsl/_async/test_document.py b/test_elasticsearch/test_dsl/_async/test_document.py index c986e588e..a110d1caf 100644 --- a/test_elasticsearch/test_dsl/_async/test_document.py +++ b/test_elasticsearch/test_dsl/_async/test_document.py @@ -821,10 +821,9 @@ class TypedDocAnnotated(AsyncDocument): s = doc_class.search().sort(doc_class.st, -doc_class.dt, +doc_class.ob.st) s.aggs.bucket("terms_agg", "terms", field=doc_class.k1) - assert s.to_dict() == { - "aggs": {"terms_agg": {"terms": {"field": "k1"}}}, - "sort": ["st", {"dt": {"order": "desc"}}, "ob.st"], - } + d = s.to_dict() + assert d["aggs"] == {"terms_agg": {"terms": {"field": "k1"}}} + assert d["sort"] == ["st", {"dt": {"order": "desc"}}, "ob.st"] @pytest.mark.skipif(sys.version_info < (3, 10), reason="requires Python 3.10") diff --git a/test_elasticsearch/test_dsl/_sync/test_document.py b/test_elasticsearch/test_dsl/_sync/test_document.py index c4e0cc31a..a4f88e501 100644 --- a/test_elasticsearch/test_dsl/_sync/test_document.py +++ b/test_elasticsearch/test_dsl/_sync/test_document.py @@ -821,10 +821,9 @@ class TypedDocAnnotated(Document): s = doc_class.search().sort(doc_class.st, -doc_class.dt, +doc_class.ob.st) s.aggs.bucket("terms_agg", "terms", field=doc_class.k1) - assert s.to_dict() == { - "aggs": {"terms_agg": {"terms": {"field": "k1"}}}, - "sort": ["st", {"dt": {"order": "desc"}}, "ob.st"], - } + d = s.to_dict() + assert d["aggs"] == {"terms_agg": {"terms": {"field": "k1"}}} + assert d["sort"] == ["st", {"dt": {"order": "desc"}}, "ob.st"] @pytest.mark.skipif(sys.version_info < (3, 10), reason="requires Python 3.10") diff --git a/test_elasticsearch/test_dsl/test_integration/_async/test_document.py b/test_elasticsearch/test_dsl/test_integration/_async/test_document.py index 274db1027..0dd0de0ca 100644 --- a/test_elasticsearch/test_dsl/test_integration/_async/test_document.py +++ b/test_elasticsearch/test_dsl/test_integration/_async/test_document.py @@ -864,7 +864,7 @@ async def test_dense_vector( class Doc(AsyncDocument): float_vector: List[float] = mapped_field(DenseVector()) byte_vector: List[int] = mapped_field(DenseVector(element_type="byte")) - bit_vector: str = mapped_field(DenseVector(element_type="bit")) + bit_vector: List[int] = mapped_field(DenseVector(element_type="bit")) class Index: name = "vectors" @@ -873,13 +873,15 @@ class Index: await Doc.init() doc = Doc( - float_vector=[1.0, 1.2, 2.3], byte_vector=[12, 23, 34, 45], bit_vector="12abf0" + float_vector=[1.0, 1.2, 2.3], + byte_vector=[12, 23, 34, 45], + bit_vector=[18, -43, -112], ) await doc.save(refresh=True) docs = await Doc.search().execute() assert len(docs) == 1 - assert docs[0].float_vector == doc.float_vector + assert [round(v, 1) for v in docs[0].float_vector] == doc.float_vector assert docs[0].byte_vector == doc.byte_vector assert docs[0].bit_vector == doc.bit_vector diff --git a/test_elasticsearch/test_dsl/test_integration/_sync/test_document.py b/test_elasticsearch/test_dsl/test_integration/_sync/test_document.py index 62857cd9a..ce6ac03ad 100644 --- a/test_elasticsearch/test_dsl/test_integration/_sync/test_document.py +++ b/test_elasticsearch/test_dsl/test_integration/_sync/test_document.py @@ -852,7 +852,7 @@ def test_dense_vector(client: Elasticsearch, es_version: Tuple[int, ...]) -> Non class Doc(Document): float_vector: List[float] = mapped_field(DenseVector()) byte_vector: List[int] = mapped_field(DenseVector(element_type="byte")) - bit_vector: str = mapped_field(DenseVector(element_type="bit")) + bit_vector: List[int] = mapped_field(DenseVector(element_type="bit")) class Index: name = "vectors" @@ -861,13 +861,15 @@ class Index: Doc.init() doc = Doc( - float_vector=[1.0, 1.2, 2.3], byte_vector=[12, 23, 34, 45], bit_vector="12abf0" + float_vector=[1.0, 1.2, 2.3], + byte_vector=[12, 23, 34, 45], + bit_vector=[18, -43, -112], ) doc.save(refresh=True) docs = Doc.search().execute() assert len(docs) == 1 - assert docs[0].float_vector == doc.float_vector + assert [round(v, 1) for v in docs[0].float_vector] == doc.float_vector assert docs[0].byte_vector == doc.byte_vector assert docs[0].bit_vector == doc.bit_vector From 9764113a80bc8cc455a8fc23d27128cec04e7c2e Mon Sep 17 00:00:00 2001 From: Miguel Grinberg Date: Thu, 23 Oct 2025 16:34:10 +0100 Subject: [PATCH 2/5] more fixes --- elasticsearch/dsl/_async/document.py | 4 ++-- elasticsearch/dsl/_sync/document.py | 4 ++-- .../test_dsl/test_integration/_async/test_search.py | 2 +- .../test_dsl/test_integration/_sync/test_search.py | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/elasticsearch/dsl/_async/document.py b/elasticsearch/dsl/_async/document.py index 0366e0a74..41e3d203d 100644 --- a/elasticsearch/dsl/_async/document.py +++ b/elasticsearch/dsl/_async/document.py @@ -126,10 +126,10 @@ def search( Create an :class:`~elasticsearch.dsl.Search` instance that will search over this ``Document``. """ - s = AsyncSearch( + s = AsyncSearch[Self]( using=cls._get_using(using), index=cls._default_index(index), doc_type=[cls] ) - return s.source(cls._get_field_names()) + return s.source(cls._get_field_names()) # type: ignore[arg-type] @classmethod async def get( diff --git a/elasticsearch/dsl/_sync/document.py b/elasticsearch/dsl/_sync/document.py index f8f857ed8..132e636b3 100644 --- a/elasticsearch/dsl/_sync/document.py +++ b/elasticsearch/dsl/_sync/document.py @@ -120,10 +120,10 @@ def search( Create an :class:`~elasticsearch.dsl.Search` instance that will search over this ``Document``. """ - s = Search( + s = Search[Self]( using=cls._get_using(using), index=cls._default_index(index), doc_type=[cls] ) - return s.source(cls._get_field_names()) + return s.source(cls._get_field_names()) # type: ignore[arg-type] @classmethod def get( diff --git a/test_elasticsearch/test_dsl/test_integration/_async/test_search.py b/test_elasticsearch/test_dsl/test_integration/_async/test_search.py index 2499b8d03..883bfdff3 100644 --- a/test_elasticsearch/test_dsl/test_integration/_async/test_search.py +++ b/test_elasticsearch/test_dsl/test_integration/_async/test_search.py @@ -135,7 +135,7 @@ async def test_inner_hits_are_serialized_to_dict( @pytest.mark.anyio async def test_scan_respects_doc_types(async_data_client: AsyncElasticsearch) -> None: - repos = [repo async for repo in Repository.search().scan()] + repos = [repo async for repo in Repository.search().source("organization").scan()] assert 1 == len(repos) assert isinstance(repos[0], Repository) diff --git a/test_elasticsearch/test_dsl/test_integration/_sync/test_search.py b/test_elasticsearch/test_dsl/test_integration/_sync/test_search.py index 41dd720a3..04876345e 100644 --- a/test_elasticsearch/test_dsl/test_integration/_sync/test_search.py +++ b/test_elasticsearch/test_dsl/test_integration/_sync/test_search.py @@ -135,7 +135,7 @@ def test_inner_hits_are_serialized_to_dict( @pytest.mark.sync def test_scan_respects_doc_types(data_client: Elasticsearch) -> None: - repos = [repo for repo in Repository.search().scan()] + repos = [repo for repo in Repository.search().source("organization").scan()] assert 1 == len(repos) assert isinstance(repos[0], Repository) From eb54d3bfcb08f0cf4dcc0e73dca142fa2cfddc0b Mon Sep 17 00:00:00 2001 From: Miguel Grinberg Date: Fri, 24 Oct 2025 12:37:14 +0100 Subject: [PATCH 3/5] use exclude_vectors=False --- elasticsearch/dsl/_async/document.py | 2 +- elasticsearch/dsl/_sync/document.py | 2 +- elasticsearch/dsl/search_base.py | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/elasticsearch/dsl/_async/document.py b/elasticsearch/dsl/_async/document.py index 41e3d203d..c3631260a 100644 --- a/elasticsearch/dsl/_async/document.py +++ b/elasticsearch/dsl/_async/document.py @@ -129,7 +129,7 @@ def search( s = AsyncSearch[Self]( using=cls._get_using(using), index=cls._default_index(index), doc_type=[cls] ) - return s.source(cls._get_field_names()) # type: ignore[arg-type] + return s.source(exclude_vectors=False) @classmethod async def get( diff --git a/elasticsearch/dsl/_sync/document.py b/elasticsearch/dsl/_sync/document.py index 132e636b3..c37c7639d 100644 --- a/elasticsearch/dsl/_sync/document.py +++ b/elasticsearch/dsl/_sync/document.py @@ -123,7 +123,7 @@ def search( s = Search[Self]( using=cls._get_using(using), index=cls._default_index(index), doc_type=[cls] ) - return s.source(cls._get_field_names()) # type: ignore[arg-type] + return s.source(exclude_vectors=False) @classmethod def get( diff --git a/elasticsearch/dsl/search_base.py b/elasticsearch/dsl/search_base.py index cd690ddae..142d0f86e 100644 --- a/elasticsearch/dsl/search_base.py +++ b/elasticsearch/dsl/search_base.py @@ -729,6 +729,9 @@ def ensure_strings( ) -> Union[str, List[str], Dict[str, List[str]]]: if isinstance(fields, dict): return {k: ensure_strings(v) for k, v in fields.items()} + elif isinstance(fields, bool): + # boolean settings should stay the way they are + return fields elif not isinstance(fields, (str, InstrumentedField)): # we assume that if `fields` is not a any of [dict, str, # InstrumentedField] then it is an iterable of strings or From 84256cc8b5264ccc97e9eb834d5838db51d62b52 Mon Sep 17 00:00:00 2001 From: Miguel Grinberg Date: Fri, 24 Oct 2025 15:55:42 +0100 Subject: [PATCH 4/5] remove unneeded source call --- .../test_dsl/test_integration/_async/test_search.py | 2 +- .../test_dsl/test_integration/_sync/test_search.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test_elasticsearch/test_dsl/test_integration/_async/test_search.py b/test_elasticsearch/test_dsl/test_integration/_async/test_search.py index 883bfdff3..2499b8d03 100644 --- a/test_elasticsearch/test_dsl/test_integration/_async/test_search.py +++ b/test_elasticsearch/test_dsl/test_integration/_async/test_search.py @@ -135,7 +135,7 @@ async def test_inner_hits_are_serialized_to_dict( @pytest.mark.anyio async def test_scan_respects_doc_types(async_data_client: AsyncElasticsearch) -> None: - repos = [repo async for repo in Repository.search().source("organization").scan()] + repos = [repo async for repo in Repository.search().scan()] assert 1 == len(repos) assert isinstance(repos[0], Repository) diff --git a/test_elasticsearch/test_dsl/test_integration/_sync/test_search.py b/test_elasticsearch/test_dsl/test_integration/_sync/test_search.py index 04876345e..41dd720a3 100644 --- a/test_elasticsearch/test_dsl/test_integration/_sync/test_search.py +++ b/test_elasticsearch/test_dsl/test_integration/_sync/test_search.py @@ -135,7 +135,7 @@ def test_inner_hits_are_serialized_to_dict( @pytest.mark.sync def test_scan_respects_doc_types(data_client: Elasticsearch) -> None: - repos = [repo for repo in Repository.search().source("organization").scan()] + repos = [repo for repo in Repository.search().scan()] assert 1 == len(repos) assert isinstance(repos[0], Repository) From 7d746a825f56dd33d489f362f28d8413df53ff2a Mon Sep 17 00:00:00 2001 From: Miguel Grinberg Date: Fri, 24 Oct 2025 16:02:05 +0100 Subject: [PATCH 5/5] add missing type hint --- elasticsearch/dsl/search_base.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/elasticsearch/dsl/search_base.py b/elasticsearch/dsl/search_base.py index 142d0f86e..c5b12c788 100644 --- a/elasticsearch/dsl/search_base.py +++ b/elasticsearch/dsl/search_base.py @@ -721,12 +721,13 @@ def ensure_strings( def ensure_strings( fields: Union[ + bool, str, "InstrumentedField", List[Union[str, "InstrumentedField"]], Dict[str, List[Union[str, "InstrumentedField"]]], ], - ) -> Union[str, List[str], Dict[str, List[str]]]: + ) -> Union[bool, str, List[str], Dict[str, List[str]]]: if isinstance(fields, dict): return {k: ensure_strings(v) for k, v in fields.items()} elif isinstance(fields, bool):