[SERVER-51779] Strange MapReduce issue - not working without JSON.stringify Created: 09/Oct/20  Updated: 27/Oct/23  Resolved: 04/Feb/22

Status: Closed
Project: Core Server
Component/s: MapReduce, Shell
Affects Version/s: None
Fix Version/s: None

Type: Bug Priority: Trivial - P5
Reporter: Benjamin Delacour Assignee: Backlog - Query Optimization
Resolution: Gone away Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified
Environment:

Ubuntu 16.04 and MacOS 10.15.6.
With Mongo 4.2.1 and 4.4.0


Assigned Teams:
Query Optimization
Participants:

 Description   

Hello,

Sorry if it is a duplicate, I didn't find any similar issue.

We have a problem with a MapReduce we are writing. It works when we test the functions in the shell, all guidelines from documentation concerning reduce are fulfilled but we have empty result when executing it on a collection.

I wrote a file to reproduce the problem :

db = db.getSiblingDB('test');
db.baskets.drop();
 
for (const x of Array(5).keys()) {
    db.baskets.insert({"fruit": "orange"});
}
 
for (const x of Array(8).keys()) {
    db.baskets.insert({"fruit": "apple"});
}
 
for (const x of Array(27).keys()) {
    db.baskets.insert({"fruit": "banana"});
}
 
print("This should work")
cursor = db.baskets.mapReduce(function () {
    emit("fruit", {[this.fruit]: 1});
}, function (key, values) {
    return values.reduce(function (a, c) {
        for (const [fruit, count] of Object.entries(c)) {
            if (!a[fruit]) {
                a[fruit] = 0;
            }
            a[fruit] += count;
        }
        return a;
    });
}, {out: {inline: 1}});
printjson(cursor);
 
print("\n\nThis is the same but with a useless JSON.stringify of values in reduce")
cursor = db.baskets.mapReduce(function () {
    emit("fruit", {[this.fruit]: 1});
}, function (key, values) {
    JSON.stringify(values); // This line is completely useless... But it doesn't work without
    return values.reduce(function (a, c) {
        for (const [fruit, count] of Object.entries(c)) {
            if (!a[fruit]) {
                a[fruit] = 0;
            }
            a[fruit] += count;
        }
        return a;
    });
}, {out: {inline: 1}});
printjson(cursor);

Here is the result :

This should work
{
	"results" : [
		{
			"_id" : "fruit",
			"value" : {
				"banana" : 1
			}
		}
	],
	"ok" : 1
}
 
 
This is the same but with a useless JSON.stringify of values in reduce
{
	"results" : [
		{
			"_id" : "fruit",
			"value" : {
				"banana" : 27,
				"apple" : 8,
				"orange" : 5
			}
		}
	],
	"ok" : 1
}

I know I could write it using an aggregation but I want to emit more keys from other fields in the future instead of running multiple aggregations. (May be you have a better idea on this ?)

 

Thank you !



 Comments   
Comment by Esha Bhargava [ 04/Feb/22 ]

Closing these tickets as part of the deprecation of mapReduce.

Comment by Edwin Zhou [ 03/Jun/21 ]

Hi benjamin.delacour@jobijoba.com,

I apologize for the delay in our response; we're extremely grateful for your patience.

I'm able to reproduce this issue in 4.4.6 as well. It's indeed strange behavior that we need to call JSON.stringify on the resulting mapped values before calling reduce. I'll pass this on to the appropriate team to further investigate this behavior.

Best,
Edwin

Comment by Benjamin Delacour [ 21/Oct/20 ]

Hi @Jessica Sigafoos, on the server side I'm using binary installed from here : "http://repo.mongodb.org/apt/ubuntu/dists/xenial/mongodb-org/4.2/multiverse/binary-amd64/mongodb-org-server_4.2.1_amd64.deb" (installed from `mongo-org` with apt).

On the client side, I'm using binary installed from here : "https://fastdl.mongodb.org/osx/mongodb-macos-x86_64-4.4.0.tgz" (installed from `mongodb-community`using brew).

Comment by Jessica Sigafoos [ 21/Oct/20 ]

Hi benjamin.delacour@jobijoba.com, which binary are you using?

Comment by Benjamin Delacour [ 09/Oct/20 ]

Iterating on keys instead of entries works... so I'm not blocked, but that's still strange.

This is how I resolved my problem :

db.baskets.mapReduce(function () {
    emit("fruit", {[this.fruit]: 1});
}, function (key, values) {
    return values.reduce(function (a, c) {
        for (const fruit in c) { // Iterate on keys instead of entries
            if (c.hasOwnProperty(fruit)) {
                if (!a[fruit]) {
                    a[fruit] = 0;
                }
                a[fruit] += c[fruit];
            }
        }
        return a;
    });
}, {out: {inline: 1}});

Generated at Thu Feb 08 05:26:24 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.