Object-Document Mapping in MongoDB

MongoDB expresses data to be saved in a JSON-like format and saves it as a document. The function of associating such a document with an object in a programming language is called object-document mapping (O/D mapping).

As in the O/R mapping chapter described, one document here is also associated with one object in the O/D mapping.

Since MongoDB documents are JSON-like formats, it is possible to have a hierarchical (nested) structure, but in O/D mapping, an object doesn’t correspond to two or more levels of documents. For example, the following example shows the only supported simple form in O/D mapping:

{
  "name": "John Smith",
  "age": 20,
  "email": "foo@example.com"
}

Setup

If you are not sure anymore how to setup a MongoDB connection properly, please refer to the Access MongoDB chapter.

For generating the class for O/D mapping, execute the following command in the application root directory. In this example, we create a collection named foo. The name of the model will be Foo.

 $ tspawn mm  foo
  created   models/mongoobjects/fooobject.h
  created   models/foo.h
  created   models/foo.cpp
  updated   models/models.pro

The next step is to define the data to be stored in the document. Then we edit the c++ header file models/mongoobjects/fooobject.h by adding the QString variables title and body.

class T_MODEL_EXPORT FooObject : public TMongoObject, public QSharedData
{
public:
    QString title;     // ← Add here
    QString body;      // ← Add here
    QString _id;
    QDateTime createdAt;
    QDateTime updatedAt;
    int lockRevision;
    enum PropertyIndex {
        Id = 0,
        CreatedAt,
        UpdatedAt,
        LockRevision,
    };
 :

Variables other than _id are not mandatory, so you can delete them. The variable _id is equivalent to the ObjectID in MongoDB, therefore please don’t delete it.

  • This object is responsible for accessing MongoDB, which is why we call it “Mongo object” from the time being.

Execute the following command again in order to reflect the added contents to other files.

 $ tspawn mm foo

Type ‘Y’ for all files with changes.
This completes the model with CRUD.

If you want to generate scaffolds including controllers and views, you can execute the following command instead of ‘tspawn mm foo’.

 $ tspawn ms foo

Now, scaffolding has been generated. After compiling, try running the AP server.

 $ treefrog -d -e dev

By accessing http://localhost:8800/foo/ in the browser, a screen which contains a list will be displayed. Use this screen as a starting point for registering, editing and deleting data.

As you can see, this file is similar to the migration file (in Rails), because you automatically define/change the layout of the Mongo document by editing the class of the Mongo object.

Read a Mongo object

Let’s see how a Mongo object, which is referring to the class created by scaffolding, can be read. We do this by loading a Mongo object by using the object ID as a key.

QString id;
id = ...
TMongoODMapper<FooObject> mapper;
FooObject foo = mapper.findByObjectId(id));

Create a Mongo object

Make it the same way as instantiating ordinary objects by setting properties. After this is done, calling the create() method will create a new document in the MongoDB for you.

FooObject foo;
foo.title = ...
foo.body = ...
foo.create();

Since the object ID is generated automatically, please don’t set anything.

Update a Mongo object

Obtain the Mongo object, for example, by calling its object ID and set a new value. When this is done, the update() method will eventually update the object in the MongoDB internally.

TMongoODMapper<FooObject> mapper;
FooObject foo = mapper.findByObjectId(id));
foo.title = ...
foo.update();

There is also a save() method for saving the document.
This calls create() method if the corresponding document does not exist in the MongoDB, and then the update() method if it exists.

Delete a Mongo object

Deleting a Mongo object deletes the document. Use the remove() method to remove the object.

TMongoODMapper<FooObject> mapper;
FooObject foo = mapper.findByObjectId(id));
foo.remove();
Supplement

As mentioned above, Mongo objects can be used in the same way as ORM objects (O/R mapper objects). Looking from the controller’s point of view, since the functions provided by the model class are the same, there is no difference in their use. These objects are hidden in the ‘private’ area of the model.

In other words, if you define model class names which do not overlap, you can even have access to MongoDB and RDB simultaneously, which enables you to easily distribute data to multiple DB systems. If implemented correctly, you can reduce the load of RDB that sometimes may tend to be a bottleneck of the system.
However, when distributing it, you should consider whether the data is supposed to be saved in RDB or in MongoDB depending on the nature of the data. Probably, the question is whether you really want to use transactions or not?

In this way, you can easily access to database systems with different mechanisms which gives you the opportunity to build highly scalable systems as a Web application.

Differences between the Mongo object class and the ORM object class:

In a Mongo object class, you can define QStringLists as an instance variable like the following sequence of code visualizes:

class T_MODEL_EXPORT FooObject : public TMongoObject, public QSharedData
{
public:
    QString _id;
    QStringList  texts;
     :
  • Please consider: QStringList cannot be defined in ORM object classes.