python-arango-async 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
arangoasync/aql.py ADDED
@@ -0,0 +1,760 @@
1
+ __all__ = ["AQL", "AQLQueryCache"]
2
+
3
+
4
+ from typing import Optional, cast
5
+
6
+ from arangoasync.cursor import Cursor
7
+ from arangoasync.errno import HTTP_NOT_FOUND
8
+ from arangoasync.exceptions import (
9
+ AQLCacheClearError,
10
+ AQLCacheConfigureError,
11
+ AQLCacheEntriesError,
12
+ AQLCachePropertiesError,
13
+ AQLFunctionCreateError,
14
+ AQLFunctionDeleteError,
15
+ AQLFunctionListError,
16
+ AQLQueryClearError,
17
+ AQLQueryExecuteError,
18
+ AQLQueryExplainError,
19
+ AQLQueryKillError,
20
+ AQLQueryListError,
21
+ AQLQueryRulesGetError,
22
+ AQLQueryTrackingGetError,
23
+ AQLQueryTrackingSetError,
24
+ AQLQueryValidateError,
25
+ )
26
+ from arangoasync.executor import ApiExecutor, DefaultApiExecutor, NonAsyncExecutor
27
+ from arangoasync.request import Method, Request
28
+ from arangoasync.response import Response
29
+ from arangoasync.result import Result
30
+ from arangoasync.serialization import Deserializer, Serializer
31
+ from arangoasync.typings import (
32
+ Json,
33
+ Jsons,
34
+ QueryCacheProperties,
35
+ QueryExplainOptions,
36
+ QueryProperties,
37
+ QueryTrackingConfiguration,
38
+ )
39
+
40
+
41
+ class AQLQueryCache:
42
+ """AQL Query Cache API wrapper.
43
+
44
+ Args:
45
+ executor: API executor. Required to execute the API requests.
46
+ """
47
+
48
+ def __init__(self, executor: ApiExecutor) -> None:
49
+ self._executor = executor
50
+
51
+ @property
52
+ def name(self) -> str:
53
+ """Return the name of the current database."""
54
+ return self._executor.db_name
55
+
56
+ @property
57
+ def serializer(self) -> Serializer[Json]:
58
+ """Return the serializer."""
59
+ return self._executor.serializer
60
+
61
+ @property
62
+ def deserializer(self) -> Deserializer[Json, Jsons]:
63
+ """Return the deserializer."""
64
+ return self._executor.deserializer
65
+
66
+ def __repr__(self) -> str:
67
+ return f"<AQLQueryCache in {self.name}>"
68
+
69
+ async def entries(self) -> Result[Jsons]:
70
+ """Return a list of all AQL query results cache entries.
71
+
72
+
73
+ Returns:
74
+ list: List of AQL query results cache entries.
75
+
76
+ Raises:
77
+ AQLCacheEntriesError: If retrieval fails.
78
+
79
+ References:
80
+ - `list-the-entries-of-the-aql-query-results-cache <https://docs.arangodb.com/stable/develop/http-api/queries/aql-query-results-cache/#list-the-entries-of-the-aql-query-results-cache>`__
81
+ """ # noqa: E501
82
+ request = Request(method=Method.GET, endpoint="/_api/query-cache/entries")
83
+
84
+ def response_handler(resp: Response) -> Jsons:
85
+ if not resp.is_success:
86
+ raise AQLCacheEntriesError(resp, request)
87
+ return self.deserializer.loads_many(resp.raw_body)
88
+
89
+ return await self._executor.execute(request, response_handler)
90
+
91
+ async def plan_entries(self) -> Result[Jsons]:
92
+ """Return a list of all AQL query plan cache entries.
93
+
94
+ Returns:
95
+ list: List of AQL query plan cache entries.
96
+
97
+ Raises:
98
+ AQLCacheEntriesError: If retrieval fails.
99
+
100
+ References:
101
+ - `list-the-entries-of-the-aql-query-plan-cache <https://docs.arangodb.com/stable/develop/http-api/queries/aql-query-plan-cache/#list-the-entries-of-the-aql-query-plan-cache>`__
102
+ """ # noqa: E501
103
+ request = Request(method=Method.GET, endpoint="/_api/query-plan-cache")
104
+
105
+ def response_handler(resp: Response) -> Jsons:
106
+ if not resp.is_success:
107
+ raise AQLCacheEntriesError(resp, request)
108
+ return self.deserializer.loads_many(resp.raw_body)
109
+
110
+ return await self._executor.execute(request, response_handler)
111
+
112
+ async def clear(self) -> Result[None]:
113
+ """Clear the AQL query results cache.
114
+
115
+ Raises:
116
+ AQLCacheClearError: If clearing the cache fails.
117
+
118
+ References:
119
+ - `clear-the-aql-query-results-cache <https://docs.arangodb.com/stable/develop/http-api/queries/aql-query-results-cache/#clear-the-aql-query-results-cache>`__
120
+ """ # noqa: E501
121
+ request = Request(method=Method.DELETE, endpoint="/_api/query-cache")
122
+
123
+ def response_handler(resp: Response) -> None:
124
+ if not resp.is_success:
125
+ raise AQLCacheClearError(resp, request)
126
+
127
+ return await self._executor.execute(request, response_handler)
128
+
129
+ async def clear_plan(self) -> Result[None]:
130
+ """Clear the AQL query plan cache.
131
+
132
+ Raises:
133
+ AQLCacheClearError: If clearing the cache fails.
134
+
135
+ References:
136
+ - `clear-the-aql-query-plan-cache <https://docs.arangodb.com/stable/develop/http-api/queries/aql-query-plan-cache/#clear-the-aql-query-plan-cache>`__
137
+ """ # noqa: E501
138
+ request = Request(method=Method.DELETE, endpoint="/_api/query-plan-cache")
139
+
140
+ def response_handler(resp: Response) -> None:
141
+ if not resp.is_success:
142
+ raise AQLCacheClearError(resp, request)
143
+
144
+ return await self._executor.execute(request, response_handler)
145
+
146
+ async def properties(self) -> Result[QueryCacheProperties]:
147
+ """Return the current AQL query results cache configuration.
148
+
149
+ Returns:
150
+ QueryCacheProperties: Current AQL query cache properties.
151
+
152
+ Raises:
153
+ AQLCachePropertiesError: If retrieval fails.
154
+
155
+ References:
156
+ - `get-the-aql-query-results-cache-configuration <https://docs.arangodb.com/stable/develop/http-api/queries/aql-query-results-cache/#get-the-aql-query-results-cache-configuration>`__
157
+ """ # noqa: E501
158
+ request = Request(method=Method.GET, endpoint="/_api/query-cache/properties")
159
+
160
+ def response_handler(resp: Response) -> QueryCacheProperties:
161
+ if not resp.is_success:
162
+ raise AQLCachePropertiesError(resp, request)
163
+ return QueryCacheProperties(self.deserializer.loads(resp.raw_body))
164
+
165
+ return await self._executor.execute(request, response_handler)
166
+
167
+ async def configure(
168
+ self,
169
+ mode: Optional[str] = None,
170
+ max_results: Optional[int] = None,
171
+ max_results_size: Optional[int] = None,
172
+ max_entry_size: Optional[int] = None,
173
+ include_system: Optional[bool] = None,
174
+ ) -> Result[QueryCacheProperties]:
175
+ """Configure the AQL query results cache.
176
+
177
+ Args:
178
+ mode (str | None): Cache mode. Allowed values are `"off"`, `"on"`,
179
+ and `"demand"`.
180
+ max_results (int | None): Max number of query results stored per
181
+ database-specific cache.
182
+ max_results_size (int | None): Max cumulative size of query results stored
183
+ per database-specific cache.
184
+ max_entry_size (int | None): Max entry size of each query result stored per
185
+ database-specific cache.
186
+ include_system (bool | None): Store results of queries in system collections.
187
+
188
+ Returns:
189
+ QueryCacheProperties: Updated AQL query cache properties.
190
+
191
+ Raises:
192
+ AQLCacheConfigureError: If setting the configuration fails.
193
+
194
+ References:
195
+ - `set-the-aql-query-results-cache-configuration <https://docs.arangodb.com/stable/develop/http-api/queries/aql-query-results-cache/#set-the-aql-query-results-cache-configuration>`__
196
+ """ # noqa: E501
197
+ data: Json = dict()
198
+ if mode is not None:
199
+ data["mode"] = mode
200
+ if max_results is not None:
201
+ data["maxResults"] = max_results
202
+ if max_results_size is not None:
203
+ data["maxResultsSize"] = max_results_size
204
+ if max_entry_size is not None:
205
+ data["maxEntrySize"] = max_entry_size
206
+ if include_system is not None:
207
+ data["includeSystem"] = include_system
208
+
209
+ request = Request(
210
+ method=Method.PUT,
211
+ endpoint="/_api/query-cache/properties",
212
+ data=self.serializer.dumps(data),
213
+ )
214
+
215
+ def response_handler(resp: Response) -> QueryCacheProperties:
216
+ if not resp.is_success:
217
+ raise AQLCacheConfigureError(resp, request)
218
+ return QueryCacheProperties(self.deserializer.loads(resp.raw_body))
219
+
220
+ return await self._executor.execute(request, response_handler)
221
+
222
+
223
+ class AQL:
224
+ """AQL (ArangoDB Query Language) API wrapper.
225
+
226
+ Allows you to execute, track, kill, explain, and validate queries written
227
+ in ArangoDB’s query language.
228
+
229
+ Args:
230
+ executor: API executor. Required to execute the API requests.
231
+ """
232
+
233
+ def __init__(self, executor: ApiExecutor) -> None:
234
+ self._executor = executor
235
+
236
+ @property
237
+ def name(self) -> str:
238
+ """Return the name of the current database."""
239
+ return self._executor.db_name
240
+
241
+ @property
242
+ def serializer(self) -> Serializer[Json]:
243
+ """Return the serializer."""
244
+ return self._executor.serializer
245
+
246
+ @property
247
+ def deserializer(self) -> Deserializer[Json, Jsons]:
248
+ """Return the deserializer."""
249
+ return self._executor.deserializer
250
+
251
+ @property
252
+ def cache(self) -> AQLQueryCache:
253
+ """Return the AQL Query Cache API wrapper."""
254
+ return AQLQueryCache(self._executor)
255
+
256
+ def __repr__(self) -> str:
257
+ return f"<AQL in {self.name}>"
258
+
259
+ async def execute(
260
+ self,
261
+ query: str,
262
+ count: Optional[bool] = None,
263
+ batch_size: Optional[int] = None,
264
+ bind_vars: Optional[Json] = None,
265
+ cache: Optional[bool] = None,
266
+ memory_limit: Optional[int] = None,
267
+ ttl: Optional[int] = None,
268
+ allow_dirty_read: Optional[bool] = None,
269
+ options: Optional[QueryProperties | Json] = None,
270
+ ) -> Result[Cursor]:
271
+ """Execute the query and return the result cursor.
272
+
273
+ Args:
274
+ query (str): Query string to be executed.
275
+ count (bool | None): If set to `True`, the total document count is
276
+ calculated and included in the result cursor.
277
+ batch_size (int | None): Maximum number of result documents to be
278
+ transferred from the server to the client in one roundtrip.
279
+ bind_vars (dict | None): An object with key/value pairs representing
280
+ the bind parameters.
281
+ cache (bool | None): Flag to determine whether the AQL query results
282
+ cache shall be used.
283
+ memory_limit (int | None): Maximum memory (in bytes) that the query is
284
+ allowed to use.
285
+ ttl (int | None): The time-to-live for the cursor (in seconds). The cursor
286
+ will be removed on the server automatically after the specified amount
287
+ of time.
288
+ allow_dirty_read (bool | None): Allow reads from followers in a cluster.
289
+ options (QueryProperties | dict | None): Extra options for the query.
290
+
291
+ Returns:
292
+ Cursor: Result cursor.
293
+
294
+ References:
295
+ - `create-a-cursor <https://docs.arangodb.com/stable/develop/http-api/queries/aql-queries/#create-a-cursor>`__
296
+ """ # noqa: E501
297
+ data: Json = dict(query=query)
298
+ if count is not None:
299
+ data["count"] = count
300
+ if batch_size is not None:
301
+ data["batchSize"] = batch_size
302
+ if bind_vars is not None:
303
+ data["bindVars"] = bind_vars
304
+ if cache is not None:
305
+ data["cache"] = cache
306
+ if memory_limit is not None:
307
+ data["memoryLimit"] = memory_limit
308
+ if ttl is not None:
309
+ data["ttl"] = ttl
310
+ if options is not None:
311
+ if isinstance(options, QueryProperties):
312
+ options = options.to_dict()
313
+ data["options"] = options
314
+
315
+ headers = dict()
316
+ if allow_dirty_read is not None:
317
+ headers["x-arango-allow-dirty-read"] = str(allow_dirty_read).lower()
318
+
319
+ request = Request(
320
+ method=Method.POST,
321
+ endpoint="/_api/cursor",
322
+ data=self.serializer.dumps(data),
323
+ headers=headers,
324
+ )
325
+
326
+ def response_handler(resp: Response) -> Cursor:
327
+ if not resp.is_success:
328
+ raise AQLQueryExecuteError(resp, request)
329
+ if self._executor.context == "async":
330
+ # We cannot have a cursor giving back async jobs
331
+ executor: NonAsyncExecutor = DefaultApiExecutor(
332
+ self._executor.connection
333
+ )
334
+ else:
335
+ executor = cast(NonAsyncExecutor, self._executor)
336
+ return Cursor(executor, self.deserializer.loads(resp.raw_body))
337
+
338
+ return await self._executor.execute(request, response_handler)
339
+
340
+ async def tracking(self) -> Result[QueryTrackingConfiguration]:
341
+ """Returns the current query tracking configuration.
342
+
343
+ Returns:
344
+ QueryTrackingConfiguration: Returns the current query tracking configuration.
345
+
346
+ Raises:
347
+ AQLQueryTrackingGetError: If retrieval fails.
348
+
349
+ References:
350
+ - `get-the-aql-query-tracking-configuration <https://docs.arangodb.com/stable/develop/http-api/queries/aql-queries/#get-the-aql-query-tracking-configuration>`__
351
+ """ # noqa: E501
352
+ request = Request(method=Method.GET, endpoint="/_api/query/properties")
353
+
354
+ def response_handler(resp: Response) -> QueryTrackingConfiguration:
355
+ if not resp.is_success:
356
+ raise AQLQueryTrackingGetError(resp, request)
357
+ return QueryTrackingConfiguration(self.deserializer.loads(resp.raw_body))
358
+
359
+ return await self._executor.execute(request, response_handler)
360
+
361
+ async def set_tracking(
362
+ self,
363
+ enabled: Optional[bool] = None,
364
+ max_slow_queries: Optional[int] = None,
365
+ slow_query_threshold: Optional[int] = None,
366
+ max_query_string_length: Optional[int] = None,
367
+ track_bind_vars: Optional[bool] = None,
368
+ track_slow_queries: Optional[int] = None,
369
+ ) -> Result[QueryTrackingConfiguration]:
370
+ """Configure AQL query tracking properties.
371
+
372
+ Args:
373
+ enabled (bool | None): If set to `True`, then queries will be tracked.
374
+ If set to `False`, neither queries nor slow queries will be tracked.
375
+ max_slow_queries (int | None): Maximum number of slow queries to track. Oldest
376
+ entries are discarded first.
377
+ slow_query_threshold (int | None): Runtime threshold (in seconds) for treating a
378
+ query as slow.
379
+ max_query_string_length (int | None): The maximum query string length (in bytes)
380
+ to keep in the list of queries.
381
+ track_bind_vars (bool | None): If set to `True`, track bind variables used in
382
+ queries.
383
+ track_slow_queries (int | None): If set to `True`, then slow queries will be
384
+ tracked in the list of slow queries if their runtime exceeds the
385
+ value set in `slowQueryThreshold`.
386
+
387
+ Returns:
388
+ QueryTrackingConfiguration: Returns the updated query tracking configuration.
389
+
390
+ Raises:
391
+ AQLQueryTrackingSetError: If setting the configuration fails.
392
+
393
+ References:
394
+ - `update-the-aql-query-tracking-configuration <https://docs.arangodb.com/stable/develop/http-api/queries/aql-queries/#update-the-aql-query-tracking-configuration>`__
395
+ """ # noqa: E501
396
+ data: Json = dict()
397
+
398
+ if enabled is not None:
399
+ data["enabled"] = enabled
400
+ if max_slow_queries is not None:
401
+ data["maxSlowQueries"] = max_slow_queries
402
+ if max_query_string_length is not None:
403
+ data["maxQueryStringLength"] = max_query_string_length
404
+ if slow_query_threshold is not None:
405
+ data["slowQueryThreshold"] = slow_query_threshold
406
+ if track_bind_vars is not None:
407
+ data["trackBindVars"] = track_bind_vars
408
+ if track_slow_queries is not None:
409
+ data["trackSlowQueries"] = track_slow_queries
410
+
411
+ request = Request(
412
+ method=Method.PUT,
413
+ endpoint="/_api/query/properties",
414
+ data=self.serializer.dumps(data),
415
+ )
416
+
417
+ def response_handler(resp: Response) -> QueryTrackingConfiguration:
418
+ if not resp.is_success:
419
+ raise AQLQueryTrackingSetError(resp, request)
420
+ return QueryTrackingConfiguration(self.deserializer.loads(resp.raw_body))
421
+
422
+ return await self._executor.execute(request, response_handler)
423
+
424
+ async def queries(self, all_queries: bool = False) -> Result[Jsons]:
425
+ """Return a list of currently running queries.
426
+
427
+ Args:
428
+ all_queries (bool): If set to `True`, will return the currently
429
+ running queries in all databases, not just the selected one.
430
+ Using the parameter is only allowed in the `_system` database
431
+ and with superuser privileges.
432
+
433
+ Returns:
434
+ list: List of currently running queries and their properties.
435
+
436
+ Raises:
437
+ AQLQueryListError: If retrieval fails.
438
+
439
+ References:
440
+ - `list-the-running-queries <https://docs.arangodb.com/stable/develop/http-api/queries/aql-queries/#list-the-running-queries>`__
441
+ """ # noqa: E501
442
+ request = Request(
443
+ method=Method.GET,
444
+ endpoint="/_api/query/current",
445
+ params={"all": all_queries},
446
+ )
447
+
448
+ def response_handler(resp: Response) -> Jsons:
449
+ if not resp.is_success:
450
+ raise AQLQueryListError(resp, request)
451
+ return self.deserializer.loads_many(resp.raw_body)
452
+
453
+ return await self._executor.execute(request, response_handler)
454
+
455
+ async def slow_queries(self, all_queries: bool = False) -> Result[Jsons]:
456
+ """Returns a list containing the last AQL queries that are finished and
457
+ have exceeded the slow query threshold in the selected database.
458
+
459
+ Args:
460
+ all_queries (bool): If set to `True`, will return the slow queries
461
+ in all databases, not just the selected one. Using the parameter
462
+ is only allowed in the `_system` database and with superuser privileges.
463
+
464
+ Returns:
465
+ list: List of slow queries.
466
+
467
+ Raises:
468
+ AQLQueryListError: If retrieval fails.
469
+
470
+ References:
471
+ - `list-the-slow-aql-queries <https://docs.arangodb.com/stable/develop/http-api/queries/aql-queries/#list-the-slow-aql-queries>`__
472
+ """ # noqa: E501
473
+ request = Request(
474
+ method=Method.GET,
475
+ endpoint="/_api/query/slow",
476
+ params={"all": all_queries},
477
+ )
478
+
479
+ def response_handler(resp: Response) -> Jsons:
480
+ if not resp.is_success:
481
+ raise AQLQueryListError(resp, request)
482
+ return self.deserializer.loads_many(resp.raw_body)
483
+
484
+ return await self._executor.execute(request, response_handler)
485
+
486
+ async def clear_slow_queries(self, all_queries: bool = False) -> Result[None]:
487
+ """Clears the list of slow queries.
488
+
489
+ Args:
490
+ all_queries (bool): If set to `True`, will clear the slow queries
491
+ in all databases, not just the selected one. Using the parameter
492
+ is only allowed in the `_system` database and with superuser privileges.
493
+
494
+ Returns:
495
+ dict: Empty dictionary.
496
+
497
+ Raises:
498
+ AQLQueryClearError: If retrieval fails.
499
+
500
+ References:
501
+ - `clear-the-list-of-slow-aql-queries <https://docs.arangodb.com/stable/develop/http-api/queries/aql-queries/#clear-the-list-of-slow-aql-queries>`__
502
+ """ # noqa: E501
503
+ request = Request(
504
+ method=Method.DELETE,
505
+ endpoint="/_api/query/slow",
506
+ params={"all": all_queries},
507
+ )
508
+
509
+ def response_handler(resp: Response) -> None:
510
+ if not resp.is_success:
511
+ raise AQLQueryClearError(resp, request)
512
+
513
+ return await self._executor.execute(request, response_handler)
514
+
515
+ async def kill(
516
+ self,
517
+ query_id: str,
518
+ ignore_missing: bool = False,
519
+ all_queries: bool = False,
520
+ ) -> Result[bool]:
521
+ """Kill a running query.
522
+
523
+ Args:
524
+ query_id (str): Thea ID of the query to kill.
525
+ ignore_missing (bool): If set to `True`, will not raise an exception
526
+ if the query is not found.
527
+ all_queries (bool): If set to `True`, will kill the query in all databases,
528
+ not just the selected one. Using the parameter is only allowed in the
529
+ `_system` database and with superuser privileges.
530
+
531
+ Returns:
532
+ bool: `True` if the query was killed successfully.
533
+
534
+ Raises:
535
+ AQLQueryKillError: If killing the query fails.
536
+
537
+ References:
538
+ - `kill-a-running-aql-query <https://docs.arangodb.com/stable/develop/http-api/queries/aql-queries/#kill-a-running-aql-query>`__
539
+ """ # noqa: E501
540
+ request = Request(
541
+ method=Method.DELETE,
542
+ endpoint=f"/_api/query/{query_id}",
543
+ params={"all": all_queries},
544
+ )
545
+
546
+ def response_handler(resp: Response) -> bool:
547
+ if resp.is_success:
548
+ return True
549
+ if resp.status_code == HTTP_NOT_FOUND and ignore_missing:
550
+ return False
551
+ raise AQLQueryKillError(resp, request)
552
+
553
+ return await self._executor.execute(request, response_handler)
554
+
555
+ async def explain(
556
+ self,
557
+ query: str,
558
+ bind_vars: Optional[Json] = None,
559
+ options: Optional[QueryExplainOptions | Json] = None,
560
+ ) -> Result[Json]:
561
+ """Inspect the query and return its metadata without executing it.
562
+
563
+ Args:
564
+ query (str): Query string to be explained.
565
+ bind_vars (dict | None): An object with key/value pairs representing
566
+ the bind parameters.
567
+ options (QueryExplainOptions | dict | None): Extra options for the query.
568
+
569
+ Returns:
570
+ dict: Query execution plan.
571
+
572
+ Raises:
573
+ AQLQueryExplainError: If retrieval fails.
574
+
575
+ References:
576
+ - `explain-an-aql-query <https://docs.arangodb.com/stable/develop/http-api/queries/aql-queries/#explain-an-aql-query>`__
577
+ """ # noqa: E501
578
+ data: Json = dict(query=query)
579
+ if bind_vars is not None:
580
+ data["bindVars"] = bind_vars
581
+ if options is not None:
582
+ if isinstance(options, QueryExplainOptions):
583
+ options = options.to_dict()
584
+ data["options"] = options
585
+
586
+ request = Request(
587
+ method=Method.POST,
588
+ endpoint="/_api/explain",
589
+ data=self.serializer.dumps(data),
590
+ )
591
+
592
+ def response_handler(resp: Response) -> Json:
593
+ if not resp.is_success:
594
+ raise AQLQueryExplainError(resp, request)
595
+ return self.deserializer.loads(resp.raw_body)
596
+
597
+ return await self._executor.execute(request, response_handler)
598
+
599
+ async def validate(self, query: str) -> Result[Json]:
600
+ """Parse and validate the query without executing it.
601
+
602
+ Args:
603
+ query (str): Query string to be validated.
604
+
605
+ Returns:
606
+ dict: Query information.
607
+
608
+ Raises:
609
+ AQLQueryValidateError: If validation fails.
610
+
611
+ References:
612
+ - `parse-an-aql-query <https://docs.arangodb.com/stable/develop/http-api/queries/aql-queries/#parse-an-aql-query>`__
613
+ """ # noqa: E501
614
+ request = Request(
615
+ method=Method.POST,
616
+ endpoint="/_api/query",
617
+ data=self.serializer.dumps(dict(query=query)),
618
+ )
619
+
620
+ def response_handler(resp: Response) -> Json:
621
+ if not resp.is_success:
622
+ raise AQLQueryValidateError(resp, request)
623
+ return self.deserializer.loads(resp.raw_body)
624
+
625
+ return await self._executor.execute(request, response_handler)
626
+
627
+ async def query_rules(self) -> Result[Jsons]:
628
+ """A list of all optimizer rules and their properties.
629
+
630
+ Returns:
631
+ list: Available optimizer rules.
632
+
633
+ Raises:
634
+ AQLQueryRulesGetError: If retrieval fails.
635
+
636
+ References:
637
+ - `list-all-aql-optimizer-rules <https://docs.arangodb.com/stable/develop/http-api/queries/aql-queries/#list-all-aql-optimizer-rules>`__
638
+ """ # noqa: E501
639
+ request = Request(method=Method.GET, endpoint="/_api/query/rules")
640
+
641
+ def response_handler(resp: Response) -> Jsons:
642
+ if not resp.is_success:
643
+ raise AQLQueryRulesGetError(resp, request)
644
+ return self.deserializer.loads_many(resp.raw_body)
645
+
646
+ return await self._executor.execute(request, response_handler)
647
+
648
+ async def functions(self, namespace: Optional[str] = None) -> Result[Jsons]:
649
+ """List the registered used-defined AQL functions.
650
+
651
+ Args:
652
+ namespace (str | None): Returns all registered AQL user functions from
653
+ the specified namespace.
654
+
655
+ Returns:
656
+ list: List of the AQL functions defined in the database.
657
+
658
+ Raises:
659
+ AQLFunctionListError: If retrieval fails.
660
+
661
+ References:
662
+ - `list-the-registered-user-defined-aql-functions <https://docs.arangodb.com/stable/develop/http-api/queries/user-defined-aql-functions/#list-the-registered-user-defined-aql-functions>`__
663
+ """ # noqa: E501
664
+ params: Json = dict()
665
+ if namespace is not None:
666
+ params["namespace"] = namespace
667
+ request = Request(
668
+ method=Method.GET,
669
+ endpoint="/_api/aqlfunction",
670
+ params=params,
671
+ )
672
+
673
+ def response_handler(resp: Response) -> Jsons:
674
+ if not resp.is_success:
675
+ raise AQLFunctionListError(resp, request)
676
+ result = cast(Jsons, self.deserializer.loads(resp.raw_body).get("result"))
677
+ if result is None:
678
+ raise AQLFunctionListError(resp, request)
679
+ return result
680
+
681
+ return await self._executor.execute(request, response_handler)
682
+
683
+ async def create_function(
684
+ self,
685
+ name: str,
686
+ code: str,
687
+ is_deterministic: Optional[bool] = None,
688
+ ) -> Result[Json]:
689
+ """Registers a user-defined AQL function (UDF) written in JavaScript.
690
+
691
+ Args:
692
+ name (str): Name of the function.
693
+ code (str): JavaScript code of the function.
694
+ is_deterministic (bool | None): If set to `True`, the function is
695
+ deterministic.
696
+
697
+ Returns:
698
+ dict: Information about the registered function.
699
+
700
+ Raises:
701
+ AQLFunctionCreateError: If registration fails.
702
+
703
+ References:
704
+ - `create-a-user-defined-aql-function <https://docs.arangodb.com/stable/develop/http-api/queries/user-defined-aql-functions/#create-a-user-defined-aql-function>`__
705
+ """ # noqa: E501
706
+ request = Request(
707
+ method=Method.POST,
708
+ endpoint="/_api/aqlfunction",
709
+ data=self.serializer.dumps(
710
+ dict(name=name, code=code, isDeterministic=is_deterministic)
711
+ ),
712
+ )
713
+
714
+ def response_handler(resp: Response) -> Json:
715
+ if not resp.is_success:
716
+ raise AQLFunctionCreateError(resp, request)
717
+ return self.deserializer.loads(resp.raw_body)
718
+
719
+ return await self._executor.execute(request, response_handler)
720
+
721
+ async def delete_function(
722
+ self,
723
+ name: str,
724
+ group: Optional[bool] = None,
725
+ ignore_missing: bool = False,
726
+ ) -> Result[Json]:
727
+ """Remove a user-defined AQL function.
728
+
729
+ Args:
730
+ name (str): Name of the function.
731
+ group (bool | None): If set to `True`, the function name is treated
732
+ as a namespace prefix.
733
+ ignore_missing (bool): If set to `True`, will not raise an exception
734
+ if the function is not found.
735
+
736
+ Returns:
737
+ dict: Information about the removed functions (their count).
738
+
739
+ Raises:
740
+ AQLFunctionDeleteError: If removal fails.
741
+
742
+ References:
743
+ - `remove-a-user-defined-aql-function <https://docs.arangodb.com/stable/develop/http-api/queries/user-defined-aql-functions/#remove-a-user-defined-aql-function>`__
744
+ """ # noqa: E501
745
+ params: Json = dict()
746
+ if group is not None:
747
+ params["group"] = group
748
+ request = Request(
749
+ method=Method.DELETE,
750
+ endpoint=f"/_api/aqlfunction/{name}",
751
+ params=params,
752
+ )
753
+
754
+ def response_handler(resp: Response) -> Json:
755
+ if not resp.is_success:
756
+ if not (resp.status_code == HTTP_NOT_FOUND and ignore_missing):
757
+ raise AQLFunctionDeleteError(resp, request)
758
+ return self.deserializer.loads(resp.raw_body)
759
+
760
+ return await self._executor.execute(request, response_handler)