Skip to content

Commit 00dfa61

Browse files
committed
Adding insert_many method
1 parent e320bb5 commit 00dfa61

File tree

3 files changed

+144
-4
lines changed

3 files changed

+144
-4
lines changed

arangoasync/collection.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1163,3 +1163,106 @@ def response_handler(resp: Response) -> Cursor:
11631163
return Cursor(executor, self.deserializer.loads(resp.raw_body))
11641164

11651165
return await self._executor.execute(request, response_handler)
1166+
1167+
async def insert_many(
1168+
self,
1169+
documents: Sequence[T],
1170+
wait_for_sync: Optional[bool] = None,
1171+
return_new: Optional[bool] = None,
1172+
return_old: Optional[bool] = None,
1173+
silent: Optional[bool] = None,
1174+
overwrite: Optional[bool] = None,
1175+
overwrite_mode: Optional[str] = None,
1176+
keep_null: Optional[bool] = None,
1177+
merge_objects: Optional[bool] = None,
1178+
refill_index_caches: Optional[bool] = None,
1179+
version_attribute: Optional[str] = None,
1180+
) -> Result[Optional[Jsons]]:
1181+
"""Insert multiple documents.
1182+
1183+
Note:
1184+
If inserting a document fails, the exception is not raised but
1185+
returned as an object in the "errors" list. It is up to you to
1186+
inspect the list to determine which documents were inserted
1187+
successfully (returns document metadata) and which were not
1188+
(returns exception object).
1189+
1190+
Args:
1191+
documents (list): Documents to insert. If an item contains the "_key" or
1192+
"_id" field, the value is used as the key of the new document
1193+
(otherwise it is auto-generated). Any "_rev" field is ignored.
1194+
wait_for_sync (bool | None): Wait until documents have been synced to disk.
1195+
return_new (bool | None): Additionally return the complete new document
1196+
under the attribute `new` in the result.
1197+
return_old (bool | None): Additionally return the complete old document
1198+
under the attribute `old` in the result. Only available if the
1199+
`overwrite` option is used.
1200+
silent (bool | None): If set to `True`, an empty object is returned as
1201+
response if all document operations succeed. No meta-data is returned
1202+
for the created documents. If any of the operations raises an error,
1203+
an array with the error object(s) is returned.
1204+
overwrite (bool | None): If set to `True`, operation does not fail on
1205+
duplicate key and existing document is overwritten (replace-insert).
1206+
overwrite_mode (str | None): Overwrite mode. Supersedes **overwrite**
1207+
option. May be one of "ignore", "replace", "update" or "conflict".
1208+
keep_null (bool | None): If set to `True`, fields with value None are
1209+
retained in the document. Otherwise, they are removed completely.
1210+
Applies only when **overwrite_mode** is set to "update"
1211+
(update-insert).
1212+
merge_objects (bool | None): If set to `True`, sub-dictionaries are merged
1213+
instead of the new one overwriting the old one. Applies only when
1214+
**overwrite_mode** is set to "update" (update-insert).
1215+
refill_index_caches (bool | None): Whether to add new entries to
1216+
in-memory index caches if document insertions affect the edge index
1217+
or cache-enabled persistent indexes.
1218+
version_attribute (str | None): Support for simple external versioning to
1219+
document operations. Only applicable if **overwrite** is set to `True`
1220+
or **overwrite_mode** is set to "update" or "replace".
1221+
1222+
Returns:
1223+
None | list: Documents metadata (e.g. document id, key, revision) and
1224+
errors or `None` if **silent** is set to `True`.
1225+
1226+
Raises:
1227+
DocumentInsertError: If insertion fails.
1228+
1229+
References:
1230+
- `create-multiple-documents <https://docs.arangodb.com/stable/develop/http-api/documents/#create-multiple-documents>`__
1231+
""" # noqa: E501
1232+
params: Params = {}
1233+
if wait_for_sync is not None:
1234+
params["waitForSync"] = wait_for_sync
1235+
if return_new is not None:
1236+
params["returnNew"] = return_new
1237+
if return_old is not None:
1238+
params["returnOld"] = return_old
1239+
if silent is not None:
1240+
params["silent"] = silent
1241+
if overwrite is not None:
1242+
params["overwrite"] = overwrite
1243+
if overwrite_mode is not None:
1244+
params["overwriteMode"] = overwrite_mode
1245+
if keep_null is not None:
1246+
params["keepNull"] = keep_null
1247+
if merge_objects is not None:
1248+
params["mergeObjects"] = merge_objects
1249+
if refill_index_caches is not None:
1250+
params["refillIndexCaches"] = refill_index_caches
1251+
if version_attribute is not None:
1252+
params["versionAttribute"] = version_attribute
1253+
1254+
request = Request(
1255+
method=Method.POST,
1256+
endpoint=f"/_api/document/{self.name}",
1257+
data=self._doc_serializer.dumps(documents),
1258+
params=params,
1259+
)
1260+
1261+
def response_handler(
1262+
resp: Response,
1263+
) -> Jsons:
1264+
if not resp.is_success:
1265+
raise DocumentInsertError(resp, request)
1266+
return self.deserializer.loads_many(resp.raw_body)
1267+
1268+
return await self._executor.execute(request, response_handler)

arangoasync/serialization.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,7 @@ def loads(self, data: bytes) -> Json:
104104
raise DeserializationError("Failed to deserialize data from JSON.") from e
105105

106106
def loads_many(self, data: bytes) -> Jsons:
107-
try:
108-
return json.loads(data) # type: ignore[no-any-return]
109-
except Exception as e:
110-
raise DeserializationError("Failed to deserialize data from JSON.") from e
107+
return self.loads(data) # type: ignore[return-value]
111108

112109

113110
DefaultSerializer = JsonSerializer

tests/test_document.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,13 @@ async def test_document_get_many(doc_col, bad_col, docs):
218218
many = await doc_col.get_many([doc["_key"] for doc in docs])
219219
assert len(many) == len(docs)
220220

221+
# Test with partially good keys
222+
keys = [doc["_key"] for doc in docs]
223+
keys.append("invalid_key")
224+
many = await doc_col.get_many(keys)
225+
assert len(many) == len(keys)
226+
assert "error" in many[-1]
227+
221228
# Test with full documents
222229
many = await doc_col.get_many(docs)
223230
assert len(many) == len(docs)
@@ -293,5 +300,38 @@ async def test_document_find(doc_col, bad_col, docs):
293300
{}, sort=[{"sort_by": "text", "sort_order": "ASC"}]
294301
):
295302
filter_docs.append(doc)
303+
296304
for idx in range(len(filter_docs) - 1):
297305
assert filter_docs[idx]["text"] <= filter_docs[idx + 1]["text"]
306+
307+
308+
@pytest.mark.asyncio
309+
async def test_document_insert_many(doc_col, bad_col, docs):
310+
# Check errors
311+
with pytest.raises(DocumentInsertError):
312+
await bad_col.insert_many(docs)
313+
314+
# Insert all documents
315+
result = await doc_col.insert_many(docs, return_new=True)
316+
assert len(result) == len(docs)
317+
for res in result:
318+
assert "error" not in res
319+
320+
# Empty list
321+
result = await doc_col.insert_many([])
322+
assert len(result) == 0
323+
324+
# Insert again (should not work due to unique constraint)
325+
result = await doc_col.insert_many(docs)
326+
assert len(result) == len(docs)
327+
for res in result:
328+
assert "error" in res
329+
330+
# Silent mode
331+
result = await doc_col.insert_many(docs, silent=True)
332+
assert len(result) == len(docs)
333+
for res in result:
334+
assert "error" in res
335+
await doc_col.truncate()
336+
result = await doc_col.insert_many(docs, silent=True)
337+
assert len(result) == 0

0 commit comments

Comments
 (0)