Skip to content

Commit dc48f84

Browse files
pandegodavidsbatista
authored andcommitted
fix: DeleteAllAsyncTest, DeleteByFilterAsyncTest, (deepset-ai#10952)
* fix: address deepset-ai#10919 * adding delete_all_documents_async missing in InMemoryDocumentStore --------- Co-authored-by: David S. Batista <dsbatista@gmail.com>
1 parent 2ad51ae commit dc48f84

2 files changed

Lines changed: 201 additions & 0 deletions

File tree

haystack/document_stores/in_memory/document_store.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -863,6 +863,12 @@ async def delete_documents_async(self, document_ids: list[str]) -> None:
863863
self.executor, lambda: self.delete_documents(document_ids=document_ids)
864864
)
865865

866+
async def delete_all_documents_async(self) -> None:
867+
"""
868+
Deletes all documents in the document store.
869+
"""
870+
await asyncio.get_running_loop().run_in_executor(self.executor, self.delete_all_documents)
871+
866872
async def bm25_retrieval_async(
867873
self, query: str, filters: dict[str, Any] | None = None, top_k: int = 10, scale_score: bool = False
868874
) -> list[Document]:

haystack/testing/document_store_async.py

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#
33
# SPDX-License-Identifier: Apache-2.0
44

5+
import inspect
56
from typing import Any, Protocol
67

78
import pytest
@@ -40,6 +41,58 @@ async def delete_documents_async(self, document_ids: list[str]) -> None:
4041
...
4142

4243

44+
class DeleteAllAsyncTest:
45+
"""
46+
Tests for Document Store delete_all_documents_async().
47+
48+
To use it create a custom test class and override the `document_store` fixture.
49+
Only mix in for stores that implement delete_all_documents_async.
50+
"""
51+
52+
@staticmethod
53+
def _delete_all_supports_recreate(document_store: AsyncDocumentStore) -> tuple[bool, str | None]:
54+
"""
55+
Return (True, param_name) if delete_all_documents_async has recreate_index or recreate_collection.
56+
"""
57+
sig = inspect.signature(document_store.delete_all_documents_async) # type:ignore[attr-defined]
58+
if "recreate_index" in sig.parameters:
59+
return True, "recreate_index"
60+
if "recreate_collection" in sig.parameters:
61+
return True, "recreate_collection"
62+
return False, None
63+
64+
@staticmethod
65+
@pytest.mark.asyncio
66+
async def test_delete_all_documents_async(document_store: AsyncDocumentStore):
67+
"""
68+
Test delete_all_documents_async() normal behaviour.
69+
70+
This test verifies that delete_all_documents_async() removes all documents from the store
71+
and that the store remains functional after deletion.
72+
"""
73+
docs = [Document(content="first doc", id="1"), Document(content="second doc", id="2")]
74+
await document_store.write_documents_async(docs)
75+
assert await document_store.count_documents_async() == 2
76+
77+
await document_store.delete_all_documents_async() # type:ignore[attr-defined]
78+
assert await document_store.count_documents_async() == 0
79+
80+
new_doc = Document(content="new doc after delete all", id="3")
81+
await document_store.write_documents_async([new_doc])
82+
assert await document_store.count_documents_async() == 1
83+
84+
@staticmethod
85+
@pytest.mark.asyncio
86+
async def test_delete_all_documents_empty_store_async(document_store: AsyncDocumentStore):
87+
"""
88+
Test delete_all_documents_async() on an empty store.
89+
90+
This should not raise an error and should leave the store empty.
91+
"""
92+
assert await document_store.count_documents_async() == 0
93+
await document_store.delete_all_documents_async() # type:ignore[attr-defined]
94+
95+
4396
class CountDocumentsAsyncTest:
4497
"""
4598
Utility class to test a Document Store `count_documents_async` method.
@@ -63,6 +116,148 @@ async def test_count_empty_async(document_store: AsyncDocumentStore):
63116

64117
@staticmethod
65118
@pytest.mark.asyncio
119+
async def test_delete_all_documents_without_recreate_index_async(document_store: AsyncDocumentStore):
120+
"""
121+
Test delete_all_documents_async() with recreate_index/recreate_collection=False when supported.
122+
123+
Skipped if the store's delete_all_documents_async does not have recreate_index or recreate_collection.
124+
"""
125+
supports, param_name = DeleteAllAsyncTest._delete_all_supports_recreate(document_store)
126+
if not supports or param_name is None:
127+
pytest.skip("delete_all_documents_async has no recreate_index or recreate_collection parameter")
128+
129+
docs = [Document(id="1", content="A first document"), Document(id="2", content="Second document")]
130+
await document_store.write_documents_async(docs)
131+
assert await document_store.count_documents_async() == 2
132+
133+
await document_store.delete_all_documents_async(**{param_name: False}) # type:ignore[attr-defined]
134+
assert await document_store.count_documents_async() == 0
135+
136+
new_doc = Document(id="3", content="New document after delete all")
137+
await document_store.write_documents_async([new_doc])
138+
assert await document_store.count_documents_async() == 1
139+
140+
@staticmethod
141+
@pytest.mark.asyncio
142+
async def test_delete_all_documents_with_recreate_index_async(document_store: AsyncDocumentStore):
143+
"""
144+
Test delete_all_documents_async() with recreate_index/recreate_collection=True when supported.
145+
146+
Skipped if the store's delete_all_documents_async does not have recreate_index or recreate_collection.
147+
"""
148+
supports, param_name = DeleteAllAsyncTest._delete_all_supports_recreate(document_store)
149+
if not supports or param_name is None:
150+
pytest.skip("delete_all_documents_async has no recreate_index or recreate_collection parameter")
151+
152+
docs = [Document(id="1", content="A first document"), Document(id="2", content="Second document")]
153+
await document_store.write_documents_async(docs)
154+
assert await document_store.count_documents_async() == 2
155+
156+
await document_store.delete_all_documents_async(**{param_name: True}) # type:ignore[attr-defined]
157+
assert await document_store.count_documents_async() == 0
158+
159+
new_doc = Document(id="3", content="New document after delete all with recreate")
160+
await document_store.write_documents_async([new_doc])
161+
assert await document_store.count_documents_async() == 1
162+
163+
retrieved = await document_store.filter_documents_async()
164+
assert len(retrieved) == 1
165+
assert retrieved[0].content == "New document after delete all with recreate"
166+
167+
168+
class DeleteByFilterAsyncTest:
169+
"""
170+
Tests for Document Store delete_by_filter_async().
171+
"""
172+
173+
@staticmethod
174+
def _delete_by_filter_params(document_store: AsyncDocumentStore) -> dict[str, bool]:
175+
"""
176+
Return optional parameters supported by delete_by_filter_async.
177+
"""
178+
sig = inspect.signature(document_store.delete_by_filter_async) # type:ignore[attr-defined]
179+
return {"refresh": True} if "refresh" in sig.parameters else {}
180+
181+
@staticmethod
182+
@pytest.mark.asyncio
183+
async def test_delete_by_filter_async(document_store: AsyncDocumentStore):
184+
"""Delete documents matching a filter and verify count and remaining docs."""
185+
docs = [
186+
Document(content="Doc 1", meta={"category": "Alpha"}),
187+
Document(content="Doc 2", meta={"category": "Beta"}),
188+
Document(content="Doc 3", meta={"category": "Alpha"}),
189+
]
190+
await document_store.write_documents_async(docs)
191+
assert await document_store.count_documents_async() == 3
192+
193+
params = DeleteByFilterAsyncTest._delete_by_filter_params(document_store)
194+
deleted_count = await document_store.delete_by_filter_async( # type:ignore[attr-defined]
195+
filters={"field": "meta.category", "operator": "==", "value": "Alpha"}, **params
196+
)
197+
assert deleted_count == 2
198+
assert await document_store.count_documents_async() == 1
199+
200+
remaining_docs = await document_store.filter_documents_async()
201+
assert len(remaining_docs) == 1
202+
assert remaining_docs[0].meta["category"] == "Beta"
203+
204+
@staticmethod
205+
@pytest.mark.asyncio
206+
async def test_delete_by_filter_no_matches_async(document_store: AsyncDocumentStore):
207+
"""Delete with a filter that matches no documents returns 0 and leaves store unchanged."""
208+
docs = [
209+
Document(content="Doc 1", meta={"category": "Alpha"}),
210+
Document(content="Doc 2", meta={"category": "Beta"}),
211+
]
212+
await document_store.write_documents_async(docs)
213+
assert await document_store.count_documents_async() == 2
214+
215+
params = DeleteByFilterAsyncTest._delete_by_filter_params(document_store)
216+
deleted_count = await document_store.delete_by_filter_async( # type:ignore[attr-defined]
217+
filters={"field": "meta.category", "operator": "==", "value": "Gamma"}, **params
218+
)
219+
assert deleted_count == 0
220+
assert await document_store.count_documents_async() == 2
221+
222+
@staticmethod
223+
@pytest.mark.asyncio
224+
async def test_delete_by_filter_advanced_filters_async(document_store: AsyncDocumentStore):
225+
"""Delete with AND/OR filter combinations and verify remaining documents."""
226+
docs = [
227+
Document(content="Doc 1", meta={"category": "Alpha", "year": 2023, "status": "draft"}),
228+
Document(content="Doc 2", meta={"category": "Alpha", "year": 2024, "status": "published"}),
229+
Document(content="Doc 3", meta={"category": "Beta", "year": 2023, "status": "draft"}),
230+
]
231+
await document_store.write_documents_async(docs)
232+
assert await document_store.count_documents_async() == 3
233+
234+
params = DeleteByFilterAsyncTest._delete_by_filter_params(document_store)
235+
deleted_count = await document_store.delete_by_filter_async( # type:ignore[attr-defined]
236+
filters={
237+
"operator": "AND",
238+
"conditions": [
239+
{"field": "meta.category", "operator": "==", "value": "Alpha"},
240+
{"field": "meta.year", "operator": "==", "value": 2023},
241+
],
242+
},
243+
**params,
244+
)
245+
assert deleted_count == 1
246+
assert await document_store.count_documents_async() == 2
247+
248+
deleted_count = await document_store.delete_by_filter_async( # type:ignore[attr-defined]
249+
filters={
250+
"operator": "OR",
251+
"conditions": [
252+
{"field": "meta.category", "operator": "==", "value": "Beta"},
253+
{"field": "meta.status", "operator": "==", "value": "published"},
254+
],
255+
},
256+
**params,
257+
)
258+
assert deleted_count == 2
259+
assert await document_store.count_documents_async() == 0
260+
66261
async def test_count_not_empty_async(document_store: AsyncDocumentStore):
67262
"""Test count is greater than zero if the document store contains documents."""
68263
await document_store.write_documents_async(

0 commit comments

Comments
 (0)