4
4
from typing import Generic , List , Optional , Tuple , TypeVar , cast
5
5
6
6
from arangoasync .errno import (
7
+ DOCUMENT_NOT_FOUND ,
7
8
HTTP_BAD_PARAMETER ,
8
9
HTTP_NOT_FOUND ,
9
10
HTTP_PRECONDITION_FAILED ,
10
11
)
11
12
from arangoasync .exceptions import (
12
13
CollectionPropertiesError ,
14
+ DocumentDeleteError ,
13
15
DocumentGetError ,
14
16
DocumentInsertError ,
15
17
DocumentParseError ,
33
35
Json ,
34
36
Jsons ,
35
37
Params ,
38
+ RequestHeaders ,
36
39
)
37
40
38
41
T = TypeVar ("T" )
@@ -392,23 +395,23 @@ def response_handler(resp: Response) -> CollectionProperties:
392
395
async def get (
393
396
self ,
394
397
document : str | Json ,
395
- rev : Optional [str ] = None ,
396
- check_rev : bool = True ,
397
398
allow_dirty_read : bool = False ,
399
+ if_match : Optional [str ] = None ,
400
+ if_none_match : Optional [str ] = None ,
398
401
) -> Result [Optional [U ]]:
399
402
"""Return a document.
400
403
401
404
Args:
402
405
document (str | dict): Document ID, key or body.
403
- Document body must contain the "_id" or "_key" field.
404
- rev (str | None): Expected document revision. Overrides the
405
- value of "_rev" field in **document** if present.
406
- check_rev (bool): If set to True, revision of **document** (if given)
407
- is compared against the revision of target document.
406
+ Document body must contain the "_id" or "_key" field.
408
407
allow_dirty_read (bool): Allow reads from followers in a cluster.
408
+ if_match (str | None): The document is returned, if it has the same
409
+ revision as the given ETag.
410
+ if_none_match (str | None): The document is returned, if it has a
411
+ different revision than the given ETag.
409
412
410
413
Returns:
411
- Document or None if not found.
414
+ Document or ` None` if not found.
412
415
413
416
Raises:
414
417
DocumentRevisionError: If the revision is incorrect.
@@ -417,10 +420,15 @@ async def get(
417
420
References:
418
421
- `get-a-document <https://docs.arangodb.com/stable/develop/http-api/documents/#get-a-document>`__
419
422
""" # noqa: E501
420
- handle , headers = self ._prep_from_doc (document , rev , check_rev )
423
+ handle , _ = self ._prep_from_doc (document )
421
424
425
+ headers : RequestHeaders = {}
422
426
if allow_dirty_read :
423
427
headers ["x-arango-allow-dirty-read" ] = "true"
428
+ if if_match is not None :
429
+ headers ["If-Match" ] = if_match
430
+ if if_none_match is not None :
431
+ headers ["If-None-Match" ] = if_none_match
424
432
425
433
request = Request (
426
434
method = Method .GET ,
@@ -432,7 +440,66 @@ def response_handler(resp: Response) -> Optional[U]:
432
440
if resp .is_success :
433
441
return self ._doc_deserializer .loads (resp .raw_body )
434
442
elif resp .status_code == HTTP_NOT_FOUND :
435
- return None
443
+ if resp .error_code == DOCUMENT_NOT_FOUND :
444
+ return None
445
+ else :
446
+ raise DocumentGetError (resp , request )
447
+ elif resp .status_code == HTTP_PRECONDITION_FAILED :
448
+ raise DocumentRevisionError (resp , request )
449
+ else :
450
+ raise DocumentGetError (resp , request )
451
+
452
+ return await self ._executor .execute (request , response_handler )
453
+
454
+ async def has (
455
+ self ,
456
+ document : str | Json ,
457
+ allow_dirty_read : bool = False ,
458
+ if_match : Optional [str ] = None ,
459
+ if_none_match : Optional [str ] = None ,
460
+ ) -> Result [bool ]:
461
+ """Check if a document exists in the collection.
462
+
463
+ Args:
464
+ document (str | dict): Document ID, key or body.
465
+ Document body must contain the "_id" or "_key" field.
466
+ allow_dirty_read (bool): Allow reads from followers in a cluster.
467
+ if_match (str | None): The document is returned, if it has the same
468
+ revision as the given ETag.
469
+ if_none_match (str | None): The document is returned, if it has a
470
+ different revision than the given ETag.
471
+
472
+ Returns:
473
+ `True` if the document exists, `False` otherwise.
474
+
475
+ Raises:
476
+ DocumentRevisionError: If the revision is incorrect.
477
+ DocumentGetError: If retrieval fails.
478
+
479
+ References:
480
+ - `get-a-document-header <https://docs.arangodb.com/stable/develop/http-api/documents/#get-a-document-header>`__
481
+ """ # noqa: E501
482
+ handle , _ = self ._prep_from_doc (document )
483
+
484
+ headers : RequestHeaders = {}
485
+ if allow_dirty_read :
486
+ headers ["x-arango-allow-dirty-read" ] = "true"
487
+ if if_match is not None :
488
+ headers ["If-Match" ] = if_match
489
+ if if_none_match is not None :
490
+ headers ["If-None-Match" ] = if_none_match
491
+
492
+ request = Request (
493
+ method = Method .HEAD ,
494
+ endpoint = f"/_api/document/{ handle } " ,
495
+ headers = headers ,
496
+ )
497
+
498
+ def response_handler (resp : Response ) -> bool :
499
+ if resp .is_success :
500
+ return True
501
+ elif resp .status_code == HTTP_NOT_FOUND :
502
+ return False
436
503
elif resp .status_code == HTTP_PRECONDITION_FAILED :
437
504
raise DocumentRevisionError (resp , request )
438
505
else :
@@ -558,6 +625,7 @@ async def update(
558
625
merge_objects : Optional [bool ] = None ,
559
626
refill_index_caches : Optional [bool ] = None ,
560
627
version_attribute : Optional [str ] = None ,
628
+ if_match : Optional [str ] = None ,
561
629
) -> Result [bool | Json ]:
562
630
"""Insert a new document.
563
631
@@ -587,6 +655,8 @@ async def update(
587
655
or cache-enabled persistent indexes.
588
656
version_attribute (str | None): Support for simple external versioning to
589
657
document operations.
658
+ if_match (str | None): You can conditionally update a document based on a
659
+ target revision id by using the "if-match" HTTP header.
590
660
591
661
Returns:
592
662
bool | dict: Document metadata (e.g. document id, key, revision) or `True`
@@ -619,10 +689,15 @@ async def update(
619
689
if version_attribute is not None :
620
690
params ["versionAttribute" ] = version_attribute
621
691
692
+ headers : RequestHeaders = {}
693
+ if if_match is not None :
694
+ headers ["If-Match" ] = if_match
695
+
622
696
request = Request (
623
697
method = Method .PATCH ,
624
698
endpoint = f"/_api/document/{ self ._extract_id (cast (Json , document ))} " ,
625
699
params = params ,
700
+ headers = headers ,
626
701
data = self ._doc_serializer .dumps (document ),
627
702
)
628
703
@@ -650,6 +725,7 @@ async def replace(
650
725
silent : Optional [bool ] = None ,
651
726
refill_index_caches : Optional [bool ] = None ,
652
727
version_attribute : Optional [str ] = None ,
728
+ if_match : Optional [str ] = None ,
653
729
) -> Result [bool | Json ]:
654
730
"""Replace a document.
655
731
@@ -673,6 +749,8 @@ async def replace(
673
749
or cache-enabled persistent indexes.
674
750
version_attribute (str | None): Support for simple external versioning to
675
751
document operations.
752
+ if_match (str | None): You can conditionally replace a document based on a
753
+ target revision id by using the "if-match" HTTP header.
676
754
677
755
Returns:
678
756
bool | dict: Document metadata (e.g. document id, key, revision) or `True`
@@ -701,10 +779,15 @@ async def replace(
701
779
if version_attribute is not None :
702
780
params ["versionAttribute" ] = version_attribute
703
781
782
+ headers : RequestHeaders = {}
783
+ if if_match is not None :
784
+ headers ["If-Match" ] = if_match
785
+
704
786
request = Request (
705
787
method = Method .PUT ,
706
788
endpoint = f"/_api/document/{ self ._extract_id (cast (Json , document ))} " ,
707
789
params = params ,
790
+ headers = headers ,
708
791
data = self ._doc_serializer .dumps (document ),
709
792
)
710
793
@@ -721,3 +804,88 @@ def response_handler(resp: Response) -> bool | Json:
721
804
raise DocumentReplaceError (resp , request , msg )
722
805
723
806
return await self ._executor .execute (request , response_handler )
807
+
808
+ async def delete (
809
+ self ,
810
+ document : T ,
811
+ ignore_revs : Optional [bool ] = None ,
812
+ ignore_missing : bool = False ,
813
+ wait_for_sync : Optional [bool ] = None ,
814
+ return_old : Optional [bool ] = None ,
815
+ silent : Optional [bool ] = None ,
816
+ refill_index_caches : Optional [bool ] = None ,
817
+ if_match : Optional [str ] = None ,
818
+ ) -> Result [bool | Json ]:
819
+ """Delete a document.
820
+
821
+ Args:
822
+ document (dict): Document ID, key or body. The body must contain the
823
+ "_key" or "_id" field.
824
+ ignore_revs (bool | None): If set to `True`, the `_rev` attribute in the
825
+ document is ignored. If this is set to `False`, then the `_rev`
826
+ attribute given in the body document is taken as a precondition.
827
+ The document is only replaced if the current revision is the one
828
+ specified.
829
+ ignore_missing (bool): Do not raise an exception on missing document.
830
+ This parameter has no effect in transactions where an exception is
831
+ always raised on failures.
832
+ wait_for_sync (bool | None): Wait until operation has been synced to disk.
833
+ return_old (bool | None): Additionally return the complete old document
834
+ under the attribute `old` in the result.
835
+ silent (bool | None): If set to `True`, no document metadata is returned.
836
+ This can be used to save resources.
837
+ refill_index_caches (bool | None): Whether to add new entries to
838
+ in-memory index caches if document updates affect the edge index
839
+ or cache-enabled persistent indexes.
840
+ if_match (bool | None): You can conditionally remove a document based
841
+ on a target revision id by using the "if-match" HTTP header.
842
+
843
+ Returns:
844
+ bool | dict: Document metadata (e.g. document id, key, revision) or `True`
845
+ if **silent** is set to `True` and the document was found.
846
+
847
+ Raises:
848
+ DocumentRevisionError: If precondition was violated.
849
+ DocumentDeleteError: If deletion fails.
850
+
851
+ References:
852
+ - `remove-a-document <https://docs.arangodb.com/stable/develop/http-api/documents/#remove-a-document>`__
853
+ """ # noqa: E501
854
+ params : Params = {}
855
+ if ignore_revs is not None :
856
+ params ["ignoreRevs" ] = ignore_revs
857
+ if wait_for_sync is not None :
858
+ params ["waitForSync" ] = wait_for_sync
859
+ if return_old is not None :
860
+ params ["returnOld" ] = return_old
861
+ if silent is not None :
862
+ params ["silent" ] = silent
863
+ if refill_index_caches is not None :
864
+ params ["refillIndexCaches" ] = refill_index_caches
865
+
866
+ headers : RequestHeaders = {}
867
+ if if_match is not None :
868
+ headers ["If-Match" ] = if_match
869
+
870
+ request = Request (
871
+ method = Method .DELETE ,
872
+ endpoint = f"/_api/document/{ self ._extract_id (cast (Json , document ))} " ,
873
+ params = params ,
874
+ headers = headers ,
875
+ )
876
+
877
+ def response_handler (resp : Response ) -> bool | Json :
878
+ if resp .is_success :
879
+ if silent is True :
880
+ return True
881
+ return self ._executor .deserialize (resp .raw_body )
882
+ msg : Optional [str ] = None
883
+ if resp .status_code == HTTP_PRECONDITION_FAILED :
884
+ raise DocumentRevisionError (resp , request )
885
+ elif resp .status_code == HTTP_NOT_FOUND :
886
+ if resp .error_code == DOCUMENT_NOT_FOUND and ignore_missing :
887
+ return False
888
+ msg = "Document, collection or transaction not found."
889
+ raise DocumentDeleteError (resp , request , msg )
890
+
891
+ return await self ._executor .execute (request , response_handler )
0 commit comments