预安装
需要先安装依赖库OpenSSL,来建立ssl连接到MongoDB
RedHat / Fedora系统:
$ sudo yum install pkg-config openssl-devel cyrus-sasl-devel
Debian / Ubuntu系统:
$ sudo apt-get install pkg-config libssl-dev libsasl2-dev
FreeBSD系统:
$ su -c 'pkg install pkgconf openssl cyrus-sasl2'
参考:http://api.mongodb.org/c/1.3.3/installing.html#installing-unix
安装MongoDB发行包
$ wget https://github.com/mongodb/mongo-c-driver/releases/download/1.3.3/mongo-c-driver-1.3.3.tar.gz
$ tar xzf mongo-c-driver-1.3.3.tar.gz
$ cd mongo-c-driver-1.3.3
$ . /configure --help
$ ./configure --enable-static --enable-shared
安装成功会出现如下配置描述
libmongoc was configured with the following options:
Build configuration:
Enable debugging (slow) : no
Compile with debug symbols (slow) : no
Enable GCC build optimization : yes
Enable automatic binary hardening : yes
Code coverage support : no
Cross Compiling : no
Fast counters : no
SASL : sasl2
SSL : yes
Libbson : bundled
Documentation:
Generate man pages : no
Install man pages : no
其中libbson是附带的捆绑的库,系统若无,脚本会自动安装。
$ make
$ sudo make install
find -name *.a 查询本目录下编译出来的静态库
连接到MongoDB并测试
$ mongo --host localhost --port 27017
MongoDB shell version: 3.0.6
connecting to: localhost:27017/test
Libmongoc提供一系列的存储访问接口,以及存储和传输结构使用类型。其提供的为同步接口。
使用libmongoc驱动创建到mongo实例的连接
#include <bson.h>
#include <bcon.h>
#include <mongoc.h>
int
main (int argc,
char *argv[])
{
mongoc_client_t *client;
mongoc_database_t *database;
mongoc_collection_t *collection;
bson_t *command,
reply,
*insert;
bson_error_t error;
char *str;
bool retval;
/*
* Required to initialize libmongoc's internals
*/
mongoc_init ();//初始化libmongoc驱动
/*
* Create a new client instance
*/
client = mongoc_client_new ("mongodb://localhost:27017");//创建连接对象
/*
* Get a handle on the database "db_name" and collection "coll_name"
*/
database = mongoc_client_get_database (client, "db_name");//获取数据库
collection = mongoc_client_get_collection (client, "db_name", "coll_name");//获取指定数据库和集合
/*
* Do work. This example pings the database, prints the result as JSON and
* performs an insert
*/
command = BCON_NEW ("ping", BCON_INT32 (1));
retval = mongoc_client_command_simple (client, "admin", command, NULL, &reply, &error);//执行命令
if (!retval) {
fprintf (stderr, "%s\n", error.message);
return EXIT_FAILURE;
}
str = bson_as_json (&reply, NULL);
printf ("%s\n", str);
insert = BCON_NEW ("hello", BCON_UTF8 ("world"));//字段为hello,值为world字符串
if (!mongoc_collection_insert (collection, MONGOC_INSERT_NONE, insert, NULL, &error)) {//插入文档
fprintf (stderr, "%s\n", error.message);
}
bson_destroy (insert);
bson_destroy (&reply);
bson_destroy (command);
bson_free (str);
/*
* Release our handles and clean up libmongoc
*/
mongoc_collection_destroy (collection);//释放表对象
mongoc_database_destroy (database);//释放数据库对象
mongoc_client_destroy (client);//释放连接对象
mongoc_cleanup ();//释放libmongoc驱动
return 0;
}
MongoDB使用了BSON这种结构来存储数据和网络数据交换。把这种格式转化成一文档这个概念,这里的一个文档也可以理解成关系数据库中的一条记录,只是这里的文档的变化更丰富一些,如文档可以嵌套。MongoDB以BSON做为其存储结构的一种重要原因是其可遍历性。
mongodb c驱动使用libbson来创建BSON文档。有几种构建文档的方式:使用BSON插入键值对,或者解析json。
#include <bson.h>
int
main (int argc,
char *argv[])
{
bson_t *document;
bson_t child;
char *str;
document = bson_new ();
/*
* Append {"hello" : "world"} to the document.
* Passing -1 for the length argument tells libbson to calculate the string length.
*/
bson_append_utf8 (document, "hello", -1, "world", -1);
/*
* For convenience, this macro is equivalent.
*/
BSON_APPEND_UTF8 (document, "hello", "world");
/*
* Begin a subdocument.
*/
BSON_APPEND_DOCUMENT_BEGIN (document, "subdoc", &child);
BSON_APPEND_UTF8 (&child, "subkey", "value");
bson_append_document_end (document, &child);
/*
* Print the document as a JSON string.
*/
str = bson_as_json (document, NULL);
printf ("%s\n", str);
bson_free (str);
/*
* Clean up allocated bson documents.
*/
bson_destroy (document);
return 0;
}
可以查看libbson文档来查看能被添加到bson对象的数据类型。
#include <bcon.h>
#include <bson.h>
#include <stdio.h>
int
main (int argc,
char *argv[])
{
bson_t *doc;
char *str;
doc = BCON_NEW ("name", BCON_UTF8 ("Babe Ruth"),
"statistics", "{",
"batting_average", BCON_DOUBLE (.342),
"hits", BCON_INT32 (2873),
"home_runs", BCON_INT32 (714),
"rbi", BCON_INT32 (2213),
"}",
"nicknames", "[",
BCON_UTF8 ("the Sultan of Swat"),
BCON_UTF8 ("the Bambino"),
"]");
str = bson_as_json (doc, NULL);
printf ("%s\n", str);
bson_free (str);
bson_destroy (doc);
return 0;
}
#include <bson.h>
int
main (int argc,
char *argv[])
{
bson_error_t error;
bson_t *bson;
char *string;
const char *json = "{\"hello\": \"world\"}";
bson = bson_new_from_json ((const uint8_t *)json, -1, &error);
if (!bson) {
fprintf (stderr, "%s\n", error.message);
return EXIT_FAILURE;
}
string = bson_as_json (bson, NULL);
printf ("%s\n", string);
bson_free (string);
return 0;
}
插入文档{ "_id" : ObjectId("55ef43766cb5f36a3bae6ee4"), "hello" : "world" }
代码如下:
#include <bson.h>
#include <mongoc.h>
#include <stdio.h>
int
main (int argc,
char *argv[])
{
mongoc_client_t *client;
mongoc_collection_t *collection;
bson_error_t error;
bson_oid_t oid;
bson_t *doc;
mongoc_init ();
client = mongoc_client_new ("mongodb://localhost:27017/");
collection = mongoc_client_get_collection (client, "mydb", "mycoll");//插入到数据库mydb的集合mycoll中
doc = bson_new ();
bson_oid_init (&oid, NULL);
BSON_APPEND_OID (doc, "_id", &oid);//文档id
BSON_APPEND_UTF8 (doc, "hello", "world");//hello字段
if (!mongoc_collection_insert (collection, MONGOC_INSERT_NONE, doc, NULL, &error)) {
fprintf (stderr, "%s\n", error.message);
}
bson_destroy (doc);
mongoc_collection_destroy (collection);
mongoc_client_destroy (client);
mongoc_cleanup ();
return 0;
}
测试结果
$ mongo
MongoDB shell version: 3.0.6
connecting to: test
> use mydb
switched to db mydb
> db.mycoll.find()
{ "_id" : ObjectId("55ef43766cb5f36a3bae6ee4"), "hello" : "world" }
使用函数mongc_collection_find()来返回符合的结果文档的游标,需要传入bson查询对象。通过游标遍历结果集。
bson查询对象,比如:{ "color" : "red" },则会获取到字段为"color" 和值为"red" 的所有的文档。空查询对象则会获取所有的结果文档。
#include <bson.h>
#include <mongoc.h>
#include <stdio.h>
int
main (int argc,
char *argv[])
{
mongoc_client_t *client;
mongoc_collection_t *collection;
mongoc_cursor_t *cursor;
const bson_t *doc;
bson_t *query;
char *str;
mongoc_init ();
client = mongoc_client_new ("mongodb://localhost:27017/");
collection = mongoc_client_get_collection (client, "mydb", "mycoll");
query = bson_new ();
cursor = mongoc_collection_find (collection, MONGOC_QUERY_NONE, 0, 0, 0, query, NULL, NULL);//空查询对象获取所有的文档
while (mongoc_cursor_next (cursor, &doc)) {
str = bson_as_json (doc, NULL);
printf ("%s\n", str);
bson_free (str);
}
bson_destroy (query);
mongoc_cursor_destroy (cursor);
mongoc_collection_destroy (collection);
mongoc_client_destroy (client);
mongoc_cleanup ();
return 0;
}
编译运行,输出查询结果
$ gcc -o find find.c
$ ./find
{ "_id" : { "$oid" : "55ef43766cb5f36a3bae6ee4" }, "hello" : "world" }
先插入一个文档,根据文档id来更新文档。
代码如下:
#include <bcon.h>
#include <bson.h>
#include <mongoc.h>
#include <stdio.h>
int
main (int argc,
char *argv[])
{
mongoc_collection_t *collection;
mongoc_client_t *client;
bson_error_t error;
bson_oid_t oid;
bson_t *doc = NULL;
bson_t *update = NULL;
bson_t *query = NULL;
mongoc_init ();
client = mongoc_client_new ("mongodb://localhost:27017/");//创建连接
collection = mongoc_client_get_collection (client, "mydb", "mycoll");//指定数据库和集合(即表)
bson_oid_init (&oid, NULL);
doc = BCON_NEW ("_id", BCON_OID (&oid),
"key", BCON_UTF8 ("old_value"));//先插入{ "_id" : ObjectId("55ef549236fe322f9490e17b"), "key" : "old_value" }
if (!mongoc_collection_insert (collection, MONGOC_INSERT_NONE, doc, NULL, &error)) {
fprintf (stderr, "%s\n", error.message);
goto fail;
}
query = BCON_NEW ("_id", BCON_OID (&oid));//条件为id "_id" : ObjectId("55ef549236fe322f9490e17b")
update = BCON_NEW ("$set", "{",
"key", BCON_UTF8 ("new_value"),
"updated", BCON_BOOL (true),
"}");
if (!mongoc_collection_update (collection, MONGOC_UPDATE_NONE, query, update, NULL, &error)) {
fprintf (stderr, "%s\n", error.message);
goto fail;
}
fail:
if (doc)
bson_destroy (doc);
if (query)
bson_destroy (query);
if (update)
bson_destroy (update);
mongoc_collection_destroy (collection);
mongoc_client_destroy (client);
mongoc_cleanup ();
return 0;
}
$ gcc -o update update.c
$ ./update
检查更新结果
$ mongo
MongoDB shell version: 3.0.6
connecting to: test
> use mydb
switched to db mydb
> db.mycoll.find({"updated" : true})
{ "_id" : ObjectId("55ef549236fe322f9490e17b"), "updated" : true, "key" : "new_value" }
删除符合条件 {"hello" : "world"}的文档
#include <bson.h>
#include <mongoc.h>
#include <stdio.h>
int
main (int argc,
char *argv[])
{
mongoc_client_t *client;
mongoc_collection_t *collection;
bson_error_t error;
bson_oid_t oid;
bson_t *doc;
mongoc_init ();
client = mongoc_client_new ("mongodb://localhost:27017/");//创建连接到指定ip和端口的mongodb实例
collection = mongoc_client_get_collection (client, "mydb", "mycoll");//指定数据库和集合
doc = bson_new ();
bson_oid_init (&oid, NULL);
BSON_APPEND_OID (doc, "_id", &oid);
BSON_APPEND_UTF8 (doc, "hello", "world");//构建文档对象
if (!mongoc_collection_insert (collection, MONGOC_INSERT_NONE, doc, NULL, &error)) {
fprintf (stderr, "Insert failed: %s\n", error.message);//插入文档
}
bson_destroy (doc);
doc = bson_new ();
BSON_APPEND_OID (doc, "_id", &oid);
if (!mongoc_collection_remove (collection, MONGOC_REMOVE_SINGLE_REMOVE, doc, NULL, &error)) {
fprintf (stderr, "Delete failed: %s\n", error.message);
}
bson_destroy (doc);
mongoc_collection_destroy (collection);
mongoc_client_destroy (client);
mongoc_cleanup ();
return 0;
}
编译运行
$ gcc -o delete delete.c
$ ./delete
检查删除结果,计数字段为"hello" 值为"world"的文档的个数
$ mongo
> use mydb
switched to db mydb
> db.mycoll.count({"hello" : "world"})
0
计算符合条件 {"hello" : "world"} 的文档的个数,只要包含该条件的文档都会计数。
#include <bson.h>
#include <mongoc.h>
#include <stdio.h>
int
main (int argc,
char *argv[])
{
mongoc_client_t *client;
mongoc_collection_t *collection;
bson_error_t error;
bson_t *doc;
int64_t count;
mongoc_init ();
client = mongoc_client_new ("mongodb://localhost:27017/");//创建连接
collection = mongoc_client_get_collection (client, "mydb", "mycoll");//指定数据库和集合
doc = bson_new_from_json ((const uint8_t *)"{\"hello\" : \"world\"}", -1, &error);//构造计数条件
count = mongoc_collection_count (collection, MONGOC_QUERY_NONE, doc, 0, 0, NULL, &error);
if (count < 0) {
fprintf (stderr, "%s\n", error.message);
} else {
printf ("%" PRId64 "\n", count);
}
bson_destroy (doc);
mongoc_collection_destroy (collection);
mongoc_client_destroy (client);
mongoc_cleanup ();
return 0;
}
编译运行
$ gcc -o count count.c $(pkg-config --cflags --libs libmongoc-1.0)
$ ./count
1
函数mongoc_collection_command_simple可执行MongoDB 命令,传入执行命令的结果,函数返回执行的结果成功与否。
代码如下,执行 collStats命令并输出结果
executing.c
#include <bson.h>
#include <bcon.h>
#include <mongoc.h>
#include <stdio.h>
int
main (int argc,
char *argv[])
{
mongoc_client_t *client;
mongoc_collection_t *collection;
bson_error_t error;
bson_t *command;
bson_t reply;
char *str;
mongoc_init ();
client = mongoc_client_new ("mongodb://localhost:27017/");
collection = mongoc_client_get_collection (client, "mydb", "mycoll");
command = BCON_NEW ("collStats", BCON_UTF8 ("mycoll"));
if (mongoc_collection_command_simple (collection, command, NULL, &reply, &error)) {
str = bson_as_json (&reply, NULL);//输出执行命令后的结果
printf ("%s\n", str);
bson_free (str);
} else {
fprintf (stderr, "Failed to run command: %s\n", error.message);
}
bson_destroy (command);
bson_destroy (&reply);
mongoc_collection_destroy (collection);
mongoc_client_destroy (client);
mongoc_cleanup ();
return 0;
}
编译运行:
$ gcc -o executing executing.c
$ ./executing
{ "ns" : "mydb.mycoll", "count" : 1, "size" : 48, "avgObjSize" : 48, "numExtents" : 1, "storageSize" : 8192,
"lastExtentSize" : 8192.000000, "paddingFactor" : 1.000000, "userFlags" : 1, "capped" : false, "nindexes" : 1,
"indexDetails" : { }, "totalIndexSize" : 8176, "indexSizes" : { "_id_" : 8176 }, "ok" : 1.000000 }
数据库表mydb.mycoll,文档数1,大小48字节,平均大小48字节,存储大小8192字节,索引和其大小为{ "_id_" : 8176 }
MongoDB C Driver 多数操作都不是线程安全的,需要自己来保证。
提供了线程安全的连接来获取连接对象。
代码如下:
#include <mongoc.h>
#include <pthread.h>
#define N_THREADS 10
static void *
worker (void *data) {
mongoc_client_pool_t *pool = data;
mongoc_client_t *client;
client = mongoc_client_pool_pop (pool);//从连接池中获取连接对象
/* Do something... */
mongoc_client_pool_push (pool, client);
return NULL;
}
int
main (int argc,
char *argv[])
{
mongoc_client_pool_t *pool;
mongoc_uri_t *uri;
pthread_t threads[N_THREADS];
mongoc_init ();
uri = mongoc_uri_new ("mongodb://localhost/");//url指定ip地址,没有指定
pool = mongoc_client_pool_new (uri);
for (i = 0; i < N_THREADS; i++) {
pthread_create (&threads[i], NULL, worker, pool);
}
for (i = 0; i < N_THREADS; i++) {
pthread_join (threads[i], NULL);
}
mongoc_client_pool_destroy (pool);
mongoc_uri_destroy (uri);
mongoc_cleanup ();
return 0;
}
GridFS是一种在MongoDB中存储大二进制文件的机制。使用GridFS存文件有如下几个原因:
利用Grid可以简化需求。要是已经用了MongoDB,GridFS就可以不需要使用独立文件存储架构。
GridFS会直接利用业已建立的复制或分片机制,所以对于文件存储来说故障恢复和扩展都很容易。
GridFS可以避免用于存储用户上传内容的文件系统出现的某些问题。例如,GridFS在同一个目录下放置大量的文件是没有任何问题的。
GridFS不产生磁盘碎片,因为MongoDB分配数据文件空间时以2GB为一块。
使用场景:
1) 有大量的上传图片(用户上传或者系统本身的文件发布等)
2) 文件的量级处于飞速增长,有可能打到单机操作系统自己的文件系统的查询性能瓶颈,甚至超过单机硬盘的扩容范围.
3) 文件的备份(不适用gridfs这种三方也可以做,但是不尽方便),文件系统访问的故障转移和修复..
4) 文件的索引,存储除文件本身以外还需要关联更多的元数据信息(比如,不仅仅存储文件,还要保存一些文件的发布式作者/发布时间/文件tag属性等等自定义信息)并且需要索引的。
5) 基于4),对文件的分类模糊,如果采用操作系统的文件系统,文件夹分类关系混乱或者无法分类时
6) 当前系统是基于web的,对图片的访问根据url了规则路由的..(如搭配nginx用,让nginx直接读取gridfs的文件)
7) 文件尺寸较小,而且众多,且文件有可能被迁移/删除等
8)用于存储和恢复那些超过16M(BSON文件限制)的文件
接口代码如下:
mongoc_gridfs_t提供的是MongoDB 的gridfs 文件系统的接口。地理信息文件系统包含gridfs_files 和gridfs_file_lists以及相关的api。
mongoc_gridfs_t 是非线程安全的。释放mongoc_gridfs_t之前需要先释放mongoc_gridfs_file_t和mongoc_gridfs_file_list_t
示例代码如下:
#include <mongoc.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
int main (int argc, char *argv[])
{
mongoc_gridfs_t *gridfs;
mongoc_gridfs_file_t *file;
mongoc_gridfs_file_list_t *list;
mongoc_gridfs_file_opt_t opt = { 0 };
mongoc_client_t *client;
mongoc_stream_t *stream;
bson_t query;
bson_t child;
bson_error_t error;
ssize_t r;
char buf[4096];
mongoc_iovec_t iov;
const char * filename;
const char * command;
if (argc < 2) {
fprintf(stderr, "usage - %s command ...\n", argv[0]);
return 1;
}
mongoc_init();
iov.iov_base = (void *)buf;
iov.iov_len = sizeof buf;
/* connect to localhost client */
client = mongoc_client_new ("mongodb://127.0.0.1:27017");//创建连接
assert(client);//检查创建连接结果
/* grab a gridfs handle in test prefixed by fs */
gridfs = mongoc_client_get_gridfs (client, "test", "fs", &error);
assert(gridfs);
command = argv[1];
filename = argv[2];
if (strcmp(command, "read") == 0) {//读取指定文档
if (argc != 3) {
fprintf(stderr, "usage - %s read filename\n", argv[0]);
return 1;
}
file = mongoc_gridfs_find_one_by_filename(gridfs, filename, &error);
assert(file);
stream = mongoc_stream_gridfs_new (file);
assert(stream);
for (;;) {
r = mongoc_stream_readv (stream, &iov, 1, -1, 0);
assert (r >= 0);
if (r == 0) {
break;
}
if (fwrite (iov.iov_base, 1, r, stdout) != r) {
MONGOC_ERROR ("Failed to write to stdout. Exiting.\n");
exit (1);
}
}
mongoc_stream_destroy (stream);
mongoc_gridfs_file_destroy (file);
} else if (strcmp(command, "list") == 0) {//列举所有文档
bson_init (&query);
bson_append_document_begin (&query, "$orderby", -1, &child);
bson_append_int32 (&child, "filename", -1, 1);
bson_append_document_end (&query, &child);
bson_append_document_begin (&query, "$query", -1, &child);
bson_append_document_end (&query, &child);
list = mongoc_gridfs_find (gridfs, &query);
bson_destroy (&query);
while ((file = mongoc_gridfs_file_list_next (list))) {
const char * name = mongoc_gridfs_file_get_filename(file);
printf("%s\n", name ? name : "?");
mongoc_gridfs_file_destroy (file);
}
mongoc_gridfs_file_list_destroy (list);
} else if (strcmp(command, "write") == 0) {//写文档
if (argc != 4) {
fprintf(stderr, "usage - %s write filename input_file\n", argv[0]);
return 1;
}
stream = mongoc_stream_file_new_for_path (argv [3], O_RDONLY, 0);
assert (stream);
opt.filename = filename;
file = mongoc_gridfs_create_file_from_stream (gridfs, stream, &opt);
assert(file);
mongoc_gridfs_file_save(file);
mongoc_gridfs_file_destroy(file);
} else {
fprintf(stderr, "Unknown command");
return 1;
}
mongoc_gridfs_destroy (gridfs);
mongoc_client_destroy (client);
mongoc_cleanup ();
return 0;
}