Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MapReduce returns NaN

I have a M/R function, and I get NaN as a value for some of the results. I dont have any experience with JS. Im escaping JS using Java Drivers.

String map = "function(){" + " emit({"
            + "country: this.info.location.country, "
            + "industry: this.info.industry}, {count : 1});  }";

String reduce = "function(key, values){var count = 0.0;"
            + "values.forEach(function(v){ count += v['count'];});"
            + "return count;}";
MapReduceOutput output = collection.mapReduce(map, reduce, null,
            new BasicDBObject("lid", "foo"));

An example ti help clear things:

{"_id":{"country":"gb", "industry":"foo"}, "value":NaN}

Thanks a lot.

like image 315
theTuxRacer Avatar asked Dec 16 '22 21:12

theTuxRacer


2 Answers

I see you have solved the problem, but here's why the problem occurred.

MongoDB may rerun your reduce function on the results of previous reduce passes. This is called a re-reduce.

If a re-reduce occurred in your original scenario, you could end up with values like this:

[
  { count: 1 },
  { count: 1 },
  4
  { count: 1 },
  { count: 1 },
  { count: 1 },
  3
  { count: 1 },
]

All the { count: 1 } values are emitted by your map function. The numbers 4 and 3 are the results of previous reduce calls. Your reduce function will then access the count property of a number:

count += 4["count"];

A number doesn't have a count property, so it will return undefined. Adding undefined to a number will result in NaN.

To avoid this kind of hard-to-debug problem, you need to make sure your reduce function is idempotent. The easiest way to accomplish this is to return what you emit; the value you return in the reduce function should be of the same format as the values you emit in the map function.

like image 196
Niels van der Rest Avatar answered Dec 28 '22 20:12

Niels van der Rest


I solved it. Changed the loop, as anirvan said. But still I got a NaN. So i changed the return statement as such:

for(var i in values) { count += values[i].count; } return {count: count}; }

That did the trick. Ill need an extra line to parse it, but IDC.

like image 21
theTuxRacer Avatar answered Dec 28 '22 19:12

theTuxRacer