MONGOC_TUTORIAL(3)
π MONGOC_TUTORIAL β libmongoc's lil' get-started guide π
okay so bestie, welcome to the MongoDB C Driver tutorial (the manual page is MONGOC_TUTORIAL(3) from the libmongoc crew, and yes we're doing this whole thing together). This guide offers a brief introduction to the MongoDB C Driver. π¦
Wanna go deeper on the C API? Slay, go check out the API Reference. It's giving encyclopedia, but in a good way.
β¨ INSTALLING β¨
For detailed instructions on installing the MongoDB C Driver on a particular platform, please see the installation guide. Pick your platform, follow the steps, boom, you're in. π
πΈ STARTING MONGODB πΈ
To run the examples in this tutorial, MongoDB must be installed and running on localhost on the default port, 27017. No cap. To check if it is up and running, connect to it with the MongoDB shell like so:
$ mongosh --host localhost --port 27017 --quiet
Enterprise rs0 [direct: primary] test> db.version()
7.0.0
>
If you get a version number back, you're so good. Main character energy. π
π INCLUDE AND LINK LIBMONGOC IN YOUR C PROGRAM π
π Include mongoc.h
All libmongoc's functions and types are available in one header file. One! Single! Header! Simply include mongoc/mongoc.h:
#include <mongoc/mongoc.h>
That's it. That's the whole vibe. β¨
π CMake
The libmongoc installation includes a CMake config-file package, so you can use CMake's find_package command to import libmongoc's CMake target and link to libmongoc (as a shared library):
CMakeLists.txt
# Specify the minimum version you require.
find_package (mongoc-1.0 1.7 REQUIRED)
# The "hello_mongoc.c" sample program is shared among four tests.
add_executable (hello_mongoc ../../hello_mongoc.c)
target_link_libraries (hello_mongoc PRIVATE mongo::mongoc_shared)
You can also use libmongoc as a static library instead β use the mongo::mongoc_static CMake target:
# Specify the minimum version you require.
find_package (mongoc-1.0 1.7 REQUIRED)
# The "hello_mongoc.c" sample program is shared among four tests.
add_executable (hello_mongoc ../../hello_mongoc.c)
target_link_libraries (hello_mongoc PRIVATE mongo::mongoc_static)
Shared or static, pick your fighter. π₯
π pkg-config
If you're not using CMake, use pkg-config on the command line to set header and library paths:
gcc -o hello_mongoc hello_mongoc.c $(pkg-config --libs --cflags libmongoc-1.0)
Or, if you wanna statically link to libmongoc:
gcc -o hello_mongoc hello_mongoc.c $(pkg-config --libs --cflags libmongoc-static-1.0)
π Specifying header and include paths manually
If you aren't using CMake or pkg-config, paths and libraries can be managed manually. Doing it by hand, old school, we respect it. π«Ά
$ gcc -o hello_mongoc hello_mongoc.c \
-I/usr/local/include/libbson-1.0 -I/usr/local/include/libmongoc-1.0 \
-lmongoc-1.0 -lbson-1.0
$ ./hello_mongoc
{ "ok" : 1.000000 }
For Windows users, the code can be compiled and run with the following commands. (This assumes that the MongoDB C Driver has been installed to C:\mongo-c-driver; change the include directory as needed.)
C:\> cl.exe /IC:\mongo-c-driver\include\libbson-1.0 /IC:\mongo-c-driver\include\libmongoc-1.0 hello_mongoc.c
C:\> hello_mongoc
{ "ok" : 1.000000 }
π USE LIBMONGOC IN A MICROSOFT VISUAL STUDIO PROJECT π
See the libmongoc and Visual Studio guide. Yeah, there's a whole separate page for that, we're not cramming it in here. π
π₯ MAKING A CONNECTION π₯
Access MongoDB with a mongoc_client_t. It transparently connects to standalone servers, replica sets and sharded clusters on demand β like it just knows. β¨ To perform operations on a database or collection, create a mongoc_database_t or mongoc_collection_t struct from the mongoc_client_t.
At the start of an application, call mongoc_init() before any other libmongoc functions. At the end, call the appropriate destroy function for each collection, database, or client handle, in reverse order from how they were constructed. And call mongoc_cleanup() before exiting.
bestie this order is NOT optional, it's giving choreography. Init first, cleanup last, destroy in reverse. iykyk. π
The example below establishes a connection to a standalone server on localhost, registers the client application as "connect-example," and performs a simple command.
More information about database operations can be found in the CRUD Operations and Executing Commands sections. Examples of connecting to replica sets and sharded clusters can be found in the Advanced Connections page, while examples of data compression can be found in the Data Compression page.
hello_mongoc.c
#include <mongoc/mongoc.h>
int
main (int argc, char *argv[])
{
const char *uri_string = "mongodb://localhost:27017";
mongoc_uri_t *uri;
mongoc_client_t *client;
mongoc_database_t *database;
mongoc_collection_t *collection;
bson_t *command, reply, *insert;
bson_error_t error;
char *str;
bool retval;
/*
* Required to initialize libmongoc's internals
*/
mongoc_init ();
/*
* Optionally get MongoDB URI from command line
*/
if (argc > 1) {
uri_string = argv[1];
}
/*
* Safely create a MongoDB URI object from the given string
*/
uri = mongoc_uri_new_with_error (uri_string, &error);
if (!uri) {
fprintf (stderr,
"failed to parse URI: %s\n"
"error message: %s\n",
uri_string,
error.message);
return EXIT_FAILURE;
}
/*
* Create a new client instance
*/
client = mongoc_client_new_from_uri (uri);
if (!client) {
return EXIT_FAILURE;
}
/*
* Register the application name so we can track it in the profile logs
* on the server. This can also be done from the URI (see other examples).
*/
mongoc_client_set_appname (client, "connect-example");
/*
* Get a handle on the database "db_name" and collection "coll_name"
*/
database = mongoc_client_get_database (client, "db_name");
collection = mongoc_client_get_collection (client, "db_name", "coll_name");
/*
* Do work. This example pings the database, prints the result as JSON and
* performs an insert
*/
command = BCON_NEW ("ping", BCON_INT32 (1));
retval = mongoc_client_command_simple (
client, "admin", command, NULL, &reply, &error);
if (!retval) {
fprintf (stderr, "%s\n", error.message);
return EXIT_FAILURE;
}
str = bson_as_json (&reply, NULL);
printf ("%s\n", str);
insert = BCON_NEW ("hello", BCON_UTF8 ("world"));
if (!mongoc_collection_insert_one (collection, insert, NULL, NULL, &error)) {
fprintf (stderr, "%s\n", error.message);
}
bson_destroy (insert);
bson_destroy (&reply);
bson_destroy (command);
bson_free (str);
/*
* Release our handles and clean up libmongoc
*/
mongoc_collection_destroy (collection);
mongoc_database_destroy (database);
mongoc_uri_destroy (uri);
mongoc_client_destroy (client);
mongoc_cleanup ();
return EXIT_SUCCESS;
}
and that's only part 1, bestie β there's so much more coming, don't go anywhere π
β¨ CREATING BSON DOCUMENTS β¨
So MongoDB stores everything in its own lil data format called BSON, and the C driver outsources all that to libbson. Bestie, you've got three whole ways to build these documents: you can append key-value pairs one at a time, you can use BCON, or you can just parse some JSON. Pick your fighter π
πΈ Appending BSON πΈ
A BSON document in code is a bson_t, and you can construct it one field at a time using libbson's append functions. It's giving arts-and-crafts but make it data π
So like, say you want a document that looks exactly like this:
{
born : ISODate("1906-12-09"),
died : ISODate("1992-01-01"),
name : {
first : "Grace",
last : "Hopper"
},
languages : [ "MATH-MATIC", "FLOW-MATIC", "COBOL" ],
degrees: [ { degree: "BA", school: "Vassar" }, { degree: "PhD", school: "Yale" } ]
}
Then you write this, no cap:
#include <bson/bson.h>
int
main (int argc, char *argv[])
{
struct tm born = {0};
struct tm died = {0};
const char *lang_names[] = {"MATH-MATIC", "FLOW-MATIC", "COBOL"};
const char *schools[] = {"Vassar", "Yale"};
const char *degrees[] = {"BA", "PhD"};
uint32_t i;
bson_t *document;
bson_t child;
bson_array_builder_t *bab;
char *str;
document = bson_new ();
/*
* Append { "born" : ISODate("1906-12-09") } to the document.
* Passing -1 for the length argument tells libbson to calculate the
* string length.
*/
born.tm_year = 6; /* years are 1900-based */
born.tm_mon = 11; /* months are 0-based */
born.tm_mday = 9;
bson_append_date_time (document, "born", -1, mktime (&born) * 1000);
/*
* Append { "died" : ISODate("1992-01-01") } to the document.
*/
died.tm_year = 92;
died.tm_mon = 0;
died.tm_mday = 1;
/*
* For convenience, this macro passes length -1 by default.
*/
BSON_APPEND_DATE_TIME (document, "died", mktime (&died) * 1000);
/*
* Append a subdocument.
*/
BSON_APPEND_DOCUMENT_BEGIN (document, "name", &child);
BSON_APPEND_UTF8 (&child, "first", "Grace");
BSON_APPEND_UTF8 (&child, "last", "Hopper");
bson_append_document_end (document, &child);
/*
* Append array of strings. Generate keys "0", "1", "2".
*/
BSON_APPEND_ARRAY_BUILDER_BEGIN (document, "languages", &bab);
for (i = 0; i < sizeof lang_names / sizeof (char *); ++i) {
bson_array_builder_append_utf8 (bab, lang_names[i], -1);
}
bson_append_array_builder_end (document, bab);
/*
* Array of subdocuments:
* degrees: [ { degree: "BA", school: "Vassar" }, ... ]
*/
BSON_APPEND_ARRAY_BUILDER_BEGIN (document, "degrees", &bab);
for (i = 0; i < sizeof degrees / sizeof (char *); ++i) {
bson_array_builder_append_document_begin (bab, &child);
BSON_APPEND_UTF8 (&child, "degree", degrees[i]);
BSON_APPEND_UTF8 (&child, "school", schools[i]);
bson_array_builder_append_document_end (bab, &child);
}
bson_append_array_builder_end (document, bab);
/*
* Print the document as a JSON string.
*/
str = bson_as_canonical_extended_json (document, NULL);
printf ("%s\n", str);
bson_free (str);
/*
* Clean up allocated bson documents.
*/
bson_destroy (document);
return 0;
}
And if you wanna know every single type you can shove into a bson_t? Peep the libbson documentation, she has the full menu π«Ά
π Using BCON π
BSON C Object Notation, or BCON for short (because bestie, who has time for the whole thing), is a whole other way to build BSON documents that looks way closer to the format you're actually picturing in your head. Downside: it's got less type-safety than BSON's append functions. Upside: way less code. Trade-offs, we love her π
#include <bson/bson.h>
int
main (int argc,
char *argv[])
{
struct tm born = { 0 };
struct tm died = { 0 };
bson_t *document;
char *str;
born.tm_year = 6;
born.tm_mon = 11;
born.tm_mday = 9;
died.tm_year = 92;
died.tm_mon = 0;
died.tm_mday = 1;
document = BCON_NEW (
"born", BCON_DATE_TIME (mktime (&born) * 1000),
"died", BCON_DATE_TIME (mktime (&died) * 1000),
"name", "{",
"first", BCON_UTF8 ("Grace"),
"last", BCON_UTF8 ("Hopper"),
"}",
"languages", "[",
BCON_UTF8 ("MATH-MATIC"),
BCON_UTF8 ("FLOW-MATIC"),
BCON_UTF8 ("COBOL"),
"]",
"degrees", "[",
"{", "degree", BCON_UTF8 ("BA"), "school", BCON_UTF8 ("Vassar"), "}",
"{", "degree", BCON_UTF8 ("PhD"), "school", BCON_UTF8 ("Yale"), "}",
"]");
/*
* Print the document as a JSON string.
*/
str = bson_as_canonical_extended_json (document, NULL);
printf ("%s\n", str);
bson_free (str);
/*
* Clean up allocated bson documents.
*/
bson_destroy (document);
return 0;
}
Noticed it? π BCON can spin up arrays, subdocuments, and honestly whatever arbitrary fields you want. She does it all, iconic behavior β¨
π¦ Creating BSON from JSON π¦
For single documents, you can just make BSON straight from a JSON string with bson_new_from_json. The laziness is valid, honestly.
#include <bson/bson.h>
int
main (int argc,
char *argv[])
{
bson_error_t error;
bson_t *bson;
char *string;
const char *json = "{\"name\": {\"first\":\"Grace\", \"last\":\"Hopper\"}}";
bson = bson_new_from_json ((const uint8_t *)json, -1, &error);
if (!bson) {
fprintf (stderr, "%s\n", error.message);
return EXIT_FAILURE;
}
string = bson_as_canonical_extended_json (bson, NULL);
printf ("%s\n", string);
bson_free (string);
return 0;
}
And if you've got a whole sequence of JSON documents to initialize from? Use bson_json_reader_t. Go off, reader π
π₯ BASIC CRUD OPERATIONS π₯
Okay so this section is where we show you the basics of using the C Driver to talk to MongoDB. The four letters we all know and love, let's get into it π
π Inserting a Document π
To insert documents into a collection, first you grab yourself a handle to a mongoc_collection_t via a mongoc_client_t. Then you hit mongoc_collection_insert_one() to add your BSON documents to the collection. This example slides right into the database "mydb" and the collection "mycoll".
And when you're done, bestie, please make sure you free all the allocated structures by calling their respective destroy functions. Nobody wants a memory leak, it's not the vibe π
#include <bson/bson.h>
#include <mongoc/mongoc.h>
#include <stdio.h>
int
main (int argc,
char *argv[])
{
mongoc_client_t *client;
mongoc_collection_t *collection;
bson_error_t error;
bson_oid_t oid;
bson_t *doc;
mongoc_init ();
client = mongoc_client_new ("mongodb://localhost:27017/?appname=insert-example");
collection = mongoc_client_get_collection (client, "mydb", "mycoll");
doc = bson_new ();
bson_oid_init (&oid, NULL);
BSON_APPEND_OID (doc, "_id", &oid);
BSON_APPEND_UTF8 (doc, "hello", "world");
if (!mongoc_collection_insert_one (
collection, doc, NULL, NULL, &error)) {
fprintf (stderr, "%s\n", error.message);
}
bson_destroy (doc);
mongoc_collection_destroy (collection);
mongoc_client_destroy (client);
mongoc_cleanup ();
return 0;
}
Now compile it and run it:
$ gcc -o insert insert.c $(pkg-config --cflags --libs libmongoc-1.0)
$ ./insert
On Windows:
C:\> cl.exe /IC:\mongo-c-driver\include\libbson-1.0 /IC:\mongo-c-driver\include\libmongoc-1.0 insert.c
C:\> insert
To verify that the insert actually ate, connect with the MongoDB shell:
$ mongo
MongoDB shell version: 3.0.6
connecting to: test
> use mydb
switched to db mydb
> db.mycoll.find()
{ "_id" : ObjectId("55ef43766cb5f36a3bae6ee4"), "hello" : "world" }
>
There she is π₯Ή proof of life, proof of insert.
π Finding a Document π
To query a MongoDB collection with the C driver, you use the function mongoc_collection_find_with_opts(). This hands you back a cursor to the matching documents. The following examples walk through the result cursors and print the matches to stdout as JSON strings.
You use a document as your query specifier; so like, for example,
{ "color" : "red" }
will match any document that has a field named "color" with the value "red". And an empty document {}? That's your match-everything wildcard, it catches all documents.
This first example uses an empty query specifier to find all the documents in the database "mydb" and collection "mycoll".
#include <bson/bson.h>
#include <mongoc/mongoc.h>
#include <stdio.h>
int
main (int argc, char *argv[])
{
mongoc_client_t *client;
mongoc_collection_t *collection;
mongoc_cursor_t *cursor;
const bson_t *doc;
bson_t *query;
char *str;
mongoc_init ();
client =
mongoc_client_new ("mongodb://localhost:27017/?appname=find-example");
collection = mongoc_client_get_collection (client, "mydb", "mycoll");
query = bson_new ();
cursor = mongoc_collection_find_with_opts (collection, query, NULL, NULL);
while (mongoc_cursor_next (cursor, &doc)) {
str = bson_as_canonical_extended_json (doc, NULL);
printf ("%s\n", str);
bson_free (str);
}
bson_destroy (query);
mongoc_cursor_destroy (cursor);
mongoc_collection_destroy (collection);
mongoc_client_destroy (client);
mongoc_cleanup ();
return 0;
}
Compile the code and run it:
$ gcc -o find find.c $(pkg-config --cflags --libs libmongoc-1.0)
$ ./find
{ "_id" : { "$oid" : "55ef43766cb5f36a3bae6ee4" }, "hello" : "world" }
On Windows:
C:\> cl.exe /IC:\mongo-c-driver\include\libbson-1.0 /IC:\mongo-c-driver\include\libmongoc-1.0 find.c
C:\> find
{ "_id" : { "$oid" : "55ef43766cb5f36a3bae6ee4" }, "hello" : "world" }
Now to look for a specific document, you add a specifier to query. This example adds a call to BSON_APPEND_UTF8() to look for all documents matching {"hello" : "world"}.
#include <bson/bson.h>
#include <mongoc/mongoc.h>
#include <stdio.h>
(and bestie, the rest of this one is coming right up in the next part, don't you dare scroll away π)
int
main (int argc, char *argv[])
{
mongoc_client_t *client;
mongoc_collection_t *collection;
mongoc_cursor_t *cursor;
const bson_t *doc;
bson_t *query;
char *str;
mongoc_init ();
client = mongoc_client_new (
"mongodb://localhost:27017/?appname=find-specific-example");
collection = mongoc_client_get_collection (client, "mydb", "mycoll");
query = bson_new ();
BSON_APPEND_UTF8 (query, "hello", "world");
cursor = mongoc_collection_find_with_opts (collection, query, NULL, NULL);
while (mongoc_cursor_next (cursor, &doc)) {
str = bson_as_canonical_extended_json (doc, NULL);
printf ("%s\n", str);
bson_free (str);
}
bson_destroy (query);
mongoc_cursor_destroy (cursor);
mongoc_collection_destroy (collection);
mongoc_client_destroy (client);
mongoc_cleanup ();
return 0;
}
okay compile time bestie, watch this β¨
$ gcc -o find-specific find-specific.c $(pkg-config --cflags --libs libmongoc-1.0)
$ ./find-specific
{ "_id" : { "$oid" : "55ef43766cb5f36a3bae6ee4" }, "hello" : "world" }
and on Windows, same energy, different shell π
C:\> cl.exe /IC:\mongo-c-driver\include\libbson-1.0 /IC:\mongo-c-driver\include\libmongoc-1.0 find-specific.c
C:\> find-specific
{ "_id" : { "$oid" : "55ef43766cb5f36a3bae6ee4" }, "hello" : "world" }
π¦ Updating a Document π¦
so this snippet is the glow-up arc β it shows you how to use mongoc_collection_update_one() to change up the fields of a document. The way that documents can just... become new versions of themselves? iconic behaviour honestly.
Using the "mydb" database, this example drops an example document into the "mycoll" collection. Then, using its _id field, the document gets updated with different values and a whole new field. main character development fr fr β¨
#include <bson/bson.h>
#include <mongoc/mongoc.h>
#include <stdio.h>
int
main (int argc, char *argv[])
{
mongoc_collection_t *collection;
mongoc_client_t *client;
bson_error_t error;
bson_oid_t oid;
bson_t *doc = NULL;
bson_t *update = NULL;
bson_t *query = NULL;
mongoc_init ();
client =
mongoc_client_new ("mongodb://localhost:27017/?appname=update-example");
collection = mongoc_client_get_collection (client, "mydb", "mycoll");
bson_oid_init (&oid, NULL);
doc = BCON_NEW ("_id", BCON_OID (&oid), "key", BCON_UTF8 ("old_value"));
if (!mongoc_collection_insert_one (collection, doc, NULL, &error)) {
fprintf (stderr, "%s\n", error.message);
goto fail;
}
query = BCON_NEW ("_id", BCON_OID (&oid));
update = BCON_NEW ("$set",
"{",
"key",
BCON_UTF8 ("new_value"),
"updated",
BCON_BOOL (true),
"}");
if (!mongoc_collection_update_one (
collection, query, update, NULL, NULL, &error)) {
fprintf (stderr, "%s\n", error.message);
goto fail;
}
fail:
if (doc)
bson_destroy (doc);
if (query)
bson_destroy (query);
if (update)
bson_destroy (update);
mongoc_collection_destroy (collection);
mongoc_client_destroy (client);
mongoc_cleanup ();
return 0;
}
Compile the code and run it:
$ gcc -o update update.c $(pkg-config --cflags --libs libmongoc-1.0)
$ ./update
On Windows:
C:\> cl.exe /IC:\mongo-c-driver\include\libbson-1.0 /IC:\mongo-c-driver\include\libmongoc-1.0 update.c
C:\> update
{ "_id" : { "$oid" : "55ef43766cb5f36a3bae6ee4" }, "hello" : "world" }
okay but how do we KNOW the update actually landed? vibe check time β connect with the MongoDB shell:
$ mongo
MongoDB shell version: 3.0.6
connecting to: test
> use mydb
switched to db mydb
> db.mycoll.find({"updated" : true})
{ "_id" : ObjectId("55ef549236fe322f9490e17b"), "updated" : true, "key" : "new_value" }
>
see?? "key" went from "old_value" to "new_value" and "updated" : true is just... there now. ate and left no crumbs π«Ά
π Deleting a Document π
This example shows off the use of mongoc_collection_delete_one() to delete a document. bye bye bestie π
The code below inserts a sample document into the database "mydb" and collection "mycoll". Then it deletes all documents matching {"hello" : "world"}.
#include <bson/bson.h>
#include <mongoc/mongoc.h>
#include <stdio.h>
int
main (int argc, char *argv[])
{
mongoc_client_t *client;
mongoc_collection_t *collection;
bson_error_t error;
bson_oid_t oid;
bson_t *doc;
mongoc_init ();
client =
mongoc_client_new ("mongodb://localhost:27017/?appname=delete-example");
collection = mongoc_client_get_collection (client, "test", "test");
doc = bson_new ();
bson_oid_init (&oid, NULL);
BSON_APPEND_OID (doc, "_id", &oid);
BSON_APPEND_UTF8 (doc, "hello", "world");
if (!mongoc_collection_insert_one (collection, doc, NULL, &error)) {
fprintf (stderr, "Insert failed: %s\n", error.message);
}
bson_destroy (doc);
doc = bson_new ();
BSON_APPEND_OID (doc, "_id", &oid);
if (!mongoc_collection_delete_one (
collection, doc, NULL, NULL, &error)) {
fprintf (stderr, "Delete failed: %s\n", error.message);
}
bson_destroy (doc);
mongoc_collection_destroy (collection);
mongoc_client_destroy (client);
mongoc_cleanup ();
return 0;
}
Compile the code and run it:
$ gcc -o delete delete.c $(pkg-config --cflags --libs libmongoc-1.0)
$ ./delete
On Windows:
C:\> cl.exe /IC:\mongo-c-driver\include\libbson-1.0 /IC:\mongo-c-driver\include\libmongoc-1.0 delete.c
C:\> delete
Use the MongoDB shell to prove that the documents have been removed successfully. receipts only, no cap π§Ύ
$ mongo
MongoDB shell version: 3.0.6
connecting to: test
> use mydb
switched to db mydb
> db.mycoll.count({"hello" : "world"})
0
>
0!!! it's giving ghost town, it's giving empty, it's giving the document truly said "and I oop" and vanished π
πΈ Counting Documents πΈ
Counting how many documents live in a MongoDB collection is lowkey just like doing a find operation. This example counts the number of documents matching {"hello" : "world"} in the database "mydb" and collection "mycoll". math but make it MongoDB β¨
#include <bson/bson.h>
#include <mongoc/mongoc.h>
#include <stdio.h>
int
main (int argc, char *argv[])
{
mongoc_client_t *client;
mongoc_collection_t *collection;
bson_error_t error;
bson_t *doc;
int64_t count;
mongoc_init ();
client =
mongoc_client_new ("mongodb://localhost:27017/?appname=count-example");
collection = mongoc_client_get_collection (client, "mydb", "mycoll");
doc = bson_new_from_json (
(const uint8_t *) "{\"hello\" : \"world\"}", -1, &error);
count = mongoc_collection_count (
collection, MONGOC_QUERY_NONE, doc, 0, 0, NULL, &error);
if (count < 0) {
fprintf (stderr, "%s\n", error.message);
} else {
printf ("%" PRId64 "\n", count);
}
bson_destroy (doc);
mongoc_collection_destroy (collection);
mongoc_client_destroy (client);
mongoc_cleanup ();
return 0;
}
Compile the code and run it:
$ gcc -o count count.c $(pkg-config --cflags --libs libmongoc-1.0)
$ ./count
1
On Windows:
C:\> cl.exe /IC:\mongo-c-driver\include\libbson-1.0 /IC:\mongo-c-driver\include\libmongoc-1.0 count.c
C:\> count
1
one (1) whole document. we love a single-document household π
π EXECUTING COMMANDS π
okay so the driver is out here handing you helper functions for running MongoDB commands on client, database and collection structures. the _simple variants just return booleans telling you success or failure β super low effort, super high reward.
This example runs the ping command against the database "mydb". like facetiming your database to make sure it's still alive fr fr π
#include <bson/bson.h>
#include <mongoc/mongoc.h>
#include <stdio.h>
int
main (int argc, char *argv[])
{
mongoc_client_t *client;
bson_error_t error;
bson_t *command;
bson_t reply;
char *str;
mongoc_init ();
client = mongoc_client_new (
"mongodb://localhost:27017/?appname=executing-example");
command = BCON_NEW ("ping", BCON_INT32 (1));
if (mongoc_client_command_simple (
client, "mydb", command, NULL, &reply, &error)) {
str = bson_as_canonical_extended_json (&reply, NULL);
printf ("%s\n", str);
bson_free (str);
} else {
fprintf (stderr, "Failed to run command: %s\n", error.message);
}
bson_destroy (command);
bson_destroy (&reply);
mongoc_client_destroy (client);
mongoc_cleanup ();
return 0;
}
Compile the code and run it:
$ gcc -o executing executing.c $(pkg-config --cflags --libs libmongoc-1.0)
$ ./executing
{ "ok" : { "$numberDouble" : "1.0" }, "$clusterTime" : { "clusterTime" : { "$timestamp" : { "t" : 1682609211, "i" : 1 } }, "signature" : { "hash" : { "$binary" : { "base64" : "AAAAAAAAAAAAAAAAAAAAAAAAAAA=", "subType" : "00" } }, "keyId" : { "$numberLong" : "0" } } }, "operationTime" : { "$timestamp" : { "t" : 1682609211, "i" : 1 } } }
On Windows:
C:\> cl.exe /IC:\mongo-c-driver\include\libbson-1.0 /IC:\mongo-c-driver\include\libmongoc-1.0 executing.c
C:\> executing
{ "ok" : { "$numberDouble" : "1.0" }, "$clusterTime" : { "clusterTime" : { "$timestamp" : { "t" : 1682609211, "i" : 1 } }, "signature" : { "hash" : { "$binary" : { "base64" : "AAAAAAAAAAAAAAAAAAAAAAAAAAA=", "subType" : "00" } }, "keyId" : { "$numberLong" : "0" } } }, "operationTime" : { "$timestamp" : { "t" : 1682609211, "i" : 1 } } }
"ok" : { "$numberDouble" : "1.0" } β that's the database saying "yeah bestie I'm here" and honestly? that's all we needed π
π THREADING (yes bestie, we're going there)
okay so real talk: the MongoDB C Driver is thread-unaware in the vast majority of its operations. π« That means it's literally up to you, the programmer, to guarantee thread-safety. No one's holding your hand here, babe.
BUT β and this is the iconic part β mongoc_client_pool_t is thread-safe, and you use it to fetch a mongoc_client_t in a thread-safe manner. β¨ Once you've retrieved a client from the pool, that client structure should be considered owned by the calling thread. And when the thread is done? Put the client back into the pool. Like returning your shopping cart. Be a good citizen. π
π example-pool.c
/* gcc example-pool.c -o example-pool $(pkg-config --cflags --libs
* libmongoc-1.0) */
/* ./example-pool [CONNECTION_STRING] */
#include <mongoc/mongoc.h>
#include <pthread.h>
#include <stdio.h>
static pthread_mutex_t mutex;
static bool in_shutdown = false;
static void *
worker (void *data)
{
mongoc_client_pool_t *pool = data;
mongoc_client_t *client;
bson_t ping = BSON_INITIALIZER;
bson_error_t error;
bool r;
BSON_APPEND_INT32 (&ping, "ping", 1);
while (true) {
client = mongoc_client_pool_pop (pool);
/* Do something with client. If you are writing an HTTP server, you
* probably only want to hold onto the client for the portion of the
* request performing database queries.
*/
r = mongoc_client_command_simple (
client, "admin", &ping, NULL, NULL, &error);
if (!r) {
fprintf (stderr, "%s\n", error.message);
}
mongoc_client_pool_push (pool, client);
pthread_mutex_lock (&mutex);
if (in_shutdown || !r) {
pthread_mutex_unlock (&mutex);
break;
}
pthread_mutex_unlock (&mutex);
}
bson_destroy (&ping);
return NULL;
}
int
main (int argc, char *argv[])
{
const char *uri_string = "mongodb://127.0.0.1/?appname=pool-example";
mongoc_uri_t *uri;
bson_error_t error;
mongoc_client_pool_t *pool;
pthread_t threads[10];
unsigned i;
void *ret;
pthread_mutex_init (&mutex, NULL);
mongoc_init ();
if (argc > 1) {
uri_string = argv[1];
}
uri = mongoc_uri_new_with_error (uri_string, &error);
if (!uri) {
fprintf (stderr,
"failed to parse URI: %s\n"
"error message: %s\n",
uri_string,
error.message);
return EXIT_FAILURE;
}
pool = mongoc_client_pool_new (uri);
mongoc_client_pool_set_error_api (pool, 2);
for (i = 0; i < 10; i++) {
pthread_create (&threads[i], NULL, worker, pool);
}
sleep (10);
pthread_mutex_lock (&mutex);
in_shutdown = true;
pthread_mutex_unlock (&mutex);
for (i = 0; i < 10; i++) {
pthread_join (threads[i], &ret);
}
mongoc_client_pool_destroy (pool);
mongoc_uri_destroy (uri);
mongoc_cleanup ();
return EXIT_SUCCESS;
}
π¦ NEXT STEPS
Wanna go deeper? To find information on advanced topics, browse the rest of the C driver guide or the official MongoDB documentation. π
For help with common issues, consult the Troubleshooting page. And if you wanna report a bug or request a new feature, follow those instructions. We love a feedback moment. π
π AUTHOR
MongoDB, Inc
πΈ COPYRIGHT
2017-present, MongoDB, Inc
and that's a wrap on the whole tutorial, bestie!! π You made it to the end, and honestly? main character energy. This page was served up from version 1.26.0, dated Mar 31, 2024, and yes, it's the one and only MONGOC_TUTORIAL(3). Now go write some absolutely unhinged C, and remember: pop your client, push your client, and hydrate. π«Άβ¨
π¦ where this page comes from
- ubuntu noble: libmongoc-doc 1.26.0-1.1ubuntu2 Β· Apache-2.0
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms