
J’ai décidé de me replonger dans C++ le temps d’un exercice proposé aux étudiants qui suivent ma formation sur MongoDB en école d’ingénieur. N’ayant pas pratiqué ce langage depuis l’époque où la monnaie nationale était encore le franc, j’ai renoué avec plaisir avec le langage de Bjarne Stroustrup. Voici les différentes étapes qui m’ont conduit à écrire le code permettant d’interroger, en C++, un cluster MongoDB hébergé sur Atlas.
Tout d’abord, il m’a fallu installer le pilote (driver) dédié à ce langage : mongocxx.
Installation et compilation du pilote mongocxx
Rien de plus simple pour y parvenir :
cd mongo-cxx-driver
mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local ..
make -j$(nproc)
sudo make install
Le programme principal
Une fois le driver installé, j’ai pu commencer à coder le petit programme pour accéder à mon cluster Atlas et y exécuter toutes les requêtes préparées dans mon header exercices.hpp . Le voici :
#include <iostream>
#include <bsoncxx/json.hpp>
#include <mongocxx/client.hpp>
#include <mongocxx/instance.hpp>
#include <mongocxx/uri.hpp>
#include <mongocxx/exception/exception.hpp>
#include "exercices.hpp"
using bsoncxx::to_json;
int main() {
const std::string USERNAME = "mongosensei";
const std::string PASSWORD = "xxxxx";
const std::string DATABASE = "xxxxx";
const std::string COLLECTION = "salles";
const std::string ATLAS_CLUSTER = "xxx.mongodb.net";
mongocxx::instance inst{};
try {
std::string uriString = "mongodb+srv://" + USERNAME + ":" + PASSWORD + "@" + ATLAS_CLUSTER + "/" + DATABASE;
mongocxx::uri uri(uriString);
mongocxx::client conn(uri);
std::vector<bsoncxx::document::value> exercices = getExercices();
for (const auto& exercice : exercices) {
auto numExercice = exercice["num"].get_int32();
auto filterDocument = exercice["filtre"].get_document();
auto projectionDocument = exercice["options"]["projection"].get_document();
auto options = mongocxx::options::find{};
options.projection(projectionDocument.view());
auto collection = conn[DATABASE][COLLECTION];
auto cursor = collection.find(filterDocument.view(), options);
int nbDocuments = 0;
for (auto&& doc : cursor) {
nbDocuments++;
std::cout << bsoncxx::to_json(doc) << std::endl;
}
std::cout << "Nb de documents pour l'exercice " << numExercice << " : " << nbDocuments << std::endl;
}
} catch (const mongocxx::exception& e) {
std::cerr << "Erreur de connexion : " << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Ce programme fait appel à un fichier qui contient la solution à chacun des exercices contenus dans le PDF dont le lien a été donné plus haut. Voici ce à quoi il ressemble :
#ifndef EXERCICES_HPP
#define EXERCICES_HPP
#include <vector>
#include <bsoncxx/builder/basic/document.hpp>
time_t parseDate(const std::string& dateStr) {
std::tm tm = {};
strptime(dateStr.c_str(), "%Y-%m-%d", &tm);
return mktime(&tm);
}
std::vector<bsoncxx::document::value> getExercices() {
using bsoncxx::builder::basic::kvp;
using bsoncxx::builder::basic::make_document;
using bsoncxx::builder::basic::make_array;
std::string date_str = "2021-11-15";
auto timestamp = std::chrono::milliseconds{parseDate(date_str) * 1000};
std::vector<bsoncxx::document::value> exercices = {
make_document(
kvp("num", 1),
kvp("filtre", make_document(
kvp("smac", true)
)),
kvp("options", make_document(
kvp("projection", make_document(
kvp("_id", true),
kvp("nom", true)
))
))
),
make_document(
kvp("num", 2),
kvp("filtre", make_document(
kvp("capacite", make_document(
kvp("$gt", bsoncxx::types::b_int64{1000})
))
)),
kvp("options", make_document(
kvp("projection", make_document(
kvp("_id", false),
kvp("nom", true)
))
))
),
make_document(
kvp("num", 3),
kvp("filtre", make_document(
kvp("adresse.numero", make_document(
kvp("$exists", false)
))
)),
kvp("options", make_document(
kvp("projection", make_document(
kvp("_id", true)
))
))
),
make_document(
kvp("num", 4),
kvp("filtre", make_document(
kvp("avis", make_document(
kvp("$size", bsoncxx::types::b_int64{1})
))
)),
kvp("options", make_document(
kvp("projection", make_document(
kvp("nom", true)
))
))
),
make_document(
kvp("num", 5),
kvp("filtre", make_document(
kvp("styles", "blues")
)),
kvp("options", make_document(
kvp("projection", make_document(
kvp("_id", false),
kvp("styles", true)
))
))
),
make_document(
kvp("num", 6),
kvp("filtre", make_document(
kvp("styles.0", "blues")
)),
kvp("options", make_document(
kvp("projection", make_document(
kvp("_id", false),
kvp("styles", true)
))
))
),
make_document(
kvp("num", 7),
kvp("filtre", make_document(
kvp("$and", make_array(
make_document(
kvp("adresse.codePostal", bsoncxx::types::b_regex{"^84"}),
kvp("capacite", make_document(
kvp("$lt", 500)
))
)
))
)),
kvp("options", make_document(
kvp("projection", make_document(
kvp("_id", false),
kvp("adresse.ville", true)
))
))
),
make_document(
kvp("num", 8),
kvp("filtre", make_document(
kvp("$or", make_array(
make_document(
kvp("_id", make_document(kvp("$mod", make_array(2, 0))))
),
make_document(
kvp("avis", make_document(
kvp("$exists", false)
))
)
))
)),
kvp("options", make_document(
kvp("projection", make_document(
kvp("_id", true)
))
))
),
make_document(
kvp("num", 9),
kvp("filtre", make_document(
kvp("avis", make_document(
kvp("$elemMatch", make_document(
kvp("note", make_document(
kvp("$gte", 8),
kvp("$lte", 10)
))
))
))
)),
kvp("options", make_document(
kvp("projection", make_document(
kvp("_id", false),
kvp("nom", true)
))
))
),
make_document(
kvp("num", 10),
kvp("filtre", make_document(
kvp("avis.date", make_document(
kvp("$gt", bsoncxx::types::b_date{timestamp}
))
))
),
kvp("options", make_document(
kvp("projection", make_document(
kvp("_id", false),
kvp("nom", true)
))
))
),
};
return exercices;
}
#endif // EXERCICES_HPP
La fonction parseDate est là pour nous faciliter la transformation des dates pour leur utilisation avec mes méthodes de la lib BSON, elle ne sert ici qu’une fois.
Pour construire les documents, j’ai utilisé la version basic du builder BSON, la version stream étant beaucoup moins lisible pour moi.
Compilation et exécution du code
J’ai essayé plusieurs solutions :
- la compilation avec c++
- la compilation avec g++
- l’installation de l’outil pkg-config, MongoDB proposant un fichier .pc
D’abord, la version avec pkg-config :
sudo apt install pkg-config
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
c++ -o mongo_exercices --std=c++11 mongo_exercices.cpp $(pkg-config --cflags --libs libmongocxx)
./mongo_exercices
Ensuite la version avec g++, qui marche aussi avec c++ :
g++ -o mongo_exercices mongo_exercices.cpp -lmongocxx -lbsoncxx -std=c++11 \
-I/usr/local/include/mongocxx/v_noabi \
-I/usr/local/include/bsoncxx/v_noabi \
-I/usr/local/include/bsoncxx/v_noabi/bsoncxx/third_party/mnmlstc \
-L/usr/local/lib -Wl,-rpath=/usr/local/lib \
&& ./mongo_exercices
Voici la sortie produite par notre exécutable :
{ "_id" : 1, "nom" : "AJMI Jazz Club" }
{ "_id" : 2, "nom" : "Paloma" }
Nb de documents pour l'exercice 1 : 2
{ "nom" : "Paloma" }
Nb de documents pour l'exercice 2 : 1
{ "_id" : 3 }
{ "_id" : { "$oid" : "65e08cc6fa1cb047c0a5e5ea" } }
Nb de documents pour l'exercice 3 : 2
{ "_id" : 2, "nom" : "Paloma" }
Nb de documents pour l'exercice 4 : 1
{ "styles" : [ "jazz", "soul", "funk", "blues" ] }
{ "styles" : [ "blues", "rock" ] }
Nb de documents pour l'exercice 5 : 2
{ "styles" : [ "blues", "rock" ] }
Nb de documents pour l'exercice 6 : 1
{ "adresse" : { "ville" : "Avignon" } }
{ "adresse" : { "ville" : "Le Thor" } }
Nb de documents pour l'exercice 7 : 2
{ "_id" : 2 }
{ "_id" : 3 }
{ "_id" : { "$oid" : "65e08cc6fa1cb047c0a5e5ea" } }
Nb de documents pour l'exercice 8 : 3
{ "nom" : "AJMI Jazz Club" }
{ "nom" : "Paloma" }
Nb de documents pour l'exercice 9 : 2
{ "nom" : "AJMI Jazz Club" }
{ "nom" : "Paloma" }
Nb de documents pour l'exercice 10 : 2
Pour pousser plus loin votre expérience avec C++, suivez ce lien !




