libmongo-client  0.1.8
A full-blown application

As the next step of our tutorial, we will write a full blown application. While it does not solve any real-life problems, and what it does is entirely pointless, it nevertheless is a good example to showcase certain patterns one is likely to run into while developing with libmongo-client.

#include <mongo.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

Our first task is to add a handful of items to our test collection. We'll have two static keys, and one that's different for each key.

static void
do_inserts (mongo_sync_connection *conn)
{
bson *base;
gint i;

First, we'll build a base BSON object:

base = bson_build
(BSON_TYPE_STRING, "tutorial-program", "tut_hl_client.c", -1,
BSON_TYPE_INT32, "the answer to life, the universe and everything", 42,
bson_finish (base);

Then, we create a copy, append a counter element to the object, insert it, and do this a thousand times over.

for (i = 0; i < 1000; i++)
{
bson *n;
n = bson_new_from_data (bson_data (base), bson_size (base) - 1);
bson_append_int32 (n, "counter", i);
if (!mongo_sync_cmd_insert (conn, "lmc.tutorial", n, NULL))
{
fprintf (stderr, "Error inserting document %d: %s\n", i,
strerror (errno));
exit (1);
}
bson_free (n);
}

This was pretty simple, wasn't it? And we even have error handling! Lets finish this function up, and move on.

bson_free (base);
}

Next up comes the interesting part: doing queries. We will use the cursor API to iterate over all our results, hiding the gory details of database access behind its convenience layer.

static void
do_query (mongo_sync_connection *conn)
{

We'll need a couple of things: a cursor, a query, and a string to store error messages in, if any.

mongo_sync_cursor *c;
bson *query;
gchar *error = NULL;

Before we can query the database, we must build a query object:

query = bson_build
(BSON_TYPE_STRING, "tutorial-program", "tut_hl_client.c", -1,
bson_finish (query);

Once that is done, we create a cursor, cleverly embedding the mongo_sync_cmd_query() call into the constructor:

c = mongo_sync_cursor_new (conn, "lmc.tutorial",
mongo_sync_cmd_query (conn, "lmc.tutorial", 0,
0, 10, query, NULL));
if (!c)
{
fprintf (stderr, "Error creating the query cursor: %s\n",
strerror (errno));
exit (1);
}
bson_free (query);

Again, we properly handle errors. It is very important to not just blindly assume things will work. While the library tries its best to handle invalid data gracefully, it's easy to get lost between the layers when one forgets to handle error cases at the appropriate level.

But I digress, lets get back to our program!

We have a nice little query cursor, it's time to loop through the database, extract the counter from the current BSON object, and move on:

{
bson_cursor *bc;
gint32 cnt;
if (!b)
{
int e = errno;
mongo_sync_cmd_get_last_error (conn, "lmc", &error);
fprintf (stderr, "Error retrieving cursor data: %s\n",
(error) ? error : strerror (e));
exit (1);
}

At this point, we have the current document in the b variable, handled the error case, and as such, we're ready to dig deep into the BSON object!

bc = bson_find (b, "counter");
printf ("\rCounter: %d", cnt);

And once we're done working with the BSON object, we free the cursor, and the object, and continue the loop.

And in the end, we emit a newline, and free the cursor to wrap up our query routine.

printf ("\n");
}

All that is left now, is the glue that holds this together, and connects to MongoDB:

int
main (void)
{
mongo_sync_connection *conn;
conn = mongo_sync_connect ("localhost", 27017, FALSE);
if (!conn)
{
fprintf (stderr, "Connection failed: %s\n", strerror (errno));
return 1;
}
do_inserts (conn);
do_query (conn);
return 0;
}

I believe that does not need any further explanation.

As an exercise, one can add another feature: dropping the temporary collection on error. Or perhaps, count the number of documents returned, and see if and how the count changes between subsequent runs of the test program.

BSON_TYPE_INT32
@ BSON_TYPE_INT32
4byte integer
Definition: bson.h:84
bson_cursor_free
void bson_cursor_free(bson_cursor *c)
Delete a cursor, and free up all resources used by it.
Definition: bson.c:800
mongo_sync_cursor_free
void mongo_sync_cursor_free(mongo_sync_cursor *cursor)
Free a MongoDB cursor.
Definition: mongo-sync-cursor.c:83
BSON_TYPE_NONE
@ BSON_TYPE_NONE
Only used for errors.
Definition: bson.h:65
mongo_sync_cursor_next
gboolean mongo_sync_cursor_next(mongo_sync_cursor *cursor)
Iterate a MongoDB cursor.
Definition: mongo-sync-cursor.c:56
mongo_sync_cursor_new
mongo_sync_cursor * mongo_sync_cursor_new(mongo_sync_connection *conn, const gchar *ns, mongo_packet *packet)
Create a new MongoDB Cursor.
Definition: mongo-sync-cursor.c:28
mongo_sync_cursor_get_data
bson * mongo_sync_cursor_get_data(mongo_sync_cursor *cursor)
Retrieve the BSON document at the cursor's position.
Definition: mongo-sync-cursor.c:99
BSON_TYPE_STRING
@ BSON_TYPE_STRING
4byte length + NULL terminated string
Definition: bson.h:67
mongo_sync_cmd_insert
gboolean mongo_sync_cmd_insert(mongo_sync_connection *conn, const gchar *ns,...)
Send an insert command to MongoDB.
Definition: mongo-sync.c:943
bson_data
const guint8 * bson_data(const bson *b)
Return the raw bytestream form of the BSON object.
Definition: bson.c:554
bson_build
bson * bson_build(bson_type type, const gchar *name,...)
Build a BSON object in one go.
Definition: bson.c:448
mongo_sync_connect
mongo_sync_connection * mongo_sync_connect(const gchar *address, gint port, gboolean slaveok)
Synchronously connect to a MongoDB server.
Definition: mongo-sync.c:201
mongo_sync_cmd_get_last_error
gboolean mongo_sync_cmd_get_last_error(mongo_sync_connection *conn, const gchar *db, gchar **error)
Get the last error from MongoDB.
Definition: mongo-sync.c:1399
mongo_sync_cmd_query
mongo_packet * mongo_sync_cmd_query(mongo_sync_connection *conn, const gchar *ns, gint32 flags, gint32 skip, gint32 ret, const bson *query, const bson *sel)
Send a query command to MongoDB.
Definition: mongo-sync.c:986
bson_finish
gboolean bson_finish(bson *b)
Finish a BSON object.
Definition: bson.c:521
bson_free
void bson_free(bson *b)
Free the memory associated with a BSON object.
Definition: bson.c:579
bson_new_from_data
bson * bson_new_from_data(const guint8 *data, gint32 size)
Create a BSON object from existing data.
Definition: bson.c:269
bson_find
bson_cursor * bson_find(const bson *b, const gchar *name)
Create a new cursor positioned at a given key.
Definition: bson.c:955
bson_cursor_get_int32
gboolean bson_cursor_get_int32(const bson_cursor *c, gint32 *dest)
Get the value stored at the cursor, as a 32-bit integer.
Definition: bson.c:1212
bson_size
gint32 bson_size(const bson *b)
Return the size of a finished BSON object.
Definition: bson.c:542
mongo_sync_disconnect
void mongo_sync_disconnect(mongo_sync_connection *conn)
Close and free a synchronous MongoDB connection.
Definition: mongo-sync.c:396
bson_append_int32
gboolean bson_append_int32(bson *b, const gchar *name, gint32 i)
Append a 32-bit integer to a BSON object.
Definition: bson.c:761