Now that you have Mangrove installed on your system, we can get started with a quick tour of Mangrove’s features. To do this, we’ll develop the data model for a very simple persistent blog application.
This guide assumes a basic working knowledge of both C++ and MongoDB. You can find a basic introduction to the concepts of MongoDB in MongoDB’s manual .
First, we’ll want to set up a class to represent a blog entry. A blog entry will have a title, an author, some content, and a posting time. We can represent this in C++ with something like the following:
#include <chrono>
class BlogEntry {
public:
std::string title;
std::string author;
std::string content;
std::chrono::system_clock::time_point time_posted;
}
If we want to store blog entries in the database, we’ll have to make the BlogEntry
class a Mangrove model. Making a class a model gives the class the semantics of a MongoDB collection, as well as instance save()
and remove()
functions for
active record
style access to the database.
To make BlogEntry
a model, simply have it inherit from mangrove::model<BlogEntry>
:
#include <chrono>
#include <mangrove/model.hpp>
class BlogEntry : public mangrove::model<BlogEntry> {
public:
std::string title;
std::string author;
std::string content;
std::chrono::system_clock::time_point time_posted;
}
Notice that the model class is templated on the class that you want to make a model. This is an example of a curiously recurring template parameter , a C++ pattern that lets the parent class have access to the type inheriting from it.
Before we can use the model, we must also specify which fields we want to be included when a BlogEntry
is saved in the database. We can do this with the MANGROVE_MAKE_KEYS_MODEL
macro:
#include <chrono>
#include <mangrove/nvp.hpp>
class BlogEntry : public mangrove::model<BlogEntry> {
public:
std::string title;
std::string contents;
std::string author;
std::chrono::system_clock::time_point time_posted;
MANGROVE_MAKE_KEYS_MODEL(BlogEntry,
MANGROVE_NVP(title),
MANGROVE_NVP(contents),
MANGROVE_NVP(author),
MANGROVE_NVP(time_posted))
}
This macro specifies that blog entries should be stored in the database with the title, author, content, and time posted, and where the name of those fields should be the name of the variables. Manually specifying which class members are included when saving to the database lets you have class members that you might not necessarily want to store persistently in the database.
The last step you must take before you can use the BlogEntry
model is to register the model with a MongoDB database using the MongoDB C++ Driver. In the following example, we’ll use a connection to the database hosted at localhost
on port 27017.
#include <mongocxx/client.hpp>
#include <mongocxx/instance.hpp>
int main() {
mongocxx::instance{};
mongocxx::client
conn{mongocxx::uri{"mongodb://localhost:27017"};
BlogEntry::setCollection(conn["my_blog"]["entries"]);
}
The BlogEntry
model has a static function setCollection()
that lets you specify in which collection to save and retrieve instances of BlogEntry
. It accepts a mongocxx::collection
, which is accessible via the mongocxx::client
. The code above sets BlogEntry
’s collection to the "entries"
collection in the "my_blog"
database hosted at "mongodb://localhost:27017"
.
If you’re planning on writing multi-threaded applications with Mangrove, be sure to carefully read the warning here.
Now that you’ve set up the model and registered it with a collection, you can start interacting with the database. The simplest way to do this is to create instances of the object and call the save() method. The following very simple function will save a blog entry to the database.
void newEntry(std::string title, std::string author, std::string content) {
BlogEntry entry{title, author, content,
std::chrono::system_clock::now()};
entry.save();
}
This particular function doesn’t do anything with the object after saving it to the database, but you are able to modify the entry
object and call save() again to modify the object and save the changes in the database.
Querying objects in Mangrove is just as easy as saving them. When you make BlogEntry
a model, you gave it the static methods find()
and findOne()
, which have very similar semantics to
their
equivalents
in the mongo shell.
The following function prints out the titles of every blog entry by a particular author using a find()
query:
#include <iostream>
#include <mangrove/query_builder.hpp>
void printTitlesBy(std::string author) {
std::cout << "All blog entries by " << author << ":\n";
for (auto entry : BlogEntry::find(MANGROVE_KEY(BlogEntry::author) == author)) {
std::cout << entry.title << "\n";
}
}
There’s a lot happening here, but it’s very simple once you understand what’s going on.
Inside the parameter list for BlogEntry::find()
, you’ll see MANGROVE_KEY(BlogEntry::author) == author
. In Mangrove, you can build MongoDB queries using the standard C++ comparison and logical operators. This is discussed more in Chapter 3, but the main thing you have to remember is to wrap the value you are comparing in a MANGROVE_KEY
macro.
The return type of BlogEntry::find()
is an iterator of BlogEntry
s. This makes it very easy to work with the often large cursors of objects that are returned from MongoDB queries. In this case, we are interacting with the cursor via the C++11
range-based for loop
.
The iterator returned by the model’s find()
method has the semantics of a MongoDB cursor. This is useful for large queries where you want to periodically read from the database as you’re reading from the query results, but this also gives the cursors some interesting behavior such as inactive cursor closure and the lack of cursor isolation. If you want to learn more about these behaviors, check out
this page
in the MongoDB manual.