mongodb c driver bson的巢狀訪問與層次結構

秋來葉黃發表於2023-11-27

使用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,再遞迴一層,才能拿到對應的資料。

https://www.mongodb.com/docs/drivers/c/
http://mongoc.org/libbson/current/index.html
http://mongoc.org/

相關文章