Skip to content

Collection API completeness #59

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
240 changes: 233 additions & 7 deletions arangoasync/collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@
HTTP_PRECONDITION_FAILED,
)
from arangoasync.exceptions import (
CollectionChecksumError,
CollectionConfigureError,
CollectionPropertiesError,
CollectionResponsibleShardError,
CollectionRevisionError,
CollectionShardsError,
CollectionStatisticsError,
CollectionTruncateError,
DocumentCountError,
DocumentDeleteError,
Expand All @@ -41,6 +47,7 @@
from arangoasync.serialization import Deserializer, Serializer
from arangoasync.typings import (
CollectionProperties,
CollectionStatistics,
IndexProperties,
Json,
Jsons,
Expand Down Expand Up @@ -501,7 +508,71 @@ async def properties(self) -> Result[CollectionProperties]:
def response_handler(resp: Response) -> CollectionProperties:
if not resp.is_success:
raise CollectionPropertiesError(resp, request)
return CollectionProperties(self._executor.deserialize(resp.raw_body))
return CollectionProperties(self.deserializer.loads(resp.raw_body))

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

async def configure(
self,
cache_enabled: Optional[bool] = None,
computed_values: Optional[Jsons] = None,
replication_factor: Optional[int | str] = None,
schema: Optional[Json] = None,
wait_for_sync: Optional[bool] = None,
write_concern: Optional[int] = None,
) -> Result[CollectionProperties]:
"""Changes the properties of a collection.

Only the provided attributes are updated.

Args:
cache_enabled (bool | None): Whether the in-memory hash cache
for documents should be enabled for this collection.
computed_values (list | None): An optional list of objects, each
representing a computed value.
replication_factor (int | None): In a cluster, this attribute determines
how many copies of each shard are kept on different DB-Servers.
For SatelliteCollections, it needs to be the string "satellite".
schema (dict | None): The configuration of the collection-level schema
validation for documents.
wait_for_sync (bool | None): If set to `True`, the data is synchronized
to disk before returning from a document create, update, replace or
removal operation.
write_concern (int | None): Determines how many copies of each shard are
required to be in sync on the different DB-Servers.

Returns:
CollectionProperties: Properties.

Raises:
CollectionConfigureError: If configuration fails.

References:
- `change-the-properties-of-a-collection <https://docs.arangodb.com/stable/develop/http-api/collections/#change-the-properties-of-a-collection>`__
""" # noqa: E501
data: Json = {}
if cache_enabled is not None:
data["cacheEnabled"] = cache_enabled
if computed_values is not None:
data["computedValues"] = computed_values
if replication_factor is not None:
data["replicationFactor"] = replication_factor
if schema is not None:
data["schema"] = schema
if wait_for_sync is not None:
data["waitForSync"] = wait_for_sync
if write_concern is not None:
data["writeConcern"] = write_concern
request = Request(
method=Method.PUT,
endpoint=f"/_api/collection/{self.name}/properties",
data=self.serializer.dumps(data),
)

def response_handler(resp: Response) -> CollectionProperties:
if not resp.is_success:
raise CollectionConfigureError(resp, request)
return CollectionProperties(self.deserializer.loads(resp.raw_body))

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

Expand Down Expand Up @@ -552,7 +623,10 @@ async def count(self) -> Result[int]:

Raises:
DocumentCountError: If retrieval fails.
"""

References:
- `get-the-document-count-of-a-collection <https://docs.arangodb.com/stable/develop/http-api/collections/#get-the-document-count-of-a-collection>`__
""" # noqa: E501
request = Request(
method=Method.GET, endpoint=f"/_api/collection/{self.name}/count"
)
Expand All @@ -565,6 +639,158 @@ def response_handler(resp: Response) -> int:

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

async def statistics(self) -> Result[CollectionStatistics]:
"""Get additional statistical information about the collection.

Returns:
CollectionStatistics: Collection statistics.

Raises:
CollectionStatisticsError: If retrieval fails.

References:
- `get-the-collection-statistics <https://docs.arangodb.com/stable/develop/http-api/collections/#get-the-collection-statistics>`__
""" # noqa: E501
request = Request(
method=Method.GET,
endpoint=f"/_api/collection/{self.name}/figures",
)

def response_handler(resp: Response) -> CollectionStatistics:
if not resp.is_success:
raise CollectionStatisticsError(resp, request)
return CollectionStatistics(self.deserializer.loads(resp.raw_body))

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

async def responsible_shard(self, document: Json) -> Result[str]:
"""Return the ID of the shard responsible for given document.

If the document does not exist, return the shard that would be
responsible.

Args:
document (dict): Document body with "_key" field.

Returns:
str: Shard ID.

Raises:
CollectionResponsibleShardError: If retrieval fails.

References:
- `get-the-responsible-shard-for-a-document <https://docs.arangodb.com/stable/develop/http-api/collections/#get-the-responsible-shard-for-a-document>`__
""" # noqa: E501
request = Request(
method=Method.PUT,
endpoint=f"/_api/collection/{self.name}/responsibleShard",
data=self.serializer.dumps(document),
)

def response_handler(resp: Response) -> str:
if resp.is_success:
body = self.deserializer.loads(resp.raw_body)
return cast(str, body["shardId"])
raise CollectionResponsibleShardError(resp, request)

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

async def shards(self, details: Optional[bool] = None) -> Result[Json]:
"""Return collection shards and properties.

Available only in a cluster setup.

Args:
details (bool | None): If set to `True`, include responsible
servers for these shards.

Returns:
dict: Collection shards.

Raises:
CollectionShardsError: If retrieval fails.

References:
- `get-the-shard-ids-of-a-collection <https://docs.arangodb.com/stable/develop/http-api/collections/#get-the-shard-ids-of-a-collection>`__
""" # noqa: E501
params: Params = {}
if details is not None:
params["details"] = details

request = Request(
method=Method.GET,
endpoint=f"/_api/collection/{self.name}/shards",
params=params,
)

def response_handler(resp: Response) -> Json:
if not resp.is_success:
raise CollectionShardsError(resp, request)
return cast(Json, self.deserializer.loads(resp.raw_body)["shards"])

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

async def revision(self) -> Result[str]:
"""Return collection revision.

Returns:
str: Collection revision.

Raises:
CollectionRevisionError: If retrieval fails.

References:
- `get-the-collection-revision-id <https://docs.arangodb.com/stable/develop/http-api/collections/#get-the-collection-revision-id>`__
""" # noqa: E501
request = Request(
method=Method.GET,
endpoint=f"/_api/collection/{self.name}/revision",
)

def response_handler(resp: Response) -> str:
if not resp.is_success:
raise CollectionRevisionError(resp, request)
return cast(str, self.deserializer.loads(resp.raw_body)["revision"])

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

async def checksum(
self, with_rev: Optional[bool] = None, with_data: Optional[bool] = None
) -> Result[str]:
"""Calculate collection checksum.

Args:
with_rev (bool | None): Include document revisions in checksum calculation.
with_data (bool | None): Include document data in checksum calculation.

Returns:
str: Collection checksum.

Raises:
CollectionChecksumError: If retrieval fails.

References:
- `get-the-collection-checksum <https://docs.arangodb.com/stable/develop/http-api/collections/#get-the-collection-checksum>`__
""" # noqa: E501
params: Params = {}
if with_rev is not None:
params["withRevision"] = with_rev
if with_data is not None:
params["withData"] = with_data

request = Request(
method=Method.GET,
endpoint=f"/_api/collection/{self.name}/checksum",
params=params,
)

def response_handler(resp: Response) -> str:
if not resp.is_success:
raise CollectionChecksumError(resp, request)
return cast(str, self.deserializer.loads(resp.raw_body)["checksum"])

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

async def has(
self,
document: str | Json,
Expand Down Expand Up @@ -1444,9 +1670,9 @@ async def insert(

def response_handler(resp: Response) -> bool | Json:
if resp.is_success:
if silent is True:
if silent:
return True
return self._executor.deserialize(resp.raw_body)
return self.deserializer.loads(resp.raw_body)
msg: Optional[str] = None
if resp.status_code == HTTP_BAD_PARAMETER:
msg = (
Expand Down Expand Up @@ -1551,7 +1777,7 @@ def response_handler(resp: Response) -> bool | Json:
if resp.is_success:
if silent is True:
return True
return self._executor.deserialize(resp.raw_body)
return self.deserializer.loads(resp.raw_body)
msg: Optional[str] = None
if resp.status_code == HTTP_PRECONDITION_FAILED:
raise DocumentRevisionError(resp, request)
Expand Down Expand Up @@ -1641,7 +1867,7 @@ def response_handler(resp: Response) -> bool | Json:
if resp.is_success:
if silent is True:
return True
return self._executor.deserialize(resp.raw_body)
return self.deserializer.loads(resp.raw_body)
msg: Optional[str] = None
if resp.status_code == HTTP_PRECONDITION_FAILED:
raise DocumentRevisionError(resp, request)
Expand Down Expand Up @@ -1726,7 +1952,7 @@ def response_handler(resp: Response) -> bool | Json:
if resp.is_success:
if silent is True:
return True
return self._executor.deserialize(resp.raw_body)
return self.deserializer.loads(resp.raw_body)
msg: Optional[str] = None
if resp.status_code == HTTP_PRECONDITION_FAILED:
raise DocumentRevisionError(resp, request)
Expand Down
24 changes: 24 additions & 0 deletions arangoasync/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
AsyncJobListError,
CollectionCreateError,
CollectionDeleteError,
CollectionKeyGeneratorsError,
CollectionListError,
DatabaseCreateError,
DatabaseDeleteError,
Expand Down Expand Up @@ -695,6 +696,29 @@ def response_handler(resp: Response) -> bool:

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

async def key_generators(self) -> Result[List[str]]:
"""Returns the available key generators for collections.

Returns:
list: List of available key generators.

Raises:
CollectionKeyGeneratorsError: If retrieval fails.

References:
- `get-the-available-key-generators <https://docs.arangodb.com/stable/develop/http-api/collections/#get-the-available-key-generators>`__
""" # noqa: E501
request = Request(method=Method.GET, endpoint="/_api/key-generators")

def response_handler(resp: Response) -> List[str]:
if not resp.is_success:
raise CollectionKeyGeneratorsError(resp, request)
return cast(
List[str], self.deserializer.loads(resp.raw_body)["keyGenerators"]
)

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

async def has_document(
self,
document: str | Json,
Expand Down
Loading
Loading