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