From 4b7242287f8a47247cdbf9f728aa72094ebaca0a Mon Sep 17 00:00:00 2001 From: George Panchuk Date: Wed, 2 Sep 2026 17:20:03 +0700 Subject: [PATCH] fix: fix local single prefetch --- qdrant_client/local/async_qdrant_local.py | 3 -- qdrant_client/local/qdrant_local.py | 5 +-- tests/congruence_tests/test_query.py | 55 +++++++++++++++++++++++ 3 files changed, 57 insertions(+), 6 deletions(-) diff --git a/qdrant_client/local/async_qdrant_local.py b/qdrant_client/local/async_qdrant_local.py index 971ad5e8..af44c485 100644 --- a/qdrant_client/local/async_qdrant_local.py +++ b/qdrant_client/local/async_qdrant_local.py @@ -320,9 +320,6 @@ class AsyncQdrantLocal(AsyncQdrantBase): prefetches = [] if isinstance(prefetch, types.Prefetch): prefetches = [prefetch] - prefetches.extend( - prefetch.prefetch if isinstance(prefetch.prefetch, list) else [prefetch.prefetch] - ) elif isinstance(prefetch, Sequence): prefetches = list(prefetch) return [ diff --git a/qdrant_client/local/qdrant_local.py b/qdrant_client/local/qdrant_local.py index 9e02df82..ec14f9ec 100644 --- a/qdrant_client/local/qdrant_local.py +++ b/qdrant_client/local/qdrant_local.py @@ -351,10 +351,9 @@ class QdrantLocal(QdrantBase): prefetches = [] if isinstance(prefetch, types.Prefetch): + # nested prefetches are resolved recursively in `_resolve_prefetch_input`, + # they must not be hoisted to this level prefetches = [prefetch] - prefetches.extend( - prefetch.prefetch if isinstance(prefetch.prefetch, list) else [prefetch.prefetch] - ) elif isinstance(prefetch, Sequence): prefetches = list(prefetch) diff --git a/tests/congruence_tests/test_query.py b/tests/congruence_tests/test_query.py index e0e5546c..09a8e7b4 100644 --- a/tests/congruence_tests/test_query.py +++ b/tests/congruence_tests/test_query.py @@ -799,6 +799,47 @@ class TestSimpleSearcher: using="text", ) + def dense_query_text_nested_single_prefetch(self, client: QdrantBase) -> models.QueryResponse: + # a single prefetch object instead of a list of one: the nested prefetch must only feed + # the prefetch it belongs to, it is not an extra source of the root query + return client.query_points( + collection_name=COLLECTION_NAME, + prefetch=models.Prefetch( + prefetch=models.Prefetch( + query=self.dense_vector_query_text, + using="text", + limit=10, + ), + query=self.dense_vector_query_code, + using="code", + limit=5, + ), + query=models.FusionQuery(fusion=models.Fusion.RRF), + with_payload=True, + limit=10, + ) + + def dense_query_text_nested_single_prefetch_rescore( + self, client: QdrantBase + ) -> models.QueryResponse: + return client.query_points( + collection_name=COLLECTION_NAME, + prefetch=models.Prefetch( + prefetch=models.Prefetch( + query=self.dense_vector_query_text, + using="text", + limit=10, + ), + query=self.dense_vector_query_code, + using="code", + limit=5, + ), + query=self.dense_vector_query_text, + using="text", + with_payload=True, + limit=10, + ) + @classmethod def dense_recommend_image(cls, client: QdrantBase) -> models.QueryResponse: return client.query_points( @@ -1208,6 +1249,20 @@ def test_dense_query_nested_prefetch(): ) +def test_dense_query_nested_single_prefetch(): + fixture_points = generate_fixtures() + + searcher = TestSimpleSearcher() + + local_client, http_client, grpc_client = init_clients(fixture_points) + + for query in ( + searcher.dense_query_text_nested_single_prefetch, + searcher.dense_query_text_nested_single_prefetch_rescore, + ): + compare_clients_results(local_client, http_client, grpc_client, query) + + def test_dense_query_filtered_prefetch(): fixture_points = generate_fixtures()