Skip to content

Commit 928441e

Browse files
committed
Adding delete_match
1 parent 4b4e0a0 commit 928441e

File tree

2 files changed

+80
-19
lines changed

2 files changed

+80
-19
lines changed

arangoasync/collection.py

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1172,7 +1172,6 @@ async def update_match(
11721172
keep_none: Optional[bool] = None,
11731173
wait_for_sync: Optional[bool] = None,
11741174
merge_objects: Optional[bool] = None,
1175-
allow_dirty_read: Optional[bool] = None,
11761175
) -> Result[int]:
11771176
"""Update matching documents.
11781177
@@ -1185,10 +1184,9 @@ async def update_match(
11851184
wait_for_sync (bool | None): Wait until operation has been synced to disk.
11861185
merge_objects (bool | None): If set to `True`, sub-dictionaries are merged
11871186
instead of the new one overwriting the old one.
1188-
allow_dirty_read (bool | None): Allow reads from followers in a cluster.
11891187
11901188
Returns:
1191-
int: Number of documents updated.
1189+
int: Number of documents that got updated.
11921190
11931191
Raises:
11941192
DocumentUpdateError: If update fails.
@@ -1213,18 +1211,11 @@ async def update_match(
12131211
"merge": merge_objects,
12141212
}
12151213
data = {"query": query, "bindVars": bind_vars}
1216-
headers: RequestHeaders = {}
1217-
if allow_dirty_read is not None:
1218-
if allow_dirty_read is True:
1219-
headers["x-arango-allow-dirty-read"] = "true"
1220-
else:
1221-
headers["x-arango-allow-dirty-read"] = "false"
12221214

12231215
request = Request(
12241216
method=Method.POST,
12251217
endpoint="/_api/cursor",
12261218
data=self.serializer.dumps(data),
1227-
headers=headers,
12281219
)
12291220

12301221
def response_handler(resp: Response) -> int:
@@ -1241,7 +1232,6 @@ async def replace_match(
12411232
body: T,
12421233
limit: Optional[int | str] = None,
12431234
wait_for_sync: Optional[bool] = None,
1244-
allow_dirty_read: Optional[bool] = None,
12451235
) -> Result[int]:
12461236
"""Replace matching documents.
12471237
@@ -1250,7 +1240,6 @@ async def replace_match(
12501240
body (dict): New document body.
12511241
limit (int | str | None): Maximum number of documents to replace.
12521242
wait_for_sync (bool | None): Wait until operation has been synced to disk.
1253-
allow_dirty_read (bool | None): Allow reads from followers in a cluster.
12541243
12551244
Returns:
12561245
int: Number of documents that got replaced.
@@ -1276,18 +1265,11 @@ async def replace_match(
12761265
"body": body,
12771266
}
12781267
data = {"query": query, "bindVars": bind_vars}
1279-
headers: RequestHeaders = {}
1280-
if allow_dirty_read is not None:
1281-
if allow_dirty_read is True:
1282-
headers["x-arango-allow-dirty-read"] = "true"
1283-
else:
1284-
headers["x-arango-allow-dirty-read"] = "false"
12851268

12861269
request = Request(
12871270
method=Method.POST,
12881271
endpoint="/_api/cursor",
12891272
data=self.serializer.dumps(data),
1290-
headers=headers,
12911273
)
12921274

12931275
def response_handler(resp: Response) -> int:
@@ -1298,6 +1280,55 @@ def response_handler(resp: Response) -> int:
12981280

12991281
return await self._executor.execute(request, response_handler)
13001282

1283+
async def delete_match(
1284+
self,
1285+
filters: Json,
1286+
limit: Optional[int | str] = None,
1287+
wait_for_sync: Optional[bool] = None,
1288+
) -> Result[int]:
1289+
"""Delete matching documents.
1290+
1291+
Args:
1292+
filters (dict | None): Query filters.
1293+
limit (int | str | None): Maximum number of documents to delete.
1294+
wait_for_sync (bool | None): Wait until operation has been synced to disk.
1295+
1296+
Returns:
1297+
int: Number of documents that got deleted.
1298+
1299+
Raises:
1300+
DocumentDeleteError: If delete fails.
1301+
"""
1302+
if not self._is_none_or_dict(filters):
1303+
raise ValueError("filters parameter must be a dict")
1304+
if not (self._is_none_or_int(limit) or limit == "null"):
1305+
raise ValueError("limit parameter must be a non-negative int")
1306+
1307+
sync = f"waitForSync: {wait_for_sync}" if wait_for_sync is not None else ""
1308+
query = f"""
1309+
FOR doc IN @@collection
1310+
{self._build_filter_conditions(filters)}
1311+
{f"LIMIT {limit}" if limit is not None else ""}
1312+
REMOVE doc IN @@collection
1313+
{f"OPTIONS {{ {sync} }}" if sync else ""}
1314+
""" # noqa: E201 E202
1315+
bind_vars = {"@collection": self.name}
1316+
data = {"query": query, "bindVars": bind_vars}
1317+
1318+
request = Request(
1319+
method=Method.POST,
1320+
endpoint="/_api/cursor",
1321+
data=self.serializer.dumps(data),
1322+
)
1323+
1324+
def response_handler(resp: Response) -> int:
1325+
if resp.is_success:
1326+
result = self.deserializer.loads(resp.raw_body)
1327+
return cast(int, result["extra"]["stats"]["writesExecuted"])
1328+
raise DocumentDeleteError(resp, request)
1329+
1330+
return await self._executor.execute(request, response_handler)
1331+
13011332
async def insert_many(
13021333
self,
13031334
documents: Sequence[T],

tests/test_document.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,3 +523,33 @@ async def test_document_replace_match(doc_col, bad_col, docs):
523523
async for doc in await doc_col.find():
524524
assert doc["val"] != -1
525525
assert count == 0
526+
527+
528+
@pytest.mark.asyncio
529+
async def test_document_delete_match(doc_col, bad_col, docs):
530+
# Check errors first
531+
with pytest.raises(DocumentDeleteError):
532+
await bad_col.delete_match({})
533+
with pytest.raises(ValueError):
534+
await doc_col.delete_match({}, limit=-1)
535+
with pytest.raises(ValueError):
536+
await doc_col.delete_match("abcd")
537+
538+
# Delete all documents
539+
await doc_col.insert_many(docs)
540+
count = await doc_col.delete_match({})
541+
assert count == len(docs)
542+
assert await doc_col.count() == 0
543+
544+
# Delete documents partially
545+
await doc_col.insert_many(docs)
546+
count = await doc_col.delete_match({"text": "foo"})
547+
async for doc in await doc_col.find():
548+
assert doc["text"] != "foo"
549+
assert count == sum([1 for doc in docs if doc["text"] == "foo"])
550+
await doc_col.truncate()
551+
552+
# No matching documents
553+
await doc_col.insert_many(docs)
554+
count = await doc_col.delete_match({"text": "no_matching"})
555+
assert count == 0

0 commit comments

Comments
 (0)