weaviate-haystack 7.2.0__tar.gz → 7.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. {weaviate_haystack-7.2.0 → weaviate_haystack-7.4.0}/CHANGELOG.md +23 -0
  2. {weaviate_haystack-7.2.0 → weaviate_haystack-7.4.0}/PKG-INFO +2 -2
  3. {weaviate_haystack-7.2.0 → weaviate_haystack-7.4.0}/docker-compose.yml +1 -1
  4. {weaviate_haystack-7.2.0 → weaviate_haystack-7.4.0}/pyproject.toml +1 -1
  5. {weaviate_haystack-7.2.0 → weaviate_haystack-7.4.0}/src/haystack_integrations/components/retrievers/weaviate/hybrid_retriever.py +3 -3
  6. {weaviate_haystack-7.2.0 → weaviate_haystack-7.4.0}/src/haystack_integrations/document_stores/weaviate/document_store.py +284 -46
  7. {weaviate_haystack-7.2.0 → weaviate_haystack-7.4.0}/tests/test_document_store.py +20 -17
  8. {weaviate_haystack-7.2.0 → weaviate_haystack-7.4.0}/tests/test_document_store_async.py +132 -34
  9. {weaviate_haystack-7.2.0 → weaviate_haystack-7.4.0}/tests/test_hybrid_retriever.py +16 -11
  10. {weaviate_haystack-7.2.0 → weaviate_haystack-7.4.0}/.gitignore +0 -0
  11. {weaviate_haystack-7.2.0 → weaviate_haystack-7.4.0}/LICENSE.txt +0 -0
  12. {weaviate_haystack-7.2.0 → weaviate_haystack-7.4.0}/README.md +0 -0
  13. {weaviate_haystack-7.2.0 → weaviate_haystack-7.4.0}/pydoc/config_docusaurus.yml +0 -0
  14. {weaviate_haystack-7.2.0 → weaviate_haystack-7.4.0}/src/haystack_integrations/components/retrievers/py.typed +0 -0
  15. {weaviate_haystack-7.2.0 → weaviate_haystack-7.4.0}/src/haystack_integrations/components/retrievers/weaviate/__init__.py +0 -0
  16. {weaviate_haystack-7.2.0 → weaviate_haystack-7.4.0}/src/haystack_integrations/components/retrievers/weaviate/bm25_retriever.py +0 -0
  17. {weaviate_haystack-7.2.0 → weaviate_haystack-7.4.0}/src/haystack_integrations/components/retrievers/weaviate/embedding_retriever.py +0 -0
  18. {weaviate_haystack-7.2.0 → weaviate_haystack-7.4.0}/src/haystack_integrations/document_stores/py.typed +0 -0
  19. {weaviate_haystack-7.2.0 → weaviate_haystack-7.4.0}/src/haystack_integrations/document_stores/weaviate/__init__.py +0 -0
  20. {weaviate_haystack-7.2.0 → weaviate_haystack-7.4.0}/src/haystack_integrations/document_stores/weaviate/_filters.py +0 -0
  21. {weaviate_haystack-7.2.0 → weaviate_haystack-7.4.0}/src/haystack_integrations/document_stores/weaviate/auth.py +0 -0
  22. {weaviate_haystack-7.2.0 → weaviate_haystack-7.4.0}/tests/__init__.py +0 -0
  23. {weaviate_haystack-7.2.0 → weaviate_haystack-7.4.0}/tests/conftest.py +0 -0
  24. {weaviate_haystack-7.2.0 → weaviate_haystack-7.4.0}/tests/test_auth.py +0 -0
  25. {weaviate_haystack-7.2.0 → weaviate_haystack-7.4.0}/tests/test_bm25_retriever.py +0 -0
  26. {weaviate_haystack-7.2.0 → weaviate_haystack-7.4.0}/tests/test_bm25_retriever_async.py +0 -0
  27. {weaviate_haystack-7.2.0 → weaviate_haystack-7.4.0}/tests/test_embedding_retriever.py +0 -0
  28. {weaviate_haystack-7.2.0 → weaviate_haystack-7.4.0}/tests/test_embedding_retriever_async.py +0 -0
  29. {weaviate_haystack-7.2.0 → weaviate_haystack-7.4.0}/tests/test_files/robot1.jpg +0 -0
  30. {weaviate_haystack-7.2.0 → weaviate_haystack-7.4.0}/tests/test_filters.py +0 -0
  31. {weaviate_haystack-7.2.0 → weaviate_haystack-7.4.0}/tests/test_hybrid_retriever_async.py +0 -0
@@ -1,5 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## [integrations/weaviate-v7.3.0] - 2026-03-11
4
+
5
+ ### 🚀 Features
6
+
7
+ - Add missing async methods for `WeaviateDocumentStore` (#2929)
8
+
9
+
10
+ ## [integrations/weaviate-v7.2.0] - 2026-03-06
11
+
12
+ ### 🐛 Bug Fixes
13
+
14
+ - Remove unnecessary connection test and add `close`/`close_async` methods to `WeaviateDocumentStore` (#2891)
15
+
16
+ ### 📚 Documentation
17
+
18
+ - Simplify pydoc configs (#2855)
19
+
20
+ ### 🧪 Testing
21
+
22
+ - Replacing each `DocumentStore` specific tests and used the generalised ones from `haystack.testing.document_store` (#2812)
23
+ - Fix Weaviate tests to include grpc_config (#2871)
24
+
25
+
3
26
  ## [integrations/weaviate-v7.1.0] - 2026-01-29
4
27
 
5
28
  ### 🚀 Features
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: weaviate-haystack
3
- Version: 7.2.0
3
+ Version: 7.4.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.9
23
+ Requires-Dist: weaviate-client>=4.20
24
24
  Description-Content-Type: text/markdown
25
25
 
26
26
  # weaviate-haystack
@@ -7,7 +7,7 @@ services:
7
7
  - '8080'
8
8
  - --scheme
9
9
  - http
10
- image: semitechnologies/weaviate:1.30.17
10
+ image: semitechnologies/weaviate:1.36.2
11
11
  ports:
12
12
  - 8080:8080
13
13
  - 50051:50051
@@ -24,7 +24,7 @@ classifiers = [
24
24
  ]
25
25
  dependencies = [
26
26
  "haystack-ai>=2.24.0",
27
- "weaviate-client>=4.9",
27
+ "weaviate-client>=4.20",
28
28
  "python-dateutil",
29
29
  ]
30
30
 
@@ -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 | None = None,
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
- If `None`, the Weaviate server default is used.
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 alpha is not None and not 0.0 <= alpha <= 1.0:
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.data import DataObject
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.
@@ -733,15 +745,6 @@ class WeaviateDocumentStore:
733
745
  # The embedding vector is stored separately from the rest of the data
734
746
  del data["embedding"]
735
747
 
736
- # _split_overlap meta field is unsupported because of a bug
737
- # https://github.com/deepset-ai/haystack-core-integrations/issues/1172
738
- if "_split_overlap" in data:
739
- data.pop("_split_overlap")
740
- logger.warning(
741
- "Document {id} has the unsupported `_split_overlap` meta field. It will be ignored.",
742
- id=data["_original_id"],
743
- )
744
-
745
748
  if "sparse_embedding" in data:
746
749
  sparse_embedding = data.pop("sparse_embedding", None)
747
750
  if sparse_embedding:
@@ -755,7 +758,7 @@ class WeaviateDocumentStore:
755
758
  return data
756
759
 
757
760
  @staticmethod
758
- def _to_document(data: DataObject[dict[str, Any], None]) -> Document:
761
+ def _to_document(data: Object[dict[str, Any], None]) -> Document:
759
762
  """
760
763
  Converts a data object read from Weaviate into a Document.
761
764
  """
@@ -793,7 +796,7 @@ class WeaviateDocumentStore:
793
796
 
794
797
  return Document.from_dict(document_data)
795
798
 
796
- def _query(self) -> list[DataObject[dict[str, Any], None]]:
799
+ def _query(self) -> _ObjectIterator[dict[str, Any], None]:
797
800
  properties = [p.name for p in self.collection.config.get().properties]
798
801
  try:
799
802
  result = self.collection.iterator(include_vector=True, return_properties=properties)
@@ -802,7 +805,17 @@ class WeaviateDocumentStore:
802
805
  raise DocumentStoreError(msg) from e
803
806
  return result
804
807
 
805
- def _query_with_filters(self, filters: dict[str, Any]) -> list[DataObject[dict[str, Any], None]]:
808
+ async def _query_async(self) -> _ObjectAIterator[dict[str, Any], None]:
809
+ collection = await self.async_collection
810
+ properties = [p.name for p in (await collection.config.get()).properties]
811
+ try:
812
+ result = collection.iterator(include_vector=True, return_properties=properties)
813
+ except weaviate.exceptions.WeaviateQueryError as e:
814
+ msg = f"Failed to query documents in Weaviate. Error: {e.message}"
815
+ raise DocumentStoreError(msg) from e
816
+ return result
817
+
818
+ def _query_with_filters(self, filters: dict[str, Any]) -> list[Object[dict[str, Any], None]]:
806
819
  properties = [p.name for p in self.collection.config.get().properties]
807
820
  # When querying with filters we need to paginate using limit and offset as using
808
821
  # a cursor with after is not possible. See the official docs:
@@ -833,6 +846,38 @@ class WeaviateDocumentStore:
833
846
  offset += DEFAULT_QUERY_LIMIT
834
847
  return result
835
848
 
849
+ async def _query_with_filters_async(self, filters: dict[str, Any]) -> list[Object[dict[str, Any], None]]:
850
+ collection = await self.async_collection
851
+ properties = [p.name for p in (await collection.config.get()).properties]
852
+ # When querying with filters we need to paginate using limit and offset as using
853
+ # a cursor with after is not possible. See the official docs:
854
+ # https://weaviate.io/developers/weaviate/api/graphql/additional-operators#cursor-with-after
855
+ #
856
+ # Nonetheless there's also another issue, paginating with limit and offset is not efficient
857
+ # and it's still restricted by the QUERY_MAXIMUM_RESULTS environment variable.
858
+ # If the sum of limit and offset is greater than QUERY_MAXIMUM_RESULTS an error is raised.
859
+ # See the official docs for more:
860
+ # https://weaviate.io/developers/weaviate/api/graphql/additional-operators#performance-considerations
861
+ offset = 0
862
+ partial_result = None
863
+ result = []
864
+ # Keep querying until we get all documents matching the filters
865
+ while partial_result is None or len(partial_result.objects) == DEFAULT_QUERY_LIMIT:
866
+ try:
867
+ partial_result = await collection.query.fetch_objects(
868
+ filters=convert_filters(filters),
869
+ include_vector=True,
870
+ limit=DEFAULT_QUERY_LIMIT,
871
+ offset=offset,
872
+ return_properties=properties,
873
+ )
874
+ except weaviate.exceptions.WeaviateQueryError as e:
875
+ msg = f"Failed to query documents in Weaviate. Error: {e.message}"
876
+ raise DocumentStoreError(msg) from e
877
+ result.extend(partial_result.objects)
878
+ offset += DEFAULT_QUERY_LIMIT
879
+ return result
880
+
836
881
  def filter_documents(self, filters: dict[str, Any] | None = None) -> list[Document]:
837
882
  """
838
883
  Returns the documents that match the filters provided.
@@ -852,9 +897,48 @@ class WeaviateDocumentStore:
852
897
  result = []
853
898
  if filters:
854
899
  result = self._query_with_filters(filters)
855
- else:
856
- result = self._query()
857
- return [WeaviateDocumentStore._to_document(doc) for doc in result]
900
+ return [WeaviateDocumentStore._to_document(doc) for doc in result]
901
+ result_iter = self._query()
902
+ return [WeaviateDocumentStore._to_document(doc) for doc in result_iter]
903
+
904
+ async def filter_documents_async(self, filters: dict[str, Any] | None = None) -> list[Document]:
905
+ """
906
+ Asynchronously returns the documents that match the filters provided.
907
+
908
+ For a detailed specification of the filters, refer to the
909
+ DocumentStore.filter_documents() protocol documentation.
910
+
911
+ Note: The ``contains`` filter operator is case-sensitive (substring
912
+ matching). For case-insensitive matching, normalize the value before
913
+ building the filter.
914
+
915
+ :param filters: The filters to apply to the document list.
916
+ :returns: A list of Documents that match the given filters.
917
+ """
918
+ validate_filters(filters)
919
+
920
+ result = []
921
+ if filters:
922
+ result = await self._query_with_filters_async(filters)
923
+ return [WeaviateDocumentStore._to_document(doc) for doc in result]
924
+ result_iter = await self._query_async()
925
+ return [WeaviateDocumentStore._to_document(doc) async for doc in result_iter]
926
+
927
+ @staticmethod
928
+ def _handle_failed_objects(failed_objects: list[ErrorObject]) -> NoReturn:
929
+ # We fall back to use the UUID if the _original_id is not present, this is just to be
930
+ mapped_objects = {}
931
+ for obj in failed_objects:
932
+ properties = obj.object_.properties or {}
933
+ # We get the object uuid just in case the _original_id is not present.
934
+ # That's extremely unlikely to happen but let's stay on the safe side.
935
+ id_ = properties.get("_original_id", obj.object_.uuid)
936
+ mapped_objects[id_] = obj.message
937
+
938
+ msg = "\n".join(
939
+ [f"Failed to write object with id '{id_}'. Error: '{message}'" for id_, message in mapped_objects.items()]
940
+ )
941
+ raise DocumentStoreError(msg)
858
942
 
859
943
  def _batch_write(self, documents: list[Document]) -> int:
860
944
  """
@@ -876,22 +960,35 @@ class WeaviateDocumentStore:
876
960
  vector=doc.embedding,
877
961
  )
878
962
  if failed_objects := self.client.batch.failed_objects:
879
- # We fall back to use the UUID if the _original_id is not present, this is just to be
880
- mapped_objects = {}
881
- for obj in failed_objects:
882
- properties = obj.object_.properties or {}
883
- # We get the object uuid just in case the _original_id is not present.
884
- # That's extremely unlikely to happen but let's stay on the safe side.
885
- id_ = properties.get("_original_id", obj.object_.uuid)
886
- mapped_objects[id_] = obj.message
887
-
888
- msg = "\n".join(
889
- [
890
- f"Failed to write object with id '{id_}'. Error: '{message}'"
891
- for id_, message in mapped_objects.items()
892
- ]
893
- )
894
- raise DocumentStoreError(msg)
963
+ self._handle_failed_objects(failed_objects)
964
+
965
+ # If the document already exists we get no status message back from Weaviate.
966
+ # So we assume that all Documents were written.
967
+ return len(documents)
968
+
969
+ async def _batch_write_async(self, documents: list[Document]) -> int:
970
+ """
971
+ Asynchronously writes document to Weaviate in batches.
972
+ Documents with the same id will be overwritten.
973
+ Raises in case of errors.
974
+ """
975
+ client = await self.async_client
976
+
977
+ async with client.batch.stream() as batch:
978
+ for doc in documents:
979
+ if not isinstance(doc, Document):
980
+ msg = f"Expected a Document, got '{type(doc)}' instead."
981
+ raise ValueError(msg)
982
+
983
+ await batch.add_object(
984
+ properties=WeaviateDocumentStore._to_data_object(doc),
985
+ collection=(await self.async_collection).name,
986
+ uuid=generate_uuid5(doc.id),
987
+ vector=doc.embedding,
988
+ )
989
+
990
+ if failed_objects := client.batch.failed_objects:
991
+ self._handle_failed_objects(failed_objects)
895
992
 
896
993
  # If the document already exists we get no status message back from Weaviate.
897
994
  # So we assume that all Documents were written.
@@ -931,6 +1028,43 @@ class WeaviateDocumentStore:
931
1028
  raise DuplicateDocumentError(msg)
932
1029
  return written
933
1030
 
1031
+ async def _write_async(self, documents: list[Document], policy: DuplicatePolicy) -> int:
1032
+ """
1033
+ Asynchronously writes documents to Weaviate using the specified policy.
1034
+ This doesn't use the batch API, so it's slower than _batch_write.
1035
+ If policy is set to SKIP it will skip any document that already exists.
1036
+ If policy is set to FAIL it will raise an exception if any of the documents already exists.
1037
+ """
1038
+ collection = await self.async_collection
1039
+
1040
+ duplicate_errors_ids = []
1041
+ for doc in documents:
1042
+ if not isinstance(doc, Document):
1043
+ msg = f"Expected a Document, got '{type(doc)}' instead."
1044
+ raise ValueError(msg)
1045
+
1046
+ if policy == DuplicatePolicy.SKIP and await (await self.async_collection).data.exists(
1047
+ uuid=generate_uuid5(doc.id)
1048
+ ):
1049
+ # This Document already exists, continue
1050
+ continue
1051
+
1052
+ try:
1053
+ await collection.data.insert(
1054
+ uuid=generate_uuid5(doc.id),
1055
+ properties=WeaviateDocumentStore._to_data_object(doc),
1056
+ vector=doc.embedding,
1057
+ )
1058
+
1059
+ except weaviate.exceptions.UnexpectedStatusCodeError:
1060
+ if policy == DuplicatePolicy.FAIL:
1061
+ duplicate_errors_ids.append(doc.id)
1062
+
1063
+ if duplicate_errors_ids:
1064
+ msg = f"IDs '{', '.join(duplicate_errors_ids)}' already exist in the document store."
1065
+ raise DuplicateDocumentError(msg)
1066
+ return len(documents)
1067
+
934
1068
  def write_documents(self, documents: list[Document], policy: DuplicatePolicy = DuplicatePolicy.NONE) -> int:
935
1069
  """
936
1070
  Writes documents to Weaviate using the specified policy.
@@ -939,12 +1073,59 @@ class WeaviateDocumentStore:
939
1073
  We can't use the batch API for other policies as it doesn't return any information whether the document
940
1074
  already exists or not. That prevents us from returning errors when using the FAIL policy or skipping a
941
1075
  Document when using the SKIP policy.
1076
+
1077
+ :param documents:
1078
+ A list of documents to write into the document store.
1079
+ :param policy:
1080
+ DuplicatePolicy to apply when a document with the same ID already exists in the document store.
1081
+
1082
+ :raises ValueError:
1083
+ When input is not valid.
1084
+ :raises DuplicateDocumentError:
1085
+ When duplicate documents are found and using a FAIL policy.
1086
+ :raises DocumentStoreError:
1087
+ When documents have failed to be batch written.
1088
+
1089
+ :returns:
1090
+ The number of documents written.
942
1091
  """
943
1092
  if policy in [DuplicatePolicy.NONE, DuplicatePolicy.OVERWRITE]:
944
1093
  return self._batch_write(documents)
945
1094
 
946
1095
  return self._write(documents, policy)
947
1096
 
1097
+ async def write_documents_async(
1098
+ self, documents: list[Document], policy: DuplicatePolicy = DuplicatePolicy.NONE
1099
+ ) -> int:
1100
+ """
1101
+ Asynchronously writes documents to Weaviate using the specified policy.
1102
+ We recommend using a OVERWRITE policy as it's faster than other policies for Weaviate since it uses
1103
+ the batch API.
1104
+ We can't use the batch API for other policies as it doesn't return any information whether the document
1105
+ already exists or not. That prevents us from returning errors when using the FAIL policy or skipping a
1106
+ Document when using the SKIP policy.
1107
+
1108
+
1109
+ :param documents:
1110
+ A list of documents to write into the document store.
1111
+ :param policy:
1112
+ DuplicatePolicy to apply when a document with the same ID already exists in the document store.
1113
+
1114
+ :raises ValueError:
1115
+ When input is not valid.
1116
+ :raises DuplicateDocumentError:
1117
+ When duplicate documents are found and using a FAIL policy.
1118
+ :raises DocumentStoreError:
1119
+ When documents have failed to be batch written.
1120
+
1121
+ :returns:
1122
+ The number of documents written.
1123
+ """
1124
+ if policy in [DuplicatePolicy.NONE, DuplicatePolicy.OVERWRITE]:
1125
+ return await self._batch_write_async(documents)
1126
+
1127
+ return await self._write_async(documents, policy)
1128
+
948
1129
  def delete_documents(self, document_ids: list[str]) -> None:
949
1130
  """
950
1131
  Deletes all documents with matching document_ids from the DocumentStore.
@@ -954,6 +1135,16 @@ class WeaviateDocumentStore:
954
1135
  weaviate_ids = [generate_uuid5(doc_id) for doc_id in document_ids]
955
1136
  self.collection.data.delete_many(where=weaviate.classes.query.Filter.by_id().contains_any(weaviate_ids))
956
1137
 
1138
+ async def delete_documents_async(self, document_ids: list[str]) -> None:
1139
+ """
1140
+ Asynchronously deletes all documents with matching document_ids from the DocumentStore.
1141
+
1142
+ :param document_ids: The object_ids to delete.
1143
+ """
1144
+ weaviate_ids = [generate_uuid5(doc_id) for doc_id in document_ids]
1145
+ collection = await self.async_collection
1146
+ await collection.data.delete_many(where=weaviate.classes.query.Filter.by_id().contains_any(weaviate_ids))
1147
+
957
1148
  def delete_all_documents(self, *, recreate_index: bool = False, batch_size: int = 1000) -> None:
958
1149
  """
959
1150
  Deletes all documents in a collection.
@@ -1003,6 +1194,57 @@ class WeaviateDocumentStore:
1003
1194
  "Make sure to specify a deletion `batch_size` which is less than `QUERY_MAXIMUM_RESULTS`.",
1004
1195
  )
1005
1196
 
1197
+ async def delete_all_documents_async(self, *, recreate_index: bool = False, batch_size: int = 1000) -> None:
1198
+ """
1199
+ Asynchronously deletes all documents in a collection.
1200
+
1201
+ If recreate_index is False, it keeps the collection but deletes documents iteratively.
1202
+ If recreate_index is True, the collection is dropped and faithfully recreated.
1203
+ This is recommended for performance reasons.
1204
+
1205
+ :param recreate_index: Use drop and recreate strategy. (recommended for performance)
1206
+ :param batch_size: Only relevant if recreate_index is false. Defines the deletion batch size.
1207
+ Note that this parameter needs to be less or equal to the set `QUERY_MAXIMUM_RESULTS` variable
1208
+ set for the weaviate deployment (default is 10000).
1209
+ Reference: https://docs.weaviate.io/weaviate/manage-objects/delete#delete-all-objects
1210
+ """
1211
+ client = await self.async_client
1212
+
1213
+ if recreate_index:
1214
+ # get current up-to-date config from server, so we can recreate the collection faithfully
1215
+ cfg = (await client.collections.get(self._collection_settings["class"]).config.get()).to_dict()
1216
+ class_name = cfg.get("class", self._collection_settings["class"])
1217
+
1218
+ await client.collections.delete(class_name)
1219
+ await client.collections.create_from_dict(cfg)
1220
+
1221
+ self._collection_settings = cfg
1222
+ self._async_collection = client.collections.get(class_name)
1223
+ return
1224
+
1225
+ uuids = []
1226
+ batch_size = max(1, int(batch_size))
1227
+
1228
+ collection = await self.async_collection
1229
+ async for obj in collection.iterator(return_properties=[], include_vector=False):
1230
+ uuids.append(obj.uuid)
1231
+ if len(uuids) >= batch_size:
1232
+ res = await collection.data.delete_many(where=weaviate.classes.query.Filter.by_id().contains_any(uuids))
1233
+ if res.successful < len(uuids):
1234
+ logger.warning(
1235
+ "Not all documents in the batch have been deleted. "
1236
+ "Make sure to specify a deletion `batch_size` which is less than `QUERY_MAXIMUM_RESULTS`.",
1237
+ )
1238
+ uuids.clear()
1239
+
1240
+ if uuids:
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 have been deleted. "
1245
+ "Make sure to specify a deletion `batch_size` which is less than `QUERY_MAXIMUM_RESULTS`.",
1246
+ )
1247
+
1006
1248
  def delete_by_filter(self, filters: dict[str, Any]) -> int:
1007
1249
  """
1008
1250
  Deletes all documents that match the provided filters.
@@ -1095,11 +1337,9 @@ class WeaviateDocumentStore:
1095
1337
 
1096
1338
  # Update the object, preserving the vector
1097
1339
  # Get the vector from the object to preserve it during replace
1098
- vector = None
1099
- if isinstance(obj.vector, list):
1340
+ vector: VECTORS | None = None
1341
+ if isinstance(obj.vector, (list, dict)):
1100
1342
  vector = obj.vector
1101
- elif isinstance(obj.vector, dict):
1102
- vector = obj.vector.get("default")
1103
1343
 
1104
1344
  self.collection.data.replace(
1105
1345
  uuid=obj.uuid,
@@ -1191,11 +1431,9 @@ class WeaviateDocumentStore:
1191
1431
 
1192
1432
  # Update the object, preserving the vector
1193
1433
  # Get the vector from the object to preserve it during replace
1194
- vector = None
1195
- if isinstance(obj.vector, list):
1434
+ vector: VECTORS | None = None
1435
+ if isinstance(obj.vector, (list, dict)):
1196
1436
  vector = obj.vector
1197
- elif isinstance(obj.vector, dict):
1198
- vector = obj.vector.get("default")
1199
1437
 
1200
1438
  await collection.data.replace(
1201
1439
  uuid=obj.uuid,
@@ -1322,7 +1560,7 @@ class WeaviateDocumentStore:
1322
1560
  query_embedding: list[float],
1323
1561
  filters: dict[str, Any] | None = None,
1324
1562
  top_k: int | None = None,
1325
- alpha: float | None = None,
1563
+ alpha: float = 0.7,
1326
1564
  max_vector_distance: float | None = None,
1327
1565
  ) -> list[Document]:
1328
1566
  properties = [p.name for p in self.collection.config.get().properties]
@@ -1347,7 +1585,7 @@ class WeaviateDocumentStore:
1347
1585
  query_embedding: list[float],
1348
1586
  filters: dict[str, Any] | None = None,
1349
1587
  top_k: int | None = None,
1350
- alpha: float | None = None,
1588
+ alpha: float = 0.7,
1351
1589
  max_vector_distance: float | None = None,
1352
1590
  ) -> list[Document]:
1353
1591
  collection = await self.async_collection
@@ -464,29 +464,32 @@ class TestWeaviateDocumentStore(DocumentStoreBaseExtendedTests):
464
464
  ],
465
465
  )
466
466
 
467
- def test_meta_split_overlap_is_skipped(self, document_store):
467
+ def test_split_overlap_preserved(self, document_store):
468
+ """Split overlap meta is written and read back correctly."""
469
+ overlap = [
470
+ {"range": [3.0, 13.0], "doc_id": "34326b7e6be489cb4c031152fc378cb50479ca5fcc3861e7e61dfb2e4e4e968b"},
471
+ {"range": [0.0, 13.0], "doc_id": "780f791c09d499c0bf01f87bce047b45c44224d36c79f0c9d8c1405a3197fc1a"},
472
+ ]
468
473
  doc = Document(
469
- content="The moonlight shimmered ",
474
+ id="6edd24e8b01f3cd6e4b71fef7d57b52f17664e14db5ab01b8ef429f97add3620",
475
+ content="an eighth test. ",
470
476
  meta={
471
- "source_id": "62049ba1d1e1d5ebb1f6230b0b00c5356b8706c56e0b9c36b1dfc86084cd75f0",
472
- "page_number": 1,
473
- "split_id": 0,
474
- "split_idx_start": 0,
475
- "_split_overlap": [
476
- {"doc_id": "68ed48ba830048c5d7815874ed2de794722e6d10866b6c55349a914fd9a0df65", "range": (0, 20)}
477
- ],
477
+ "_split_overlap": overlap,
478
+ "page_number": 1.0,
479
+ "split_id": 33.0,
480
+ "split_idx_start": 159.0,
481
+ "source_id": "fdbde6d217f04d3dd60c01f36541794f3153a61f13b4ca669655f4c5610c1664",
478
482
  },
479
483
  )
480
484
  document_store.write_documents([doc])
481
-
482
485
  written_doc = document_store.filter_documents()[0]
483
-
484
- assert written_doc.content == "The moonlight shimmered "
485
- assert written_doc.meta["source_id"] == "62049ba1d1e1d5ebb1f6230b0b00c5356b8706c56e0b9c36b1dfc86084cd75f0"
486
- assert written_doc.meta["page_number"] == 1.0
487
- assert written_doc.meta["split_id"] == 0.0
488
- assert written_doc.meta["split_idx_start"] == 0.0
489
- assert "_split_overlap" not in written_doc.meta
486
+ assert "_split_overlap" in written_doc.meta
487
+ written_overlap = written_doc.meta["_split_overlap"]
488
+ assert len(written_overlap) == 2
489
+ assert written_overlap[0]["doc_id"] == overlap[0]["doc_id"]
490
+ assert list(written_overlap[0]["range"]) == [3, 13]
491
+ assert written_overlap[1]["doc_id"] == overlap[1]["doc_id"]
492
+ assert list(written_overlap[1]["range"]) == [0, 13]
490
493
 
491
494
  def test_bm25_retrieval(self, document_store):
492
495
  document_store.write_documents(
@@ -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.client.collections.delete(collection_settings["class"])
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.write_documents(
54
- [
55
- Document(content="Haskell is a functional programming language"),
56
- Document(content="Lisp is a functional programming language"),
57
- Document(content="Python is an object oriented programming language"),
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 await document_store.count_documents_by_filter_async(filters) == 1
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.write_documents(
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.write_documents(
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.write_documents(
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.write_documents(
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.write_documents(
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.write_documents(
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.write_documents(
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.write_documents(docs)
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.filter_documents()
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.write_documents(docs)
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.filter_documents(
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.write_documents(docs)
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.filter_documents(
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.write_documents(docs)
288
- assert document_store.count_documents() == 4
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.write_documents(docs)
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.write_documents(docs)
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.write_documents(docs)
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.write_documents(docs)
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.write_documents(docs)
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.write_documents(docs)
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.write_documents(docs)
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.write_documents(docs)
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.write_documents(docs)
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 is None
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": None,
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": None,
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 is None
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", query_embedding=[0.1, 0.2, 0.3], filters={}, top_k=10, alpha=None, max_vector_distance=None
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=None,
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=None, max_vector_distance=None
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": None,
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 is None
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=None,
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=None,
410
+ alpha=0.7,
406
411
  max_vector_distance=0.0,
407
412
  )
408
413