Loading...
Searching...
No Matches
Traversing BSON objects

Now that we can build BSON objects (see the previous section), it is time that we learn how to find things in them!

We will use the same JSON structure as in the previous example.

First, we include our stuff, and create a function that gives us a BSON object to work with:

#include <mongo.h>
#include <string.h>
#include <stdio.h>
bson *
tut_bson (void)
{
bson *b;
b = bson_build_full
(BSON_TYPE_STRING, "author", FALSE, "Gergely Nagy", -1,
BSON_TYPE_ARRAY, "pages", TRUE,
bson_build_full (BSON_TYPE_DOCUMENT, "1", TRUE,
bson_build (BSON_TYPE_STRING, "title", "BSON tutorial", -1,
BSON_TYPE_STRING, "content", "...", -1,
BSON_TYPE_INT32, "importance", 1,
BSON_TYPE_NONE),
BSON_TYPE_DOCUMENT, "2", TRUE,
bson_build (BSON_TYPE_STRING, "title", "Some other thing", -1,
BSON_TYPE_STRING, "content", "...", -1,
BSON_TYPE_INT32, "importance", 0,
BSON_TYPE_NONE),
BSON_TYPE_NONE),
BSON_TYPE_BOOLEAN, "inline", FALSE, TRUE,
BSON_TYPE_NONE);
bson_finish (b);
return b;
bson_finish
gboolean bson_finish(bson *b)
Finish a BSON object.
Definition bson.c:521
bson_build
bson * bson_build(bson_type type, const gchar *name,...)
Build a BSON object in one go.
Definition bson.c:448
bson_build_full
bson * bson_build_full(bson_type type, const gchar *name, gboolean free_after,...)
Build a BSON object in one go, with full control.
Definition bson.c:484
BSON_TYPE_DOCUMENT
@ BSON_TYPE_DOCUMENT
4byte length + NULL terminated document
Definition bson.h:68
BSON_TYPE_ARRAY
@ BSON_TYPE_ARRAY
4byte length + NULL terminated document
Definition bson.h:69
BSON_TYPE_STRING
@ BSON_TYPE_STRING
4byte length + NULL terminated string
Definition bson.h:67
BSON_TYPE_NONE
@ BSON_TYPE_NONE
Only used for errors.
Definition bson.h:65
BSON_TYPE_INT32
@ BSON_TYPE_INT32
4byte integer
Definition bson.h:84
BSON_TYPE_BOOLEAN
@ BSON_TYPE_BOOLEAN
1byte boolean value
Definition bson.h:73
}
int
main (void)
{

First, we need a variable to hold our BSON object, and a cursor, with which we can find the keys we're looking for.

bson *doc;
bson_cursor *c, *c_arr, *c_page;

Then we need a few variables to store the retrieved information in. Extracting data out of a BSON object involves storing them in a variable of our own.

bson *v_doc, *v_array;
gboolean v_bool;
const gchar *v_str;
doc = tut_bson ();

In order to find something in a BSON object, we must know the keys name. There's two ways to find information within a BSON object: bson_find() and bson_cursor_next() and its friends.

The first takes a BSON object, and returns a cursor that points to the key we wanted to find (or to NULL, if it was not found, of course). We can then extract that value, and either close the cursor, or use bson_cursor_next() to iterate over the rest of the keys.

The advantage of bson_find() is that we can jump to a specific key easily, but at the cost of having to use a new cursor every time, which in turn will start searching from the beginning of a BSON document.

On the other hand, bson_cursor_next() remembers its position, and jumping to the next key is straightforward. It can also be used in conjunction with bson_find() to locate the first key we're interested in, and move on to the next until we're done.

For our first traversal example, lets say we want to see who the author of our BSON object is! We can extract that information by locating the "author" key, and retrieving the string contents:

c = bson_find (doc, "author");
bson_cursor_get_string (c, &v_str);
printf ("Author: %s\n", v_str);
bson_cursor_get_string
gboolean bson_cursor_get_string(const bson_cursor *c, const gchar **dest)
Get the value stored at the cursor, as string.
Definition bson.c:1008
bson_find
bson_cursor * bson_find(const bson *b, const gchar *name)
Create a new cursor positioned at a given key.
Definition bson.c:956

Now, if we also want to know whether the 'inline' key is set to true or false, we have two options: if we know the exact order of keys, we can use the appropriate amount of bson_cursor_next() calls:

bson_cursor_next (c);
bson_cursor_next (c);
bson_cursor_get_boolean (c, &v_bool);
printf ("inline: %s\n", (v_bool) ? "TRUE" : "FALSE");
bson_cursor_get_boolean
gboolean bson_cursor_get_boolean(const bson_cursor *c, gboolean *dest)
Get the value stored at the cursor, as a boolean.
Definition bson.c:1114
bson_cursor_next
gboolean bson_cursor_next(bson_cursor *c)
Position the cursor to the next key.
Definition bson.c:863

But this is not only ugly, it's very sensitive to the order of the keys, and it's hard to understand aswell, without being familiar with the document's structure.

So, while it is a little more inconvenient, it's better to find the key in some other way. But first, let's close our cursor, as we'll need to start again anyway...

bson_cursor_free (c);
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

First, we'll go the easy route, and use bson_find():

c = bson_find (doc, "author");
bson_cursor_get_string (c, &v_str);
bson_cursor_free (c);
c = bson_find (doc, "inline");
bson_cursor_get_boolean (c, &v_bool);
bson_cursor_free (c);
printf ("Author: %s; inline: %s; (bson_find)\n",

Now, the downside of this, is that we ran through the BSON object twice. Well, once and a little, since the "author" key was the first. But using bson_find() this way has the potential of traversing through a document multiple times.

Now, if we know the one key always comes after another, then we can use bson_cursor_next() to help us find it, without having to restart from the beginning. We do this by extracting the key name from the cursor, and comparing it to whatever we're searching for:

v_str, (v_bool) ? "TRUE" : "FALSE");
c = bson_find (doc, "author");
bson_cursor_get_string (c, &v_str);
while (bson_cursor_next (c))
{
if (strcmp (bson_cursor_key (c), "inline") == 0)
{
bson_cursor_get_boolean (c, &v_bool);
break;
}
}
bson_cursor_free (c);
printf ("Author: %s; inline: %s; (bson_cursor_next)\n",
bson_cursor_key
const gchar * bson_cursor_key(const bson_cursor *c)
Determine the name of the current elements key.
Definition bson.c:990

Now the above is still picky about key order, but it's at least more flexible: we can put any number of keys between author and inline, and it will work. In most cases, that's good enough. In every other case, where we have no idea about key ordering, bson_find() is still there for us.

Even better, the library itself provides a function that does something similar: it takes a cursor and a key name, and attempts to find the key past the cursor's current position. So if we already have the author, we can use this function to find the next inline key aswell:

v_str, (v_bool) ? "TRUE" : "FALSE");
c = bson_find (doc, "author");
bson_cursor_get_string (c, &v_str);
bson_cursor_find_next (c, "inline");
bson_cursor_get_boolean (c, &v_bool);
bson_cursor_free (c);
printf ("Author: %s; inline: %s; (bson_cursor_find_next)\n",
bson_cursor_find_next
gboolean bson_cursor_find_next(bson_cursor *c, const gchar *name)
Move the cursor to a given key, past the current one.
Definition bson.c:946

However, if we suspect that a key might come after the current position, but we're not sure, and still want to find the key with as little code as possible, we shall not despair! The bson_cursor_find() function does just that: it will try to find the key starting from the current position, and wrap over once if it can't.

Lets see how it works! First, we find a key in the middle:

v_str, (v_bool) ? "TRUE" : "FALSE");
c = bson_find (doc, "pages");

Now that we have a key in the middle, lets find a key after it:

bson_cursor_find (c, "inline");
bson_cursor_get_boolean (c, &v_bool);
bson_cursor_find
gboolean bson_cursor_find(bson_cursor *c, const gchar *name)
Move the cursor to a given key.
Definition bson.c:936

And once we have that - and we're at the end of our BSON object -, lets try finding the author key aswell:

bson_cursor_find (c, "author");
bson_cursor_get_string (c, &v_str);

That works, just like that! To verify, we go ahead and print the results, which should be the same as it was in the previous examples:

bson_cursor_free (c);
printf ("Author: %s; inline: %s; (bson_cursor_find)\n",

One thing we did not explore yet, is bson_cursor_new(): this will create a new cursor, and point to to the very beginning of our BSON object, just before the first key. Thus, the first bson_cursor_next() done afterwards will yield the first key.

v_str, (v_bool) ? "TRUE" : "FALSE");
c = bson_cursor_new (doc);
while (bson_cursor_next (c))
{
printf ("Key: %s; type=%s\n", bson_cursor_key (c),
bson_cursor_type_as_string (c));
}
bson_cursor_free (c);
bson_cursor_new
bson_cursor * bson_cursor_new(const bson *b)
Create a new cursor.
Definition bson.c:786
bson_cursor_type_as_string
const gchar * bson_cursor_type_as_string(const bson_cursor *c)
Retrieve the type of the current element, as string.
Definition bson.c:981

Very well! But what if we want to see the title of the second page within the pages array? Unfortunately, that is a bit more work to accomplish: we'll need to extract the pages array from our document, and then extract its second element, and then we can find stuff in that:

c = bson_find (doc, "pages");
bson_cursor_get_array (c, &v_array);
c_arr = bson_find (v_array, "2");
bson_cursor_get_document (c_arr, &v_doc);
c_page = bson_find (v_doc, "title");
bson_cursor_get_string (c_page, &v_str);
bson_cursor_free (c_page);
bson_cursor_free (c_arr);
bson_cursor_free (c);
printf ("Title of the 2nd page in the pages array: %s\n", v_str);
bson_cursor_get_document
gboolean bson_cursor_get_document(const bson_cursor *c, bson **dest)
Get the value stored at the cursor, as a BSON document.
Definition bson.c:1035
bson_cursor_get_array
gboolean bson_cursor_get_array(const bson_cursor *c, bson **dest)
Get the value stored at the cursor, as a BSON array.
Definition bson.c:1059

And that concludes our BSON traversing tutorial!

bson_free (doc);
return 0;
}
bson_free
void bson_free(bson *b)
Free the memory associated with a BSON object.
Definition bson.c:579