Este tutorial explica como aproveitar os recursos de operação de gravação em massa do driver MongoDB C. A execução de operações de gravação em lotes reduz o número de idas e voltas da rede, aumentando a taxa de transferência de gravação.
Bulk Insert
Primeiro, precisamos buscar um identificador de operação em massa do mongoc_collection_t.
mongoc_bulk_operation_t *bulk = mongoc_collection_create_bulk_operation_with_opts (collection, NULL);
Agora podemos começar a inserir documentos na operação em massa. Eles serão armazenados em buffer até executarmos a operação.
A operação em massa agrupará as inserções como um único lote para cada chamada consecutiva para mongoc_bulk_operation_insert. Isso cria um efeito pipelined quando possível.
Para executar a operação em massa e receber o resultado, chamamos de mongoc_bulk_operation_execute.
static void bulk1 (mongoc_collection_t *collection) { mongoc_bulk_operation_t *bulk; bson_error_t error; bson_t *doc; bson_t reply; char *str; bool ret; int i; bulk = mongoc_collection_create_bulk_operation_with_opts (collection, NULL); for (i = 0; i < 10000; i++) { doc = BCON_NEW ("i", BCON_INT32 (i)); mongoc_bulk_operation_insert (bulk, doc); bson_destroy (doc); } ret = mongoc_bulk_operation_execute (bulk, &reply, &error); str = bson_as_canonical_extended_json (&reply, NULL); printf ("%s\n", str); bson_free (str); if (!ret) { fprintf (stderr, "Error: %s\n", error.message); } bson_destroy (&reply); mongoc_bulk_operation_destroy (bulk); } int main (void) { mongoc_client_t *client; mongoc_collection_t *collection; const char *uri_string = "mongodb://localhost/?appname=bulk1-example"; mongoc_uri_t *uri; bson_error_t error; mongoc_init (); uri = mongoc_uri_new_with_error (uri_string, &error); if (!uri) { fprintf (stderr, "failed to parse URI: %s\n" "error message: %s\n", uri_string, error.message); return EXIT_FAILURE; } client = mongoc_client_new_from_uri (uri); if (!client) { return EXIT_FAILURE; } mongoc_client_set_error_api (client, 2); collection = mongoc_client_get_collection (client, "test", "test"); bulk1 (collection); mongoc_uri_destroy (uri); mongoc_collection_destroy (collection); mongoc_client_destroy (client); mongoc_cleanup (); return EXIT_SUCCESS; }
Exemplo de documento reply
:
{"nInserted" : 10000, "nMatched" : 0, "nModified" : 0, "nRemoved" : 0, "nUpserted" : 0, "writeErrors" : [] "writeConcernErrors" : [] }
Operações de gravação em massa mista
O driver MongoDB C também suporta a execução de operações mistas de escrita em massa. Um lote de operações de inserção, atualização e remoção pode ser executado junto usando a API de operações de gravação em massa.
Operações de gravação em massa ordenadas
As operações de gravação em massa ordenadas são agrupadas e enviadas ao servidor na ordem fornecida para a execução serial. O documento reply
descreve o tipo e a contagem de operações executadas.
static void bulk2 (mongoc_collection_t *collection) { mongoc_bulk_operation_t *bulk; bson_error_t error; bson_t *query; bson_t *doc; bson_t *opts; bson_t reply; char *str; bool ret; int i; bulk = mongoc_collection_create_bulk_operation_with_opts (collection, NULL); /* Remove everything */ query = bson_new (); mongoc_bulk_operation_remove (bulk, query); bson_destroy (query); /* Add a few documents */ for (i = 1; i < 4; i++) { doc = BCON_NEW ("_id", BCON_INT32 (i)); mongoc_bulk_operation_insert (bulk, doc); bson_destroy (doc); } /* {_id: 1} => {$set: {foo: "bar"}} */ query = BCON_NEW ("_id", BCON_INT32 (1)); doc = BCON_NEW ("$set", "{", "foo", BCON_UTF8 ("bar"), "}"); mongoc_bulk_operation_update_many_with_opts (bulk, query, doc, NULL, &error); bson_destroy (query); bson_destroy (doc); /* {_id: 4} => {'$inc': {'j': 1}} (upsert) */ opts = BCON_NEW ("upsert", BCON_BOOL (true)); query = BCON_NEW ("_id", BCON_INT32 (4)); doc = BCON_NEW ("$inc", "{", "j", BCON_INT32 (1), "}"); mongoc_bulk_operation_update_many_with_opts (bulk, query, doc, opts, &error); bson_destroy (query); bson_destroy (doc); bson_destroy (opts); /* replace {j:1} with {j:2} */ query = BCON_NEW ("j", BCON_INT32 (1)); doc = BCON_NEW ("j", BCON_INT32 (2)); mongoc_bulk_operation_replace_one_with_opts (bulk, query, doc, NULL, &error); bson_destroy (query); bson_destroy (doc); ret = mongoc_bulk_operation_execute (bulk, &reply, &error); str = bson_as_canonical_extended_json (&reply, NULL); printf ("%s\n", str); bson_free (str); if (!ret) { printf ("Error: %s\n", error.message); } bson_destroy (&reply); mongoc_bulk_operation_destroy (bulk); } int main (void) { mongoc_client_t *client; mongoc_collection_t *collection; const char *uri_string = "mongodb://localhost/?appname=bulk2-example"; mongoc_uri_t *uri; bson_error_t error; mongoc_init (); uri = mongoc_uri_new_with_error (uri_string, &error); if (!uri) { fprintf (stderr, "failed to parse URI: %s\n" "error message: %s\n", uri_string, error.message); return EXIT_FAILURE; } client = mongoc_client_new_from_uri (uri); if (!client) { return EXIT_FAILURE; } mongoc_client_set_error_api (client, 2); collection = mongoc_client_get_collection (client, "test", "test"); bulk2 (collection); mongoc_uri_destroy (uri); mongoc_collection_destroy (collection); mongoc_client_destroy (client); mongoc_cleanup (); return EXIT_SUCCESS; }
Exemplo de documento reply
:
{ "nInserted" : 3, "nMatched" : 2, "nModified" : 2, "nRemoved" : 10000, "nUpserted" : 1, "upserted" : [{"index" : 5, "_id" : 4}], "writeErrors" : [] "writeConcernErrors" : [] }
O campo index
na array upserted
é o índice baseado em 0da operação upsert; neste exemplo, a seis operação da operação em massa geral foi um upsert, então seu índice é 5.
Operações de gravação em massa não ordenadas
As operações de gravação em massa não ordenadas são agrupadas e enviadas ao servidor em ordem arbitrária, onde podem ser executadas em paralelo. Quaisquer erros que ocorrem são relatados depois que todas as operações são tentadas.
No próximo exemplo , a primeira e a terceira operações falham devido à restrição exclusiva em _id
. Como estamos fazendo execução não ordenada, a segunda e a quarta operações são bem-sucedidas.
static void bulk3 (mongoc_collection_t *collection) { bson_t opts = BSON_INITIALIZER; mongoc_bulk_operation_t *bulk; bson_error_t error; bson_t *query; bson_t *doc; bson_t reply; char *str; bool ret; /* false indicates unordered */ BSON_APPEND_BOOL (&opts, "ordered", false); bulk = mongoc_collection_create_bulk_operation_with_opts (collection, &opts); bson_destroy (&opts); /* Add a document */ doc = BCON_NEW ("_id", BCON_INT32 (1)); mongoc_bulk_operation_insert (bulk, doc); bson_destroy (doc); /* remove {_id: 2} */ query = BCON_NEW ("_id", BCON_INT32 (2)); mongoc_bulk_operation_remove_one (bulk, query); bson_destroy (query); /* insert {_id: 3} */ doc = BCON_NEW ("_id", BCON_INT32 (3)); mongoc_bulk_operation_insert (bulk, doc); bson_destroy (doc); /* replace {_id:4} {'i': 1} */ query = BCON_NEW ("_id", BCON_INT32 (4)); doc = BCON_NEW ("i", BCON_INT32 (1)); mongoc_bulk_operation_replace_one (bulk, query, doc, false); bson_destroy (query); bson_destroy (doc); ret = mongoc_bulk_operation_execute (bulk, &reply, &error); str = bson_as_canonical_extended_json (&reply, NULL); printf ("%s\n", str); bson_free (str); if (!ret) { printf ("Error: %s\n", error.message); } bson_destroy (&reply); mongoc_bulk_operation_destroy (bulk); bson_destroy (&opts); } int main (void) { mongoc_client_t *client; mongoc_collection_t *collection; const char *uri_string = "mongodb://localhost/?appname=bulk3-example"; mongoc_uri_t *uri; bson_error_t error; mongoc_init (); uri = mongoc_uri_new_with_error (uri_string, &error); if (!uri) { fprintf (stderr, "failed to parse URI: %s\n" "error message: %s\n", uri_string, error.message); return EXIT_FAILURE; } client = mongoc_client_new_from_uri (uri); if (!client) { return EXIT_FAILURE; } mongoc_client_set_error_api (client, 2); collection = mongoc_client_get_collection (client, "test", "test"); bulk3 (collection); mongoc_uri_destroy (uri); mongoc_collection_destroy (collection); mongoc_client_destroy (client); mongoc_cleanup (); return EXIT_SUCCESS; }
Exemplo de documento reply
:
{ "nInserted" : 0, "nMatched" : 1, "nModified" : 1, "nRemoved" : 1, "nUpserted" : 0, "writeErrors" : [ { "index" : 0, "code" : 11000, "errmsg" : "E11000 duplicate key error index: test.test.$_id_ dup key: { : 1 }" }, { "index" : 2, "code" : 11000, "errmsg" : "E11000 duplicate key error index: test.test.$_id_ dup key: { : 3 }" } ], "writeConcernErrors" : [] } Error: E11000 duplicate key error index: test.test.$_id_ dup key: { : 1 }
O domínio bson_error_t é MONGOC_ERROR_COMMAND
e seu código 11000 é.
Operação em massa ignorando a validação do documento
Esse recurso só está disponível ao usar o MongoDB 3.2 e posterior.
Por padrão, operações em massa são validadas em relação ao esquema, se houver algum definido. Em certos casos, no entanto, pode ser necessário ignorar a validação do documento .
static void bulk5_fail (mongoc_collection_t *collection) { mongoc_bulk_operation_t *bulk; bson_error_t error; bson_t *doc; bson_t reply; char *str; bool ret; bulk = mongoc_collection_create_bulk_operation_with_opts (collection, NULL); /* Two inserts */ doc = BCON_NEW ("_id", BCON_INT32 (31)); mongoc_bulk_operation_insert (bulk, doc); bson_destroy (doc); doc = BCON_NEW ("_id", BCON_INT32 (32)); mongoc_bulk_operation_insert (bulk, doc); bson_destroy (doc); /* The above documents do not comply to the schema validation rules * we created previously, so this will result in an error */ ret = mongoc_bulk_operation_execute (bulk, &reply, &error); str = bson_as_canonical_extended_json (&reply, NULL); printf ("%s\n", str); bson_free (str); if (!ret) { printf ("Error: %s\n", error.message); } bson_destroy (&reply); mongoc_bulk_operation_destroy (bulk); } static void bulk5_success (mongoc_collection_t *collection) { mongoc_bulk_operation_t *bulk; bson_error_t error; bson_t *doc; bson_t reply; char *str; bool ret; bulk = mongoc_collection_create_bulk_operation_with_opts (collection, NULL); /* Allow this document to bypass document validation. * NOTE: When authentication is enabled, the authenticated user must have * either the "dbadmin" or "restore" roles to bypass document validation */ mongoc_bulk_operation_set_bypass_document_validation (bulk, true); /* Two inserts */ doc = BCON_NEW ("_id", BCON_INT32 (31)); mongoc_bulk_operation_insert (bulk, doc); bson_destroy (doc); doc = BCON_NEW ("_id", BCON_INT32 (32)); mongoc_bulk_operation_insert (bulk, doc); bson_destroy (doc); ret = mongoc_bulk_operation_execute (bulk, &reply, &error); str = bson_as_canonical_extended_json (&reply, NULL); printf ("%s\n", str); bson_free (str); if (!ret) { printf ("Error: %s\n", error.message); } bson_destroy (&reply); mongoc_bulk_operation_destroy (bulk); } int main (void) { bson_t *options; bson_error_t error; mongoc_client_t *client; mongoc_collection_t *collection; mongoc_database_t *database; const char *uri_string = "mongodb://localhost/?appname=bulk5-example"; mongoc_uri_t *uri; mongoc_init (); uri = mongoc_uri_new_with_error (uri_string, &error); if (!uri) { fprintf (stderr, "failed to parse URI: %s\n" "error message: %s\n", uri_string, error.message); return EXIT_FAILURE; } client = mongoc_client_new_from_uri (uri); if (!client) { return EXIT_FAILURE; } mongoc_client_set_error_api (client, 2); database = mongoc_client_get_database (client, "testasdf"); /* Create schema validator */ options = BCON_NEW ("validator", "{", "number", "{", "$gte", BCON_INT32 (5), "}", "}"); collection = mongoc_database_create_collection (database, "collname", options, &error); if (collection) { bulk5_fail (collection); bulk5_success (collection); mongoc_collection_destroy (collection); } else { fprintf (stderr, "Couldn't create collection: '%s'\n", error.message); } bson_free (options); mongoc_uri_destroy (uri); mongoc_database_destroy (database); mongoc_client_destroy (client); mongoc_cleanup (); return EXIT_SUCCESS; }
Executar o exemplo acima resultará em:
{ "nInserted" : 0, "nMatched" : 0, "nModified" : 0, "nRemoved" : 0, "nUpserted" : 0, "writeErrors" : [ { "index" : 0, "code" : 121, "errmsg" : "Document failed validation" } ] } Error: Document failed validation { "nInserted" : 2, "nMatched" : 0, "nModified" : 0, "nRemoved" : 0, "nUpserted" : 0, "writeErrors" : [] }
O domínio bson_error_t MONGOC_ERROR_COMMAND
é.
Preocupações com gravação de operações em massa
Por padrão, operações em massa são executadas com o mongoc_write_concern_t da collection em que são executadas. Uma preocupação de gravação personalizada pode ser passada para o método mongoc_collection_create_bulk_operation_with_opts. Erros de write concern (por exemplo, wtimeout) serão relatados depois que todas as operações forem tentadas, independentemente da ordem de execução.
static void bulk4 (mongoc_collection_t *collection) { bson_t opts = BSON_INITIALIZER; mongoc_write_concern_t *wc; mongoc_bulk_operation_t *bulk; bson_error_t error; bson_t *doc; bson_t reply; char *str; bool ret; wc = mongoc_write_concern_new (); mongoc_write_concern_set_w (wc, 4); mongoc_write_concern_set_wtimeout_int64 (wc, 100); /* milliseconds */ mongoc_write_concern_append (wc, &opts); bulk = mongoc_collection_create_bulk_operation_with_opts (collection, &opts); /* Two inserts */ doc = BCON_NEW ("_id", BCON_INT32 (10)); mongoc_bulk_operation_insert (bulk, doc); bson_destroy (doc); doc = BCON_NEW ("_id", BCON_INT32 (11)); mongoc_bulk_operation_insert (bulk, doc); bson_destroy (doc); ret = mongoc_bulk_operation_execute (bulk, &reply, &error); str = bson_as_canonical_extended_json (&reply, NULL); printf ("%s\n", str); bson_free (str); if (!ret) { printf ("Error: %s\n", error.message); } bson_destroy (&reply); mongoc_bulk_operation_destroy (bulk); mongoc_write_concern_destroy (wc); bson_destroy (&opts); } int main (void) { mongoc_client_t *client; mongoc_collection_t *collection; const char *uri_string = "mongodb://localhost/?appname=bulk4-example"; mongoc_uri_t *uri; bson_error_t error; mongoc_init (); uri = mongoc_uri_new_with_error (uri_string, &error); if (!uri) { fprintf (stderr, "failed to parse URI: %s\n" "error message: %s\n", uri_string, error.message); return EXIT_FAILURE; } client = mongoc_client_new_from_uri (uri); if (!client) { return EXIT_FAILURE; } mongoc_client_set_error_api (client, 2); collection = mongoc_client_get_collection (client, "test", "test"); bulk4 (collection); mongoc_uri_destroy (uri); mongoc_collection_destroy (collection); mongoc_client_destroy (client); mongoc_cleanup (); return EXIT_SUCCESS; }
Exemplo de documento reply
e mensagem de erro:
{ "nInserted" : 2, "nMatched" : 0, "nModified" : 0, "nRemoved" : 0, "nUpserted" : 0, "writeErrors" : [], "writeConcernErrors" : [ { "code" : 64, "errmsg" : "waiting for replication timed out" } ] } Error: waiting for replication timed out
O domínio bson_error_t é MONGOC_ERROR_WRITE_CONCERN
se houver erros de preocupação de gravação e nenhum erro de write. Os erros de escrita indicam operações com falha, portanto, eles têm precedência sobre os erros de preocupação de gravação , o que média apenas que a preocupação de gravação ainda não está satisfeita.
Definindo ordem de agrupamento
Esse recurso só está disponível ao usar o MongoDB 3.4 e posterior.
static void bulk_collation (mongoc_collection_t *collection) { mongoc_bulk_operation_t *bulk; bson_t *opts; bson_t *doc; bson_t *selector; bson_t *update; bson_error_t error; bson_t reply; char *str; uint32_t ret; /* insert {_id: "one"} and {_id: "One"} */ bulk = mongoc_collection_create_bulk_operation_with_opts (collection, NULL); doc = BCON_NEW ("_id", BCON_UTF8 ("one")); mongoc_bulk_operation_insert (bulk, doc); bson_destroy (doc); doc = BCON_NEW ("_id", BCON_UTF8 ("One")); mongoc_bulk_operation_insert (bulk, doc); bson_destroy (doc); /* "One" normally sorts before "one"; make "one" come first */ opts = BCON_NEW ("collation", "{", "locale", BCON_UTF8 ("en_US"), "caseFirst", BCON_UTF8 ("lower"), "}"); /* set x=1 on the document with _id "One", which now sorts after "one" */ update = BCON_NEW ("$set", "{", "x", BCON_INT64 (1), "}"); selector = BCON_NEW ("_id", "{", "$gt", BCON_UTF8 ("one"), "}"); mongoc_bulk_operation_update_one_with_opts (bulk, selector, update, opts, &error); ret = mongoc_bulk_operation_execute (bulk, &reply, &error); str = bson_as_canonical_extended_json (&reply, NULL); printf ("%s\n", str); bson_free (str); if (!ret) { printf ("Error: %s\n", error.message); } bson_destroy (&reply); bson_destroy (update); bson_destroy (selector); bson_destroy (opts); mongoc_bulk_operation_destroy (bulk); } int main (void) { mongoc_client_t *client; mongoc_collection_t *collection; const char *uri_string = "mongodb://localhost/?appname=bulk-collation"; mongoc_uri_t *uri; bson_error_t error; mongoc_init (); uri = mongoc_uri_new_with_error (uri_string, &error); if (!uri) { fprintf (stderr, "failed to parse URI: %s\n" "error message: %s\n", uri_string, error.message); return EXIT_FAILURE; } client = mongoc_client_new_from_uri (uri); if (!client) { return EXIT_FAILURE; } mongoc_client_set_error_api (client, 2); collection = mongoc_client_get_collection (client, "db", "collection"); bulk_collation (collection); mongoc_uri_destroy (uri); mongoc_collection_destroy (collection); mongoc_client_destroy (client); mongoc_cleanup (); return EXIT_SUCCESS; }
Executar o exemplo acima resultará em:
{ "nInserted" : 2, "nMatched" : 1, "nModified" : 1, "nRemoved" : 0, "nUpserted" : 0, "writeErrors" : [ ] }
Gravações em massa não reconhecidas
Defina "w" como zero para uma escrita não reconhecida. O driver envia gravações não reconhecidas usando os códigos de operação legado OP_INSERT
, OP_UPDATE
e OP_DELETE
.
static void bulk6 (mongoc_collection_t *collection) { bson_t opts = BSON_INITIALIZER; mongoc_write_concern_t *wc; mongoc_bulk_operation_t *bulk; bson_error_t error; bson_t *doc; bson_t *selector; bson_t reply; char *str; bool ret; wc = mongoc_write_concern_new (); mongoc_write_concern_set_w (wc, 0); mongoc_write_concern_append (wc, &opts); bulk = mongoc_collection_create_bulk_operation_with_opts (collection, &opts); doc = BCON_NEW ("_id", BCON_INT32 (10)); mongoc_bulk_operation_insert (bulk, doc); bson_destroy (doc); selector = BCON_NEW ("_id", BCON_INT32 (11)); mongoc_bulk_operation_remove_one (bulk, selector); bson_destroy (selector); ret = mongoc_bulk_operation_execute (bulk, &reply, &error); str = bson_as_canonical_extended_json (&reply, NULL); printf ("%s\n", str); bson_free (str); if (!ret) { printf ("Error: %s\n", error.message); } bson_destroy (&reply); mongoc_bulk_operation_destroy (bulk); mongoc_write_concern_destroy (wc); bson_destroy (&opts); } int main (void) { mongoc_client_t *client; mongoc_collection_t *collection; const char *uri_string = "mongodb://localhost/?appname=bulk6-example"; mongoc_uri_t *uri; bson_error_t error; mongoc_init (); uri = mongoc_uri_new_with_error (uri_string, &error); if (!uri) { fprintf (stderr, "failed to parse URI: %s\n" "error message: %s\n", uri_string, error.message); return EXIT_FAILURE; } client = mongoc_client_new_from_uri (uri); if (!client) { return EXIT_FAILURE; } mongoc_client_set_error_api (client, 2); collection = mongoc_client_get_collection (client, "test", "test"); bulk6 (collection); mongoc_uri_destroy (uri); mongoc_collection_destroy (collection); mongoc_client_destroy (client); mongoc_cleanup (); return EXIT_SUCCESS; }
O documento reply
está vazio:
{ }
Leitura adicional
Consulte a Especificação da API do driver em massa, que descreve as operações de gravação em massa para todos os drivers do MongoDB .