1MONGOC_GUIDES(3) libmongoc MONGOC_GUIDES(3)
2
3
4
6 mongoc_guides - Guides
7
9 Drivers for some other languages provide helper functions to perform
10 certain common tasks. In the C Driver we must explicitly build commands
11 to send to the server.
12
13 Setup
14 First we'll write some code to insert sample data: doc-com‐
15 mon-insert.c.INDENT 0.0
16
17 /* Don't try to compile this file on its own. It's meant to be #included
18 by example code */
19
20 /* Insert some sample data */
21 bool
22 insert_data (mongoc_collection_t *collection)
23 {
24 mongoc_bulk_operation_t *bulk;
25 enum N { ndocs = 4 };
26 bson_t *docs[ndocs];
27 bson_error_t error;
28 int i = 0;
29 bool ret;
30
31 bulk = mongoc_collection_create_bulk_operation_with_opts (collection, NULL);
32
33 docs[0] = BCON_NEW ("x", BCON_DOUBLE (1.0), "tags", "[", "dog", "cat", "]");
34 docs[1] = BCON_NEW ("x", BCON_DOUBLE (2.0), "tags", "[", "cat", "]");
35 docs[2] = BCON_NEW (
36 "x", BCON_DOUBLE (2.0), "tags", "[", "mouse", "cat", "dog", "]");
37 docs[3] = BCON_NEW ("x", BCON_DOUBLE (3.0), "tags", "[", "]");
38
39 for (i = 0; i < ndocs; i++) {
40 mongoc_bulk_operation_insert (bulk, docs[i]);
41 bson_destroy (docs[i]);
42 docs[i] = NULL;
43 }
44
45 ret = mongoc_bulk_operation_execute (bulk, NULL, &error);
46
47 if (!ret) {
48 fprintf (stderr, "Error inserting data: %s\n", error.message);
49 }
50
51 mongoc_bulk_operation_destroy (bulk);
52 return ret;
53 }
54
55 /* A helper which we'll use a lot later on */
56 void
57 print_res (const bson_t *reply)
58 {
59 char *str;
60 BSON_ASSERT (reply);
61 str = bson_as_canonical_extended_json (reply, NULL);
62 printf ("%s\n", str);
63 bson_free (str);
64 }
65
66
67 explain Command
68 This is how to use the explain command in MongoDB 3.2+:
69 explain.c.INDENT 0.0
70
71 bool
72 explain (mongoc_collection_t *collection)
73 {
74 bson_t *command;
75 bson_t reply;
76 bson_error_t error;
77 bool res;
78
79 command = BCON_NEW ("explain",
80 "{",
81 "find",
82 BCON_UTF8 (COLLECTION_NAME),
83 "filter",
84 "{",
85 "x",
86 BCON_INT32 (1),
87 "}",
88 "}");
89 res = mongoc_collection_command_simple (
90 collection, command, NULL, &reply, &error);
91 if (!res) {
92 fprintf (stderr, "Error with explain: %s\n", error.message);
93 goto cleanup;
94 }
95
96 /* Do something with the reply */
97 print_res (&reply);
98
99 cleanup:
100 bson_destroy (&reply);
101 bson_destroy (command);
102 return res;
103 }
104
105
106 Running the Examples
107 common-operations.c.INDENT 0.0
108
109 /*
110 * Copyright 2016 MongoDB, Inc.
111 *
112 * Licensed under the Apache License, Version 2.0 (the "License");
113 * you may not use this file except in compliance with the License.
114 * You may obtain a copy of the License at
115 *
116 * http://www.apache.org/licenses/LICENSE-2.0
117 *
118 * Unless required by applicable law or agreed to in writing, software
119 * distributed under the License is distributed on an "AS IS" BASIS,
120 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
121 * See the License for the specific language governing permissions and
122 * limitations under the License.
123 */
124
125
126 #include <mongoc/mongoc.h>
127 #include <stdio.h>
128
129
130 const char *COLLECTION_NAME = "things";
131
132 #include "../doc-common-insert.c"
133 #include "explain.c"
134
135
136 int
137 main (int argc, char *argv[])
138 {
139 mongoc_database_t *database = NULL;
140 mongoc_client_t *client = NULL;
141 mongoc_collection_t *collection = NULL;
142 mongoc_uri_t *uri = NULL;
143 bson_error_t error;
144 char *host_and_port;
145 int res = 0;
146
147 if (argc < 2 || argc > 3) {
148 fprintf (stderr,
149 "usage: %s MONGOD-1-CONNECTION-STRING "
150 "[MONGOD-2-HOST-NAME:MONGOD-2-PORT]\n",
151 argv[0]);
152 fprintf (stderr,
153 "MONGOD-1-CONNECTION-STRING can be "
154 "of the following forms:\n");
155 fprintf (stderr, "localhost\t\t\t\tlocal machine\n");
156 fprintf (stderr, "localhost:27018\t\t\t\tlocal machine on port 27018\n");
157 fprintf (stderr,
158 "mongodb://user:pass@localhost:27017\t"
159 "local machine on port 27017, and authenticate with username "
160 "user and password pass\n");
161 return EXIT_FAILURE;
162 }
163
164 mongoc_init ();
165
166 if (strncmp (argv[1], "mongodb://", 10) == 0) {
167 host_and_port = bson_strdup (argv[1]);
168 } else {
169 host_and_port = bson_strdup_printf ("mongodb://%s", argv[1]);
170 }
171
172 uri = mongoc_uri_new_with_error (host_and_port, &error);
173 if (!uri) {
174 fprintf (stderr,
175 "failed to parse URI: %s\n"
176 "error message: %s\n",
177 host_and_port,
178 error.message);
179 res = EXIT_FAILURE;
180 goto cleanup;
181 }
182
183 client = mongoc_client_new_from_uri (uri);
184 if (!client) {
185 res = EXIT_FAILURE;
186 goto cleanup;
187 }
188
189 mongoc_client_set_error_api (client, 2);
190 database = mongoc_client_get_database (client, "test");
191 collection = mongoc_database_get_collection (database, COLLECTION_NAME);
192
193 printf ("Inserting data\n");
194 if (!insert_data (collection)) {
195 res = EXIT_FAILURE;
196 goto cleanup;
197 }
198
199 printf ("explain\n");
200 if (!explain (collection)) {
201 res = EXIT_FAILURE;
202 goto cleanup;
203 }
204
205 cleanup:
206 if (collection) {
207 mongoc_collection_destroy (collection);
208 }
209
210 if (database) {
211 mongoc_database_destroy (database);
212 }
213
214 if (client) {
215 mongoc_client_destroy (client);
216 }
217
218 if (uri) {
219 mongoc_uri_destroy (uri);
220 }
221
222 bson_free (host_and_port);
223 mongoc_cleanup ();
224 return res;
225 }
226
227
228First launch two separate instances of mongod (must be done from separate
229shells):
230
231 $ mongod
232
233 $ mkdir /tmp/db2
234 $ mongod --dbpath /tmp/db2 --port 27018 # second instance
235
236 Now compile and run the example program:
237
238 $ cd examples/common_operations/$ gcc -Wall -o example common-operations.c $(pkg-config --cflags --libs libmongoc-1.0)$ ./example localhost:27017 localhost:27018
239 Inserting data
240 explain
241 {
242 "executionStats" : {
243 "allPlansExecution" : [],
244 "executionStages" : {
245 "advanced" : 19,
246 "direction" : "forward" ,
247 "docsExamined" : 76,
248 "executionTimeMillisEstimate" : 0,
249 "filter" : {
250 "x" : {
251 "$eq" : 1
252 }
253 },
254 "invalidates" : 0,
255 "isEOF" : 1,
256 "nReturned" : 19,
257 "needTime" : 58,
258 "needYield" : 0,
259 "restoreState" : 0,
260 "saveState" : 0,
261 "stage" : "COLLSCAN" ,
262 "works" : 78
263 },
264 "executionSuccess" : true,
265 "executionTimeMillis" : 0,
266 "nReturned" : 19,
267 "totalDocsExamined" : 76,
268 "totalKeysExamined" : 0
269 },
270 "ok" : 1,
271 "queryPlanner" : {
272 "indexFilterSet" : false,
273 "namespace" : "test.things",
274 "parsedQuery" : {
275 "x" : {
276 "$eq" : 1
277 }
278 },
279 "plannerVersion" : 1,
280 "rejectedPlans" : [],
281 "winningPlan" : {
282 "direction" : "forward" ,
283 "filter" : {
284 "x" : {
285 "$eq" : 1
286 }
287 },
288 "stage" : "COLLSCAN"
289 }
290 },
291 "serverInfo" : {
292 "gitVersion" : "05552b562c7a0b3143a729aaa0838e558dc49b25" ,
293 "host" : "MacBook-Pro-57.local",
294 "port" : 27017,
295 "version" : "3.2.6"
296 }
297 }
298
300 The following guide contains information specific to certain types of
301 MongoDB configurations.
302
303 For an example of connecting to a simple standalone server, see the
304 Tutorial. To establish a connection with authentication options
305 enabled, see the Authentication page.
306
307 Connecting to a Replica Set
308 Connecting to a replica set is much like connecting to a standalone
309 MongoDB server. Simply specify the replica set name using the ?repli‐
310 caSet=myreplset URI option.
311
312 #include <bson/bson.h>
313 #include <mongoc/mongoc.h>
314
315 int
316 main (int argc, char *argv[])
317 {
318 mongoc_client_t *client;
319
320 mongoc_init ();
321
322 /* Create our MongoDB Client */
323 client = mongoc_client_new (
324 "mongodb://host01:27017,host02:27017,host03:27017/?replicaSet=myreplset");
325
326 /* Do some work */
327 /* TODO */
328
329 /* Clean up */
330 mongoc_client_destroy (client);
331 mongoc_cleanup ();
332
333 return 0;
334 }
335
336 TIP:
337 Multiple hostnames can be specified in the MongoDB connection string
338 URI, with a comma separating hosts in the seed list.
339
340 It is recommended to use a seed list of members of the replica set
341 to allow the driver to connect to any node.
342
343 Connecting to a Sharded Cluster
344 To connect to a sharded cluster, specify the mongos nodes the client
345 should connect to. The C Driver will automatically detect that it has
346 connected to a mongos sharding server.
347
348 If more than one hostname is specified, a seed list will be created to
349 attempt failover between the mongos instances.
350
351 WARNING:
352 Specifying the replicaSet parameter when connecting to a mongos
353 sharding server is invalid.
354
355 #include <bson/bson.h>
356 #include <mongoc/mongoc.h>
357
358 int
359 main (int argc, char *argv[])
360 {
361 mongoc_client_t *client;
362
363 mongoc_init ();
364
365 /* Create our MongoDB Client */
366 client = mongoc_client_new ("mongodb://myshard01:27017/");
367
368 /* Do something with client ... */
369
370 /* Free the client */
371 mongoc_client_destroy (client);
372
373 mongoc_cleanup ();
374
375 return 0;
376 }
377
378 Connecting to an IPv6 Address
379 The MongoDB C Driver will automatically resolve IPv6 addresses from
380 host names. However, to specify an IPv6 address directly, wrap the
381 address in [].
382
383 mongoc_uri_t *uri = mongoc_uri_new ("mongodb://[::1]:27017");
384
385 Connecting with IPv4 and IPv6
386 If connecting to a hostname that has both IPv4 and IPv6 DNS records,
387 the behavior follows RFC-6555. A connection to the IPv6 address is
388 attempted first. If IPv6 fails, then a connection is attempted to the
389 IPv4 address. If the connection attempt to IPv6 does not complete
390 within 250ms, then IPv4 is tried in parallel. Whichever succeeds con‐
391 nection first cancels the other. The successful DNS result is cached
392 for 10 minutes.
393
394 As a consequence, attempts to connect to a mongod only listening on
395 IPv4 may be delayed if there are both A (IPv4) and AAAA (IPv6) DNS
396 records associated with the host.
397
398 To avoid a delay, configure hostnames to match the MongoDB configura‐
399 tion. That is, only create an A record if the mongod is only listening
400 on IPv4.
401
402 Connecting to a UNIX Domain Socket
403 On UNIX-like systems, the C Driver can connect directly to a MongoDB
404 server using a UNIX domain socket. Pass the URL-encoded path to the
405 socket, which must be suffixed with .sock. For example, to connect to a
406 domain socket at /tmp/mongodb-27017.sock:
407
408 mongoc_uri_t *uri = mongoc_uri_new ("mongodb://%2Ftmp%2Fmongodb-27017.sock");
409
410 Include username and password like so:
411
412 mongoc_uri_t *uri = mongoc_uri_new ("mongodb://user:pass@%2Ftmp%2Fmongodb-27017.sock");
413
414 Connecting to a server over SSL
415 These are instructions for configuring TLS/SSL connections.
416
417 To run a server locally (on port 27017, for example):
418
419 $ mongod --port 27017 --sslMode requireSSL --sslPEMKeyFile server.pem --sslCAFile ca.pem
420
421 Add /?ssl=true to the end of a client URI.
422
423 mongoc_client_t *client = NULL;
424 client = mongoc_client_new ("mongodb://localhost:27017/?ssl=true");
425
426 MongoDB requires client certificates by default, unless the --sslAllow‐
427 ConnectionsWithoutCertificates is provided. The C Driver can be config‐
428 ured to present a client certificate using a mongoc_ssl_opt_t:
429
430 const mongoc_ssl_opt_t *ssl_default = mongoc_ssl_opt_get_default ();
431 mongoc_ssl_opt_t ssl_opts = { 0 };
432
433 /* optionally copy in a custom trust directory or file; otherwise the default is used. */
434 memcpy (&ssl_opts, ssl_default, sizeof ssl_opts);
435 ssl_opts.pem_file = "client.pem"
436
437 mongoc_client_set_ssl_opts (client, &ssl_opts);
438
439 The client certificate provided by pem_file must be issued by one of
440 the server trusted Certificate Authorities listed in --sslCAFile, or
441 issued by a CA in the native certificate store on the server when omit‐
442 ted.
443
444 To verify the server certificate against a specific CA, provide a PEM
445 armored file with a CA certificate, or concatenated list of CA certifi‐
446 cates using the ca_file option, or c_rehash directory structure of CAs,
447 pointed to using the ca_dir option. When no ca_file or ca_dir is pro‐
448 vided, the driver will use CAs provided by the native platform certifi‐
449 cate store.
450
451 See mongoc_ssl_opt_t for more information on the various SSL related
452 options.
453
454 Compressing data to and from MongoDB
455 MongoDB 3.4 added Snappy compression support, zlib compression in 3.6,
456 and zstd compression in 4.2. To enable compression support the client
457 must be configured with which compressors to use:
458
459 mongoc_client_t *client = NULL;
460 client = mongoc_client_new ("mongodb://localhost:27017/?compressors=snappy,zlib,zstd");
461
462 The compressors option specifies the priority order of compressors the
463 client wants to use. Messages are compressed if the client and server
464 share any compressors in common.
465
466 Note that the compressor used by the server might not be the same com‐
467 pressor as the client used. For example, if the client uses the con‐
468 nection string compressors=zlib,snappy the client will use zlib com‐
469 pression to send data (if possible), but the server might still reply
470 using snappy, depending on how the server was configured.
471
472 The driver must be built with zlib and/or snappy and/or zstd support to
473 enable compression support, any unknown (or not compiled in) compressor
474 value will be ignored. Note: to build with zstd requires cmake 3.12 or
475 higher.
476
477 Additional Connection Options
478 The full list of connection options can be found in the mongoc_uri_t
479 docs.
480
481 Certain socket/connection related options are not configurable:
482
483 ┌──────────────┬─────────────────────┬─────────────────────┐
484 │Option │ Description │ Value │
485 ├──────────────┼─────────────────────┼─────────────────────┤
486 │SO_KEEPALIVE │ TCP Keep Alive │ Enabled │
487 ├──────────────┼─────────────────────┼─────────────────────┤
488 │TCP_KEEPIDLE │ How long a connec‐ │ 300 seconds │
489 │ │ tion needs to │ │
490 │ │ remain idle before │ │
491 │ │ TCP starts sending │ │
492 │ │ keepalive probes │ │
493 ├──────────────┼─────────────────────┼─────────────────────┤
494 │TCP_KEEPINTVL │ The time in seconds │ 10 seconds │
495 │ │ between TCP probes │ │
496 ├──────────────┼─────────────────────┼─────────────────────┤
497 │TCP_KEEPCNT │ How many probes to │ 9 probes │
498 │ │ send, without │ │
499 │ │ acknowledgement, │ │
500 │ │ before dropping the │ │
501 │ │ connection │ │
502 ├──────────────┼─────────────────────┼─────────────────────┤
503 │TCP_NODELAY │ Send packets as │ Enabled (no buffer‐ │
504 │ │ soon as possible or │ ing) │
505 │ │ buffer small pack‐ │ │
506 │ │ ets (Nagle algo‐ │ │
507 │ │ rithm) │ │
508 └──────────────┴─────────────────────┴─────────────────────┘
509
511 The MongoDB C driver has two connection modes: single-threaded and
512 pooled. Single-threaded mode is optimized for embedding the driver
513 within languages like PHP. Multi-threaded programs should use pooled
514 mode: this mode minimizes the total connection count, and in pooled
515 mode a background thread monitors the MongoDB server topology, so the
516 program need not block to scan it.
517
518 Single Mode
519 In single mode, your program creates a mongoc_client_t directly:
520
521 mongoc_client_t *client = mongoc_client_new (
522 "mongodb://hostA,hostB/?replicaSet=my_rs");
523
524 The client connects on demand when your program first uses it for a
525 MongoDB operation. Using a non-blocking socket per server, it begins a
526 check on each server concurrently, and uses the asynchronous poll or
527 select function to receive events from the sockets, until all have
528 responded or timed out. Put another way, in single-threaded mode the C
529 Driver fans out to begin all checks concurrently, then fans in once all
530 checks have completed or timed out. Once the scan completes, the client
531 executes your program's operation and returns.
532
533 In single mode, the client re-scans the server topology roughly once
534 per minute. If more than a minute has elapsed since the previous scan,
535 the next operation on the client will block while the client completes
536 its scan. This interval is configurable with heartbeatFrequencyMS in
537 the connection string. (See mongoc_uri_t.)
538
539 A single client opens one connection per server in your topology: these
540 connections are used both for scanning the topology and performing nor‐
541 mal operations.
542
543 Pooled Mode
544 To activate pooled mode, create a mongoc_client_pool_t:
545
546 mongoc_uri_t *uri = mongoc_uri_new (
547 "mongodb://hostA,hostB/?replicaSet=my_rs");
548
549 mongoc_client_pool_t *pool = mongoc_client_pool_new (uri);
550
551 When your program first calls mongoc_client_pool_pop, the pool launches
552 a background thread for monitoring. The thread fans out and connects to
553 all servers in the connection string, using non-blocking sockets and a
554 simple event loop. As it receives ismaster responses from the servers,
555 it updates its view of the server topology. Each time the thread dis‐
556 covers a new server it begins connecting to it, and adds the new socket
557 to the list of non-blocking sockets in the event loop.
558
559 Each thread that executes MongoDB operations must check out a client
560 from the pool:
561
562 mongoc_client_t *client = mongoc_client_pool_pop (pool);
563
564 /* use the client for operations ... */
565
566 mongoc_client_pool_push (pool, client);
567
568 The mongoc_client_t object is not thread-safe, only the mon‐
569 goc_client_pool_t is.
570
571 When the driver is in pooled mode, your program's operations are
572 unblocked as soon as monitoring discovers a usable server. For example,
573 if a thread in your program is waiting to execute an "insert" on the
574 primary, it is unblocked as soon as the primary is discovered, rather
575 than waiting for all secondaries to be checked as well.
576
577 The pool opens one connection per server for monitoring, and each
578 client opens its own connection to each server it uses for application
579 operations. The background thread re-scans the server topology roughly
580 every 10 seconds. This interval is configurable with heartbeatFrequen‐
581 cyMS in the connection string. (See mongoc_uri_t.)
582
583 See connection_pool_options to configure pool size and behavior, and
584 see mongoc_client_pool_t for an extended example of a multi-threaded
585 program that uses the driver in pooled mode.
586
588 Handling Cursor Failures
589 Cursors exist on a MongoDB server. However, the mongoc_cursor_t struc‐
590 ture gives the local process a handle to the cursor. It is possible for
591 errors to occur on the server while iterating a cursor on the client.
592 Even a network partition may occur. This means that applications should
593 be robust in handling cursor failures.
594
595 While iterating cursors, you should check to see if an error has
596 occurred. See the following example for how to robustly check for
597 errors.
598
599 static void
600 print_all_documents (mongoc_collection_t *collection)
601 {
602 mongoc_cursor_t *cursor;
603 const bson_t *doc;
604 bson_error_t error;
605 bson_t query = BSON_INITIALIZER;
606 char *str;
607
608 cursor = mongoc_collection_find_with_opts (collection, query, NULL, NULL);
609
610 while (mongoc_cursor_next (cursor, &doc)) {
611 str = bson_as_canonical_extended_json (doc, NULL);
612 printf ("%s\n", str);
613 bson_free (str);
614 }
615
616 if (mongoc_cursor_error (cursor, &error)) {
617 fprintf (stderr, "Failed to iterate all documents: %s\n", error.message);
618 }
619
620 mongoc_cursor_destroy (cursor);
621 }
622
623 Destroying Server-Side Cursors
624 The MongoDB C driver will automatically destroy a server-side cursor
625 when mongoc_cursor_destroy() is called. Failure to call this function
626 when done with a cursor will leak memory client side as well as consume
627 extra memory server side. If the cursor was configured to never time‐
628 out, it will become a memory leak on the server.
629
630 Tailable Cursors
631 Tailable cursors are cursors that remain open even after they've
632 returned a final result. This way, if more documents are added to a
633 collection (i.e., to the cursor's result set), then you can continue to
634 call mongoc_cursor_next() to retrieve those additional results.
635
636 Here's a complete test case that demonstrates the use of tailable cur‐
637 sors.
638
639 NOTE:
640 Tailable cursors are for capped collections only.
641
642 An example to tail the oplog from a replica set. mongoc-tail.c.INDENT
643 0.0
644
645 #include <bson/bson.h>
646 #include <mongoc/mongoc.h>
647 #include <stdio.h>
648 #include <stdlib.h>
649
650 #ifdef _WIN32
651 #define sleep(_n) Sleep ((_n) *1000)
652 #endif
653
654
655 static void
656 print_bson (const bson_t *b)
657 {
658 char *str;
659
660 str = bson_as_canonical_extended_json (b, NULL);
661 fprintf (stdout, "%s\n", str);
662 bson_free (str);
663 }
664
665
666 static mongoc_cursor_t *
667 query_collection (mongoc_collection_t *collection, uint32_t last_time)
668 {
669 mongoc_cursor_t *cursor;
670 bson_t query;
671 bson_t gt;
672 bson_t opts;
673
674 BSON_ASSERT (collection);
675
676 bson_init (&query);
677 BSON_APPEND_DOCUMENT_BEGIN (&query, "ts", >);
678 BSON_APPEND_TIMESTAMP (>, "$gt", last_time, 0);
679 bson_append_document_end (&query, >);
680
681 bson_init (&opts);
682 BSON_APPEND_BOOL (&opts, "tailable", true);
683 BSON_APPEND_BOOL (&opts, "awaitData", true);
684
685 cursor = mongoc_collection_find_with_opts (collection, &query, &opts, NULL);
686
687 bson_destroy (&query);
688 bson_destroy (&opts);
689
690 return cursor;
691 }
692
693
694 static void
695 tail_collection (mongoc_collection_t *collection)
696 {
697 mongoc_cursor_t *cursor;
698 uint32_t last_time;
699 const bson_t *doc;
700 bson_error_t error;
701 bson_iter_t iter;
702
703 BSON_ASSERT (collection);
704
705 last_time = (uint32_t) time (NULL);
706
707 while (true) {
708 cursor = query_collection (collection, last_time);
709 while (!mongoc_cursor_error (cursor, &error) &&
710 mongoc_cursor_more (cursor)) {
711 if (mongoc_cursor_next (cursor, &doc)) {
712 if (bson_iter_init_find (&iter, doc, "ts") &&
713 BSON_ITER_HOLDS_TIMESTAMP (&iter)) {
714 bson_iter_timestamp (&iter, &last_time, NULL);
715 }
716 print_bson (doc);
717 }
718 }
719 if (mongoc_cursor_error (cursor, &error)) {
720 if (error.domain == MONGOC_ERROR_SERVER) {
721 fprintf (stderr, "%s\n", error.message);
722 exit (1);
723 }
724 }
725
726 mongoc_cursor_destroy (cursor);
727 sleep (1);
728 }
729 }
730
731
732 int
733 main (int argc, char *argv[])
734 {
735 mongoc_collection_t *collection;
736 mongoc_client_t *client;
737 mongoc_uri_t *uri;
738 bson_error_t error;
739
740 if (argc != 2) {
741 fprintf (stderr, "usage: %s MONGO_URI\n", argv[0]);
742 return EXIT_FAILURE;
743 }
744
745 mongoc_init ();
746
747 uri = mongoc_uri_new_with_error (argv[1], &error);
748 if (!uri) {
749 fprintf (stderr,
750 "failed to parse URI: %s\n"
751 "error message: %s\n",
752 argv[1],
753 error.message);
754 return EXIT_FAILURE;
755 }
756
757 client = mongoc_client_new_from_uri (uri);
758 if (!client) {
759 return EXIT_FAILURE;
760 }
761
762 mongoc_client_set_error_api (client, 2);
763
764 collection = mongoc_client_get_collection (client, "local", "oplog.rs");
765
766 tail_collection (collection);
767
768 mongoc_collection_destroy (collection);
769 mongoc_uri_destroy (uri);
770 mongoc_client_destroy (client);
771
772 return EXIT_SUCCESS;
773 }
774
775
776Let's compile and run this example against a replica set to see updates as
777they are made.
778
779 $ gcc -Wall -o mongoc-tail mongoc-tail.c $(pkg-config --cflags --libs libmongoc-1.0)
780 $ ./mongoc-tail mongodb://example.com/?replicaSet=myReplSet
781 {
782 "h" : -8458503739429355503,
783 "ns" : "test.test",
784 "o" : {
785 "_id" : {
786 "$oid" : "5372ab0a25164be923d10d50"
787 }
788 },
789 "op" : "i",
790 "ts" : {
791 "$timestamp" : {
792 "i" : 1,
793 "t" : 1400023818
794 }
795 },
796 "v" : 2
797 }
798
799 The line of output is a sample from performing db.test.insert({}) from
800 the mongo shell on the replica set.
801
802 See also mongoc_cursor_set_max_await_time_ms.
803
805 This tutorial explains how to take advantage of MongoDB C driver bulk
806 write operation features. Executing write operations in batches reduces
807 the number of network round trips, increasing write throughput.
808
809 Bulk Insert
810 First we need to fetch a bulk operation handle from the mongoc_collec‐
811 tion_t.
812
813 mongoc_bulk_operation_t *bulk =
814 mongoc_collection_create_bulk_operation_with_opts (collection, NULL);
815
816 We can now start inserting documents to the bulk operation. These will
817 be buffered until we execute the operation.
818
819 The bulk operation will coalesce insertions as a single batch for each
820 consecutive call to mongoc_bulk_operation_insert(). This creates a
821 pipelined effect when possible.
822
823 To execute the bulk operation and receive the result we call mon‐
824 goc_bulk_operation_execute(). bulk1.c.INDENT 0.0
825
826 #include <assert.h>
827 #include <mongoc/mongoc.h>
828 #include <stdio.h>
829
830 static void
831 bulk1 (mongoc_collection_t *collection)
832 {
833 mongoc_bulk_operation_t *bulk;
834 bson_error_t error;
835 bson_t *doc;
836 bson_t reply;
837 char *str;
838 bool ret;
839 int i;
840
841 bulk = mongoc_collection_create_bulk_operation_with_opts (collection, NULL);
842
843 for (i = 0; i < 10000; i++) {
844 doc = BCON_NEW ("i", BCON_INT32 (i));
845 mongoc_bulk_operation_insert (bulk, doc);
846 bson_destroy (doc);
847 }
848
849 ret = mongoc_bulk_operation_execute (bulk, &reply, &error);
850
851 str = bson_as_canonical_extended_json (&reply, NULL);
852 printf ("%s\n", str);
853 bson_free (str);
854
855 if (!ret) {
856 fprintf (stderr, "Error: %s\n", error.message);
857 }
858
859 bson_destroy (&reply);
860 mongoc_bulk_operation_destroy (bulk);
861 }
862
863 int
864 main (int argc, char *argv[])
865 {
866 mongoc_client_t *client;
867 mongoc_collection_t *collection;
868 const char *uri_string = "mongodb://localhost/?appname=bulk1-example";
869 mongoc_uri_t *uri;
870 bson_error_t error;
871
872 mongoc_init ();
873
874 uri = mongoc_uri_new_with_error (uri_string, &error);
875 if (!uri) {
876 fprintf (stderr,
877 "failed to parse URI: %s\n"
878 "error message: %s\n",
879 uri_string,
880 error.message);
881 return EXIT_FAILURE;
882 }
883
884 client = mongoc_client_new_from_uri (uri);
885 if (!client) {
886 return EXIT_FAILURE;
887 }
888
889 mongoc_client_set_error_api (client, 2);
890 collection = mongoc_client_get_collection (client, "test", "test");
891
892 bulk1 (collection);
893
894 mongoc_uri_destroy (uri);
895 mongoc_collection_destroy (collection);
896 mongoc_client_destroy (client);
897
898 mongoc_cleanup ();
899
900 return EXIT_SUCCESS;
901 }
902
903
904Example reply document:
905
906 {"nInserted" : 10000,
907 "nMatched" : 0,
908 "nModified" : 0,
909 "nRemoved" : 0,
910 "nUpserted" : 0,
911 "writeErrors" : []
912 "writeConcernErrors" : [] }
913
914 Mixed Bulk Write Operations
915 MongoDB C driver also supports executing mixed bulk write operations. A
916 batch of insert, update, and remove operations can be executed together
917 using the bulk write operations API.
918
919 Ordered Bulk Write Operations
920 Ordered bulk write operations are batched and sent to the server in the
921 order provided for serial execution. The reply document describes the
922 type and count of operations performed. bulk2.c.INDENT 0.0
923
924 #include <assert.h>
925 #include <mongoc/mongoc.h>
926 #include <stdio.h>
927
928 static void
929 bulk2 (mongoc_collection_t *collection)
930 {
931 mongoc_bulk_operation_t *bulk;
932 bson_error_t error;
933 bson_t *query;
934 bson_t *doc;
935 bson_t *opts;
936 bson_t reply;
937 char *str;
938 bool ret;
939 int i;
940
941 bulk = mongoc_collection_create_bulk_operation_with_opts (collection, NULL);
942
943 /* Remove everything */
944 query = bson_new ();
945 mongoc_bulk_operation_remove (bulk, query);
946 bson_destroy (query);
947
948 /* Add a few documents */
949 for (i = 1; i < 4; i++) {
950 doc = BCON_NEW ("_id", BCON_INT32 (i));
951 mongoc_bulk_operation_insert (bulk, doc);
952 bson_destroy (doc);
953 }
954
955 /* {_id: 1} => {$set: {foo: "bar"}} */
956 query = BCON_NEW ("_id", BCON_INT32 (1));
957 doc = BCON_NEW ("$set", "{", "foo", BCON_UTF8 ("bar"), "}");
958 mongoc_bulk_operation_update_many_with_opts (bulk, query, doc, NULL, &error);
959 bson_destroy (query);
960 bson_destroy (doc);
961
962 /* {_id: 4} => {'$inc': {'j': 1}} (upsert) */
963 opts = BCON_NEW ("upsert", BCON_BOOL (true));
964 query = BCON_NEW ("_id", BCON_INT32 (4));
965 doc = BCON_NEW ("$inc", "{", "j", BCON_INT32 (1), "}");
966 mongoc_bulk_operation_update_many_with_opts (bulk, query, doc, opts, &error);
967 bson_destroy (query);
968 bson_destroy (doc);
969 bson_destroy (opts);
970
971 /* replace {j:1} with {j:2} */
972 query = BCON_NEW ("j", BCON_INT32 (1));
973 doc = BCON_NEW ("j", BCON_INT32 (2));
974 mongoc_bulk_operation_replace_one_with_opts (bulk, query, doc, NULL, &error);
975 bson_destroy (query);
976 bson_destroy (doc);
977
978 ret = mongoc_bulk_operation_execute (bulk, &reply, &error);
979
980 str = bson_as_canonical_extended_json (&reply, NULL);
981 printf ("%s\n", str);
982 bson_free (str);
983
984 if (!ret) {
985 printf ("Error: %s\n", error.message);
986 }
987
988 bson_destroy (&reply);
989 mongoc_bulk_operation_destroy (bulk);
990 }
991
992 int
993 main (int argc, char *argv[])
994 {
995 mongoc_client_t *client;
996 mongoc_collection_t *collection;
997 const char *uri_string = "mongodb://localhost/?appname=bulk2-example";
998 mongoc_uri_t *uri;
999 bson_error_t error;
1000
1001 mongoc_init ();
1002
1003 uri = mongoc_uri_new_with_error (uri_string, &error);
1004 if (!uri) {
1005 fprintf (stderr,
1006 "failed to parse URI: %s\n"
1007 "error message: %s\n",
1008 uri_string,
1009 error.message);
1010 return EXIT_FAILURE;
1011 }
1012
1013 client = mongoc_client_new_from_uri (uri);
1014 if (!client) {
1015 return EXIT_FAILURE;
1016 }
1017
1018 mongoc_client_set_error_api (client, 2);
1019 collection = mongoc_client_get_collection (client, "test", "test");
1020
1021 bulk2 (collection);
1022
1023 mongoc_uri_destroy (uri);
1024 mongoc_collection_destroy (collection);
1025 mongoc_client_destroy (client);
1026
1027 mongoc_cleanup ();
1028
1029 return EXIT_SUCCESS;
1030 }
1031
1032
1033Example reply document:
1034
1035 { "nInserted" : 3,
1036 "nMatched" : 2,
1037 "nModified" : 2,
1038 "nRemoved" : 10000,
1039 "nUpserted" : 1,
1040 "upserted" : [{"index" : 5, "_id" : 4}],
1041 "writeErrors" : []
1042 "writeConcernErrors" : [] }
1043
1044 The index field in the upserted array is the 0-based index of the
1045 upsert operation; in this example, the sixth operation of the overall
1046 bulk operation was an upsert, so its index is 5.
1047
1048 Unordered Bulk Write Operations
1049 Unordered bulk write operations are batched and sent to the server in
1050 arbitrary order where they may be executed in parallel. Any errors that
1051 occur are reported after all operations are attempted.
1052
1053 In the next example the first and third operations fail due to the
1054 unique constraint on _id. Since we are doing unordered execution the
1055 second and fourth operations succeed. bulk3.c.INDENT 0.0
1056
1057 #include <assert.h>
1058 #include <mongoc/mongoc.h>
1059 #include <stdio.h>
1060
1061 static void
1062 bulk3 (mongoc_collection_t *collection)
1063 {
1064 bson_t opts = BSON_INITIALIZER;
1065 mongoc_bulk_operation_t *bulk;
1066 bson_error_t error;
1067 bson_t *query;
1068 bson_t *doc;
1069 bson_t reply;
1070 char *str;
1071 bool ret;
1072
1073 /* false indicates unordered */
1074 BSON_APPEND_BOOL (&opts, "ordered", false);
1075 bulk = mongoc_collection_create_bulk_operation_with_opts (collection, &opts);
1076 bson_destroy (&opts);
1077
1078 /* Add a document */
1079 doc = BCON_NEW ("_id", BCON_INT32 (1));
1080 mongoc_bulk_operation_insert (bulk, doc);
1081 bson_destroy (doc);
1082
1083 /* remove {_id: 2} */
1084 query = BCON_NEW ("_id", BCON_INT32 (2));
1085 mongoc_bulk_operation_remove_one (bulk, query);
1086 bson_destroy (query);
1087
1088 /* insert {_id: 3} */
1089 doc = BCON_NEW ("_id", BCON_INT32 (3));
1090 mongoc_bulk_operation_insert (bulk, doc);
1091 bson_destroy (doc);
1092
1093 /* replace {_id:4} {'i': 1} */
1094 query = BCON_NEW ("_id", BCON_INT32 (4));
1095 doc = BCON_NEW ("i", BCON_INT32 (1));
1096 mongoc_bulk_operation_replace_one (bulk, query, doc, false);
1097 bson_destroy (query);
1098 bson_destroy (doc);
1099
1100 ret = mongoc_bulk_operation_execute (bulk, &reply, &error);
1101
1102 str = bson_as_canonical_extended_json (&reply, NULL);
1103 printf ("%s\n", str);
1104 bson_free (str);
1105
1106 if (!ret) {
1107 printf ("Error: %s\n", error.message);
1108 }
1109
1110 bson_destroy (&reply);
1111 mongoc_bulk_operation_destroy (bulk);
1112 bson_destroy (&opts);
1113 }
1114
1115 int
1116 main (int argc, char *argv[])
1117 {
1118 mongoc_client_t *client;
1119 mongoc_collection_t *collection;
1120 const char *uri_string = "mongodb://localhost/?appname=bulk3-example";
1121 mongoc_uri_t *uri;
1122 bson_error_t error;
1123
1124 mongoc_init ();
1125
1126 uri = mongoc_uri_new_with_error (uri_string, &error);
1127 if (!uri) {
1128 fprintf (stderr,
1129 "failed to parse URI: %s\n"
1130 "error message: %s\n",
1131 uri_string,
1132 error.message);
1133 return EXIT_FAILURE;
1134 }
1135
1136 client = mongoc_client_new_from_uri (uri);
1137 if (!client) {
1138 return EXIT_FAILURE;
1139 }
1140
1141 mongoc_client_set_error_api (client, 2);
1142 collection = mongoc_client_get_collection (client, "test", "test");
1143
1144 bulk3 (collection);
1145
1146 mongoc_uri_destroy (uri);
1147 mongoc_collection_destroy (collection);
1148 mongoc_client_destroy (client);
1149
1150 mongoc_cleanup ();
1151
1152 return EXIT_SUCCESS;
1153 }
1154
1155
1156Example reply document:
1157
1158 { "nInserted" : 0,
1159 "nMatched" : 1,
1160 "nModified" : 1,
1161 "nRemoved" : 1,
1162 "nUpserted" : 0,
1163 "writeErrors" : [
1164 { "index" : 0,
1165 "code" : 11000,
1166 "errmsg" : "E11000 duplicate key error index: test.test.$_id_ dup key: { : 1 }" },
1167 { "index" : 2,
1168 "code" : 11000,
1169 "errmsg" : "E11000 duplicate key error index: test.test.$_id_ dup key: { : 3 }" } ],
1170 "writeConcernErrors" : [] }
1171
1172 Error: E11000 duplicate key error index: test.test.$_id_ dup key: { : 1 }
1173
1174 The bson_error_t domain is MONGOC_ERROR_COMMAND and its code is 11000.
1175
1176 Bulk Operation Bypassing Document Validation
1177 This feature is only available when using MongoDB 3.2 and later.
1178
1179 By default bulk operations are validated against the schema, if any is
1180 defined. In certain cases however it may be necessary to bypass the
1181 document validation. bulk5.c.INDENT 0.0
1182
1183 #include <assert.h>
1184 #include <mongoc/mongoc.h>
1185 #include <stdio.h>
1186
1187 static void
1188 bulk5_fail (mongoc_collection_t *collection)
1189 {
1190 mongoc_bulk_operation_t *bulk;
1191 bson_error_t error;
1192 bson_t *doc;
1193 bson_t reply;
1194 char *str;
1195 bool ret;
1196
1197 bulk = mongoc_collection_create_bulk_operation_with_opts (collection, NULL);
1198
1199 /* Two inserts */
1200 doc = BCON_NEW ("_id", BCON_INT32 (31));
1201 mongoc_bulk_operation_insert (bulk, doc);
1202 bson_destroy (doc);
1203
1204 doc = BCON_NEW ("_id", BCON_INT32 (32));
1205 mongoc_bulk_operation_insert (bulk, doc);
1206 bson_destroy (doc);
1207
1208 /* The above documents do not comply to the schema validation rules
1209 * we created previously, so this will result in an error */
1210 ret = mongoc_bulk_operation_execute (bulk, &reply, &error);
1211
1212 str = bson_as_canonical_extended_json (&reply, NULL);
1213 printf ("%s\n", str);
1214 bson_free (str);
1215
1216 if (!ret) {
1217 printf ("Error: %s\n", error.message);
1218 }
1219
1220 bson_destroy (&reply);
1221 mongoc_bulk_operation_destroy (bulk);
1222 }
1223
1224 static void
1225 bulk5_success (mongoc_collection_t *collection)
1226 {
1227 mongoc_bulk_operation_t *bulk;
1228 bson_error_t error;
1229 bson_t *doc;
1230 bson_t reply;
1231 char *str;
1232 bool ret;
1233
1234 bulk = mongoc_collection_create_bulk_operation_with_opts (collection, NULL);
1235
1236 /* Allow this document to bypass document validation.
1237 * NOTE: When authentication is enabled, the authenticated user must have
1238 * either the "dbadmin" or "restore" roles to bypass document validation */
1239 mongoc_bulk_operation_set_bypass_document_validation (bulk, true);
1240
1241 /* Two inserts */
1242 doc = BCON_NEW ("_id", BCON_INT32 (31));
1243 mongoc_bulk_operation_insert (bulk, doc);
1244 bson_destroy (doc);
1245
1246 doc = BCON_NEW ("_id", BCON_INT32 (32));
1247 mongoc_bulk_operation_insert (bulk, doc);
1248 bson_destroy (doc);
1249
1250 ret = mongoc_bulk_operation_execute (bulk, &reply, &error);
1251
1252 str = bson_as_canonical_extended_json (&reply, NULL);
1253 printf ("%s\n", str);
1254 bson_free (str);
1255
1256 if (!ret) {
1257 printf ("Error: %s\n", error.message);
1258 }
1259
1260 bson_destroy (&reply);
1261 mongoc_bulk_operation_destroy (bulk);
1262 }
1263
1264 int
1265 main (int argc, char *argv[])
1266 {
1267 bson_t *options;
1268 bson_error_t error;
1269 mongoc_client_t *client;
1270 mongoc_collection_t *collection;
1271 mongoc_database_t *database;
1272 const char *uri_string = "mongodb://localhost/?appname=bulk5-example";
1273 mongoc_uri_t *uri;
1274
1275 mongoc_init ();
1276
1277 uri = mongoc_uri_new_with_error (uri_string, &error);
1278 if (!uri) {
1279 fprintf (stderr,
1280 "failed to parse URI: %s\n"
1281 "error message: %s\n",
1282 uri_string,
1283 error.message);
1284 return EXIT_FAILURE;
1285 }
1286
1287 client = mongoc_client_new_from_uri (uri);
1288 if (!client) {
1289 return EXIT_FAILURE;
1290 }
1291
1292 mongoc_client_set_error_api (client, 2);
1293 database = mongoc_client_get_database (client, "testasdf");
1294
1295 /* Create schema validator */
1296 options = BCON_NEW (
1297 "validator", "{", "number", "{", "$gte", BCON_INT32 (5), "}", "}");
1298 collection =
1299 mongoc_database_create_collection (database, "collname", options, &error);
1300
1301 if (collection) {
1302 bulk5_fail (collection);
1303 bulk5_success (collection);
1304 mongoc_collection_destroy (collection);
1305 } else {
1306 fprintf (stderr, "Couldn't create collection: '%s'\n", error.message);
1307 }
1308
1309 bson_free (options);
1310 mongoc_uri_destroy (uri);
1311 mongoc_database_destroy (database);
1312 mongoc_client_destroy (client);
1313
1314 mongoc_cleanup ();
1315
1316 return EXIT_SUCCESS;
1317 }
1318
1319
1320Running the above example will result in:
1321
1322 { "nInserted" : 0,
1323 "nMatched" : 0,
1324 "nModified" : 0,
1325 "nRemoved" : 0,
1326 "nUpserted" : 0,
1327 "writeErrors" : [
1328 { "index" : 0,
1329 "code" : 121,
1330 "errmsg" : "Document failed validation" } ] }
1331
1332 Error: Document failed validation
1333
1334 { "nInserted" : 2,
1335 "nMatched" : 0,
1336 "nModified" : 0,
1337 "nRemoved" : 0,
1338 "nUpserted" : 0,
1339 "writeErrors" : [] }
1340
1341 The bson_error_t domain is MONGOC_ERROR_COMMAND.
1342
1343 Bulk Operation Write Concerns
1344 By default bulk operations are executed with the write_concern of the
1345 collection they are executed against. A custom write concern can be
1346 passed to the mongoc_collection_create_bulk_operation_with_opts()
1347 method. Write concern errors (e.g. wtimeout) will be reported after all
1348 operations are attempted, regardless of execution order.
1349 bulk4.c.INDENT 0.0
1350
1351 #include <assert.h>
1352 #include <mongoc/mongoc.h>
1353 #include <stdio.h>
1354
1355 static void
1356 bulk4 (mongoc_collection_t *collection)
1357 {
1358 bson_t opts = BSON_INITIALIZER;
1359 mongoc_write_concern_t *wc;
1360 mongoc_bulk_operation_t *bulk;
1361 bson_error_t error;
1362 bson_t *doc;
1363 bson_t reply;
1364 char *str;
1365 bool ret;
1366
1367 wc = mongoc_write_concern_new ();
1368 mongoc_write_concern_set_w (wc, 4);
1369 mongoc_write_concern_set_wtimeout_int64 (wc, 100); /* milliseconds */
1370 mongoc_write_concern_append (wc, &opts);
1371
1372 bulk = mongoc_collection_create_bulk_operation_with_opts (collection, &opts);
1373
1374 /* Two inserts */
1375 doc = BCON_NEW ("_id", BCON_INT32 (10));
1376 mongoc_bulk_operation_insert (bulk, doc);
1377 bson_destroy (doc);
1378
1379 doc = BCON_NEW ("_id", BCON_INT32 (11));
1380 mongoc_bulk_operation_insert (bulk, doc);
1381 bson_destroy (doc);
1382
1383 ret = mongoc_bulk_operation_execute (bulk, &reply, &error);
1384
1385 str = bson_as_canonical_extended_json (&reply, NULL);
1386 printf ("%s\n", str);
1387 bson_free (str);
1388
1389 if (!ret) {
1390 printf ("Error: %s\n", error.message);
1391 }
1392
1393 bson_destroy (&reply);
1394 mongoc_bulk_operation_destroy (bulk);
1395 mongoc_write_concern_destroy (wc);
1396 bson_destroy (&opts);
1397 }
1398
1399 int
1400 main (int argc, char *argv[])
1401 {
1402 mongoc_client_t *client;
1403 mongoc_collection_t *collection;
1404 const char *uri_string = "mongodb://localhost/?appname=bulk4-example";
1405 mongoc_uri_t *uri;
1406 bson_error_t error;
1407
1408 mongoc_init ();
1409
1410 uri = mongoc_uri_new_with_error (uri_string, &error);
1411 if (!uri) {
1412 fprintf (stderr,
1413 "failed to parse URI: %s\n"
1414 "error message: %s\n",
1415 uri_string,
1416 error.message);
1417 return EXIT_FAILURE;
1418 }
1419
1420 client = mongoc_client_new_from_uri (uri);
1421 if (!client) {
1422 return EXIT_FAILURE;
1423 }
1424
1425 mongoc_client_set_error_api (client, 2);
1426 collection = mongoc_client_get_collection (client, "test", "test");
1427
1428 bulk4 (collection);
1429
1430 mongoc_uri_destroy (uri);
1431 mongoc_collection_destroy (collection);
1432 mongoc_client_destroy (client);
1433
1434 mongoc_cleanup ();
1435
1436 return EXIT_SUCCESS;
1437 }
1438
1439
1440Example reply document and error message:
1441
1442 { "nInserted" : 2,
1443 "nMatched" : 0,
1444 "nModified" : 0,
1445 "nRemoved" : 0,
1446 "nUpserted" : 0,
1447 "writeErrors" : [],
1448 "writeConcernErrors" : [
1449 { "code" : 64,
1450 "errmsg" : "waiting for replication timed out" }
1451 ] }
1452
1453 Error: waiting for replication timed out
1454
1455 The bson_error_t domain is MONGOC_ERROR_WRITE_CONCERN if there are
1456 write concern errors and no write errors. Write errors indicate failed
1457 operations, so they take precedence over write concern errors, which
1458 mean merely that the write concern is not satisfied yet.
1459
1460 Setting Collation Order
1461 This feature is only available when using MongoDB 3.4 and later.
1462 bulk-collation.c.INDENT 0.0
1463
1464 #include <mongoc/mongoc.h>
1465 #include <stdio.h>
1466
1467 static void
1468 bulk_collation (mongoc_collection_t *collection)
1469 {
1470 mongoc_bulk_operation_t *bulk;
1471 bson_t *opts;
1472 bson_t *doc;
1473 bson_t *selector;
1474 bson_t *update;
1475 bson_error_t error;
1476 bson_t reply;
1477 char *str;
1478 uint32_t ret;
1479
1480 /* insert {_id: "one"} and {_id: "One"} */
1481 bulk = mongoc_collection_create_bulk_operation_with_opts (
1482 collection, NULL);
1483 doc = BCON_NEW ("_id", BCON_UTF8 ("one"));
1484 mongoc_bulk_operation_insert (bulk, doc);
1485 bson_destroy (doc);
1486
1487 doc = BCON_NEW ("_id", BCON_UTF8 ("One"));
1488 mongoc_bulk_operation_insert (bulk, doc);
1489 bson_destroy (doc);
1490
1491 /* "One" normally sorts before "one"; make "one" come first */
1492 opts = BCON_NEW ("collation",
1493 "{",
1494 "locale",
1495 BCON_UTF8 ("en_US"),
1496 "caseFirst",
1497 BCON_UTF8 ("lower"),
1498 "}");
1499
1500 /* set x=1 on the document with _id "One", which now sorts after "one" */
1501 update = BCON_NEW ("$set", "{", "x", BCON_INT64 (1), "}");
1502 selector = BCON_NEW ("_id", "{", "$gt", BCON_UTF8 ("one"), "}");
1503 mongoc_bulk_operation_update_one_with_opts (
1504 bulk, selector, update, opts, &error);
1505
1506 ret = mongoc_bulk_operation_execute (bulk, &reply, &error);
1507
1508 str = bson_as_canonical_extended_json (&reply, NULL);
1509 printf ("%s\n", str);
1510 bson_free (str);
1511
1512 if (!ret) {
1513 printf ("Error: %s\n", error.message);
1514 }
1515
1516 bson_destroy (&reply);
1517 bson_destroy (update);
1518 bson_destroy (selector);
1519 bson_destroy (opts);
1520 mongoc_bulk_operation_destroy (bulk);
1521 }
1522
1523 int
1524 main (int argc, char *argv[])
1525 {
1526 mongoc_client_t *client;
1527 mongoc_collection_t *collection;
1528 const char *uri_string = "mongodb://localhost/?appname=bulk-collation";
1529 mongoc_uri_t *uri;
1530 bson_error_t error;
1531
1532 mongoc_init ();
1533
1534 uri = mongoc_uri_new_with_error (uri_string, &error);
1535 if (!uri) {
1536 fprintf (stderr,
1537 "failed to parse URI: %s\n"
1538 "error message: %s\n",
1539 uri_string,
1540 error.message);
1541 return EXIT_FAILURE;
1542 }
1543
1544 client = mongoc_client_new_from_uri (uri);
1545 if (!client) {
1546 return EXIT_FAILURE;
1547 }
1548
1549 mongoc_client_set_error_api (client, 2);
1550 collection = mongoc_client_get_collection (client, "db", "collection");
1551 bulk_collation (collection);
1552
1553 mongoc_uri_destroy (uri);
1554 mongoc_collection_destroy (collection);
1555 mongoc_client_destroy (client);
1556
1557 mongoc_cleanup ();
1558
1559 return EXIT_SUCCESS;
1560 }
1561
1562
1563Running the above example will result in:
1564
1565 { "nInserted" : 2,
1566 "nMatched" : 1,
1567 "nModified" : 1,
1568 "nRemoved" : 0,
1569 "nUpserted" : 0,
1570 "writeErrors" : [ ]
1571 }
1572
1573 Unacknowledged Bulk Writes
1574 Set "w" to zero for an unacknowledged write. The driver sends unac‐
1575 knowledged writes using the legacy opcodes OP_INSERT, OP_UPDATE, and
1576 OP_DELETE. bulk6.c.INDENT 0.0
1577
1578 #include <mongoc/mongoc.h>
1579 #include <stdio.h>
1580
1581 static void
1582 bulk6 (mongoc_collection_t *collection)
1583 {
1584 bson_t opts = BSON_INITIALIZER;
1585 mongoc_write_concern_t *wc;
1586 mongoc_bulk_operation_t *bulk;
1587 bson_error_t error;
1588 bson_t *doc;
1589 bson_t *selector;
1590 bson_t reply;
1591 char *str;
1592 bool ret;
1593
1594 wc = mongoc_write_concern_new ();
1595 mongoc_write_concern_set_w (wc, 0);
1596 mongoc_write_concern_append (wc, &opts);
1597
1598 bulk = mongoc_collection_create_bulk_operation_with_opts (collection, &opts);
1599
1600 doc = BCON_NEW ("_id", BCON_INT32 (10));
1601 mongoc_bulk_operation_insert (bulk, doc);
1602 bson_destroy (doc);
1603
1604 selector = BCON_NEW ("_id", BCON_INT32 (11));
1605 mongoc_bulk_operation_remove_one (bulk, selector);
1606 bson_destroy (selector);
1607
1608 ret = mongoc_bulk_operation_execute (bulk, &reply, &error);
1609
1610 str = bson_as_canonical_extended_json (&reply, NULL);
1611 printf ("%s\n", str);
1612 bson_free (str);
1613
1614 if (!ret) {
1615 printf ("Error: %s\n", error.message);
1616 }
1617
1618 bson_destroy (&reply);
1619 mongoc_bulk_operation_destroy (bulk);
1620 mongoc_write_concern_destroy (wc);
1621 bson_destroy (&opts);
1622 }
1623
1624 int
1625 main (int argc, char *argv[])
1626 {
1627 mongoc_client_t *client;
1628 mongoc_collection_t *collection;
1629 const char *uri_string = "mongodb://localhost/?appname=bulk6-example";
1630 mongoc_uri_t *uri;
1631 bson_error_t error;
1632
1633 mongoc_init ();
1634
1635 uri = mongoc_uri_new_with_error (uri_string, &error);
1636 if (!uri) {
1637 fprintf (stderr,
1638 "failed to parse URI: %s\n"
1639 "error message: %s\n",
1640 uri_string,
1641 error.message);
1642 return EXIT_FAILURE;
1643 }
1644
1645 client = mongoc_client_new_from_uri (uri);
1646 if (!client) {
1647 return EXIT_FAILURE;
1648 }
1649
1650 mongoc_client_set_error_api (client, 2);
1651 collection = mongoc_client_get_collection (client, "test", "test");
1652
1653 bulk6 (collection);
1654
1655 mongoc_uri_destroy (uri);
1656 mongoc_collection_destroy (collection);
1657 mongoc_client_destroy (client);
1658
1659 mongoc_cleanup ();
1660
1661 return EXIT_SUCCESS;
1662 }
1663
1664
1665The reply document is empty:
1666
1667 { }
1668
1669 Further Reading
1670 See the Driver Bulk API Spec, which describes bulk write operations for
1671 all MongoDB drivers.
1672
1674 This document provides a number of practical examples that display the
1675 capabilities of the aggregation framework.
1676
1677 The Aggregations using the Zip Codes Data Set examples uses a publicly
1678 available data set of all zipcodes and populations in the United
1679 States. These data are available at: zips.json.
1680
1681 Requirements
1682 Let's check if everything is installed.
1683
1684 Use the following command to load zips.json data set into mongod
1685 instance:
1686
1687 $ mongoimport --drop -d test -c zipcodes zips.json
1688
1689 Let's use the MongoDB shell to verify that everything was imported suc‐
1690 cessfully.
1691
1692 $ mongo test
1693 connecting to: test
1694 > db.zipcodes.count()
1695 29467
1696 > db.zipcodes.findOne()
1697 {
1698 "_id" : "35004",
1699 "city" : "ACMAR",
1700 "loc" : [
1701 -86.51557,
1702 33.584132
1703 ],
1704 "pop" : 6055,
1705 "state" : "AL"
1706 }
1707
1708 Aggregations using the Zip Codes Data Set
1709 Each document in this collection has the following form:
1710
1711 {
1712 "_id" : "35004",
1713 "city" : "Acmar",
1714 "state" : "AL",
1715 "pop" : 6055,
1716 "loc" : [-86.51557, 33.584132]
1717 }
1718
1719 In these documents:
1720
1721 · The _id field holds the zipcode as a string.
1722
1723 · The city field holds the city name.
1724
1725 · The state field holds the two letter state abbreviation.
1726
1727 · The pop field holds the population.
1728
1729 · The loc field holds the location as a [latitude, longitude] array.
1730
1731 States with Populations Over 10 Million
1732 To get all states with a population greater than 10 million, use the
1733 following aggregation pipeline: aggregation1.c.INDENT 0.0
1734
1735 #include <mongoc/mongoc.h>
1736 #include <stdio.h>
1737
1738 static void
1739 print_pipeline (mongoc_collection_t *collection)
1740 {
1741 mongoc_cursor_t *cursor;
1742 bson_error_t error;
1743 const bson_t *doc;
1744 bson_t *pipeline;
1745 char *str;
1746
1747 pipeline = BCON_NEW ("pipeline",
1748 "[",
1749 "{",
1750 "$group",
1751 "{",
1752 "_id",
1753 "$state",
1754 "total_pop",
1755 "{",
1756 "$sum",
1757 "$pop",
1758 "}",
1759 "}",
1760 "}",
1761 "{",
1762 "$match",
1763 "{",
1764 "total_pop",
1765 "{",
1766 "$gte",
1767 BCON_INT32 (10000000),
1768 "}",
1769 "}",
1770 "}",
1771 "]");
1772
1773 cursor = mongoc_collection_aggregate (
1774 collection, MONGOC_QUERY_NONE, pipeline, NULL, NULL);
1775
1776 while (mongoc_cursor_next (cursor, &doc)) {
1777 str = bson_as_canonical_extended_json (doc, NULL);
1778 printf ("%s\n", str);
1779 bson_free (str);
1780 }
1781
1782 if (mongoc_cursor_error (cursor, &error)) {
1783 fprintf (stderr, "Cursor Failure: %s\n", error.message);
1784 }
1785
1786 mongoc_cursor_destroy (cursor);
1787 bson_destroy (pipeline);
1788 }
1789
1790 int
1791 main (int argc, char *argv[])
1792 {
1793 mongoc_client_t *client;
1794 mongoc_collection_t *collection;
1795 const char *uri_string =
1796 "mongodb://localhost:27017/?appname=aggregation-example";
1797 mongoc_uri_t *uri;
1798 bson_error_t error;
1799
1800 mongoc_init ();
1801
1802 uri = mongoc_uri_new_with_error (uri_string, &error);
1803 if (!uri) {
1804 fprintf (stderr,
1805 "failed to parse URI: %s\n"
1806 "error message: %s\n",
1807 uri_string,
1808 error.message);
1809 return EXIT_FAILURE;
1810 }
1811
1812 client = mongoc_client_new_from_uri (uri);
1813 if (!client) {
1814 return EXIT_FAILURE;
1815 }
1816
1817 mongoc_client_set_error_api (client, 2);
1818 collection = mongoc_client_get_collection (client, "test", "zipcodes");
1819
1820 print_pipeline (collection);
1821
1822 mongoc_uri_destroy (uri);
1823 mongoc_collection_destroy (collection);
1824 mongoc_client_destroy (client);
1825
1826 mongoc_cleanup ();
1827
1828 return EXIT_SUCCESS;
1829 }
1830
1831
1832You should see a result like the following:
1833
1834 { "_id" : "PA", "total_pop" : 11881643 }
1835 { "_id" : "OH", "total_pop" : 10847115 }
1836 { "_id" : "NY", "total_pop" : 17990455 }
1837 { "_id" : "FL", "total_pop" : 12937284 }
1838 { "_id" : "TX", "total_pop" : 16986510 }
1839 { "_id" : "IL", "total_pop" : 11430472 }
1840 { "_id" : "CA", "total_pop" : 29760021 }
1841
1842 The above aggregation pipeline is build from two pipeline operators:
1843 $group and $match.
1844
1845 The $group pipeline operator requires _id field where we specify group‐
1846 ing; remaining fields specify how to generate composite value and must
1847 use one of the group aggregation functions: $addToSet, $first, $last,
1848 $max, $min, $avg, $push, $sum. The $match pipeline operator syntax is
1849 the same as the read operation query syntax.
1850
1851 The $group process reads all documents and for each state it creates a
1852 separate document, for example:
1853
1854 { "_id" : "WA", "total_pop" : 4866692 }
1855
1856 The total_pop field uses the $sum aggregation function to sum the val‐
1857 ues of all pop fields in the source documents.
1858
1859 Documents created by $group are piped to the $match pipeline operator.
1860 It returns the documents with the value of total_pop field greater than
1861 or equal to 10 million.
1862
1863 Average City Population by State
1864 To get the first three states with the greatest average population per
1865 city, use the following aggregation:
1866
1867 pipeline = BCON_NEW ("pipeline", "[",
1868 "{", "$group", "{", "_id", "{", "state", "$state", "city", "$city", "}", "pop", "{", "$sum", "$pop", "}", "}", "}",
1869 "{", "$group", "{", "_id", "$_id.state", "avg_city_pop", "{", "$avg", "$pop", "}", "}", "}",
1870 "{", "$sort", "{", "avg_city_pop", BCON_INT32 (-1), "}", "}",
1871 "{", "$limit", BCON_INT32 (3) "}",
1872 "]");
1873
1874 This aggregate pipeline produces:
1875
1876 { "_id" : "DC", "avg_city_pop" : 303450.0 }
1877 { "_id" : "FL", "avg_city_pop" : 27942.29805615551 }
1878 { "_id" : "CA", "avg_city_pop" : 27735.341099720412 }
1879
1880 The above aggregation pipeline is build from three pipeline operators:
1881 $group, $sort and $limit.
1882
1883 The first $group operator creates the following documents:
1884
1885 { "_id" : { "state" : "WY", "city" : "Smoot" }, "pop" : 414 }
1886
1887 Note, that the $group operator can't use nested documents except the
1888 _id field.
1889
1890 The second $group uses these documents to create the following docu‐
1891 ments:
1892
1893 { "_id" : "FL", "avg_city_pop" : 27942.29805615551 }
1894
1895 These documents are sorted by the avg_city_pop field in descending
1896 order. Finally, the $limit pipeline operator returns the first 3 docu‐
1897 ments from the sorted set.
1898
1900 This document provides some practical, simple, examples to demonstrate
1901 the distinct and mapReduce commands.
1902
1903 Setup
1904 First we'll write some code to insert sample data: doc-com‐
1905 mon-insert.c.INDENT 0.0
1906
1907 /* Don't try to compile this file on its own. It's meant to be #included
1908 by example code */
1909
1910 /* Insert some sample data */
1911 bool
1912 insert_data (mongoc_collection_t *collection)
1913 {
1914 mongoc_bulk_operation_t *bulk;
1915 enum N { ndocs = 4 };
1916 bson_t *docs[ndocs];
1917 bson_error_t error;
1918 int i = 0;
1919 bool ret;
1920
1921 bulk = mongoc_collection_create_bulk_operation_with_opts (collection, NULL);
1922
1923 docs[0] = BCON_NEW ("x", BCON_DOUBLE (1.0), "tags", "[", "dog", "cat", "]");
1924 docs[1] = BCON_NEW ("x", BCON_DOUBLE (2.0), "tags", "[", "cat", "]");
1925 docs[2] = BCON_NEW (
1926 "x", BCON_DOUBLE (2.0), "tags", "[", "mouse", "cat", "dog", "]");
1927 docs[3] = BCON_NEW ("x", BCON_DOUBLE (3.0), "tags", "[", "]");
1928
1929 for (i = 0; i < ndocs; i++) {
1930 mongoc_bulk_operation_insert (bulk, docs[i]);
1931 bson_destroy (docs[i]);
1932 docs[i] = NULL;
1933 }
1934
1935 ret = mongoc_bulk_operation_execute (bulk, NULL, &error);
1936
1937 if (!ret) {
1938 fprintf (stderr, "Error inserting data: %s\n", error.message);
1939 }
1940
1941 mongoc_bulk_operation_destroy (bulk);
1942 return ret;
1943 }
1944
1945 /* A helper which we'll use a lot later on */
1946 void
1947 print_res (const bson_t *reply)
1948 {
1949 char *str;
1950 BSON_ASSERT (reply);
1951 str = bson_as_canonical_extended_json (reply, NULL);
1952 printf ("%s\n", str);
1953 bson_free (str);
1954 }
1955
1956
1957 distinct command
1958 This is how to use the distinct command to get the distinct values of x
1959 which are greater than 1: distinct.c.INDENT 0.0
1960
1961 bool
1962 distinct (mongoc_database_t *database)
1963 {
1964 bson_t *command;
1965 bson_t reply;
1966 bson_error_t error;
1967 bool res;
1968 bson_iter_t iter;
1969 bson_iter_t array_iter;
1970 double val;
1971
1972 command = BCON_NEW ("distinct",
1973 BCON_UTF8 (COLLECTION_NAME),
1974 "key",
1975 BCON_UTF8 ("x"),
1976 "query",
1977 "{",
1978 "x",
1979 "{",
1980 "$gt",
1981 BCON_DOUBLE (1.0),
1982 "}",
1983 "}");
1984 res =
1985 mongoc_database_command_simple (database, command, NULL, &reply, &error);
1986 if (!res) {
1987 fprintf (stderr, "Error with distinct: %s\n", error.message);
1988 goto cleanup;
1989 }
1990
1991 /* Do something with reply (in this case iterate through the values) */
1992 if (!(bson_iter_init_find (&iter, &reply, "values") &&
1993 BSON_ITER_HOLDS_ARRAY (&iter) &&
1994 bson_iter_recurse (&iter, &array_iter))) {
1995 fprintf (stderr, "Couldn't extract \"values\" field from response\n");
1996 goto cleanup;
1997 }
1998
1999 while (bson_iter_next (&array_iter)) {
2000 if (BSON_ITER_HOLDS_DOUBLE (&array_iter)) {
2001 val = bson_iter_double (&array_iter);
2002 printf ("Next double: %f\n", val);
2003 }
2004 }
2005
2006 cleanup:
2007 /* cleanup */
2008 bson_destroy (command);
2009 bson_destroy (&reply);
2010 return res;
2011 }
2012
2013
2014 mapReduce - basic example
2015 A simple example using the map reduce framework. It simply adds up the
2016 number of occurrences of each "tag".
2017
2018 First define the map and reduce functions: constants.c.INDENT 0.0
2019
2020 const char *const COLLECTION_NAME = "things";
2021
2022 /* Our map function just emits a single (key, 1) pair for each tag
2023 in the array: */
2024 const char *const MAPPER = "function () {"
2025 "this.tags.forEach(function(z) {"
2026 "emit(z, 1);"
2027 "});"
2028 "}";
2029
2030 /* The reduce function sums over all of the emitted values for a
2031 given key: */
2032 const char *const REDUCER = "function (key, values) {"
2033 "var total = 0;"
2034 "for (var i = 0; i < values.length; i++) {"
2035 "total += values[i];"
2036 "}"
2037 "return total;"
2038 "}";
2039 /* Note We can't just return values.length as the reduce function
2040 might be called iteratively on the results of other reduce
2041 steps. */
2042
2043
2044Run the mapReduce command. Use the generic command helpers (e.g. mongoc_data‐
2047operations. If retryable reads are enabled, those operations will retry once
2048on a retryable error, giving undesirable behavior for mapReduce.
2049map-reduce-basic.c.INDENT 0.0
2050
2051 bool
2052 map_reduce_basic (mongoc_database_t *database)
2053 {
2054 bson_t reply;
2055 bson_t *command;
2056 bool res;
2057 bson_error_t error;
2058 mongoc_cursor_t *cursor;
2059 const bson_t *doc;
2060
2061 bool query_done = false;
2062
2063 const char *out_collection_name = "outCollection";
2064 mongoc_collection_t *out_collection;
2065
2066 /* Empty find query */
2067 bson_t find_query = BSON_INITIALIZER;
2068
2069 /* Construct the mapReduce command */
2070
2071 /* Other arguments can also be specified here, like "query" or
2072 "limit" and so on */
2073 command = BCON_NEW ("mapReduce",
2074 BCON_UTF8 (COLLECTION_NAME),
2075 "map",
2076 BCON_CODE (MAPPER),
2077 "reduce",
2078 BCON_CODE (REDUCER),
2079 "out",
2080 BCON_UTF8 (out_collection_name));
2081 res =
2082 mongoc_database_command_simple (database, command, NULL, &reply, &error);
2083
2084 if (!res) {
2085 fprintf (stderr, "MapReduce failed: %s\n", error.message);
2086 goto cleanup;
2087 }
2088
2089 /* Do something with the reply (it doesn't contain the mapReduce results) */
2090 print_res (&reply);
2091
2092 /* Now we'll query outCollection to see what the results are */
2093 out_collection =
2094 mongoc_database_get_collection (database, out_collection_name);
2095 cursor = mongoc_collection_find_with_opts (
2096 out_collection, &find_query, NULL, NULL);
2097 query_done = true;
2098
2099 /* Do something with the results */
2100 while (mongoc_cursor_next (cursor, &doc)) {
2101 print_res (doc);
2102 }
2103
2104 if (mongoc_cursor_error (cursor, &error)) {
2105 fprintf (stderr, "ERROR: %s\n", error.message);
2106 res = false;
2107 goto cleanup;
2108 }
2109
2110 cleanup:
2111 /* cleanup */
2112 if (query_done) {
2113 mongoc_cursor_destroy (cursor);
2114 mongoc_collection_destroy (out_collection);
2115 }
2116
2117 bson_destroy (&reply);
2118 bson_destroy (command);
2119
2120 return res;
2121 }
2122
2123
2124 mapReduce - more complicated example
2125 You must have replica set running for this.
2126
2127 In this example we contact a secondary in the replica set and do an
2128 "inline" map reduce, so the results are returned immediately:
2129 map-reduce-advanced.c.INDENT 0.0
2130
2131 bool
2132 map_reduce_advanced (mongoc_database_t *database)
2133 {
2134 bson_t *command;
2135 bson_error_t error;
2136 bool res = true;
2137 mongoc_cursor_t *cursor;
2138 mongoc_read_prefs_t *read_pref;
2139 const bson_t *doc;
2140
2141 /* Construct the mapReduce command */
2142 /* Other arguments can also be specified here, like "query" or "limit"
2143 and so on */
2144
2145 /* Read the results inline from a secondary replica */
2146 command = BCON_NEW ("mapReduce",
2147 BCON_UTF8 (COLLECTION_NAME),
2148 "map",
2149 BCON_CODE (MAPPER),
2150 "reduce",
2151 BCON_CODE (REDUCER),
2152 "out",
2153 "{",
2154 "inline",
2155 "1",
2156 "}");
2157
2158 read_pref = mongoc_read_prefs_new (MONGOC_READ_SECONDARY);
2159 cursor = mongoc_database_command (
2160 database, MONGOC_QUERY_NONE, 0, 0, 0, command, NULL, read_pref);
2161
2162 /* Do something with the results */
2163 while (mongoc_cursor_next (cursor, &doc)) {
2164 print_res (doc);
2165 }
2166
2167 if (mongoc_cursor_error (cursor, &error)) {
2168 fprintf (stderr, "ERROR: %s\n", error.message);
2169 res = false;
2170 }
2171
2172 mongoc_cursor_destroy (cursor);
2173 mongoc_read_prefs_destroy (read_pref);
2174 bson_destroy (command);
2175
2176 return res;
2177 }
2178
2179
2180 Running the Examples
2181 Here's how to run the example code basic-aggregation.c.INDENT 0.0
2182
2183 /*
2184 * Copyright 2016 MongoDB, Inc.
2185 *
2186 * Licensed under the Apache License, Version 2.0 (the "License");
2187 * you may not use this file except in compliance with the License.
2188 * You may obtain a copy of the License at
2189 *
2190 * http://www.apache.org/licenses/LICENSE-2.0
2191 *
2192 * Unless required by applicable law or agreed to in writing, software
2193 * distributed under the License is distributed on an "AS IS" BASIS,
2194 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2195 * See the License for the specific language governing permissions and
2196 * limitations under the License.
2197 */
2198
2199
2200 #include <mongoc/mongoc.h>
2201 #include <stdio.h>
2202
2203
2204 #include "constants.c"
2205
2206 #include "../doc-common-insert.c"
2207 #include "distinct.c"
2208 #include "map-reduce-basic.c"
2209 #include "map-reduce-advanced.c"
2210
2211
2212 int
2213 main (int argc, char *argv[])
2214 {
2215 mongoc_database_t *database = NULL;
2216 mongoc_client_t *client = NULL;
2217 mongoc_collection_t *collection = NULL;
2218 mongoc_uri_t *uri = NULL;
2219 bson_error_t error;
2220 char *host_and_port = NULL;
2221 int exit_code = EXIT_FAILURE;
2222
2223 if (argc != 2) {
2224 fprintf (stderr, "usage: %s CONNECTION-STRING\n", argv[0]);
2225 fprintf (stderr,
2226 "the connection string can be of the following forms:\n");
2227 fprintf (stderr, "localhost\t\t\t\tlocal machine\n");
2228 fprintf (stderr, "localhost:27018\t\t\t\tlocal machine on port 27018\n");
2229 fprintf (stderr,
2230 "mongodb://user:pass@localhost:27017\t"
2231 "local machine on port 27017, and authenticate with username "
2232 "user and password pass\n");
2233 return exit_code;
2234 }
2235
2236 mongoc_init ();
2237
2238 if (strncmp (argv[1], "mongodb://", 10) == 0) {
2239 host_and_port = bson_strdup (argv[1]);
2240 } else {
2241 host_and_port = bson_strdup_printf ("mongodb://%s", argv[1]);
2242 }
2243
2244 uri = mongoc_uri_new_with_error (host_and_port, &error);
2245 if (!uri) {
2246 fprintf (stderr,
2247 "failed to parse URI: %s\n"
2248 "error message: %s\n",
2249 host_and_port,
2250 error.message);
2251 goto cleanup;
2252 }
2253
2254 client = mongoc_client_new_from_uri (uri);
2255 if (!client) {
2256 goto cleanup;
2257 }
2258
2259 mongoc_client_set_error_api (client, 2);
2260 database = mongoc_client_get_database (client, "test");
2261 collection = mongoc_database_get_collection (database, COLLECTION_NAME);
2262
2263 printf ("Inserting data\n");
2264 if (!insert_data (collection)) {
2265 goto cleanup;
2266 }
2267
2268 printf ("distinct\n");
2269 if (!distinct (database)) {
2270 goto cleanup;
2271 }
2272
2273 printf ("map reduce\n");
2274 if (!map_reduce_basic (database)) {
2275 goto cleanup;
2276 }
2277
2278 printf ("more complicated map reduce\n");
2279 if (!map_reduce_advanced (database)) {
2280 goto cleanup;
2281 }
2282
2283 exit_code = EXIT_SUCCESS;
2284
2285 cleanup:
2286 if (collection) {
2287 mongoc_collection_destroy (collection);
2288 }
2289
2290 if (database) {
2291 mongoc_database_destroy (database);
2292 }
2293
2294 if (client) {
2295 mongoc_client_destroy (client);
2296 }
2297
2298 if (uri) {
2299 mongoc_uri_destroy (uri);
2300 }
2301
2302 if (host_and_port) {
2303 bson_free (host_and_port);
2304 }
2305
2306 mongoc_cleanup ();
2307 return exit_code;
2308 }
2309
2310
2311If you want to try the advanced map reduce example with a secondary, start a
2312replica set (instructions for how to do this can be found here).
2313
2314Otherwise, just start an instance of MongoDB:
2315
2316 $ mongod
2317
2318 Now compile and run the example program:
2319
2320 $ cd examples/basic_aggregation/
2321 $ gcc -Wall -o agg-example basic-aggregation.c $(pkg-config --cflags --libs libmongoc-1.0)
2322 $ ./agg-example localhost
2323
2324 Inserting data
2325 distinct
2326 Next double: 2.000000
2327 Next double: 3.000000
2328 map reduce
2329 { "result" : "outCollection", "timeMillis" : 155, "counts" : { "input" : 84, "emit" : 126, "reduce" : 3, "output" : 3 }, "ok" : 1 }
2330 { "_id" : "cat", "value" : 63 }
2331 { "_id" : "dog", "value" : 42 }
2332 { "_id" : "mouse", "value" : 21 }
2333 more complicated map reduce
2334 { "results" : [ { "_id" : "cat", "value" : 63 }, { "_id" : "dog", "value" : 42 }, { "_id" : "mouse", "value" : 21 } ], "timeMillis" : 14, "counts" : { "input" : 84, "emit" : 126, "reduce" : 3, "output" : 3 }, "ok" : 1 }
2335
2337 Download and install libmongoc on your system, then open Visual Studio,
2338 select "File→New→Project...", and create a new Win32 Console Applica‐
2339 tion. [image]
2340
2341 Remember to switch the platform from 32-bit to 64-bit: [image]
2342
2343 Right-click on your console application in the Solution Explorer and
2344 select "Properties". Choose to edit properties for "All Configura‐
2345 tions", expand the "C/C++" options and choose "General". Add to the
2346 "Additional Include Directories" these paths:
2347
2348 C:\mongo-c-driver\include\libbson-1.0
2349 C:\mongo-c-driver\include\libmongoc-1.0
2350 [image]
2351
2352 (If you chose a different CMAKE_INSTALL_PREFIX when you ran CMake, your
2353 include paths will be different.)
2354
2355 Also in the Properties dialog, expand the "Linker" options and choose
2356 "Input", and add to the "Additional Dependencies" these libraries:
2357
2358 C:\mongo-c-driver\lib\bson-1.0.lib
2359 C:\mongo-c-driver\lib\mongoc-1.0.lib
2360 [image]
2361
2362 Adding these libraries as dependencies provides linker symbols to build
2363 your application, but to actually run it, libbson's and libmongoc's
2364 DLLs must be in your executable path. Select "Debugging" in the Proper‐
2365 ties dialog, and set the "Environment" option to:
2366
2367 PATH=c:/mongo-c-driver/bin
2368 [image]
2369
2370 Finally, include "mongoc/mongoc.h" in your project's "stdafx.h":
2371
2372 #include <mongoc/mongoc.h>
2373
2374 Static linking
2375 Following the instructions above, you have dynamically linked your
2376 application to the libbson and libmongoc DLLs. This is usually the
2377 right choice. If you want to link statically instead, update your
2378 "Additional Dependencies" list by removing bson-1.0.lib and mon‐
2379 goc-1.0.lib and replacing them with these libraries:
2380
2381 C:\mongo-c-driver\lib\bson-static-1.0.lib
2382 C:\mongo-c-driver\lib\mongoc-static-1.0.lib
2383 ws2_32.lib
2384 Secur32.lib
2385 Crypt32.lib
2386 BCrypt.lib
2387 [image]
2388
2389 (To explain the purpose of each library: bson-static-1.0.lib and mon‐
2390 goc-static-1.0.lib are static archives of the driver code. The socket
2391 library ws2_32 is required by libbson, which uses the socket routine
2392 gethostname to help guarantee ObjectId uniqueness. The BCrypt library
2393 is used by libmongoc for SSL connections to MongoDB, and Secur32 and
2394 Crypt32 are required for enterprise authentication methods like Ker‐
2395 beros.)
2396
2397 Finally, define two preprocessor symbols before including mongoc/mon‐
2398 goc.h in your stdafx.h:
2399
2400 #define BSON_STATIC
2401 #define MONGOC_STATIC
2402 #include <mongoc/mongoc.h>
2403
2404 Making these changes to your project is only required for static link‐
2405 ing; for most people, the dynamic-linking instructions above are pre‐
2406 ferred.
2407
2408 Next Steps
2409 Now you can build and debug applications in Visual Studio that use
2410 libbson and libmongoc. Proceed to making-a-connection in the tutorial
2411 to learn how connect to MongoDB and perform operations.
2412
2414 To create indexes on a MongoDB collection, execute the createIndexes
2415 command with a command function like mongoc_database_write_com‐
2416 mand_with_opts or mongoc_collection_write_command_with_opts. See the
2417 MongoDB Manual entry for the createIndexes command for details.
2418
2419 Example
2420 example-create-indexes.c.INDENT 0.0
2421
2422 /* gcc example-create-indexes.c -o example-create-indexes $(pkg-config --cflags
2423 * --libs libmongoc-1.0) */
2424
2425 /* ./example-create-indexes [CONNECTION_STRING [COLLECTION_NAME]] */
2426
2427 #include <mongoc/mongoc.h>
2428 #include <stdio.h>
2429 #include <stdlib.h>
2430
2431 int
2432 main (int argc, char *argv[])
2433 {
2434 mongoc_client_t *client;
2435 const char *uri_string =
2436 "mongodb://127.0.0.1/?appname=create-indexes-example";
2437 mongoc_uri_t *uri;
2438 mongoc_database_t *db;
2439 const char *collection_name = "test";
2440 bson_t keys;
2441 char *index_name;
2442 bson_t *create_indexes;
2443 bson_t reply;
2444 char *reply_str;
2445 bson_error_t error;
2446 bool r;
2447
2448 mongoc_init ();
2449
2450 if (argc > 1) {
2451 uri_string = argv[1];
2452 }
2453
2454 if (argc > 2) {
2455 collection_name = argv[2];
2456 }
2457
2458 uri = mongoc_uri_new_with_error (uri_string, &error);
2459 if (!uri) {
2460 fprintf (stderr,
2461 "failed to parse URI: %s\n"
2462 "error message: %s\n",
2463 uri_string,
2464 error.message);
2465 return EXIT_FAILURE;
2466 }
2467
2468 client = mongoc_client_new_from_uri (uri);
2469 if (!client) {
2470 return EXIT_FAILURE;
2471 }
2472
2473 mongoc_client_set_error_api (client, 2);
2474 db = mongoc_client_get_database (client, "test");
2475
2476 /* ascending index on field "x" */
2477 bson_init (&keys);
2478 BSON_APPEND_INT32 (&keys, "x", 1);
2479 index_name = mongoc_collection_keys_to_index_string (&keys);
2480 create_indexes = BCON_NEW ("createIndexes",
2481 BCON_UTF8 (collection_name),
2482 "indexes",
2483 "[",
2484 "{",
2485 "key",
2486 BCON_DOCUMENT (&keys),
2487 "name",
2488 BCON_UTF8 (index_name),
2489 "}",
2490 "]");
2491
2492 r = mongoc_database_write_command_with_opts (
2493 db, create_indexes, NULL /* opts */, &reply, &error);
2494
2495 reply_str = bson_as_json (&reply, NULL);
2496 printf ("%s\n", reply_str);
2497
2498 if (!r) {
2499 fprintf (stderr, "Error in createIndexes: %s\n", error.message);
2500 }
2501
2502 bson_free (index_name);
2503 bson_free (reply_str);
2504 bson_destroy (&reply);
2505 bson_destroy (create_indexes);
2506 mongoc_database_destroy (db);
2507 mongoc_uri_destroy (uri);
2508 mongoc_client_destroy (client);
2509
2510 mongoc_cleanup ();
2511
2512 return r ? EXIT_SUCCESS : EXIT_FAILURE;
2513 }
2514
2515
2517 GDB
2518 This repository contains a .gdbinit file that contains helper functions
2519 to aid debugging of data structures. GDB will load this file
2520 automatically if you have added the directory which contains the
2521 .gdbinit file to GDB's auto-load safe-path, and you start GDB from the
2522 directory which holds the .gdbinit file.
2523
2524 You can see the safe-path with show auto-load safe-path on a GDB
2525 prompt. You can configure it by setting it in ~/.gdbinit with:
2526
2527 add-auto-load-safe-path /path/to/mongo-c-driver
2528
2529 If you haven't added the path to your auto-load safe-path, or start GDB
2530 in another directory, load the file with:
2531
2532 source path/to/mongo-c-driver/.gdbinit
2533
2534 The .gdbinit file defines the printbson function, which shows the con‐
2535 tents of a bson_t * variable. If you have a local bson_t, then you
2536 must prefix the variable with a &.
2537
2538 An example GDB session looks like:
2539
2540 (gdb) printbson bson
2541 ALLOC [0x555556cd7310 + 0] (len=475)
2542 {
2543 'bool' : true,
2544 'int32' : NumberInt("42"),
2545 'int64' : NumberLong("3000000042"),
2546 'string' : "Stŕìñg",
2547 'objectId' : ObjectID("5A1442F3122D331C3C6757E1"),
2548 'utcDateTime' : UTCDateTime(1511277299031),
2549 'arrayOfInts' : [
2550 '0' : NumberInt("1"),
2551 '1' : NumberInt("2")
2552 ],
2553 'embeddedDocument' : {
2554 'arrayOfStrings' : [
2555 '0' : "one",
2556 '1' : "two"
2557 ],
2558 'double' : 2.718280,
2559 'notherDoc' : {
2560 'true' : NumberInt("1"),
2561 'false' : false
2562 }
2563 },
2564 'binary' : Binary("02", "3031343532333637"),
2565 'regex' : Regex("@[a-z]+@", "im"),
2566 'null' : null,
2567 'js' : JavaScript("print foo"),
2568 'jsws' : JavaScript("print foo") with scope: {
2569 'f' : NumberInt("42"),
2570 'a' : [
2571 '0' : 3.141593,
2572 '1' : 2.718282
2573 ]
2574 },
2575 'timestamp' : Timestamp(4294967295, 4294967295),
2576 'double' : 3.141593
2577 }
2578
2579 LLDB
2580 This repository also includes a script that customizes LLDB's standard
2581 print command to print a bson_t or bson_t * as JSON:
2582
2583 (lldb) print b
2584 (bson_t) $0 = {"x": 1, "y": 2}
2585
2586 The custom bson command provides more options:
2587
2588 (lldb) bson --verbose b
2589 len=19
2590 flags=INLINE|STATIC
2591 {
2592 "x": 1,
2593 "y": 2
2594 }
2595 (lldb) bson --raw b
2596 '\x13\x00\x00\x00\x10x\x00\x01\x00\x00\x00\x10y\x00\x02\x00\x00\x00\x00'
2597
2598 Type help bson for a list of options.
2599
2600 The script requires a build of libbson with debug symbols, and an
2601 installation of PyMongo. Install PyMongo with:
2602
2603 python -m pip install pymongo
2604
2605 If you see "No module named pip" then you must install pip, then run
2606 the previous command again.
2607
2608 Create a file ~/.lldbinit containing:
2609
2610 command script import /path/to/mongo-c-driver/lldb_bson.py
2611
2612 If you see "bson command installed by lldb_bson" at the beginning of
2613 your LLDB session, you've installed the script correctly.
2614
2616 New in MongoDB 4.2, Client-Side Field Level Encryption (also referred
2617 to as Client-Side Encryption) allows administrators and developers to
2618 encrypt specific data fields in addition to other MongoDB encryption
2619 features.
2620
2621 With Client-Side Encryption, developers can encrypt fields client side
2622 without any server-side configuration or directives. Client-Side
2623 Encryption supports workloads where applications must guarantee that
2624 unauthorized parties, including server administrators, cannot read the
2625 encrypted data.
2626
2627 Automatic encryption, where sensitive fields in commands are encrypted
2628 automatically, requires an Enterprise-only process to do query analy‐
2629 sis.
2630
2631 Installation
2632 libmongocrypt
2633 There is a separate library, libmongocrypt, that must be installed
2634 prior to configuring libmongoc to enable Client-Side Encryption.
2635
2636 libmongocrypt depends on libbson. To build libmongoc with Client-Side
2637 Encryption support you must:
2638
2639 1. Install libbson
2640
2641 2. Build and install libmongocrypt
2642
2643 3. Build libmongoc
2644
2645 To install libbson, follow the instructions to install with a package
2646 manager: Install libbson with a Package Manager or build from source
2647 with cmake (disable building libmongoc with -DENABLE_MONGOC=OFF):
2648
2649 $ cd mongo-c-driver
2650 $ mkdir cmake-build && cd cmake-build
2651 $ cmake -DENABLE_AUTOMATIC_INIT_AND_CLEANUP=OFF -DENABLE_MONGOC=OFF ..
2652 $ cmake --build . --target install
2653
2654 To build and install libmongocrypt, clone the repository and configure
2655 as follows:
2656
2657 $ cd libmongocrypt
2658 $ mkdir cmake-build && cd cmake-build
2659 $ cmake -DENABLE_SHARED_BSON=ON ..
2660 $ cmake --build . --target install
2661
2662 Then, you should be able to build libmongoc with Client-Side Encryp‐
2663 tion.
2664
2665 $ cd mongo-c-driver
2666 $ mkdir cmake-build && cd cmake-build
2667 $ cmake -DENABLE_AUTOMATIC_INIT_AND_CLEANUP=OFF -DENABLE_MONGOC=ON -DENABLE_CLIENT_SIDE_ENCRYPTION=ON ..
2668 $ cmake --build . --target install
2669
2670 mongocryptd
2671 The mongocryptd binary is required for automatic Client-Side Encryption
2672 and is included as a component in the MongoDB Enterprise Server pack‐
2673 age. For detailed installation instructions see the MongoDB documenta‐
2674 tion on mongocryptd.
2675
2676 mongocryptd performs the following:
2677
2678 · Parses the automatic encryption rules specified to the database con‐
2679 nection. If the JSON schema contains invalid automatic encryption
2680 syntax or any document validation syntax, mongocryptd returns an
2681 error.
2682
2683 · Uses the specified automatic encryption rules to mark fields in read
2684 and write operations for encryption.
2685
2686 · Rejects read/write operations that may return unexpected or incorrect
2687 results when applied to an encrypted field. For supported and unsup‐
2688 ported operations, see Read/Write Support with Automatic Field Level
2689 Encryption.
2690
2691 A mongoc_client_t configured with auto encryption will automatically
2692 spawn the mongocryptd process from the application's PATH. Applications
2693 can control the spawning behavior as part of the automatic encryption
2694 options. For example, to set a custom path to the mongocryptd process,
2695 set the mongocryptdSpawnPath with mongoc_auto_encryp‐
2696 tion_opts_set_extra().
2697
2698 bson_t *extra = BCON_NEW ("mongocryptdSpawnPath", "/path/to/mongocryptd");
2699 mongoc_auto_encryption_opts_set_extra (opts, extra);
2700
2701 To control the logging output of mongocryptd pass mongocryptdSpawnArgs
2702 to mongoc_auto_encryption_opts_set_extra():
2703
2704 bson_t *extra = BCON_NEW ("mongocryptdSpawnArgs",
2705 "[", "--logpath=/path/to/mongocryptd.log", "--logappend", "]");
2706 mongoc_auto_encryption_opts_set_extra (opts, extra);
2707
2708 If your application wishes to manage the mongocryptd process manually,
2709 it is possible to disable spawning mongocryptd:
2710
2711 bson_t *extra = BCON_NEW ("mongocryptdBypassSpawn",
2712 BCON_BOOL(true), "mongocryptdURI", "mongodb://localhost:27020");
2713 mongoc_auto_encryption_opts_set_extra (opts, extra);
2714
2715 mongocryptd is only responsible for supporting automatic Client-Side
2716 Encryption in the driver and does not itself perform any encryption or
2717 decryption.
2718
2719 Automatic Client-Side Field Level Encryption
2720 Automatic Client-Side Encryption is enabled by calling mon‐
2721 goc_client_enable_auto_encryption() on a mongoc_client_t. The following
2722 examples show how to set up automatic client-side field level encryp‐
2723 tion using mongoc_client_encryption_t to create a new encryption data
2724 key.
2725
2726 NOTE:
2727 Automatic client-side field level encryption requires MongoDB 4.2
2728 enterprise or a MongoDB 4.2 Atlas cluster. The community version of
2729 the server supports automatic decryption as well as Explicit Encryp‐
2730 tion.
2731
2732 Providing Local Automatic Encryption Rules
2733 The following example shows how to specify automatic encryption rules
2734 using a schema map set with mongoc_auto_encryp‐
2735 tion_opts_set_schema_map(). The automatic encryption rules are
2736 expressed using a strict subset of the JSON Schema syntax.
2737
2738 Supplying a schema map provides more security than relying on JSON
2739 Schemas obtained from the server. It protects against a malicious
2740 server advertising a false JSON Schema, which could trick the client
2741 into sending unencrypted data that should be encrypted.
2742
2743 JSON Schemas supplied in the schema map only apply to configuring auto‐
2744 matic client-side field level encryption. Other validation rules in the
2745 JSON schema will not be enforced by the driver and will result in an
2746 error: client-side-encryption-schema-map.c.INDENT 0.0
2747
2748 #include <mongoc/mongoc.h>
2749 #include <stdio.h>
2750 #include <stdlib.h>
2751
2752 #include "client-side-encryption-helpers.h"
2753
2754 /* Helper method to create a new data key in the key vault, a schema to use that
2755 * key, and writes the schema to a file for later use. */
2756 static bool
2757 create_schema_file (bson_t *kms_providers,
2758 const char *keyvault_db,
2759 const char *keyvault_coll,
2760 mongoc_client_t *keyvault_client,
2761 bson_error_t *error)
2762 {
2763 mongoc_client_encryption_t *client_encryption = NULL;
2764 mongoc_client_encryption_opts_t *client_encryption_opts = NULL;
2765 mongoc_client_encryption_datakey_opts_t *datakey_opts = NULL;
2766 bson_value_t datakey_id = {0};
2767 char *keyaltnames[] = {"mongoc_encryption_example_1"};
2768 bson_t *schema = NULL;
2769 char *schema_string = NULL;
2770 size_t schema_string_len;
2771 FILE *outfile = NULL;
2772 bool ret = false;
2773
2774 client_encryption_opts = mongoc_client_encryption_opts_new ();
2775 mongoc_client_encryption_opts_set_kms_providers (client_encryption_opts,
2776 kms_providers);
2777 mongoc_client_encryption_opts_set_keyvault_namespace (
2778 client_encryption_opts, keyvault_db, keyvault_coll);
2779 mongoc_client_encryption_opts_set_keyvault_client (client_encryption_opts,
2780 keyvault_client);
2781
2782 client_encryption =
2783 mongoc_client_encryption_new (client_encryption_opts, error);
2784 if (!client_encryption) {
2785 goto fail;
2786 }
2787
2788 /* Create a new data key and json schema for the encryptedField.
2789 * https://dochub.mongodb.org/core/client-side-field-level-encryption-automatic-encryption-rules
2790 */
2791 datakey_opts = mongoc_client_encryption_datakey_opts_new ();
2792 mongoc_client_encryption_datakey_opts_set_keyaltnames (
2793 datakey_opts, keyaltnames, 1);
2794 if (!mongoc_client_encryption_create_datakey (
2795 client_encryption, "local", datakey_opts, &datakey_id, error)) {
2796 goto fail;
2797 }
2798
2799 /* Create a schema describing that "encryptedField" is a string encrypted
2800 * with the newly created data key using deterministic encryption. */
2801 schema = BCON_NEW ("properties",
2802 "{",
2803 "encryptedField",
2804 "{",
2805 "encrypt",
2806 "{",
2807 "keyId",
2808 "[",
2809 BCON_BIN (datakey_id.value.v_binary.subtype,
2810 datakey_id.value.v_binary.data,
2811 datakey_id.value.v_binary.data_len),
2812 "]",
2813 "bsonType",
2814 "string",
2815 "algorithm",
2816 MONGOC_AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC,
2817 "}",
2818 "}",
2819 "}",
2820 "bsonType",
2821 "object");
2822
2823 /* Use canonical JSON so that other drivers and tools will be
2824 * able to parse the MongoDB extended JSON file. */
2825 schema_string = bson_as_canonical_extended_json (schema, &schema_string_len);
2826 outfile = fopen ("jsonSchema.json", "w");
2827 if (0 == fwrite (schema_string, sizeof (char), schema_string_len, outfile)) {
2828 fprintf (stderr, "failed to write to file\n");
2829 goto fail;
2830 }
2831
2832 ret = true;
2833 fail:
2834 mongoc_client_encryption_destroy (client_encryption);
2835 mongoc_client_encryption_datakey_opts_destroy (datakey_opts);
2836 mongoc_client_encryption_opts_destroy (client_encryption_opts);
2837 bson_free (schema_string);
2838 bson_destroy (schema);
2839 bson_value_destroy (&datakey_id);
2840 if (outfile) {
2841 fclose (outfile);
2842 }
2843 return true;
2844 }
2845
2846 /* This example demonstrates how to use automatic encryption with a client-side
2847 * schema map using the enterprise version of MongoDB */
2848 int
2849 main (int argc, char **argv)
2850 {
2851 /* The collection used to store the encryption data keys. */
2852 #define KEYVAULT_DB "encryption"
2853 #define KEYVAULT_COLL "__libmongocTestKeyVault"
2854 /* The collection used to store the encrypted documents in this example. */
2855 #define ENCRYPTED_DB "test"
2856 #define ENCRYPTED_COLL "coll"
2857
2858 int exit_status = EXIT_FAILURE;
2859 bool ret;
2860 uint8_t *local_masterkey = NULL;
2861 uint32_t local_masterkey_len;
2862 bson_t *kms_providers = NULL;
2863 bson_error_t error = {0};
2864 bson_t *index_keys = NULL;
2865 char *index_name = NULL;
2866 bson_t *create_index_cmd = NULL;
2867 bson_json_reader_t *reader = NULL;
2868 bson_t schema = BSON_INITIALIZER;
2869 bson_t *schema_map = NULL;
2870
2871 /* The MongoClient used to access the key vault (keyvault_namespace). */
2872 mongoc_client_t *keyvault_client = NULL;
2873 mongoc_collection_t *keyvault_coll = NULL;
2874 mongoc_auto_encryption_opts_t *auto_encryption_opts = NULL;
2875 mongoc_client_t *client = NULL;
2876 mongoc_collection_t *coll = NULL;
2877 bson_t *to_insert = NULL;
2878 mongoc_client_t *unencrypted_client = NULL;
2879 mongoc_collection_t *unencrypted_coll = NULL;
2880
2881 mongoc_init ();
2882
2883 /* Configure the master key. This must be the same master key that was used
2884 * to create the encryption key. */
2885 local_masterkey =
2886 hex_to_bin (getenv ("LOCAL_MASTERKEY"), &local_masterkey_len);
2887 if (!local_masterkey || local_masterkey_len != 96) {
2888 fprintf (stderr,
2889 "Specify LOCAL_MASTERKEY environment variable as a "
2890 "secure random 96 byte hex value.\n");
2891 goto fail;
2892 }
2893
2894 kms_providers = BCON_NEW ("local",
2895 "{",
2896 "key",
2897 BCON_BIN (0, local_masterkey, local_masterkey_len),
2898 "}");
2899
2900 /* Set up the key vault for this example. */
2901 keyvault_client = mongoc_client_new (
2902 "mongodb://localhost/?appname=client-side-encryption-keyvault");
2903 keyvault_coll = mongoc_client_get_collection (
2904 keyvault_client, KEYVAULT_DB, KEYVAULT_COLL);
2905 mongoc_collection_drop (keyvault_coll, NULL);
2906
2907 /* Create a unique index to ensure that two data keys cannot share the same
2908 * keyAltName. This is recommended practice for the key vault. */
2909 index_keys = BCON_NEW ("keyAltNames", BCON_INT32 (1));
2910 index_name = mongoc_collection_keys_to_index_string (index_keys);
2911 create_index_cmd = BCON_NEW ("createIndexes",
2912 KEYVAULT_COLL,
2913 "indexes",
2914 "[",
2915 "{",
2916 "key",
2917 BCON_DOCUMENT (index_keys),
2918 "name",
2919 index_name,
2920 "unique",
2921 BCON_BOOL (true),
2922 "partialFilterExpression",
2923 "{",
2924 "keyAltNames",
2925 "{",
2926 "$exists",
2927 BCON_BOOL (true),
2928 "}",
2929 "}",
2930 "}",
2931 "]");
2932 ret = mongoc_client_command_simple (keyvault_client,
2933 KEYVAULT_DB,
2934 create_index_cmd,
2935 NULL /* read prefs */,
2936 NULL /* reply */,
2937 &error);
2938
2939 if (!ret) {
2940 goto fail;
2941 }
2942
2943 /* Create a new data key and a schema using it for encryption. Save the
2944 * schema to the file jsonSchema.json */
2945 ret = create_schema_file (
2946 kms_providers, KEYVAULT_DB, KEYVAULT_COLL, keyvault_client, &error);
2947
2948 if (!ret) {
2949 goto fail;
2950 }
2951
2952 /* Load the JSON Schema and construct the local schema_map option. */
2953 reader = bson_json_reader_new_from_file ("jsonSchema.json", &error);
2954 if (!reader) {
2955 goto fail;
2956 }
2957
2958 bson_json_reader_read (reader, &schema, &error);
2959
2960 /* Construct the schema map, mapping the namespace of the collection to the
2961 * schema describing encryption. */
2962 schema_map =
2963 BCON_NEW (ENCRYPTED_DB "." ENCRYPTED_COLL, BCON_DOCUMENT (&schema));
2964
2965 auto_encryption_opts = mongoc_auto_encryption_opts_new ();
2966 mongoc_auto_encryption_opts_set_keyvault_client (auto_encryption_opts,
2967 keyvault_client);
2968 mongoc_auto_encryption_opts_set_keyvault_namespace (
2969 auto_encryption_opts, KEYVAULT_DB, KEYVAULT_COLL);
2970 mongoc_auto_encryption_opts_set_kms_providers (auto_encryption_opts,
2971 kms_providers);
2972 mongoc_auto_encryption_opts_set_schema_map (auto_encryption_opts,
2973 schema_map);
2974
2975 client =
2976 mongoc_client_new ("mongodb://localhost/?appname=client-side-encryption");
2977
2978 /* Enable automatic encryption. It will determine that encryption is
2979 * necessary from the schema map instead of relying on the server to provide
2980 * a schema. */
2981 ret = mongoc_client_enable_auto_encryption (
2982 client, auto_encryption_opts, &error);
2983 if (!ret) {
2984 goto fail;
2985 }
2986
2987 coll = mongoc_client_get_collection (client, ENCRYPTED_DB, ENCRYPTED_COLL);
2988
2989 /* Clear old data */
2990 mongoc_collection_drop (coll, NULL);
2991
2992 to_insert = BCON_NEW ("encryptedField", "123456789");
2993 ret = mongoc_collection_insert_one (
2994 coll, to_insert, NULL /* opts */, NULL /* reply */, &error);
2995 if (!ret) {
2996 goto fail;
2997 }
2998 printf ("decrypted document: ");
2999 if (!print_one_document (coll, &error)) {
3000 goto fail;
3001 }
3002 printf ("\n");
3003
3004 unencrypted_client = mongoc_client_new (
3005 "mongodb://localhost/?appname=client-side-encryption-unencrypted");
3006 unencrypted_coll = mongoc_client_get_collection (
3007 unencrypted_client, ENCRYPTED_DB, ENCRYPTED_COLL);
3008 printf ("encrypted document: ");
3009 if (!print_one_document (unencrypted_coll, &error)) {
3010 goto fail;
3011 }
3012 printf ("\n");
3013
3014 exit_status = EXIT_SUCCESS;
3015 fail:
3016 if (error.code) {
3017 fprintf (stderr, "error: %s\n", error.message);
3018 }
3019
3020 bson_free (local_masterkey);
3021 bson_destroy (kms_providers);
3022 mongoc_collection_destroy (keyvault_coll);
3023 bson_destroy (index_keys);
3024 bson_free (index_name);
3025 bson_destroy (create_index_cmd);
3026 bson_json_reader_destroy (reader);
3027 mongoc_auto_encryption_opts_destroy (auto_encryption_opts);
3028 mongoc_collection_destroy (coll);
3029 mongoc_client_destroy (client);
3030 bson_destroy (to_insert);
3031 mongoc_collection_destroy (unencrypted_coll);
3032 mongoc_client_destroy (unencrypted_client);
3033 mongoc_client_destroy (keyvault_client);
3034 bson_destroy (&schema);
3035 bson_destroy (schema_map);
3036 mongoc_cleanup ();
3037 return exit_status;
3038 }
3039
3040 Server-Side Field Level Encryption Enforcement
3041 The MongoDB 4.2 server supports using schema validation to enforce
3042 encryption of specific fields in a collection. This schema validation
3043 will prevent an application from inserting unencrypted values for any
3044 fields marked with the "encrypt" JSON schema keyword.
3045
3046 The following example shows how to set up automatic client-side field
3047 level encryption using mongoc_client_encryption_t to create a new
3048 encryption data key and create a collection with the Automatic Encryp‐
3049 tion JSON Schema Syntax: client-side-encryption-server-schema.c.INDENT
3050 0.0
3051
3052 #include <mongoc/mongoc.h>
3053 #include <stdio.h>
3054 #include <stdlib.h>
3055
3056 #include "client-side-encryption-helpers.h"
3057
3058 /* Helper method to create and return a JSON schema to use for encryption.
3059 The caller will use the returned schema for server-side encryption validation.
3060 */
3061 static bson_t *
3062 create_schema (bson_t *kms_providers,
3063 const char *keyvault_db,
3064 const char *keyvault_coll,
3065 mongoc_client_t *keyvault_client,
3066 bson_error_t *error)
3067 {
3068 mongoc_client_encryption_t *client_encryption = NULL;
3069 mongoc_client_encryption_opts_t *client_encryption_opts = NULL;
3070 mongoc_client_encryption_datakey_opts_t *datakey_opts = NULL;
3071 bson_value_t datakey_id = {0};
3072 char *keyaltnames[] = {"mongoc_encryption_example_2"};
3073 bson_t *schema = NULL;
3074
3075 client_encryption_opts = mongoc_client_encryption_opts_new ();
3076 mongoc_client_encryption_opts_set_kms_providers (client_encryption_opts,
3077 kms_providers);
3078 mongoc_client_encryption_opts_set_keyvault_namespace (
3079 client_encryption_opts, keyvault_db, keyvault_coll);
3080 mongoc_client_encryption_opts_set_keyvault_client (client_encryption_opts,
3081 keyvault_client);
3082
3083 client_encryption =
3084 mongoc_client_encryption_new (client_encryption_opts, error);
3085 if (!client_encryption) {
3086 goto fail;
3087 }
3088
3089 /* Create a new data key and json schema for the encryptedField.
3090 * https://dochub.mongodb.org/core/client-side-field-level-encryption-automatic-encryption-rules
3091 */
3092 datakey_opts = mongoc_client_encryption_datakey_opts_new ();
3093 mongoc_client_encryption_datakey_opts_set_keyaltnames (
3094 datakey_opts, keyaltnames, 1);
3095 if (!mongoc_client_encryption_create_datakey (
3096 client_encryption, "local", datakey_opts, &datakey_id, error)) {
3097 goto fail;
3098 }
3099
3100 /* Create a schema describing that "encryptedField" is a string encrypted
3101 * with the newly created data key using deterministic encryption. */
3102 schema = BCON_NEW ("properties",
3103 "{",
3104 "encryptedField",
3105 "{",
3106 "encrypt",
3107 "{",
3108 "keyId",
3109 "[",
3110 BCON_BIN (datakey_id.value.v_binary.subtype,
3111 datakey_id.value.v_binary.data,
3112 datakey_id.value.v_binary.data_len),
3113 "]",
3114 "bsonType",
3115 "string",
3116 "algorithm",
3117 MONGOC_AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC,
3118 "}",
3119 "}",
3120 "}",
3121 "bsonType",
3122 "object");
3123
3124 fail:
3125 mongoc_client_encryption_destroy (client_encryption);
3126 mongoc_client_encryption_datakey_opts_destroy (datakey_opts);
3127 mongoc_client_encryption_opts_destroy (client_encryption_opts);
3128 bson_value_destroy (&datakey_id);
3129 return schema;
3130 }
3131
3132 /* This example demonstrates how to use automatic encryption with a server-side
3133 * schema using the enterprise version of MongoDB */
3134 int
3135 main (int argc, char **argv)
3136 {
3137 /* The collection used to store the encryption data keys. */
3138 #define KEYVAULT_DB "encryption"
3139 #define KEYVAULT_COLL "__libmongocTestKeyVault"
3140 /* The collection used to store the encrypted documents in this example. */
3141 #define ENCRYPTED_DB "test"
3142 #define ENCRYPTED_COLL "coll"
3143
3144 int exit_status = EXIT_FAILURE;
3145 bool ret;
3146 uint8_t *local_masterkey = NULL;
3147 uint32_t local_masterkey_len;
3148 bson_t *kms_providers = NULL;
3149 bson_error_t error = {0};
3150 bson_t *index_keys = NULL;
3151 char *index_name = NULL;
3152 bson_t *create_index_cmd = NULL;
3153 bson_json_reader_t *reader = NULL;
3154 bson_t *schema = NULL;
3155
3156 /* The MongoClient used to access the key vault (keyvault_namespace). */
3157 mongoc_client_t *keyvault_client = NULL;
3158 mongoc_collection_t *keyvault_coll = NULL;
3159 mongoc_auto_encryption_opts_t *auto_encryption_opts = NULL;
3160 mongoc_client_t *client = NULL;
3161 mongoc_collection_t *coll = NULL;
3162 bson_t *to_insert = NULL;
3163 mongoc_client_t *unencrypted_client = NULL;
3164 mongoc_collection_t *unencrypted_coll = NULL;
3165 bson_t *create_cmd = NULL;
3166 bson_t *create_cmd_opts = NULL;
3167 mongoc_write_concern_t *wc = NULL;
3168
3169 mongoc_init ();
3170
3171 /* Configure the master key. This must be the same master key that was used
3172 * to create
3173 * the encryption key. */
3174 local_masterkey =
3175 hex_to_bin (getenv ("LOCAL_MASTERKEY"), &local_masterkey_len);
3176 if (!local_masterkey || local_masterkey_len != 96) {
3177 fprintf (stderr,
3178 "Specify LOCAL_MASTERKEY environment variable as a "
3179 "secure random 96 byte hex value.\n");
3180 goto fail;
3181 }
3182
3183 kms_providers = BCON_NEW ("local",
3184 "{",
3185 "key",
3186 BCON_BIN (0, local_masterkey, local_masterkey_len),
3187 "}");
3188
3189 /* Set up the key vault for this example. */
3190 keyvault_client = mongoc_client_new (
3191 "mongodb://localhost/?appname=client-side-encryption-keyvault");
3192 keyvault_coll = mongoc_client_get_collection (
3193 keyvault_client, KEYVAULT_DB, KEYVAULT_COLL);
3194 mongoc_collection_drop (keyvault_coll, NULL);
3195
3196 /* Create a unique index to ensure that two data keys cannot share the same
3197 * keyAltName. This is recommended practice for the key vault. */
3198 index_keys = BCON_NEW ("keyAltNames", BCON_INT32 (1));
3199 index_name = mongoc_collection_keys_to_index_string (index_keys);
3200 create_index_cmd = BCON_NEW ("createIndexes",
3201 KEYVAULT_COLL,
3202 "indexes",
3203 "[",
3204 "{",
3205 "key",
3206 BCON_DOCUMENT (index_keys),
3207 "name",
3208 index_name,
3209 "unique",
3210 BCON_BOOL (true),
3211 "partialFilterExpression",
3212 "{",
3213 "keyAltNames",
3214 "{",
3215 "$exists",
3216 BCON_BOOL (true),
3217 "}",
3218 "}",
3219 "}",
3220 "]");
3221 ret = mongoc_client_command_simple (keyvault_client,
3222 KEYVAULT_DB,
3223 create_index_cmd,
3224 NULL /* read prefs */,
3225 NULL /* reply */,
3226 &error);
3227
3228 if (!ret) {
3229 goto fail;
3230 }
3231
3232 auto_encryption_opts = mongoc_auto_encryption_opts_new ();
3233 mongoc_auto_encryption_opts_set_keyvault_client (auto_encryption_opts,
3234 keyvault_client);
3235 mongoc_auto_encryption_opts_set_keyvault_namespace (
3236 auto_encryption_opts, KEYVAULT_DB, KEYVAULT_COLL);
3237 mongoc_auto_encryption_opts_set_kms_providers (auto_encryption_opts,
3238 kms_providers);
3239 schema = create_schema (
3240 kms_providers, KEYVAULT_DB, KEYVAULT_COLL, keyvault_client, &error);
3241
3242 if (!schema) {
3243 goto fail;
3244 }
3245
3246 client =
3247 mongoc_client_new ("mongodb://localhost/?appname=client-side-encryption");
3248 ret = mongoc_client_enable_auto_encryption (
3249 client, auto_encryption_opts, &error);
3250 if (!ret) {
3251 goto fail;
3252 }
3253
3254 coll = mongoc_client_get_collection (client, ENCRYPTED_DB, ENCRYPTED_COLL);
3255
3256 /* Clear old data */
3257 mongoc_collection_drop (coll, NULL);
3258
3259 /* Create the collection with the encryption JSON Schema. */
3260 create_cmd = BCON_NEW ("create",
3261 ENCRYPTED_COLL,
3262 "validator",
3263 "{",
3264 "$jsonSchema",
3265 BCON_DOCUMENT (schema),
3266 "}");
3267 wc = mongoc_write_concern_new ();
3268 mongoc_write_concern_set_wmajority (wc, 0);
3269 create_cmd_opts = bson_new ();
3270 mongoc_write_concern_append (wc, create_cmd_opts);
3271 ret = mongoc_client_command_with_opts (client,
3272 ENCRYPTED_DB,
3273 create_cmd,
3274 NULL /* read prefs */,
3275 create_cmd_opts,
3276 NULL /* reply */,
3277 &error);
3278 if (!ret) {
3279 goto fail;
3280 }
3281
3282 to_insert = BCON_NEW ("encryptedField", "123456789");
3283 ret = mongoc_collection_insert_one (
3284 coll, to_insert, NULL /* opts */, NULL /* reply */, &error);
3285 if (!ret) {
3286 goto fail;
3287 }
3288 printf ("decrypted document: ");
3289 if (!print_one_document (coll, &error)) {
3290 goto fail;
3291 }
3292 printf ("\n");
3293
3294 unencrypted_client = mongoc_client_new (
3295 "mongodb://localhost/?appname=client-side-encryption-unencrypted");
3296 unencrypted_coll = mongoc_client_get_collection (
3297 unencrypted_client, ENCRYPTED_DB, ENCRYPTED_COLL);
3298 printf ("encrypted document: ");
3299 if (!print_one_document (unencrypted_coll, &error)) {
3300 goto fail;
3301 }
3302 printf ("\n");
3303
3304 /* Expect a server-side error if inserting with the unencrypted collection.
3305 */
3306 ret = mongoc_collection_insert_one (
3307 unencrypted_coll, to_insert, NULL /* opts */, NULL /* reply */, &error);
3308 if (!ret) {
3309 printf ("insert with unencrypted collection failed: %s\n", error.message);
3310 memset (&error, 0, sizeof (error));
3311 }
3312
3313 exit_status = EXIT_SUCCESS;
3314 fail:
3315 if (error.code) {
3316 fprintf (stderr, "error: %s\n", error.message);
3317 }
3318
3319 bson_free (local_masterkey);
3320 bson_destroy (kms_providers);
3321 mongoc_collection_destroy (keyvault_coll);
3322 bson_destroy (index_keys);
3323 bson_free (index_name);
3324 bson_destroy (create_index_cmd);
3325 bson_json_reader_destroy (reader);
3326 mongoc_auto_encryption_opts_destroy (auto_encryption_opts);
3327 mongoc_collection_destroy (coll);
3328 mongoc_client_destroy (client);
3329 bson_destroy (to_insert);
3330 mongoc_collection_destroy (unencrypted_coll);
3331 mongoc_client_destroy (unencrypted_client);
3332 mongoc_client_destroy (keyvault_client);
3333 bson_destroy (schema);
3334 bson_destroy (create_cmd);
3335 bson_destroy (create_cmd_opts);
3336 mongoc_write_concern_destroy (wc);
3337
3338 mongoc_cleanup ();
3339 return exit_status;
3340 }
3341
3342 Explicit Encryption
3343 Explicit encryption is a MongoDB community feature and does not use the
3344 mongocryptd process. Explicit encryption is provided by the mon‐
3345 goc_client_encryption_t class, for example: client-side-encryp‐
3346 tion-explicit.c.INDENT 0.0
3347
3348 #include <mongoc/mongoc.h>
3349 #include <stdio.h>
3350 #include <stdlib.h>
3351
3352 #include "client-side-encryption-helpers.h"
3353
3354 /* This example demonstrates how to use explicit encryption and decryption using
3355 * the community version of MongoDB */
3356 int
3357 main (int argc, char **argv)
3358 {
3359 /* The collection used to store the encryption data keys. */
3360 #define KEYVAULT_DB "encryption"
3361 #define KEYVAULT_COLL "__libmongocTestKeyVault"
3362 /* The collection used to store the encrypted documents in this example. */
3363 #define ENCRYPTED_DB "test"
3364 #define ENCRYPTED_COLL "coll"
3365
3366 int exit_status = EXIT_FAILURE;
3367 bool ret;
3368 uint8_t *local_masterkey = NULL;
3369 uint32_t local_masterkey_len;
3370 bson_t *kms_providers = NULL;
3371 bson_error_t error = {0};
3372 bson_t *index_keys = NULL;
3373 char *index_name = NULL;
3374 bson_t *create_index_cmd = NULL;
3375 bson_t *schema = NULL;
3376 mongoc_client_t *client = NULL;
3377 mongoc_collection_t *coll = NULL;
3378 mongoc_collection_t *keyvault_coll = NULL;
3379 bson_t *to_insert = NULL;
3380 bson_t *create_cmd = NULL;
3381 bson_t *create_cmd_opts = NULL;
3382 mongoc_write_concern_t *wc = NULL;
3383 mongoc_client_encryption_t *client_encryption = NULL;
3384 mongoc_client_encryption_opts_t *client_encryption_opts = NULL;
3385 mongoc_client_encryption_datakey_opts_t *datakey_opts = NULL;
3386 char *keyaltnames[] = {"mongoc_encryption_example_3"};
3387 bson_value_t datakey_id = {0};
3388 bson_value_t encrypted_field = {0};
3389 bson_value_t to_encrypt = {0};
3390 mongoc_client_encryption_encrypt_opts_t *encrypt_opts = NULL;
3391 bson_value_t decrypted = {0};
3392
3393 mongoc_init ();
3394
3395 /* Configure the master key. This must be the same master key that was used
3396 * to create the encryption key. */
3397 local_masterkey =
3398 hex_to_bin (getenv ("LOCAL_MASTERKEY"), &local_masterkey_len);
3399 if (!local_masterkey || local_masterkey_len != 96) {
3400 fprintf (stderr,
3401 "Specify LOCAL_MASTERKEY environment variable as a "
3402 "secure random 96 byte hex value.\n");
3403 goto fail;
3404 }
3405
3406 kms_providers = BCON_NEW ("local",
3407 "{",
3408 "key",
3409 BCON_BIN (0, local_masterkey, local_masterkey_len),
3410 "}");
3411
3412 /* The mongoc_client_t used to read/write application data. */
3413 client =
3414 mongoc_client_new ("mongodb://localhost/?appname=client-side-encryption");
3415 coll = mongoc_client_get_collection (client, ENCRYPTED_DB, ENCRYPTED_COLL);
3416
3417 /* Clear old data */
3418 mongoc_collection_drop (coll, NULL);
3419
3420 /* Set up the key vault for this example. */
3421 keyvault_coll =
3422 mongoc_client_get_collection (client, KEYVAULT_DB, KEYVAULT_COLL);
3423 mongoc_collection_drop (keyvault_coll, NULL);
3424
3425 /* Create a unique index to ensure that two data keys cannot share the same
3426 * keyAltName. This is recommended practice for the key vault. */
3427 index_keys = BCON_NEW ("keyAltNames", BCON_INT32 (1));
3428 index_name = mongoc_collection_keys_to_index_string (index_keys);
3429 create_index_cmd = BCON_NEW ("createIndexes",
3430 KEYVAULT_COLL,
3431 "indexes",
3432 "[",
3433 "{",
3434 "key",
3435 BCON_DOCUMENT (index_keys),
3436 "name",
3437 index_name,
3438 "unique",
3439 BCON_BOOL (true),
3440 "partialFilterExpression",
3441 "{",
3442 "keyAltNames",
3443 "{",
3444 "$exists",
3445 BCON_BOOL (true),
3446 "}",
3447 "}",
3448 "}",
3449 "]");
3450 ret = mongoc_client_command_simple (client,
3451 KEYVAULT_DB,
3452 create_index_cmd,
3453 NULL /* read prefs */,
3454 NULL /* reply */,
3455 &error);
3456
3457 if (!ret) {
3458 goto fail;
3459 }
3460
3461 client_encryption_opts = mongoc_client_encryption_opts_new ();
3462 mongoc_client_encryption_opts_set_kms_providers (client_encryption_opts,
3463 kms_providers);
3464 mongoc_client_encryption_opts_set_keyvault_namespace (
3465 client_encryption_opts, KEYVAULT_DB, KEYVAULT_COLL);
3466
3467 /* Set a mongoc_client_t to use for reading/writing to the key vault. This
3468 * can be the same mongoc_client_t used by the main application. */
3469 mongoc_client_encryption_opts_set_keyvault_client (client_encryption_opts,
3470 client);
3471 client_encryption =
3472 mongoc_client_encryption_new (client_encryption_opts, &error);
3473 if (!client_encryption) {
3474 goto fail;
3475 }
3476
3477 /* Create a new data key for the encryptedField.
3478 * https://dochub.mongodb.org/core/client-side-field-level-encryption-automatic-encryption-rules
3479 */
3480 datakey_opts = mongoc_client_encryption_datakey_opts_new ();
3481 mongoc_client_encryption_datakey_opts_set_keyaltnames (
3482 datakey_opts, keyaltnames, 1);
3483 if (!mongoc_client_encryption_create_datakey (
3484 client_encryption, "local", datakey_opts, &datakey_id, &error)) {
3485 goto fail;
3486 }
3487
3488 /* Explicitly encrypt a field */
3489 encrypt_opts = mongoc_client_encryption_encrypt_opts_new ();
3490 mongoc_client_encryption_encrypt_opts_set_algorithm (
3491 encrypt_opts, MONGOC_AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC);
3492 mongoc_client_encryption_encrypt_opts_set_keyid (encrypt_opts, &datakey_id);
3493 to_encrypt.value_type = BSON_TYPE_UTF8;
3494 to_encrypt.value.v_utf8.str = "123456789";
3495 to_encrypt.value.v_utf8.len = strlen (to_encrypt.value.v_utf8.str);
3496
3497 ret = mongoc_client_encryption_encrypt (
3498 client_encryption, &to_encrypt, encrypt_opts, &encrypted_field, &error);
3499 if (!ret) {
3500 goto fail;
3501 }
3502
3503 to_insert = bson_new ();
3504 BSON_APPEND_VALUE (to_insert, "encryptedField", &encrypted_field);
3505 ret = mongoc_collection_insert_one (
3506 coll, to_insert, NULL /* opts */, NULL /* reply */, &error);
3507 if (!ret) {
3508 goto fail;
3509 }
3510
3511 printf ("encrypted document: ");
3512 if (!print_one_document (coll, &error)) {
3513 goto fail;
3514 }
3515 printf ("\n");
3516
3517 /* Explicitly decrypt a field */
3518 ret = mongoc_client_encryption_decrypt (
3519 client_encryption, &encrypted_field, &decrypted, &error);
3520 if (!ret) {
3521 goto fail;
3522 }
3523 printf ("decrypted value: %s\n", decrypted.value.v_utf8.str);
3524
3525 exit_status = EXIT_SUCCESS;
3526 fail:
3527 if (error.code) {
3528 fprintf (stderr, "error: %s\n", error.message);
3529 }
3530
3531 bson_free (local_masterkey);
3532 bson_destroy (kms_providers);
3533 mongoc_collection_destroy (keyvault_coll);
3534 bson_destroy (index_keys);
3535 bson_free (index_name);
3536 bson_destroy (create_index_cmd);
3537 mongoc_collection_destroy (coll);
3538 mongoc_client_destroy (client);
3539 bson_destroy (to_insert);
3540 bson_destroy (schema);
3541 bson_destroy (create_cmd);
3542 bson_destroy (create_cmd_opts);
3543 mongoc_write_concern_destroy (wc);
3544 mongoc_client_encryption_destroy (client_encryption);
3545 mongoc_client_encryption_datakey_opts_destroy (datakey_opts);
3546 mongoc_client_encryption_opts_destroy (client_encryption_opts);
3547 bson_value_destroy (&encrypted_field);
3548 mongoc_client_encryption_encrypt_opts_destroy (encrypt_opts);
3549 bson_value_destroy (&decrypted);
3550 bson_value_destroy (&datakey_id);
3551
3552 mongoc_cleanup ();
3553 return exit_status;
3554 }
3555
3556 Explicit Encryption with Automatic Decryption
3557 Although automatic encryption requires MongoDB 4.2 enterprise or a Mon‐
3558 goDB 4.2 Atlas cluster, automatic decryption is supported for all
3559 users. To configure automatic decryption without automatic encryption
3560 set bypass_auto_encryption=True in mongoc_auto_encryption_opts_t:
3561 client-side-encryption-auto-decryption.c.INDENT 0.0
3562
3563 #include <mongoc/mongoc.h>
3564 #include <stdio.h>
3565 #include <stdlib.h>
3566
3567 #include "client-side-encryption-helpers.h"
3568
3569 /* This example demonstrates how to set up automatic decryption without
3570 * automatic encryption using the community version of MongoDB */
3571 int
3572 main (int argc, char **argv)
3573 {
3574 /* The collection used to store the encryption data keys. */
3575 #define KEYVAULT_DB "encryption"
3576 #define KEYVAULT_COLL "__libmongocTestKeyVault"
3577 /* The collection used to store the encrypted documents in this example. */
3578 #define ENCRYPTED_DB "test"
3579 #define ENCRYPTED_COLL "coll"
3580
3581 int exit_status = EXIT_FAILURE;
3582 bool ret;
3583 uint8_t *local_masterkey = NULL;
3584 uint32_t local_masterkey_len;
3585 bson_t *kms_providers = NULL;
3586 bson_error_t error = {0};
3587 bson_t *index_keys = NULL;
3588 char *index_name = NULL;
3589 bson_t *create_index_cmd = NULL;
3590 bson_t *schema = NULL;
3591 mongoc_client_t *client = NULL;
3592 mongoc_collection_t *coll = NULL;
3593 mongoc_collection_t *keyvault_coll = NULL;
3594 bson_t *to_insert = NULL;
3595 bson_t *create_cmd = NULL;
3596 bson_t *create_cmd_opts = NULL;
3597 mongoc_write_concern_t *wc = NULL;
3598 mongoc_client_encryption_t *client_encryption = NULL;
3599 mongoc_client_encryption_opts_t *client_encryption_opts = NULL;
3600 mongoc_client_encryption_datakey_opts_t *datakey_opts = NULL;
3601 char *keyaltnames[] = {"mongoc_encryption_example_4"};
3602 bson_value_t datakey_id = {0};
3603 bson_value_t encrypted_field = {0};
3604 bson_value_t to_encrypt = {0};
3605 mongoc_client_encryption_encrypt_opts_t *encrypt_opts = NULL;
3606 bson_value_t decrypted = {0};
3607 mongoc_auto_encryption_opts_t *auto_encryption_opts = NULL;
3608 mongoc_client_t *unencrypted_client = NULL;
3609 mongoc_collection_t *unencrypted_coll = NULL;
3610
3611 mongoc_init ();
3612
3613 /* Configure the master key. This must be the same master key that was used
3614 * to create the encryption key. */
3615 local_masterkey =
3616 hex_to_bin (getenv ("LOCAL_MASTERKEY"), &local_masterkey_len);
3617 if (!local_masterkey || local_masterkey_len != 96) {
3618 fprintf (stderr,
3619 "Specify LOCAL_MASTERKEY environment variable as a "
3620 "secure random 96 byte hex value.\n");
3621 goto fail;
3622 }
3623
3624 kms_providers = BCON_NEW ("local",
3625 "{",
3626 "key",
3627 BCON_BIN (0, local_masterkey, local_masterkey_len),
3628 "}");
3629
3630 client =
3631 mongoc_client_new ("mongodb://localhost/?appname=client-side-encryption");
3632 auto_encryption_opts = mongoc_auto_encryption_opts_new ();
3633 mongoc_auto_encryption_opts_set_keyvault_namespace (
3634 auto_encryption_opts, KEYVAULT_DB, KEYVAULT_COLL);
3635 mongoc_auto_encryption_opts_set_kms_providers (auto_encryption_opts,
3636 kms_providers);
3637
3638 /* Setting bypass_auto_encryption to true disables automatic encryption but
3639 * keeps the automatic decryption behavior. bypass_auto_encryption will also
3640 * disable spawning mongocryptd */
3641 mongoc_auto_encryption_opts_set_bypass_auto_encryption (auto_encryption_opts,
3642 true);
3643
3644 /* Once bypass_auto_encryption is set, community users can enable auto
3645 * encryption on the client. This will, in fact, only perform automatic
3646 * decryption. */
3647 ret = mongoc_client_enable_auto_encryption (
3648 client, auto_encryption_opts, &error);
3649 if (!ret) {
3650 goto fail;
3651 }
3652
3653 /* Now that automatic decryption is on, we can test it by inserting a
3654 * document with an explicitly encrypted value into the collection. When we
3655 * look up the document later, it should be automatically decrypted for us.
3656 */
3657 coll = mongoc_client_get_collection (client, ENCRYPTED_DB, ENCRYPTED_COLL);
3658
3659 /* Clear old data */
3660 mongoc_collection_drop (coll, NULL);
3661
3662 /* Set up the key vault for this example. */
3663 keyvault_coll =
3664 mongoc_client_get_collection (client, KEYVAULT_DB, KEYVAULT_COLL);
3665 mongoc_collection_drop (keyvault_coll, NULL);
3666
3667 /* Create a unique index to ensure that two data keys cannot share the same
3668 * keyAltName. This is recommended practice for the key vault. */
3669 index_keys = BCON_NEW ("keyAltNames", BCON_INT32 (1));
3670 index_name = mongoc_collection_keys_to_index_string (index_keys);
3671 create_index_cmd = BCON_NEW ("createIndexes",
3672 KEYVAULT_COLL,
3673 "indexes",
3674 "[",
3675 "{",
3676 "key",
3677 BCON_DOCUMENT (index_keys),
3678 "name",
3679 index_name,
3680 "unique",
3681 BCON_BOOL (true),
3682 "partialFilterExpression",
3683 "{",
3684 "keyAltNames",
3685 "{",
3686 "$exists",
3687 BCON_BOOL (true),
3688 "}",
3689 "}",
3690 "}",
3691 "]");
3692 ret = mongoc_client_command_simple (client,
3693 KEYVAULT_DB,
3694 create_index_cmd,
3695 NULL /* read prefs */,
3696 NULL /* reply */,
3697 &error);
3698
3699 if (!ret) {
3700 goto fail;
3701 }
3702
3703 client_encryption_opts = mongoc_client_encryption_opts_new ();
3704 mongoc_client_encryption_opts_set_kms_providers (client_encryption_opts,
3705 kms_providers);
3706 mongoc_client_encryption_opts_set_keyvault_namespace (
3707 client_encryption_opts, KEYVAULT_DB, KEYVAULT_COLL);
3708
3709 /* The key vault client is used for reading to/from the key vault. This can
3710 * be the same mongoc_client_t used by the application. */
3711 mongoc_client_encryption_opts_set_keyvault_client (client_encryption_opts,
3712 client);
3713 client_encryption =
3714 mongoc_client_encryption_new (client_encryption_opts, &error);
3715 if (!client_encryption) {
3716 goto fail;
3717 }
3718
3719 /* Create a new data key for the encryptedField.
3720 * https://dochub.mongodb.org/core/client-side-field-level-encryption-automatic-encryption-rules
3721 */
3722 datakey_opts = mongoc_client_encryption_datakey_opts_new ();
3723 mongoc_client_encryption_datakey_opts_set_keyaltnames (
3724 datakey_opts, keyaltnames, 1);
3725 ret = mongoc_client_encryption_create_datakey (
3726 client_encryption, "local", datakey_opts, &datakey_id, &error);
3727 if (!ret) {
3728 goto fail;
3729 }
3730
3731 /* Explicitly encrypt a field. */
3732 encrypt_opts = mongoc_client_encryption_encrypt_opts_new ();
3733 mongoc_client_encryption_encrypt_opts_set_algorithm (
3734 encrypt_opts, MONGOC_AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC);
3735 mongoc_client_encryption_encrypt_opts_set_keyaltname (
3736 encrypt_opts, "mongoc_encryption_example_4");
3737 to_encrypt.value_type = BSON_TYPE_UTF8;
3738 to_encrypt.value.v_utf8.str = "123456789";
3739 to_encrypt.value.v_utf8.len = strlen (to_encrypt.value.v_utf8.str);
3740
3741 ret = mongoc_client_encryption_encrypt (
3742 client_encryption, &to_encrypt, encrypt_opts, &encrypted_field, &error);
3743 if (!ret) {
3744 goto fail;
3745 }
3746
3747 to_insert = bson_new ();
3748 BSON_APPEND_VALUE (to_insert, "encryptedField", &encrypted_field);
3749 ret = mongoc_collection_insert_one (
3750 coll, to_insert, NULL /* opts */, NULL /* reply */, &error);
3751 if (!ret) {
3752 goto fail;
3753 }
3754
3755 /* When we retrieve the document, any encrypted fields will get automatically
3756 * decrypted by the driver. */
3757 printf ("decrypted document: ");
3758 if (!print_one_document (coll, &error)) {
3759 goto fail;
3760 }
3761 printf ("\n");
3762
3763 unencrypted_client =
3764 mongoc_client_new ("mongodb://localhost/?appname=client-side-encryption");
3765 unencrypted_coll = mongoc_client_get_collection (
3766 unencrypted_client, ENCRYPTED_DB, ENCRYPTED_COLL);
3767
3768 printf ("encrypted document: ");
3769 if (!print_one_document (unencrypted_coll, &error)) {
3770 goto fail;
3771 }
3772 printf ("\n");
3773
3774 exit_status = EXIT_SUCCESS;
3775 fail:
3776 if (error.code) {
3777 fprintf (stderr, "error: %s\n", error.message);
3778 }
3779
3780 bson_free (local_masterkey);
3781 bson_destroy (kms_providers);
3782 mongoc_collection_destroy (keyvault_coll);
3783 bson_destroy (index_keys);
3784 bson_free (index_name);
3785 bson_destroy (create_index_cmd);
3786 mongoc_collection_destroy (coll);
3787 mongoc_client_destroy (client);
3788 bson_destroy (to_insert);
3789 bson_destroy (schema);
3790 bson_destroy (create_cmd);
3791 bson_destroy (create_cmd_opts);
3792 mongoc_write_concern_destroy (wc);
3793 mongoc_client_encryption_destroy (client_encryption);
3794 mongoc_client_encryption_datakey_opts_destroy (datakey_opts);
3795 mongoc_client_encryption_opts_destroy (client_encryption_opts);
3796 bson_value_destroy (&encrypted_field);
3797 mongoc_client_encryption_encrypt_opts_destroy (encrypt_opts);
3798 bson_value_destroy (&decrypted);
3799 bson_value_destroy (&datakey_id);
3800 mongoc_collection_destroy (unencrypted_coll);
3801 mongoc_client_destroy (unencrypted_client);
3802 mongoc_auto_encryption_opts_destroy (auto_encryption_opts);
3803
3804 mongoc_cleanup ();
3805 return exit_status;
3806 }
3807
3809 MongoDB, Inc
3810
3812 2017-present, MongoDB, Inc
3813
3814
3815
3816
38171.16.2 Feb 25, 2020 MONGOC_GUIDES(3)