qdrant-haystack 10.5.0__tar.gz → 11.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.
- {qdrant_haystack-10.5.0 → qdrant_haystack-11.0.0}/CHANGELOG.md +52 -19
- {qdrant_haystack-10.5.0 → qdrant_haystack-11.0.0}/LICENSE.txt +1 -1
- {qdrant_haystack-10.5.0 → qdrant_haystack-11.0.0}/PKG-INFO +2 -2
- {qdrant_haystack-10.5.0 → qdrant_haystack-11.0.0}/src/haystack_integrations/document_stores/qdrant/document_store.py +119 -101
- {qdrant_haystack-10.5.0 → qdrant_haystack-11.0.0}/tests/test_document_store.py +27 -73
- {qdrant_haystack-10.5.0 → qdrant_haystack-11.0.0}/tests/test_document_store_async.py +14 -40
- {qdrant_haystack-10.5.0 → qdrant_haystack-11.0.0}/.gitignore +0 -0
- {qdrant_haystack-10.5.0 → qdrant_haystack-11.0.0}/README.md +0 -0
- {qdrant_haystack-10.5.0 → qdrant_haystack-11.0.0}/examples/embedding_retrieval.py +0 -0
- {qdrant_haystack-10.5.0 → qdrant_haystack-11.0.0}/pydoc/config_docusaurus.yml +0 -0
- {qdrant_haystack-10.5.0 → qdrant_haystack-11.0.0}/pyproject.toml +0 -0
- {qdrant_haystack-10.5.0 → qdrant_haystack-11.0.0}/src/haystack_integrations/components/retrievers/py.typed +0 -0
- {qdrant_haystack-10.5.0 → qdrant_haystack-11.0.0}/src/haystack_integrations/components/retrievers/qdrant/__init__.py +0 -0
- {qdrant_haystack-10.5.0 → qdrant_haystack-11.0.0}/src/haystack_integrations/components/retrievers/qdrant/retriever.py +0 -0
- {qdrant_haystack-10.5.0 → qdrant_haystack-11.0.0}/src/haystack_integrations/document_stores/py.typed +0 -0
- {qdrant_haystack-10.5.0 → qdrant_haystack-11.0.0}/src/haystack_integrations/document_stores/qdrant/__init__.py +0 -0
- {qdrant_haystack-10.5.0 → qdrant_haystack-11.0.0}/src/haystack_integrations/document_stores/qdrant/converters.py +0 -0
- {qdrant_haystack-10.5.0 → qdrant_haystack-11.0.0}/src/haystack_integrations/document_stores/qdrant/filters.py +0 -0
- {qdrant_haystack-10.5.0 → qdrant_haystack-11.0.0}/src/haystack_integrations/document_stores/qdrant/migrate_to_sparse.py +0 -0
- {qdrant_haystack-10.5.0 → qdrant_haystack-11.0.0}/tests/__init__.py +0 -0
- {qdrant_haystack-10.5.0 → qdrant_haystack-11.0.0}/tests/conftest.py +0 -0
- {qdrant_haystack-10.5.0 → qdrant_haystack-11.0.0}/tests/test_converters.py +0 -0
- {qdrant_haystack-10.5.0 → qdrant_haystack-11.0.0}/tests/test_dict_converters.py +0 -0
- {qdrant_haystack-10.5.0 → qdrant_haystack-11.0.0}/tests/test_embedding_retriever.py +0 -0
- {qdrant_haystack-10.5.0 → qdrant_haystack-11.0.0}/tests/test_filters.py +0 -0
- {qdrant_haystack-10.5.0 → qdrant_haystack-11.0.0}/tests/test_hybrid_retriever.py +0 -0
- {qdrant_haystack-10.5.0 → qdrant_haystack-11.0.0}/tests/test_sparse_embedding_retriever.py +0 -0
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [integrations/qdrant-v10.6.0] - 2026-09-01
|
|
4
|
+
|
|
5
|
+
### 🐛 Bug Fixes
|
|
6
|
+
|
|
7
|
+
- Standardize license files (#3771)
|
|
8
|
+
- `QdrantDocumentStore` removing duplicated tests already covered by haystack Mixin tests + fixing dedup + adding missing Async test (#3785)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## [integrations/qdrant-v10.5.0] - 2026-08-03
|
|
12
|
+
|
|
13
|
+
### 🚀 Features
|
|
14
|
+
|
|
15
|
+
- *(qdrant)* Expose RRF k and weights parameters in QdrantHybridRetriever (#3702)
|
|
16
|
+
|
|
17
|
+
### 🐛 Bug Fixes
|
|
18
|
+
|
|
19
|
+
- `Qdrant` normalisation of .meta usage in `get_metadata_field_unique_values()` (#3642)
|
|
20
|
+
- `Qdrant` adding offset based pagination to `get_metadata_field_unique_values()` (#3697)
|
|
21
|
+
|
|
22
|
+
|
|
3
23
|
## [integrations/qdrant-v10.4.0] - 2026-07-27
|
|
4
24
|
|
|
5
25
|
### 🚀 Features
|
|
@@ -67,7 +87,7 @@
|
|
|
67
87
|
|
|
68
88
|
- Feat: `QdrantDocumentStore` return number deleted docs on `delete_by_filter` (#2807)
|
|
69
89
|
|
|
70
|
-
## [integrations/qdrant-
|
|
90
|
+
## [integrations/qdrant-v10.1.0] - 2026-02-02
|
|
71
91
|
|
|
72
92
|
### 🚀 Features
|
|
73
93
|
|
|
@@ -75,11 +95,15 @@
|
|
|
75
95
|
|
|
76
96
|
### 🧹 Chores
|
|
77
97
|
|
|
78
|
-
- Make fmt command more forgiving (#2671)
|
|
79
|
-
- [**breaking**] Qdrant - drop Python 3.9 and use X|Y typing (#2726)
|
|
80
98
|
- Disabling progress bar in `QdrantDocumentStore` tests (#2797)
|
|
81
99
|
|
|
82
|
-
|
|
100
|
+
|
|
101
|
+
## [integrations/qdrant-v10.0.0] - 2026-01-13
|
|
102
|
+
|
|
103
|
+
### 🧹 Chores
|
|
104
|
+
|
|
105
|
+
- Make fmt command more forgiving (#2671)
|
|
106
|
+
- [**breaking**] Qdrant - drop Python 3.9 and use X|Y typing (#2726)
|
|
83
107
|
|
|
84
108
|
|
|
85
109
|
## [integrations/qdrant-v9.5.0] - 2026-01-07
|
|
@@ -139,6 +163,8 @@
|
|
|
139
163
|
- Align core-integrations Hatch scripts (#1898)
|
|
140
164
|
- Update md files for new hatch scripts (#1911)
|
|
141
165
|
|
|
166
|
+
## [integrations/qdrant-v9.1.3] - 2025-05-28
|
|
167
|
+
|
|
142
168
|
### 🌀 Miscellaneous
|
|
143
169
|
|
|
144
170
|
- Add pins for Qdrant (#1853)
|
|
@@ -362,6 +388,18 @@
|
|
|
362
388
|
- *(Qdrant)* Allow payload indexing + on disk vectors (#553)
|
|
363
389
|
- Qdrant datetime filtering support (#570)
|
|
364
390
|
|
|
391
|
+
### 📚 Documentation
|
|
392
|
+
|
|
393
|
+
- Disable-class-def (#556)
|
|
394
|
+
|
|
395
|
+
### 🌀 Miscellaneous
|
|
396
|
+
|
|
397
|
+
- Make tests show coverage (#566)
|
|
398
|
+
- Remove references to Python 3.7 (#601)
|
|
399
|
+
- Make Document Stores initially skip `SparseEmbedding` (#606)
|
|
400
|
+
|
|
401
|
+
## [integrations/qdrant-v3.1.0] - 2024-03-06
|
|
402
|
+
|
|
365
403
|
### 🐛 Bug Fixes
|
|
366
404
|
|
|
367
405
|
- Fix: fix linter errors (#282)
|
|
@@ -376,18 +414,11 @@
|
|
|
376
414
|
|
|
377
415
|
- Update category slug (#442)
|
|
378
416
|
- Small consistency improvements (#536)
|
|
379
|
-
- Disable-class-def (#556)
|
|
380
417
|
|
|
381
418
|
### ⚙️ CI
|
|
382
419
|
|
|
383
420
|
- Generate API docs for Qdrant (#361)
|
|
384
421
|
|
|
385
|
-
### 🌀 Miscellaneous
|
|
386
|
-
|
|
387
|
-
- Make tests show coverage (#566)
|
|
388
|
-
- Remove references to Python 3.7 (#601)
|
|
389
|
-
- Make Document Stores initially skip `SparseEmbedding` (#606)
|
|
390
|
-
|
|
391
422
|
## [integrations/qdrant-v3.0.0] - 2024-01-22
|
|
392
423
|
|
|
393
424
|
### 🌀 Miscellaneous
|
|
@@ -396,20 +427,22 @@
|
|
|
396
427
|
|
|
397
428
|
## [integrations/qdrant-v2.0.1] - 2024-01-18
|
|
398
429
|
|
|
399
|
-
### 🚀 Features
|
|
400
|
-
|
|
401
|
-
- Add Qdrant integration (#98)
|
|
402
|
-
|
|
403
430
|
### 🐛 Bug Fixes
|
|
404
431
|
|
|
405
432
|
- Fix import paths for beta5 (#237)
|
|
406
433
|
|
|
407
|
-
### 🚜 Refactor
|
|
408
|
-
|
|
409
|
-
- Use `hatch_vcs` to manage integrations versioning (#103)
|
|
410
|
-
|
|
411
434
|
### 🌀 Miscellaneous
|
|
412
435
|
|
|
413
436
|
- Renamed QdrntRetriever to QdrntEmbeddingRetriever (#174)
|
|
414
437
|
|
|
438
|
+
## [integrations/qdrant-v2.0.0] - 2023-12-18
|
|
439
|
+
|
|
440
|
+
### 🚀 Features
|
|
441
|
+
|
|
442
|
+
- Add Qdrant integration (#98)
|
|
443
|
+
|
|
444
|
+
### 🚜 Refactor
|
|
445
|
+
|
|
446
|
+
- Use `hatch_vcs` to manage integrations versioning (#103)
|
|
447
|
+
|
|
415
448
|
<!-- generated by git-cliff -->
|
|
@@ -58,7 +58,7 @@ APPENDIX: How to apply the Apache License to your work.
|
|
|
58
58
|
|
|
59
59
|
To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
|
|
60
60
|
|
|
61
|
-
Copyright
|
|
61
|
+
Copyright 2023-present deepset GmbH
|
|
62
62
|
|
|
63
63
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
64
64
|
you may not use this file except in compliance with the License.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: qdrant-haystack
|
|
3
|
-
Version:
|
|
3
|
+
Version: 11.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
|
|
@@ -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
|
|
350
|
-
|
|
351
|
-
|
|
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
|
|
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
|
|
367
|
-
|
|
368
|
-
|
|
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,
|
|
@@ -716,7 +717,7 @@ class QdrantDocumentStore:
|
|
|
716
717
|
|
|
717
718
|
@staticmethod
|
|
718
719
|
def _process_records_count_unique(
|
|
719
|
-
records: list[Any], metadata_fields: list[str], unique_values_by_field: dict[str, set[Any]]
|
|
720
|
+
records: list[Any], metadata_fields: list[str], unique_values_by_field: dict[str, set[tuple[type, Any]]]
|
|
720
721
|
) -> None:
|
|
721
722
|
"""
|
|
722
723
|
Update unique_values_by_field from a batch of Qdrant records.
|
|
@@ -730,10 +731,11 @@ class QdrantDocumentStore:
|
|
|
730
731
|
if field in meta:
|
|
731
732
|
value = meta[field]
|
|
732
733
|
if value is not None:
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
734
|
+
# Values of different types can be equal in Python (`1 == True`, `1 == 1.0`), so
|
|
735
|
+
# a plain set would silently merge them. Dedupe by (type, value) instead - lists
|
|
736
|
+
# and dicts aren't hashable, so they're keyed by their str() form.
|
|
737
|
+
hashable_value = str(value) if isinstance(value, (list, dict)) else value
|
|
738
|
+
unique_values_by_field[field].add((type(value), hashable_value))
|
|
737
739
|
|
|
738
740
|
@staticmethod
|
|
739
741
|
def _process_records_unique_values(
|
|
@@ -752,9 +754,13 @@ class QdrantDocumentStore:
|
|
|
752
754
|
if value is not None:
|
|
753
755
|
if search_term is not None and search_term.lower() not in str(value).lower():
|
|
754
756
|
continue
|
|
757
|
+
# Values of different types can be equal in Python (`1 == True`, `1 == 1.0`), so
|
|
758
|
+
# a plain set would silently merge them. Dedupe by (type, value) instead - lists
|
|
759
|
+
# and dicts aren't hashable, so they're keyed by their str() form.
|
|
755
760
|
hashable_value = str(value) if isinstance(value, (list, dict)) else value
|
|
756
|
-
|
|
757
|
-
|
|
761
|
+
dedup_key = (type(value), hashable_value)
|
|
762
|
+
if dedup_key not in unique_values_set:
|
|
763
|
+
unique_values_set.add(dedup_key)
|
|
758
764
|
unique_values.append(value)
|
|
759
765
|
|
|
760
766
|
@staticmethod
|
|
@@ -916,38 +922,39 @@ class QdrantDocumentStore:
|
|
|
916
922
|
Deletes all documents from the document store.
|
|
917
923
|
|
|
918
924
|
:param recreate_index: Whether to recreate the index after deleting all documents.
|
|
925
|
+
:raises QdrantStoreError: Deleting documents or recreating the collection fails.
|
|
919
926
|
"""
|
|
920
927
|
|
|
921
928
|
self._initialize_client()
|
|
922
929
|
assert self._client is not None
|
|
923
930
|
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
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()
|
|
928
936
|
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
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
|
|
932
940
|
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
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
|
|
936
944
|
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
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
|
+
)
|
|
948
956
|
|
|
949
|
-
|
|
950
|
-
try:
|
|
957
|
+
else:
|
|
951
958
|
self._client.delete(
|
|
952
959
|
collection_name=self.index,
|
|
953
960
|
points_selector=rest.FilterSelector(
|
|
@@ -957,48 +964,48 @@ class QdrantDocumentStore:
|
|
|
957
964
|
),
|
|
958
965
|
wait=self.wait_result_from_api,
|
|
959
966
|
)
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
)
|
|
967
|
+
except Exception as e:
|
|
968
|
+
msg = f"Error when calling QdrantDocumentStore.delete_all_documents(): {e}"
|
|
969
|
+
raise QdrantStoreError(msg) from e
|
|
964
970
|
|
|
965
971
|
async def delete_all_documents_async(self, recreate_index: bool = False) -> None:
|
|
966
972
|
"""
|
|
967
973
|
Asynchronously deletes all documents from the document store.
|
|
968
974
|
|
|
969
975
|
:param recreate_index: Whether to recreate the index after deleting all documents.
|
|
976
|
+
:raises QdrantStoreError: Deleting documents or recreating the collection fails.
|
|
970
977
|
"""
|
|
971
978
|
|
|
972
979
|
await self._initialize_async_client()
|
|
973
980
|
assert self._async_client is not None
|
|
974
981
|
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
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()
|
|
979
987
|
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
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
|
|
983
991
|
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
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
|
|
987
995
|
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
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
|
+
)
|
|
999
1007
|
|
|
1000
|
-
|
|
1001
|
-
try:
|
|
1008
|
+
else:
|
|
1002
1009
|
await self._async_client.delete(
|
|
1003
1010
|
collection_name=self.index,
|
|
1004
1011
|
points_selector=rest.FilterSelector(
|
|
@@ -1008,10 +1015,9 @@ class QdrantDocumentStore:
|
|
|
1008
1015
|
),
|
|
1009
1016
|
wait=self.wait_result_from_api,
|
|
1010
1017
|
)
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
)
|
|
1018
|
+
except Exception as e:
|
|
1019
|
+
msg = f"Error when calling QdrantDocumentStore.delete_all_documents_async(): {e}"
|
|
1020
|
+
raise QdrantStoreError(msg) from e
|
|
1015
1021
|
|
|
1016
1022
|
def count_documents_by_filter(self, filters: dict[str, Any]) -> int:
|
|
1017
1023
|
"""
|
|
@@ -1021,6 +1027,7 @@ class QdrantDocumentStore:
|
|
|
1021
1027
|
For filter syntax, see [Haystack metadata filtering](https://docs.haystack.deepset.ai/docs/metadata-filtering)
|
|
1022
1028
|
|
|
1023
1029
|
:returns: The number of documents that match the filters.
|
|
1030
|
+
:raises QdrantStoreError: Counting documents matching the filters fails.
|
|
1024
1031
|
"""
|
|
1025
1032
|
self._initialize_client()
|
|
1026
1033
|
assert self._client is not None
|
|
@@ -1032,9 +1039,9 @@ class QdrantDocumentStore:
|
|
|
1032
1039
|
count_filter=qdrant_filter,
|
|
1033
1040
|
)
|
|
1034
1041
|
return response.count
|
|
1035
|
-
except
|
|
1036
|
-
|
|
1037
|
-
|
|
1042
|
+
except Exception as e:
|
|
1043
|
+
msg = f"Error when calling QdrantDocumentStore.count_documents_by_filter(): {e}"
|
|
1044
|
+
raise QdrantStoreError(msg) from e
|
|
1038
1045
|
|
|
1039
1046
|
async def count_documents_by_filter_async(self, filters: dict[str, Any]) -> int:
|
|
1040
1047
|
"""
|
|
@@ -1045,6 +1052,7 @@ class QdrantDocumentStore:
|
|
|
1045
1052
|
|
|
1046
1053
|
:returns:
|
|
1047
1054
|
The number of documents that match the filters.
|
|
1055
|
+
:raises QdrantStoreError: Counting documents matching the filters fails.
|
|
1048
1056
|
"""
|
|
1049
1057
|
await self._initialize_async_client()
|
|
1050
1058
|
assert self._async_client is not None
|
|
@@ -1056,9 +1064,9 @@ class QdrantDocumentStore:
|
|
|
1056
1064
|
count_filter=qdrant_filter,
|
|
1057
1065
|
)
|
|
1058
1066
|
return response.count
|
|
1059
|
-
except
|
|
1060
|
-
|
|
1061
|
-
|
|
1067
|
+
except Exception as e:
|
|
1068
|
+
msg = f"Error when calling QdrantDocumentStore.count_documents_by_filter_async(): {e}"
|
|
1069
|
+
raise QdrantStoreError(msg) from e
|
|
1062
1070
|
|
|
1063
1071
|
def get_metadata_fields_info(self) -> dict[str, dict[str, str]]:
|
|
1064
1072
|
"""
|
|
@@ -1073,6 +1081,7 @@ class QdrantDocumentStore:
|
|
|
1073
1081
|
```python
|
|
1074
1082
|
{"category": {"type": "keyword"}, "priority": {"type": "long"}}
|
|
1075
1083
|
```
|
|
1084
|
+
:raises QdrantStoreError: Retrieving or processing metadata field information fails.
|
|
1076
1085
|
"""
|
|
1077
1086
|
self._initialize_client()
|
|
1078
1087
|
assert self._client is not None
|
|
@@ -1098,9 +1107,9 @@ class QdrantDocumentStore:
|
|
|
1098
1107
|
break
|
|
1099
1108
|
|
|
1100
1109
|
return fields_info
|
|
1101
|
-
except
|
|
1102
|
-
|
|
1103
|
-
|
|
1110
|
+
except Exception as e:
|
|
1111
|
+
msg = f"Error when calling QdrantDocumentStore.get_metadata_fields_info(): {e}"
|
|
1112
|
+
raise QdrantStoreError(msg) from e
|
|
1104
1113
|
|
|
1105
1114
|
async def get_metadata_fields_info_async(self) -> dict[str, dict[str, str]]:
|
|
1106
1115
|
"""
|
|
@@ -1115,6 +1124,7 @@ class QdrantDocumentStore:
|
|
|
1115
1124
|
```python
|
|
1116
1125
|
{"category": {"type": "keyword"}, "priority": {"type": "long"}}
|
|
1117
1126
|
```
|
|
1127
|
+
:raises QdrantStoreError: Retrieving or processing metadata field information fails.
|
|
1118
1128
|
"""
|
|
1119
1129
|
await self._initialize_async_client()
|
|
1120
1130
|
assert self._async_client is not None
|
|
@@ -1140,9 +1150,9 @@ class QdrantDocumentStore:
|
|
|
1140
1150
|
break
|
|
1141
1151
|
|
|
1142
1152
|
return fields_info
|
|
1143
|
-
except
|
|
1144
|
-
|
|
1145
|
-
|
|
1153
|
+
except Exception as e:
|
|
1154
|
+
msg = f"Error when calling QdrantDocumentStore.get_metadata_fields_info_async(): {e}"
|
|
1155
|
+
raise QdrantStoreError(msg) from e
|
|
1146
1156
|
|
|
1147
1157
|
def get_metadata_field_min_max(self, metadata_field: str) -> dict[str, Any]:
|
|
1148
1158
|
"""
|
|
@@ -1153,6 +1163,7 @@ class QdrantDocumentStore:
|
|
|
1153
1163
|
:returns: A dictionary with the keys "min" and "max", where each value is the minimum or maximum value of the
|
|
1154
1164
|
metadata field across all documents. Returns ``{"min": None, "max": None}`` if no documents have
|
|
1155
1165
|
the field.
|
|
1166
|
+
:raises QdrantStoreError: Retrieving or computing the metadata bounds fails.
|
|
1156
1167
|
"""
|
|
1157
1168
|
self._initialize_client()
|
|
1158
1169
|
assert self._client is not None
|
|
@@ -1178,8 +1189,8 @@ class QdrantDocumentStore:
|
|
|
1178
1189
|
|
|
1179
1190
|
return {"min": min_value, "max": max_value}
|
|
1180
1191
|
except Exception as e:
|
|
1181
|
-
|
|
1182
|
-
|
|
1192
|
+
msg = f"Error when calling QdrantDocumentStore.get_metadata_field_min_max(): {e}"
|
|
1193
|
+
raise QdrantStoreError(msg) from e
|
|
1183
1194
|
|
|
1184
1195
|
async def get_metadata_field_min_max_async(self, metadata_field: str) -> dict[str, Any]:
|
|
1185
1196
|
"""
|
|
@@ -1190,6 +1201,7 @@ class QdrantDocumentStore:
|
|
|
1190
1201
|
:returns: A dictionary with the keys "min" and "max", where each value is the minimum or maximum value of the
|
|
1191
1202
|
metadata field across all documents. Returns ``{"min": None, "max": None}`` if no documents have
|
|
1192
1203
|
the field.
|
|
1204
|
+
:raises QdrantStoreError: Retrieving or computing the metadata bounds fails.
|
|
1193
1205
|
"""
|
|
1194
1206
|
await self._initialize_async_client()
|
|
1195
1207
|
assert self._async_client is not None
|
|
@@ -1215,8 +1227,8 @@ class QdrantDocumentStore:
|
|
|
1215
1227
|
|
|
1216
1228
|
return {"min": min_value, "max": max_value}
|
|
1217
1229
|
except Exception as e:
|
|
1218
|
-
|
|
1219
|
-
|
|
1230
|
+
msg = f"Error when calling QdrantDocumentStore.get_metadata_field_min_max_async(): {e}"
|
|
1231
|
+
raise QdrantStoreError(msg) from e
|
|
1220
1232
|
|
|
1221
1233
|
def count_unique_metadata_by_filter(self, filters: dict[str, Any], metadata_fields: list[str]) -> dict[str, int]:
|
|
1222
1234
|
"""
|
|
@@ -1228,12 +1240,13 @@ class QdrantDocumentStore:
|
|
|
1228
1240
|
|
|
1229
1241
|
:returns: A dictionary mapping each metadata field name to the count of its unique values among the filtered
|
|
1230
1242
|
documents.
|
|
1243
|
+
:raises QdrantStoreError: Retrieving or counting unique metadata values fails.
|
|
1231
1244
|
"""
|
|
1232
1245
|
self._initialize_client()
|
|
1233
1246
|
assert self._client is not None
|
|
1234
1247
|
|
|
1235
1248
|
qdrant_filter = convert_filters_to_qdrant(filters) if filters else None
|
|
1236
|
-
unique_values_by_field: dict[str, set[Any]] = {field: set() for field in metadata_fields}
|
|
1249
|
+
unique_values_by_field: dict[str, set[tuple[type, Any]]] = {field: set() for field in metadata_fields}
|
|
1237
1250
|
|
|
1238
1251
|
try:
|
|
1239
1252
|
next_offset = None
|
|
@@ -1252,8 +1265,8 @@ class QdrantDocumentStore:
|
|
|
1252
1265
|
|
|
1253
1266
|
return {field: len(unique_values_by_field[field]) for field in metadata_fields}
|
|
1254
1267
|
except Exception as e:
|
|
1255
|
-
|
|
1256
|
-
|
|
1268
|
+
msg = f"Error when calling QdrantDocumentStore.count_unique_metadata_by_filter(): {e}"
|
|
1269
|
+
raise QdrantStoreError(msg) from e
|
|
1257
1270
|
|
|
1258
1271
|
async def count_unique_metadata_by_filter_async(
|
|
1259
1272
|
self, filters: dict[str, Any], metadata_fields: list[str]
|
|
@@ -1269,12 +1282,13 @@ class QdrantDocumentStore:
|
|
|
1269
1282
|
|
|
1270
1283
|
:returns: A dictionary mapping each metadata field name to the count of its unique values among the filtered
|
|
1271
1284
|
documents.
|
|
1285
|
+
:raises QdrantStoreError: Retrieving or counting unique metadata values fails.
|
|
1272
1286
|
"""
|
|
1273
1287
|
await self._initialize_async_client()
|
|
1274
1288
|
assert self._async_client is not None
|
|
1275
1289
|
|
|
1276
1290
|
qdrant_filter = convert_filters_to_qdrant(filters) if filters else None
|
|
1277
|
-
unique_values_by_field: dict[str, set[Any]] = {field: set() for field in metadata_fields}
|
|
1291
|
+
unique_values_by_field: dict[str, set[tuple[type, Any]]] = {field: set() for field in metadata_fields}
|
|
1278
1292
|
|
|
1279
1293
|
try:
|
|
1280
1294
|
next_offset = None
|
|
@@ -1293,8 +1307,8 @@ class QdrantDocumentStore:
|
|
|
1293
1307
|
|
|
1294
1308
|
return {field: len(unique_values_by_field[field]) for field in metadata_fields}
|
|
1295
1309
|
except Exception as e:
|
|
1296
|
-
|
|
1297
|
-
|
|
1310
|
+
msg = f"Error when calling QdrantDocumentStore.count_unique_metadata_by_filter_async(): {e}"
|
|
1311
|
+
raise QdrantStoreError(msg) from e
|
|
1298
1312
|
|
|
1299
1313
|
def get_metadata_field_unique_values(
|
|
1300
1314
|
self,
|
|
@@ -1307,7 +1321,7 @@ class QdrantDocumentStore:
|
|
|
1307
1321
|
"""
|
|
1308
1322
|
Returns unique values for a metadata field, with optional filters, search term and pagination.
|
|
1309
1323
|
|
|
1310
|
-
Unique values are
|
|
1324
|
+
Unique values are sorted by string representation, then by type name, before pagination is applied.
|
|
1311
1325
|
|
|
1312
1326
|
**Note**: This operation can be expensive for metadata fields with many unique values, since all
|
|
1313
1327
|
matching documents must be scrolled through to compute the total count.
|
|
@@ -1320,6 +1334,7 @@ class QdrantDocumentStore:
|
|
|
1320
1334
|
For filter syntax, see [Haystack metadata filtering](https://docs.haystack.deepset.ai/docs/metadata-filtering)
|
|
1321
1335
|
|
|
1322
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.
|
|
1323
1338
|
"""
|
|
1324
1339
|
self._initialize_client()
|
|
1325
1340
|
assert self._client is not None
|
|
@@ -1344,11 +1359,12 @@ class QdrantDocumentStore:
|
|
|
1344
1359
|
if self._check_stop_scrolling(next_offset):
|
|
1345
1360
|
break
|
|
1346
1361
|
|
|
1362
|
+
unique_values.sort(key=lambda v: (str(v), type(v).__name__))
|
|
1347
1363
|
total_count = len(unique_values)
|
|
1348
1364
|
return unique_values[from_ : from_ + size], total_count
|
|
1349
1365
|
except Exception as e:
|
|
1350
|
-
|
|
1351
|
-
|
|
1366
|
+
msg = f"Error when calling QdrantDocumentStore.get_metadata_field_unique_values(): {e}"
|
|
1367
|
+
raise QdrantStoreError(msg) from e
|
|
1352
1368
|
|
|
1353
1369
|
async def get_metadata_field_unique_values_async(
|
|
1354
1370
|
self,
|
|
@@ -1361,7 +1377,7 @@ class QdrantDocumentStore:
|
|
|
1361
1377
|
"""
|
|
1362
1378
|
Asynchronously returns unique values for a metadata field, with optional filters, search term and pagination.
|
|
1363
1379
|
|
|
1364
|
-
Unique values are
|
|
1380
|
+
Unique values are sorted by string representation, then by type name, before pagination is applied.
|
|
1365
1381
|
|
|
1366
1382
|
**Note**: This operation can be expensive for metadata fields with many unique values, since all
|
|
1367
1383
|
matching documents must be scrolled through to compute the total count.
|
|
@@ -1374,6 +1390,7 @@ class QdrantDocumentStore:
|
|
|
1374
1390
|
For filter syntax, see [Haystack metadata filtering](https://docs.haystack.deepset.ai/docs/metadata-filtering)
|
|
1375
1391
|
|
|
1376
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.
|
|
1377
1394
|
"""
|
|
1378
1395
|
await self._initialize_async_client()
|
|
1379
1396
|
assert self._async_client is not None
|
|
@@ -1398,11 +1415,12 @@ class QdrantDocumentStore:
|
|
|
1398
1415
|
if self._check_stop_scrolling(next_offset):
|
|
1399
1416
|
break
|
|
1400
1417
|
|
|
1418
|
+
unique_values.sort(key=lambda v: (str(v), type(v).__name__))
|
|
1401
1419
|
total_count = len(unique_values)
|
|
1402
1420
|
return unique_values[from_ : from_ + size], total_count
|
|
1403
1421
|
except Exception as e:
|
|
1404
|
-
|
|
1405
|
-
|
|
1422
|
+
msg = f"Error when calling QdrantDocumentStore.get_metadata_field_unique_values_async(): {e}"
|
|
1423
|
+
raise QdrantStoreError(msg) from e
|
|
1406
1424
|
|
|
1407
1425
|
@classmethod
|
|
1408
1426
|
def from_dict(cls, data: dict[str, Any]) -> "QdrantDocumentStore":
|
|
@@ -267,8 +267,19 @@ class TestQdrantDocumentStoreUnit:
|
|
|
267
267
|
]
|
|
268
268
|
unique: dict = {"category": set(), "tags": set()}
|
|
269
269
|
QdrantDocumentStore._process_records_count_unique(records, ["category", "tags"], unique)
|
|
270
|
-
assert unique["category"] ==
|
|
271
|
-
assert unique["tags"] ==
|
|
270
|
+
assert len(unique["category"]) == 2
|
|
271
|
+
assert len(unique["tags"]) == 2
|
|
272
|
+
|
|
273
|
+
def test_process_records_count_unique_does_not_merge_different_types(self):
|
|
274
|
+
"""Values that are `==` across types (`1 == True`, `1 == 1.0`) must still count as distinct."""
|
|
275
|
+
records = [
|
|
276
|
+
SimpleNamespace(payload={"meta": {"value": 1}}),
|
|
277
|
+
SimpleNamespace(payload={"meta": {"value": True}}),
|
|
278
|
+
SimpleNamespace(payload={"meta": {"value": 1.0}}),
|
|
279
|
+
]
|
|
280
|
+
unique: dict = {"value": set()}
|
|
281
|
+
QdrantDocumentStore._process_records_count_unique(records, ["value"], unique)
|
|
282
|
+
assert len(unique["value"]) == 3
|
|
272
283
|
|
|
273
284
|
def test_process_records_unique_values_collects_all(self):
|
|
274
285
|
records = [SimpleNamespace(payload={"meta": {"v": i}}) for i in range(10)]
|
|
@@ -366,17 +377,19 @@ class TestQdrantDocumentStoreUnit:
|
|
|
366
377
|
document_store._query_by_sparse(query_sparse_embedding=sparse_embedding)
|
|
367
378
|
|
|
368
379
|
@pytest.mark.parametrize(
|
|
369
|
-
("method_name", "args"
|
|
380
|
+
("method_name", "args"),
|
|
370
381
|
[
|
|
371
|
-
("count_documents", ()
|
|
372
|
-
("
|
|
373
|
-
("
|
|
374
|
-
("
|
|
375
|
-
("
|
|
376
|
-
("
|
|
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",)),
|
|
377
390
|
],
|
|
378
391
|
)
|
|
379
|
-
def
|
|
392
|
+
def test_operations_raise_on_client_errors(self, method_name, args):
|
|
380
393
|
document_store = QdrantDocumentStore(location=":memory:")
|
|
381
394
|
document_store._initialize_client()
|
|
382
395
|
err = ValueError("boom")
|
|
@@ -384,8 +397,11 @@ class TestQdrantDocumentStoreUnit:
|
|
|
384
397
|
patch.object(document_store._client, "count", side_effect=err),
|
|
385
398
|
patch.object(document_store._client, "scroll", side_effect=err),
|
|
386
399
|
patch.object(document_store._client, "get_collection", side_effect=err),
|
|
400
|
+
patch.object(document_store._client, "delete", side_effect=err),
|
|
387
401
|
):
|
|
388
|
-
|
|
402
|
+
with pytest.raises(QdrantStoreError) as exc_info:
|
|
403
|
+
getattr(document_store, method_name)(*args)
|
|
404
|
+
assert exc_info.value.__cause__ is err
|
|
389
405
|
|
|
390
406
|
def test_close(self):
|
|
391
407
|
document_store = QdrantDocumentStore(location=":memory:")
|
|
@@ -611,65 +627,3 @@ class TestQdrantDocumentStore(
|
|
|
611
627
|
assert len(updated_docs) == 1
|
|
612
628
|
assert updated_docs[0].embedding is not None
|
|
613
629
|
assert len(updated_docs[0].embedding) == 768
|
|
614
|
-
|
|
615
|
-
def test_get_metadata_field_unique_values_pagination(self, document_store: QdrantDocumentStore):
|
|
616
|
-
"""Test getting unique metadata field values with pagination, total reflects the full unpaginated count."""
|
|
617
|
-
docs = [Document(content=f"Doc {i}", meta={"value": i % 5}) for i in range(10)]
|
|
618
|
-
document_store.write_documents(docs)
|
|
619
|
-
|
|
620
|
-
# Get first 2 unique values
|
|
621
|
-
values_page_1, total_1 = document_store.get_metadata_field_unique_values("value", from_=0, size=2)
|
|
622
|
-
assert len(values_page_1) == 2
|
|
623
|
-
assert total_1 == 5
|
|
624
|
-
|
|
625
|
-
# Get next 2 unique values
|
|
626
|
-
values_page_2, total_2 = document_store.get_metadata_field_unique_values("value", from_=2, size=2)
|
|
627
|
-
assert len(values_page_2) == 2
|
|
628
|
-
assert total_2 == 5
|
|
629
|
-
|
|
630
|
-
# Values should not overlap
|
|
631
|
-
assert set(values_page_1) != set(values_page_2)
|
|
632
|
-
|
|
633
|
-
def test_get_metadata_field_unique_values_with_filter(self, document_store: QdrantDocumentStore):
|
|
634
|
-
"""Test getting unique metadata field values with filtering."""
|
|
635
|
-
docs = [
|
|
636
|
-
Document(content="Doc 1", meta={"category": "A", "status": "active"}),
|
|
637
|
-
Document(content="Doc 2", meta={"category": "B", "status": "active"}),
|
|
638
|
-
Document(content="Doc 3", meta={"category": "A", "status": "inactive"}),
|
|
639
|
-
]
|
|
640
|
-
document_store.write_documents(docs)
|
|
641
|
-
|
|
642
|
-
values, total = document_store.get_metadata_field_unique_values(
|
|
643
|
-
"category", filters={"field": "meta.status", "operator": "==", "value": "active"}
|
|
644
|
-
)
|
|
645
|
-
assert set(values) == {"A", "B"}
|
|
646
|
-
assert total == 2
|
|
647
|
-
|
|
648
|
-
def test_get_metadata_field_unique_values_with_meta_prefix(self, document_store: QdrantDocumentStore):
|
|
649
|
-
"""Test that a 'meta.'-prefixed field name is normalized before lookup."""
|
|
650
|
-
docs = [
|
|
651
|
-
Document(content="Doc 1", meta={"category": "A"}),
|
|
652
|
-
Document(content="Doc 2", meta={"category": "B"}),
|
|
653
|
-
]
|
|
654
|
-
document_store.write_documents(docs)
|
|
655
|
-
|
|
656
|
-
values, total = document_store.get_metadata_field_unique_values("meta.category")
|
|
657
|
-
assert set(values) == {"A", "B"}
|
|
658
|
-
assert total == 2
|
|
659
|
-
|
|
660
|
-
def test_get_metadata_field_unique_values_with_search_term(self, document_store: QdrantDocumentStore):
|
|
661
|
-
"""Test that search_term filters unique values by a case-insensitive substring match on the field value."""
|
|
662
|
-
docs = [
|
|
663
|
-
Document(content="Doc 1", meta={"category": "Apple"}),
|
|
664
|
-
Document(content="Doc 2", meta={"category": "Banana"}),
|
|
665
|
-
Document(content="Doc 3", meta={"category": "Apricot"}),
|
|
666
|
-
]
|
|
667
|
-
document_store.write_documents(docs)
|
|
668
|
-
|
|
669
|
-
values, total = document_store.get_metadata_field_unique_values("category", search_term="ap")
|
|
670
|
-
assert set(values) == {"Apple", "Apricot"}
|
|
671
|
-
assert total == 2
|
|
672
|
-
|
|
673
|
-
values, total = document_store.get_metadata_field_unique_values("category", search_term="nonexistent")
|
|
674
|
-
assert values == []
|
|
675
|
-
assert total == 0
|
|
@@ -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"
|
|
144
|
+
("method_name", "args"),
|
|
145
145
|
[
|
|
146
|
-
("count_documents_async", ()
|
|
147
|
-
("
|
|
148
|
-
("
|
|
149
|
-
("
|
|
150
|
-
("
|
|
151
|
-
("
|
|
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
|
|
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
|
-
|
|
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:")
|
|
@@ -342,34 +347,3 @@ class TestQdrantDocumentStoreAsync(
|
|
|
342
347
|
assert len(updated_docs) == 1
|
|
343
348
|
assert updated_docs[0].embedding is not None
|
|
344
349
|
assert len(updated_docs[0].embedding) == 768
|
|
345
|
-
|
|
346
|
-
async def test_get_metadata_field_unique_values_with_meta_prefix_async(self, document_store: QdrantDocumentStore):
|
|
347
|
-
"""Test that a 'meta.'-prefixed field name is normalized before lookup."""
|
|
348
|
-
docs = [
|
|
349
|
-
Document(content="Doc 1", meta={"category": "A"}),
|
|
350
|
-
Document(content="Doc 2", meta={"category": "B"}),
|
|
351
|
-
]
|
|
352
|
-
await document_store.write_documents_async(docs)
|
|
353
|
-
|
|
354
|
-
values, total = await document_store.get_metadata_field_unique_values_async("meta.category")
|
|
355
|
-
assert set(values) == {"A", "B"}
|
|
356
|
-
assert total == 2
|
|
357
|
-
|
|
358
|
-
async def test_get_metadata_field_unique_values_with_search_term_async(self, document_store: QdrantDocumentStore):
|
|
359
|
-
"""Test that search_term filters unique values by a case-insensitive substring match on the field value."""
|
|
360
|
-
docs = [
|
|
361
|
-
Document(content="Doc 1", meta={"category": "Apple"}),
|
|
362
|
-
Document(content="Doc 2", meta={"category": "Banana"}),
|
|
363
|
-
Document(content="Doc 3", meta={"category": "Apricot"}),
|
|
364
|
-
]
|
|
365
|
-
await document_store.write_documents_async(docs)
|
|
366
|
-
|
|
367
|
-
values, total = await document_store.get_metadata_field_unique_values_async("category", search_term="ap")
|
|
368
|
-
assert set(values) == {"Apple", "Apricot"}
|
|
369
|
-
assert total == 2
|
|
370
|
-
|
|
371
|
-
values, total = await document_store.get_metadata_field_unique_values_async(
|
|
372
|
-
"category", search_term="nonexistent"
|
|
373
|
-
)
|
|
374
|
-
assert values == []
|
|
375
|
-
assert total == 0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{qdrant_haystack-10.5.0 → qdrant_haystack-11.0.0}/src/haystack_integrations/document_stores/py.typed
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|