mongodb的c驅動使用
1. 程式安裝
預安裝
需要先安裝依賴庫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'
1.1. 安裝MongoDB
安裝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 查詢本目錄下編譯出來的靜態庫
1.2. 測試安裝MongoDB
連線到MongoDB並測試
$ mongo --host localhost --port 27017
MongoDB shell version: 3.0.6
connecting to: localhost:27017/test
2. api操作
Libmongoc提供一系列的儲存訪問介面,以及儲存和傳輸結構使用型別。其提供的為同步介面。
2.1. 建立連線
使用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;
}
2.2. 文件
MongoDB使用了BSON這種結構來儲存資料和網路資料交換。把這種格式轉化成一文件這個概念,這裡的一個文件也可以理解成關係資料庫中的一條記錄,只是這裡的文件的變化更豐富一些,如文件可以巢狀。MongoDB以BSON做為其儲存結構的一種重要原因是其可遍歷性。
2.3. 建立文件
mongodb c驅動使用libbson來建立BSON文件。有幾種構建文件的方式:使用BSON插入鍵值對,或者解析json。
2.3.1. 使用BSON插入鍵值對
#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物件的資料型別。
2.3.2. 使用BCON建構函式來構建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;
}
2.3.3. 從json字串建立bson物件
#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;
}
2.4. 基本CRUD操作
2.4.1. 插入文件
插入文件{ "_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" }
2.4.2. 查詢文件
使用函式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" }
2.4.3. 更新文件
先插入一個文件,根據文件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" }
2.4.4. 刪除文件
刪除符合條件 {"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
2.4.5. 計算文件個數
計算符合條件 {"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
2.5. 執行命令
函式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 }
2.6. 執行緒安全
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;
}
2.7. 大結構檔案系統
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;
}