ROS交流群
ROS Group
产品服务
Product Service
开源代码库
Github
官网
Official website
技术交流
Technological exchanges
激光雷达
LIDAR
ROS教程
ROS Tourials
深度学习
Deep Learning
机器视觉
Computer Vision

mongodb c++ 驱动的使用



  • Mongodb也算是一个比较成熟的数据库了,以前用python和js很多时候都是用它作为数据库的。因为不用写schema,用起来很方便。但是没想到在c++里使用mongodb 却是一个大坑。主要是由于现在mongodb的c++驱动刚刚用C11的标准全部重写了一遍。不仅官方文档少得可怜,代码的稳定性也有问题。可能之后会好很多吧。

    首先是安装
    安装方面我就不多说了,官方的文档写得很详细。

    接着就是使用了
    数据库的基本操作,增删查改。但是在开始增删查改之前先要了解一下bson。这个是mongodb的数据表现形式。在存入数据库之前,都要先把数据变成这种格式。实际上和json很类似。
    创建一个bson对象

    auto doc = builder::basic::document{};
    

    之后就可以添加数据了

    doc.append(kvp(“name”, “John”));
    doc.append(kvp(“age”, “12”));
    

    kvp是key value pair的缩写。对于string,bool这样的类型可以直接写进去。但是有些变量必须指定数据类型才能添加, 比如

    doc.append(kvp(“size”, types::b_double{3.134}));
    

    具体有哪些可以使用的类型,可以直接看源代码。注意上面b_double{}这里是大括号。这是初始化一个结构体。并不是调用函数。所有的types里面的类型都是结构体。
    bson也可以添加比较复杂的数据类型。比如这样的一个json

    {
       “name”: “John”,
       “age”: 12,
       “friends”: [
           {
             “name”: “Jim”,
             “age”: 12,
           },
           {
             “name”: “Bob”,
             “age”: 12,
           }
        ]
    }
    

    这里面有嵌套的字典和列表。这时候就要用到sub_document 和sub_array了

    auto doc = builder::basic::document{};
    doc.append(kvp(“name”, “John”));
    doc.append(kvp(“age”, types::b_int32{12}));
    doc.append(kvp(“friends”, [&](sub_array array){
       array.append([&](sub_document sub_doc){
           sub_doc.append(kvp(“name”, “Jim”));
           sub_doc.append(kvp(“age”, types::b_int32{12}));
       }); 
       Array.append([&](sub_document sub_doc){
           sub_doc.append(kvp(“name”, “Bob”));
           sub_doc.append(kvp(“age”, types::b_int32{12}))
       });
    }));
    

    sub_document 和sub_array 是bson的内部数据类型。也就是不能从外面自己实例化一个然后去用。只能通过lamda函数去接收它传来的数据,然后更改其内容。
    数据的创建大致上就是如此。然后就是数据的访问了。给你一个bson对象如何访问各个属性呢?
    以上面创建的doc为例。

    std::string name =  stdx::string_view(doc.view()[“name”].get_utf8()).to_string();
    

    不知道官方是怎么想的,读取一个值要这么麻烦。而且还没文档。我是看源代码,一点一点找出到底怎么读出来的。
    如果访问不存在的属性会返回NULL

    auto data = doc["phone"];
    if(data){
       // 数据存在时执行此处
    }{
       // 数据不存在时执行此处
    }
    

    这个地方官方的例子写错了。正确方式如上。同样访问不存在的数据变量时也是这样。获取数据的时候要根据自己存入时的数据类型调对应的的get_<type>取出。具体的可以看源代码
    数据OK,下一步就可以进行具体的数据库操作了。这个起来还是比较容易的。
    插入

    mongocxx::instance inst{};
    mongocxx::client conn{mongocxx::uri{}};
    auto collection = conn["test"]["test"];
    collection.insert_one(doc.view());
    

    其余的操作也差不多。但是值得注意的是如果连续快速插入数据就程序就会报出插入失败的错误。
    比如

    While(true){
       collection.insert_one(doc.view());
    }
    

    经过测试两次插入之间必须要延时3ms左右才能保证稳定执行。即使这时采用bulk write的方式写入还是会有错误。可能是由于新版驱动还不稳定所以才引入的bug。

    程序写完之后,编译又是一个问题。官方的文档只字未提cmake应该怎么引入mongodb的包。导致我刚开始还以为要自己去写findmongodb.cmake文件。实际上mongodb是自带cmake config文件的。如果采用默认安装的话,文件的位置应该和下图一样。

    alt text

    在cmake里面要引入libbsoncxx和libmongocxx两个库。下面是一个具体的例子

    find_package(libmongocxx REQUIRED)
    find_package(libbsoncxx REQUIRED)
    
    include_directories(
    ${PROJECT_SOURCE_DIR}
    ${PROJECT_SOURCE_DIR}/include
    ${LIBMONGOCXX_INCLUDE_DIRS}
    ${LIBBSONCXX_INCLUDE_DIRS}
    )
    
    target_link_libraries(${PROJECT_NAME}
    ${LIBMONGOCXX_LIBRARY_DIRS}/libmongocxx.so
    ${LIBBSONCXX_LIBRARY_DIRS}/libbsoncxx.so
    )
    

    这样就可以编译了。