qdrant-haystack 9.5.0__tar.gz → 10.0.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-9.5.0 → qdrant_haystack-10.0.0}/CHANGELOG.md +15 -0
  2. {qdrant_haystack-9.5.0 → qdrant_haystack-10.0.0}/PKG-INFO +3 -4
  3. {qdrant_haystack-9.5.0 → qdrant_haystack-10.0.0}/pyproject.toml +3 -9
  4. {qdrant_haystack-9.5.0 → qdrant_haystack-10.0.0}/src/haystack_integrations/components/retrievers/qdrant/retriever.py +56 -56
  5. {qdrant_haystack-9.5.0 → qdrant_haystack-10.0.0}/src/haystack_integrations/document_stores/qdrant/converters.py +1 -2
  6. {qdrant_haystack-9.5.0 → qdrant_haystack-10.0.0}/src/haystack_integrations/document_stores/qdrant/document_store.py +66 -66
  7. {qdrant_haystack-9.5.0 → qdrant_haystack-10.0.0}/src/haystack_integrations/document_stores/qdrant/filters.py +11 -10
  8. {qdrant_haystack-9.5.0 → qdrant_haystack-10.0.0}/.gitignore +0 -0
  9. {qdrant_haystack-9.5.0 → qdrant_haystack-10.0.0}/LICENSE.txt +0 -0
  10. {qdrant_haystack-9.5.0 → qdrant_haystack-10.0.0}/README.md +0 -0
  11. {qdrant_haystack-9.5.0 → qdrant_haystack-10.0.0}/examples/embedding_retrieval.py +0 -0
  12. {qdrant_haystack-9.5.0 → qdrant_haystack-10.0.0}/pydoc/config_docusaurus.yml +0 -0
  13. {qdrant_haystack-9.5.0 → qdrant_haystack-10.0.0}/src/haystack_integrations/components/retrievers/py.typed +0 -0
  14. {qdrant_haystack-9.5.0 → qdrant_haystack-10.0.0}/src/haystack_integrations/components/retrievers/qdrant/__init__.py +0 -0
  15. {qdrant_haystack-9.5.0 → qdrant_haystack-10.0.0}/src/haystack_integrations/document_stores/py.typed +0 -0
  16. {qdrant_haystack-9.5.0 → qdrant_haystack-10.0.0}/src/haystack_integrations/document_stores/qdrant/__init__.py +0 -0
  17. {qdrant_haystack-9.5.0 → qdrant_haystack-10.0.0}/src/haystack_integrations/document_stores/qdrant/migrate_to_sparse.py +0 -0
  18. {qdrant_haystack-9.5.0 → qdrant_haystack-10.0.0}/tests/__init__.py +0 -0
  19. {qdrant_haystack-9.5.0 → qdrant_haystack-10.0.0}/tests/conftest.py +0 -0
  20. {qdrant_haystack-9.5.0 → qdrant_haystack-10.0.0}/tests/test_converters.py +0 -0
  21. {qdrant_haystack-9.5.0 → qdrant_haystack-10.0.0}/tests/test_dict_converters.py +0 -0
  22. {qdrant_haystack-9.5.0 → qdrant_haystack-10.0.0}/tests/test_document_store.py +0 -0
  23. {qdrant_haystack-9.5.0 → qdrant_haystack-10.0.0}/tests/test_document_store_async.py +0 -0
  24. {qdrant_haystack-9.5.0 → qdrant_haystack-10.0.0}/tests/test_embedding_retriever.py +0 -0
  25. {qdrant_haystack-9.5.0 → qdrant_haystack-10.0.0}/tests/test_filters.py +0 -0
  26. {qdrant_haystack-9.5.0 → qdrant_haystack-10.0.0}/tests/test_hybrid_retriever.py +0 -0
  27. {qdrant_haystack-9.5.0 → qdrant_haystack-10.0.0}/tests/test_sparse_embedding_retriever.py +0 -0
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## [integrations/qdrant-v9.5.0] - 2026-01-07
4
+
5
+ ### 🚀 Features
6
+
7
+ - Adding `delete_by_filter()` and `update_by_filter()` to `QdrantDocumentStore` (#2650)
8
+
9
+ ### 🐛 Bug Fixes
10
+
11
+ - Fix types for qdrant-client 1.16.1 (#2561)
12
+
13
+ ### 🧹 Chores
14
+
15
+ - Remove Readme API CI workflow and configs (#2573)
16
+
17
+
3
18
  ## [integrations/qdrant-v9.4.0] - 2025-11-18
4
19
 
5
20
  ### 🧹 Chores
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qdrant-haystack
3
- Version: 9.5.0
3
+ Version: 10.0.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
@@ -11,15 +11,14 @@ License-File: LICENSE.txt
11
11
  Classifier: Development Status :: 4 - Beta
12
12
  Classifier: License :: OSI Approved :: Apache Software License
13
13
  Classifier: Programming Language :: Python
14
- Classifier: Programming Language :: Python :: 3.9
15
14
  Classifier: Programming Language :: Python :: 3.10
16
15
  Classifier: Programming Language :: Python :: 3.11
17
16
  Classifier: Programming Language :: Python :: 3.12
18
17
  Classifier: Programming Language :: Python :: 3.13
19
18
  Classifier: Programming Language :: Python :: Implementation :: CPython
20
19
  Classifier: Programming Language :: Python :: Implementation :: PyPy
21
- Requires-Python: >=3.9
22
- Requires-Dist: haystack-ai>=2.11.0
20
+ Requires-Python: >=3.10
21
+ Requires-Dist: haystack-ai>=2.22.0
23
22
  Requires-Dist: qdrant-client>=1.12.0
24
23
  Description-Content-Type: text/markdown
25
24
 
@@ -7,7 +7,7 @@ name = "qdrant-haystack"
7
7
  dynamic = ["version"]
8
8
  description = 'An integration of Qdrant ANN vector database backend with Haystack'
9
9
  readme = "README.md"
10
- requires-python = ">=3.9"
10
+ requires-python = ">=3.10"
11
11
  license = "Apache-2.0"
12
12
  keywords = []
13
13
  authors = [
@@ -18,7 +18,6 @@ classifiers = [
18
18
  "License :: OSI Approved :: Apache Software License",
19
19
  "Development Status :: 4 - Beta",
20
20
  "Programming Language :: Python",
21
- "Programming Language :: Python :: 3.9",
22
21
  "Programming Language :: Python :: 3.10",
23
22
  "Programming Language :: Python :: 3.11",
24
23
  "Programming Language :: Python :: 3.12",
@@ -26,7 +25,7 @@ classifiers = [
26
25
  "Programming Language :: Python :: Implementation :: CPython",
27
26
  "Programming Language :: Python :: Implementation :: PyPy",
28
27
  ]
29
- dependencies = ["haystack-ai>=2.11.0", "qdrant-client>=1.12.0"]
28
+ dependencies = ["haystack-ai>=2.22.0", "qdrant-client>=1.12.0"]
30
29
 
31
30
  [project.urls]
32
31
  Source = "https://github.com/deepset-ai/haystack-core-integrations"
@@ -50,7 +49,7 @@ dependencies = ["haystack-pydoc-tools", "ruff"]
50
49
 
51
50
  [tool.hatch.envs.default.scripts]
52
51
  docs = ["pydoc-markdown pydoc/config_docusaurus.yml"]
53
- fmt = "ruff check --fix {args} && ruff format {args}"
52
+ fmt = "ruff check --fix {args}; ruff format {args}"
54
53
  fmt-check = "ruff check {args} && ruff format --check {args}"
55
54
 
56
55
  [tool.hatch.envs.test]
@@ -80,7 +79,6 @@ disallow_incomplete_defs = true
80
79
 
81
80
 
82
81
  [tool.ruff]
83
- target-version = "py39"
84
82
  line-length = 120
85
83
 
86
84
  [tool.ruff.lint]
@@ -132,10 +130,6 @@ ignore = [
132
130
  # Ignore assertions
133
131
  "S101",
134
132
  ]
135
- unfixable = [
136
- # Don't touch unused imports
137
- "F401",
138
- ]
139
133
 
140
134
  [tool.ruff.lint.flake8-tidy-imports]
141
135
  ban-relative-imports = "parents"
@@ -1,4 +1,4 @@
1
- from typing import Any, Optional, Union
1
+ from typing import Any
2
2
 
3
3
  from haystack import Document, component, default_from_dict, default_to_dict
4
4
  from haystack.dataclasses.sparse_embedding import SparseEmbedding
@@ -43,14 +43,14 @@ class QdrantEmbeddingRetriever:
43
43
  def __init__(
44
44
  self,
45
45
  document_store: QdrantDocumentStore,
46
- filters: Optional[Union[dict[str, Any], models.Filter]] = None,
46
+ filters: dict[str, Any] | models.Filter | None = None,
47
47
  top_k: int = 10,
48
48
  scale_score: bool = False,
49
49
  return_embedding: bool = False,
50
- filter_policy: Union[str, FilterPolicy] = FilterPolicy.REPLACE,
51
- score_threshold: Optional[float] = None,
52
- group_by: Optional[str] = None,
53
- group_size: Optional[int] = None,
50
+ filter_policy: str | FilterPolicy = FilterPolicy.REPLACE,
51
+ score_threshold: float | None = None,
52
+ group_by: str | None = None,
53
+ group_size: int | None = None,
54
54
  ) -> None:
55
55
  """
56
56
  Create a QdrantEmbeddingRetriever component.
@@ -134,13 +134,13 @@ class QdrantEmbeddingRetriever:
134
134
  def run(
135
135
  self,
136
136
  query_embedding: list[float],
137
- filters: Optional[Union[dict[str, Any], models.Filter]] = None,
138
- top_k: Optional[int] = None,
139
- scale_score: Optional[bool] = None,
140
- return_embedding: Optional[bool] = None,
141
- score_threshold: Optional[float] = None,
142
- group_by: Optional[str] = None,
143
- group_size: Optional[int] = None,
137
+ filters: dict[str, Any] | models.Filter | None = None,
138
+ top_k: int | None = None,
139
+ scale_score: bool | None = None,
140
+ return_embedding: bool | None = None,
141
+ score_threshold: float | None = None,
142
+ group_by: str | None = None,
143
+ group_size: int | None = None,
144
144
  ) -> dict[str, list[Document]]:
145
145
  """
146
146
  Run the Embedding Retriever on the given input data.
@@ -189,13 +189,13 @@ class QdrantEmbeddingRetriever:
189
189
  async def run_async(
190
190
  self,
191
191
  query_embedding: list[float],
192
- filters: Optional[Union[dict[str, Any], models.Filter]] = None,
193
- top_k: Optional[int] = None,
194
- scale_score: Optional[bool] = None,
195
- return_embedding: Optional[bool] = None,
196
- score_threshold: Optional[float] = None,
197
- group_by: Optional[str] = None,
198
- group_size: Optional[int] = None,
192
+ filters: dict[str, Any] | models.Filter | None = None,
193
+ top_k: int | None = None,
194
+ scale_score: bool | None = None,
195
+ return_embedding: bool | None = None,
196
+ score_threshold: float | None = None,
197
+ group_by: str | None = None,
198
+ group_size: int | None = None,
199
199
  ) -> dict[str, list[Document]]:
200
200
  """
201
201
  Asynchronously run the Embedding Retriever on the given input data.
@@ -271,14 +271,14 @@ class QdrantSparseEmbeddingRetriever:
271
271
  def __init__(
272
272
  self,
273
273
  document_store: QdrantDocumentStore,
274
- filters: Optional[Union[dict[str, Any], models.Filter]] = None,
274
+ filters: dict[str, Any] | models.Filter | None = None,
275
275
  top_k: int = 10,
276
276
  scale_score: bool = False,
277
277
  return_embedding: bool = False,
278
- filter_policy: Union[str, FilterPolicy] = FilterPolicy.REPLACE,
279
- score_threshold: Optional[float] = None,
280
- group_by: Optional[str] = None,
281
- group_size: Optional[int] = None,
278
+ filter_policy: str | FilterPolicy = FilterPolicy.REPLACE,
279
+ score_threshold: float | None = None,
280
+ group_by: str | None = None,
281
+ group_size: int | None = None,
282
282
  ) -> None:
283
283
  """
284
284
  Create a QdrantSparseEmbeddingRetriever component.
@@ -362,13 +362,13 @@ class QdrantSparseEmbeddingRetriever:
362
362
  def run(
363
363
  self,
364
364
  query_sparse_embedding: SparseEmbedding,
365
- filters: Optional[Union[dict[str, Any], models.Filter]] = None,
366
- top_k: Optional[int] = None,
367
- scale_score: Optional[bool] = None,
368
- return_embedding: Optional[bool] = None,
369
- score_threshold: Optional[float] = None,
370
- group_by: Optional[str] = None,
371
- group_size: Optional[int] = None,
365
+ filters: dict[str, Any] | models.Filter | None = None,
366
+ top_k: int | None = None,
367
+ scale_score: bool | None = None,
368
+ return_embedding: bool | None = None,
369
+ score_threshold: float | None = None,
370
+ group_by: str | None = None,
371
+ group_size: int | None = None,
372
372
  ) -> dict[str, list[Document]]:
373
373
  """
374
374
  Run the Sparse Embedding Retriever on the given input data.
@@ -422,13 +422,13 @@ class QdrantSparseEmbeddingRetriever:
422
422
  async def run_async(
423
423
  self,
424
424
  query_sparse_embedding: SparseEmbedding,
425
- filters: Optional[Union[dict[str, Any], models.Filter]] = None,
426
- top_k: Optional[int] = None,
427
- scale_score: Optional[bool] = None,
428
- return_embedding: Optional[bool] = None,
429
- score_threshold: Optional[float] = None,
430
- group_by: Optional[str] = None,
431
- group_size: Optional[int] = None,
425
+ filters: dict[str, Any] | models.Filter | None = None,
426
+ top_k: int | None = None,
427
+ scale_score: bool | None = None,
428
+ return_embedding: bool | None = None,
429
+ score_threshold: float | None = None,
430
+ group_by: str | None = None,
431
+ group_size: int | None = None,
432
432
  ) -> dict[str, list[Document]]:
433
433
  """
434
434
  Asynchronously run the Sparse Embedding Retriever on the given input data.
@@ -515,13 +515,13 @@ class QdrantHybridRetriever:
515
515
  def __init__(
516
516
  self,
517
517
  document_store: QdrantDocumentStore,
518
- filters: Optional[Union[dict[str, Any], models.Filter]] = None,
518
+ filters: dict[str, Any] | models.Filter | None = None,
519
519
  top_k: int = 10,
520
520
  return_embedding: bool = False,
521
- filter_policy: Union[str, FilterPolicy] = FilterPolicy.REPLACE,
522
- score_threshold: Optional[float] = None,
523
- group_by: Optional[str] = None,
524
- group_size: Optional[int] = None,
521
+ filter_policy: str | FilterPolicy = FilterPolicy.REPLACE,
522
+ score_threshold: float | None = None,
523
+ group_by: str | None = None,
524
+ group_size: int | None = None,
525
525
  ) -> None:
526
526
  """
527
527
  Create a QdrantHybridRetriever component.
@@ -600,12 +600,12 @@ class QdrantHybridRetriever:
600
600
  self,
601
601
  query_embedding: list[float],
602
602
  query_sparse_embedding: SparseEmbedding,
603
- filters: Optional[Union[dict[str, Any], models.Filter]] = None,
604
- top_k: Optional[int] = None,
605
- return_embedding: Optional[bool] = None,
606
- score_threshold: Optional[float] = None,
607
- group_by: Optional[str] = None,
608
- group_size: Optional[int] = None,
603
+ filters: dict[str, Any] | models.Filter | None = None,
604
+ top_k: int | None = None,
605
+ return_embedding: bool | None = None,
606
+ score_threshold: float | None = None,
607
+ group_by: str | None = None,
608
+ group_size: int | None = None,
609
609
  ) -> dict[str, list[Document]]:
610
610
  """
611
611
  Run the Sparse Embedding Retriever on the given input data.
@@ -660,12 +660,12 @@ class QdrantHybridRetriever:
660
660
  self,
661
661
  query_embedding: list[float],
662
662
  query_sparse_embedding: SparseEmbedding,
663
- filters: Optional[Union[dict[str, Any], models.Filter]] = None,
664
- top_k: Optional[int] = None,
665
- return_embedding: Optional[bool] = None,
666
- score_threshold: Optional[float] = None,
667
- group_by: Optional[str] = None,
668
- group_size: Optional[int] = None,
663
+ filters: dict[str, Any] | models.Filter | None = None,
664
+ top_k: int | None = None,
665
+ return_embedding: bool | None = None,
666
+ score_threshold: float | None = None,
667
+ group_by: str | None = None,
668
+ group_size: int | None = None,
669
669
  ) -> dict[str, list[Document]]:
670
670
  """
671
671
  Asynchronously run the Sparse Embedding Retriever on the given input data.
@@ -1,5 +1,4 @@
1
1
  import uuid
2
- from typing import Union
3
2
 
4
3
  from haystack import logging
5
4
  from haystack.dataclasses import Document
@@ -58,7 +57,7 @@ def convert_id(_id: str) -> str:
58
57
  return uuid.uuid5(UUID_NAMESPACE, _id).hex
59
58
 
60
59
 
61
- QdrantPoint = Union[rest.ScoredPoint, rest.Record]
60
+ QdrantPoint = rest.ScoredPoint | rest.Record
62
61
 
63
62
 
64
63
  def convert_qdrant_point_to_haystack_document(point: QdrantPoint, use_sparse_embeddings: bool) -> Document:
@@ -1,7 +1,7 @@
1
1
  import inspect
2
2
  from collections.abc import AsyncGenerator, Generator
3
3
  from itertools import islice
4
- from typing import Any, ClassVar, Optional, Union, cast
4
+ from typing import Any, ClassVar, cast
5
5
 
6
6
  import qdrant_client
7
7
  from haystack import default_from_dict, default_to_dict, logging
@@ -37,7 +37,7 @@ class QdrantStoreError(DocumentStoreError):
37
37
  pass
38
38
 
39
39
 
40
- FilterType = dict[str, Union[dict[str, Any], list[Any], str, int, float, bool]]
40
+ FilterType = dict[str, dict[str, Any] | list[Any] | str | int | float | bool]
41
41
 
42
42
 
43
43
  def get_batches_from_generator(iterable: list, n: int) -> Generator:
@@ -98,17 +98,17 @@ class QdrantDocumentStore:
98
98
 
99
99
  def __init__(
100
100
  self,
101
- location: Optional[str] = None,
102
- url: Optional[str] = None,
101
+ location: str | None = None,
102
+ url: str | None = None,
103
103
  port: int = 6333,
104
104
  grpc_port: int = 6334,
105
105
  prefer_grpc: bool = False,
106
- https: Optional[bool] = None,
107
- api_key: Optional[Secret] = None,
108
- prefix: Optional[str] = None,
109
- timeout: Optional[int] = None,
110
- host: Optional[str] = None,
111
- path: Optional[str] = None,
106
+ https: bool | None = None,
107
+ api_key: Secret | None = None,
108
+ prefix: str | None = None,
109
+ timeout: int | None = None,
110
+ host: str | None = None,
111
+ path: str | None = None,
112
112
  force_disable_check_same_thread: bool = False,
113
113
  index: str = "Document",
114
114
  embedding_dim: int = 768,
@@ -119,19 +119,19 @@ class QdrantDocumentStore:
119
119
  return_embedding: bool = False,
120
120
  progress_bar: bool = True,
121
121
  recreate_index: bool = False,
122
- shard_number: Optional[int] = None,
123
- replication_factor: Optional[int] = None,
124
- write_consistency_factor: Optional[int] = None,
125
- on_disk_payload: Optional[bool] = None,
126
- hnsw_config: Optional[dict] = None,
127
- optimizers_config: Optional[dict] = None,
128
- wal_config: Optional[dict] = None,
129
- quantization_config: Optional[dict] = None,
122
+ shard_number: int | None = None,
123
+ replication_factor: int | None = None,
124
+ write_consistency_factor: int | None = None,
125
+ on_disk_payload: bool | None = None,
126
+ hnsw_config: dict | None = None,
127
+ optimizers_config: dict | None = None,
128
+ wal_config: dict | None = None,
129
+ quantization_config: dict | None = None,
130
130
  wait_result_from_api: bool = True,
131
- metadata: Optional[dict] = None,
131
+ metadata: dict | None = None,
132
132
  write_batch_size: int = 100,
133
133
  scroll_size: int = 10_000,
134
- payload_fields_to_index: Optional[list[dict]] = None,
134
+ payload_fields_to_index: list[dict] | None = None,
135
135
  ) -> None:
136
136
  """
137
137
  Initializes a QdrantDocumentStore.
@@ -220,8 +220,8 @@ class QdrantDocumentStore:
220
220
  List of payload fields to index.
221
221
  """
222
222
 
223
- self._client: Optional[qdrant_client.QdrantClient] = None
224
- self._async_client: Optional[qdrant_client.AsyncQdrantClient] = None
223
+ self._client: qdrant_client.QdrantClient | None = None
224
+ self._async_client: qdrant_client.AsyncQdrantClient | None = None
225
225
 
226
226
  # Store the Qdrant client specific attributes
227
227
  self.location = location
@@ -334,7 +334,7 @@ class QdrantDocumentStore:
334
334
 
335
335
  def filter_documents(
336
336
  self,
337
- filters: Optional[Union[dict[str, Any], rest.Filter]] = None,
337
+ filters: dict[str, Any] | rest.Filter | None = None,
338
338
  ) -> list[Document]:
339
339
  """
340
340
  Returns the documents that match the provided filters.
@@ -357,7 +357,7 @@ class QdrantDocumentStore:
357
357
 
358
358
  async def filter_documents_async(
359
359
  self,
360
- filters: Optional[Union[dict[str, Any], rest.Filter]] = None,
360
+ filters: dict[str, Any] | rest.Filter | None = None,
361
361
  ) -> list[Document]:
362
362
  """
363
363
  Asynchronously returns the documents that match the provided filters.
@@ -878,7 +878,7 @@ class QdrantDocumentStore:
878
878
 
879
879
  def _get_documents_generator(
880
880
  self,
881
- filters: Optional[Union[dict[str, Any], rest.Filter]] = None,
881
+ filters: dict[str, Any] | rest.Filter | None = None,
882
882
  ) -> Generator[Document, None, None]:
883
883
  """
884
884
  Returns a generator that yields documents from Qdrant based on the provided filters.
@@ -918,7 +918,7 @@ class QdrantDocumentStore:
918
918
 
919
919
  async def _get_documents_generator_async(
920
920
  self,
921
- filters: Optional[Union[dict[str, Any], rest.Filter]] = None,
921
+ filters: dict[str, Any] | rest.Filter | None = None,
922
922
  ) -> AsyncGenerator[Document, None]:
923
923
  """
924
924
  Returns an asynchronous generator that yields documents from Qdrant based on the provided filters.
@@ -1021,13 +1021,13 @@ class QdrantDocumentStore:
1021
1021
  def _query_by_sparse(
1022
1022
  self,
1023
1023
  query_sparse_embedding: SparseEmbedding,
1024
- filters: Optional[Union[dict[str, Any], rest.Filter]] = None,
1024
+ filters: dict[str, Any] | rest.Filter | None = None,
1025
1025
  top_k: int = 10,
1026
1026
  scale_score: bool = False,
1027
1027
  return_embedding: bool = False,
1028
- score_threshold: Optional[float] = None,
1029
- group_by: Optional[str] = None,
1030
- group_size: Optional[int] = None,
1028
+ score_threshold: float | None = None,
1029
+ group_by: str | None = None,
1030
+ group_size: int | None = None,
1031
1031
  ) -> list[Document]:
1032
1032
  """
1033
1033
  Queries Qdrant using a sparse embedding and returns the most relevant documents.
@@ -1098,13 +1098,13 @@ class QdrantDocumentStore:
1098
1098
  def _query_by_embedding(
1099
1099
  self,
1100
1100
  query_embedding: list[float],
1101
- filters: Optional[Union[dict[str, Any], rest.Filter]] = None,
1101
+ filters: dict[str, Any] | rest.Filter | None = None,
1102
1102
  top_k: int = 10,
1103
1103
  scale_score: bool = False,
1104
1104
  return_embedding: bool = False,
1105
- score_threshold: Optional[float] = None,
1106
- group_by: Optional[str] = None,
1107
- group_size: Optional[int] = None,
1105
+ score_threshold: float | None = None,
1106
+ group_by: str | None = None,
1107
+ group_size: int | None = None,
1108
1108
  ) -> list[Document]:
1109
1109
  """
1110
1110
  Queries Qdrant using a dense embedding and returns the most relevant documents.
@@ -1159,12 +1159,12 @@ class QdrantDocumentStore:
1159
1159
  self,
1160
1160
  query_embedding: list[float],
1161
1161
  query_sparse_embedding: SparseEmbedding,
1162
- filters: Optional[Union[dict[str, Any], rest.Filter]] = None,
1162
+ filters: dict[str, Any] | rest.Filter | None = None,
1163
1163
  top_k: int = 10,
1164
1164
  return_embedding: bool = False,
1165
- score_threshold: Optional[float] = None,
1166
- group_by: Optional[str] = None,
1167
- group_size: Optional[int] = None,
1165
+ score_threshold: float | None = None,
1166
+ group_by: str | None = None,
1167
+ group_size: int | None = None,
1168
1168
  ) -> list[Document]:
1169
1169
  """
1170
1170
  Retrieves documents based on dense and sparse embeddings and fuses the results using Reciprocal Rank Fusion.
@@ -1271,13 +1271,13 @@ class QdrantDocumentStore:
1271
1271
  async def _query_by_sparse_async(
1272
1272
  self,
1273
1273
  query_sparse_embedding: SparseEmbedding,
1274
- filters: Optional[Union[dict[str, Any], rest.Filter]] = None,
1274
+ filters: dict[str, Any] | rest.Filter | None = None,
1275
1275
  top_k: int = 10,
1276
1276
  scale_score: bool = False,
1277
1277
  return_embedding: bool = False,
1278
- score_threshold: Optional[float] = None,
1279
- group_by: Optional[str] = None,
1280
- group_size: Optional[int] = None,
1278
+ score_threshold: float | None = None,
1279
+ group_by: str | None = None,
1280
+ group_size: int | None = None,
1281
1281
  ) -> list[Document]:
1282
1282
  """
1283
1283
  Asynchronously queries Qdrant using a sparse embedding and returns the most relevant documents.
@@ -1351,13 +1351,13 @@ class QdrantDocumentStore:
1351
1351
  async def _query_by_embedding_async(
1352
1352
  self,
1353
1353
  query_embedding: list[float],
1354
- filters: Optional[Union[dict[str, Any], rest.Filter]] = None,
1354
+ filters: dict[str, Any] | rest.Filter | None = None,
1355
1355
  top_k: int = 10,
1356
1356
  scale_score: bool = False,
1357
1357
  return_embedding: bool = False,
1358
- score_threshold: Optional[float] = None,
1359
- group_by: Optional[str] = None,
1360
- group_size: Optional[int] = None,
1358
+ score_threshold: float | None = None,
1359
+ group_by: str | None = None,
1360
+ group_size: int | None = None,
1361
1361
  ) -> list[Document]:
1362
1362
  """
1363
1363
  Asynchronously queries Qdrant using a dense embedding and returns the most relevant documents.
@@ -1413,12 +1413,12 @@ class QdrantDocumentStore:
1413
1413
  self,
1414
1414
  query_embedding: list[float],
1415
1415
  query_sparse_embedding: SparseEmbedding,
1416
- filters: Optional[Union[dict[str, Any], rest.Filter]] = None,
1416
+ filters: dict[str, Any] | rest.Filter | None = None,
1417
1417
  top_k: int = 10,
1418
1418
  return_embedding: bool = False,
1419
- score_threshold: Optional[float] = None,
1420
- group_by: Optional[str] = None,
1421
- group_size: Optional[int] = None,
1419
+ score_threshold: float | None = None,
1420
+ group_by: str | None = None,
1421
+ group_size: int | None = None,
1422
1422
  ) -> list[Document]:
1423
1423
  """
1424
1424
  Asynchronously retrieves documents based on dense and sparse embeddings and fuses
@@ -1543,7 +1543,7 @@ class QdrantDocumentStore:
1543
1543
  )
1544
1544
  raise QdrantStoreError(msg) from ke
1545
1545
 
1546
- def _create_payload_index(self, collection_name: str, payload_fields_to_index: Optional[list[dict]] = None) -> None:
1546
+ def _create_payload_index(self, collection_name: str, payload_fields_to_index: list[dict] | None = None) -> None:
1547
1547
  """
1548
1548
  Create payload index for the collection if payload_fields_to_index is provided.
1549
1549
 
@@ -1562,7 +1562,7 @@ class QdrantDocumentStore:
1562
1562
  )
1563
1563
 
1564
1564
  async def _create_payload_index_async(
1565
- self, collection_name: str, payload_fields_to_index: Optional[list[dict]] = None
1565
+ self, collection_name: str, payload_fields_to_index: list[dict] | None = None
1566
1566
  ) -> None:
1567
1567
  """
1568
1568
  Asynchronously create payload index for the collection if payload_fields_to_index is provided.
@@ -1590,7 +1590,7 @@ class QdrantDocumentStore:
1590
1590
  use_sparse_embeddings: bool,
1591
1591
  sparse_idf: bool,
1592
1592
  on_disk: bool = False,
1593
- payload_fields_to_index: Optional[list[dict]] = None,
1593
+ payload_fields_to_index: list[dict] | None = None,
1594
1594
  ) -> None:
1595
1595
  """
1596
1596
  Sets up the Qdrant collection with the specified parameters.
@@ -1647,7 +1647,7 @@ class QdrantDocumentStore:
1647
1647
  use_sparse_embeddings: bool,
1648
1648
  sparse_idf: bool,
1649
1649
  on_disk: bool = False,
1650
- payload_fields_to_index: Optional[list[dict]] = None,
1650
+ payload_fields_to_index: list[dict] | None = None,
1651
1651
  ) -> None:
1652
1652
  """
1653
1653
  Asynchronously sets up the Qdrant collection with the specified parameters.
@@ -1700,8 +1700,8 @@ class QdrantDocumentStore:
1700
1700
  collection_name: str,
1701
1701
  distance: rest.Distance,
1702
1702
  embedding_dim: int,
1703
- on_disk: Optional[bool] = None,
1704
- use_sparse_embeddings: Optional[bool] = None,
1703
+ on_disk: bool | None = None,
1704
+ use_sparse_embeddings: bool | None = None,
1705
1705
  sparse_idf: bool = False,
1706
1706
  ) -> None:
1707
1707
  """
@@ -1743,8 +1743,8 @@ class QdrantDocumentStore:
1743
1743
  collection_name: str,
1744
1744
  distance: rest.Distance,
1745
1745
  embedding_dim: int,
1746
- on_disk: Optional[bool] = None,
1747
- use_sparse_embeddings: Optional[bool] = None,
1746
+ on_disk: bool | None = None,
1747
+ use_sparse_embeddings: bool | None = None,
1748
1748
  sparse_idf: bool = False,
1749
1749
  ) -> None:
1750
1750
  """
@@ -1784,7 +1784,7 @@ class QdrantDocumentStore:
1784
1784
  def _handle_duplicate_documents(
1785
1785
  self,
1786
1786
  documents: list[Document],
1787
- policy: Optional[DuplicatePolicy] = None,
1787
+ policy: DuplicatePolicy | None = None,
1788
1788
  ) -> list[Document]:
1789
1789
  """
1790
1790
  Checks whether any of the passed documents is already existing in the chosen index and returns a list of
@@ -1811,7 +1811,7 @@ class QdrantDocumentStore:
1811
1811
  async def _handle_duplicate_documents_async(
1812
1812
  self,
1813
1813
  documents: list[Document],
1814
- policy: Optional[DuplicatePolicy] = None,
1814
+ policy: DuplicatePolicy | None = None,
1815
1815
  ) -> list[Document]:
1816
1816
  """
1817
1817
  Asynchronously checks whether any of the passed documents is already existing
@@ -1900,10 +1900,10 @@ class QdrantDocumentStore:
1900
1900
  self,
1901
1901
  embedding_dim: int,
1902
1902
  distance: rest.Distance,
1903
- on_disk: Optional[bool] = None,
1904
- use_sparse_embeddings: Optional[bool] = None,
1903
+ on_disk: bool | None = None,
1904
+ use_sparse_embeddings: bool | None = None,
1905
1905
  sparse_idf: bool = False,
1906
- ) -> tuple[Union[dict[str, rest.VectorParams], rest.VectorParams], Optional[dict[str, rest.SparseVectorParams]]]:
1906
+ ) -> tuple[dict[str, rest.VectorParams] | rest.VectorParams, dict[str, rest.SparseVectorParams] | None]:
1907
1907
  """
1908
1908
  Prepares the configuration for creating or recreating a Qdrant collection.
1909
1909
 
@@ -1916,9 +1916,9 @@ class QdrantDocumentStore:
1916
1916
 
1917
1917
  # dense vectors configuration
1918
1918
  base_vectors_config = rest.VectorParams(size=embedding_dim, on_disk=on_disk, distance=distance)
1919
- vectors_config: Union[rest.VectorParams, dict[str, rest.VectorParams]] = base_vectors_config
1919
+ vectors_config: rest.VectorParams | dict[str, rest.VectorParams] = base_vectors_config
1920
1920
 
1921
- sparse_vectors_config: Optional[dict[str, rest.SparseVectorParams]] = None
1921
+ sparse_vectors_config: dict[str, rest.SparseVectorParams] | None = None
1922
1922
 
1923
1923
  if use_sparse_embeddings:
1924
1924
  # in this case, we need to define named vectors
@@ -1936,7 +1936,7 @@ class QdrantDocumentStore:
1936
1936
  return vectors_config, sparse_vectors_config
1937
1937
 
1938
1938
  @staticmethod
1939
- def _validate_filters(filters: Optional[Union[dict[str, Any], rest.Filter]] = None) -> None:
1939
+ def _validate_filters(filters: dict[str, Any] | rest.Filter | None = None) -> None:
1940
1940
  """
1941
1941
  Validates the filters provided for querying.
1942
1942
 
@@ -1,13 +1,14 @@
1
+ from collections.abc import Callable
1
2
  from datetime import datetime
2
- from typing import Any, Callable, Optional, Union
3
+ from typing import Any
3
4
 
4
5
  from haystack.utils.filters import COMPARISON_OPERATORS, LOGICAL_OPERATORS, FilterError
5
6
  from qdrant_client.http import models
6
7
 
7
8
 
8
9
  def convert_filters_to_qdrant(
9
- filter_term: Optional[Union[list[dict[str, Any]], dict[str, Any], models.Filter]] = None,
10
- ) -> Optional[models.Filter]:
10
+ filter_term: list[dict[str, Any]] | dict[str, Any] | models.Filter | None = None,
11
+ ) -> models.Filter | None:
11
12
  """Converts Haystack filters to the format used by Qdrant.
12
13
 
13
14
  :param filter_term: the haystack filter to be converted to qdrant.
@@ -52,7 +53,7 @@ def _process_filter_items(filter_items: list[dict[str, Any]]) -> list[models.Con
52
53
  return all_conditions
53
54
 
54
55
 
55
- def _process_logical_operator(item: dict[str, Any]) -> Optional[models.Condition]:
56
+ def _process_logical_operator(item: dict[str, Any]) -> models.Condition | None:
56
57
  """Process a logical operator (AND, OR, NOT) and return the corresponding condition."""
57
58
  operator = item["operator"]
58
59
  conditions = item.get("conditions")
@@ -78,7 +79,7 @@ def _process_logical_operator(item: dict[str, Any]) -> Optional[models.Condition
78
79
  return None
79
80
 
80
81
 
81
- def _process_comparison_operator(item: dict[str, Any]) -> Optional[models.Condition]:
82
+ def _process_comparison_operator(item: dict[str, Any]) -> models.Condition | None:
82
83
  """Process a comparison operator and return the corresponding condition."""
83
84
  operator = item["operator"]
84
85
  field = item.get("field")
@@ -91,7 +92,7 @@ def _process_comparison_operator(item: dict[str, Any]) -> Optional[models.Condit
91
92
  return _build_comparison_condition(operator, field, value)
92
93
 
93
94
 
94
- def _build_final_filter(conditions: list[models.Condition]) -> Optional[models.Filter]:
95
+ def _build_final_filter(conditions: list[models.Condition]) -> models.Filter | None:
95
96
  """Build the final filter from a list of conditions."""
96
97
  if not conditions:
97
98
  return None
@@ -178,7 +179,7 @@ def _build_nin_condition(key: str, value: list[models.ValueVariants]) -> models.
178
179
  )
179
180
 
180
181
 
181
- def _build_lt_condition(key: str, value: Union[str, float, int]) -> models.Condition:
182
+ def _build_lt_condition(key: str, value: str | float | int) -> models.Condition:
182
183
  if isinstance(value, str) and is_datetime_string(value):
183
184
  dt_value = datetime.fromisoformat(value)
184
185
  return models.FieldCondition(key=key, range=models.DatetimeRange(lt=dt_value))
@@ -190,7 +191,7 @@ def _build_lt_condition(key: str, value: Union[str, float, int]) -> models.Condi
190
191
  raise FilterError(msg)
191
192
 
192
193
 
193
- def _build_lte_condition(key: str, value: Union[str, float, int]) -> models.Condition:
194
+ def _build_lte_condition(key: str, value: str | float | int) -> models.Condition:
194
195
  if isinstance(value, str) and is_datetime_string(value):
195
196
  dt_value = datetime.fromisoformat(value)
196
197
  return models.FieldCondition(key=key, range=models.DatetimeRange(lte=dt_value))
@@ -202,7 +203,7 @@ def _build_lte_condition(key: str, value: Union[str, float, int]) -> models.Cond
202
203
  raise FilterError(msg)
203
204
 
204
205
 
205
- def _build_gt_condition(key: str, value: Union[str, float, int]) -> models.Condition:
206
+ def _build_gt_condition(key: str, value: str | float | int) -> models.Condition:
206
207
  if isinstance(value, str) and is_datetime_string(value):
207
208
  dt_value = datetime.fromisoformat(value)
208
209
  return models.FieldCondition(key=key, range=models.DatetimeRange(gt=dt_value))
@@ -214,7 +215,7 @@ def _build_gt_condition(key: str, value: Union[str, float, int]) -> models.Condi
214
215
  raise FilterError(msg)
215
216
 
216
217
 
217
- def _build_gte_condition(key: str, value: Union[str, float, int]) -> models.Condition:
218
+ def _build_gte_condition(key: str, value: str | float | int) -> models.Condition:
218
219
  if isinstance(value, str) and is_datetime_string(value):
219
220
  dt_value = datetime.fromisoformat(value)
220
221
  return models.FieldCondition(key=key, range=models.DatetimeRange(gte=dt_value))