Search code examples
mongodbmongodb-java

mapReduce inline results with java mongodb driver 3.2


How to have inline results from a mapReducet with the mongodb java driver 3.2?

with driver version 2.x I was doing:

DBColleciont coll = client.getDB(dbName).getCollection(collName);
coll.mapReduce(map, reduce, null, OutputType.INLINE, query);

the new 3.x driver has two mapReduce() methods returning MapReduceIterable which misses a method to specify the INLINE output mode.

MongoCollection<Documetn> coll = client.getDatabase(dbName).getCollection(collName)
coll
    .mapReduce(map, reduce).
    .filter(query);

Solution

  • I think I found it... I had a deeper look into mongodb's Java driver source and it seems that the INLINE output feature is implicitly accessible:

    The class MapReduceIterableImpl<TDocument, TResult>(MapReduceIterableImpl.java), which is the default implementation of the Interface return type of mapReduce(), holds a private boolean inline with initial value true.

    The only place where this can ever be switched to false is the method collectionName(final String collectionName) for which the description is as follows:

    Sets the collectionName for the output of the MapReduce The default action is replace the collection if it exists, to change this use action(com.mongodb.client.model.MapReduceAction).

    If you never call this method on the object instance after mapReduce(), it will remain true as initialized...meaning: if there is no output collection, it must be inline.

    Later on, when you access your result with iterator(), first(), forEach(...) etc internally the execute() method gets called which hast the magic if condition:

    if (inline) {
            MapReduceWithInlineResultsOperation<TResult> operation =
                    new MapReduceWithInlineResultsOperation<TResult>(namespace,
                            new BsonJavaScript(mapFunction),
                            new BsonJavaScript(reduceFunction),
                            codecRegistry.get(resultClass))
                            .filter(toBsonDocument(filter))
                            .limit(limit)
                            .maxTime(maxTimeMS, MILLISECONDS)
                            .jsMode(jsMode)
                            .scope(toBsonDocument(scope))
                            .sort(toBsonDocument(sort))
                            .verbose(verbose)
                            .readConcern(readConcern);
    ....
    
    } else {
            MapReduceToCollectionOperation operation =
                    new MapReduceToCollectionOperation(namespace, new BsonJavaScript(mapFunction), new BsonJavaScript(reduceFunction),
                            collectionName)
                            .filter(toBsonDocument(filter))
                            .limit(limit)
                            .maxTime(maxTimeMS, MILLISECONDS)
                            .jsMode(jsMode)
                            .scope(toBsonDocument(scope))
                            .sort(toBsonDocument(sort))
                            .verbose(verbose)
                            .action(action.getValue())
                            .nonAtomic(nonAtomic)
                            .sharded(sharded)
                            .databaseName(databaseName)
                            .bypassDocumentValidation(bypassDocumentValidation);
    

    ...so it is instanciating MapReduceWithInlineResultsOperation when collectionName() has not been called.

    I had no chance to test it because my NetBeans hates me at the moment, but I think it is pretty clear. What do you think, did I miss something?

    Would be glad if I could help you shift the code to API 3.x, great project!