Skip to content

Commit 51fca52

Browse files
committed
Cursor creation works
1 parent abe25bb commit 51fca52

File tree

7 files changed

+421
-2
lines changed

7 files changed

+421
-2
lines changed

arangoasync/aql.py

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
__all__ = ["AQL"]
2+
3+
4+
from typing import Optional
5+
6+
from arangoasync.cursor import Cursor
7+
from arangoasync.exceptions import AQLQueryExecuteError
8+
from arangoasync.executor import ApiExecutor
9+
from arangoasync.request import Method, Request
10+
from arangoasync.response import Response
11+
from arangoasync.serialization import Deserializer, Serializer
12+
from arangoasync.typings import Json, Jsons, QueryProperties, Result
13+
14+
15+
class AQL:
16+
"""AQL (ArangoDB Query Language) API wrapper.
17+
18+
Allows you to execute, track, kill, explain, and validate queries written
19+
in ArangoDB’s query language.
20+
21+
Args:
22+
executor: API executor. Required to execute the API requests.
23+
"""
24+
25+
def __init__(self, executor: ApiExecutor) -> None:
26+
self._executor = executor
27+
28+
@property
29+
def name(self) -> str:
30+
"""Return the name of the current database."""
31+
return self._executor.db_name
32+
33+
@property
34+
def serializer(self) -> Serializer[Json]:
35+
"""Return the serializer."""
36+
return self._executor.serializer
37+
38+
@property
39+
def deserializer(self) -> Deserializer[Json, Jsons]:
40+
"""Return the deserializer."""
41+
return self._executor.deserializer
42+
43+
def __repr__(self) -> str:
44+
return f"<AQL in {self.name}>"
45+
46+
async def execute(
47+
self,
48+
query: str,
49+
count: Optional[bool] = None,
50+
batch_size: Optional[int] = None,
51+
bind_vars: Optional[Json] = None,
52+
cache: Optional[bool] = None,
53+
memory_limit: Optional[int] = None,
54+
ttl: Optional[int] = None,
55+
allow_dirty_read: Optional[bool] = None,
56+
options: Optional[QueryProperties] = None,
57+
) -> Result[Cursor]:
58+
"""Execute the query and return the result cursor.
59+
60+
Args:
61+
query (str): Query string to be executed.
62+
count (bool | None): If set to `True`, the total document count is
63+
calculated and included in the result cursor.
64+
batch_size (int | None): Maximum number of result documents to be
65+
transferred from the server to the client in one roundtrip.
66+
bind_vars (dict | None): An object with key/value pairs representing
67+
the bind parameters.
68+
cache (bool | None): Flag to determine whether the AQL query results
69+
cache shall be used.
70+
memory_limit (int | None): Maximum memory (in bytes) that the query is
71+
allowed to use.
72+
ttl (int | None): The time-to-live for the cursor (in seconds). The cursor
73+
will be removed on the server automatically after the specified amount
74+
of time.
75+
allow_dirty_read (bool | None): Allow reads from followers in a cluster.
76+
options (QueryProperties | None): Extra options for the query.
77+
78+
References:
79+
- `create-a-cursor <https://docs.arangodb.com/stable/develop/http-api/queries/aql-queries/#create-a-cursor>`__
80+
""" # noqa: E501
81+
data: Json = dict(query=query)
82+
if count is not None:
83+
data["count"] = count
84+
if batch_size is not None:
85+
data["batchSize"] = batch_size
86+
if bind_vars is not None:
87+
data["bindVars"] = bind_vars
88+
if cache is not None:
89+
data["cache"] = cache
90+
if memory_limit is not None:
91+
data["memoryLimit"] = memory_limit
92+
if ttl is not None:
93+
data["ttl"] = ttl
94+
if options is not None:
95+
data["options"] = options.to_dict()
96+
97+
headers = dict()
98+
if allow_dirty_read is not None:
99+
headers["x-arango-allow-dirty-read"] = str(allow_dirty_read).lower()
100+
101+
request = Request(
102+
method=Method.POST,
103+
endpoint="/_api/cursor",
104+
data=self.serializer.dumps(data),
105+
headers=headers,
106+
)
107+
108+
def response_handler(resp: Response) -> Cursor:
109+
if not resp.is_success:
110+
raise AQLQueryExecuteError(resp, request)
111+
return Cursor(self._executor, self.deserializer.loads(resp.raw_body))
112+
113+
return await self._executor.execute(request, response_handler)

arangoasync/cursor.py

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
__all__ = ["Cursor"]
2+
3+
4+
from arangoasync.executor import ApiExecutor
5+
from arangoasync.typings import Json
6+
7+
8+
class Cursor:
9+
"""Cursor API wrapper.
10+
11+
Cursors fetch query results from ArangoDB server in batches. Cursor objects
12+
are *stateful* as they store the fetched items in-memory. They must not be
13+
shared across threads without proper locking mechanism.
14+
15+
Args:
16+
executor: Required to execute the API requests.
17+
data: Cursor initialization data.
18+
"""
19+
20+
def __init__(self, executor: ApiExecutor, data: Json) -> None:
21+
self._executor = executor
22+
print(data)
23+
# TODO complete this

arangoasync/database.py

+11-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from typing import Any, List, Optional, Sequence, TypeVar, cast
99
from warnings import warn
1010

11+
from arangoasync.aql import AQL
1112
from arangoasync.collection import StandardCollection
1213
from arangoasync.connection import Connection
1314
from arangoasync.errno import HTTP_FORBIDDEN, HTTP_NOT_FOUND
@@ -80,7 +81,7 @@ def connection(self) -> Connection:
8081
@property
8182
def name(self) -> str:
8283
"""Return the name of the current database."""
83-
return self.connection.db_name
84+
return self._executor.db_name
8485

8586
@property
8687
def serializer(self) -> Serializer[Json]:
@@ -98,10 +99,18 @@ def context(self) -> str:
9899
99100
Returns:
100101
str: API execution context. Possible values are "default", "transaction".
101-
:rtype: str
102102
"""
103103
return self._executor.context
104104

105+
@property
106+
def aql(self) -> AQL:
107+
"""Return the AQL API wrapper.
108+
109+
Returns:
110+
arangoasync.aql.AQL: AQL API wrapper.
111+
"""
112+
return AQL(self._executor)
113+
105114
async def properties(self) -> Result[DatabaseProperties]:
106115
"""Return database properties.
107116

arangoasync/exceptions.py

+4
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ def __init__(
7171
self.http_headers = resp.headers
7272

7373

74+
class AQLQueryExecuteError(ArangoServerError):
75+
"""Failed to execute query."""
76+
77+
7478
class AuthHeaderError(ArangoClientError):
7579
"""The authentication header could not be determined."""
7680

0 commit comments

Comments
 (0)