weaviate-haystack 7.2.0__tar.gz → 7.3.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.
- {weaviate_haystack-7.2.0 → weaviate_haystack-7.3.0}/CHANGELOG.md +16 -0
- {weaviate_haystack-7.2.0 → weaviate_haystack-7.3.0}/PKG-INFO +2 -2
- {weaviate_haystack-7.2.0 → weaviate_haystack-7.3.0}/docker-compose.yml +1 -1
- {weaviate_haystack-7.2.0 → weaviate_haystack-7.3.0}/pyproject.toml +1 -1
- {weaviate_haystack-7.2.0 → weaviate_haystack-7.3.0}/src/haystack_integrations/components/retrievers/weaviate/hybrid_retriever.py +3 -3
- {weaviate_haystack-7.2.0 → weaviate_haystack-7.3.0}/src/haystack_integrations/document_stores/weaviate/document_store.py +284 -37
- {weaviate_haystack-7.2.0 → weaviate_haystack-7.3.0}/tests/test_document_store_async.py +132 -34
- {weaviate_haystack-7.2.0 → weaviate_haystack-7.3.0}/tests/test_hybrid_retriever.py +16 -11
- {weaviate_haystack-7.2.0 → weaviate_haystack-7.3.0}/.gitignore +0 -0
- {weaviate_haystack-7.2.0 → weaviate_haystack-7.3.0}/LICENSE.txt +0 -0
- {weaviate_haystack-7.2.0 → weaviate_haystack-7.3.0}/README.md +0 -0
- {weaviate_haystack-7.2.0 → weaviate_haystack-7.3.0}/pydoc/config_docusaurus.yml +0 -0
- {weaviate_haystack-7.2.0 → weaviate_haystack-7.3.0}/src/haystack_integrations/components/retrievers/py.typed +0 -0
- {weaviate_haystack-7.2.0 → weaviate_haystack-7.3.0}/src/haystack_integrations/components/retrievers/weaviate/__init__.py +0 -0
- {weaviate_haystack-7.2.0 → weaviate_haystack-7.3.0}/src/haystack_integrations/components/retrievers/weaviate/bm25_retriever.py +0 -0
- {weaviate_haystack-7.2.0 → weaviate_haystack-7.3.0}/src/haystack_integrations/components/retrievers/weaviate/embedding_retriever.py +0 -0
- {weaviate_haystack-7.2.0 → weaviate_haystack-7.3.0}/src/haystack_integrations/document_stores/py.typed +0 -0
- {weaviate_haystack-7.2.0 → weaviate_haystack-7.3.0}/src/haystack_integrations/document_stores/weaviate/__init__.py +0 -0
- {weaviate_haystack-7.2.0 → weaviate_haystack-7.3.0}/src/haystack_integrations/document_stores/weaviate/_filters.py +0 -0
- {weaviate_haystack-7.2.0 → weaviate_haystack-7.3.0}/src/haystack_integrations/document_stores/weaviate/auth.py +0 -0
- {weaviate_haystack-7.2.0 → weaviate_haystack-7.3.0}/tests/__init__.py +0 -0
- {weaviate_haystack-7.2.0 → weaviate_haystack-7.3.0}/tests/conftest.py +0 -0
- {weaviate_haystack-7.2.0 → weaviate_haystack-7.3.0}/tests/test_auth.py +0 -0
- {weaviate_haystack-7.2.0 → weaviate_haystack-7.3.0}/tests/test_bm25_retriever.py +0 -0
- {weaviate_haystack-7.2.0 → weaviate_haystack-7.3.0}/tests/test_bm25_retriever_async.py +0 -0
- {weaviate_haystack-7.2.0 → weaviate_haystack-7.3.0}/tests/test_document_store.py +0 -0
- {weaviate_haystack-7.2.0 → weaviate_haystack-7.3.0}/tests/test_embedding_retriever.py +0 -0
- {weaviate_haystack-7.2.0 → weaviate_haystack-7.3.0}/tests/test_embedding_retriever_async.py +0 -0
- {weaviate_haystack-7.2.0 → weaviate_haystack-7.3.0}/tests/test_files/robot1.jpg +0 -0
- {weaviate_haystack-7.2.0 → weaviate_haystack-7.3.0}/tests/test_filters.py +0 -0
- {weaviate_haystack-7.2.0 → weaviate_haystack-7.3.0}/tests/test_hybrid_retriever_async.py +0 -0
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [integrations/weaviate-v7.2.0] - 2026-03-06
|
|
4
|
+
|
|
5
|
+
### 🐛 Bug Fixes
|
|
6
|
+
|
|
7
|
+
- Remove unnecessary connection test and add `close`/`close_async` methods to `WeaviateDocumentStore` (#2891)
|
|
8
|
+
|
|
9
|
+
### 📚 Documentation
|
|
10
|
+
|
|
11
|
+
- Simplify pydoc configs (#2855)
|
|
12
|
+
|
|
13
|
+
### 🧪 Testing
|
|
14
|
+
|
|
15
|
+
- Replacing each `DocumentStore` specific tests and used the generalised ones from `haystack.testing.document_store` (#2812)
|
|
16
|
+
- Fix Weaviate tests to include grpc_config (#2871)
|
|
17
|
+
|
|
18
|
+
|
|
3
19
|
## [integrations/weaviate-v7.1.0] - 2026-01-29
|
|
4
20
|
|
|
5
21
|
### 🚀 Features
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: weaviate-haystack
|
|
3
|
-
Version: 7.
|
|
3
|
+
Version: 7.3.0
|
|
4
4
|
Summary: An integration of Weaviate vector database 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/weaviate/README.md
|
|
@@ -20,7 +20,7 @@ Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
|
20
20
|
Requires-Python: >=3.10
|
|
21
21
|
Requires-Dist: haystack-ai>=2.24.0
|
|
22
22
|
Requires-Dist: python-dateutil
|
|
23
|
-
Requires-Dist: weaviate-client>=4.
|
|
23
|
+
Requires-Dist: weaviate-client>=4.20
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
|
|
26
26
|
# weaviate-haystack
|
|
@@ -23,7 +23,7 @@ class WeaviateHybridRetriever:
|
|
|
23
23
|
document_store: WeaviateDocumentStore,
|
|
24
24
|
filters: dict[str, Any] | None = None,
|
|
25
25
|
top_k: int = 10,
|
|
26
|
-
alpha: float
|
|
26
|
+
alpha: float = 0.7,
|
|
27
27
|
max_vector_distance: float | None = None,
|
|
28
28
|
filter_policy: str | FilterPolicy = FilterPolicy.REPLACE,
|
|
29
29
|
):
|
|
@@ -46,7 +46,7 @@ class WeaviateHybridRetriever:
|
|
|
46
46
|
- `alpha = 1.0`: only vector similarity scoring is used.
|
|
47
47
|
- Values in between blend the two; higher values favor the vector score, lower values favor BM25.
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
By default, 0.7 is used which is the Weaviate server default.
|
|
50
50
|
|
|
51
51
|
See the official Weaviate docs on Hybrid Search parameters for more details:
|
|
52
52
|
- [Hybrid search parameters](https://weaviate.io/developers/weaviate/search/hybrid#parameters)
|
|
@@ -66,7 +66,7 @@ class WeaviateHybridRetriever:
|
|
|
66
66
|
Policy to determine how filters are applied.
|
|
67
67
|
"""
|
|
68
68
|
|
|
69
|
-
if
|
|
69
|
+
if not 0.0 <= alpha <= 1.0:
|
|
70
70
|
msg = f"alpha ({alpha}) must be in the range [0.0, 1.0]"
|
|
71
71
|
raise ValueError(msg)
|
|
72
72
|
|
|
@@ -6,7 +6,7 @@ import base64
|
|
|
6
6
|
import datetime
|
|
7
7
|
import json
|
|
8
8
|
from dataclasses import asdict
|
|
9
|
-
from typing import Any
|
|
9
|
+
from typing import Any, NoReturn
|
|
10
10
|
|
|
11
11
|
from haystack import logging
|
|
12
12
|
from haystack.core.serialization import default_from_dict, default_to_dict
|
|
@@ -22,9 +22,13 @@ from weaviate.collections.classes.aggregate import (
|
|
|
22
22
|
_MetricsInteger,
|
|
23
23
|
_MetricsNumber,
|
|
24
24
|
)
|
|
25
|
-
from weaviate.collections.classes.
|
|
25
|
+
from weaviate.collections.classes.batch import ErrorObject
|
|
26
|
+
from weaviate.collections.classes.internal import Object
|
|
27
|
+
from weaviate.collections.collection import Collection, CollectionAsync
|
|
28
|
+
from weaviate.collections.iterator import _ObjectAIterator, _ObjectIterator
|
|
26
29
|
from weaviate.config import AdditionalConfig
|
|
27
30
|
from weaviate.embedded import EmbeddedOptions
|
|
31
|
+
from weaviate.types import VECTORS
|
|
28
32
|
from weaviate.util import generate_uuid5
|
|
29
33
|
|
|
30
34
|
from ._filters import convert_filters, validate_filters
|
|
@@ -105,7 +109,7 @@ class WeaviateDocumentStore:
|
|
|
105
109
|
additional_config: AdditionalConfig | None = None,
|
|
106
110
|
grpc_port: int = 50051,
|
|
107
111
|
grpc_secure: bool = False,
|
|
108
|
-
):
|
|
112
|
+
) -> None:
|
|
109
113
|
"""
|
|
110
114
|
Create a new instance of WeaviateDocumentStore and connects to the Weaviate instance.
|
|
111
115
|
|
|
@@ -267,7 +271,7 @@ class WeaviateDocumentStore:
|
|
|
267
271
|
return self._async_client
|
|
268
272
|
|
|
269
273
|
@property
|
|
270
|
-
def collection(self):
|
|
274
|
+
def collection(self) -> Collection[dict[str, Any], None]:
|
|
271
275
|
if self._collection:
|
|
272
276
|
return self._collection
|
|
273
277
|
|
|
@@ -276,7 +280,7 @@ class WeaviateDocumentStore:
|
|
|
276
280
|
return self._collection
|
|
277
281
|
|
|
278
282
|
@property
|
|
279
|
-
async def async_collection(self):
|
|
283
|
+
async def async_collection(self) -> CollectionAsync[dict[str, Any], None]:
|
|
280
284
|
if self._async_collection:
|
|
281
285
|
return self._async_collection
|
|
282
286
|
|
|
@@ -352,6 +356,14 @@ class WeaviateDocumentStore:
|
|
|
352
356
|
total = self.collection.aggregate.over_all(total_count=True).total_count
|
|
353
357
|
return total if total else 0
|
|
354
358
|
|
|
359
|
+
async def count_documents_async(self) -> int:
|
|
360
|
+
"""
|
|
361
|
+
Asynchronously returns the number of documents present in the DocumentStore.
|
|
362
|
+
"""
|
|
363
|
+
collection = await self.async_collection
|
|
364
|
+
total = (await collection.aggregate.over_all(total_count=True)).total_count
|
|
365
|
+
return total if total else 0
|
|
366
|
+
|
|
355
367
|
def count_documents_by_filter(self, filters: dict[str, Any]) -> int:
|
|
356
368
|
"""
|
|
357
369
|
Returns the number of documents that match the provided filters.
|
|
@@ -755,7 +767,7 @@ class WeaviateDocumentStore:
|
|
|
755
767
|
return data
|
|
756
768
|
|
|
757
769
|
@staticmethod
|
|
758
|
-
def _to_document(data:
|
|
770
|
+
def _to_document(data: Object[dict[str, Any], None]) -> Document:
|
|
759
771
|
"""
|
|
760
772
|
Converts a data object read from Weaviate into a Document.
|
|
761
773
|
"""
|
|
@@ -793,7 +805,7 @@ class WeaviateDocumentStore:
|
|
|
793
805
|
|
|
794
806
|
return Document.from_dict(document_data)
|
|
795
807
|
|
|
796
|
-
def _query(self) ->
|
|
808
|
+
def _query(self) -> _ObjectIterator[dict[str, Any], None]:
|
|
797
809
|
properties = [p.name for p in self.collection.config.get().properties]
|
|
798
810
|
try:
|
|
799
811
|
result = self.collection.iterator(include_vector=True, return_properties=properties)
|
|
@@ -802,7 +814,17 @@ class WeaviateDocumentStore:
|
|
|
802
814
|
raise DocumentStoreError(msg) from e
|
|
803
815
|
return result
|
|
804
816
|
|
|
805
|
-
def
|
|
817
|
+
async def _query_async(self) -> _ObjectAIterator[dict[str, Any], None]:
|
|
818
|
+
collection = await self.async_collection
|
|
819
|
+
properties = [p.name for p in (await collection.config.get()).properties]
|
|
820
|
+
try:
|
|
821
|
+
result = collection.iterator(include_vector=True, return_properties=properties)
|
|
822
|
+
except weaviate.exceptions.WeaviateQueryError as e:
|
|
823
|
+
msg = f"Failed to query documents in Weaviate. Error: {e.message}"
|
|
824
|
+
raise DocumentStoreError(msg) from e
|
|
825
|
+
return result
|
|
826
|
+
|
|
827
|
+
def _query_with_filters(self, filters: dict[str, Any]) -> list[Object[dict[str, Any], None]]:
|
|
806
828
|
properties = [p.name for p in self.collection.config.get().properties]
|
|
807
829
|
# When querying with filters we need to paginate using limit and offset as using
|
|
808
830
|
# a cursor with after is not possible. See the official docs:
|
|
@@ -833,6 +855,38 @@ class WeaviateDocumentStore:
|
|
|
833
855
|
offset += DEFAULT_QUERY_LIMIT
|
|
834
856
|
return result
|
|
835
857
|
|
|
858
|
+
async def _query_with_filters_async(self, filters: dict[str, Any]) -> list[Object[dict[str, Any], None]]:
|
|
859
|
+
collection = await self.async_collection
|
|
860
|
+
properties = [p.name for p in (await collection.config.get()).properties]
|
|
861
|
+
# When querying with filters we need to paginate using limit and offset as using
|
|
862
|
+
# a cursor with after is not possible. See the official docs:
|
|
863
|
+
# https://weaviate.io/developers/weaviate/api/graphql/additional-operators#cursor-with-after
|
|
864
|
+
#
|
|
865
|
+
# Nonetheless there's also another issue, paginating with limit and offset is not efficient
|
|
866
|
+
# and it's still restricted by the QUERY_MAXIMUM_RESULTS environment variable.
|
|
867
|
+
# If the sum of limit and offset is greater than QUERY_MAXIMUM_RESULTS an error is raised.
|
|
868
|
+
# See the official docs for more:
|
|
869
|
+
# https://weaviate.io/developers/weaviate/api/graphql/additional-operators#performance-considerations
|
|
870
|
+
offset = 0
|
|
871
|
+
partial_result = None
|
|
872
|
+
result = []
|
|
873
|
+
# Keep querying until we get all documents matching the filters
|
|
874
|
+
while partial_result is None or len(partial_result.objects) == DEFAULT_QUERY_LIMIT:
|
|
875
|
+
try:
|
|
876
|
+
partial_result = await collection.query.fetch_objects(
|
|
877
|
+
filters=convert_filters(filters),
|
|
878
|
+
include_vector=True,
|
|
879
|
+
limit=DEFAULT_QUERY_LIMIT,
|
|
880
|
+
offset=offset,
|
|
881
|
+
return_properties=properties,
|
|
882
|
+
)
|
|
883
|
+
except weaviate.exceptions.WeaviateQueryError as e:
|
|
884
|
+
msg = f"Failed to query documents in Weaviate. Error: {e.message}"
|
|
885
|
+
raise DocumentStoreError(msg) from e
|
|
886
|
+
result.extend(partial_result.objects)
|
|
887
|
+
offset += DEFAULT_QUERY_LIMIT
|
|
888
|
+
return result
|
|
889
|
+
|
|
836
890
|
def filter_documents(self, filters: dict[str, Any] | None = None) -> list[Document]:
|
|
837
891
|
"""
|
|
838
892
|
Returns the documents that match the filters provided.
|
|
@@ -852,9 +906,48 @@ class WeaviateDocumentStore:
|
|
|
852
906
|
result = []
|
|
853
907
|
if filters:
|
|
854
908
|
result = self._query_with_filters(filters)
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
return [WeaviateDocumentStore._to_document(doc) for doc in
|
|
909
|
+
return [WeaviateDocumentStore._to_document(doc) for doc in result]
|
|
910
|
+
result_iter = self._query()
|
|
911
|
+
return [WeaviateDocumentStore._to_document(doc) for doc in result_iter]
|
|
912
|
+
|
|
913
|
+
async def filter_documents_async(self, filters: dict[str, Any] | None = None) -> list[Document]:
|
|
914
|
+
"""
|
|
915
|
+
Asynchronously returns the documents that match the filters provided.
|
|
916
|
+
|
|
917
|
+
For a detailed specification of the filters, refer to the
|
|
918
|
+
DocumentStore.filter_documents() protocol documentation.
|
|
919
|
+
|
|
920
|
+
Note: The ``contains`` filter operator is case-sensitive (substring
|
|
921
|
+
matching). For case-insensitive matching, normalize the value before
|
|
922
|
+
building the filter.
|
|
923
|
+
|
|
924
|
+
:param filters: The filters to apply to the document list.
|
|
925
|
+
:returns: A list of Documents that match the given filters.
|
|
926
|
+
"""
|
|
927
|
+
validate_filters(filters)
|
|
928
|
+
|
|
929
|
+
result = []
|
|
930
|
+
if filters:
|
|
931
|
+
result = await self._query_with_filters_async(filters)
|
|
932
|
+
return [WeaviateDocumentStore._to_document(doc) for doc in result]
|
|
933
|
+
result_iter = await self._query_async()
|
|
934
|
+
return [WeaviateDocumentStore._to_document(doc) async for doc in result_iter]
|
|
935
|
+
|
|
936
|
+
@staticmethod
|
|
937
|
+
def _handle_failed_objects(failed_objects: list[ErrorObject]) -> NoReturn:
|
|
938
|
+
# We fall back to use the UUID if the _original_id is not present, this is just to be
|
|
939
|
+
mapped_objects = {}
|
|
940
|
+
for obj in failed_objects:
|
|
941
|
+
properties = obj.object_.properties or {}
|
|
942
|
+
# We get the object uuid just in case the _original_id is not present.
|
|
943
|
+
# That's extremely unlikely to happen but let's stay on the safe side.
|
|
944
|
+
id_ = properties.get("_original_id", obj.object_.uuid)
|
|
945
|
+
mapped_objects[id_] = obj.message
|
|
946
|
+
|
|
947
|
+
msg = "\n".join(
|
|
948
|
+
[f"Failed to write object with id '{id_}'. Error: '{message}'" for id_, message in mapped_objects.items()]
|
|
949
|
+
)
|
|
950
|
+
raise DocumentStoreError(msg)
|
|
858
951
|
|
|
859
952
|
def _batch_write(self, documents: list[Document]) -> int:
|
|
860
953
|
"""
|
|
@@ -876,22 +969,35 @@ class WeaviateDocumentStore:
|
|
|
876
969
|
vector=doc.embedding,
|
|
877
970
|
)
|
|
878
971
|
if failed_objects := self.client.batch.failed_objects:
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
972
|
+
self._handle_failed_objects(failed_objects)
|
|
973
|
+
|
|
974
|
+
# If the document already exists we get no status message back from Weaviate.
|
|
975
|
+
# So we assume that all Documents were written.
|
|
976
|
+
return len(documents)
|
|
977
|
+
|
|
978
|
+
async def _batch_write_async(self, documents: list[Document]) -> int:
|
|
979
|
+
"""
|
|
980
|
+
Asynchronously writes document to Weaviate in batches.
|
|
981
|
+
Documents with the same id will be overwritten.
|
|
982
|
+
Raises in case of errors.
|
|
983
|
+
"""
|
|
984
|
+
client = await self.async_client
|
|
985
|
+
|
|
986
|
+
async with client.batch.stream() as batch:
|
|
987
|
+
for doc in documents:
|
|
988
|
+
if not isinstance(doc, Document):
|
|
989
|
+
msg = f"Expected a Document, got '{type(doc)}' instead."
|
|
990
|
+
raise ValueError(msg)
|
|
991
|
+
|
|
992
|
+
await batch.add_object(
|
|
993
|
+
properties=WeaviateDocumentStore._to_data_object(doc),
|
|
994
|
+
collection=(await self.async_collection).name,
|
|
995
|
+
uuid=generate_uuid5(doc.id),
|
|
996
|
+
vector=doc.embedding,
|
|
997
|
+
)
|
|
998
|
+
|
|
999
|
+
if failed_objects := client.batch.failed_objects:
|
|
1000
|
+
self._handle_failed_objects(failed_objects)
|
|
895
1001
|
|
|
896
1002
|
# If the document already exists we get no status message back from Weaviate.
|
|
897
1003
|
# So we assume that all Documents were written.
|
|
@@ -931,6 +1037,43 @@ class WeaviateDocumentStore:
|
|
|
931
1037
|
raise DuplicateDocumentError(msg)
|
|
932
1038
|
return written
|
|
933
1039
|
|
|
1040
|
+
async def _write_async(self, documents: list[Document], policy: DuplicatePolicy) -> int:
|
|
1041
|
+
"""
|
|
1042
|
+
Asynchronously writes documents to Weaviate using the specified policy.
|
|
1043
|
+
This doesn't use the batch API, so it's slower than _batch_write.
|
|
1044
|
+
If policy is set to SKIP it will skip any document that already exists.
|
|
1045
|
+
If policy is set to FAIL it will raise an exception if any of the documents already exists.
|
|
1046
|
+
"""
|
|
1047
|
+
collection = await self.async_collection
|
|
1048
|
+
|
|
1049
|
+
duplicate_errors_ids = []
|
|
1050
|
+
for doc in documents:
|
|
1051
|
+
if not isinstance(doc, Document):
|
|
1052
|
+
msg = f"Expected a Document, got '{type(doc)}' instead."
|
|
1053
|
+
raise ValueError(msg)
|
|
1054
|
+
|
|
1055
|
+
if policy == DuplicatePolicy.SKIP and await (await self.async_collection).data.exists(
|
|
1056
|
+
uuid=generate_uuid5(doc.id)
|
|
1057
|
+
):
|
|
1058
|
+
# This Document already exists, continue
|
|
1059
|
+
continue
|
|
1060
|
+
|
|
1061
|
+
try:
|
|
1062
|
+
await collection.data.insert(
|
|
1063
|
+
uuid=generate_uuid5(doc.id),
|
|
1064
|
+
properties=WeaviateDocumentStore._to_data_object(doc),
|
|
1065
|
+
vector=doc.embedding,
|
|
1066
|
+
)
|
|
1067
|
+
|
|
1068
|
+
except weaviate.exceptions.UnexpectedStatusCodeError:
|
|
1069
|
+
if policy == DuplicatePolicy.FAIL:
|
|
1070
|
+
duplicate_errors_ids.append(doc.id)
|
|
1071
|
+
|
|
1072
|
+
if duplicate_errors_ids:
|
|
1073
|
+
msg = f"IDs '{', '.join(duplicate_errors_ids)}' already exist in the document store."
|
|
1074
|
+
raise DuplicateDocumentError(msg)
|
|
1075
|
+
return len(documents)
|
|
1076
|
+
|
|
934
1077
|
def write_documents(self, documents: list[Document], policy: DuplicatePolicy = DuplicatePolicy.NONE) -> int:
|
|
935
1078
|
"""
|
|
936
1079
|
Writes documents to Weaviate using the specified policy.
|
|
@@ -939,12 +1082,59 @@ class WeaviateDocumentStore:
|
|
|
939
1082
|
We can't use the batch API for other policies as it doesn't return any information whether the document
|
|
940
1083
|
already exists or not. That prevents us from returning errors when using the FAIL policy or skipping a
|
|
941
1084
|
Document when using the SKIP policy.
|
|
1085
|
+
|
|
1086
|
+
:param documents:
|
|
1087
|
+
A list of documents to write into the document store.
|
|
1088
|
+
:param policy:
|
|
1089
|
+
DuplicatePolicy to apply when a document with the same ID already exists in the document store.
|
|
1090
|
+
|
|
1091
|
+
:raises ValueError:
|
|
1092
|
+
When input is not valid.
|
|
1093
|
+
:raises DuplicateDocumentError:
|
|
1094
|
+
When duplicate documents are found and using a FAIL policy.
|
|
1095
|
+
:raises DocumentStoreError:
|
|
1096
|
+
When documents have failed to be batch written.
|
|
1097
|
+
|
|
1098
|
+
:returns:
|
|
1099
|
+
The number of documents written.
|
|
942
1100
|
"""
|
|
943
1101
|
if policy in [DuplicatePolicy.NONE, DuplicatePolicy.OVERWRITE]:
|
|
944
1102
|
return self._batch_write(documents)
|
|
945
1103
|
|
|
946
1104
|
return self._write(documents, policy)
|
|
947
1105
|
|
|
1106
|
+
async def write_documents_async(
|
|
1107
|
+
self, documents: list[Document], policy: DuplicatePolicy = DuplicatePolicy.NONE
|
|
1108
|
+
) -> int:
|
|
1109
|
+
"""
|
|
1110
|
+
Asynchronously writes documents to Weaviate using the specified policy.
|
|
1111
|
+
We recommend using a OVERWRITE policy as it's faster than other policies for Weaviate since it uses
|
|
1112
|
+
the batch API.
|
|
1113
|
+
We can't use the batch API for other policies as it doesn't return any information whether the document
|
|
1114
|
+
already exists or not. That prevents us from returning errors when using the FAIL policy or skipping a
|
|
1115
|
+
Document when using the SKIP policy.
|
|
1116
|
+
|
|
1117
|
+
|
|
1118
|
+
:param documents:
|
|
1119
|
+
A list of documents to write into the document store.
|
|
1120
|
+
:param policy:
|
|
1121
|
+
DuplicatePolicy to apply when a document with the same ID already exists in the document store.
|
|
1122
|
+
|
|
1123
|
+
:raises ValueError:
|
|
1124
|
+
When input is not valid.
|
|
1125
|
+
:raises DuplicateDocumentError:
|
|
1126
|
+
When duplicate documents are found and using a FAIL policy.
|
|
1127
|
+
:raises DocumentStoreError:
|
|
1128
|
+
When documents have failed to be batch written.
|
|
1129
|
+
|
|
1130
|
+
:returns:
|
|
1131
|
+
The number of documents written.
|
|
1132
|
+
"""
|
|
1133
|
+
if policy in [DuplicatePolicy.NONE, DuplicatePolicy.OVERWRITE]:
|
|
1134
|
+
return await self._batch_write_async(documents)
|
|
1135
|
+
|
|
1136
|
+
return await self._write_async(documents, policy)
|
|
1137
|
+
|
|
948
1138
|
def delete_documents(self, document_ids: list[str]) -> None:
|
|
949
1139
|
"""
|
|
950
1140
|
Deletes all documents with matching document_ids from the DocumentStore.
|
|
@@ -954,6 +1144,16 @@ class WeaviateDocumentStore:
|
|
|
954
1144
|
weaviate_ids = [generate_uuid5(doc_id) for doc_id in document_ids]
|
|
955
1145
|
self.collection.data.delete_many(where=weaviate.classes.query.Filter.by_id().contains_any(weaviate_ids))
|
|
956
1146
|
|
|
1147
|
+
async def delete_documents_async(self, document_ids: list[str]) -> None:
|
|
1148
|
+
"""
|
|
1149
|
+
Asynchronously deletes all documents with matching document_ids from the DocumentStore.
|
|
1150
|
+
|
|
1151
|
+
:param document_ids: The object_ids to delete.
|
|
1152
|
+
"""
|
|
1153
|
+
weaviate_ids = [generate_uuid5(doc_id) for doc_id in document_ids]
|
|
1154
|
+
collection = await self.async_collection
|
|
1155
|
+
await collection.data.delete_many(where=weaviate.classes.query.Filter.by_id().contains_any(weaviate_ids))
|
|
1156
|
+
|
|
957
1157
|
def delete_all_documents(self, *, recreate_index: bool = False, batch_size: int = 1000) -> None:
|
|
958
1158
|
"""
|
|
959
1159
|
Deletes all documents in a collection.
|
|
@@ -1003,6 +1203,57 @@ class WeaviateDocumentStore:
|
|
|
1003
1203
|
"Make sure to specify a deletion `batch_size` which is less than `QUERY_MAXIMUM_RESULTS`.",
|
|
1004
1204
|
)
|
|
1005
1205
|
|
|
1206
|
+
async def delete_all_documents_async(self, *, recreate_index: bool = False, batch_size: int = 1000) -> None:
|
|
1207
|
+
"""
|
|
1208
|
+
Asynchronously deletes all documents in a collection.
|
|
1209
|
+
|
|
1210
|
+
If recreate_index is False, it keeps the collection but deletes documents iteratively.
|
|
1211
|
+
If recreate_index is True, the collection is dropped and faithfully recreated.
|
|
1212
|
+
This is recommended for performance reasons.
|
|
1213
|
+
|
|
1214
|
+
:param recreate_index: Use drop and recreate strategy. (recommended for performance)
|
|
1215
|
+
:param batch_size: Only relevant if recreate_index is false. Defines the deletion batch size.
|
|
1216
|
+
Note that this parameter needs to be less or equal to the set `QUERY_MAXIMUM_RESULTS` variable
|
|
1217
|
+
set for the weaviate deployment (default is 10000).
|
|
1218
|
+
Reference: https://docs.weaviate.io/weaviate/manage-objects/delete#delete-all-objects
|
|
1219
|
+
"""
|
|
1220
|
+
client = await self.async_client
|
|
1221
|
+
|
|
1222
|
+
if recreate_index:
|
|
1223
|
+
# get current up-to-date config from server, so we can recreate the collection faithfully
|
|
1224
|
+
cfg = (await client.collections.get(self._collection_settings["class"]).config.get()).to_dict()
|
|
1225
|
+
class_name = cfg.get("class", self._collection_settings["class"])
|
|
1226
|
+
|
|
1227
|
+
await client.collections.delete(class_name)
|
|
1228
|
+
await client.collections.create_from_dict(cfg)
|
|
1229
|
+
|
|
1230
|
+
self._collection_settings = cfg
|
|
1231
|
+
self._async_collection = client.collections.get(class_name)
|
|
1232
|
+
return
|
|
1233
|
+
|
|
1234
|
+
uuids = []
|
|
1235
|
+
batch_size = max(1, int(batch_size))
|
|
1236
|
+
|
|
1237
|
+
collection = await self.async_collection
|
|
1238
|
+
async for obj in collection.iterator(return_properties=[], include_vector=False):
|
|
1239
|
+
uuids.append(obj.uuid)
|
|
1240
|
+
if len(uuids) >= batch_size:
|
|
1241
|
+
res = await collection.data.delete_many(where=weaviate.classes.query.Filter.by_id().contains_any(uuids))
|
|
1242
|
+
if res.successful < len(uuids):
|
|
1243
|
+
logger.warning(
|
|
1244
|
+
"Not all documents in the batch have been deleted. "
|
|
1245
|
+
"Make sure to specify a deletion `batch_size` which is less than `QUERY_MAXIMUM_RESULTS`.",
|
|
1246
|
+
)
|
|
1247
|
+
uuids.clear()
|
|
1248
|
+
|
|
1249
|
+
if uuids:
|
|
1250
|
+
res = await collection.data.delete_many(where=weaviate.classes.query.Filter.by_id().contains_any(uuids))
|
|
1251
|
+
if res.successful < len(uuids):
|
|
1252
|
+
logger.warning(
|
|
1253
|
+
"Not all documents have been deleted. "
|
|
1254
|
+
"Make sure to specify a deletion `batch_size` which is less than `QUERY_MAXIMUM_RESULTS`.",
|
|
1255
|
+
)
|
|
1256
|
+
|
|
1006
1257
|
def delete_by_filter(self, filters: dict[str, Any]) -> int:
|
|
1007
1258
|
"""
|
|
1008
1259
|
Deletes all documents that match the provided filters.
|
|
@@ -1095,11 +1346,9 @@ class WeaviateDocumentStore:
|
|
|
1095
1346
|
|
|
1096
1347
|
# Update the object, preserving the vector
|
|
1097
1348
|
# Get the vector from the object to preserve it during replace
|
|
1098
|
-
vector = None
|
|
1099
|
-
if isinstance(obj.vector, list):
|
|
1349
|
+
vector: VECTORS | None = None
|
|
1350
|
+
if isinstance(obj.vector, (list, dict)):
|
|
1100
1351
|
vector = obj.vector
|
|
1101
|
-
elif isinstance(obj.vector, dict):
|
|
1102
|
-
vector = obj.vector.get("default")
|
|
1103
1352
|
|
|
1104
1353
|
self.collection.data.replace(
|
|
1105
1354
|
uuid=obj.uuid,
|
|
@@ -1191,11 +1440,9 @@ class WeaviateDocumentStore:
|
|
|
1191
1440
|
|
|
1192
1441
|
# Update the object, preserving the vector
|
|
1193
1442
|
# Get the vector from the object to preserve it during replace
|
|
1194
|
-
vector = None
|
|
1195
|
-
if isinstance(obj.vector, list):
|
|
1443
|
+
vector: VECTORS | None = None
|
|
1444
|
+
if isinstance(obj.vector, (list, dict)):
|
|
1196
1445
|
vector = obj.vector
|
|
1197
|
-
elif isinstance(obj.vector, dict):
|
|
1198
|
-
vector = obj.vector.get("default")
|
|
1199
1446
|
|
|
1200
1447
|
await collection.data.replace(
|
|
1201
1448
|
uuid=obj.uuid,
|
|
@@ -1322,7 +1569,7 @@ class WeaviateDocumentStore:
|
|
|
1322
1569
|
query_embedding: list[float],
|
|
1323
1570
|
filters: dict[str, Any] | None = None,
|
|
1324
1571
|
top_k: int | None = None,
|
|
1325
|
-
alpha: float
|
|
1572
|
+
alpha: float = 0.7,
|
|
1326
1573
|
max_vector_distance: float | None = None,
|
|
1327
1574
|
) -> list[Document]:
|
|
1328
1575
|
properties = [p.name for p in self.collection.config.get().properties]
|
|
@@ -1347,7 +1594,7 @@ class WeaviateDocumentStore:
|
|
|
1347
1594
|
query_embedding: list[float],
|
|
1348
1595
|
filters: dict[str, Any] | None = None,
|
|
1349
1596
|
top_k: int | None = None,
|
|
1350
|
-
alpha: float
|
|
1597
|
+
alpha: float = 0.7,
|
|
1351
1598
|
max_vector_distance: float | None = None,
|
|
1352
1599
|
) -> list[Document]:
|
|
1353
1600
|
collection = await self.async_collection
|
|
@@ -2,11 +2,15 @@
|
|
|
2
2
|
#
|
|
3
3
|
# SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
|
|
5
|
+
import logging
|
|
5
6
|
from collections.abc import AsyncGenerator
|
|
7
|
+
from pathlib import Path
|
|
6
8
|
|
|
7
9
|
import pytest
|
|
8
10
|
import pytest_asyncio
|
|
11
|
+
from haystack.dataclasses.byte_stream import ByteStream
|
|
9
12
|
from haystack.dataclasses.document import Document
|
|
13
|
+
from haystack.document_stores.errors import DocumentStoreError
|
|
10
14
|
|
|
11
15
|
from haystack_integrations.document_stores.weaviate import WeaviateDocumentStore
|
|
12
16
|
from haystack_integrations.document_stores.weaviate.document_store import DOCUMENT_COLLECTION_PROPERTIES
|
|
@@ -31,8 +35,7 @@ class TestWeaviateDocumentStoreAsync:
|
|
|
31
35
|
collection_settings=collection_settings,
|
|
32
36
|
)
|
|
33
37
|
yield store
|
|
34
|
-
store.
|
|
35
|
-
store.close()
|
|
38
|
+
await (await store.async_client).collections.delete(collection_settings["class"])
|
|
36
39
|
await store.close_async()
|
|
37
40
|
|
|
38
41
|
@pytest.mark.asyncio
|
|
@@ -50,20 +53,68 @@ class TestWeaviateDocumentStoreAsync:
|
|
|
50
53
|
assert await document_store.async_client is not None
|
|
51
54
|
assert await document_store.async_collection is not None
|
|
52
55
|
|
|
53
|
-
document_store.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
assert await document_store.count_documents_async() == 0
|
|
57
|
+
|
|
58
|
+
@pytest.mark.asyncio
|
|
59
|
+
async def test_filter_documents_with_blob_data_async(
|
|
60
|
+
self, document_store: WeaviateDocumentStore, test_files_path: Path
|
|
61
|
+
) -> None:
|
|
62
|
+
image = ByteStream.from_file_path(test_files_path / "robot1.jpg", mime_type="image/jpeg")
|
|
63
|
+
doc = Document(content="test doc", blob=image)
|
|
64
|
+
assert await document_store.write_documents_async([doc]) == 1
|
|
65
|
+
|
|
66
|
+
docs = await document_store.filter_documents_async()
|
|
67
|
+
|
|
68
|
+
assert len(docs) == 1
|
|
69
|
+
assert docs[0].blob == image
|
|
70
|
+
|
|
71
|
+
@pytest.mark.asyncio
|
|
72
|
+
async def test_filter_documents_below_default_limit_async(self, document_store: WeaviateDocumentStore) -> None:
|
|
73
|
+
docs = []
|
|
74
|
+
for index in range(9998):
|
|
75
|
+
docs.append(Document(content="This is some content", meta={"index": index}))
|
|
76
|
+
await document_store.write_documents_async(docs)
|
|
77
|
+
result = await document_store.filter_documents_async(
|
|
78
|
+
{"field": "content", "operator": "==", "value": "This is some content"}
|
|
59
79
|
)
|
|
60
|
-
filters = {"field": "content", "operator": "==", "value": "Haskell"}
|
|
61
80
|
|
|
62
|
-
assert
|
|
81
|
+
assert len(result) == 9998
|
|
82
|
+
|
|
83
|
+
@pytest.mark.asyncio
|
|
84
|
+
async def test_filter_documents_over_default_limit(self, document_store: WeaviateDocumentStore) -> None:
|
|
85
|
+
docs = []
|
|
86
|
+
for index in range(10000):
|
|
87
|
+
docs.append(Document(content="This is some content", meta={"index": index}))
|
|
88
|
+
await document_store.write_documents_async(docs)
|
|
89
|
+
with pytest.raises(DocumentStoreError):
|
|
90
|
+
await document_store.filter_documents_async(
|
|
91
|
+
{"field": "content", "operator": "==", "value": "This is some content"}
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
@pytest.mark.asyncio
|
|
95
|
+
async def test_write_documents_async(self, document_store: WeaviateDocumentStore) -> None:
|
|
96
|
+
"""
|
|
97
|
+
Test write_documents() with default policy overwrites existing documents.
|
|
98
|
+
"""
|
|
99
|
+
doc = Document(content="test doc")
|
|
100
|
+
assert await document_store.write_documents_async([doc]) == 1
|
|
101
|
+
assert await document_store.count_documents_async() == 1
|
|
102
|
+
|
|
103
|
+
doc.content = "test doc 2"
|
|
104
|
+
assert await document_store.write_documents_async([doc]) == 1
|
|
105
|
+
assert await document_store.count_documents_async() == 1
|
|
106
|
+
|
|
107
|
+
@pytest.mark.asyncio
|
|
108
|
+
async def test_write_documents_with_blob_data_async(
|
|
109
|
+
self, document_store: WeaviateDocumentStore, test_files_path: Path
|
|
110
|
+
) -> None:
|
|
111
|
+
image = ByteStream.from_file_path(test_files_path / "robot1.jpg", mime_type="image/jpeg")
|
|
112
|
+
doc = Document(content="test doc", blob=image)
|
|
113
|
+
assert await document_store.write_documents_async([doc]) == 1
|
|
63
114
|
|
|
64
115
|
@pytest.mark.asyncio
|
|
65
116
|
async def test_bm25_retrieval_async(self, document_store):
|
|
66
|
-
document_store.
|
|
117
|
+
await document_store.write_documents_async(
|
|
67
118
|
[
|
|
68
119
|
Document(content="Haskell is a functional programming language"),
|
|
69
120
|
Document(content="Lisp is a functional programming language"),
|
|
@@ -80,7 +131,7 @@ class TestWeaviateDocumentStoreAsync:
|
|
|
80
131
|
|
|
81
132
|
@pytest.mark.asyncio
|
|
82
133
|
async def test_bm25_retrieval_async_with_filters(self, document_store):
|
|
83
|
-
document_store.
|
|
134
|
+
await document_store.write_documents_async(
|
|
84
135
|
[
|
|
85
136
|
Document(content="Haskell is a functional programming language"),
|
|
86
137
|
Document(content="Lisp is a functional programming language"),
|
|
@@ -96,7 +147,7 @@ class TestWeaviateDocumentStoreAsync:
|
|
|
96
147
|
|
|
97
148
|
@pytest.mark.asyncio
|
|
98
149
|
async def test_embedding_retrieval_async(self, document_store):
|
|
99
|
-
document_store.
|
|
150
|
+
await document_store.write_documents_async(
|
|
100
151
|
[
|
|
101
152
|
Document(content="The document", embedding=[1.0, 1.0, 1.0, 1.0]),
|
|
102
153
|
Document(content="Another document", embedding=[0.8, 0.8, 0.8, 1.0]),
|
|
@@ -112,7 +163,7 @@ class TestWeaviateDocumentStoreAsync:
|
|
|
112
163
|
|
|
113
164
|
@pytest.mark.asyncio
|
|
114
165
|
async def test_embedding_retrieval_async_with_filters(self, document_store):
|
|
115
|
-
document_store.
|
|
166
|
+
await document_store.write_documents_async(
|
|
116
167
|
[
|
|
117
168
|
Document(content="The document I want", embedding=[1.0, 1.0, 1.0, 1.0]),
|
|
118
169
|
Document(content="Another document", embedding=[0.8, 0.8, 0.8, 1.0]),
|
|
@@ -134,7 +185,7 @@ class TestWeaviateDocumentStoreAsync:
|
|
|
134
185
|
|
|
135
186
|
@pytest.mark.asyncio
|
|
136
187
|
async def test_hybrid_retrieval_async(self, document_store):
|
|
137
|
-
document_store.
|
|
188
|
+
await document_store.write_documents_async(
|
|
138
189
|
[
|
|
139
190
|
Document(content="Haskell is a functional programming language", embedding=[1.0, 0.8, 0.2, 0.1]),
|
|
140
191
|
Document(content="Lisp is a functional programming language", embedding=[0.9, 0.7, 0.3, 0.2]),
|
|
@@ -154,7 +205,7 @@ class TestWeaviateDocumentStoreAsync:
|
|
|
154
205
|
|
|
155
206
|
@pytest.mark.asyncio
|
|
156
207
|
async def test_hybrid_retrieval_async_with_filters(self, document_store):
|
|
157
|
-
document_store.
|
|
208
|
+
await document_store.write_documents_async(
|
|
158
209
|
[
|
|
159
210
|
Document(content="Haskell is a functional programming language", embedding=[1.0, 0.8, 0.2, 0.1]),
|
|
160
211
|
Document(content="Lisp is a functional programming language", embedding=[0.9, 0.7, 0.3, 0.2]),
|
|
@@ -173,7 +224,7 @@ class TestWeaviateDocumentStoreAsync:
|
|
|
173
224
|
|
|
174
225
|
@pytest.mark.asyncio
|
|
175
226
|
async def test_hybrid_retrieval_async_with_alpha(self, document_store):
|
|
176
|
-
document_store.
|
|
227
|
+
await document_store.write_documents_async(
|
|
177
228
|
[
|
|
178
229
|
Document(content="Haskell is a functional programming language", embedding=[1.0, 0.8, 0.2, 0.1]),
|
|
179
230
|
Document(content="Python is an object oriented language", embedding=[0.1, 0.2, 0.8, 0.9]),
|
|
@@ -205,7 +256,7 @@ class TestWeaviateDocumentStoreAsync:
|
|
|
205
256
|
Document(content="Doc 2", meta={"category": "TypeB"}),
|
|
206
257
|
Document(content="Doc 3", meta={"category": "TypeA"}),
|
|
207
258
|
]
|
|
208
|
-
document_store.
|
|
259
|
+
await document_store.write_documents_async(docs)
|
|
209
260
|
|
|
210
261
|
# delete documents with category="TypeA"
|
|
211
262
|
deleted_count = await document_store.delete_by_filter_async(
|
|
@@ -215,7 +266,7 @@ class TestWeaviateDocumentStoreAsync:
|
|
|
215
266
|
assert document_store.count_documents() == 1
|
|
216
267
|
|
|
217
268
|
# verify only category TypeB remains
|
|
218
|
-
remaining_docs = document_store.
|
|
269
|
+
remaining_docs = await document_store.filter_documents_async()
|
|
219
270
|
assert len(remaining_docs) == 1
|
|
220
271
|
assert remaining_docs[0].meta["category"] == "TypeB"
|
|
221
272
|
|
|
@@ -226,7 +277,7 @@ class TestWeaviateDocumentStoreAsync:
|
|
|
226
277
|
Document(content="Doc 2", meta={"category": "TypeB", "status": "draft"}),
|
|
227
278
|
Document(content="Doc 3", meta={"category": "TypeA", "status": "draft"}),
|
|
228
279
|
]
|
|
229
|
-
document_store.
|
|
280
|
+
await document_store.write_documents_async(docs)
|
|
230
281
|
assert document_store.count_documents() == 3
|
|
231
282
|
|
|
232
283
|
# update status for category="TypeA" documents
|
|
@@ -236,7 +287,7 @@ class TestWeaviateDocumentStoreAsync:
|
|
|
236
287
|
assert updated_count == 2
|
|
237
288
|
|
|
238
289
|
# Verify the updates
|
|
239
|
-
published_docs = document_store.
|
|
290
|
+
published_docs = await document_store.filter_documents_async(
|
|
240
291
|
filters={"field": "meta.status", "operator": "==", "value": "published"}
|
|
241
292
|
)
|
|
242
293
|
assert len(published_docs) == 2
|
|
@@ -257,7 +308,7 @@ class TestWeaviateDocumentStoreAsync:
|
|
|
257
308
|
meta={"index": index, "status": "draft", "category": "test"},
|
|
258
309
|
)
|
|
259
310
|
)
|
|
260
|
-
document_store.
|
|
311
|
+
await document_store.write_documents_async(docs)
|
|
261
312
|
|
|
262
313
|
# update all documents should trigger pagination (3 pages)
|
|
263
314
|
updated_count = await document_store.update_by_filter_async(
|
|
@@ -266,7 +317,7 @@ class TestWeaviateDocumentStoreAsync:
|
|
|
266
317
|
)
|
|
267
318
|
assert updated_count == 250
|
|
268
319
|
|
|
269
|
-
published_docs = document_store.
|
|
320
|
+
published_docs = await document_store.filter_documents_async(
|
|
270
321
|
filters={"field": "status", "operator": "==", "value": "published"}
|
|
271
322
|
)
|
|
272
323
|
assert len(published_docs) == 250
|
|
@@ -276,6 +327,17 @@ class TestWeaviateDocumentStoreAsync:
|
|
|
276
327
|
assert "index" in doc.meta
|
|
277
328
|
assert 0 <= doc.meta["index"] < 250
|
|
278
329
|
|
|
330
|
+
@pytest.mark.asyncio
|
|
331
|
+
async def test_count_documents_async(self, document_store: WeaviateDocumentStore) -> None:
|
|
332
|
+
docs = [
|
|
333
|
+
Document(content="Doc 1", meta={"category": "TypeA"}),
|
|
334
|
+
Document(content="Doc 2", meta={"category": "TypeB"}),
|
|
335
|
+
Document(content="Doc 3", meta={"category": "TypeA"}),
|
|
336
|
+
Document(content="Doc 4", meta={"category": "TypeA"}),
|
|
337
|
+
]
|
|
338
|
+
await document_store.write_documents_async(docs)
|
|
339
|
+
assert await document_store.count_documents_async() == 4
|
|
340
|
+
|
|
279
341
|
@pytest.mark.asyncio
|
|
280
342
|
async def test_count_documents_by_filter_async(self, document_store):
|
|
281
343
|
docs = [
|
|
@@ -284,8 +346,8 @@ class TestWeaviateDocumentStoreAsync:
|
|
|
284
346
|
Document(content="Doc 3", meta={"category": "TypeA"}),
|
|
285
347
|
Document(content="Doc 4", meta={"category": "TypeA"}),
|
|
286
348
|
]
|
|
287
|
-
document_store.
|
|
288
|
-
assert document_store.
|
|
349
|
+
await document_store.write_documents_async(docs)
|
|
350
|
+
assert await document_store.count_documents_async() == 4
|
|
289
351
|
|
|
290
352
|
count = await document_store.count_documents_by_filter_async(
|
|
291
353
|
filters={"field": "meta.category", "operator": "==", "value": "TypeA"}
|
|
@@ -325,7 +387,7 @@ class TestWeaviateDocumentStoreAsync:
|
|
|
325
387
|
Document(content="Doc 3", meta={"number": 20}),
|
|
326
388
|
Document(content="Doc 4", meta={"number": 15}),
|
|
327
389
|
]
|
|
328
|
-
document_store.
|
|
390
|
+
await document_store.write_documents_async(docs)
|
|
329
391
|
|
|
330
392
|
result = await document_store.get_metadata_field_min_max_async("number")
|
|
331
393
|
assert result["min"] == 5
|
|
@@ -337,7 +399,7 @@ class TestWeaviateDocumentStoreAsync:
|
|
|
337
399
|
Document(content="Doc 1", meta={"number": 100}),
|
|
338
400
|
Document(content="Doc 2", meta={"number": 200}),
|
|
339
401
|
]
|
|
340
|
-
document_store.
|
|
402
|
+
await document_store.write_documents_async(docs)
|
|
341
403
|
|
|
342
404
|
result = await document_store.get_metadata_field_min_max_async("meta.number")
|
|
343
405
|
assert result["min"] == 100
|
|
@@ -362,7 +424,7 @@ class TestWeaviateDocumentStoreAsync:
|
|
|
362
424
|
Document(content="Doc 4", meta={"category": "TypeC", "status": "published"}),
|
|
363
425
|
Document(content="Doc 5", meta={"category": "TypeA", "status": "archived"}),
|
|
364
426
|
]
|
|
365
|
-
document_store.
|
|
427
|
+
await document_store.write_documents_async(docs)
|
|
366
428
|
|
|
367
429
|
result = await document_store.count_unique_metadata_by_filter_async(
|
|
368
430
|
filters={"field": "meta.category", "operator": "==", "value": "TypeA"}, metadata_fields=["status"]
|
|
@@ -388,7 +450,7 @@ class TestWeaviateDocumentStoreAsync:
|
|
|
388
450
|
Document(content="Doc 1", meta={"category": "TypeA"}),
|
|
389
451
|
Document(content="Doc 2", meta={"category": "TypeB"}),
|
|
390
452
|
]
|
|
391
|
-
document_store.
|
|
453
|
+
await document_store.write_documents_async(docs)
|
|
392
454
|
|
|
393
455
|
result = await document_store.count_unique_metadata_by_filter_async(
|
|
394
456
|
filters={"field": "meta.category", "operator": "in", "value": ["TypeA", "TypeB"]},
|
|
@@ -401,7 +463,7 @@ class TestWeaviateDocumentStoreAsync:
|
|
|
401
463
|
docs = [
|
|
402
464
|
Document(content="Doc 1", meta={"category": "TypeA"}),
|
|
403
465
|
]
|
|
404
|
-
document_store.
|
|
466
|
+
await document_store.write_documents_async(docs)
|
|
405
467
|
|
|
406
468
|
result = await document_store.count_unique_metadata_by_filter_async(
|
|
407
469
|
filters={"field": "meta.category", "operator": "==", "value": "NonExistent"},
|
|
@@ -426,7 +488,7 @@ class TestWeaviateDocumentStoreAsync:
|
|
|
426
488
|
Document(content="Doc 4", meta={"category": "TypeC"}),
|
|
427
489
|
Document(content="Doc 5", meta={"category": "TypeB"}),
|
|
428
490
|
]
|
|
429
|
-
document_store.
|
|
491
|
+
await document_store.write_documents_async(docs)
|
|
430
492
|
|
|
431
493
|
values, total_count = await document_store.get_metadata_field_unique_values_async("category")
|
|
432
494
|
assert total_count == 3
|
|
@@ -438,7 +500,7 @@ class TestWeaviateDocumentStoreAsync:
|
|
|
438
500
|
Document(content="Doc 1", meta={"category": "TypeA"}),
|
|
439
501
|
Document(content="Doc 2", meta={"category": "TypeB"}),
|
|
440
502
|
]
|
|
441
|
-
document_store.
|
|
503
|
+
await document_store.write_documents_async(docs)
|
|
442
504
|
|
|
443
505
|
values, total_count = await document_store.get_metadata_field_unique_values_async("meta.category")
|
|
444
506
|
assert total_count == 2
|
|
@@ -452,7 +514,7 @@ class TestWeaviateDocumentStoreAsync:
|
|
|
452
514
|
Document(content="Python is great", meta={"category": "TypeC"}),
|
|
453
515
|
Document(content="JavaScript tutorial", meta={"category": "TypeD"}),
|
|
454
516
|
]
|
|
455
|
-
document_store.
|
|
517
|
+
await document_store.write_documents_async(docs)
|
|
456
518
|
|
|
457
519
|
values, total_count = await document_store.get_metadata_field_unique_values_async(
|
|
458
520
|
"category", search_term="Python"
|
|
@@ -469,7 +531,7 @@ class TestWeaviateDocumentStoreAsync:
|
|
|
469
531
|
Document(content="Doc 4", meta={"category": "TypeD"}),
|
|
470
532
|
Document(content="Doc 5", meta={"category": "TypeE"}),
|
|
471
533
|
]
|
|
472
|
-
document_store.
|
|
534
|
+
await document_store.write_documents_async(docs)
|
|
473
535
|
|
|
474
536
|
values, total_count = await document_store.get_metadata_field_unique_values_async("category", from_=0, size=2)
|
|
475
537
|
assert total_count == 5
|
|
@@ -491,3 +553,39 @@ class TestWeaviateDocumentStoreAsync:
|
|
|
491
553
|
values, total_count = await document_store.get_metadata_field_unique_values_async("category")
|
|
492
554
|
assert total_count == 0
|
|
493
555
|
assert values == []
|
|
556
|
+
|
|
557
|
+
@pytest.mark.asyncio
|
|
558
|
+
async def test_delete_all_documents_recreate_async(self, document_store: WeaviateDocumentStore) -> None:
|
|
559
|
+
docs = [Document(content="test doc 1"), Document(content="test doc 2")]
|
|
560
|
+
assert await document_store.write_documents_async(docs) == 2
|
|
561
|
+
assert await document_store.count_documents_async() == 2
|
|
562
|
+
|
|
563
|
+
cls = document_store._collection_settings["class"]
|
|
564
|
+
collection = (await document_store.async_client).collections.get(cls)
|
|
565
|
+
previous_config = (await collection.config.get()).to_dict()
|
|
566
|
+
|
|
567
|
+
await document_store.delete_all_documents_async(recreate_index=True)
|
|
568
|
+
assert await document_store.count_documents_async() == 0
|
|
569
|
+
|
|
570
|
+
new_config = (await (await document_store.async_client).collections.get(cls).config.get()).to_dict()
|
|
571
|
+
assert previous_config == new_config
|
|
572
|
+
|
|
573
|
+
@pytest.mark.asyncio
|
|
574
|
+
async def test_delete_all_documents_batch_size_async(self, document_store: WeaviateDocumentStore) -> None:
|
|
575
|
+
docs = [Document(content=str(i)) for i in range(0, 5)]
|
|
576
|
+
assert await document_store.write_documents_async(docs) == 5
|
|
577
|
+
await document_store.delete_all_documents_async(batch_size=2)
|
|
578
|
+
assert await document_store.count_documents_async() == 0
|
|
579
|
+
|
|
580
|
+
@pytest.mark.asyncio
|
|
581
|
+
async def test_delete_all_documents_excessive_batch_size_async(
|
|
582
|
+
self, document_store: WeaviateDocumentStore, caplog: pytest.LogCaptureFixture
|
|
583
|
+
) -> None:
|
|
584
|
+
"""Test that the deletion is not complete if the batch size exceeds the QUERY_MAXIMUM_RESULTS."""
|
|
585
|
+
# assume QUERY_MAXIMUM_RESULTS == 10000 with standard deployment
|
|
586
|
+
docs = [Document(content=str(i)) for i in range(0, 10005)]
|
|
587
|
+
assert await document_store.write_documents_async(docs) == 10005
|
|
588
|
+
with caplog.at_level(logging.WARNING):
|
|
589
|
+
await document_store.delete_all_documents_async(batch_size=20000)
|
|
590
|
+
assert await document_store.count_documents_async() == 5
|
|
591
|
+
assert "Not all documents have been deleted." in caplog.text
|
|
@@ -17,7 +17,7 @@ def test_init_default():
|
|
|
17
17
|
assert retriever._document_store == mock_document_store
|
|
18
18
|
assert retriever._filters == {}
|
|
19
19
|
assert retriever._top_k == 10
|
|
20
|
-
assert retriever._alpha
|
|
20
|
+
assert retriever._alpha == 0.7
|
|
21
21
|
assert retriever._max_vector_distance is None
|
|
22
22
|
assert retriever._filter_policy == FilterPolicy.REPLACE
|
|
23
23
|
|
|
@@ -56,7 +56,7 @@ def test_to_dict(_mock_weaviate):
|
|
|
56
56
|
"init_parameters": {
|
|
57
57
|
"filters": {},
|
|
58
58
|
"top_k": 10,
|
|
59
|
-
"alpha":
|
|
59
|
+
"alpha": 0.7,
|
|
60
60
|
"max_vector_distance": None,
|
|
61
61
|
"filter_policy": "replace",
|
|
62
62
|
"document_store": {
|
|
@@ -112,7 +112,7 @@ def test_from_dict(_mock_weaviate):
|
|
|
112
112
|
"init_parameters": {
|
|
113
113
|
"filters": {},
|
|
114
114
|
"top_k": 10,
|
|
115
|
-
"alpha":
|
|
115
|
+
"alpha": 0.7,
|
|
116
116
|
"max_vector_distance": None,
|
|
117
117
|
"filter_policy": "replace",
|
|
118
118
|
"document_store": {
|
|
@@ -142,7 +142,7 @@ def test_from_dict(_mock_weaviate):
|
|
|
142
142
|
assert retriever._document_store
|
|
143
143
|
assert retriever._filters == {}
|
|
144
144
|
assert retriever._top_k == 10
|
|
145
|
-
assert retriever._alpha
|
|
145
|
+
assert retriever._alpha == 0.7
|
|
146
146
|
assert retriever._max_vector_distance is None
|
|
147
147
|
|
|
148
148
|
|
|
@@ -200,7 +200,12 @@ def test_run_basic():
|
|
|
200
200
|
assert "documents" in result
|
|
201
201
|
assert len(result["documents"]) == 1
|
|
202
202
|
mock_document_store._hybrid_retrieval.assert_called_once_with(
|
|
203
|
-
query="test query",
|
|
203
|
+
query="test query",
|
|
204
|
+
query_embedding=[0.1, 0.2, 0.3],
|
|
205
|
+
filters={},
|
|
206
|
+
top_k=10,
|
|
207
|
+
alpha=0.7,
|
|
208
|
+
max_vector_distance=None,
|
|
204
209
|
)
|
|
205
210
|
|
|
206
211
|
|
|
@@ -217,7 +222,7 @@ def test_run_with_runtime_filters():
|
|
|
217
222
|
query_embedding=[0.1, 0.2, 0.3],
|
|
218
223
|
filters={"runtime": "filter"},
|
|
219
224
|
top_k=10,
|
|
220
|
-
alpha=
|
|
225
|
+
alpha=0.7,
|
|
221
226
|
max_vector_distance=None,
|
|
222
227
|
)
|
|
223
228
|
|
|
@@ -259,7 +264,7 @@ def test_run_empty_query():
|
|
|
259
264
|
assert "documents" in result
|
|
260
265
|
assert len(result["documents"]) == 0
|
|
261
266
|
mock_document_store._hybrid_retrieval.assert_called_once_with(
|
|
262
|
-
query="", query_embedding=[0.1, 0.2, 0.3], filters={}, top_k=10, alpha=
|
|
267
|
+
query="", query_embedding=[0.1, 0.2, 0.3], filters={}, top_k=10, alpha=0.7, max_vector_distance=None
|
|
263
268
|
)
|
|
264
269
|
|
|
265
270
|
|
|
@@ -288,7 +293,7 @@ def test_from_dict_no_filter_policy(_mock_weaviate):
|
|
|
288
293
|
"init_parameters": {
|
|
289
294
|
"filters": {},
|
|
290
295
|
"top_k": 10,
|
|
291
|
-
"alpha":
|
|
296
|
+
"alpha": 0.7,
|
|
292
297
|
"max_vector_distance": None,
|
|
293
298
|
# filter_policy intentionally omitted
|
|
294
299
|
"document_store": {
|
|
@@ -318,7 +323,7 @@ def test_from_dict_no_filter_policy(_mock_weaviate):
|
|
|
318
323
|
assert retriever._document_store
|
|
319
324
|
assert retriever._filters == {}
|
|
320
325
|
assert retriever._top_k == 10
|
|
321
|
-
assert retriever._alpha
|
|
326
|
+
assert retriever._alpha == 0.7
|
|
322
327
|
assert retriever._max_vector_distance is None
|
|
323
328
|
assert retriever._filter_policy == FilterPolicy.REPLACE
|
|
324
329
|
|
|
@@ -381,7 +386,7 @@ def test_run_with_max_vector_distance_zero_runtime():
|
|
|
381
386
|
query_embedding=[0.1, 0.2],
|
|
382
387
|
filters={},
|
|
383
388
|
top_k=10,
|
|
384
|
-
alpha=
|
|
389
|
+
alpha=0.7,
|
|
385
390
|
max_vector_distance=0.0,
|
|
386
391
|
)
|
|
387
392
|
|
|
@@ -402,7 +407,7 @@ def test_run_with_max_vector_distance_zero_init_and_none_runtime():
|
|
|
402
407
|
query_embedding=[0.1, 0.2],
|
|
403
408
|
filters={},
|
|
404
409
|
top_k=10,
|
|
405
|
-
alpha=
|
|
410
|
+
alpha=0.7,
|
|
406
411
|
max_vector_distance=0.0,
|
|
407
412
|
)
|
|
408
413
|
|
|
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
|
|
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
|