Skip to content

Commit 79e0366

Browse files
committed
add ordered argument to insert method
fix MongoEngine#2169
1 parent 5fe9436 commit 79e0366

File tree

2 files changed

+67
-17
lines changed

2 files changed

+67
-17
lines changed

mongoengine/queryset/base.py

+39-16
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,12 @@ def first(self):
293293
return result
294294

295295
def insert(
296-
self, doc_or_docs, load_bulk=True, write_concern=None, signal_kwargs=None
296+
self,
297+
doc_or_docs,
298+
load_bulk=True,
299+
write_concern=None,
300+
signal_kwargs=None,
301+
ordered=True,
297302
):
298303
"""bulk insert documents
299304
@@ -309,6 +314,11 @@ def insert(
309314
each server being written to.
310315
:param signal_kwargs: (optional) kwargs dictionary to be passed to
311316
the signal calls.
317+
:param ordered (optional): If True (the default) documents will be
318+
inserted on the server serially, in the order provided. If an error
319+
occurs all remaining inserts are aborted. If False, documents will
320+
be inserted on the server in arbitrary order, possibly in parallel,
321+
and all document inserts will be attempted.
312322
313323
By default returns document instances, set ``load_bulk`` to False to
314324
return just ``ObjectIds``
@@ -341,12 +351,14 @@ def insert(
341351

342352
with set_write_concern(self._collection, write_concern) as collection:
343353
insert_func = collection.insert_many
354+
insert_func_kwargs = {"ordered": ordered}
344355
if return_one:
345356
raw = raw[0]
346357
insert_func = collection.insert_one
358+
insert_func_kwargs = {}
347359

348360
try:
349-
inserted_result = insert_func(raw)
361+
inserted_result = insert_func(raw, **insert_func_kwargs)
350362
ids = (
351363
[inserted_result.inserted_id]
352364
if return_one
@@ -358,6 +370,17 @@ def insert(
358370
except pymongo.errors.BulkWriteError as err:
359371
# inserting documents that already have an _id field will
360372
# give huge performance debt or raise
373+
if ordered:
374+
inserted = err.details["nInserted"]
375+
for doc, raw_doc in zip(docs[:inserted], raw[:inserted]):
376+
doc.pk = raw_doc["_id"]
377+
else:
378+
not_writed_ids = [
379+
error["op"]["_id"] for error in err.details["writeErrors"]
380+
]
381+
for doc, raw_doc in zip(docs, raw):
382+
if raw_doc["_id"] not in not_writed_ids:
383+
doc.pk = raw_doc["_id"]
361384
message = "Bulk write error: (%s)"
362385
raise BulkWriteError(message % err.details)
363386
except pymongo.errors.OperationFailure as err:
@@ -1715,29 +1738,29 @@ def no_dereference(self):
17151738

17161739
def _item_frequencies_map_reduce(self, field, normalize=False):
17171740
map_func = """
1718-
function() {
1719-
var path = '{{~%(field)s}}'.split('.');
1741+
function() {{
1742+
var path = '{{{{~{field}}}}}'.split('.');
17201743
var field = this;
17211744
1722-
for (p in path) {
1745+
for (p in path) {{
17231746
if (typeof field != 'undefined')
17241747
field = field[path[p]];
17251748
else
17261749
break;
1727-
}
1728-
if (field && field.constructor == Array) {
1729-
field.forEach(function(item) {
1750+
}}
1751+
if (field && field.constructor == Array) {{
1752+
field.forEach(function(item) {{
17301753
emit(item, 1);
1731-
});
1732-
} else if (typeof field != 'undefined') {
1754+
}});
1755+
}} else if (typeof field != 'undefined') {{
17331756
emit(field, 1);
1734-
} else {
1757+
}} else {{
17351758
emit(null, 1);
1736-
}
1737-
}
1738-
""" % {
1739-
"field": field
1740-
}
1759+
}}
1760+
}}
1761+
""".format(
1762+
field=field
1763+
)
17411764
reduce_func = """
17421765
function(key, values) {
17431766
var total = 0;

tests/queryset/test_queryset.py

+28-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from mongoengine import *
1313
from mongoengine.connection import get_db
1414
from mongoengine.context_managers import query_counter, switch_db
15-
from mongoengine.errors import InvalidQueryError
15+
from mongoengine.errors import BulkWriteError, InvalidQueryError
1616
from mongoengine.mongodb_support import (
1717
MONGODB_36,
1818
get_mongodb_version,
@@ -1067,6 +1067,33 @@ class Comment(Document):
10671067
com2 = Comment(id=1)
10681068
Comment.objects.insert([com1, com2])
10691069

1070+
def test_bulk_insert_ordered(self):
1071+
class Comment(Document):
1072+
name = StringField(unique=True)
1073+
1074+
Comment.drop_collection()
1075+
Comment.objects.insert(Comment(name="b"), ordered=True)
1076+
comments = [Comment(name="a"), Comment(name="b"), Comment(name="c")]
1077+
with pytest.raises(BulkWriteError):
1078+
Comment.objects.insert(comments, ordered=True)
1079+
Comment.objects.get(name="a")
1080+
with pytest.raises(DoesNotExist):
1081+
Comment.objects.get(name="c")
1082+
assert comments[0].pk is not None
1083+
assert comments[1].pk is None
1084+
assert comments[2].pk is None
1085+
1086+
Comment.drop_collection()
1087+
Comment.objects.insert(Comment(name="b"), ordered=False)
1088+
comments = [Comment(name="a"), Comment(name="b"), Comment(name="c")]
1089+
with pytest.raises(BulkWriteError):
1090+
Comment.objects.insert(comments, ordered=False)
1091+
Comment.objects.get(name="a")
1092+
Comment.objects.get(name="c")
1093+
assert comments[0].pk is not None
1094+
assert comments[1].pk is None
1095+
assert comments[2].pk is not None
1096+
10701097
def test_insert_raise_if_duplicate_in_constraint(self):
10711098
class Comment(Document):
10721099
id = IntField(primary_key=True)

0 commit comments

Comments
 (0)