1. 程式人生 > >mongodb的c驅動使用

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這種結構來儲存資料和網路資料交換。把這種格式轉化成一文件這個概念,這裡的一個文件也可以理解成關係資料庫中的一條記錄,只是這裡的文件的變化更豐富一些,如文件可以巢狀。MongoDBBSON做為其儲存結構的一種重要原因是其可遍歷性。

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可以簡化需求。要是已經用了MongoDBGridFS就可以不需要使用獨立檔案儲存架構。

GridFS會直接利用業已建立的複製或分片機制,所以對於檔案儲存來說故障恢復和擴充套件都很容易。

GridFS可以避免用於儲存使用者上傳內容的檔案系統出現的某些問題。例如,GridFS在同一個目錄下放置大量的檔案是沒有任何問題的。

GridFS不產生磁碟碎片,因為MongoDB分配資料檔案空間時以2GB為一塊。

使用場景:

1) 有大量的上傳圖片(使用者上傳或者系統本身的檔案釋出等)

2) 檔案的量級處於飛速增長,有可能打到單機作業系統自己的檔案系統的查詢效能瓶頸,甚至超過單機硬碟的擴容範圍.

3) 檔案的備份(不適用gridfs這種三方也可以做,但是不盡方便),檔案系統訪問的故障轉移和修復..

4) 檔案的索引,儲存除檔案本身以外還需要關聯更多的元資料資訊(比如,不僅僅儲存檔案,還要儲存一些檔案的釋出式作者/釋出時間/檔案tag屬性等等自定義資訊)並且需要索引的

5) 基於4),對檔案的分類模糊,如果採用作業系統的檔案系統,資料夾分類關係混亂或者無法分類時

6) 當前系統是基於web,對圖片的訪問根據url了規則路由的..(如搭配nginx用,讓nginx直接讀取gridfs的檔案)

7) 檔案尺寸較小,而且眾多,且檔案有可能被遷移/刪除等

8)用於儲存和恢復那些超過16MBSON檔案限制)的檔案

介面程式碼如下:

mongoc_gridfs_t提供的是MongoDB gridfs 檔案系統的介面。地理資訊檔案系統包含gridfs_files gridfs_file_lists以及相關的api

mongoc_gridfs_t 是非執行緒安全的。釋放mongoc_gridfs_t之前需要先釋放mongoc_gridfs_file_tmongoc_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;
}