使用c访问mongodb,需要用到mongodb c driver。c++的driver也是基于c driver封装的。

在使用c driver访问mongodb时,需要与bson打交道,不过c driver访问bson有几点需要注意的,不然会导致报错,或者找不到数据。

迭代器使用后的有效性

在mongodb c driver中,使用bson_iter_find等操作迭代器后,不要再次使用,有几点原因:

  • 如果api报错,比如没找到key,迭代器就会失效,如果继续用,会导致程序崩溃
  • 如果api没报错,找到了key,那么下一次继续使用,查找的value,必须是在当前找到的value后面,如果在迭代器前面,就无法找到。

所以一般操作是在使用迭代器之前,赋值一个局部变量,对这个局部变量进行操作。

官方有如下解释:

The bson_iter_find() function shall advance iter to the first element named key or exhaust all elements of iter. If iter is exhausted, false is returned and iter should be considered invalid.

示例

const bson_t *doc = nullptr;
bson_iter_t iter;
while (mongoc_cursor_next(cursor, &doc))
{
    // 从doc初始化获得iter
    if (!bson_iter_init(&iter, doc))
    {
        continue;
    }
    // 创建一个临时变量的迭代器,用作后续操作
    bson_iter_t proiter;
    
    // 把iter赋值给proiter,避免iter失效
    proiter = iter;
    // 使用proiter进行查找
    if (bson_iter_find(&proiter, "key1"))
    {
        ...
    }

    // 再次把iter赋值给proiter,进行后续操作
    proiter = iter;
    if (bson_iter_find(&proiter, "key2"))
    {
        ...
    }
    ...
}

子对象与数组遍历的层次结构

使用mongodb c driver访问数组结构或者子对象时,也需要注意,其中多了一层,如果少进入一层,就拿不到数据。

数组是结构体

{
    "key1": "111",
    "key2":
    [
        {
            "ip": "127.0.0.1",
            "protocal": "tcp"
        },
        {
            "ip": "127.0.0.2",
            "protocal": "udp"
        }
    ]
}

如上,有一个json数据,key1是一个字符串,key2是一个结构体数组,当我们遍历key2时,其每个元素也是key-value结构,key是数组索引0、1、2...,value才是数组元素。所以进入key2之后,还要再进入一层,才能拿到ip和protocal数据。

示例

// 找到key2,然后递归一层,下一层数据的迭代器赋值给sub_iter
if (bson_iter_find(&proiter, "key2") && bson_iter_recurse(&proiter, &sub_iter))
{
    // 遍历sub_iter的所有元素,这里就是上面说的,key是数组的索引,value是ip和protocal结构体
    while (bson_iter_next(&sub_iter))
    {
        bson_iter_t subsubiter;
        // 赋值给pproiter局部变量,避免sub_iter失效
        bson_iter_t pproiter = sub_iter;
        // 再递归一层,才能拿到ip和protocal
        if (bson_iter_recurse(&pproiter, &subsubiter))
        {
            bson_iter_t ppproiter = subsubiter;
            // 找到ip
            if (bson_iter_find(&ppproiter, "ip"))
            {
            }
        }
    }
}

数组是value(字符串)

"key1":
{
    "ip":
    [
        "192.168.1.10",
        "192.168.1.12",
        "192.168.1.13",
        "192.168.1.17"
    ]
}

如果数组是字符串呢?实际上是少了一层,但是也不要忘记,找到数组的迭代器,还是需要递归一层才可以拿到数据。

示例

// 找到key1
if (bson_iter_find(&iter, "key1"))
{
    // 递归一层,获得key1的下一层ip
    bson_iter_recurse(&iter, &subiter);
    // 找到ip
    if (bson_iter_find(&subiter, "ip"))
    {
        bson_iter_t piter;
        // 递归一层,找到ip下一层的数据
        bson_iter_recurse(&subiter, &piter);
        // 遍历ip数组内的元素
        while(bson_iter_next(&piter))
        {
            const char *name = bson_iter_utf8 (&piter, NULL);
            printf("%s\n", name);
        }
    }
}

从上面可以看出,mongodb c driver的下一层级的数据遍历,都是先找到对应的key,再递归一层,才能拿到对应的数据。