嗯……环境都准备好了,那么现在可以开始用我们敬爱的C++通过接口来间接操控MongoDB数据库。
注意,要有一定的MongoDB数据库基础知识哦~
启动MongoDB服务器
首先要做的,就是启动服务器
mongod --config ${MONGODB}/conf.d/mongo.conf
要停止服务器,不建议直接kill 掉,而是加上一个 –shutdown 参数
我们还要创建一个可读写指定数据库的用户,比如
use admin
db.createUser({user:’user_test’,pwd:’12345’,roles:[{role:’readWrite’,db:’db_test’}]}
这里创建了用户 user_test,密码 12345,只能读写数据库 db_test
mongoc
我们只需引用头文件 libmongoc-1.0/mongoc.h
在 main 函数要调用 mongoc_init() 来初始化库以及结尾 mongoc_cleanup() 来释放
要连接到MongoDB数据库,就得提供一个连接字符串,类似于
mongodb://username:password@host:port/db
例如: mongodb://localhost:27017表示连接到本地回环地址127.0.0.1,以及端口27017的服务器,这个连接方式无需验证身份(没有开启 auth )。
然而,这个例子的连接字符串为: mongodb://user_test:[email protected] :27017/db_test
有了连接字符串,那么我们怎么连接到服务器?可以通过 mongoc_client_get_database() 来建立一个client
mongoc_client_t * mongoc_client_new (const char *uri_string) ;
该函数返回一个 mongoc_client_t* 结构指针,之后的操作就是通过该指针来访问 database、collection……
除了直接通过一个连接字符串来建立client,还可以‘构造’一个 uri 建立client。这一步也很简单,可以调用 mongoc_uri* 系列函数,再
mongoc_client_new_from_uri(),如:
mongoc_uri_t uri=mongoc_uri_new(“mongodb://127.0.0.1:27017”);
mongoc_uri_set_database(uri,”xxxx”);
mongoc_uri_set_username(uri,”xxxx”);
mongoc_uri_set_password(uri,”xxxx”);
mongoc_client_t client= mongoc_client_new_from_uri(uri);
连接成功后,可以通过来获取 mongoc_client_get_default_database 来获取默认的数据库(前提是刚才连接字符串提供了一个数据库),或者 mongoc_client_get_database 来获取指定名称的数据库
mongoc_database_t * mongoc_client_get_database (mongoc_client_t *client, const char *name) ;mongoc_database_t * mongoc_client_get_default_database (mongoc_client_t *client) ;
通过 mongoc_database_has_collection() 判断是否存在一个 collection
bool mongoc_database_has_collection (mongoc_database_t *database,const char *name,bson_error_t *error) ;typedef struct _bson_error_t { uint32_t domain; uint32_t code; char message[BSON_ERROR_BUFFER_SIZE]; } bson_error_t ;
有了database,那么就可以获取某个collection,方法有两个
mongoc_collection_t * mongoc_client_get_collection (mongoc_client_t *client,const char *db,const char *collection) ;mongoc_collection_t *mongoc_database_get_collection (mongoc_database_t *database, const char *name) ;
接下来,就是重头戏了
Creating BSON Documents
BSON,类似与JSON,但又有点不同。通过创建BSON Document来实现与MongoDB的交互。如:
> db.test.insert({'name':'xiaoming','age':10 ,info:['shy','helpful']}) > db.test.find().pretty() { "_id" : ObjectId("5a8e1dced077dc147db71f81" ), "name" : "xiaoming" , "age" : 10 , "info" : [ "shy" , "helpful" ] }
BSON文档就有点类似与 {‘name’:’xiaoming’,’age’:10,info:[‘shy’,’helpful’]}
mongoc有几种方式创建BSON文档: appending key-value pairs, using BCON, or parsing JSON
其中,最简单的就是BCON,不过再次之前先了解下使用 bson**,只需引用 bson.h 即可
bson
bson_t *doc=bson_new ();bson_append_utf8 (doc,"name" ,-1 ,"xiaodong" ,-1 );bson_append_int32 (doc,"age" ,-1 ,10 );BSON_APPEND_UTF8 (doc,"name" ,"xiaoqiang" );BSON_APPEND_INT32 (doc,"age" ,11 );char *jsonstr= bson_as_canonical_extended_json (doc,NULL );printf (jsonstr)bson_free (jsonstr);bson_destroy (doc);
结果
{ “name” : “xiaodong”, “age” : { “numberInt” : “10” }, “name” : “xiaoqiang”, “age” : { “ numberInt” : “11” } }
可通过 bson** 系列函数 或者 BSON**宏 来添加BSON文档
通过 bson_t 获取 JSON 字符串,可以调用以下函数
char * bson_as_canonical_extended_json (const bson_t *bson, size_t *length) char * bson_as_json (const bson_t *bson, size_t *length) char * bson_as_relaxed_extended_json (const bson_t *bson, size_t *length)
必须要有对应的 bson_free() 来释放该分配的内存;bson_destroy() 来释放一个BSON文档
BCON
用 BCON_* 宏来操控BSON Document我认为更简单些,而且写法也更高级。
BSON C Object Notation, BCON for short, is an alternative way of constructing BSON documents in a manner closer to the intended format. It has less type-safety than BSON’s append functions but results in less code
bson_t *bson=BCON_NEW ( "name" ,BCON_UTF8 ("xiaoHong" ), "age" ,BCON_INT32 (15 ), "info" , "[" , "{" ,"country" ,BCON_UTF8 ("China" ), "}" , "{" ,"phone" ,BCON_UTF8 ("110" ), "}" , "]" );
{ “name” : “xiaoHong”, “age” : { “$numberInt” : “15” }, “info” : [ { “country” : “China” }, { “phone” : “110” } ] }
通过使用 BCON_* 函数更直观、方便简单的创建BSON文档。
Creating BSON from JSON
上面介绍了创建一个BSON文档以及从BSON文档获取一个JSON字符串。
那么,我们是否可以通过JSON字符串反向解析一个BSON文档呢?答案是肯定的。不过,这好像是对于单文档而言,
bson_t * bson_new_from_json (const uint8_t *data, ssize_t len, bson_error_t *error) ;
const char *json = "{\"name\":\"xiaoJun\",\"age\":15}" ;bson_t *newbs= bson_new_from_json ((const uint8_t *)json,-1 ,NULL ); jsonstr=bson_as_canonical_extended_json (newbs,NULL );printf (json);bson_free (jsonstr);bson_destroy (newbs);
有了这些基础后,那么接下来就不会那么吃力了
遍历 collections
在MongoDB交互shell中,我们可以直接通过 db.test.find().pretty()
来列出所有的数据。注意 find() 是可以提供参数的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 > db.test.find function (query, fields, limit, skip, batchSize, options) { var cursor = new DBQuery(this._mongo, this._db, this, this._fullName, this._massageObject(query), fields, limit, skip, batchSize, options || this.getQueryOptions()); { const session = this.getDB().getSession(); const readPreference = session._serverSession.client.getReadPreference(session); if (readPreference !== null) { cursor.readPref(readPreference.mode, readPreference.tags); } const readConcern = session._serverSession.client.getReadConcern(session); if (readConcern !== null) { cursor.readConcern(readConcern.level); } } return cursor; }
mongoc提供了 mongoc_collection_find_with_opts 函数来获取一个 游标(Cursor ) ,注意 mongoc_collection_find 被弃用了。
mongoc_cursor_t * mongoc_collection_find_with_opts (mongoc_collection_t *collection,const bson_t *filter,const bson_t *opts,const mongoc_read_prefs_t *read_prefs)
mongoc_collection_find_with_opts 要求4个参数,参数我们只关注前两个参数,其中 bson_t *filter 至关重要,类似于 MYSQL select from tb_xxx *where id = 5
bson_t *bson=BCON_NEW (NULL );mongoc_cursor_t *cursor= mongoc_collection_find_with_opts (coll,bson,NULL ,NULL );const bson_t *ps=mongoc_cursor_current (cursor);while (mongoc_cursor_next (cursor,&ps)) { char *json=bson_as_canonical_extended_json (ps,&size); printf (json); bson_free (json); }mongoc_cursor_destroy (cursor);
bson_t bson=BCON_NEW(NULL); 等价于 db.test.find({}) 或者 db.test.find()当然,也可以这么来写bson_t bson=BCON_NEW(“age”,”{“,”gt”,BCON_INT32(13),”}”);
这就类似于 db.test.find({‘age’:{‘ gt’:13}})
增删查改
像大多数数据库一样,增删查改是必不可少的基本步骤。
insert
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 MONGOC_EXPORT (bool )mongoc_collection_insert (mongoc_collection_t *collection, mongoc_insert_flags_t flags, const bson_t *document, const mongoc_write_concern_t *write_concern, bson_error_t *error);MONGOC_EXPORT (bool )mongoc_collection_insert_one (mongoc_collection_t *collection, const bson_t *document, const bson_t *opts, bson_t *reply, bson_error_t *error);MONGOC_EXPORT (bool )mongoc_collection_insert_many (mongoc_collection_t *collection, const bson_t **documents, size_t n_documents, const bson_t *opts, bson_t *reply, bson_error_t *error);
千万别被这么多参数给吓倒了。这里我们只需关注
mongoc_collection_t *collectionconst bson_t *document
bson_error_t *error(可选,表示错误信息)
下面是一个简单的例子,省略了大部分代码
bson_t *bson=BCON_NEW ( "name" ,BCON_UTF8 ("xiaoDong" ), "age" ,BCON_INT32 (18 ), "info" , "[" , "{" ,"country" ,BCON_UTF8 ("China" ), "}" , "{" ,"phone" ,BCON_UTF8 ("150" ), "}" , "]" ); retr=mongoc_collection_insert_one (coll,,NULL ,NULL ,&error);if (!retr){ printf (error.message); }bson_destroy (bson);
delete
MONGOC_EXPORT (bool )mongoc_collection_delete_one (mongoc_collection_t *collection, const bson_t *selector, const bson_t *opts, bson_t *reply, bson_error_t *error);MONGOC_EXPORT (bool )mongoc_collection_delete_many (mongoc_collection_t *collection, const bson_t *selector, const bson_t *opts, bson_t *reply, bson_error_t *error);
mongoc_collection_delete 已被弃用,这里没有列出
这个类似Insert,例子就没有了哈 😃
find
这个前面就是前面的 遍历collections ……
update
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 MONGOC_EXPORT (bool )mongoc_collection_update (mongoc_collection_t *collection, mongoc_update_flags_t flags, const bson_t *selector, const bson_t *update, const mongoc_write_concern_t *write_concern, bson_error_t *error);MONGOC_EXPORT (bool )mongoc_collection_update_one (mongoc_collection_t *collection, const bson_t *selector, const bson_t *update, const bson_t *opts, bson_t *reply, bson_error_t *error);MONGOC_EXPORT (bool )mongoc_collection_update_many (mongoc_collection_t *collection, const bson_t *selector, const bson_t *update, const bson_t *opts, bson_t *reply, bson_error_t *error);
注意那个 mongoc_update_flags_t ,这个经常用到。
typedef enum { MONGOC_UPDATE_NONE = 0 , MONGOC_UPDATE_UPSERT = 1 << 0 , MONGOC_UPDATE_MULTI_UPDATE = 1 << 1 , } mongoc_update_flags_t ;
现在就 mongoc_collection_update 这个函数为例,除了那个 const mongoc_write_concern_t *write_concern 参数以外,其他的似乎都挺常用。。。不过再次之前,先来看看Mongo下update数据。
当前 db_test 存在如下数据
db.test .find () { "_id" : ObjectId ("5a8e30c0d077dc147db71f83" ), "name" : "xiaolong" , "age" : 14 } { "_id" : ObjectId ("5a8e3102d077dc147db71f84" ), "name" : "xiaoqiang" , "age" : 10 } { "_id" : ObjectId ("5a8e3108d077dc147db71f85" ), "name" : "xiaofeng" , "age" : 12 } { "_id" : ObjectId ("5a8e38ba53d6700f027556c2" ), "name" : "xiaoHong" , "age" : 15 , "info" : [ { "country" : "China" }, { "phone" : "110"
我要查看 age 大或等于 12 的记录,那么我可以这样做
db.test .find ({'age' :{'$gte' :12 }}) >{ "_id" : ObjectId("5a8e30c0d077dc147db71f83" ), "name" : "xiaolong" , "age" : 14 }"xiaofeng" , "age" : 12 } { "_id" : ObjectId("5a8e38ba53d6700f027556c2" ), "name" : "xiaoHong" , "age" : 15 , "info" : [ { "country" : "China" }, { "phone" : "110" } ] }
要修改 age 大或等于 12 的 name 的所有 记录,那么我么可以这样做
db.test.update({‘age’:{‘gte’:12}},{‘ set’:{‘name’:’XXXXX’}},false,true )
类似与MySQL:update tb_xxx set name=’xxxxx’ where age >=12
这时候回过头来看看
const bson_t *selector => {'age' :{'$gte ' :12 } }const bson_t *update => {'$set ' :{'name' :'XXXXX' }} mongoc_update_flags_t flags => MONGOC_UPDATE_MULTI_UPDATE
于是可以这样写
bson_t *query=BCON_NEW ("age" ,"{" , "$gte" ,BCON_INT32 (12 ), "}" );bson_t *update=BCON_NEW ("$set" ,"{" , "age" ,BCON_INT32 (888 ), "}" );bool retr= mongoc_collection_update (coll,MONGOC_UPDATE_MULTI_UPDATE,query,update,NULL ,&error);if (!retr){ cout<<error.message<<endl; }bson_destroy (query);bson_destroy (update);
注意,“age”,”{“ 要单独写,不能写成 “age{“ ,否则 src/bson/bcon.c:784 bcon_append_ctx_va(): precondition failed: type == BCON_TYPE_UTF8 这是最坑爹的地方
内存释放
别以为这样就结束了哦,还要记得要释放所有的内存
bson_free (void *mem);bson_destroy (bson_t *bson);mongoc_cursor_destroy (cursor);mongoc_collection_destroy (coll);mongoc_database_destroy (database);mongoc_client_destroy (client);mongoc_cleanup ();
编译
编译方法也很简单
g++ main.cpp -o main pkg-config –libs –cflags libmongoc-1.0
或者
g++ main.cpp -o main -lbson-1.0 -lmongoc-1.0
结尾
不得不说,用mongoc来操控mongoDB数据库还挺复杂的。因此建议自己把这些函数封装成类对象。其实,关于mongoc封装成类,Mongoc 目前已经发展到了MongoCXX ,以Mongoc为基础,所有操作都被封装到类里面。引入了几个特殊的对象 view、value、view_or_value 。同时,构建(Build )一个BSON文档有两种方法:basic、stream 。可 参阅这里
以后有空再介绍***mongocxx***吧~
参考 http://mongoc.org/libmongoc/current/tutorial.html