Ensure that the update actions are grouped rather than serial.

This is a performance update. When multiple properties of the same
entity have been deleted and modified, 2 calls to update the entity are
made, one {"$set": … } and another {"$unset": … }. This is 2 network
interface calls which is a performance killer (even at lan speeds).

Fixes: #210
This commit is contained in:
Nick Joyce 2013-01-07 14:49:39 +00:00 committed by Nick Joyce
parent e537369d98
commit 7bb9c7d47f
2 changed files with 38 additions and 3 deletions

View File

@ -234,11 +234,16 @@ class Document(BaseDocument):
select_dict[actual_key] = doc[actual_key] select_dict[actual_key] = doc[actual_key]
upsert = self._created upsert = self._created
work = {}
if updates: if updates:
collection.update(select_dict, {"$set": updates}, work["$set"] = updates
upsert=upsert, safe=safe, **write_options)
if removals: if removals:
collection.update(select_dict, {"$unset": removals}, work["$unset"] = removals
if work:
collection.update(select_dict, work,
upsert=upsert, safe=safe, **write_options) upsert=upsert, safe=safe, **write_options)
warn_cascade = not cascade and 'cascade' not in self._meta warn_cascade = not cascade and 'cascade' not in self._meta

View File

@ -3515,6 +3515,36 @@ class ValidatorErrorTest(unittest.TestCase):
self.assertRaises(OperationError, change_shard_key) self.assertRaises(OperationError, change_shard_key)
def test_set_unset_one_operation(self):
"""Ensure that $set and $unset actions are performed in the same
operation.
"""
class FooBar(Document):
meta = {
'collection': 'foobar',
}
foo = StringField(default=None)
bar = StringField(default=None)
FooBar.drop_collection()
# write an entity with a single prop
foo = FooBar(foo='foo')
foo.save()
self.assertEqual(foo.foo, 'foo')
# set foo to the default causing mongoengine to $unset foo.
foo.foo = None
foo.bar = 'bar'
foo.save()
foo.reload()
self.assertIsNone(foo.foo)
self.assertEqual(foo.bar, 'bar')
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()