1MONGOC_DISTINCT_MAPREDUCE(3) libmongoc MONGOC_DISTINCT_MAPREDUCE(3)
2
3
4
6 mongoc_distinct_mapreduce - "distinct" and "mapReduce"
7
8 This document provides some practical, simple, examples to demonstrate
9 the distinct and mapReduce commands.
10
12 First we'll write some code to insert sample data: doc-com‐
13 mon-insert.c.INDENT 0.0
14
15 /* Don't try to compile this file on its own. It's meant to be #included
16 by example code */
17
18 /* Insert some sample data */
19 bool
20 insert_data (mongoc_collection_t *collection)
21 {
22 mongoc_bulk_operation_t *bulk;
23 enum N { ndocs = 4 };
24 bson_t *docs[ndocs];
25 bson_error_t error;
26 int i = 0;
27 bool ret;
28
29 bulk = mongoc_collection_create_bulk_operation_with_opts (collection, NULL);
30
31 docs[0] = BCON_NEW ("x", BCON_DOUBLE (1.0), "tags", "[", "dog", "cat", "]");
32 docs[1] = BCON_NEW ("x", BCON_DOUBLE (2.0), "tags", "[", "cat", "]");
33 docs[2] = BCON_NEW (
34 "x", BCON_DOUBLE (2.0), "tags", "[", "mouse", "cat", "dog", "]");
35 docs[3] = BCON_NEW ("x", BCON_DOUBLE (3.0), "tags", "[", "]");
36
37 for (i = 0; i < ndocs; i++) {
38 mongoc_bulk_operation_insert (bulk, docs[i]);
39 bson_destroy (docs[i]);
40 docs[i] = NULL;
41 }
42
43 ret = mongoc_bulk_operation_execute (bulk, NULL, &error);
44
45 if (!ret) {
46 fprintf (stderr, "Error inserting data: %s\n", error.message);
47 }
48
49 mongoc_bulk_operation_destroy (bulk);
50 return ret;
51 }
52
53 /* A helper which we'll use a lot later on */
54 void
55 print_res (const bson_t *reply)
56 {
57 char *str;
58 BSON_ASSERT (reply);
59 str = bson_as_canonical_extended_json (reply, NULL);
60 printf ("%s\n", str);
61 bson_free (str);
62 }
63
64
66 This is how to use the distinct command to get the distinct values of x
67 which are greater than 1: distinct.c.INDENT 0.0
68
69 bool
70 distinct (mongoc_database_t *database)
71 {
72 bson_t *command;
73 bson_t reply;
74 bson_error_t error;
75 bool res;
76 bson_iter_t iter;
77 bson_iter_t array_iter;
78 double val;
79
80 command = BCON_NEW ("distinct",
81 BCON_UTF8 (COLLECTION_NAME),
82 "key",
83 BCON_UTF8 ("x"),
84 "query",
85 "{",
86 "x",
87 "{",
88 "$gt",
89 BCON_DOUBLE (1.0),
90 "}",
91 "}");
92 res =
93 mongoc_database_command_simple (database, command, NULL, &reply, &error);
94 if (!res) {
95 fprintf (stderr, "Error with distinct: %s\n", error.message);
96 goto cleanup;
97 }
98
99 /* Do something with reply (in this case iterate through the values) */
100 if (!(bson_iter_init_find (&iter, &reply, "values") &&
101 BSON_ITER_HOLDS_ARRAY (&iter) &&
102 bson_iter_recurse (&iter, &array_iter))) {
103 fprintf (stderr, "Couldn't extract \"values\" field from response\n");
104 goto cleanup;
105 }
106
107 while (bson_iter_next (&array_iter)) {
108 if (BSON_ITER_HOLDS_DOUBLE (&array_iter)) {
109 val = bson_iter_double (&array_iter);
110 printf ("Next double: %f\n", val);
111 }
112 }
113
114 cleanup:
115 /* cleanup */
116 bson_destroy (command);
117 bson_destroy (&reply);
118 return res;
119 }
120
121
123 A simple example using the map reduce framework. It simply adds up the
124 number of occurrences of each "tag".
125
126 First define the map and reduce functions: constants.c.INDENT 0.0
127
128 const char *const COLLECTION_NAME = "things";
129
130 /* Our map function just emits a single (key, 1) pair for each tag
131 in the array: */
132 const char *const MAPPER = "function () {"
133 "this.tags.forEach(function(z) {"
134 "emit(z, 1);"
135 "});"
136 "}";
137
138 /* The reduce function sums over all of the emitted values for a
139 given key: */
140 const char *const REDUCER = "function (key, values) {"
141 "var total = 0;"
142 "for (var i = 0; i < values.length; i++) {"
143 "total += values[i];"
144 "}"
145 "return total;"
146 "}";
147 /* Note We can't just return values.length as the reduce function
148 might be called iteratively on the results of other reduce
149 steps. */
150
151
152Run the mapReduce command. Use the generic command helpers (e.g. mongoc_data‐
155operations. If retryable reads are enabled, those operations will retry once
156on a retryable error, giving undesirable behavior for mapReduce.
157map-reduce-basic.c.INDENT 0.0
158
159 bool
160 map_reduce_basic (mongoc_database_t *database)
161 {
162 bson_t reply;
163 bson_t *command;
164 bool res;
165 bson_error_t error;
166 mongoc_cursor_t *cursor;
167 const bson_t *doc;
168
169 bool query_done = false;
170
171 const char *out_collection_name = "outCollection";
172 mongoc_collection_t *out_collection;
173
174 /* Empty find query */
175 bson_t find_query = BSON_INITIALIZER;
176
177 /* Construct the mapReduce command */
178
179 /* Other arguments can also be specified here, like "query" or
180 "limit" and so on */
181 command = BCON_NEW ("mapReduce",
182 BCON_UTF8 (COLLECTION_NAME),
183 "map",
184 BCON_CODE (MAPPER),
185 "reduce",
186 BCON_CODE (REDUCER),
187 "out",
188 BCON_UTF8 (out_collection_name));
189 res =
190 mongoc_database_command_simple (database, command, NULL, &reply, &error);
191
192 if (!res) {
193 fprintf (stderr, "MapReduce failed: %s\n", error.message);
194 goto cleanup;
195 }
196
197 /* Do something with the reply (it doesn't contain the mapReduce results) */
198 print_res (&reply);
199
200 /* Now we'll query outCollection to see what the results are */
201 out_collection =
202 mongoc_database_get_collection (database, out_collection_name);
203 cursor = mongoc_collection_find_with_opts (
204 out_collection, &find_query, NULL, NULL);
205 query_done = true;
206
207 /* Do something with the results */
208 while (mongoc_cursor_next (cursor, &doc)) {
209 print_res (doc);
210 }
211
212 if (mongoc_cursor_error (cursor, &error)) {
213 fprintf (stderr, "ERROR: %s\n", error.message);
214 res = false;
215 goto cleanup;
216 }
217
218 cleanup:
219 /* cleanup */
220 if (query_done) {
221 mongoc_cursor_destroy (cursor);
222 mongoc_collection_destroy (out_collection);
223 }
224
225 bson_destroy (&reply);
226 bson_destroy (command);
227
228 return res;
229 }
230
231
233 You must have replica set running for this.
234
235 In this example we contact a secondary in the replica set and do an
236 "inline" map reduce, so the results are returned immediately:
237 map-reduce-advanced.c.INDENT 0.0
238
239 bool
240 map_reduce_advanced (mongoc_database_t *database)
241 {
242 bson_t *command;
243 bson_error_t error;
244 bool res = true;
245 mongoc_cursor_t *cursor;
246 mongoc_read_prefs_t *read_pref;
247 const bson_t *doc;
248
249 /* Construct the mapReduce command */
250 /* Other arguments can also be specified here, like "query" or "limit"
251 and so on */
252
253 /* Read the results inline from a secondary replica */
254 command = BCON_NEW ("mapReduce",
255 BCON_UTF8 (COLLECTION_NAME),
256 "map",
257 BCON_CODE (MAPPER),
258 "reduce",
259 BCON_CODE (REDUCER),
260 "out",
261 "{",
262 "inline",
263 "1",
264 "}");
265
266 read_pref = mongoc_read_prefs_new (MONGOC_READ_SECONDARY);
267 cursor = mongoc_database_command (
268 database, MONGOC_QUERY_NONE, 0, 0, 0, command, NULL, read_pref);
269
270 /* Do something with the results */
271 while (mongoc_cursor_next (cursor, &doc)) {
272 print_res (doc);
273 }
274
275 if (mongoc_cursor_error (cursor, &error)) {
276 fprintf (stderr, "ERROR: %s\n", error.message);
277 res = false;
278 }
279
280 mongoc_cursor_destroy (cursor);
281 mongoc_read_prefs_destroy (read_pref);
282 bson_destroy (command);
283
284 return res;
285 }
286
287
289 Here's how to run the example code basic-aggregation.c.INDENT 0.0
290
291 /*
292 * Copyright 2016 MongoDB, Inc.
293 *
294 * Licensed under the Apache License, Version 2.0 (the "License");
295 * you may not use this file except in compliance with the License.
296 * You may obtain a copy of the License at
297 *
298 * http://www.apache.org/licenses/LICENSE-2.0
299 *
300 * Unless required by applicable law or agreed to in writing, software
301 * distributed under the License is distributed on an "AS IS" BASIS,
302 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
303 * See the License for the specific language governing permissions and
304 * limitations under the License.
305 */
306
307
308 #include <mongoc/mongoc.h>
309 #include <stdio.h>
310
311
312 #include "constants.c"
313
314 #include "../doc-common-insert.c"
315 #include "distinct.c"
316 #include "map-reduce-basic.c"
317 #include "map-reduce-advanced.c"
318
319
320 int
321 main (int argc, char *argv[])
322 {
323 mongoc_database_t *database = NULL;
324 mongoc_client_t *client = NULL;
325 mongoc_collection_t *collection = NULL;
326 mongoc_uri_t *uri = NULL;
327 bson_error_t error;
328 char *host_and_port = NULL;
329 int exit_code = EXIT_FAILURE;
330
331 if (argc != 2) {
332 fprintf (stderr, "usage: %s CONNECTION-STRING\n", argv[0]);
333 fprintf (stderr,
334 "the connection string can be of the following forms:\n");
335 fprintf (stderr, "localhost\t\t\t\tlocal machine\n");
336 fprintf (stderr, "localhost:27018\t\t\t\tlocal machine on port 27018\n");
337 fprintf (stderr,
338 "mongodb://user:pass@localhost:27017\t"
339 "local machine on port 27017, and authenticate with username "
340 "user and password pass\n");
341 return exit_code;
342 }
343
344 mongoc_init ();
345
346 if (strncmp (argv[1], "mongodb://", 10) == 0) {
347 host_and_port = bson_strdup (argv[1]);
348 } else {
349 host_and_port = bson_strdup_printf ("mongodb://%s", argv[1]);
350 }
351
352 uri = mongoc_uri_new_with_error (host_and_port, &error);
353 if (!uri) {
354 fprintf (stderr,
355 "failed to parse URI: %s\n"
356 "error message: %s\n",
357 host_and_port,
358 error.message);
359 goto cleanup;
360 }
361
362 client = mongoc_client_new_from_uri (uri);
363 if (!client) {
364 goto cleanup;
365 }
366
367 mongoc_client_set_error_api (client, 2);
368 database = mongoc_client_get_database (client, "test");
369 collection = mongoc_database_get_collection (database, COLLECTION_NAME);
370
371 printf ("Inserting data\n");
372 if (!insert_data (collection)) {
373 goto cleanup;
374 }
375
376 printf ("distinct\n");
377 if (!distinct (database)) {
378 goto cleanup;
379 }
380
381 printf ("map reduce\n");
382 if (!map_reduce_basic (database)) {
383 goto cleanup;
384 }
385
386 printf ("more complicated map reduce\n");
387 if (!map_reduce_advanced (database)) {
388 goto cleanup;
389 }
390
391 exit_code = EXIT_SUCCESS;
392
393 cleanup:
394 if (collection) {
395 mongoc_collection_destroy (collection);
396 }
397
398 if (database) {
399 mongoc_database_destroy (database);
400 }
401
402 if (client) {
403 mongoc_client_destroy (client);
404 }
405
406 if (uri) {
407 mongoc_uri_destroy (uri);
408 }
409
410 if (host_and_port) {
411 bson_free (host_and_port);
412 }
413
414 mongoc_cleanup ();
415 return exit_code;
416 }
417
418
419If you want to try the advanced map reduce example with a secondary, start a
420replica set (instructions for how to do this can be found here).
421
422Otherwise, just start an instance of MongoDB:
423
424 $ mongod
425
426 Now compile and run the example program:
427
428 $ cd examples/basic_aggregation/
429 $ gcc -Wall -o agg-example basic-aggregation.c $(pkg-config --cflags --libs libmongoc-1.0)
430 $ ./agg-example localhost
431
432 Inserting data
433 distinct
434 Next double: 2.000000
435 Next double: 3.000000
436 map reduce
437 { "result" : "outCollection", "timeMillis" : 155, "counts" : { "input" : 84, "emit" : 126, "reduce" : 3, "output" : 3 }, "ok" : 1 }
438 { "_id" : "cat", "value" : 63 }
439 { "_id" : "dog", "value" : 42 }
440 { "_id" : "mouse", "value" : 21 }
441 more complicated map reduce
442 { "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 }
443
445 MongoDB, Inc
446
448 2017-present, MongoDB, Inc
449
450
451
452
4531.16.2 Feb 25, 2020 MONGOC_DISTINCT_MAPREDUCE(3)