python-arango-async 1.2.3__py3-none-any.whl → 1.2.5__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/collection.py CHANGED
@@ -194,24 +194,49 @@ class Collection(Generic[T, U, V]):
194
194
 
195
195
  return doc_id
196
196
 
197
- def _build_filter_conditions(self, filters: Optional[Json]) -> str:
197
+ @staticmethod
198
+ def _build_attribute_expression(field: str, prefix: str, bind_vars: Json) -> str:
199
+ """Build a bind-safe AQL document attribute expression."""
200
+ bind_vars[prefix] = field
201
+ field_access = f"doc[@{prefix}]"
202
+
203
+ if "." not in field:
204
+ return field_access
205
+
206
+ nested_access = "doc"
207
+ for field_index, field_part in enumerate(field.split(".")):
208
+ field_var = f"{prefix}_{field_index}"
209
+ bind_vars[field_var] = field_part
210
+ nested_access += f"[@{field_var}]"
211
+
212
+ return f"(HAS(doc, @{prefix}) ? {field_access} : {nested_access})"
213
+
214
+ def _build_filter_conditions(self, filters: Optional[Json]) -> tuple[str, Json]:
198
215
  """Build filter conditions for an AQL query.
199
216
 
200
217
  Args:
201
218
  filters (dict | None): Document filters.
202
219
 
203
220
  Returns:
204
- str: The complete AQL filter condition.
221
+ tuple: The complete AQL filter condition and its bind variables.
205
222
  """
206
223
  if not filters:
207
- return ""
224
+ return "", {}
208
225
 
209
226
  conditions = []
210
- for k, v in filters.items():
211
- field = k if "." in k else f"`{k}`"
212
- conditions.append(f"doc.{field} == {self.serializer.dumps(v)}")
227
+ bind_vars: Json = {}
228
+ for filter_index, (field, value) in enumerate(filters.items()):
229
+ field_access = self._build_attribute_expression(
230
+ field,
231
+ f"filter_field_{filter_index}",
232
+ bind_vars,
233
+ )
213
234
 
214
- return "FILTER " + " AND ".join(conditions)
235
+ value_var = f"filter_value_{filter_index}"
236
+ bind_vars[value_var] = value
237
+ conditions.append(f"{field_access} == @{value_var}")
238
+
239
+ return "FILTER " + " AND ".join(conditions), bind_vars
215
240
 
216
241
  @staticmethod
217
242
  def _is_none_or_int(obj: Any) -> bool:
@@ -259,24 +284,30 @@ class Collection(Generic[T, U, V]):
259
284
  raise SortValidationError("'sort_order' must be either 'ASC' or 'DESC'")
260
285
 
261
286
  @staticmethod
262
- def _build_sort_expression(sort: Optional[Jsons]) -> str:
287
+ def _build_sort_expression(sort: Optional[Jsons]) -> tuple[str, Json]:
263
288
  """Build a sort condition for an AQL query.
264
289
 
265
290
  Args:
266
291
  sort (list | None): Document sort parameters.
267
292
 
268
293
  Returns:
269
- str: The complete AQL sort condition.
294
+ tuple: The complete AQL sort condition and its bind variables.
270
295
  """
271
296
  if not sort:
272
- return ""
297
+ return "", {}
273
298
 
274
299
  sort_chunks = []
275
- for sort_param in sort:
276
- chunk = f"doc.{sort_param['sort_by']} {sort_param['sort_order']}"
300
+ bind_vars: Json = {}
301
+ for sort_index, sort_param in enumerate(sort):
302
+ field_access = Collection._build_attribute_expression(
303
+ sort_param["sort_by"],
304
+ f"sort_field_{sort_index}",
305
+ bind_vars,
306
+ )
307
+ chunk = f"{field_access} {sort_param['sort_order'].upper()}"
277
308
  sort_chunks.append(chunk)
278
309
 
279
- return "SORT " + ", ".join(sort_chunks)
310
+ return "SORT " + ", ".join(sort_chunks), bind_vars
280
311
 
281
312
  @property
282
313
  def name(self) -> str:
@@ -1025,14 +1056,20 @@ class Collection(Generic[T, U, V]):
1025
1056
 
1026
1057
  skip = skip if skip is not None else 0
1027
1058
  limit = limit if limit is not None else "null"
1059
+ filter_conditions, filter_bind_vars = self._build_filter_conditions(filters)
1060
+ sort_expression, sort_bind_vars = self._build_sort_expression(sort)
1028
1061
  query = f"""
1029
1062
  FOR doc IN @@collection
1030
- {self._build_filter_conditions(filters)}
1063
+ {filter_conditions}
1031
1064
  LIMIT {skip}, {limit}
1032
- {self._build_sort_expression(sort)}
1065
+ {sort_expression}
1033
1066
  RETURN doc
1034
1067
  """
1035
- bind_vars = {"@collection": self.name}
1068
+ bind_vars = {
1069
+ "@collection": self.name,
1070
+ **filter_bind_vars,
1071
+ **sort_bind_vars,
1072
+ }
1036
1073
  data: Json = {"query": query, "bindVars": bind_vars, "count": True}
1037
1074
  headers: RequestHeaders = {}
1038
1075
  if allow_dirty_read is not None:
@@ -1095,9 +1132,10 @@ class Collection(Generic[T, U, V]):
1095
1132
  raise ValueError("limit parameter must be a non-negative int")
1096
1133
 
1097
1134
  sync = f", waitForSync: {wait_for_sync}" if wait_for_sync is not None else ""
1135
+ filter_conditions, filter_bind_vars = self._build_filter_conditions(filters)
1098
1136
  query = f"""
1099
1137
  FOR doc IN @@collection
1100
- {self._build_filter_conditions(filters)}
1138
+ {filter_conditions}
1101
1139
  {f"LIMIT {limit}" if limit is not None else ""}
1102
1140
  UPDATE doc WITH @body IN @@collection
1103
1141
  OPTIONS {{ keepNull: @keep_none, mergeObjects: @merge {sync} }}
@@ -1107,6 +1145,7 @@ class Collection(Generic[T, U, V]):
1107
1145
  "body": body,
1108
1146
  "keep_none": keep_none,
1109
1147
  "merge": merge_objects,
1148
+ **filter_bind_vars,
1110
1149
  }
1111
1150
  data = {"query": query, "bindVars": bind_vars}
1112
1151
 
@@ -1151,9 +1190,10 @@ class Collection(Generic[T, U, V]):
1151
1190
  raise ValueError("limit parameter must be a non-negative int")
1152
1191
 
1153
1192
  sync = f"waitForSync: {wait_for_sync}" if wait_for_sync is not None else ""
1193
+ filter_conditions, filter_bind_vars = self._build_filter_conditions(filters)
1154
1194
  query = f"""
1155
1195
  FOR doc IN @@collection
1156
- {self._build_filter_conditions(filters)}
1196
+ {filter_conditions}
1157
1197
  {f"LIMIT {limit}" if limit is not None else ""}
1158
1198
  REPLACE doc WITH @body IN @@collection
1159
1199
  {f"OPTIONS {{ {sync} }}" if sync else ""}
@@ -1161,6 +1201,7 @@ class Collection(Generic[T, U, V]):
1161
1201
  bind_vars = {
1162
1202
  "@collection": self.name,
1163
1203
  "body": body,
1204
+ **filter_bind_vars,
1164
1205
  }
1165
1206
  data = {"query": query, "bindVars": bind_vars}
1166
1207
 
@@ -1203,14 +1244,15 @@ class Collection(Generic[T, U, V]):
1203
1244
  raise ValueError("limit parameter must be a non-negative int")
1204
1245
 
1205
1246
  sync = f"waitForSync: {wait_for_sync}" if wait_for_sync is not None else ""
1247
+ filter_conditions, filter_bind_vars = self._build_filter_conditions(filters)
1206
1248
  query = f"""
1207
1249
  FOR doc IN @@collection
1208
- {self._build_filter_conditions(filters)}
1250
+ {filter_conditions}
1209
1251
  {f"LIMIT {limit}" if limit is not None else ""}
1210
1252
  REMOVE doc IN @@collection
1211
1253
  {f"OPTIONS {{ {sync} }}" if sync else ""}
1212
1254
  """ # noqa: E201 E202
1213
- bind_vars = {"@collection": self.name}
1255
+ bind_vars = {"@collection": self.name, **filter_bind_vars}
1214
1256
  data = {"query": query, "bindVars": bind_vars}
1215
1257
 
1216
1258
  request = Request(
@@ -2,6 +2,7 @@ __all__ = ["Replication"]
2
2
 
3
3
 
4
4
  from typing import Optional
5
+ from warnings import warn
5
6
 
6
7
  from arangoasync.exceptions import (
7
8
  ReplicationApplierConfigError,
@@ -195,7 +196,9 @@ class Replication:
195
196
  return await self._executor.execute(request, response_handler)
196
197
 
197
198
  async def applier_config(self) -> Result[Json]:
198
- """Return the configuration of the replication applier.
199
+ """**Removed** in ArangoDB v3.12.10.
200
+
201
+ Return the configuration of the replication applier.
199
202
 
200
203
  Returns:
201
204
  dict: Configuration of the replication applier.
@@ -206,6 +209,9 @@ class Replication:
206
209
  References:
207
210
  - `get-the-replication-applier-configuration <https://docs.arango.ai/arangodb/stable/develop/http-api/replication/replication-applier/#get-the-replication-applier-configuration>`__
208
211
  """ # noqa: E501
212
+ m = "/_api/replication/applier-config was removed in ArangoDB v3.12.10."
213
+ warn(m, DeprecationWarning, stacklevel=2)
214
+
209
215
  request = Request(
210
216
  method=Method.GET,
211
217
  endpoint="/_api/replication/applier-config",
@@ -220,7 +226,9 @@ class Replication:
220
226
  return await self._executor.execute(request, response_handler)
221
227
 
222
228
  async def applier_state(self) -> Result[Json]:
223
- """Return the state of the replication applier.
229
+ """**Removed** in ArangoDB v3.12.10.
230
+
231
+ Return the state of the replication applier.
224
232
 
225
233
  Returns:
226
234
  dict: State of the replication applier.
@@ -231,6 +239,9 @@ class Replication:
231
239
  References:
232
240
  - `get-the-replication-applier-state <https://docs.arango.ai/arangodb/stable/develop/http-api/replication/replication-applier/#get-the-replication-applier-state>`__
233
241
  """ # noqa: E501
242
+ m = "/_api/replication/applier-state was removed in ArangoDB v3.12.10."
243
+ warn(m, DeprecationWarning, stacklevel=2)
244
+
234
245
  request = Request(
235
246
  method=Method.GET,
236
247
  endpoint="/_api/replication/applier-state",
@@ -245,7 +256,9 @@ class Replication:
245
256
  return await self._executor.execute(request, response_handler)
246
257
 
247
258
  async def server_id(self) -> Result[str]:
248
- """Return the current server's ID.
259
+ """**Removed** in ArangoDB v3.12.10.
260
+
261
+ Return the current server's ID.
249
262
 
250
263
  Returns:
251
264
  str: Server ID.
@@ -256,6 +269,9 @@ class Replication:
256
269
  References:
257
270
  - `get-the-replication-server-id <https://docs.arango.ai/arangodb/stable/develop/http-api/replication/other-replication-commands/#get-the-replication-server-id>`__
258
271
  """ # noqa: E501
272
+ m = "/_api/replication/server-id was removed in ArangoDB v3.12.10."
273
+ warn(m, DeprecationWarning, stacklevel=2)
274
+
259
275
  request = Request(
260
276
  method=Method.GET,
261
277
  endpoint="/_api/replication/server-id",
arangoasync/typings.py CHANGED
@@ -1060,6 +1060,10 @@ class IndexProperties(JsonWrapper):
1060
1060
  def legacy_polygons(self) -> Optional[bool]:
1061
1061
  return self._data.get("legacyPolygons")
1062
1062
 
1063
+ @property
1064
+ def figures(self) -> Optional[Json]:
1065
+ return self._data.get("figures")
1066
+
1063
1067
  @property
1064
1068
  def estimates(self) -> Optional[bool]:
1065
1069
  return self._data.get("estimates")
@@ -1140,6 +1144,10 @@ class IndexProperties(JsonWrapper):
1140
1144
  def training_state(self) -> Optional[str]:
1141
1145
  return self._data.get("trainingState")
1142
1146
 
1147
+ @property
1148
+ def shards(self) -> Optional[Json]:
1149
+ return self._data.get("shards")
1150
+
1143
1151
  @staticmethod
1144
1152
  def compatibility_formatter(data: Json) -> Json:
1145
1153
  """python-arango compatibility formatter."""
@@ -1170,6 +1178,8 @@ class IndexProperties(JsonWrapper):
1170
1178
  result["storedValues"] = data["storedValues"]
1171
1179
  if "legacyPolygons" in data:
1172
1180
  result["legacyPolygons"] = data["legacyPolygons"]
1181
+ if "figures" in data:
1182
+ result["figures"] = data["figures"]
1173
1183
  if "estimates" in data:
1174
1184
  result["estimates"] = data["estimates"]
1175
1185
  if "analyzer" in data:
@@ -1202,6 +1212,8 @@ class IndexProperties(JsonWrapper):
1202
1212
  result["error_message"] = data["errorMessage"]
1203
1213
  if "trainingState" in data:
1204
1214
  result["training_state"] = data["trainingState"]
1215
+ if "shards" in data:
1216
+ result["shards"] = data["shards"]
1205
1217
  return result
1206
1218
 
1207
1219
  def format(self, formatter: Optional[Formatter] = None) -> Json:
arangoasync/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "1.2.3"
1
+ __version__ = "1.2.5"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-arango-async
3
- Version: 1.2.3
3
+ Version: 1.2.5
4
4
  Summary: Async Python Driver for ArangoDB
5
5
  Author-email: Alexandru Petenchea <alexandru.petenchea@arangodb.com>, Anthony Mahanna <anthony.mahanna@arangodb.com>
6
6
  Maintainer-email: Alexandru Petenchea <alexandru.petenchea@arangodb.com>, Anthony Mahanna <anthony.mahanna@arangodb.com>
@@ -26,7 +26,7 @@ Requires-Dist: multidict>=6.0
26
26
  Requires-Dist: pyjwt>=2.10.0
27
27
  Provides-Extra: dev
28
28
  Requires-Dist: aiofiles>=24.1.0; extra == "dev"
29
- Requires-Dist: black==26.3.1; extra == "dev"
29
+ Requires-Dist: black==26.5.1; extra == "dev"
30
30
  Requires-Dist: flake8==7.3.0; extra == "dev"
31
31
  Requires-Dist: isort>=5.10.1; extra == "dev"
32
32
  Requires-Dist: mypy==1.15.0; extra == "dev"
@@ -4,7 +4,7 @@ arangoasync/auth.py,sha256=16apz1_IvAB_ZSyenoPha6F_JkbHokmOWFHxPL_swxQ,3025
4
4
  arangoasync/backup.py,sha256=6ZUDjP44bilv056K2fo8l7BnmoeksJ9G9keMVP7i9FI,10577
5
5
  arangoasync/client.py,sha256=MKLYXPdDhBezFIKX1H5e7-dKzYnIHwbfN0CO8AEsAzs,10054
6
6
  arangoasync/cluster.py,sha256=64GYlpz07wBczeNTrPpxnUd17tFocrhGijGHF_dqdoo,17260
7
- arangoasync/collection.py,sha256=BpyoYSgSIJ3PfPkqh6PwT7HLg66QLGkNA4eqx6xkYaM,122457
7
+ arangoasync/collection.py,sha256=BXpFmCxqzB4qkCMMIJth4abq3ehHh62MEYovIzicFCY,124186
8
8
  arangoasync/compression.py,sha256=vMhcg5nQPkbgplk48vxARUnJ6YeIwrCLC6pDCVJSwo0,3804
9
9
  arangoasync/connection.py,sha256=f6HQOZHayluNVyH-j-sujlE_dvQrVxC1lPWnzrfQLqY,17459
10
10
  arangoasync/cursor.py,sha256=RQJOtNpxEx5n3eKx7UYPl1N8gzWYQESjufvakr18azk,8464
@@ -17,16 +17,16 @@ arangoasync/graph.py,sha256=G-todTf-ldTQtS2uhCsiigdPL4WABbbfTrLKlIPFkhA,40953
17
17
  arangoasync/http.py,sha256=xkxnLMbqFBEngmqGCuBOgRuXgQSBUk9-dzYXB1yrL98,6154
18
18
  arangoasync/job.py,sha256=B9cPDzxaYflWDj95j941wrcCj4w5j-gnmSzs3TdNdfw,7471
19
19
  arangoasync/logger.py,sha256=Ui4n-ELcYM4fujpZV6myU4by82L_70S1cfG0bhEhf-Q,58
20
- arangoasync/replication.py,sha256=gouhtixDa0zbPUTgY5vbh1MS2JHqRMeKtbL1GWbgjts,9497
20
+ arangoasync/replication.py,sha256=nbccH3jg-aiJhi1OgrGObmleP2SJsRSg5IELm2Swddc,10042
21
21
  arangoasync/request.py,sha256=z9-MeoSh_6SP6we6VsaTICY7SFo6ZRjWuc1mbZcyN_E,3096
22
22
  arangoasync/resolver.py,sha256=icsmQIkklrvq-a88vIR8C-M3rcv5BJxhDE2PK8LenG0,3409
23
23
  arangoasync/response.py,sha256=VMnoWgtyrh1JGK7mEgRNsUtSawxWMgFjHT-Cff4d8zc,2132
24
24
  arangoasync/result.py,sha256=NVwFxpYUDujHNRCqCZIBOlGLzB7XsiSmjWPFhUgRS6k,233
25
25
  arangoasync/serialization.py,sha256=ExRqrEpbk11Hb3AfFUJGqPlGBD3dbvwQg_xsZUsrm6I,2910
26
- arangoasync/typings.py,sha256=xT6Dm63aTOWnLiiN0Gkxqm0jED4A43IyP_5p9haZKy4,70135
27
- arangoasync/version.py,sha256=C-D_WWrVkBDmQmApLcm0sWNh2CgIrwWfc8_sB5vvU-Q,22
28
- python_arango_async-1.2.3.dist-info/licenses/LICENSE,sha256=6zoGhJon2tcnvzmycirNjLNBZ6rlLJtYDdVGO76RXVM,1065
29
- python_arango_async-1.2.3.dist-info/METADATA,sha256=PBU14KUEYj8BqIOS5UjNIOu_ooOUJBrQaZMXjcJ46lo,7943
30
- python_arango_async-1.2.3.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
31
- python_arango_async-1.2.3.dist-info/top_level.txt,sha256=uabwJ6t-MiIaZQXGUvYikeSbZDcF0RK37GzikBnDz5k,12
32
- python_arango_async-1.2.3.dist-info/RECORD,,
26
+ arangoasync/typings.py,sha256=LmKV4_jNUoxZ6cI91l8H2XimhYl945ow_3FrcVEoB50,70480
27
+ arangoasync/version.py,sha256=jBmZf3HLbiQlWiolOsAA6J5-BbxXD2bqFqEqDH3lfqo,22
28
+ python_arango_async-1.2.5.dist-info/licenses/LICENSE,sha256=6zoGhJon2tcnvzmycirNjLNBZ6rlLJtYDdVGO76RXVM,1065
29
+ python_arango_async-1.2.5.dist-info/METADATA,sha256=8dPPxISrFp18gU9zhZsuvdpW-OQreOptVKoJlrFZ62Y,7943
30
+ python_arango_async-1.2.5.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
31
+ python_arango_async-1.2.5.dist-info/top_level.txt,sha256=uabwJ6t-MiIaZQXGUvYikeSbZDcF0RK37GzikBnDz5k,12
32
+ python_arango_async-1.2.5.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (82.0.1)
2
+ Generator: setuptools (84.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5