Getting Started With MongoDB and C++
Rate this tutorial
This article will show you how to utilize Microsoft Visual Studio to compile and install the MongoDB C and C++ drivers on Windows, and use these drivers to create a console application that can interact with your MongoDB data by performing basic CRUD operations.
Tools and libraries used in this tutorial:
- Microsoft Windows 11
- Microsoft Visual Studio 2022 17.3.6
- Language standard: C++17
- MongoDB C Driver version: 1.23
- MongoDB C++ Driver version: 3.7.0
- boost: 1.80.0
- Python: 3.10
- CMake: 3.25.0
- MongoDB Atlas account with a cluster created.
- Your machine’s IP address is whitelisted. Note: You can add 0.0.0.0/0 as the IP address, which should allow access from any machine. This setting is not recommended for production use.
In the Workloads tab during installation, select “Desktop development with C++.”
Step 2: Install CMake: Download | CMake
- For simplicity, choose the installer.
- In the setup, make sure to select “Add CMake to the system PATH for all users.” This enables the CMake executable to be easily accessible.
Detailed instructions and configurations available here:
C++ Driver has a dependency on C driver. Hence, we need to install C Driver first.
- Download C Driver
- Download release tarball — Releases · mongodb/mongo-c-driver — and extract it to C:\Repos\mongo-c-driver-1.23.0.
- Setup build via CMake
- Launch powershell/terminal as an administrator.
- Navigate to C:\Repos\mongo-c-driver-1.23.0 and create a new folder named cmake-build for the build files.
- Navigate to C: \Repos\mongo-c-driver-1.23.0\cmake-build.
- Run the below command to configure and generate build files using CMake.
1 cmake -G "Visual Studio 17 2022" -A x64 -S "C:\Repos\mongo-c-driver-1.23.0" -B "C:\Repos\mongo-c-driver-1.23.0\cmake-build"
Note: Build setup can be done with the CMake GUI application, as well.
- Execute build
- Visual Studio’s default build type is Debug. A release build with debug info is recommended for production use.
- Run the below command to build and install the driver
1 cmake --build . --config RelWithDebInfo --target install
- You should now see libmongoc and libbson installed in C:/Program Files/mongo-c-driver.
- Move the mongo-c-driver to C:/ for convenience. Hence, C Driver should now be present at C:/mongo-c-driver.
- Download C++ Driver
- Download release tarball — Releases · mongodb/mongo-cxx-driver — and extract it to C:\Repos\mongo-cxx-driver-r3.7.0.
- Set up build via CMake
- Launch powershell/terminal as an administrator.
- Navigate to C:\Repos\mongo-cxx-driver-r3.7.0\build.
- Run the below command to generate and configure build files via CMake.
1 cmake .. -G "Visual Studio 17 2022" -A x64 -DCMAKE_CXX_STANDARD=17 -DCMAKE_CXX_FLAGS="/Zc:__cplusplus /EHsc" -DCMAKE_PREFIX_PATH=C:\mongo-c-driver -DCMAKE_INSTALL_PREFIX=C:\mongo-cxx-driver
Note: Setting DCMAKE_CXX_FLAGS should not be required for C++ driver version 3.7.1 and above.
- Execute build
- Run the below command to build and install the driver
1 cmake --build . --config RelWithDebInfo --target install
- You should now see C++ driver installed in C:\mongo-cxx-driver.
- Create a new project in Visual Studio.
- Select Console App in the templates.
- Visual Studio should create a new project and open a .cpp file which prints “Hello World.” Navigate to the Solution Explorer panel, right-click on the solution name (MongoCXXGettingStarted, in this case), and click Properties.
- Go to Configuration Properties > C/C++ > General > Additional Include Directories and add the include directories from the C and C++ driver installation folders, as shown below.
- Go to Configuration Properties > C/C++ > Language and change the C++ Language Standard to C++17.
- Go to Configuration Properties > C/C++ > Command Line and add /Zc:__cplusplus in the Additional Options field. This flag is needed to opt into the correct definition of __cplusplus.
- Go to Configuration Properties > Linker > Input and add the driver libs in Additional Dependencies section, as shown below.
- Go to Configuration Properties > Debugging > Environment to add a path to the driver executables, as shown below.
Source available here
Let’s build an application that maintains student records. We will input student data from the user, save them in the database, and perform different CRUD operations on the database.
Let’s start with a simple program to connect to the MongoDB Atlas cluster and access the databases. Get the connection string (URI) to the cluster and create a new environment variable with key as “MONGODB_URI” and value as the connection string (URI). It’s a good practice to keep the connection string decoupled from the code.
Tip: Restart your machine after creating the environment variable in case the “getEnvironmentVariable” function fails to retrieve the environment variable.
1 #include <mongocxx/client.hpp> 2 #include <bsoncxx/builder/stream/document.hpp> 3 #include <bsoncxx/json.hpp> 4 #include <mongocxx/uri.hpp> 5 #include <mongocxx/instance.hpp> 6 #include <algorithm> 7 #include <iostream> 8 #include <vector> 9 using namespace std; 10 std::string getEnvironmentVariable(std::string environmentVarKey) 11 { 12 char* pBuffer = nullptr; 13 size_t size = 0; 14 auto key = environmentVarKey.c_str(); 15 // Use the secure version of getenv, ie. _dupenv_s to fetch environment variable. 16 if (_dupenv_s(&pBuffer, &size, key) == 0 && pBuffer != nullptr) 17 { 18 std::string environmentVarValue(pBuffer); 19 free(pBuffer); 20 return environmentVarValue; 21 } 22 else 23 { 24 return ""; 25 } 26 } 27 auto mongoURIStr = getEnvironmentVariable("MONGODB_URI"); 28 static const mongocxx::uri mongoURI = mongocxx::uri{ mongoURIStr }; 29 // Get all the databases from a given client. 30 vector<string> getDatabases(mongocxx::client& client) 31 { 32 return client.list_database_names(); 33 } 34 int main() 35 { 36 // Create an instance. 37 mongocxx::instance inst{}; 38 mongocxx::options::client client_options; 39 auto api = mongocxx::options::server_api{ mongocxx::options::server_api::version::k_version_1 }; 40 client_options.server_api_opts(api); 41 mongocxx::client conn{ mongoURI, client_options} 42 auto dbs = getDatabases(conn); 43 for (auto db : dbs) 44 { 45 cout << db << endl; 46 } 47 return 0; 48 }
Click on “Launch Debugger” to launch the console application. The output should looks something like this:
Since the database is successfully connected to our application, let’s write some helper functions to interact with the database, performing CRUD operations.
1 // Create a new collection in the given database. 2 void createCollection(mongocxx::database& db, const string& collectionName) 3 { 4 db.create_collection(collectionName); 5 } 6 // Create a document from the given key-value pairs. 7 bsoncxx::document::value createDocument(const vector<pair<string, string>>& keyValues) 8 { 9 bsoncxx::builder::stream::document document{}; 10 for (auto& keyValue : keyValues) 11 { 12 document << keyValue.first << keyValue.second; 13 } 14 return document << bsoncxx::builder::stream::finalize; 15 } 16 // Insert a document into the given collection. 17 void insertDocument(mongocxx::collection& collection, const bsoncxx::document::value& document) 18 { 19 collection.insert_one(document.view()); 20 }
1 // Print the contents of the given collection. 2 void printCollection(mongocxx::collection& collection) 3 { 4 // Check if collection is empty. 5 if (collection.count_documents({}) == 0) 6 { 7 cout << "Collection is empty." << endl; 8 return; 9 } 10 auto cursor = collection.find({}); 11 for (auto&& doc : cursor) 12 { 13 cout << bsoncxx::to_json(doc) << endl; 14 } 15 } 16 // Find the document with given key-value pair. 17 void findDocument(mongocxx::collection& collection, const string& key, const string& value) 18 { 19 // Create the query filter 20 auto filter = bsoncxx::builder::stream::document{} << key << value << bsoncxx::builder::stream::finalize; 21 //Add query filter argument in find 22 auto cursor = collection.find({ filter }); 23 for (auto&& doc : cursor) 24 { 25 cout << bsoncxx::to_json(doc) << endl; 26 } 27 }
1 // Update the document with given key-value pair. 2 void updateDocument(mongocxx::collection& collection, const string& key, const string& value, const string& newKey, const string& newValue) 3 { 4 collection.update_one(bsoncxx::builder::stream::document{} << key << value << bsoncxx::builder::stream::finalize, 5 bsoncxx::builder::stream::document{} << "$set" << bsoncxx::builder::stream::open_document << newKey << newValue << bsoncxx::builder::stream::close_document << bsoncxx::builder::stream::finalize); 6 }
1 // Delete a document from a given collection. 2 void deleteDocument(mongocxx::collection& collection, const bsoncxx::document::value& document) 3 { 4 collection.delete_one(document.view()); 5 }
With all the helper functions in place, let’s create a menu in the main function which we can use to interact with the application.
1 // ********************************************** I/O Methods ********************************************** 2 // Input student record. 3 void inputStudentRecord(mongocxx::collection& collection) 4 { 5 string name, rollNo, branch, year; 6 cout << "Enter name: "; 7 cin >> name; 8 cout << "Enter roll number: "; 9 cin >> rollNo; 10 cout << "Enter branch: "; 11 cin >> branch; 12 cout << "Enter year: "; 13 cin >> year; 14 insertDocument(collection, createDocument({ {"name", name}, {"rollNo", rollNo}, {"branch", branch}, {"year", year} })); 15 } 16 // Update student record. 17 void updateStudentRecord(mongocxx::collection& collection) 18 { 19 string rollNo, newBranch, newYear; 20 cout << "Enter roll number: "; 21 cin >> rollNo; 22 cout << "Enter new branch: "; 23 cin >> newBranch; 24 cout << "Enter new year: "; 25 cin >> newYear; 26 updateDocument(collection, "rollNo", rollNo, "branch", newBranch); 27 updateDocument(collection, "rollNo", rollNo, "year", newYear); 28 } 29 // Find student record. 30 void findStudentRecord(mongocxx::collection& collection) 31 { 32 string rollNo; 33 cout << "Enter roll number: "; 34 cin >> rollNo; 35 findDocument(collection, "rollNo", rollNo); 36 } 37 // Delete student record. 38 void deleteStudentRecord(mongocxx::collection& collection) 39 { 40 string rollNo; 41 cout << "Enter roll number: "; 42 cin >> rollNo; 43 deleteDocument(collection, createDocument({ {"rollNo", rollNo} })); 44 } 45 // Print student records. 46 void printStudentRecords(mongocxx::collection& collection) 47 { 48 printCollection(collection); 49 } 50 // ********************************************** Main ********************************************** 51 int main() 52 { 53 if(mongoURI.to_string().empty()) 54 { 55 cout << "URI is empty"; 56 return 0; 57 } 58 // Create an instance. 59 mongocxx::instance inst{}; 60 mongocxx::options::client client_options; 61 auto api = mongocxx::options::server_api{ mongocxx::options::server_api::version::k_version_1 }; 62 client_options.server_api_opts(api); 63 mongocxx::client conn{ mongoURI, client_options}; 64 const string dbName = "StudentRecords"; 65 const string collName = "StudentCollection"; 66 auto dbs = getDatabases(conn); 67 // Check if database already exists. 68 if (!(std::find(dbs.begin(), dbs.end(), dbName) != dbs.end())) 69 { 70 // Create a new database & collection for students. 71 conn[dbName]; 72 } 73 auto studentDB = conn.database(dbName); 74 auto allCollections = studentDB.list_collection_names(); 75 // Check if collection already exists. 76 if (!(std::find(allCollections.begin(), allCollections.end(), collName) != allCollections.end())) 77 { 78 createCollection(studentDB, collName); 79 } 80 auto studentCollection = studentDB.collection(collName); 81 // Create a menu for user interaction 82 int choice = -1; 83 do while (choice != 0) 84 { 85 //system("cls"); 86 cout << endl << "**************************************************************************************************************" << endl; 87 cout << "Enter 1 to input student record" << endl; 88 cout << "Enter 2 to update student record" << endl; 89 cout << "Enter 3 to find student record" << endl; 90 cout << "Enter 4 to delete student record" << endl; 91 cout << "Enter 5 to print all student records" << endl; 92 cout << "Enter 0 to exit" << endl; 93 cout << "Enter Choice : "; 94 cin >> choice; 95 cout << endl; 96 switch (choice) 97 { 98 case 1: 99 inputStudentRecord(studentCollection); 100 break; 101 case 2: 102 updateStudentRecord(studentCollection); 103 break; 104 case 3: 105 findStudentRecord(studentCollection); 106 break; 107 case 4: 108 deleteStudentRecord(studentCollection); 109 break; 110 case 5: 111 printStudentRecords(studentCollection); 112 break; 113 case 0: 114 break; 115 default: 116 cout << "Invalid choice" << endl; 117 break; 118 } 119 } while (choice != 0); 120 return 0; 121 }
When this application is executed, you can manage the student records via the console interface. Here’s a demo:
You can also see the collection in Atlas reflecting any change made via the console application.
With this article, we covered installation of C/C++ driver and creating a console application in Visual Studio that connects to MongoDB Atlas to perform basic CRUD operations.