1MONGOC_GUIDES(3)                   libmongoc                  MONGOC_GUIDES(3)
2
3
4

NAME

6       mongoc_guides - Guides
7

COMMON TASKS

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

ADVANCED CONNECTIONS

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

CONNECTION POOLING

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

CURSORS

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", &gt);
678             BSON_APPEND_TIMESTAMP (&gt, "$gt", last_time, 0);
679             bson_append_document_end (&query, &gt);
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

BULK WRITE OPERATIONS

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

AGGREGATION FRAMEWORK EXAMPLES

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

DISTINCT AND MAPREDUCE

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‐

base_command_simple()). Do not the read command helpers (e.g. mongoc_data‐

base_read_command_with_opts()) because they are considered retryable read

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

USING LIBMONGOC IN A MICROSOFT VISUAL STUDIO PROJECT

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

CREATING INDEXES

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

AIDS FOR DEBUGGING

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

USING CLIENT-SIDE FIELD LEVEL ENCRYPTION

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

AUTHOR

3809       MongoDB, Inc
3810
3812       2017-present, MongoDB, Inc
3813
3814
3815
3816
38171.16.2                           Feb 25, 2020                 MONGOC_GUIDES(3)
Impressum