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:
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;
{
fprintf (stderr, "Error inserting document %d: %s\n", i,
strerror (errno));
exit (1);
}
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.
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:
Once that is done, we create a cursor, cleverly embedding the mongo_sync_cmd_query() call into the constructor:
0, 10, query, NULL));
if (!c)
{
fprintf (stderr, "Error creating the query cursor: %s\n",
strerror (errno));
exit (1);
}
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;
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!
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:1213
bson_cursor * bson_find(const bson *b, const gchar *name)
Create a new cursor positioned at a given key.
Definition: bson.c:956
And once we're done working with the BSON object, we free the cursor, and the object, and continue the loop.
}
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;
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.