Skip to content

Commit bbd6e7c

Browse files
committed
Document update
1 parent 10ac7b7 commit bbd6e7c

File tree

3 files changed

+135
-5
lines changed

3 files changed

+135
-5
lines changed

arangoasync/collection.py

Lines changed: 98 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
DocumentInsertError,
1515
DocumentParseError,
1616
DocumentRevisionError,
17+
DocumentUpdateError,
1718
IndexCreateError,
1819
IndexDeleteError,
1920
IndexGetError,
@@ -474,15 +475,15 @@ async def insert(
474475
retained in the document. Otherwise, they are removed completely.
475476
Applies only when **overwrite_mode** is set to "update"
476477
(update-insert).
477-
merge_objects (bool | None): If set to True, sub-dictionaries are merged
478+
merge_objects (bool | None): If set to `True`, sub-dictionaries are merged
478479
instead of the new one overwriting the old one. Applies only when
479480
**overwrite_mode** is set to "update" (update-insert).
480481
refill_index_caches (bool | None): Whether to add new entries to
481482
in-memory index caches if document insertions affect the edge index
482483
or cache-enabled persistent indexes.
483484
version_attribute (str | None): Support for simple external versioning to
484-
document operations. Only applicable if **overwrite** is set to true
485-
or **overwriteMode** is set to "update" or "replace".
485+
document operations. Only applicable if **overwrite** is set to `True`
486+
or **overwrite_mode** is set to "update" or "replace".
486487
487488
Returns:
488489
bool | dict: Document metadata (e.g. document id, key, revision) or `True`
@@ -543,3 +544,97 @@ def response_handler(resp: Response) -> bool | Json:
543544
raise DocumentInsertError(resp, request, msg)
544545

545546
return await self._executor.execute(request, response_handler)
547+
548+
async def update(
549+
self,
550+
document: T,
551+
ignore_revs: Optional[bool] = None,
552+
wait_for_sync: Optional[bool] = None,
553+
return_new: Optional[bool] = None,
554+
return_old: Optional[bool] = None,
555+
silent: Optional[bool] = None,
556+
keep_null: Optional[bool] = None,
557+
merge_objects: Optional[bool] = None,
558+
refill_index_caches: Optional[bool] = None,
559+
version_attribute: Optional[str] = None,
560+
) -> Result[bool | Json]:
561+
"""Insert a new document.
562+
563+
Args:
564+
document (dict): Partial or full document with the updated values.
565+
It must contain the "_key" or "_id" field.
566+
ignore_revs (bool | None): If set to `True`, the `_rev` attribute in the
567+
document is ignored. If this is set to `False`, then the `_rev`
568+
attribute given in the body document is taken as a precondition.
569+
The document is only updated if the current revision is the one
570+
specified.
571+
wait_for_sync (bool | None): Wait until document has been synced to disk.
572+
return_new (bool | None): Additionally return the complete new document
573+
under the attribute `new` in the result.
574+
return_old (bool | None): Additionally return the complete old document
575+
under the attribute `old` in the result.
576+
silent (bool | None): If set to `True`, no document metadata is returned.
577+
This can be used to save resources.
578+
keep_null (bool | None): If the intention is to delete existing attributes
579+
with the patch command, set this parameter to `False`.
580+
merge_objects (bool | None): Controls whether objects (not arrays) are
581+
merged if present in both the existing and the patch document.
582+
If set to `False`, the value in the patch document overwrites the
583+
existing document’s value. If set to `True`, objects are merged.
584+
refill_index_caches (bool | None): Whether to add new entries to
585+
in-memory index caches if document updates affect the edge index
586+
or cache-enabled persistent indexes.
587+
version_attribute (str | None): Support for simple external versioning to
588+
document operations.
589+
590+
Returns:
591+
bool | dict: Document metadata (e.g. document id, key, revision) or `True`
592+
if **silent** is set to `True`.
593+
594+
Raises:
595+
DocumentRevisionError: If precondition was violated.
596+
DocumentUpdateError: If update fails.
597+
598+
References:
599+
- `update-a-document <https://docs.arangodb.com/stable/develop/http-api/documents/#update-a-document>`__
600+
""" # noqa: E501
601+
params: Params = {}
602+
if ignore_revs is not None:
603+
params["ignoreRevs"] = ignore_revs
604+
if wait_for_sync is not None:
605+
params["waitForSync"] = wait_for_sync
606+
if return_new is not None:
607+
params["returnNew"] = return_new
608+
if return_old is not None:
609+
params["returnOld"] = return_old
610+
if silent is not None:
611+
params["silent"] = silent
612+
if keep_null is not None:
613+
params["keepNull"] = keep_null
614+
if merge_objects is not None:
615+
params["mergeObjects"] = merge_objects
616+
if refill_index_caches is not None:
617+
params["refillIndexCaches"] = refill_index_caches
618+
if version_attribute is not None:
619+
params["versionAttribute"] = version_attribute
620+
621+
request = Request(
622+
method=Method.PATCH,
623+
endpoint=f"/_api/document/{self._extract_id(cast(Json, document))}",
624+
params=params,
625+
data=self._doc_serializer.dumps(document),
626+
)
627+
628+
def response_handler(resp: Response) -> bool | Json:
629+
if resp.is_success:
630+
if silent is True:
631+
return True
632+
return self._executor.deserialize(resp.raw_body)
633+
msg: Optional[str] = None
634+
if resp.status_code == HTTP_PRECONDITION_FAILED:
635+
raise DocumentRevisionError(resp, request)
636+
elif resp.status_code == HTTP_NOT_FOUND:
637+
msg = "Document, collection or transaction not found."
638+
raise DocumentUpdateError(resp, request, msg)
639+
640+
return await self._executor.execute(request, response_handler)

arangoasync/exceptions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,10 @@ class DocumentRevisionError(ArangoServerError):
243243
"""The expected and actual document revisions mismatched."""
244244

245245

246+
class DocumentUpdateError(ArangoServerError):
247+
"""Failed to update document."""
248+
249+
246250
class IndexCreateError(ArangoServerError):
247251
"""Failed to create collection index."""
248252

tests/test_document.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
import pytest
22

3-
from arangoasync.exceptions import DocumentParseError
3+
from arangoasync.exceptions import (
4+
DocumentInsertError,
5+
DocumentParseError,
6+
DocumentUpdateError,
7+
)
48
from tests.helpers import generate_col_name
59

610

711
@pytest.mark.asyncio
8-
async def test_document_insert(doc_col, docs):
12+
async def test_document_insert(doc_col, bad_col, docs):
913
# Test insert document with no key
1014
result = await doc_col.insert({})
1115
assert await doc_col.get(result["_key"]) is not None
@@ -22,10 +26,37 @@ async def test_document_insert(doc_col, docs):
2226
await doc_col.insert({"_id": f"{generate_col_name()}/foo"})
2327
assert "Bad collection name" in err.value.message
2428

29+
with pytest.raises(DocumentInsertError):
30+
await bad_col.insert({})
31+
2532
# Test insert with default options
2633
for doc in docs:
2734
result = await doc_col.insert(doc)
2835
assert result["_id"] == f"{doc_col.name}/{doc['_key']}"
2936
assert result["_key"] == doc["_key"]
3037
assert isinstance(result["_rev"], str)
3138
assert (await doc_col.get(doc["_key"]))["val"] == doc["val"]
39+
40+
41+
@pytest.mark.asyncio
42+
async def test_document_update(doc_col, bad_col, docs):
43+
# Test updating a non-existent document
44+
with pytest.raises(DocumentUpdateError):
45+
await bad_col.update({"_key": "non-existent", "val": 42})
46+
47+
# Verbose update
48+
doc = docs[0]
49+
assert doc["val"] != 42
50+
await doc_col.insert(doc)
51+
doc["val"] = 42
52+
updated = await doc_col.update(doc)
53+
assert updated["_key"] == doc["_key"]
54+
new_value = await doc_col.get(doc)
55+
assert new_value["val"] == doc["val"]
56+
57+
# Silent update
58+
doc["val"] = None
59+
updated = await doc_col.update(doc, silent=True, keep_null=False)
60+
assert updated is True
61+
new_value = await doc_col.get(doc)
62+
assert "val" not in new_value

0 commit comments

Comments
 (0)