qdrant-haystack 10.6.0__tar.gz → 11.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. {qdrant_haystack-10.6.0 → qdrant_haystack-11.1.0}/CHANGELOG.md +51 -19
  2. {qdrant_haystack-10.6.0 → qdrant_haystack-11.1.0}/PKG-INFO +1 -1
  3. {qdrant_haystack-10.6.0 → qdrant_haystack-11.1.0}/src/haystack_integrations/components/retrievers/qdrant/retriever.py +30 -27
  4. {qdrant_haystack-10.6.0 → qdrant_haystack-11.1.0}/src/haystack_integrations/document_stores/qdrant/document_store.py +109 -96
  5. {qdrant_haystack-10.6.0 → qdrant_haystack-11.1.0}/tests/test_document_store.py +14 -9
  6. {qdrant_haystack-10.6.0 → qdrant_haystack-11.1.0}/tests/test_document_store_async.py +14 -9
  7. {qdrant_haystack-10.6.0 → qdrant_haystack-11.1.0}/tests/test_embedding_retriever.py +31 -0
  8. {qdrant_haystack-10.6.0 → qdrant_haystack-11.1.0}/tests/test_hybrid_retriever.py +33 -0
  9. {qdrant_haystack-10.6.0 → qdrant_haystack-11.1.0}/tests/test_sparse_embedding_retriever.py +33 -0
  10. {qdrant_haystack-10.6.0 → qdrant_haystack-11.1.0}/.gitignore +0 -0
  11. {qdrant_haystack-10.6.0 → qdrant_haystack-11.1.0}/LICENSE.txt +0 -0
  12. {qdrant_haystack-10.6.0 → qdrant_haystack-11.1.0}/README.md +0 -0
  13. {qdrant_haystack-10.6.0 → qdrant_haystack-11.1.0}/examples/embedding_retrieval.py +0 -0
  14. {qdrant_haystack-10.6.0 → qdrant_haystack-11.1.0}/pydoc/config_docusaurus.yml +0 -0
  15. {qdrant_haystack-10.6.0 → qdrant_haystack-11.1.0}/pyproject.toml +0 -0
  16. {qdrant_haystack-10.6.0 → qdrant_haystack-11.1.0}/src/haystack_integrations/components/retrievers/py.typed +0 -0
  17. {qdrant_haystack-10.6.0 → qdrant_haystack-11.1.0}/src/haystack_integrations/components/retrievers/qdrant/__init__.py +0 -0
  18. {qdrant_haystack-10.6.0 → qdrant_haystack-11.1.0}/src/haystack_integrations/document_stores/py.typed +0 -0
  19. {qdrant_haystack-10.6.0 → qdrant_haystack-11.1.0}/src/haystack_integrations/document_stores/qdrant/__init__.py +0 -0
  20. {qdrant_haystack-10.6.0 → qdrant_haystack-11.1.0}/src/haystack_integrations/document_stores/qdrant/converters.py +0 -0
  21. {qdrant_haystack-10.6.0 → qdrant_haystack-11.1.0}/src/haystack_integrations/document_stores/qdrant/filters.py +0 -0
  22. {qdrant_haystack-10.6.0 → qdrant_haystack-11.1.0}/src/haystack_integrations/document_stores/qdrant/migrate_to_sparse.py +0 -0
  23. {qdrant_haystack-10.6.0 → qdrant_haystack-11.1.0}/tests/__init__.py +0 -0
  24. {qdrant_haystack-10.6.0 → qdrant_haystack-11.1.0}/tests/conftest.py +0 -0
  25. {qdrant_haystack-10.6.0 → qdrant_haystack-11.1.0}/tests/test_converters.py +0 -0
  26. {qdrant_haystack-10.6.0 → qdrant_haystack-11.1.0}/tests/test_dict_converters.py +0 -0
  27. {qdrant_haystack-10.6.0 → qdrant_haystack-11.1.0}/tests/test_filters.py +0 -0
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## [integrations/qdrant-v11.0.0] - 2026-09-25
4
+
5
+ ### 🚜 Refactor
6
+
7
+ - [**breaking**] Qdrant - propagate errors on backend failures instead of empty responses (#3997)
8
+
9
+ ### ⚙️ CI
10
+
11
+ - Improve changelog generation; fix existing changelogs (#3883)
12
+
13
+
14
+ ## [integrations/qdrant-v10.6.0] - 2026-09-01
15
+
16
+ ### 🐛 Bug Fixes
17
+
18
+ - Standardize license files (#3771)
19
+ - `QdrantDocumentStore` removing duplicated tests already covered by haystack Mixin tests + fixing dedup + adding missing Async test (#3785)
20
+
21
+
3
22
  ## [integrations/qdrant-v10.5.0] - 2026-08-03
4
23
 
5
24
  ### 🚀 Features
@@ -79,7 +98,7 @@
79
98
 
80
99
  - Feat: `QdrantDocumentStore` return number deleted docs on `delete_by_filter` (#2807)
81
100
 
82
- ## [integrations/qdrant-v9.6.0] - 2026-02-02
101
+ ## [integrations/qdrant-v10.1.0] - 2026-02-02
83
102
 
84
103
  ### 🚀 Features
85
104
 
@@ -87,11 +106,15 @@
87
106
 
88
107
  ### 🧹 Chores
89
108
 
90
- - Make fmt command more forgiving (#2671)
91
- - [**breaking**] Qdrant - drop Python 3.9 and use X|Y typing (#2726)
92
109
  - Disabling progress bar in `QdrantDocumentStore` tests (#2797)
93
110
 
94
- ### 🌀 Miscellaneous
111
+
112
+ ## [integrations/qdrant-v10.0.0] - 2026-01-13
113
+
114
+ ### 🧹 Chores
115
+
116
+ - Make fmt command more forgiving (#2671)
117
+ - [**breaking**] Qdrant - drop Python 3.9 and use X|Y typing (#2726)
95
118
 
96
119
 
97
120
  ## [integrations/qdrant-v9.5.0] - 2026-01-07
@@ -151,6 +174,8 @@
151
174
  - Align core-integrations Hatch scripts (#1898)
152
175
  - Update md files for new hatch scripts (#1911)
153
176
 
177
+ ## [integrations/qdrant-v9.1.3] - 2025-05-28
178
+
154
179
  ### 🌀 Miscellaneous
155
180
 
156
181
  - Add pins for Qdrant (#1853)
@@ -374,6 +399,18 @@
374
399
  - *(Qdrant)* Allow payload indexing + on disk vectors (#553)
375
400
  - Qdrant datetime filtering support (#570)
376
401
 
402
+ ### 📚 Documentation
403
+
404
+ - Disable-class-def (#556)
405
+
406
+ ### 🌀 Miscellaneous
407
+
408
+ - Make tests show coverage (#566)
409
+ - Remove references to Python 3.7 (#601)
410
+ - Make Document Stores initially skip `SparseEmbedding` (#606)
411
+
412
+ ## [integrations/qdrant-v3.1.0] - 2024-03-06
413
+
377
414
  ### 🐛 Bug Fixes
378
415
 
379
416
  - Fix: fix linter errors (#282)
@@ -388,18 +425,11 @@
388
425
 
389
426
  - Update category slug (#442)
390
427
  - Small consistency improvements (#536)
391
- - Disable-class-def (#556)
392
428
 
393
429
  ### ⚙️ CI
394
430
 
395
431
  - Generate API docs for Qdrant (#361)
396
432
 
397
- ### 🌀 Miscellaneous
398
-
399
- - Make tests show coverage (#566)
400
- - Remove references to Python 3.7 (#601)
401
- - Make Document Stores initially skip `SparseEmbedding` (#606)
402
-
403
433
  ## [integrations/qdrant-v3.0.0] - 2024-01-22
404
434
 
405
435
  ### 🌀 Miscellaneous
@@ -408,20 +438,22 @@
408
438
 
409
439
  ## [integrations/qdrant-v2.0.1] - 2024-01-18
410
440
 
411
- ### 🚀 Features
412
-
413
- - Add Qdrant integration (#98)
414
-
415
441
  ### 🐛 Bug Fixes
416
442
 
417
443
  - Fix import paths for beta5 (#237)
418
444
 
419
- ### 🚜 Refactor
420
-
421
- - Use `hatch_vcs` to manage integrations versioning (#103)
422
-
423
445
  ### 🌀 Miscellaneous
424
446
 
425
447
  - Renamed QdrntRetriever to QdrntEmbeddingRetriever (#174)
426
448
 
449
+ ## [integrations/qdrant-v2.0.0] - 2023-12-18
450
+
451
+ ### 🚀 Features
452
+
453
+ - Add Qdrant integration (#98)
454
+
455
+ ### 🚜 Refactor
456
+
457
+ - Use `hatch_vcs` to manage integrations versioning (#103)
458
+
427
459
  <!-- generated by git-cliff -->
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: qdrant-haystack
3
- Version: 10.6.0
3
+ Version: 11.1.0
4
4
  Summary: An integration of Qdrant ANN vector database backend with Haystack
5
5
  Project-URL: Source, https://github.com/deepset-ai/haystack-core-integrations
6
6
  Project-URL: Documentation, https://github.com/deepset-ai/haystack-core-integrations/blob/main/integrations/qdrant/README.md
@@ -188,9 +188,9 @@ class QdrantEmbeddingRetriever:
188
188
  query_embedding=query_embedding,
189
189
  filters=filters,
190
190
  top_k=top_k or self._top_k,
191
- scale_score=scale_score or self._scale_score,
192
- return_embedding=return_embedding or self._return_embedding,
193
- score_threshold=score_threshold or self._score_threshold,
191
+ scale_score=scale_score if scale_score is not None else self._scale_score,
192
+ return_embedding=return_embedding if return_embedding is not None else self._return_embedding,
193
+ score_threshold=score_threshold if score_threshold is not None else self._score_threshold,
194
194
  group_by=group_by or self._group_by,
195
195
  group_size=group_size or self._group_size,
196
196
  )
@@ -243,9 +243,9 @@ class QdrantEmbeddingRetriever:
243
243
  query_embedding=query_embedding,
244
244
  filters=filters,
245
245
  top_k=top_k or self._top_k,
246
- scale_score=scale_score or self._scale_score,
247
- return_embedding=return_embedding or self._return_embedding,
248
- score_threshold=score_threshold or self._score_threshold,
246
+ scale_score=scale_score if scale_score is not None else self._scale_score,
247
+ return_embedding=return_embedding if return_embedding is not None else self._return_embedding,
248
+ score_threshold=score_threshold if score_threshold is not None else self._score_threshold,
249
249
  group_by=group_by or self._group_by,
250
250
  group_size=group_size or self._group_size,
251
251
  )
@@ -433,9 +433,9 @@ class QdrantSparseEmbeddingRetriever:
433
433
  query_sparse_embedding=query_sparse_embedding,
434
434
  filters=filters,
435
435
  top_k=top_k or self._top_k,
436
- scale_score=scale_score or self._scale_score,
437
- return_embedding=return_embedding or self._return_embedding,
438
- score_threshold=score_threshold or self._score_threshold,
436
+ scale_score=scale_score if scale_score is not None else self._scale_score,
437
+ return_embedding=return_embedding if return_embedding is not None else self._return_embedding,
438
+ score_threshold=score_threshold if score_threshold is not None else self._score_threshold,
439
439
  group_by=group_by or self._group_by,
440
440
  group_size=group_size or self._group_size,
441
441
  )
@@ -493,9 +493,9 @@ class QdrantSparseEmbeddingRetriever:
493
493
  query_sparse_embedding=query_sparse_embedding,
494
494
  filters=filters,
495
495
  top_k=top_k or self._top_k,
496
- scale_score=scale_score or self._scale_score,
497
- return_embedding=return_embedding or self._return_embedding,
498
- score_threshold=score_threshold or self._score_threshold,
496
+ scale_score=scale_score if scale_score is not None else self._scale_score,
497
+ return_embedding=return_embedding if return_embedding is not None else self._return_embedding,
498
+ score_threshold=score_threshold if score_threshold is not None else self._score_threshold,
499
499
  group_by=group_by or self._group_by,
500
500
  group_size=group_size or self._group_size,
501
501
  )
@@ -560,9 +560,10 @@ class QdrantHybridRetriever:
560
560
  :param return_embedding: Whether to return the embeddings of the retrieved Documents.
561
561
  :param filter_policy: Policy to determine how filters are applied.
562
562
  :param score_threshold: A minimal score threshold for the result.
563
- Score of the returned result might be higher or smaller than the threshold
564
- depending on the Distance function used.
565
- E.g. for cosine similarity only higher scores will be returned.
563
+ The threshold is applied to the fused Reciprocal Rank Fusion (RRF) scores, so only documents with a
564
+ higher fused score are returned. RRF scores are not comparable to dense or sparse similarity scores,
565
+ so thresholds tuned for those retrievers don't carry over. With `group_by`, groups with no surviving
566
+ hits are dropped.
566
567
  :param group_by: Payload field to group by, must be a string or number field. If the field contains more than 1
567
568
  value, all values will be used for grouping. One point can be in multiple groups.
568
569
  :param group_size: Maximum amount of points to return per group. Default is 3.
@@ -659,7 +660,7 @@ class QdrantHybridRetriever:
659
660
  rrf_weights: list[float] | None = None,
660
661
  ) -> dict[str, list[Document]]:
661
662
  """
662
- Run the Sparse Embedding Retriever on the given input data.
663
+ Run the Hybrid Retriever on the given input data.
663
664
 
664
665
  :param query_embedding: Dense embedding of the query.
665
666
  :param query_sparse_embedding: Sparse embedding of the query.
@@ -670,9 +671,10 @@ class QdrantHybridRetriever:
670
671
  groups to return.
671
672
  :param return_embedding: Whether to return the embedding of the retrieved Documents.
672
673
  :param score_threshold: A minimal score threshold for the result.
673
- Score of the returned result might be higher or smaller than the threshold
674
- depending on the Distance function used.
675
- E.g. for cosine similarity only higher scores will be returned.
674
+ The threshold is applied to the fused Reciprocal Rank Fusion (RRF) scores, so only documents with a
675
+ higher fused score are returned. RRF scores are not comparable to dense or sparse similarity scores,
676
+ so thresholds tuned for those retrievers don't carry over. With `group_by`, groups with no surviving
677
+ hits are dropped.
676
678
  :param group_by: Payload field to group by, must be a string or number field. If the field contains more than 1
677
679
  value, all values will be used for grouping. One point can be in multiple groups.
678
680
  :param group_size: Maximum amount of points to return per group. Default is 3.
@@ -704,8 +706,8 @@ class QdrantHybridRetriever:
704
706
  query_sparse_embedding=query_sparse_embedding,
705
707
  filters=filters,
706
708
  top_k=top_k or self._top_k,
707
- return_embedding=return_embedding or self._return_embedding,
708
- score_threshold=score_threshold or self._score_threshold,
709
+ return_embedding=return_embedding if return_embedding is not None else self._return_embedding,
710
+ score_threshold=score_threshold if score_threshold is not None else self._score_threshold,
709
711
  group_by=group_by or self._group_by,
710
712
  group_size=group_size or self._group_size,
711
713
  rrf_k=rrf_k if rrf_k is not None else self._rrf_k,
@@ -729,7 +731,7 @@ class QdrantHybridRetriever:
729
731
  rrf_weights: list[float] | None = None,
730
732
  ) -> dict[str, list[Document]]:
731
733
  """
732
- Asynchronously run the Sparse Embedding Retriever on the given input data.
734
+ Asynchronously run the Hybrid Retriever on the given input data.
733
735
 
734
736
  :param query_embedding: Dense embedding of the query.
735
737
  :param query_sparse_embedding: Sparse embedding of the query.
@@ -740,9 +742,10 @@ class QdrantHybridRetriever:
740
742
  groups to return.
741
743
  :param return_embedding: Whether to return the embedding of the retrieved Documents.
742
744
  :param score_threshold: A minimal score threshold for the result.
743
- Score of the returned result might be higher or smaller than the threshold
744
- depending on the Distance function used.
745
- E.g. for cosine similarity only higher scores will be returned.
745
+ The threshold is applied to the fused Reciprocal Rank Fusion (RRF) scores, so only documents with a
746
+ higher fused score are returned. RRF scores are not comparable to dense or sparse similarity scores,
747
+ so thresholds tuned for those retrievers don't carry over. With `group_by`, groups with no surviving
748
+ hits are dropped.
746
749
  :param group_by: Payload field to group by, must be a string or number field. If the field contains more than 1
747
750
  value, all values will be used for grouping. One point can be in multiple groups.
748
751
  :param group_size: Maximum amount of points to return per group. Default is 3.
@@ -774,8 +777,8 @@ class QdrantHybridRetriever:
774
777
  query_sparse_embedding=query_sparse_embedding,
775
778
  filters=filters,
776
779
  top_k=top_k or self._top_k,
777
- return_embedding=return_embedding or self._return_embedding,
778
- score_threshold=score_threshold or self._score_threshold,
780
+ return_embedding=return_embedding if return_embedding is not None else self._return_embedding,
781
+ score_threshold=score_threshold if score_threshold is not None else self._score_threshold,
779
782
  group_by=group_by or self._group_by,
780
783
  group_size=group_size or self._group_size,
781
784
  rrf_k=rrf_k if rrf_k is not None else self._rrf_k,
@@ -15,7 +15,6 @@ from haystack.utils import Secret, deserialize_secrets_inplace
15
15
  from haystack.utils.misc import _normalize_metadata_field_name
16
16
  from numpy import exp
17
17
  from qdrant_client.http import models as rest
18
- from qdrant_client.http.exceptions import UnexpectedResponse
19
18
  from tqdm import tqdm
20
19
 
21
20
  from .converters import (
@@ -338,6 +337,9 @@ class QdrantDocumentStore:
338
337
  def count_documents(self) -> int:
339
338
  """
340
339
  Returns the number of documents present in the Document Store.
340
+
341
+ :returns: The number of documents in the collection.
342
+ :raises QdrantStoreError: Counting documents fails.
341
343
  """
342
344
  self._initialize_client()
343
345
  assert self._client is not None
@@ -346,15 +348,16 @@ class QdrantDocumentStore:
346
348
  collection_name=self.index,
347
349
  )
348
350
  return response.count
349
- except (UnexpectedResponse, ValueError):
350
- # Qdrant local raises ValueError if the collection is not found, but
351
- # with the remote server UnexpectedResponse is raised. Until that's unified,
352
- # we need to catch both.
353
- return 0
351
+ except Exception as e:
352
+ msg = f"Error when calling QdrantDocumentStore.count_documents(): {e}"
353
+ raise QdrantStoreError(msg) from e
354
354
 
355
355
  async def count_documents_async(self) -> int:
356
356
  """
357
- Asynchronously returns the number of documents present in the document dtore.
357
+ Asynchronously returns the number of documents present in the document store.
358
+
359
+ :returns: The number of documents in the collection.
360
+ :raises QdrantStoreError: Counting documents fails.
358
361
  """
359
362
  await self._initialize_async_client()
360
363
  assert self._async_client is not None
@@ -363,11 +366,9 @@ class QdrantDocumentStore:
363
366
  collection_name=self.index,
364
367
  )
365
368
  return response.count
366
- except (UnexpectedResponse, ValueError):
367
- # Qdrant local raises ValueError if the collection is not found, but
368
- # with the remote server UnexpectedResponse is raised. Until that's unified,
369
- # we need to catch both.
370
- return 0
369
+ except Exception as e:
370
+ msg = f"Error when calling QdrantDocumentStore.count_documents_async(): {e}"
371
+ raise QdrantStoreError(msg) from e
371
372
 
372
373
  def filter_documents(
373
374
  self,
@@ -921,38 +922,39 @@ class QdrantDocumentStore:
921
922
  Deletes all documents from the document store.
922
923
 
923
924
  :param recreate_index: Whether to recreate the index after deleting all documents.
925
+ :raises QdrantStoreError: Deleting documents or recreating the collection fails.
924
926
  """
925
927
 
926
928
  self._initialize_client()
927
929
  assert self._client is not None
928
930
 
929
- if recreate_index:
930
- # get current collection config as json
931
- collection_info = self._client.get_collection(collection_name=self.index)
932
- info_json = collection_info.model_dump()
931
+ try:
932
+ if recreate_index:
933
+ # get current collection config as json
934
+ collection_info = self._client.get_collection(collection_name=self.index)
935
+ info_json = collection_info.model_dump()
933
936
 
934
- # deal with the Optional use_sparse_embeddings
935
- sparse_vectors = info_json["config"]["params"]["sparse_vectors"]
936
- use_sparse_embeddings = True if sparse_vectors else False
937
+ # deal with the Optional use_sparse_embeddings
938
+ sparse_vectors = info_json["config"]["params"]["sparse_vectors"]
939
+ use_sparse_embeddings = True if sparse_vectors else False
937
940
 
938
- # deal with the Optional sparse_idf
939
- hnsw_config = info_json["config"]["params"]["vectors"].get("config", {}).get("hnsw_config", None)
940
- sparse_idf = True if use_sparse_embeddings and hnsw_config else False
941
+ # deal with the Optional sparse_idf
942
+ hnsw_config = info_json["config"]["params"]["vectors"].get("config", {}).get("hnsw_config", None)
943
+ sparse_idf = True if use_sparse_embeddings and hnsw_config else False
941
944
 
942
- # recreate collection
943
- self._set_up_collection(
944
- collection_name=self.index,
945
- embedding_dim=info_json["config"]["params"]["vectors"]["size"],
946
- recreate_collection=True,
947
- similarity=info_json["config"]["params"]["vectors"]["distance"].lower(),
948
- use_sparse_embeddings=use_sparse_embeddings,
949
- sparse_idf=sparse_idf,
950
- on_disk=info_json["config"]["hnsw_config"]["on_disk"],
951
- payload_fields_to_index=info_json["payload_schema"],
952
- )
945
+ # recreate collection
946
+ self._set_up_collection(
947
+ collection_name=self.index,
948
+ embedding_dim=info_json["config"]["params"]["vectors"]["size"],
949
+ recreate_collection=True,
950
+ similarity=info_json["config"]["params"]["vectors"]["distance"].lower(),
951
+ use_sparse_embeddings=use_sparse_embeddings,
952
+ sparse_idf=sparse_idf,
953
+ on_disk=info_json["config"]["hnsw_config"]["on_disk"],
954
+ payload_fields_to_index=info_json["payload_schema"],
955
+ )
953
956
 
954
- else:
955
- try:
957
+ else:
956
958
  self._client.delete(
957
959
  collection_name=self.index,
958
960
  points_selector=rest.FilterSelector(
@@ -962,48 +964,48 @@ class QdrantDocumentStore:
962
964
  ),
963
965
  wait=self.wait_result_from_api,
964
966
  )
965
- except Exception as e:
966
- logger.warning(
967
- f"Error {e} when calling QdrantDocumentStore.delete_all_documents()",
968
- )
967
+ except Exception as e:
968
+ msg = f"Error when calling QdrantDocumentStore.delete_all_documents(): {e}"
969
+ raise QdrantStoreError(msg) from e
969
970
 
970
971
  async def delete_all_documents_async(self, recreate_index: bool = False) -> None:
971
972
  """
972
973
  Asynchronously deletes all documents from the document store.
973
974
 
974
975
  :param recreate_index: Whether to recreate the index after deleting all documents.
976
+ :raises QdrantStoreError: Deleting documents or recreating the collection fails.
975
977
  """
976
978
 
977
979
  await self._initialize_async_client()
978
980
  assert self._async_client is not None
979
981
 
980
- if recreate_index:
981
- # get current collection config as json
982
- collection_info = await self._async_client.get_collection(collection_name=self.index)
983
- info_json = collection_info.model_dump()
982
+ try:
983
+ if recreate_index:
984
+ # get current collection config as json
985
+ collection_info = await self._async_client.get_collection(collection_name=self.index)
986
+ info_json = collection_info.model_dump()
984
987
 
985
- # deal with the Optional use_sparse_embeddings
986
- sparse_vectors = info_json["config"]["params"]["sparse_vectors"]
987
- use_sparse_embeddings = True if sparse_vectors else False
988
+ # deal with the Optional use_sparse_embeddings
989
+ sparse_vectors = info_json["config"]["params"]["sparse_vectors"]
990
+ use_sparse_embeddings = True if sparse_vectors else False
988
991
 
989
- # deal with the Optional sparse_idf
990
- hnsw_config = info_json["config"]["params"]["vectors"].get("config", {}).get("hnsw_config", None)
991
- sparse_idf = True if use_sparse_embeddings and hnsw_config else False
992
+ # deal with the Optional sparse_idf
993
+ hnsw_config = info_json["config"]["params"]["vectors"].get("config", {}).get("hnsw_config", None)
994
+ sparse_idf = True if use_sparse_embeddings and hnsw_config else False
992
995
 
993
- # recreate collection
994
- await self._set_up_collection_async(
995
- collection_name=self.index,
996
- embedding_dim=info_json["config"]["params"]["vectors"]["size"],
997
- recreate_collection=True,
998
- similarity=info_json["config"]["params"]["vectors"]["distance"].lower(),
999
- use_sparse_embeddings=use_sparse_embeddings,
1000
- sparse_idf=sparse_idf,
1001
- on_disk=info_json["config"]["hnsw_config"]["on_disk"],
1002
- payload_fields_to_index=info_json["payload_schema"],
1003
- )
996
+ # recreate collection
997
+ await self._set_up_collection_async(
998
+ collection_name=self.index,
999
+ embedding_dim=info_json["config"]["params"]["vectors"]["size"],
1000
+ recreate_collection=True,
1001
+ similarity=info_json["config"]["params"]["vectors"]["distance"].lower(),
1002
+ use_sparse_embeddings=use_sparse_embeddings,
1003
+ sparse_idf=sparse_idf,
1004
+ on_disk=info_json["config"]["hnsw_config"]["on_disk"],
1005
+ payload_fields_to_index=info_json["payload_schema"],
1006
+ )
1004
1007
 
1005
- else:
1006
- try:
1008
+ else:
1007
1009
  await self._async_client.delete(
1008
1010
  collection_name=self.index,
1009
1011
  points_selector=rest.FilterSelector(
@@ -1013,10 +1015,9 @@ class QdrantDocumentStore:
1013
1015
  ),
1014
1016
  wait=self.wait_result_from_api,
1015
1017
  )
1016
- except Exception as e:
1017
- logger.warning(
1018
- f"Error {e} when calling QdrantDocumentStore.delete_all_documents_async()",
1019
- )
1018
+ except Exception as e:
1019
+ msg = f"Error when calling QdrantDocumentStore.delete_all_documents_async(): {e}"
1020
+ raise QdrantStoreError(msg) from e
1020
1021
 
1021
1022
  def count_documents_by_filter(self, filters: dict[str, Any]) -> int:
1022
1023
  """
@@ -1026,6 +1027,7 @@ class QdrantDocumentStore:
1026
1027
  For filter syntax, see [Haystack metadata filtering](https://docs.haystack.deepset.ai/docs/metadata-filtering)
1027
1028
 
1028
1029
  :returns: The number of documents that match the filters.
1030
+ :raises QdrantStoreError: Counting documents matching the filters fails.
1029
1031
  """
1030
1032
  self._initialize_client()
1031
1033
  assert self._client is not None
@@ -1037,9 +1039,9 @@ class QdrantDocumentStore:
1037
1039
  count_filter=qdrant_filter,
1038
1040
  )
1039
1041
  return response.count
1040
- except (UnexpectedResponse, ValueError) as e:
1041
- logger.warning(f"Error {e} when calling QdrantDocumentStore.count_documents_by_filter()")
1042
- return 0
1042
+ except Exception as e:
1043
+ msg = f"Error when calling QdrantDocumentStore.count_documents_by_filter(): {e}"
1044
+ raise QdrantStoreError(msg) from e
1043
1045
 
1044
1046
  async def count_documents_by_filter_async(self, filters: dict[str, Any]) -> int:
1045
1047
  """
@@ -1050,6 +1052,7 @@ class QdrantDocumentStore:
1050
1052
 
1051
1053
  :returns:
1052
1054
  The number of documents that match the filters.
1055
+ :raises QdrantStoreError: Counting documents matching the filters fails.
1053
1056
  """
1054
1057
  await self._initialize_async_client()
1055
1058
  assert self._async_client is not None
@@ -1061,9 +1064,9 @@ class QdrantDocumentStore:
1061
1064
  count_filter=qdrant_filter,
1062
1065
  )
1063
1066
  return response.count
1064
- except (UnexpectedResponse, ValueError) as e:
1065
- logger.warning(f"Error {e} when calling QdrantDocumentStore.count_documents_by_filter_async()")
1066
- return 0
1067
+ except Exception as e:
1068
+ msg = f"Error when calling QdrantDocumentStore.count_documents_by_filter_async(): {e}"
1069
+ raise QdrantStoreError(msg) from e
1067
1070
 
1068
1071
  def get_metadata_fields_info(self) -> dict[str, dict[str, str]]:
1069
1072
  """
@@ -1078,6 +1081,7 @@ class QdrantDocumentStore:
1078
1081
  ```python
1079
1082
  {"category": {"type": "keyword"}, "priority": {"type": "long"}}
1080
1083
  ```
1084
+ :raises QdrantStoreError: Retrieving or processing metadata field information fails.
1081
1085
  """
1082
1086
  self._initialize_client()
1083
1087
  assert self._client is not None
@@ -1103,9 +1107,9 @@ class QdrantDocumentStore:
1103
1107
  break
1104
1108
 
1105
1109
  return fields_info
1106
- except (UnexpectedResponse, ValueError) as e:
1107
- logger.warning(f"Error {e} when calling QdrantDocumentStore.get_metadata_fields_info()")
1108
- return {}
1110
+ except Exception as e:
1111
+ msg = f"Error when calling QdrantDocumentStore.get_metadata_fields_info(): {e}"
1112
+ raise QdrantStoreError(msg) from e
1109
1113
 
1110
1114
  async def get_metadata_fields_info_async(self) -> dict[str, dict[str, str]]:
1111
1115
  """
@@ -1120,6 +1124,7 @@ class QdrantDocumentStore:
1120
1124
  ```python
1121
1125
  {"category": {"type": "keyword"}, "priority": {"type": "long"}}
1122
1126
  ```
1127
+ :raises QdrantStoreError: Retrieving or processing metadata field information fails.
1123
1128
  """
1124
1129
  await self._initialize_async_client()
1125
1130
  assert self._async_client is not None
@@ -1145,9 +1150,9 @@ class QdrantDocumentStore:
1145
1150
  break
1146
1151
 
1147
1152
  return fields_info
1148
- except (UnexpectedResponse, ValueError) as e:
1149
- logger.warning(f"Error {e} when calling QdrantDocumentStore.get_metadata_fields_info_async()")
1150
- return {}
1153
+ except Exception as e:
1154
+ msg = f"Error when calling QdrantDocumentStore.get_metadata_fields_info_async(): {e}"
1155
+ raise QdrantStoreError(msg) from e
1151
1156
 
1152
1157
  def get_metadata_field_min_max(self, metadata_field: str) -> dict[str, Any]:
1153
1158
  """
@@ -1158,6 +1163,7 @@ class QdrantDocumentStore:
1158
1163
  :returns: A dictionary with the keys "min" and "max", where each value is the minimum or maximum value of the
1159
1164
  metadata field across all documents. Returns ``{"min": None, "max": None}`` if no documents have
1160
1165
  the field.
1166
+ :raises QdrantStoreError: Retrieving or computing the metadata bounds fails.
1161
1167
  """
1162
1168
  self._initialize_client()
1163
1169
  assert self._client is not None
@@ -1183,8 +1189,8 @@ class QdrantDocumentStore:
1183
1189
 
1184
1190
  return {"min": min_value, "max": max_value}
1185
1191
  except Exception as e:
1186
- logger.warning(f"Error {e} when calling QdrantDocumentStore.get_metadata_field_min_max()")
1187
- return {}
1192
+ msg = f"Error when calling QdrantDocumentStore.get_metadata_field_min_max(): {e}"
1193
+ raise QdrantStoreError(msg) from e
1188
1194
 
1189
1195
  async def get_metadata_field_min_max_async(self, metadata_field: str) -> dict[str, Any]:
1190
1196
  """
@@ -1195,6 +1201,7 @@ class QdrantDocumentStore:
1195
1201
  :returns: A dictionary with the keys "min" and "max", where each value is the minimum or maximum value of the
1196
1202
  metadata field across all documents. Returns ``{"min": None, "max": None}`` if no documents have
1197
1203
  the field.
1204
+ :raises QdrantStoreError: Retrieving or computing the metadata bounds fails.
1198
1205
  """
1199
1206
  await self._initialize_async_client()
1200
1207
  assert self._async_client is not None
@@ -1220,8 +1227,8 @@ class QdrantDocumentStore:
1220
1227
 
1221
1228
  return {"min": min_value, "max": max_value}
1222
1229
  except Exception as e:
1223
- logger.warning(f"Error {e} when calling QdrantDocumentStore.get_metadata_field_min_max_async()")
1224
- return {}
1230
+ msg = f"Error when calling QdrantDocumentStore.get_metadata_field_min_max_async(): {e}"
1231
+ raise QdrantStoreError(msg) from e
1225
1232
 
1226
1233
  def count_unique_metadata_by_filter(self, filters: dict[str, Any], metadata_fields: list[str]) -> dict[str, int]:
1227
1234
  """
@@ -1233,6 +1240,7 @@ class QdrantDocumentStore:
1233
1240
 
1234
1241
  :returns: A dictionary mapping each metadata field name to the count of its unique values among the filtered
1235
1242
  documents.
1243
+ :raises QdrantStoreError: Retrieving or counting unique metadata values fails.
1236
1244
  """
1237
1245
  self._initialize_client()
1238
1246
  assert self._client is not None
@@ -1257,8 +1265,8 @@ class QdrantDocumentStore:
1257
1265
 
1258
1266
  return {field: len(unique_values_by_field[field]) for field in metadata_fields}
1259
1267
  except Exception as e:
1260
- logger.warning(f"Error {e} when calling QdrantDocumentStore.count_unique_metadata_by_filter()")
1261
- return dict.fromkeys(metadata_fields, 0)
1268
+ msg = f"Error when calling QdrantDocumentStore.count_unique_metadata_by_filter(): {e}"
1269
+ raise QdrantStoreError(msg) from e
1262
1270
 
1263
1271
  async def count_unique_metadata_by_filter_async(
1264
1272
  self, filters: dict[str, Any], metadata_fields: list[str]
@@ -1274,6 +1282,7 @@ class QdrantDocumentStore:
1274
1282
 
1275
1283
  :returns: A dictionary mapping each metadata field name to the count of its unique values among the filtered
1276
1284
  documents.
1285
+ :raises QdrantStoreError: Retrieving or counting unique metadata values fails.
1277
1286
  """
1278
1287
  await self._initialize_async_client()
1279
1288
  assert self._async_client is not None
@@ -1298,8 +1307,8 @@ class QdrantDocumentStore:
1298
1307
 
1299
1308
  return {field: len(unique_values_by_field[field]) for field in metadata_fields}
1300
1309
  except Exception as e:
1301
- logger.warning(f"Error {e} when calling QdrantDocumentStore.count_unique_metadata_by_filter_async()")
1302
- return dict.fromkeys(metadata_fields, 0)
1310
+ msg = f"Error when calling QdrantDocumentStore.count_unique_metadata_by_filter_async(): {e}"
1311
+ raise QdrantStoreError(msg) from e
1303
1312
 
1304
1313
  def get_metadata_field_unique_values(
1305
1314
  self,
@@ -1325,6 +1334,7 @@ class QdrantDocumentStore:
1325
1334
  For filter syntax, see [Haystack metadata filtering](https://docs.haystack.deepset.ai/docs/metadata-filtering)
1326
1335
 
1327
1336
  :returns: A tuple containing (list of unique values, total count of unique matching values).
1337
+ :raises QdrantStoreError: Retrieving or processing unique metadata values fails.
1328
1338
  """
1329
1339
  self._initialize_client()
1330
1340
  assert self._client is not None
@@ -1353,8 +1363,8 @@ class QdrantDocumentStore:
1353
1363
  total_count = len(unique_values)
1354
1364
  return unique_values[from_ : from_ + size], total_count
1355
1365
  except Exception as e:
1356
- logger.warning(f"Error {e} when calling QdrantDocumentStore.get_metadata_field_unique_values()")
1357
- return [], 0
1366
+ msg = f"Error when calling QdrantDocumentStore.get_metadata_field_unique_values(): {e}"
1367
+ raise QdrantStoreError(msg) from e
1358
1368
 
1359
1369
  async def get_metadata_field_unique_values_async(
1360
1370
  self,
@@ -1380,6 +1390,7 @@ class QdrantDocumentStore:
1380
1390
  For filter syntax, see [Haystack metadata filtering](https://docs.haystack.deepset.ai/docs/metadata-filtering)
1381
1391
 
1382
1392
  :returns: A tuple containing (list of unique values, total count of unique matching values).
1393
+ :raises QdrantStoreError: Retrieving or processing unique metadata values fails.
1383
1394
  """
1384
1395
  await self._initialize_async_client()
1385
1396
  assert self._async_client is not None
@@ -1408,8 +1419,8 @@ class QdrantDocumentStore:
1408
1419
  total_count = len(unique_values)
1409
1420
  return unique_values[from_ : from_ + size], total_count
1410
1421
  except Exception as e:
1411
- logger.warning(f"Error {e} when calling QdrantDocumentStore.get_metadata_field_unique_values_async()")
1412
- return [], 0
1422
+ msg = f"Error when calling QdrantDocumentStore.get_metadata_field_unique_values_async(): {e}"
1423
+ raise QdrantStoreError(msg) from e
1413
1424
 
1414
1425
  @classmethod
1415
1426
  def from_dict(cls, data: dict[str, Any]) -> "QdrantDocumentStore":
@@ -1746,9 +1757,10 @@ class QdrantDocumentStore:
1746
1757
  groups to return.
1747
1758
  :param return_embedding: Whether to return the embeddings of the retrieved documents.
1748
1759
  :param score_threshold: A minimal score threshold for the result.
1749
- Score of the returned result might be higher or smaller than the threshold
1750
- depending on the Distance function used.
1751
- E.g. for cosine similarity only higher scores will be returned.
1760
+ The threshold is applied to the fused Reciprocal Rank Fusion (RRF) scores, so only documents with a
1761
+ higher fused score are returned. RRF scores are not comparable to dense or sparse similarity scores,
1762
+ so thresholds tuned for those retrievers don't carry over. With `group_by`, groups with no surviving
1763
+ hits are dropped.
1752
1764
  :param group_by: Payload field to group by, must be a string or number field. If the field contains more than 1
1753
1765
  value, all values will be used for grouping. One point can be in multiple groups.
1754
1766
  :param group_size: Maximum amount of points to return per group. Default is 3.
@@ -2009,9 +2021,10 @@ class QdrantDocumentStore:
2009
2021
  groups to return.
2010
2022
  :param return_embedding: Whether to return the embeddings of the retrieved documents.
2011
2023
  :param score_threshold: A minimal score threshold for the result.
2012
- Score of the returned result might be higher or smaller than the threshold
2013
- depending on the Distance function used.
2014
- E.g. for cosine similarity only higher scores will be returned.
2024
+ The threshold is applied to the fused Reciprocal Rank Fusion (RRF) scores, so only documents with a
2025
+ higher fused score are returned. RRF scores are not comparable to dense or sparse similarity scores,
2026
+ so thresholds tuned for those retrievers don't carry over. With `group_by`, groups with no surviving
2027
+ hits are dropped.
2015
2028
  :param group_by: Payload field to group by, must be a string or number field. If the field contains more than 1
2016
2029
  value, all values will be used for grouping. One point can be in multiple groups.
2017
2030
  :param group_size: Maximum amount of points to return per group. Default is 3.
@@ -377,17 +377,19 @@ class TestQdrantDocumentStoreUnit:
377
377
  document_store._query_by_sparse(query_sparse_embedding=sparse_embedding)
378
378
 
379
379
  @pytest.mark.parametrize(
380
- ("method_name", "args", "expected"),
380
+ ("method_name", "args"),
381
381
  [
382
- ("count_documents", (), 0),
383
- ("count_documents_by_filter", ({},), 0),
384
- ("get_metadata_fields_info", (), {}),
385
- ("get_metadata_field_min_max", ("score",), {}),
386
- ("count_unique_metadata_by_filter", ({}, ["category"]), {"category": 0}),
387
- ("get_metadata_field_unique_values", ("category",), ([], 0)),
382
+ ("count_documents", ()),
383
+ ("delete_all_documents", (False,)),
384
+ ("delete_all_documents", (True,)),
385
+ ("count_documents_by_filter", ({},)),
386
+ ("get_metadata_fields_info", ()),
387
+ ("get_metadata_field_min_max", ("score",)),
388
+ ("count_unique_metadata_by_filter", ({}, ["category"])),
389
+ ("get_metadata_field_unique_values", ("category",)),
388
390
  ],
389
391
  )
390
- def test_metadata_methods_swallow_client_errors(self, method_name, args, expected):
392
+ def test_operations_raise_on_client_errors(self, method_name, args):
391
393
  document_store = QdrantDocumentStore(location=":memory:")
392
394
  document_store._initialize_client()
393
395
  err = ValueError("boom")
@@ -395,8 +397,11 @@ class TestQdrantDocumentStoreUnit:
395
397
  patch.object(document_store._client, "count", side_effect=err),
396
398
  patch.object(document_store._client, "scroll", side_effect=err),
397
399
  patch.object(document_store._client, "get_collection", side_effect=err),
400
+ patch.object(document_store._client, "delete", side_effect=err),
398
401
  ):
399
- assert getattr(document_store, method_name)(*args) == expected
402
+ with pytest.raises(QdrantStoreError) as exc_info:
403
+ getattr(document_store, method_name)(*args)
404
+ assert exc_info.value.__cause__ is err
400
405
 
401
406
  def test_close(self):
402
407
  document_store = QdrantDocumentStore(location=":memory:")
@@ -141,17 +141,19 @@ class TestQdrantDocumentStoreAsyncUnit:
141
141
  await document_store.delete_documents_async(["doc-1"])
142
142
 
143
143
  @pytest.mark.parametrize(
144
- ("method_name", "args", "expected"),
144
+ ("method_name", "args"),
145
145
  [
146
- ("count_documents_async", (), 0),
147
- ("count_documents_by_filter_async", ({},), 0),
148
- ("get_metadata_fields_info_async", (), {}),
149
- ("get_metadata_field_min_max_async", ("score",), {}),
150
- ("count_unique_metadata_by_filter_async", ({}, ["category"]), {"category": 0}),
151
- ("get_metadata_field_unique_values_async", ("category",), ([], 0)),
146
+ ("count_documents_async", ()),
147
+ ("delete_all_documents_async", (False,)),
148
+ ("delete_all_documents_async", (True,)),
149
+ ("count_documents_by_filter_async", ({},)),
150
+ ("get_metadata_fields_info_async", ()),
151
+ ("get_metadata_field_min_max_async", ("score",)),
152
+ ("count_unique_metadata_by_filter_async", ({}, ["category"])),
153
+ ("get_metadata_field_unique_values_async", ("category",)),
152
154
  ],
153
155
  )
154
- async def test_metadata_methods_async_absorb_client_errors(self, method_name, args, expected):
156
+ async def test_operations_async_raise_on_client_errors(self, method_name, args):
155
157
  document_store = QdrantDocumentStore(location=":memory:")
156
158
  await document_store._initialize_async_client()
157
159
  err = ValueError("boom")
@@ -159,8 +161,11 @@ class TestQdrantDocumentStoreAsyncUnit:
159
161
  patch.object(document_store._async_client, "count", side_effect=err),
160
162
  patch.object(document_store._async_client, "scroll", side_effect=err),
161
163
  patch.object(document_store._async_client, "get_collection", side_effect=err),
164
+ patch.object(document_store._async_client, "delete", side_effect=err),
162
165
  ):
163
- assert await getattr(document_store, method_name)(*args) == expected
166
+ with pytest.raises(QdrantStoreError) as exc_info:
167
+ await getattr(document_store, method_name)(*args)
168
+ assert exc_info.value.__cause__ is err
164
169
 
165
170
  async def test_close_async(self):
166
171
  document_store = QdrantDocumentStore(location=":memory:")
@@ -200,6 +200,37 @@ class TestQdrantRetriever:
200
200
  mock_store._query_by_embedding_async.assert_awaited_once()
201
201
  assert res["documents"][0].content == "doc"
202
202
 
203
+ def test_run_falsy_runtime_values_override_init(self):
204
+ mock_store = Mock(spec=QdrantDocumentStore)
205
+ mock_store._query_by_embedding.return_value = []
206
+
207
+ retriever = QdrantEmbeddingRetriever(
208
+ document_store=mock_store, scale_score=True, return_embedding=True, score_threshold=0.5
209
+ )
210
+ retriever.run(query_embedding=[0.5, 0.7], scale_score=False, return_embedding=False, score_threshold=0.0)
211
+
212
+ call_kwargs = mock_store._query_by_embedding.call_args.kwargs
213
+ assert call_kwargs["scale_score"] is False
214
+ assert call_kwargs["return_embedding"] is False
215
+ assert call_kwargs["score_threshold"] == 0.0
216
+
217
+ @pytest.mark.asyncio
218
+ async def test_run_async_falsy_runtime_values_override_init(self):
219
+ mock_store = Mock(spec=QdrantDocumentStore)
220
+ mock_store._query_by_embedding_async = AsyncMock(return_value=[])
221
+
222
+ retriever = QdrantEmbeddingRetriever(
223
+ document_store=mock_store, scale_score=True, return_embedding=True, score_threshold=0.5
224
+ )
225
+ await retriever.run_async(
226
+ query_embedding=[0.5, 0.7], scale_score=False, return_embedding=False, score_threshold=0.0
227
+ )
228
+
229
+ call_kwargs = mock_store._query_by_embedding_async.call_args.kwargs
230
+ assert call_kwargs["scale_score"] is False
231
+ assert call_kwargs["return_embedding"] is False
232
+ assert call_kwargs["score_threshold"] == 0.0
233
+
203
234
  def test_run_raises_when_merge_with_native_init_filter(self):
204
235
  document_store = QdrantDocumentStore(location=":memory:", index="test")
205
236
  retriever = QdrantEmbeddingRetriever(
@@ -229,6 +229,39 @@ class TestQdrantHybridRetriever:
229
229
  assert call_args[1]["rrf_k"] == 100
230
230
  assert call_args[1]["rrf_weights"] == [3.0, 1.0]
231
231
 
232
+ def test_run_falsy_runtime_values_override_init(self):
233
+ mock_store = Mock(spec=QdrantDocumentStore)
234
+ mock_store._query_hybrid.return_value = []
235
+
236
+ retriever = QdrantHybridRetriever(document_store=mock_store, return_embedding=True, score_threshold=0.5)
237
+ retriever.run(
238
+ query_embedding=[0.5, 0.7],
239
+ query_sparse_embedding=SparseEmbedding(indices=[0, 5], values=[0.1, 0.7]),
240
+ return_embedding=False,
241
+ score_threshold=0.0,
242
+ )
243
+
244
+ call_args = mock_store._query_hybrid.call_args
245
+ assert call_args[1]["return_embedding"] is False
246
+ assert call_args[1]["score_threshold"] == 0.0
247
+
248
+ @pytest.mark.asyncio
249
+ async def test_run_async_falsy_runtime_values_override_init(self):
250
+ mock_store = Mock(spec=QdrantDocumentStore)
251
+ mock_store._query_hybrid_async = AsyncMock(return_value=[])
252
+
253
+ retriever = QdrantHybridRetriever(document_store=mock_store, return_embedding=True, score_threshold=0.5)
254
+ await retriever.run_async(
255
+ query_embedding=[0.5, 0.7],
256
+ query_sparse_embedding=SparseEmbedding(indices=[0, 5], values=[0.1, 0.7]),
257
+ return_embedding=False,
258
+ score_threshold=0.0,
259
+ )
260
+
261
+ call_args = mock_store._query_hybrid_async.call_args
262
+ assert call_args[1]["return_embedding"] is False
263
+ assert call_args[1]["score_threshold"] == 0.0
264
+
232
265
  def test_run_with_group_by(self):
233
266
  mock_store = Mock(spec=QdrantDocumentStore)
234
267
  sparse_embedding = SparseEmbedding(indices=[0, 1, 2, 3], values=[0.1, 0.8, 0.05, 0.33])
@@ -199,6 +199,39 @@ class TestQdrantSparseEmbeddingRetriever:
199
199
  mock_store._query_by_sparse_async.assert_awaited_once()
200
200
  assert res["documents"][0].content == "doc"
201
201
 
202
+ def test_run_falsy_runtime_values_override_init(self):
203
+ mock_store = Mock(spec=QdrantDocumentStore)
204
+ mock_store._query_by_sparse.return_value = []
205
+ sparse = SparseEmbedding(indices=[0, 5], values=[0.1, 0.7])
206
+
207
+ retriever = QdrantSparseEmbeddingRetriever(
208
+ document_store=mock_store, scale_score=True, return_embedding=True, score_threshold=0.5
209
+ )
210
+ retriever.run(query_sparse_embedding=sparse, scale_score=False, return_embedding=False, score_threshold=0.0)
211
+
212
+ call_kwargs = mock_store._query_by_sparse.call_args.kwargs
213
+ assert call_kwargs["scale_score"] is False
214
+ assert call_kwargs["return_embedding"] is False
215
+ assert call_kwargs["score_threshold"] == 0.0
216
+
217
+ @pytest.mark.asyncio
218
+ async def test_run_async_falsy_runtime_values_override_init(self):
219
+ mock_store = Mock(spec=QdrantDocumentStore)
220
+ mock_store._query_by_sparse_async = AsyncMock(return_value=[])
221
+ sparse = SparseEmbedding(indices=[0, 5], values=[0.1, 0.7])
222
+
223
+ retriever = QdrantSparseEmbeddingRetriever(
224
+ document_store=mock_store, scale_score=True, return_embedding=True, score_threshold=0.5
225
+ )
226
+ await retriever.run_async(
227
+ query_sparse_embedding=sparse, scale_score=False, return_embedding=False, score_threshold=0.0
228
+ )
229
+
230
+ call_kwargs = mock_store._query_by_sparse_async.call_args.kwargs
231
+ assert call_kwargs["scale_score"] is False
232
+ assert call_kwargs["return_embedding"] is False
233
+ assert call_kwargs["score_threshold"] == 0.0
234
+
202
235
  def test_run_raises_when_merge_with_native_filter(self):
203
236
  document_store = QdrantDocumentStore(location=":memory:", index="test")
204
237
  retriever = QdrantSparseEmbeddingRetriever(