Atomic Updates in MongoDB using mongoengine

02 Feb 2015


I just started out creating a hobby app using Flask, mongoengine and flask-mongoengine. Flask is a Python microframework that allows rapid application development. flask-mongoengine is a library that allows some amount of interfacing between Flask and monogoengine.

In the application I had two models defined like this

class Student(db.Document):
    name = db.StringField(max_length=255, required=True)
    gen_reg_no = db.ListField(db.EmbeddedDocumentField('RegisterNo'))

class RegisterNo(db.Document):
    register_no = db.IntField(required=True)
    year = db.IntField(required=True)

What I needed was simple: I wanted to add an attribute to the RegisterNo model. In effect the resulting model would be like this:

class RegisterNo(db.Document):
    register_no = db.IntField(required=True)
    year = db.IntField(required=True)
    section = db.IntField(required=True)

After about two days of googling, I decided to find out with trial and error. It gave some very interesting insights, which was not very apparent from the documentation

First we query the database for the records we need to modify.

Student.objects(gen_reg_no__register_no=354)

The reason we use gen_reg_no instead of name is because we need to update gen_reg_no. mongoengine will give an error saying ‘The positional operator did not find the match needed from query’.

Next we use method-chaining to update the document. mongoengine gives three methods to update a QuerySet: update(), update_one() and modify().

The update_one() performs an atomic update on the first result matched for the query, whle update() performs updates on all the results matched by the query.

There are several modifiers that can be used with these methods. I used the update_one() method with the set modifier.

The set modifier sets a particular value. Other modifiers that operate on individual values are unset, inc and dec. All other modifiers operate on lists.

So to use the set modifier, this is what I ended up with

Student.objects(gen_reg_no__year=2015).update(set__gen_reg_no__S__section=2)

Let me explain this expression: set__gen_reg_no__S__section=2

set__gen_reg_no allows to set the value of gen_reg_no. Since what we really want to do is update a value in the embedded document which is basically a list of documents, we need to traverse through the document in the embedded document. So the final expression becomes: set__gen_reg_no__S__section

The addition of the __S allows us to update list without knowing the index position(since it is a list).

Something to note is that, you cannot query the attribute you need to change.

Previous Post Next Post