qdrant-haystack 10.3.1__tar.gz → 10.4.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.3.1 → qdrant_haystack-10.4.0}/CHANGELOG.md +23 -0
  2. {qdrant_haystack-10.3.1 → qdrant_haystack-10.4.0}/PKG-INFO +1 -1
  3. {qdrant_haystack-10.3.1 → qdrant_haystack-10.4.0}/pyproject.toml +1 -0
  4. {qdrant_haystack-10.3.1 → qdrant_haystack-10.4.0}/src/haystack_integrations/components/retrievers/qdrant/retriever.py +36 -0
  5. {qdrant_haystack-10.3.1 → qdrant_haystack-10.4.0}/src/haystack_integrations/document_stores/qdrant/document_store.py +19 -0
  6. {qdrant_haystack-10.3.1 → qdrant_haystack-10.4.0}/tests/test_document_store.py +33 -0
  7. {qdrant_haystack-10.3.1 → qdrant_haystack-10.4.0}/tests/test_document_store_async.py +34 -1
  8. {qdrant_haystack-10.3.1 → qdrant_haystack-10.4.0}/tests/test_embedding_retriever.py +20 -0
  9. {qdrant_haystack-10.3.1 → qdrant_haystack-10.4.0}/tests/test_hybrid_retriever.py +21 -1
  10. {qdrant_haystack-10.3.1 → qdrant_haystack-10.4.0}/tests/test_sparse_embedding_retriever.py +20 -0
  11. {qdrant_haystack-10.3.1 → qdrant_haystack-10.4.0}/.gitignore +0 -0
  12. {qdrant_haystack-10.3.1 → qdrant_haystack-10.4.0}/LICENSE.txt +0 -0
  13. {qdrant_haystack-10.3.1 → qdrant_haystack-10.4.0}/README.md +0 -0
  14. {qdrant_haystack-10.3.1 → qdrant_haystack-10.4.0}/examples/embedding_retrieval.py +0 -0
  15. {qdrant_haystack-10.3.1 → qdrant_haystack-10.4.0}/pydoc/config_docusaurus.yml +0 -0
  16. {qdrant_haystack-10.3.1 → qdrant_haystack-10.4.0}/src/haystack_integrations/components/retrievers/py.typed +0 -0
  17. {qdrant_haystack-10.3.1 → qdrant_haystack-10.4.0}/src/haystack_integrations/components/retrievers/qdrant/__init__.py +0 -0
  18. {qdrant_haystack-10.3.1 → qdrant_haystack-10.4.0}/src/haystack_integrations/document_stores/py.typed +0 -0
  19. {qdrant_haystack-10.3.1 → qdrant_haystack-10.4.0}/src/haystack_integrations/document_stores/qdrant/__init__.py +0 -0
  20. {qdrant_haystack-10.3.1 → qdrant_haystack-10.4.0}/src/haystack_integrations/document_stores/qdrant/converters.py +0 -0
  21. {qdrant_haystack-10.3.1 → qdrant_haystack-10.4.0}/src/haystack_integrations/document_stores/qdrant/filters.py +0 -0
  22. {qdrant_haystack-10.3.1 → qdrant_haystack-10.4.0}/src/haystack_integrations/document_stores/qdrant/migrate_to_sparse.py +0 -0
  23. {qdrant_haystack-10.3.1 → qdrant_haystack-10.4.0}/tests/__init__.py +0 -0
  24. {qdrant_haystack-10.3.1 → qdrant_haystack-10.4.0}/tests/conftest.py +0 -0
  25. {qdrant_haystack-10.3.1 → qdrant_haystack-10.4.0}/tests/test_converters.py +0 -0
  26. {qdrant_haystack-10.3.1 → qdrant_haystack-10.4.0}/tests/test_dict_converters.py +0 -0
  27. {qdrant_haystack-10.3.1 → qdrant_haystack-10.4.0}/tests/test_filters.py +0 -0
@@ -1,5 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## [integrations/qdrant-v10.3.1] - 2026-07-06
4
+
5
+ ### 🐛 Bug Fixes
6
+
7
+ - Replace in-place dataclass mutations in document stores (#3114)
8
+
9
+ ### 📚 Documentation
10
+
11
+ - Replace old haystack core imports with haystack_integrations paths (#3545)
12
+
13
+ ### 🧪 Testing
14
+
15
+ - Track test coverage for all integrations (#3065)
16
+ - Mark some tests as integration in Faiss and Qdrant (#3076)
17
+ - Qdrant - add unit tests (#3183)
18
+ - Qdrant - add more unit tests (#3221)
19
+ - *(qdrant)* Use async DocumentStore mixin tests (#3093)
20
+
21
+ ### 🧹 Chores
22
+
23
+ - Enforce ruff docstring rules in integrations 31-40 (openrouter, opensearch, optimum, paddleocr, pgvector, pinecone, pyversity, qdrant, ragas, snowflake) (#3011)
24
+
25
+
3
26
  ## [integrations/qdrant-v10.3.0] - 2026-03-23
4
27
 
5
28
  ### 📚 Documentation
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qdrant-haystack
3
- Version: 10.3.1
3
+ Version: 10.4.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
@@ -143,6 +143,7 @@ ignore = [
143
143
  "PLR0912",
144
144
  "PLR0913",
145
145
  "PLR0915",
146
+ "PLR0917",
146
147
  # Ignore assertions
147
148
  "S101",
148
149
  ]
@@ -130,6 +130,18 @@ class QdrantEmbeddingRetriever:
130
130
  data["init_parameters"]["filter_policy"] = FilterPolicy.from_str(filter_policy)
131
131
  return default_from_dict(cls, data)
132
132
 
133
+ def close(self) -> None:
134
+ """
135
+ Release the synchronous resources of the underlying Document Store.
136
+ """
137
+ self._document_store.close()
138
+
139
+ async def close_async(self) -> None:
140
+ """
141
+ Release the asynchronous resources of the underlying Document Store.
142
+ """
143
+ await self._document_store.close_async()
144
+
133
145
  @component.output_types(documents=list[Document])
134
146
  def run(
135
147
  self,
@@ -358,6 +370,18 @@ class QdrantSparseEmbeddingRetriever:
358
370
  data["init_parameters"]["filter_policy"] = FilterPolicy.from_str(filter_policy)
359
371
  return default_from_dict(cls, data)
360
372
 
373
+ def close(self) -> None:
374
+ """
375
+ Release the synchronous resources of the underlying Document Store.
376
+ """
377
+ self._document_store.close()
378
+
379
+ async def close_async(self) -> None:
380
+ """
381
+ Release the asynchronous resources of the underlying Document Store.
382
+ """
383
+ await self._document_store.close_async()
384
+
361
385
  @component.output_types(documents=list[Document])
362
386
  def run(
363
387
  self,
@@ -596,6 +620,18 @@ class QdrantHybridRetriever:
596
620
  data["init_parameters"]["filter_policy"] = FilterPolicy.from_str(filter_policy)
597
621
  return default_from_dict(cls, data)
598
622
 
623
+ def close(self) -> None:
624
+ """
625
+ Release the synchronous resources of the underlying Document Store.
626
+ """
627
+ self._document_store.close()
628
+
629
+ async def close_async(self) -> None:
630
+ """
631
+ Release the asynchronous resources of the underlying Document Store.
632
+ """
633
+ await self._document_store.close_async()
634
+
599
635
  @component.output_types(documents=list[Document])
600
636
  def run(
601
637
  self,
@@ -1,5 +1,6 @@
1
1
  import inspect
2
2
  from collections.abc import AsyncGenerator, Generator
3
+ from contextlib import suppress
3
4
  from dataclasses import replace
4
5
  from itertools import islice
5
6
  from typing import Any, ClassVar, cast
@@ -301,6 +302,24 @@ class QdrantDocumentStore:
301
302
  self.payload_fields_to_index,
302
303
  )
303
304
 
305
+ def close(self) -> None:
306
+ """
307
+ Release the associated synchronous resources.
308
+ """
309
+ if self._client is not None:
310
+ with suppress(Exception):
311
+ self._client.close()
312
+ self._client = None
313
+
314
+ async def close_async(self) -> None:
315
+ """
316
+ Release the associated asynchronous resources.
317
+ """
318
+ if self._async_client is not None:
319
+ with suppress(Exception):
320
+ await self._async_client.close()
321
+ self._async_client = None
322
+
304
323
  def count_documents(self) -> int:
305
324
  """
306
325
  Returns the number of documents present in the Document Store.
@@ -370,6 +370,29 @@ class TestQdrantDocumentStoreUnit:
370
370
  ):
371
371
  assert getattr(document_store, method_name)(*args) == expected
372
372
 
373
+ def test_close(self):
374
+ document_store = QdrantDocumentStore(location=":memory:")
375
+ mock_client = MagicMock()
376
+ document_store._client = mock_client
377
+
378
+ document_store.close()
379
+
380
+ mock_client.close.assert_called_once()
381
+ assert document_store._client is None
382
+
383
+ document_store.close()
384
+ mock_client.close.assert_called_once()
385
+
386
+ def test_close_is_exception_safe(self):
387
+ document_store = QdrantDocumentStore(location=":memory:")
388
+ mock_client = MagicMock()
389
+ mock_client.close.side_effect = RuntimeError("boom")
390
+ document_store._client = mock_client
391
+
392
+ document_store.close()
393
+
394
+ assert document_store._client is None
395
+
373
396
 
374
397
  @pytest.mark.integration
375
398
  class TestQdrantDocumentStore(
@@ -409,6 +432,16 @@ class TestQdrantDocumentStore(
409
432
  # Check that the sets are equal, meaning the content and IDs match regardless of order
410
433
  assert {doc.id for doc in received} == {doc.id for doc in expected}
411
434
 
435
+ def test_close_and_reopen(self, document_store: QdrantDocumentStore):
436
+ assert document_store.count_documents() == 0
437
+ assert document_store._client is not None
438
+
439
+ document_store.close()
440
+
441
+ assert document_store._client is None
442
+ assert document_store.count_documents() == 0
443
+ assert document_store._client is not None
444
+
412
445
  def test_prepare_client_params_no_mutability(self):
413
446
  metadata = {"key": "value"}
414
447
  doc_store = QdrantDocumentStore(
@@ -1,4 +1,4 @@
1
- from unittest.mock import MagicMock, patch
1
+ from unittest.mock import AsyncMock, MagicMock, patch
2
2
 
3
3
  import pytest
4
4
  import pytest_asyncio
@@ -162,6 +162,29 @@ class TestQdrantDocumentStoreAsyncUnit:
162
162
  ):
163
163
  assert await getattr(document_store, method_name)(*args) == expected
164
164
 
165
+ async def test_close_async(self):
166
+ document_store = QdrantDocumentStore(location=":memory:")
167
+ mock_client = AsyncMock()
168
+ document_store._async_client = mock_client
169
+
170
+ await document_store.close_async()
171
+
172
+ mock_client.close.assert_awaited_once()
173
+ assert document_store._async_client is None
174
+
175
+ await document_store.close_async()
176
+ mock_client.close.assert_awaited_once()
177
+
178
+ async def test_close_async_is_exception_safe(self):
179
+ document_store = QdrantDocumentStore(location=":memory:")
180
+ mock_client = AsyncMock()
181
+ mock_client.close.side_effect = RuntimeError("boom")
182
+ document_store._async_client = mock_client
183
+
184
+ await document_store.close_async()
185
+
186
+ assert document_store._async_client is None
187
+
165
188
 
166
189
  @pytest.mark.integration
167
190
  @pytest.mark.asyncio
@@ -195,6 +218,16 @@ class TestQdrantDocumentStoreAsync(
195
218
  assert len(received) == len(expected)
196
219
  assert {doc.id for doc in received} == {doc.id for doc in expected}
197
220
 
221
+ async def test_close_async_and_reopen(self, document_store: QdrantDocumentStore):
222
+ assert await document_store.count_documents_async() == 0
223
+ assert document_store._async_client is not None
224
+
225
+ await document_store.close_async()
226
+
227
+ assert document_store._async_client is None
228
+ assert await document_store.count_documents_async() == 0
229
+ assert document_store._async_client is not None
230
+
198
231
  @pytest.mark.asyncio
199
232
  async def test_write_documents_async(self, document_store: QdrantDocumentStore):
200
233
  docs = [Document(id="1")]
@@ -146,6 +146,26 @@ class TestQdrantRetriever:
146
146
  retriever = QdrantEmbeddingRetriever.from_dict(data)
147
147
  assert retriever._filter_policy == FilterPolicy.REPLACE # defaults to REPLACE
148
148
 
149
+ def test_close(self):
150
+ mock_store = Mock(spec=QdrantDocumentStore)
151
+ retriever = QdrantEmbeddingRetriever(document_store=mock_store)
152
+
153
+ retriever.close()
154
+
155
+ mock_store.close.assert_called_once_with()
156
+ assert retriever._document_store is mock_store
157
+
158
+ @pytest.mark.asyncio
159
+ async def test_close_async(self):
160
+ mock_store = Mock(spec=QdrantDocumentStore)
161
+ mock_store.close_async = AsyncMock()
162
+ retriever = QdrantEmbeddingRetriever(document_store=mock_store)
163
+
164
+ await retriever.close_async()
165
+
166
+ mock_store.close_async.assert_awaited_once_with()
167
+ assert retriever._document_store is mock_store
168
+
149
169
  def test_run(self):
150
170
  mock_store = Mock(spec=QdrantDocumentStore)
151
171
  mock_store._query_by_embedding.return_value = [Document(content="doc", embedding=[0.1, 0.2])]
@@ -1,4 +1,4 @@
1
- from unittest.mock import Mock
1
+ from unittest.mock import AsyncMock, Mock
2
2
 
3
3
  import pytest
4
4
  from haystack.dataclasses import Document, SparseEmbedding
@@ -146,6 +146,26 @@ class TestQdrantHybridRetriever:
146
146
  assert retriever._group_by is None
147
147
  assert retriever._group_size is None
148
148
 
149
+ def test_close(self):
150
+ mock_store = Mock(spec=QdrantDocumentStore)
151
+ retriever = QdrantHybridRetriever(document_store=mock_store)
152
+
153
+ retriever.close()
154
+
155
+ mock_store.close.assert_called_once_with()
156
+ assert retriever._document_store is mock_store
157
+
158
+ @pytest.mark.asyncio
159
+ async def test_close_async(self):
160
+ mock_store = Mock(spec=QdrantDocumentStore)
161
+ mock_store.close_async = AsyncMock()
162
+ retriever = QdrantHybridRetriever(document_store=mock_store)
163
+
164
+ await retriever.close_async()
165
+
166
+ mock_store.close_async.assert_awaited_once_with()
167
+ assert retriever._document_store is mock_store
168
+
149
169
  def test_run(self):
150
170
  mock_store = Mock(spec=QdrantDocumentStore)
151
171
  sparse_embedding = SparseEmbedding(indices=[0, 1, 2, 3], values=[0.1, 0.8, 0.05, 0.33])
@@ -154,6 +154,26 @@ class TestQdrantSparseEmbeddingRetriever:
154
154
  assert retriever._group_by is None
155
155
  assert retriever._group_size is None
156
156
 
157
+ def test_close(self):
158
+ mock_store = Mock(spec=QdrantDocumentStore)
159
+ retriever = QdrantSparseEmbeddingRetriever(document_store=mock_store)
160
+
161
+ retriever.close()
162
+
163
+ mock_store.close.assert_called_once_with()
164
+ assert retriever._document_store is mock_store
165
+
166
+ @pytest.mark.asyncio
167
+ async def test_close_async(self):
168
+ mock_store = Mock(spec=QdrantDocumentStore)
169
+ mock_store.close_async = AsyncMock()
170
+ retriever = QdrantSparseEmbeddingRetriever(document_store=mock_store)
171
+
172
+ await retriever.close_async()
173
+
174
+ mock_store.close_async.assert_awaited_once_with()
175
+ assert retriever._document_store is mock_store
176
+
157
177
  def test_run(self):
158
178
  mock_store = Mock(spec=QdrantDocumentStore)
159
179
  sparse = SparseEmbedding(indices=[0, 5], values=[0.1, 0.7])