libmongo-client 0.1.8
Loading...
Searching...
No Matches
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);
gboolean bson_finish(bson *b)
Finish a BSON object.
Definition bson.c:521
bson * bson_build(bson_type type, const gchar *name,...)
Build a BSON object in one go.
Definition bson.c:448
@ BSON_TYPE_STRING
4byte length + NULL terminated string
Definition bson.h:67
@ BSON_TYPE_NONE
Only used for errors.
Definition bson.h:65
@ BSON_TYPE_INT32
4byte integer
Definition bson.h:84

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);
gboolean bson_append_int32(bson *b, const gchar *name, gint32 i)
Append a 32-bit integer to a BSON object.
Definition bson.c:761
void bson_free(bson *b)
Free the memory associated with a BSON object.
Definition bson.c:579
bson * bson_new_from_data(const guint8 *data, gint32 size)
Create a BSON object from existing data.
Definition bson.c:269
gint32 bson_size(const bson *b)
Return the size of a finished BSON object.
Definition bson.c:542
const guint8 * bson_data(const bson *b)
Return the raw bytestream form of the BSON object.
Definition bson.c:554
gboolean mongo_sync_cmd_insert(mongo_sync_connection *conn, const gchar *ns,...)
Send an insert command to MongoDB.
Definition mongo-sync.c:943
}

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);
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_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

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);
}
gboolean mongo_sync_cursor_next(mongo_sync_cursor *cursor)
Iterate a MongoDB cursor.
Definition mongo-sync-cursor.c:56
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
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

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);
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_cursor * bson_find(const bson *b, const gchar *name)
Create a new cursor positioned at a given key.
Definition bson.c:955

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

bson_free (b);
}
void bson_cursor_free(bson_cursor *c)
Delete a cursor, and free up all resources used by it.
Definition bson.c:800

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

printf ("\n");
}
void mongo_sync_cursor_free(mongo_sync_cursor *cursor)
Free a MongoDB cursor.
Definition mongo-sync-cursor.c:83

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;
}
mongo_sync_connection * mongo_sync_connect(const gchar *address, gint port, gboolean slaveok)
Synchronously connect to a MongoDB server.
Definition mongo-sync.c:201
do_inserts (conn);
do_query (conn);
return 0;
}
void mongo_sync_disconnect(mongo_sync_connection *conn)
Close and free a synchronous MongoDB connection.
Definition mongo-sync.c:396

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.