Common Tasks

Drivers for some other languages provide helper functions to perform certain common tasks. In the C Driver we must explicitly build commands to send to the server.

This snippet contains example code for the explain, copydb and cloneCollection commands.

Setup

First we'll write some code to insert sample data:

doc-common-insert.c
/* Don't try to compile this file on its own. It's meant to be #included
   by example code */

/* Insert some sample data */
bool
insert_data (mongoc_collection_t *collection)
{
   mongoc_bulk_operation_t *bulk;
   enum N { ndocs = 4 };
   bson_t *docs[ndocs];
   bson_error_t error;
   int i = 0;
   bool ret;

   bulk = mongoc_collection_create_bulk_operation (collection, true, NULL);

   docs[0] = BCON_NEW ("x", BCON_DOUBLE (1.0), "tags", "[", "dog", "cat", "]");
   docs[1] = BCON_NEW ("x", BCON_DOUBLE (2.0), "tags", "[", "cat", "]");
   docs[2] = BCON_NEW (
      "x", BCON_DOUBLE (2.0), "tags", "[", "mouse", "cat", "dog", "]");
   docs[3] = BCON_NEW ("x", BCON_DOUBLE (3.0), "tags", "[", "]");

   for (i = 0; i < ndocs; i++) {
      mongoc_bulk_operation_insert (bulk, docs[i]);
      bson_destroy (docs[i]);
      docs[i] = NULL;
   }

   ret = mongoc_bulk_operation_execute (bulk, NULL, &error);

   if (!ret) {
      fprintf (stderr, "Error inserting data: %s\n", error.message);
   }

   mongoc_bulk_operation_destroy (bulk);
   return ret;
}

/* A helper which we'll use a lot later on */
void
print_res (const bson_t *reply)
{
   BSON_ASSERT (reply);
   char *str = bson_as_json (reply, NULL);
   printf ("%s\n", str);
   bson_free (str);
}

"explain" Command

This is how to use the explain command in MongoDB 3.2+:

explain.c
bool
explain (mongoc_collection_t *collection)
{
   bson_t *command;
   bson_t reply;
   bson_error_t error;
   bool res;

   command = BCON_NEW ("explain",
                       "{",
                       "find",
                       BCON_UTF8 (COLLECTION_NAME),
                       "filter",
                       "{",
                       "x",
                       BCON_INT32 (1),
                       "}",
                       "}");
   res = mongoc_collection_command_simple (
      collection, command, NULL, &reply, &error);
   if (!res) {
      fprintf (stderr, "Error with explain: %s\n", error.message);
      goto cleanup;
   }

   /* Do something with the reply */
   print_res (&reply);

cleanup:
   bson_destroy (&reply);
   bson_destroy (command);
   return res;
}

"copydb" Command

This example requires two instances of mongo to be running.

Here's how to use the copydb command to copy a database from another instance of MongoDB:

copydb.c
bool
copydb (mongoc_client_t *client, const char *other_host_and_port)
{
   mongoc_database_t *admindb;
   bson_t *command;
   bson_t reply;
   bson_error_t error;
   bool res;

   BSON_ASSERT (other_host_and_port);
   /* Must do this from the admin db */
   admindb = mongoc_client_get_database (client, "admin");

   command = BCON_NEW ("copydb",
                       BCON_INT32 (1),
                       "fromdb",
                       BCON_UTF8 ("test"),
                       "todb",
                       BCON_UTF8 ("test2"),

                       /* If you want from a different host */
                       "fromhost",
                       BCON_UTF8 (other_host_and_port));
   res =
      mongoc_database_command_simple (admindb, command, NULL, &reply, &error);
   if (!res) {
      fprintf (stderr, "Error with copydb: %s\n", error.message);
      goto cleanup;
   }

   /* Do something with the reply */
   print_res (&reply);

cleanup:
   bson_destroy (&reply);
   bson_destroy (command);
   mongoc_database_destroy (admindb);

   return res;
}

"cloneCollection" Command

This example requires two instances of mongo to be running.

Here's an example of the cloneCollection command to clone a collection from another instance of MongoDB:

clone-collection.c
bool
clone_collection (mongoc_database_t *database, const char *other_host_and_port)
{
   bson_t *command;
   bson_t reply;
   bson_error_t error;
   bool res;

   BSON_ASSERT (other_host_and_port);
   command = BCON_NEW ("cloneCollection",
                       BCON_UTF8 ("test.remoteThings"),
                       "from",
                       BCON_UTF8 (other_host_and_port),
                       "query",
                       "{",
                       "x",
                       BCON_INT32 (1),
                       "}");
   res =
      mongoc_database_command_simple (database, command, NULL, &reply, &error);
   if (!res) {
      fprintf (stderr, "Error with clone: %s\n", error.message);
      goto cleanup;
   }

   /* Do something with the reply */
   print_res (&reply);

cleanup:
   bson_destroy (&reply);
   bson_destroy (command);

   return res;
}

Running the Examples

common-operations.c
/*
 * Copyright 2016 MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#include <mongoc.h>
#include <stdio.h>


const char *COLLECTION_NAME = "things";

#include "../doc-common-insert.c"
#include "explain.c"
#include "copydb.c"
#include "clone-collection.c"


int
main (int argc, char *argv[])
{
   mongoc_database_t *database = NULL;
   mongoc_client_t *client = NULL;
   mongoc_collection_t *collection = NULL;
   char *host_and_port;
   int res = 0;
   char *other_host_and_port = NULL;

   if (argc < 2 || argc > 3) {
      fprintf (stderr,
               "usage: %s MONGOD-1-CONNECTION-STRING "
               "[MONGOD-2-HOST-NAME:MONGOD-2-PORT]\n",
               argv[0]);
      fprintf (stderr,
               "MONGOD-1-CONNECTION-STRING can be "
               "of the following forms:\n");
      fprintf (stderr, "localhost\t\t\t\tlocal machine\n");
      fprintf (stderr, "localhost:27018\t\t\t\tlocal machine on port 27018\n");
      fprintf (stderr,
               "mongodb://user:pass@localhost:27017\t"
               "local machine on port 27017, and authenticate with username "
               "user and password pass\n");
      return 1;
   }

   mongoc_init ();

   if (strncmp (argv[1], "mongodb://", 10) == 0) {
      host_and_port = bson_strdup (argv[1]);
   } else {
      host_and_port = bson_strdup_printf ("mongodb://%s", argv[1]);
   }
   other_host_and_port = argc > 2 ? argv[2] : NULL;

   client = mongoc_client_new (host_and_port);

   if (!client) {
      fprintf (stderr, "Invalid hostname or port: %s\n", host_and_port);
      res = 2;
      goto cleanup;
   }

   mongoc_client_set_error_api (client, 2);
   database = mongoc_client_get_database (client, "test");
   collection = mongoc_database_get_collection (database, COLLECTION_NAME);

   printf ("Inserting data\n");
   if (!insert_data (collection)) {
      res = 3;
      goto cleanup;
   }

   printf ("explain\n");
   if (!explain (collection)) {
      res = 4;
      goto cleanup;
   }

   if (other_host_and_port) {
      printf ("copydb\n");
      if (!copydb (client, other_host_and_port)) {
         res = 5;
         goto cleanup;
      }

      printf ("clone collection\n");
      if (!clone_collection (database, other_host_and_port)) {
         res = 6;
         goto cleanup;
      }
   }

cleanup:
   if (collection) {
      mongoc_collection_destroy (collection);
   }

   if (database) {
      mongoc_database_destroy (database);
   }

   if (client) {
      mongoc_client_destroy (client);
   }

   bson_free (host_and_port);
   mongoc_cleanup ();
   return res;
}

First launch two separate instances of mongod (must be done from separate shells):

$ mongod
$ mkdir /tmp/db2$ mongod --dbpath /tmp/db2 --port 27018 # second instance

Now compile and run the example program:

$ cd examples/common_operations/$ gcc -Wall -o example common-operations.c $(pkg-config --cflags --libs libmongoc-1.0)$ ./example localhost:27017 localhost:27018
Inserting data
explain
{
   "executionStats" : {
      "allPlansExecution" : [],
      "executionStages" : {
         "advanced" : 19,
         "direction" : "forward" ,
         "docsExamined" : 76,
         "executionTimeMillisEstimate" : 0,
         "filter" : {
            "x" : {
               "$eq" : 1
            }
         },
         "invalidates" : 0,
         "isEOF" : 1,
         "nReturned" : 19,
         "needTime" : 58,
         "needYield" : 0,
         "restoreState" : 0,
         "saveState" : 0,
         "stage" : "COLLSCAN" ,
         "works" : 78
      },
      "executionSuccess" : true,
      "executionTimeMillis" : 0,
      "nReturned" : 19,
      "totalDocsExamined" : 76,
      "totalKeysExamined" : 0
   },
   "ok" : 1,
   "queryPlanner" : {
      "indexFilterSet" : false,
      "namespace" : "test.things",
      "parsedQuery" : {
         "x" : {
            "$eq" : 1
         }
      },
      "plannerVersion" : 1,
      "rejectedPlans" : [],
      "winningPlan" : {
         "direction" : "forward" ,
         "filter" : {
            "x" : {
               "$eq" : 1
            }
         },
         "stage" : "COLLSCAN"
      }
   },
   "serverInfo" : {
      "gitVersion" : "05552b562c7a0b3143a729aaa0838e558dc49b25" ,
      "host" : "MacBook-Pro-57.local",
      "port" : 27017,
      "version" : "3.2.6"
   }
}
copydb
{ "ok" : 1 }
clone collection
{ "ok" : 1 }