Skip to content

Commit 6c2da6d

Browse files
committed
Completed cursor documentation
1 parent 2355c1b commit 6c2da6d

File tree

1 file changed

+211
-2
lines changed

1 file changed

+211
-2
lines changed

docs/cursor.rst

Lines changed: 211 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,217 @@
11
Cursors
22
-------
33

4-
Many operations provided by python-arango-async (e.g. executing :doc:`aql` queries)
4+
Many operations provided by the driver (e.g. executing :doc:`aql` queries)
55
return result **cursors** to batch the network communication between ArangoDB
6-
server and python-arango-async client. Each HTTP request from a cursor fetches the
6+
server and the client. Each HTTP request from a cursor fetches the
77
next batch of results (usually documents). Depending on the query, the total
88
number of items in the result set may or may not be known in advance.
9+
10+
**Example:**
11+
12+
.. code-block:: python
13+
14+
from arangoasync import ArangoClient
15+
from arangoasync.auth import Auth
16+
17+
# Initialize the client for ArangoDB.
18+
async with ArangoClient(hosts="http://localhost:8529") as client:
19+
auth = Auth(username="root", password="passwd")
20+
21+
# Connect to "test" database as root user.
22+
db = await client.db("test", auth=auth)
23+
24+
# Set up some test data to query against.
25+
await db.collection("students").insert_many([
26+
{"_key": "Abby", "age": 22},
27+
{"_key": "John", "age": 18},
28+
{"_key": "Mary", "age": 21},
29+
{"_key": "Suzy", "age": 23},
30+
{"_key": "Dave", "age": 20}
31+
])
32+
33+
# Execute an AQL query which returns a cursor object.
34+
cursor = await db.aql.execute(
35+
"FOR doc IN students FILTER doc.age > @val RETURN doc",
36+
bind_vars={"val": 17},
37+
batch_size=2,
38+
count=True
39+
)
40+
41+
# Get the cursor ID.
42+
cid = cursor.id
43+
44+
# Get the items in the current batch.
45+
batch = cursor.batch
46+
47+
# Check if the current batch is empty.
48+
is_empty = cursor.empty()
49+
50+
# Get the total count of the result set.
51+
cnt = cursor.count
52+
53+
# Flag indicating if there are more to be fetched from server.
54+
has_more = cursor.has_more
55+
56+
# Flag indicating if the results are cached.
57+
is_cached = cursor.cached
58+
59+
# Get the cursor statistics.
60+
stats = cursor.statistics
61+
62+
# Get the performance profile.
63+
profile = cursor.profile
64+
65+
# Get any warnings produced from the query.
66+
warnings = cursor.warnings
67+
68+
# Return the next item from the cursor. If current batch is depleted, the
69+
# next batch is fetched from the server automatically.
70+
await cursor.next()
71+
72+
# Return the next item from the cursor. If current batch is depleted, an
73+
# exception is thrown. You need to fetch the next batch manually.
74+
cursor.pop()
75+
76+
# Fetch the next batch and add them to the cursor object.
77+
await cursor.fetch()
78+
79+
# Delete the cursor from the server.
80+
await cursor.close()
81+
82+
See :class:`arangoasync.cursor.Cursor` for API specification.
83+
84+
Cursors can be used together with a context manager to ensure that the resources get freed up
85+
when the cursor is no longer needed. Asynchronous iteration is also supported, allowing you to
86+
iterate over the cursor results without blocking the event loop.
87+
88+
**Example:**
89+
90+
.. code-block:: python
91+
92+
from arangoasync import ArangoClient
93+
from arangoasync.auth import Auth
94+
from arangoasync.exceptions import CursorCloseError
95+
96+
# Initialize the client for ArangoDB.
97+
async with ArangoClient(hosts="http://localhost:8529") as client:
98+
auth = Auth(username="root", password="passwd")
99+
100+
# Connect to "test" database as root user.
101+
db = await client.db("test", auth=auth)
102+
103+
# Set up some test data to query against.
104+
await db.collection("students").insert_many([
105+
{"_key": "Abby", "age": 22},
106+
{"_key": "John", "age": 18},
107+
{"_key": "Mary", "age": 21},
108+
{"_key": "Suzy", "age": 23},
109+
{"_key": "Dave", "age": 20}
110+
])
111+
112+
# Execute an AQL query which returns a cursor object.
113+
cursor = await db.aql.execute(
114+
"FOR doc IN students FILTER doc.age > @val RETURN doc",
115+
bind_vars={"val": 17},
116+
batch_size=2,
117+
count=True
118+
)
119+
120+
# Iterate over the cursor in an async context manager.
121+
async with cursor as ctx:
122+
async for student in ctx:
123+
print(student)
124+
125+
# The cursor is automatically closed when exiting the context manager.
126+
try:
127+
await cursor.close()
128+
except CursorCloseError:
129+
print(f"Cursor already closed!")
130+
131+
If the fetched result batch is depleted while you are iterating over a cursor
132+
(or while calling the method :func:`arangoasync.cursor.Cursor.next`), the driver
133+
automatically sends an HTTP request to the server in order to fetch the next batch
134+
(just-in-time style). To control exactly when the fetches occur, you can use
135+
methods like :func:`arangoasync.cursor.Cursor.fetch` and :func:`arangoasync.cursor.Cursor.pop`
136+
instead.
137+
138+
**Example:**
139+
140+
.. code-block:: python
141+
142+
from arangoasync import ArangoClient
143+
from arangoasync.auth import Auth
144+
145+
# Initialize the client for ArangoDB.
146+
async with ArangoClient(hosts="http://localhost:8529") as client:
147+
auth = Auth(username="root", password="passwd")
148+
149+
# Connect to "test" database as root user.
150+
db = await client.db("test", auth=auth)
151+
152+
# Set up some test data to query against.
153+
await db.collection("students").insert_many([
154+
{"_key": "Abby", "age": 22},
155+
{"_key": "John", "age": 18},
156+
{"_key": "Mary", "age": 21}
157+
])
158+
159+
# You can manually fetch and pop for finer control.
160+
cursor = await db.aql.execute("FOR doc IN students RETURN doc", batch_size=1)
161+
while cursor.has_more: # Fetch until nothing is left on the server.
162+
await cursor.fetch()
163+
while not cursor.empty(): # Pop until nothing is left on the cursor.
164+
student = cursor.pop()
165+
print(student)
166+
167+
You can use the `allow_retry` parameter of :func:`arangoasync.aql.AQL.execute`
168+
to automatically retry the request if the cursor encountered any issues during
169+
the previous fetch operation. Note that this feature causes the server to
170+
cache the last batch. To allow re-fetching of the very last batch of the query,
171+
the server cannot automatically delete the cursor. Once you have successfully
172+
received the last batch, you should call :func:`arangoasync.cursor.Cursor.close`,
173+
or use a context manager to ensure the cursor is closed properly.
174+
175+
**Example:**
176+
177+
.. code-block:: python
178+
179+
from arangoasync import ArangoClient
180+
from arangoasync.auth import Auth
181+
from arangoasync.typings import QueryProperties
182+
183+
# Initialize the client for ArangoDB.
184+
async with ArangoClient(hosts="http://localhost:8529") as client:
185+
auth = Auth(username="root", password="passwd")
186+
187+
# Connect to "test" database as root user.
188+
db = await client.db("test", auth=auth)
189+
190+
# Set up some test data to query against.
191+
await db.collection("students").insert_many([
192+
{"_key": "Abby", "age": 22},
193+
{"_key": "John", "age": 18},
194+
{"_key": "Mary", "age": 21}
195+
])
196+
197+
cursor = await db.aql.execute(
198+
"FOR doc IN students RETURN doc",
199+
batch_size=1,
200+
options=QueryProperties(allow_retry=True)
201+
)
202+
203+
while cursor.has_more:
204+
try:
205+
await cursor.fetch()
206+
except ConnectionError:
207+
# Retry the request.
208+
continue
209+
210+
while not cursor.empty():
211+
student = cursor.pop()
212+
print(student)
213+
214+
# Delete the cursor from the server.
215+
await cursor.close()
216+
217+
For more information about various query properties, see :class:`arangoasync.typings.QueryProperties`.

0 commit comments

Comments
 (0)