Skip to content

Commit

Permalink
Cursor creation works
Browse files Browse the repository at this point in the history
  • Loading branch information
apetenchea committed Jan 4, 2025
1 parent abe25bb commit 51fca52
Show file tree
Hide file tree
Showing 7 changed files with 421 additions and 2 deletions.
113 changes: 113 additions & 0 deletions arangoasync/aql.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
__all__ = ["AQL"]


from typing import Optional

from arangoasync.cursor import Cursor
from arangoasync.exceptions import AQLQueryExecuteError
from arangoasync.executor import ApiExecutor
from arangoasync.request import Method, Request
from arangoasync.response import Response
from arangoasync.serialization import Deserializer, Serializer
from arangoasync.typings import Json, Jsons, QueryProperties, Result


class AQL:
"""AQL (ArangoDB Query Language) API wrapper.
Allows you to execute, track, kill, explain, and validate queries written
in ArangoDB’s query language.
Args:
executor: API executor. Required to execute the API requests.
"""

def __init__(self, executor: ApiExecutor) -> None:
self._executor = executor

@property
def name(self) -> str:
"""Return the name of the current database."""
return self._executor.db_name

@property
def serializer(self) -> Serializer[Json]:
"""Return the serializer."""
return self._executor.serializer

@property
def deserializer(self) -> Deserializer[Json, Jsons]:
"""Return the deserializer."""
return self._executor.deserializer

def __repr__(self) -> str:
return f"<AQL in {self.name}>"

async def execute(
self,
query: str,
count: Optional[bool] = None,
batch_size: Optional[int] = None,
bind_vars: Optional[Json] = None,
cache: Optional[bool] = None,
memory_limit: Optional[int] = None,
ttl: Optional[int] = None,
allow_dirty_read: Optional[bool] = None,
options: Optional[QueryProperties] = None,
) -> Result[Cursor]:
"""Execute the query and return the result cursor.
Args:
query (str): Query string to be executed.
count (bool | None): If set to `True`, the total document count is
calculated and included in the result cursor.
batch_size (int | None): Maximum number of result documents to be
transferred from the server to the client in one roundtrip.
bind_vars (dict | None): An object with key/value pairs representing
the bind parameters.
cache (bool | None): Flag to determine whether the AQL query results
cache shall be used.
memory_limit (int | None): Maximum memory (in bytes) that the query is
allowed to use.
ttl (int | None): The time-to-live for the cursor (in seconds). The cursor
will be removed on the server automatically after the specified amount
of time.
allow_dirty_read (bool | None): Allow reads from followers in a cluster.
options (QueryProperties | None): Extra options for the query.
References:
- `create-a-cursor <https://docs.arangodb.com/stable/develop/http-api/queries/aql-queries/#create-a-cursor>`__
""" # noqa: E501
data: Json = dict(query=query)
if count is not None:
data["count"] = count
if batch_size is not None:
data["batchSize"] = batch_size
if bind_vars is not None:
data["bindVars"] = bind_vars
if cache is not None:
data["cache"] = cache
if memory_limit is not None:
data["memoryLimit"] = memory_limit
if ttl is not None:
data["ttl"] = ttl
if options is not None:
data["options"] = options.to_dict()

headers = dict()
if allow_dirty_read is not None:
headers["x-arango-allow-dirty-read"] = str(allow_dirty_read).lower()

request = Request(
method=Method.POST,
endpoint="/_api/cursor",
data=self.serializer.dumps(data),
headers=headers,
)

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

return await self._executor.execute(request, response_handler)
23 changes: 23 additions & 0 deletions arangoasync/cursor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
__all__ = ["Cursor"]


from arangoasync.executor import ApiExecutor
from arangoasync.typings import Json


class Cursor:
"""Cursor API wrapper.
Cursors fetch query results from ArangoDB server in batches. Cursor objects
are *stateful* as they store the fetched items in-memory. They must not be
shared across threads without proper locking mechanism.
Args:
executor: Required to execute the API requests.
data: Cursor initialization data.
"""

def __init__(self, executor: ApiExecutor, data: Json) -> None:
self._executor = executor
print(data)
# TODO complete this
13 changes: 11 additions & 2 deletions arangoasync/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from typing import Any, List, Optional, Sequence, TypeVar, cast
from warnings import warn

from arangoasync.aql import AQL
from arangoasync.collection import StandardCollection
from arangoasync.connection import Connection
from arangoasync.errno import HTTP_FORBIDDEN, HTTP_NOT_FOUND
Expand Down Expand Up @@ -80,7 +81,7 @@ def connection(self) -> Connection:
@property
def name(self) -> str:
"""Return the name of the current database."""
return self.connection.db_name
return self._executor.db_name

@property
def serializer(self) -> Serializer[Json]:
Expand All @@ -98,10 +99,18 @@ def context(self) -> str:
Returns:
str: API execution context. Possible values are "default", "transaction".
:rtype: str
"""
return self._executor.context

@property
def aql(self) -> AQL:
"""Return the AQL API wrapper.
Returns:
arangoasync.aql.AQL: AQL API wrapper.
"""
return AQL(self._executor)

async def properties(self) -> Result[DatabaseProperties]:
"""Return database properties.
Expand Down
4 changes: 4 additions & 0 deletions arangoasync/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ def __init__(
self.http_headers = resp.headers


class AQLQueryExecuteError(ArangoServerError):
"""Failed to execute query."""


class AuthHeaderError(ArangoClientError):
"""The authentication header could not be determined."""

Expand Down
Loading

0 comments on commit 51fca52

Please sign in to comment.