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